feat(chat): ask_question tool — blocking clickable question card - #464
Conversation
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsFINDING -- src/kiro_crew/dashboard/server.py:690 -- function-local False positive or not applicable? A repository writer can comment: |
Design Review (Fable 5) — ✅ PASSAdvisory design-level review of Design-Verdict: PASS Sound design: mirrors the proven Suggestions
[DESIGN-REVIEWED] 878d638 |
3789565 to
b40e75f
Compare
Round 1 disposition — head
|
Opus 5 Review — ✅ human override acceptedReviewed Human judgment by @kyleseaman overrides the Opus 5 finding for Verdict recorded from an authorized human decision for commit False positive or not applicable? A repository writer can comment: |
Arbiter — ✅ no blocking findingsArbiter found no unresolved long-term items that require action before merging Second-order review for Review detailsI've read the full sub-threshold findings file and the PR diff. The findings inventory for this commit is small: Opus 5's finding was overridden by an authorized human decision (nothing remains to arbitrate there), GPT 5.6 raised a single style-level finding (a function-local import in Arbiter-Verdict: PASS No sub-threshold finding meets the long-term-impact bar. Suggested follow-ups (open as issues — non-blocking)
[ARBITER-REVIEWED] 878d638 False positive or not applicable? A repository writer can comment: For a broader accepted-risk deferral, apply |
b40e75f to
45782a8
Compare
Round 2 disposition — head
|
45782a8 to
3905496
Compare
Round 3 disposition — head
|
3905496 to
8c0a149
Compare
Round 3 disposition — head
|
8c0a149 to
90e6869
Compare
90e6869 to
aeb79c0
Compare
Round 4 — split the ACP change out (head
|
aeb79c0 to
8817dce
Compare
Babysit round — head
|
8817dce to
32dcc41
Compare
Round 5 — the eight remaining GPT findings (head
|
32dcc41 to
1b8d260
Compare
Round 6 — the HIGH plus the four sharpest MEDIUMs (head
|
1b8d260 to
b2317a1
Compare
215af12 to
6274268
Compare
6274268 to
1a5d300
Compare
|
Prior reviewed SHA: 1a5d300
|
1a5d300 to
f61e50d
Compare
|
Prior reviewed SHA: f61e50d
|
|
Prior reviewed SHA: 48b7525
|
|
Prior reviewed SHA: baa0c29
|
Lets an agent pause mid-turn, ask the dashboard user a multiple-choice
question, and receive the answer as the tool's result — no extra turn and
no [OPTIONS:] text parsing.
The QuestionCard component and the question_card websocket event already
existed but were unreachable: they keyed off an ACP tool_call titled
AskUserQuestion, and that tool does not exist in kiro-cli 2.14.0 (the
string appears nowhere in the binary). This supplies the missing trigger
from KiroCrew's own MCP server instead of waiting on the agent CLI.
The round-trip mirrors the tool-approval machinery in
DashboardState.request_approval: an asyncio future, a websocket broadcast,
and an HTTP resolve. Differences are that the resolution value is the
user's answer map rather than an allow/deny boolean, and the card is
addressed to a single slot.
- ask_question MCP tool (dashboard-only, strict session resolution so a
subagent cannot post a card into its parent's chat)
- POST /api/ask-question (blocks) + POST /api/ask-question/{id}/answer
- request_question / resolve_question / cancel_questions_for_slot
- ask_id correlation so a stale resolution cannot clear a newer card
- question_card_resolved retracts timed-out cards from the UI
- default 300s wait, 1800s ceiling (matches the `wait` tool); the socket
timeout is deliberately longer than the server window
- redaction on question text, str-coercion on answers
Also declares ACP clientCapabilities on both transports, including
elicitation. kiro-cli 2.14.0 compiles the elicitation/create schema and
gates it on this capability but returns -32601 for it, so this is a
forward-bet that costs nothing today.
Verified end-to-end against an isolated dev gateway: websocket broadcast,
caller blocked until answered, click-and-submit and typed custom answers
both resolving the blocked call, multi-select preserved. 29 new tests.
|
Prior reviewed SHA: d5bb79f
|
|
/ai-review override fable 878d638: Opus timed out three times at the 30-minute infrastructure ceiling without producing a finding; all other automated gates and two independent current-head reviews passed. |
Human judgment recorded@kyleseaman marked the fable AI finding as false positive, not applicable, or explicitly accepted for
This decision applies only to this commit. A new push requires a new judgment. |
Summary
Lets an agent pause mid-turn, ask you a multiple-choice question, and receive the answer as the tool's result — no extra turn, no
[OPTIONS:]text parsing. You click an option or type a custom answer in the card.QuestionCardand thequestion_cardwebsocket event already existed but were unreachable: they keyed off an ACPtool_calltitledAskUserQuestion, and that tool does not exist in kiro-cli 2.14.0 (the string appears nowhere in the 662MB binary). This PR supplies the missing trigger from KiroCrew's own MCP server rather than waiting on the agent CLI.Why not ACP elicitation
kiro-cli 2.14.0 does compile the ACP
elicitation/createschema (form/url modes,requestedSchemawithenum/oneOfsingle-select and array multi-select) and gates it onclientCapabilities.elicitation. Its schema maps almost exactly ontoQuestionCard's data model, so it would be the ideal wire.It isn't usable yet. I built a stub MCP server that issues
elicitation/create, registered it viasession/new, and declared the capability — the agent returned:Method not found: the MCP→ACP forwarding path is unimplemented. So
ask_questionsupplies the capability in-process instead.Advertising
clientCapabilities.elicitationso the native prompt lights up when upstream ships the bridge was originally bundled here; it is split out into #512 (no functional coupling to this PR).Flow
Mirrors
DashboardState.request_approval. The differences: the resolution value is the user's answer map rather than an allow/deny boolean, and the card is addressed to one slot rather than the whole gateway.Design decisions
/procancestor walk) so a subagent under a parent slot's process tree can't post a card into the parent's conversation. Other surfaces get a refusal pointing at[OPTIONS:].waittool, since this holds an MCP call open rather than using the 2h human approval window. The HTTP socket timeout is deliberatelytimeout_secs + 30so the socket can't trip first and strand a question you're still answering.question_card_resolvedcarries theask_idand fires infinally, so timed-out cards are always retracted instead of staying clickable and 404-ing. The reducer matches onask_idso a late resolution can't wipe a newer card.str— they're echoed into the transcript, so nested objects can't smuggle structure in.Running: @<server>/<tool>, so the== "AskUserQuestion"sniff never matches; cards from that path carry noask_idand keep their send-as-message behavior.Verification
End-to-end against an isolated dev gateway (own
KIROCREW_HOME, port 6799; live gateway never touched —kirocrew podis unavailable on this host, no D-Bus). 8/8:Browser round-trip (Playwright on the real built SPA): clicking options then Submit resolved the blocked backend call with
{"…trust model…": "Same-URL carve-out", "…environments…": "staging, prod"}— multi-select preserved. A typed custom answer likewise reached the blocked caller.Gates: pytest 17,172 passed (29 new), vitest 4,503, tsc, isort, flake8 clean. mypy clean apart from a pre-existing faiss overload false-positive in
vector_memory.py(reproduces on main).Known pre-existing failures on this host, all reproducing on clean main:
test_dashboard_origin::TestParseDashboardUrlMalformed(×3) andtest_skills::test_flat_copy_untouched_when_nested_missing. Under-n autoone additional test flakes per run with a rotating identity (a gc/unraisable-warning attribution issue —test_dashboard_approvalon one run,test_apps_registryon the next); each passes in isolation and the whole file passes 6/6 deterministically on both branch and base.Not covered
[OPTIONS:]).Spec:
src/kiro_crew/docs/agent-questions.md