Skip to content

fix(tui_gateway): queue mid-turn prompts instead of dropping them on a busy retry - #52594

Merged
OutThisLife merged 1 commit into
mainfrom
bb/queue-resubmit-on-busy
Jun 25, 2026
Merged

fix(tui_gateway): queue mid-turn prompts instead of dropping them on a busy retry#52594
OutThisLife merged 1 commit into
mainfrom
bb/queue-resubmit-on-busy

Conversation

@OutThisLife

@OutThisLife OutThisLife commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

Follow-up to #52592. Symptom reported (desktop app): "if I send a msg and then interrupt and send another one, it doesn't just stop and listen with prev context in mind — it'll often just not listen."

The fix is in the shared tui_gateway, so it covers both the desktop app and the TUI.

Root cause

When a turn is in flight, prompt.submit rejected the next message with 4009 "session busy" and left clients to cope. The desktop app turns that into a deadline-bounded busy-retry (withSessionBusyRetry, 6s). But turn teardown can outlive that window: the sequential tool executor only checks the interrupt flag between tools (agent/tool_executor.py), so a slow, non-interruptible tool already in flight (web_search, read_file, an MCP call, a long terminal) keeps running after stop. Teardown > 6s → the retry gives up → the resubmitted message is dropped → "it just doesn't listen".

Fix

Wire the previously-dead display.busy_input_mode config into prompt.submit. Instead of rejecting a mid-turn prompt, queue it to run as the next turn (drained in run()'s tail, ahead of goal/notification follow-ups):

  • interrupt (default): interrupt the live turn so it winds down promptly, then run the queued message.
  • queue: run it after the current turn finishes (no interrupt).
  • steer: inject into the live turn if accepted, else queue.

The queued slot pins the sender's transport (so the drained turn streams to the right client) and losslessly merges a second arrival (mirroring the consecutive-user merge in repair_message_sequence). No client deadline, no dropped sends. Combined with #52592, the resumed turn also keeps full alternation-clean context.

Surfaces / compatibility

  • Desktop: this is the path that dropped messages (the 6s retry deadline). The desktop ignores the prompt.submit response body, and {status:"queued"} makes its busy-retry resolve immediately instead of racing the deadline.
  • TUI: already queues mid-turn input client-side and drains on the real settle edge (busy → false), not a fixed deadline — so it didn't drop messages, it waited. This change does not conflict or double-run: the TUI doesn't call prompt.submit while busy on the normal path (it intercepts and queues locally), so the server queue isn't even hit there. The one place it optimistically sends (useSubmission.ts race fallback) previously relied on the session busy error to re-queue; now the server queues it once and returns success, so that race window gets strictly more robust. Net: the gateway becomes the single source of truth for both clients.

Test plan

  • New tests/test_tui_gateway_queue_on_busy.py (10 tests): enqueue pin/merge; _handle_busy_submit per-mode (interrupt/queue/steer + steer-rejected fallback); _drain_queued_prompt fires/claims-running/restores-transport, no-ops when empty or already-running, and releases running on dispatch failure.
  • tests/test_tui_gateway_server.py + tests/tui_gateway/ green (289 passed; 1 pre-existing failure unrelated — test_browser_manage_connect_default_local_reports_launch_hint asserts no Chromium is installed, fails only on machines that have Chrome).

…a busy retry

A prompt sent while a turn was in flight got rejected with 4009 "session busy",
which pushed clients (the desktop app) into a deadline-bounded busy-retry. When
turn teardown outlived that deadline — e.g. the user hits stop while a slow,
non-interruptible tool (web_search, read_file, an MCP call) is mid-flight, since
the sequential executor only checks the interrupt flag between tools — the
resubmitted message was silently dropped: "it just doesn't listen".

Wire the previously-dead display.busy_input_mode config into prompt.submit:
instead of rejecting, apply the policy and queue the message to run as the next
turn (drained in run()'s tail, ahead of goal/notification follow-ups). Modes:
interrupt (default) interrupts the live turn so it winds down promptly then runs
the queued message; queue runs it after the current turn finishes; steer injects
it into the live turn when accepted, else queues. The queued slot pins the
sender's transport and losslessly merges a second arrival. No client deadline,
no dropped sends.
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: bb/queue-resubmit-on-busy vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 11270 on HEAD, 11268 on base (🆕 +2)

🆕 New issues (2):

Rule Count
unresolved-attribute 2
First entries
tests/run_agent/test_credits_notices_toggle.py:76: [unresolved-attribute] unresolved-attribute: Unresolved attribute `_credits_session_start_micros` on type `AIAgent`
run_agent.py:2989: [unresolved-attribute] unresolved-attribute: Object of type `Self@get_credits_spent_micros` has no attribute `_credits_session_start_micros`

✅ Fixed issues (1):

Rule Count
invalid-assignment 1
First entries
tests/run_agent/test_credits_notices_toggle.py:76: [invalid-assignment] invalid-assignment: Object of type `None` is not assignable to attribute `_credits_session_start_micros` of type `int`

Unchanged: 5941 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@alt-glitch alt-glitch added type/bug Something isn't working comp/tui Terminal UI (ui-tui/ + tui_gateway/) P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Jun 25, 2026
@OutThisLife
OutThisLife merged commit a53fc78 into main Jun 25, 2026
27 checks passed
@OutThisLife
OutThisLife deleted the bb/queue-resubmit-on-busy branch June 25, 2026 17:50
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
…bmit-on-busy

fix(tui_gateway): queue mid-turn prompts instead of dropping them on a busy retry
habarmc1223-sudo pushed a commit to habarmc1223-sudo/hermes-agent-fluxmem that referenced this pull request Jul 8, 2026
…bmit-on-busy

fix(tui_gateway): queue mid-turn prompts instead of dropping them on a busy retry
santhreal pushed a commit to santhreal/hermes-agent that referenced this pull request Jul 13, 2026
…bmit-on-busy

fix(tui_gateway): queue mid-turn prompts instead of dropping them on a busy retry
Gravezzz pushed a commit to Gravezzz/hermes-agent that referenced this pull request Jul 21, 2026
…bmit-on-busy

fix(tui_gateway): queue mid-turn prompts instead of dropping them on a busy retry
leewenjie pushed a commit to leewenjie/hermes-agent that referenced this pull request Aug 7, 2026
…bmit-on-busy

fix(tui_gateway): queue mid-turn prompts instead of dropping them on a busy retry
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/tui Terminal UI (ui-tui/ + tui_gateway/) P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants