Skip to main content
Claude Code and Codex occasionally pause to ask the user something — for approval before running a risky command, to pick from a list of options, or for free-text input. The worker surfaces these as structured thread items and resolves the user’s reply back to the agent. In v1 users answer with plain text in the standard thread widget. The structured metadata preserves the agent’s full prompt payload so a future native renderer (approval buttons, choice pickers, multi-question forms) can pick it up without protocol changes.

Prompt kinds

Two kinds are raised in practice. The worker preserves the kind in metadata.prompt.kind. Codex has no ask_user. Approvals are its only channel to a human, so a Codex agent asks for input by requesting approval or not at all. Within ask_user_question the variants are per question, not per prompt — one call can mix them: Every option list also takes free text. The tool tells the agent so — “the user can always answer with free text via ‘Other’” — and the worker honours it: a token matching no option is handed back verbatim. A client that offers only the listed options is narrower than the contract. A kind: approval prompt is the user-facing side of the agent’s permissions: approval setting: when an agent runs under approval, each sensitive action is routed here for a human decision before it proceeds. If no human answers, the turn ends and waits — it is not denied, and the wait outlives the worker. Under permissions: autonomous there is no approval gate, so the agent does not raise these prompts. See the permissions setting for how it is resolved.

pending_prompt (worker → thread)

Posted when the agent’s turn pauses for input. The user is expected to reply by posting one or more thread items.
The agent’s turn blocks at the tool call awaiting the answer; no further thread items are emitted until the prompt resolves. The content is the question and nothing else — no header line announcing that a question is being asked, and no instructions for answering. metadata.type is what says the item is a prompt, and clients are expected to render their own affordance from it rather than reading one out of the text. The options stay numbered, because the numbering is part of the question: it is what the user is choosing between, and a reply of 2 is resolved against it. There is at most one active prompt per thread at any time.

pending_prompt_resolved (worker → thread, audit)

Posted once the prompt has been answered or cancelled. Audit-only — the worker has already delivered the answer to the agent before this item lands. The two states carry different fields, and never both: an answered prompt carries answers and no reason; a cancelled one carries reason and no answers.

Answering

An answer reaches the worker as an inbound thread item. There are two ways to give one, and they are not equally good.

Structured answers (what a surface should send)

If the item carries metadata.answers — one entry per question, in the same shape the agent’s tool returns — the worker uses it as it stands. No parsing, no matching text against options, no guessing which answer belongs to which question.
The set is checked, not trusted: there must be one entry per question and each answer must be a list. A partial set is refused and the text is mapped instead — padding it would report a question answered that nobody answered.
This is the only correct way to answer a prompt of several questions. A text answer is mapped against every question in the prompt, so one string becomes the answer to all of them.

Text answers (the fallback)

With no metadata.answers, the next inbound user item while a prompt is active is the answer, and its text is mapped per kind: Items posted while no prompt is active are queued as the next agent turn’s prompt, as usual.

Lifecycle and audit

  • Answered — the agent receives the answer and continues. Audit item prompt_state: answered, carrying the answers the agent was given, however they were arrived at.
  • Cancelled — the prompt was abandoned: the run ended, or a second question superseded it. Audit item prompt_state: cancelled with reason.
There is no third state. A prompt is either answered or cancelled, and a worker that cannot post the audit item logs the failure and leaves both sides open, which is recoverable — it does not invent a resolution the agent never saw.

Worker restart

An unresolved prompt survives a worker restart. The worker adopts it back off the thread on attach — including its questions, which is what lets an answer be matched to them — and answering it a week later resumes the same agent session. That adoption is load-bearing rather than cosmetic. The record of which prompt is open lives in memory, so a worker that came back without it would take the answer for an ordinary message: the agent would resume looking healthy, no pending_prompt_resolved would ever be posted, and anything reading “unresolved prompt” as “waiting for input” would wait forever.

If the prompt cannot be posted

The question was never asked, and the agent is told so in those terms — “Approval unavailable”, never “the user denied”. The call is still refused, but an agent told a human refused goes and picks another approach, when the right move is to ask again.

See also