Ask user middleware for interactive question-answering during agent execution.
Placeholder recorded for every question when the user cancels the prompt.
A Question with its cross-field rules applied during pydantic parsing.
Only the cross-field choices rules are scoped to this alias. The field-level
rules on Question.question and on Question.choices are unconditional, so
they also apply wherever Question itself is pydantic-parsed ā notably
TypeAdapter(AskUserRequest) in tui.textual_adapter, which re-validates the
interrupt payload client-side and re-raises on failure. Keep that in mind
before adding another field-level rule here.
Where a ValueError from one of these validators ends up depends on the path.
On the tool path it becomes a tool-call ValidationError, which ToolNode
turns into an error ToolMessage the model can correct and retry from. On the
client re-validation path there is no tool call and textual_adapter logs and
re-raises instead.
Render the placeholder answer recorded for every question on failure.
Render questions and answers as the Q:/A: transcript.
This is the text the ask_user tool returns to the model and persists in the
thread. The TUI renders that authoritative text literally, except for the
display-only re-render in render_ask_user_transcript_for_display, which
anchors on the known question text and gives up rather than guess.
Answers of every type are interpolated unescaped, so the encoding is not
unambiguously decodable: an answer containing a blank line followed by a
literal Q: <text>\nA: header is indistinguishable from a real block
boundary. Only the model reads it that way today. Any future decoder must
anchor on the known question text rather than on a generic Q: pattern,
or a crafted answer can fabricate an extra question/answer pair.
For an answer encode_multi_select_answer produced, the JSON encoding does
close the hazard above: no encoded value can carry a raw line feed, so it
cannot fabricate a block boundary. This function cannot rely on that,
because it does not validate answers and not every answer is encoded ā the
cancel and error paths in _parse_answers substitute (cancelled) and
(error: ...) placeholders for every question whatever its type, and a
non-TUI client resuming the interrupt can put arbitrary text in a
multi_select slot.
Trusted same-turn authorization recorded after an ask_user response.
Request payload sent via interrupt when asking the user questions.
A question to ask the user.
Middleware that provides an ask_user tool for interactive questioning.
This middleware adds an ask_user tool that allows agents to ask the user
questions during execution. Questions can be free-form text, multiple choice
(pick exactly one), or multi-select (pick one or more).
The tool uses LangGraph interrupts to pause execution and wait for user input.