/** * PromptOverlay — renders the active blocking prompt and binds each answer/cancel * to the matching `*.respond` RPC (spec §4 reply contract; §8 #6 deadlock fix): * clarify.respond {answer, request_id} · approval.respond {choice, session_id} · * sudo.respond {password, request_id} · secret.respond {value, request_id}. * Every cancel path (Esc/Ctrl+C) sends the deny/empty reply so the agent unblocks. * * `onRespond` is the entry-wired boundary callback (fires `gateway.request`); the * overlay also clears the store prompt so the composer returns. Narrowing is done * with reactive `as*()` accessors so each sub-prompt gets its typed payload. */ import { Match, Switch } from 'solid-js' import type { SessionStore } from '../../logic/store.ts' import { ApprovalPrompt } from './approvalPrompt.tsx' import { ClarifyPrompt } from './clarifyPrompt.tsx' import { MaskedPrompt } from './maskedPrompt.tsx' export interface PromptOverlayProps { readonly store: SessionStore readonly onRespond: (method: string, params: Record) => void readonly sessionId: () => string | undefined } export function PromptOverlay(props: PromptOverlayProps) { const prompt = () => props.store.state.prompt const respond = (method: string, params: Record) => { props.onRespond(method, params) props.store.clearPrompt() } const asApproval = () => { const p = prompt() return p && p.kind === 'approval' ? p : undefined } const asClarify = () => { const p = prompt() return p && p.kind === 'clarify' ? p : undefined } const asSudo = () => { const p = prompt() return p && p.kind === 'sudo' ? p : undefined } const asSecret = () => { const p = prompt() return p && p.kind === 'secret' ? p : undefined } return ( {p => ( respond('approval.respond', { choice, session_id: props.sessionId() })} onCancel={() => respond('approval.respond', { choice: 'deny', session_id: props.sessionId() })} /> )} {p => ( respond('clarify.respond', { answer, request_id: p().requestId })} onCancel={() => respond('clarify.respond', { answer: '', request_id: p().requestId })} /> )} {p => ( respond('sudo.respond', { password: value, request_id: p().requestId })} onCancel={() => respond('sudo.respond', { password: '', request_id: p().requestId })} /> )} {p => ( respond('secret.respond', { request_id: p().requestId, value })} onCancel={() => respond('secret.respond', { request_id: p().requestId, value: '' })} /> )} ) }