Skip to content

fix(tui): keep pending dialog when a nested sub-agent stream starts#3221

Open
aheritier wants to merge 1 commit into
mainfrom
fix/3217-elicitation-dialog-dropped
Open

fix(tui): keep pending dialog when a nested sub-agent stream starts#3221
aheritier wants to merge 1 commit into
mainfrom
fix/3217-elicitation-dialog-dropped

Conversation

@aheritier

Copy link
Copy Markdown
Contributor

Summary

Fixes #3217.

A user_prompt/elicitation (or tool-confirmation / max-iterations) dialog could be silently dropped when a sub-agent or fork-skill stream started in the same session, leaving the run blocked on user input with no visible dialog.

Root cause

pkg/tui/service/supervisor/supervisor.gohandleRuntimeEvent cleared runner.PendingEvent (and toggled IsRunning/NeedsAttn) on any StreamStarted/StreamStopped event, regardless of which session it belonged to. transfer_task and fork-mode run_skill forward the child session's stream lifecycle events up through the parent's event channel; those nested events carry a different SessionID than the runner key, so they wiped the parent runner's pending attention event. runner.PendingEvent is the only thing that keeps a backgrounded attention dialog alive across a tab switch/re-render, so once cleared, ConsumePendingEvent returned nil, the dialog was never re-displayed, and the runtime stayed blocked in elicitationHandler.

Fix

Add an isTopLevelStream(runnerID, evSessionID string) bool helper (evSessionID == "" || evSessionID == runnerID) and gate both the StreamStartedEvent and StreamStoppedEvent state mutations on it. Nested sub-session streams no longer toggle IsRunning or clear the parent runner's pending event; top-level turns still supersede stale pending events exactly as before. An empty SessionID is treated as top-level for backward compatibility (mirrors the existing handleTokenUsage guard).

This also corrects a latent tab-indicator bug: a nested StreamStopped no longer flips the parent tab's IsRunning to false mid-turn.

Tests

Added to pkg/tui/service/supervisor/supervisor_test.go:

  • TestIsTopLevelStream — table: matching / empty / child / sibling session IDs
  • TestStreamStarted_SubSessionDoesNotDropPendingEvent
  • TestStreamStopped_SubSessionDoesNotDropPendingEvent
  • TestStreamStarted_TopLevelSupersedesStalePending
  • TestStreamStopped_TopLevelClearsPendingAndNeedsAttn — covers all three attention event types (elicitation, tool confirmation, max-iterations)
  • TestStreamStarted_EmptySessionID_TreatedAsTopLevel

Validation

  • task build
  • task test ✅ (only pre-existing, unrelated baseline failures)
  • go test -race ./pkg/tui/service/supervisor/...
  • task lint ✅ (only pre-existing SlogContextual warnings)

@aheritier aheritier requested a review from a team as a code owner June 24, 2026 08:54

@docker-agent docker-agent left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assessment: 🟢 APPROVE

The fix is correct and well-targeted. isTopLevelStream cleanly guards both StreamStartedEvent and StreamStoppedEvent handlers so that nested sub-session stream lifecycle events (carrying a child SessionID) no longer wipe the parent runner's PendingEvent or toggle IsRunning. The empty-SessionID backward-compatibility path matches the existing convention in handleTokenUsage and is clearly documented. The six new tests cover the key cases — sub-session no-ops, top-level superseding stale events, and all three attention event types on StreamStopped.

No bugs were found in the introduced changes.

@aheritier aheritier added area/tui For features/issues/fixes related to the TUI kind/fix PR fixes a bug (maps to fix: commit prefix) labels Jun 24, 2026
dgageot
dgageot previously approved these changes Jun 24, 2026
Comment thread CHANGELOG.md Outdated
A user_prompt/elicitation (or tool-confirmation/max-iterations) dialog was
silently dropped when a sub-agent or fork-skill stream started in the same
session. The supervisor cleared the runner's PendingEvent on any
StreamStarted/Stopped event regardless of session, so a forwarded nested
sub-session stream wiped the parent's pending attention event, leaving the
run blocked on user input with no visible dialog.

Gate the StreamStarted/Stopped state mutations on a new isTopLevelStream
helper so nested sub-session streams no longer toggle IsRunning or clear the
parent runner's pending event, while top-level turns still supersede stale
pending events.

Fixes #3217
@aheritier aheritier force-pushed the fix/3217-elicitation-dialog-dropped branch from ec94df5 to c29bc00 Compare June 24, 2026 11:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/tui For features/issues/fixes related to the TUI kind/fix PR fixes a bug (maps to fix: commit prefix)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

user_prompt/elicitation dialog silently dropped when a background-agent stream is active in the same session

3 participants