question tool lets an agent stop and ask the user to choose between options before it continues. The agent’s reply pauses at the question, the answer is saved with the chat, and the model resumes in the same assistant message with the answer as the tool result.
Questions work in the admin chat, in @frogbotai/ui, and over HTTP for headless clients. They are opt-in per agent.
Add the tool
Importquestion from frogbot/tools and list it on the agent that may ask.
tools list offers it to every agent. Questions and answers are stored with the chat, so a waiting question survives a reload or a restart.
Question and answer shape
The model sends one or more questions in a single call.
The answer has one entry per question, in the same order.
selected holds option labels exactly as they were offered. FrogBot rejects an answer that picks an option that was not offered, answers the wrong header, selects more than one option on a single-choice question, or includes custom when the question disallows it. Both types are exported as Zod schemas and TypeScript types: QuestionInput and QuestionOutput from frogbot/tools.
Answer in the chat UI
The admin chat renders questions as a card. While a question waits, the card is docked above the composer, which is hidden until the question is answered or dismissed; the answered card then shows in the conversation. A single single-choice question submits on click. Several questions step through one at a time, and questions that allow it show a text field for a custom answer. In your own app, register the card withChatProvider.
addToolOutput, and the chat resubmits once every call in the step has an output. FrogBot validates the answer against the stored call rather than trusting the browser’s copy, and streams the continuation into the same assistant message.
Dismiss declines the question. The model is not called again; the call is recorded as a tool error with the text Dismissed by the user., and the chat is ready for the next message.
Answer over HTTP
A JSON request that reaches a question returnsstatus: "awaiting-input" and the pending calls instead of a final reply.
state and a pending array. Settle a call with its output.
settlement. If other calls in the step are still waiting, the response has status: "awaiting-input" and the remaining pending calls. Send { "toolCallId": "call_1", "dismissed": true } instead of output to dismiss.
A body without
toolCallId and exactly one of output or dismissed: true returns 400 without a code. When the chat UI resubmits answers, the same codes apply, plus pending-calls while other calls in the step still need an answer and turn-in-progress when another request already continued the turn.
The first valid answer wins. Answers from two tabs or two participants settle once and continue once.
Who can answer
Anyone who can continue the chat can answer its questions. Over HTTP, that is the chat’s owner; for a chat without an owner, it is any anonymous caller who knows the chat ID. FrogBot records who answered or dismissed each call, and when, in the message’ssettlements field.
A question in a channel conversation is answered only in its channel. Any participant who passes the agent’s access check can answer, not only the person whose message started the turn. See Slack, Discord, Microsoft Teams, Telegram, GitHub, and Linear.
Channel conversations
A conversation started in a channel, such as a Slack thread, is written only in that channel: its messages, its answers, and the agent’s replies. Its owner can open it in the admin chat to read it, but not to write to it.- A notice replaces the composer: “This conversation happens in Slack. Continue it there, or branch it into a new chat.”
- A waiting question shows “Waiting for an answer in Slack” with its questions, and no controls. Answered and closed questions look the same as in any chat.
- Messages have no Edit action.
- Branch on the notice copies the whole conversation into a new private chat, and Branch on an assistant message copies it up to that message. The branch is an ordinary web chat. Questions still waiting in the channel arrive in it closed, and nothing written in it reaches the channel.
- New channel messages appear when you reload the chat.
409 and code: "channel-chat". See who can write a chat.
Where questions are offered
FrogBot only offers the tool to the model when the caller can answer it.
If a model calls a withheld tool anyway, the call becomes a tool error and the turn keeps running, so nothing is left waiting.
Build a client tool
question is a client tool: it declares an outputSchema and a client configuration instead of execute. Define your own the same way when the answer has to come from the user or the browser.
slug, description, inputSchema, and component tool options, plus:
Render the call with a tool renderer. The renderer receives
addToolOutput and dismiss while the call is waiting.
When a step contains a client tool call, FrogBot does not run server tools emitted in the same step. They return a skipped result so the model can call them again after it has the answer.
See turns and queued messages for how a waiting chat handles new messages.