feat(tools): add AskUserQuestion clarifying-question form for plan mode#661
Merged
Conversation
Add a plan-mode-only AskUserQuestion tool that presents the user with an interactive form of 1-4 multiple-choice questions (each 2-4 options, optional multi-select, plus an auto-appended "Other" free-text choice). The agent collects the answers and folds them into the plan before RequestPlanApproval. The tool is read-only (no approval gate) and brokers the form like tool approval: Execute publishes a UserQuestionRequestedEvent carrying a buffered response channel and blocks until the user submits or cancels, then returns the answers so the agent loop continues. The broker is injected into the tool context only on the chat path (GetChatHandler is set), so headless/no-TTY runs see a nil broker and degrade gracefully instead of hanging. The form renders as a floating box over the chat (QuestionFormView) with its keys intercepted in the chat view while the form state is set, mirroring the tool-approval box; the view stays ViewStateChat. Part of #652. Follow-ups (Telegram IPC, web terminal renderer, option preview mockups) remain separate.
…chat The interactive form rendered and collected answers, but reading the UserQuestionRequestedEvent consumed the one-shot event listener and the handler never re-armed it. So after the user submitted, the tool's completion and the next LLM turn's events were published but never read - the agent finished in the background while the UI stayed frozen on the form. HandleUserQuestionRequestedEvent now re-arms the listener: the direct-exec tool channel when the request came from a !! invocation, otherwise the chat-session channel for the agent (LLM) path. A single re-arm per event, so no double listener. Adds an integration test that builds a real ChatApplication, drives the event through Update, and asserts the form renders, survives progress ticks, and that Enter delivers the answers and clears the form. Also asserts the answers reach the LLM-facing tool result (FormatResult), i.e. the conversation context.
Lets the interactive form be exercised without an LLM turn, which is handy for
testing and as a power-user escape hatch.
- The !! parser accepts a raw JSON object, e.g. !!AskUserQuestion({"questions":
[...]}). The flat key="value" parser cannot express the nested questions array.
- The direct-exec path injects a UserQuestionBroker (publishing on its own event
channel and blocking on the response) so the tool renders the form instead of
taking the headless "no interactive user" branch.
Single-select questions now behave like a radio group: a default option is pre-selected (the one whose label is marked "(Recommended)", else the first), and the selection follows the cursor, so Enter always confirms a visible choice rather than silently picking the highlight. Multi-select is unchanged - it stays explicit and still requires at least one selection (or Other text). Navigating to the Other row clears the option selection so the free text becomes the answer. Also expands the AskUserQuestion result in the conversation by default - the Q&A is the point of the tool, so it shouldn't hide behind a collapsed one-liner. (Also drops a couple of dangling count assignments in the MCP status handler left over from removing its debug logs.)
The !! tool autocomplete kept its own hardcoded plan-mode tool list, which had drifted: AskUserQuestion (and any plan-only tool) showed in standard/auto mode and would have been missing in plan mode. The LLM was never affected - it uses ListToolsForMode - this was only the suggestion list. loadTools now sources suggestions from ListToolsForMode(currentMode), the exact gating the agent uses for the model, so the two can't drift again. The autocomplete already reloads on mode change (lastAgentMode).
3 tasks
!! ran any enabled tool regardless of mode, so e.g. !!AskUserQuestion worked in standard mode and !!Bash worked in (read-only) plan mode. HandleToolCommand now refuses tools that aren't exposed in the current mode, using the same ListToolsForMode gating the agent applies for the LLM and the autocomplete - so discovery and execution agree.
10 tasks
inference-gateway-releaser Bot
added a commit
that referenced
this pull request
Jun 27, 2026
## [0.124.0](v0.123.1...v0.124.0) (2026-06-27) ### 🚀 Features * add /env shortcut for creating .env.example with provider API keys ([#655](#655)) ([3227e15](3227e15)), closes [#519](#519) * **tools:** add Agent tool for local subagents ([#658](#658)) ([1712f6e](1712f6e)), closes [#653](#653) [#653](#653) * **tools:** add AskUserQuestion clarifying-question form for plan mode ([#661](#661)) ([998bf37](998bf37)), closes [#652](#652) * add sleep to default bash allow-list ([#660](#660)) ([2d945c3](2d945c3)), closes [#659](#659) ### 🧹 Maintenance * **deps:** bump infer-action v0.17.2 -> v0.19.0 ([#654](#654)) ([c66e6d2](c66e6d2)) * **flox:** update codex and infer versions ([4268714](4268714))
Contributor
|
🎉 This PR is included in version 0.124.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
AskUserQuestion: a plan-mode-only, read-only tool that lets the agent ask the user 1–4 multiple-choice clarifying questions as a keyboard-driven TUI form, instead of guessing or asking in prose. Answers are returned as the tool result and the agent loop continues (folds them into the plan, thenRequestPlanApproval).Part of #652 (v1; the follow-ups below remain tracked there).
What's included
internal/agent/tools/ask_user_question.go): draft-07 schema — 1–4 questions, each with a ≤12-charheader,question, 2–4optionsof{label, description}, optionalmultiSelect; bounds enforced inValidate. Headless/no-TTY runs degrade gracefully (returns "no interactive user — proceed with assumptions" instead of hanging).Executepublishes a request event carrying a buffered response channel and blocks; the broker is injected via context only on the chat path (GetChatHandler), so the loop resumes once answered. The chat-events listener is re-armed across the form so the tool result + next LLM turn reach the UI.question_form_view.go): floating box over the chat —↑/↓navigate,spacetoggles (multi-select),enteradvances/submits, an auto-appended "Other" free-text row,esc/ctrl+ccancel. Single-select is a radio: a default is pre-selected (the(Recommended)-marked option, else the first) and the selection follows the cursor, soenteralways confirms a visible choice. The collected answers render expanded in the conversation.ListToolsForMode), excluded from standard/auto. The!!tool autocomplete uses the same gating, so plan-only tools no longer leak into other modes.!!direct execution (testing / power-user escape hatch):!!AskUserQuestion({...})renders the form without an LLM turn (raw-JSON arg parser + direct-exec broker).Tests
Unit tests (schema/validation bounds, answer formatting, headless degrade, mode gating, state lifecycle, default/recommended selection, form rendering, autocomplete mode gating) plus a full-
ChatApplicationintegration test that drives the request throughUpdate, survives the agent's progress ticks, and assertsenterdelivers the answers, clears the form, and the answers reach the LLM-facing tool result.task build/test/lintall green.Deferred (separate PRs)
QuestionRequest/QuestionResponse+QuestionChannel).previewmockups; exposing the tool outside plan mode.Docs
Per the feat→docs rule, a
[DOCS]ticket forinference-gateway/docsis drafted (not yet filed): document the plan-modeAskUserQuestiontool — question/option schema, keyboard controls, plan-mode-only + headless behavior.