import { useCallback, useEffect, useState } from 'react'; import { api } from '../api'; import { fmtId } from '../util'; import type { Approval, ResponseNeeded } from '../types'; export function Approvals({ refreshTick = 0, responses = [], goSessions }: { refreshTick?: number; responses?: ResponseNeeded[]; goSessions?: () => void }) { const [items, setItems] = useState(null); const [err, setErr] = useState(''); const load = useCallback(() => { api<{ approvals: Approval[] }>('/api/approvals?status=pending') .then((d) => { setItems(d.approvals); setErr(''); }).catch((e) => setErr((e as Error).message)); }, []); useEffect(() => { load(); }, [load, refreshTick]); const decide = (id: string, decision: 'approve' | 'deny') => api(`/api/approvals/${encodeURIComponent(id)}?decision=${decision}`, { method: 'POST' }) .then(load).catch((e) => alert((e as Error).message)); return ( <>

Unified response-needed inbox — formal hitl-prompt/v1 approvals, provider prompts, and agent sessions waiting for human input.

{err &&

{err}

} {responses.length > 0 && (

Response Needed

{responses.map((r) => (
{r.source === 'pty' ? 'Interactive session prompt' : r.source}

{r.prompt}

{fmtId(r.instance_id)} · {r.status}
{goSessions && }
))}
)} {!items ?

Loading…

: !items.length && !responses.length ?

No pending approvals or responses.

: items.length ? ( {items.map((p) => ( ))}
{items.length} approval request(s) awaiting your decision
RequestRiskInstanceDecision
{p.prompt} {p.risk} {fmtId(p.instance_id)} {' '}
) : null} ); }