openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
189 lines (156 loc) • 9.17 kB
Markdown
---
summary: "How ask_user pauses an agent turn for a structured human decision"
read_when:
- You want an agent to ask the user a structured question
- You are answering or debugging an ask_user prompt
- You need the ask_user schema, timeout, or channel behavior
title: "Ask user"
---
`ask_user` lets the agent ask the human one to three structured questions and
wait for the answers. It is for decisions that genuinely belong to the user,
not routine confirmation or information the agent can derive from the request,
code, or a sensible default.
The tool is available only in the main session. Subagents and other non-primary
runs do not receive it.
## Answer a question
You can answer from any supported conversation surface:
- The web Control UI docks a question panel directly above the composer. For
multi-question prompts, the panel shows one question at a time and advances
through a short stepper. After resolution, the panel closes and the chat
keeps the full question alongside your answer in a compact summary. Skipped
or expired questions keep their wording alongside the outcome.
Switching between pending questions preserves both selected choices and typed
answers, even when typed text matches an option label.
- The TUI shows a question prompt in both Gateway and local modes. Use arrow
keys or number keys to choose an option, **Other…** to type an answer, or
**Skip**. Multi-select prompts let you toggle choices before confirming;
multi-question prompts advance one question at a time. Press Esc to return
to the composer without answering, then `/question` to reopen the prompt.
- Telegram renders each choice as a full-width native button for one
single-select question. **Other…** switches to Telegram's reply input without
resolving the question.
- Discord, Slack, and Mattermost render native buttons for a single-choice,
single-question prompt. Mattermost retires its prompt on the tap it accepts;
a question that ends elsewhere leaves the buttons in place until someone taps
one and is told it was already answered.
- For a question created by an active OpenClaw run, a plain-text reply works on
any channel when your current permissions match the creator's. Reply with a
number, an option label, or your own answer. For multi-select questions,
separate choices with commas.
Questions from a standalone [attached MCP client](/cli/attach) do not carry an
OpenClaw run's creator binding. Answer those using the question controls in the
Control UI, TUI, or native app, not an ordinary channel message.
Guests with `operator.sessions.write` can answer ordinary questions from their
own authorized agent run in a session they created. The Control UI restores
those pending questions after reconnecting. Access to another person's session
does not expose their questions or make them answerable. Secret, administrative,
and sessionless questions keep their existing privileged access requirements.
Answering a question does not grant the agent additional permissions.
OpenClaw always enables a free-text **Other** answer. The agent must not add an
`Other` option to the authored option list.
Never answer `ask_user` with a credential. When the agent needs an API key it
uses the [`secrets` tool](/tools/secrets), whose masked prompt stores the value
without it entering the chat, the transcript, or the model's context.
## Platform behavior
Answers work on every supported conversation surface. The web Control UI and
TUI show one question at a time; collapsing the prompt restores the composer
with a slim pending-question indicator. The TUI's `/question` command reopens
it, and a plain reply still answers an eligible pending question. iOS, macOS, and Android show
inline cards; multiple questions stay stacked as an intentional touch-friendly
idiom. The TUI keeps a compact resolution notice in the chat; other platforms
keep the question-to-answer summary without timed eviction. **Skip** is
available everywhere and declines the entire prompt.
Multi-question and multi-select prompts degrade to readable text on messaging
channels. The Control UI and TUI keep the full structured stepper. The TUI shows
the time remaining, dismisses expired prompts, and restores pending questions
for the selected session after reconnecting or switching sessions. Local mode
keeps questions in the running process; they do not survive exiting the TUI.
## Async questions
Codex async questions use the same panel above the Control UI composer. They open
without taking keyboard focus and leave the message box available while the agent
continues working. Collapse the panel to keep a compact unanswered-question count
and the current question visible. New messages, the question's own completed
turn, and collapsed work history do not dismiss the question or reopen a minimized
panel. When a later run completes successfully, older reminders leave the dock.
Overlapping runs, commentary, interruptions, and failed runs do not retire a
question. Successful restart recovery also moves older reminders into history;
restarting alone does not.
The transcript keeps the question with **No longer pending** and an **Answer**
button to reopen it. Reopening preserves the draft until you submit, skip, or a
later run completes. Moving a reminder into history never answers it, grants
permission, or marks its underlying task complete.
The question dock also stays available when a plugin replaces the composer.
Use the panel's request arrows to switch between pending requests without losing
answer drafts. A new blocking question takes priority; async questions remain
available through the same navigation. Submitting an async answer sends an ordinary
chat message, using the existing outbox and retry controls. Skipping removes that
request from the dock without sending an answer. The transcript retains a summary.
Minimizing alone neither answers nor skips a question.
The question summary shows whether your answer is queued, sending, failed, or
confirmed in saved conversation history. If delivery fails or becomes uncertain
after reconnecting, **Retry answer** retries the existing outbox message instead
of submitting a second answer. **Discard** removes that queued answer and reopens
its preserved draft in the currently open panes for that conversation.
Saved replies remain confirmed after reload, including answers edited in the
outbox or containing quoted question headings. When those headings make individual
answers ambiguous, the summary shows the saved reply text without splitting it.
## Timeout and no answer
The default timeout is 900 seconds. `timeoutSeconds` is clamped to the range
30 through 3600 seconds. This is a maximum human wait, subject to earlier agent
run cancellation or the overall run timeout. A pending question does not extend
an explicit run budget.
If the question expires or is cancelled before an answer arrives, the tool
returns `status: "no_answer"`. The agent then continues with its best judgment.
An aborted agent run cancels its pending Gateway question.
Gateway question records include the optional originating `runId`. Clients can
use it to keep the prompt and its terminal answer summary with the correct agent
turn, including after reconnecting and recovering the question with
`question.list` or `question.get`.
## Tool schema
```ts
{
questions: Array<{
id: string; // unique snake_case answer key
header: string; // short label; truncated to 12 characters
question: string; // one sentence
options: Array<{
label: string;
description?: string;
}>; // 2-4 options
multiSelect?: boolean;
}>; // 1-3 questions
timeoutSeconds?: number; // integer; default 900, clamped to 30-3600
}
```
With `multiSelect: true`, the user can choose more than one option. Answer
values are returned as an array for every question.
Example answered result:
```json
{
"status": "answered",
"answers": {
"answers": {
"deploy_target": ["Staging (Recommended)"]
}
}
}
```
The outer `answers` is the protocol's `QuestionAnswers` envelope; its inner
`answers` map is keyed by `questionId`, with an array of selected values per
question.
## Model guidance
The model-facing contract tells the agent to:
- ask only when blocked on a genuinely user-owned decision;
- ask exactly one question per call unless several answers must be submitted
together, because one-question prompts can use native messaging controls;
- put every selectable choice in `options`, never only in question prose;
- use `multiSelect` only when several choices can be selected at once;
- put the recommended option first and suffix its label with `(Recommended)`;
- omit an authored `Other` option because free text is added automatically;
- continue with best judgment after `no_answer`.
The agent should not use `ask_user` to ask whether it may proceed or to confirm
its own plan.
## Related
- [Secrets](/tools/secrets) — the credential-safe path when the answer is a secret
- [Control UI](/web/control-ui) — where the docked answer panel appears
- [Tools overview](/tools) — the rest of the built-in tool surface