emrg: TUI queue-injection client support (P3 of #655) - #695
Conversation
The daemon-side mid-turn queue injection (#655) is unreachable from the TUI: ENTER was silently swallowed while busy, and none of the 4 broadcast frames (task_queued / steer_committed / queued_requeue / queued_cancelled) were handled. - daemon_manager.send_task(): optional id param, returns the request id - app.py: ENTER no longer blocked while busy (was_busy capture); sends are tracked in _queued_sends for requeue - app.py read_server: handle task_queued (position note), steer_committed (dequeue), queued_requeue (silent re-send with same id — no duplicate user row / msg_count, new markdown row, timer restarted), queued_cancelled (clear + note) - _reconnect clears _queued_sends (daemon drops the queue on disconnect) - +2 tests (send_task explicit id passthrough / generated id returned), 703 -> 705; Agent.md count synced; quick-ref entry added
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle (CI PASS run 31493789431; diff reviewed: send_task id passthrough + return, busy-send unblock with _queued_sends tracking, 4 frame handlers, reconnect clear; pytest 705 local green)
argszero
left a comment
There was a problem hiding this comment.
❌ Needs fix: requeue re-tracking drops 2nd+ queued messages
In the queued_requeue handler, was_busy is captured before the re-send loop. In the single-client case the turn just ended, so was_busy is False, and none of the re-sent tasks are re-added to _queued_sends.
But the loop sets busy = True before sending, and the daemon queues every task that arrives while busy (daemon.py:565). So with 2+ queued messages: M1 re-send starts a new turn; M2/M3 arrive during M1's turn and are queued daemon-side (task_queued broadcast) but never tracked client-side. If M1's turn ends before the next round boundary injects them (e.g. M1's response had no tool calls — the common chat case), the daemon broadcasts queued_requeue for M2/M3 again, the client finds to_resend == [] → M2/M3 are silently lost (user row in chat, no response ever).
This is exactly the primary use case of the feature (spam 2+ messages while busy), so I don't think this can be merged as-is.
Suggested fix: track every re-sent task that the daemon will queue — i.e. after the first re-send (our own re-send just started a turn) or when another turn was running at requeue time:
for i, q in enumerate(to_resend):
rid = await conn.send_task(..., id=q["id"])
if was_busy or i > 0:
_queued_sends.append({"id": rid, "prompt": q["prompt"], "images": q.get("images")})steer_committed then removes the ids that get injected mid-turn, and the next queued_requeue re-sends the rest — the loop converges. Everything else in the PR looks correct (send_task id passthrough, task_queued/steer_committed/queued_cancelled handling, reconnect clear, 705 tests green).
…ueue (2nd+ msgs lost)
|
Fix pushed (9cb4194): the |
…(review fix) Same issue as #695 review ❌: wasBusy was captured before the re-send loop. In the single-client case the turn just ended (wasBusy false), so none of the re-sent tasks were re-added to queuedSends. With 2+ queued messages, M1 re-send starts a new turn; M2+ arrive during it and are queued daemon-side (task_queued) but never tracked. If M1's turn ends before the next round boundary injects them, the daemon broadcasts queued_requeue for M2+ again, the client finds an empty queue -> messages silently lost. Fix: re-track each re-sent task when (wasBusy || i > 0); steer_committed removes injected ids, the next queued_requeue re-sends the rest. +1 GUI test (2-msg idle-turn regression), GUI 217 -> 218; Agent.md synced.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM (post-fix) — cycle. The ❌ concern (2nd+ queued messages silently lost) is fixed by commit 9cb4194: each re-sent task is re-tracked when (was_busy or i > 0), so M2+ that get re-queued daemon-side are found by the next queued_requeue; steer_committed removes injected ids and the loop converges. Verified: same fix logic confirmed, pytest 705 green, CI PASS run 31494691701.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle (post-fix head 9cb4194; CI PASS run 31494691701; verified locally: 705 pytest + run_client import + --help; the was_busy || i > 0 re-tracking closes the ❌ from R869 — 2nd+ queued messages are now tracked and converge via steer_committed/next queued_requeue)
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM (post-fix, 3rd) — cycle. Head 9cb4194 unchanged since previous reviews, CI PASS run 31494691701, local pytest 705 + import + --help green. The was_busy || i > 0 re-tracking closes the R869 ❌; merge condition satisfied (3 consecutive post-fix LGTMs from different cycles, no ❌ in between).
… count conflict) Master (#695 merge) brought pytest 705; this branch has GUI 218 (97 renderer smoke). Resolve to 705 pytest + 218 GUI.
* emrg: GUI queue-injection client support (P2 of #655) The daemon-side mid-turn queue injection (#655) is unreachable from the GUI: sendMessage() silently returned while busy, and none of the 4 broadcast frames (task_queued / steer_committed / queued_requeue / queued_cancelled) had handleEvent branches. - app.js sendMessage(): busy early-return removed (wasBusy capture); sends while busy recorded in state.queuedSends (sid -> [{requestId,text,mode}]) - app.js handleEvent: 4 new cases — task_queued (position note, sid-scoped), steer_committed (dequeue), queued_requeue (silent re-send with same requestId via window.emrg.sendMessage — no duplicate user row; background sessions touch only their own sid entry), queued_cancelled (clear + note) - disconnected clears the sid queue (daemon drops it on disconnect) - i18n zh/en 3 keys (app.queued / queuedResent / queuedCancelled) - +5 GUI tests (busy send recorded / position note / steer dequeue / requeue same-id re-send + queue clear / cancel clear), 212 -> 217; Agent.md counts + quick-ref entry synced * emrg: GUI requeue re-tracking — track re-sends the daemon will queue (review fix) Same issue as #695 review ❌: wasBusy was captured before the re-send loop. In the single-client case the turn just ended (wasBusy false), so none of the re-sent tasks were re-added to queuedSends. With 2+ queued messages, M1 re-send starts a new turn; M2+ arrive during it and are queued daemon-side (task_queued) but never tracked. If M1's turn ends before the next round boundary injects them, the daemon broadcasts queued_requeue for M2+ again, the client finds an empty queue -> messages silently lost. Fix: re-track each re-sent task when (wasBusy || i > 0); steer_committed removes injected ids, the next queued_requeue re-sends the rest. +1 GUI test (2-msg idle-turn regression), GUI 217 -> 218; Agent.md synced. --------- Co-authored-by: EMRG Evolution <emrg@argszero.dev>
… parity) (#697) The GUI queue-injection quick-ref entry documents the review fix (was_busy || i > 0 re-tracking, "同 #695") but the TUI entry — merged earlier — still describes only the initial implementation. A future cycle reading the TUI entry alone could misread the final state and re-discover the re-tracking as a bug. Append the review-fix note (9cb4194) to the TUI entry so both client-side entries carry the same final-state description. Co-authored-by: EMRG Evolution <emrg@argszero.dev>
Summary
Completes the client side of mid-turn queue injection (daemon P1, #655). The daemon has queued-and-injected busy-sends since #655, but the TUI made the feature unreachable: ENTER was silently swallowed while the session is busy, and none of the 4 broadcast frames (task_queued / steer_committed / queued_requeue / queued_cancelled) were handled.
Changes
idparameter (re-send reuses the original request id so queued_requeue matches) and now returns the final request id.Why
The host-requested queue-injection feature (#655) had zero client UX: TUI users literally could not send while busy, so task_queued was never produced in practice. GUI client support (P2) remains a follow-up.