Skip to content

fix(bridge): handle AskUserQuestion during continuation turns#275

Merged
floodsung merged 1 commit into
mainfrom
fix/continuation-turn-question
May 14, 2026
Merged

fix(bridge): handle AskUserQuestion during continuation turns#275
floodsung merged 1 commit into
mainfrom
fix/continuation-turn-question

Conversation

@floodsung

Copy link
Copy Markdown
Contributor

Summary

  • Follow-up to fix(bridge): surface between-turn AskUserQuestion as its own card #274. Continuation turns (handleContinuationTurn — agent's response to an SDK-injected <task-notification> after a background task settles) didn't recognise state.pendingQuestion from the stream processor. AskUserQuestion fired during a continuation turn rendered as inline text in the main card; the user's typed reply was treated as a fresh user turn that immediately blocked for 6 minutes on the still-hanging hook.
  • Reuse the between-turn question pipeline from fix(bridge): surface between-turn AskUserQuestion as its own card #274: surface a dedicated question card, intercept the reply in handleMessage, route it via executor.resolveQuestion() to unblock the hook so the continuation stream continues normally.

What changed

src/bridge/message-bridge.ts — inside the stream loop in handleContinuationTurn:

  • When state.status === 'waiting_for_input' && state.pendingQuestion:
    • update the main continuation card with a _Waiting for your answer to the question card below…_ hint (drops pendingQuestion, same pattern as runOneTurn at L1573-1586)
    • call handleBetweenTurnQuestion(chatId, { toolUseId, questions }) — reuses the bookkeeping introduced in fix(bridge): surface between-turn AskUserQuestion as its own card #274 (pendingBetweenTurnQuestions), so handleMessage's reply-interception path Just Works
    • track toolUseIds in a per-stream surfacedQuestionIds: Set<string> so the card isn't re-sent on each subsequent delta while the same question is still waiting
  • executor-removed cleanup (added in fix(bridge): surface between-turn AskUserQuestion as its own card #274) already handles the case where the executor is evicted while a continuation question is pending.

Test plan

Automated:

  • npm run build — clean.
  • npx vitest run — 298/298 pass.
  • npm run lint — 0 errors (pre-existing warnings unrelated).

Manual:

  • Trigger a background bash task that settles after a delay, then have the agent invoke AskUserQuestion in its post-settle continuation reply.
  • Confirm a dedicated question card appears below the continuation card.
  • Reply with option number or free text — question card flips to "✅ Reply: …", continuation card resumes (does not freeze on "Thinking…").
  • During the wait, /reset should mark the question card "canceled" and the continuation card "interrupted" (existing fix(bridge): surface between-turn AskUserQuestion as its own card #274 cleanup path).

🤖 Generated with Claude Code

`handleContinuationTurn` renders the agent's response to an SDK-injected
`<task-notification>` (background bash return etc.) as a fresh streaming
card. Before this fix, if the agent invoked AskUserQuestion *inside* that
continuation turn, the stream processor would set
`state.pendingQuestion` but the loop body just kept updating the main
card with the question text inline — no dedicated question card, no
reply routing. The user's typed reply was treated as a fresh user turn
that immediately blocked for 6 minutes on the still-hanging hook.

Fix: reuse the standalone between-turn question pipeline from #274. When
the continuation stream yields `waiting_for_input` + `pendingQuestion`:
  - update the main card with a "_Waiting for your answer to the
    question card below…_" hint (drops `pendingQuestion`, mirrors the
    pattern in runOneTurn)
  - call `handleBetweenTurnQuestion` to surface a dedicated v1 question
    card and register the toolUseId for reply interception in
    handleMessage
  - track surfaced toolUseIds in a per-stream Set so we don't re-send
    the card on every subsequent delta while the same question is still
    waiting

Same `executor.resolveQuestion()` path unblocks the hook; the
continuation stream then continues normally.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@floodsung floodsung merged commit 2acf106 into main May 14, 2026
3 checks passed
@floodsung floodsung deleted the fix/continuation-turn-question branch May 14, 2026 08:32
SimonYeyi pushed a commit to SimonYeyi/metabot that referenced this pull request May 26, 2026
…otics#275)

`handleContinuationTurn` renders the agent's response to an SDK-injected
`<task-notification>` (background bash return etc.) as a fresh streaming
card. Before this fix, if the agent invoked AskUserQuestion *inside* that
continuation turn, the stream processor would set
`state.pendingQuestion` but the loop body just kept updating the main
card with the question text inline — no dedicated question card, no
reply routing. The user's typed reply was treated as a fresh user turn
that immediately blocked for 6 minutes on the still-hanging hook.

Fix: reuse the standalone between-turn question pipeline from xvirobotics#274. When
the continuation stream yields `waiting_for_input` + `pendingQuestion`:
  - update the main card with a "_Waiting for your answer to the
    question card below…_" hint (drops `pendingQuestion`, mirrors the
    pattern in runOneTurn)
  - call `handleBetweenTurnQuestion` to surface a dedicated v1 question
    card and register the toolUseId for reply interception in
    handleMessage
  - track surfaced toolUseIds in a per-stream Set so we don't re-send
    the card on every subsequent delta while the same question is still
    waiting

Same `executor.resolveQuestion()` path unblocks the hook; the
continuation stream then continues normally.

Co-authored-by: Flood Sung <floodsung@xvirobotics.ai>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant