/** * Soft deadline for an `ask_user` call when someone is likely watching (a * plain interactive run). Past it the tool stops blocking: the agent proceeds * on its best guess while the question stays answerable in the app. */ export declare const ASK_USER_INTERACTIVE_DEADLINE_MS: number; /** * Soft deadline when nobody is watching — autopilot (Ken), task run-all * sweeps and scheduled prompts. Short, so an unattended run does not stall. */ export declare const ASK_USER_UNATTENDED_DEADLINE_MS: number; /** * The longest an `ask_user` call can block (the largest soft deadline). The * tool's own execution timeout is derived from this. */ export declare const ASK_USER_TIMEOUT_MS: number; /** Pick the soft deadline for a run, by whether a human is likely watching. */ export declare function askSoftDeadlineMs(unattended: boolean): number; /** One selectable answer. `value` is what the agent gets back; `label` is UI. */ export interface AskOption { label: string; value?: string; /** One short line under the label, for rows the user has to weigh up. */ hint?: string; /** Marks the agent's recommendation. Tagged in the UI, never preselected. */ recommended?: boolean; } export type AskQuestionKind = "confirm" | "choice" | "multi" | "text"; export interface AskQuestion { /** Stable key this question's answer is returned under. */ id: string; question: string; kind: AskQuestionKind; /** Optional one-line elaboration shown under the question. */ detail?: string; /** Choices for `choice`/`multi`. `confirm` defaults to Yes/No when omitted. */ options?: AskOption[]; /** Whether the free-text escape is offered (default true, forced on `text`). */ allowOther?: boolean; } export interface AskUserRequest { questions: AskQuestion[]; } /** The frame a host broadcasts so its UI can render the band. */ export interface AskUserPrompt extends AskUserRequest { id: string; } export type AskUserResult = { action: "answer"; answers: Record; } /** * No answer. `superseded` means the user replied with a message of their own * instead of picking — the question is moot, but they are still talking. */ | { action: "cancel"; superseded?: boolean; } /** * The soft deadline passed with no answer. The question stays open; a later * answer is delivered to the agent as a message (see `onLateAnswer`). */ | { action: "deferred"; }; /** An answer that arrived after the tool call had already moved on. */ export interface LateAskAnswer { prompt: AskUserPrompt; answers: Record; } /** * Questions parked on the user. A question is either *pending* (the tool call * is blocked on it) or *deferred* (its soft deadline passed, the agent moved * on, but the user can still answer it). */ export interface AskUserBridge { /** Park a question: broadcasts it, resolves on answer, cancel or soft deadline. */ park: (request: AskUserRequest) => Promise; /** * Answer or dismiss an open question. A deferred question's answer goes to * `onLateAnswer`. False when the id is not open (settled, superseded, closed). */ settle: (id: string, result: AskUserResult) => boolean; /** * Release every pending question with `result` (default: cancel) and close * every deferred one — for run abort, a superseding user message, teardown. */ cancelAll: (result?: AskUserResult) => void; /** Close deferred questions without touching a pending one (session reset). */ closeDeferred: () => void; /** Questions the turn is currently blocked on. */ readonly pendingCount: number; /** Questions past their soft deadline that can still be answered. */ readonly deferredCount: number; /** Ids of every question that can still be answered (pending or deferred). */ readonly openIds: () => string[]; /** * Every question that can still be answered, in the order asked, so a * reconnecting window can show the cards again (a reload lost them, leaving * the agent waiting on a question nobody could see). */ readonly openPrompts: () => Array<{ prompt: AskUserPrompt; deferred: boolean; }>; } export interface AskUserBridgeOptions { broadcast: (prompt: AskUserPrompt) => void; /** Soft deadline in ms, read when each question is parked. */ timeoutMs?: number | (() => number); /** The soft deadline passed: the agent proceeds, the question stays open. */ onTimeout?: (prompt: AskUserPrompt) => void; /** A deferred question was answered. Called at most once per question. */ onLateAnswer?: (late: LateAskAnswer) => void; /** Deferred questions closed without an answer (superseded, cancelled, reset). */ onClosed?: (ids: string[]) => void; } export declare function createAskUserBridge(opts: AskUserBridgeOptions): AskUserBridge; /** * What the model reads when the soft deadline passes. Short on purpose; the * safety line is the part that matters — a guessed answer must never license * an action the user cannot take back. */ export declare const ASK_DEFERRED_RESULT: string; /** * Render the user's answers as the tool result the model reads. * * Questions are echoed alongside their answers so the model never has to * remember what `store` meant, and an unanswered question is stated as such * rather than silently missing. */ export declare function formatAskResult(questions: AskQuestion[], result: AskUserResult): string; /** * Frame a late answer as a message to the agent. It names the question it * answers, because by now the agent has moved on and may have asked others. */ export declare function formatLateAnswer(late: LateAskAnswer): string; /** Where a late answer goes: the host's existing user-message queue. */ export interface LateAnswerSink { /** Queue a user message; drained as mid-run steering by a live run. */ queueMessage: (text: string) => number; /** True while a run (or a run-owning cycle) can still drain the queue. */ isBusy: () => boolean; /** The queue changed (so clients can refresh their queued strip). */ onQueued?: () => void; /** No run is live: start one from the queue (the host's stranded-queue drain). */ startIdleRun: () => void; } /** * Deliver a late answer through the existing queue rather than a new channel: * a live run drains it at its next steering boundary; an idle session runs it * as the next user turn. */ export declare function deliverLateAnswer(late: LateAskAnswer, sink: LateAnswerSink): void;