-
Notifications
You must be signed in to change notification settings - Fork 3
cli_live_input_unification
Status: ✅ core implemented surgically (commit fc5953e9 on pi-dev-integration). See "What was actually needed" below.
Scope: coding-agent CLI providers only — claude-code, codex, cursor, agy, pi. API/LLM agents are untouched.
On inspection the queue machinery was already CLI-correct everywhere except one path, so the change is 12 lines, not a sweeping rewrite:
-
The only real bug:
handleSteerMessagereturned 404 ("No running agent") wheneverrunningAgents[sessionID]was momentarily empty between turns (object torn down/rebuilt while the tmux session is alive). The UI reads that 404 as "live turn ended" → toast + local queue. Fix: when the agent object is absent, callstartNextTurnFromSteer(it already works without an agent object) before falling through to 404. The CLI resumes its session and processes the message natively. -
DeliverUserMessage— already never queues for live-input providers (SendCodingAgentLiveInput);AddSteerMessageis API-only. No change needed. -
canSteerSession— already falls back toSessionHasBusyCodingTmux. No change needed. -
Frontend toast /
queueStreamingMessage— with the 404 race gone, the toast now fires only on genuine transport failures (a realDeliverUserMessageerror or an uninitialized session with no prior query). That's correct behavior — left as a genuine-failure safety net, not ripped out.
So the sections below are the original design exploration; the delivered change is just the liveness-aware startNextTurnFromSteer fallback in handleSteerMessage. Verified: compiles, steer unit tests pass (one pre-existing unrelated red test on the branch).
For CLI agents, the server maintains its own steer-vs-queue concept on top of a thing the CLI already does natively: a running CLI process owns its own input buffer and decides live-vs-queue itself. Our parallel queue is keyed to the Go turn-object lifecycle (runningAgents[sessionID]), which blips empty between turns — and that mismatch is the source of two live bugs:
- The 404 → "The live coding-agent turn has ended" toast: a steer lands in the gap where the Go agent object is momentarily absent, even though the tmux session is alive and ready.
- "Message queued but never goes": once in our queue, flushing depends on fragile turn-boundary logic.
New real-E2E tests (…ProcessesQueuedFollowupContract, in multi-llm-provider-go, branch codex-fix-and-queue-tests) prove every CLI natively queues + processes a mid-turn message delivered straight to its tmux pane (bypassing the server queue):
| Provider | Result |
|---|---|
| claude-code | ✅ processed in-turn |
| cursor | ✅ processed in-turn |
| pi | ✅ processed in-turn |
| codex | ✅ processed as the next turn (fine) |
| agy | ✅ processed in-turn (behaviorally confirmed) |
None reject a mid-turn message. So the server queue is redundant for CLI agents.
deliver(sessionID, msg):
if provider is a CLI coding agent:
if tmux session is alive: send-keys to the pane # the CLI owns live-vs-queue
else: start a new turn/session with msg as the opener
else (API/LLM agent):
keep the existing steer-vs-queue (load-bearing there)
One fork only for CLI agents: is the tmux session alive? No runningAgents-object gate, no 404/409 "no foreground turn", no separate queue, no frontend toast.
Today it 404s on runningAgents[sessionID] being empty and 409s on !hasActiveTurnCancel && !tmuxBusy. Replace the CLI path with a session-liveness gate:
- If the tmux session exists for this
sessionID→ deliver (the existingrunningAgent.DeliverUserMessage(... IntentLiveInput)/SendCodingAgentLiveInput). Deliver regardless ofhasActiveTurnCancel— the pane being alive is sufficient. (canSteerSessionalready half-admits this viaSessionHasBusyCodingTmux; promote that to the primary gate.) - If the tmux session is gone →
startNextTurnFromSteer(...)(already exists, ~6653) — start a fresh turn with the message as opener. -
Remove the
404 "No running agent for this session"and409 "No active foreground turn"returns for CLI providers. They should be unreachable: alive→deliver, gone→new turn. - Keep the existing
DeliverUserMessageerror→409only as a genuine transport failure (tmux paste failed), not as a routine "turn ended".
Note: the source of truth for "session alive" is the tmux session (via the adapter's session registry /
SessionHasBusyCodingTmux/ a newCodingSessionAlive(sessionID)helper), NOTrunningAgents.
Currently: if isCodingAgent && SupportsLiveInput → SendCodingAgentLiveInput; else AddSteerMessage (queue).
- For CLI (
SupportsLiveInput) providers, never fall through toAddSteerMessage— always deliver to the live session (or signal "session gone" so the server starts a new turn). - Keep
AddSteerMessage/QueuedForInjectiononly for non-live (API) providers.
Simplify the CLI case to tmux-session liveness as the primary signal (it already falls back to SessionHasBusyCodingTmux). This is what the frontend can_steer reads, so it must agree with handleSteerMessage — both gate on the same liveness check to kill the race.
- In
sendLiveCodingAgentMessage(~1770): on a CLI session, drop the steer→queue fallback + the toast.isLiveCodingSessionGoneStatus(404/409/410) should no longer occur for CLI sends; if it somehow does, treat it as "start next turn", not "queue locally + toast". - Remove (or repurpose) the "The live coding-agent turn has ended. Sending this as the next turn." toast (line ~1805) for CLI sessions.
-
Optional UX to preserve: the pending-message list (
queuedMessages+handleSteerQueuedMessage) can stay purely as a display/cancel/edit affordance — but it must NOT be a delivery gate. Default behavior is immediate send-keys. - Keep the keyboard-passthrough fixes (space + coalescing, PR #75) — orthogonal.
-
BG-agent / orchestration path — sub-agent completions are delivered into a busy session via the same steer mechanism (
[BG AGENT] Steered completion … into busy session). It must ride the same unified "session alive → send-keys" path. - API/LLM agent steer-vs-queue — still correct (no live channel; queue for next request).
-
The paste→settle→Enter reliability (
sendInputToActiveTmux,waitForPromptPaste, submit backoff) — orthogonal render-race handling; keep it.
-
Codex turn-boundary: codex's
GenerateContentreturns after turn 1, so a follow-up lands as the next turn. Under this model that's correct — the message reaches codex and is processed; it's not lost. -
Session genuinely gone (CLI exited / retention expired): the only real fork —
startNextTurnFromSteeropens a fresh session/turn. - Copy-mode can't eat input: the pane is a detached, server-owned tmux session never attached by an interactive client, so user "scroll" is browser-side only. No copy-mode guard needed.
- Multiple rapid messages: send-keys each; the CLI queues them natively in order.
- The
…ProcessesQueuedFollowupContractE2E tests (5 providers) stay green. - Manual: send a message to a live CLI session between turns (the old 404 window) → it must deliver with no toast, no lost message.
- Confirm a normal send no longer emits the "live coding-agent turn has ended" toast.
- Confirm BG-agent sub-agent completions still steer into busy sessions.
- API-agent chats unchanged (steer-vs-queue still applies).
-
agy turn-completion stability race (declares done mid-
Generating…before the first tool call). Fix lives in agy's stability/timing logic; "spinner = busy" is the wrong layer (breaks stale-status handling). -
codex wrapped-prompt extraction leak — already fixed on branch
codex-fix-and-queue-tests.
Auto-synced from docs/ on main. Edit there, not here.