Repository navigation
Description
Summary
When a user sends a prompt to an ACP agent that does not support mid-turn steering, Codeg's "Redirected the active turn with your correction" flow fabricates an empty turn_complete ~1ms later, moves the conversation to cancelled or pending_review (the status is not deterministic), and leaves the row unable to send. The prompt is either lost entirely, or — worse — it does land in the agent's store and gets answered, but the reply never streams back because the turn was already closed client-side.
Observed with: codeg-server v0.28.1 (native binary), agent = Hermes 0.20.5 via hermes acp, which advertises steering: advertised=false. Four incidents on our fleet in 48 hours (JAR-1103, JAR-1133, JAR-990, and another today), all on the same production server.
Wire evidence (server log, connection d7f4cd4d-…, conversation "Tackle App Launch review batch")
Real turns take seconds to minutes:
2026-08-28T00:01:41.494228Z status_changed session=...0278c6f6... Connected -> Prompting
2026-08-28T00:05:18.239865Z turn_complete session=...0278c6f6... stop_reason=end_turn # 3m37s — real
Two sends while the agent had a live turn that was invisible to Codeg's session state (an out-of-band background-process notification had just been injected into the session at 00:30:00Z):
2026-08-28T00:31:26.905952Z status_changed session=...0278c6f6... Connected -> Prompting
2026-08-28T00:31:26.907866Z turn_complete session=...0278c6f6... stop_reason=end_turn # 1.9 ms — synthetic, empty
2026-08-28T00:33:29.680022Z status_changed session=...0278c6f6... Connected -> Prompting
2026-08-28T00:33:29.680733Z turn_complete session=...0278c6f6... stop_reason=end_turn # 0.7 ms — synthetic, empty
After that: the conversation row stayed pending_review (an earlier incident flipped to cancelled instead — non-deterministic), every further send failed with no surfaced error, and the agent's real turn_complete never arrived (row still stuck 10+ minutes later; auto-reconcile is not guaranteed).
The insidious part — the first send (00:31:26) was lost for good (never reached the agent store), but the second (00:33:29.680Z, same instant as the synthetic completion) landed and was answered: the agent persisted its reply at 00:34:10 (finish_reason=stop), and it never streamed to the tab because the turn had already been closed client-side.
Diagnostic signature for anyone reproducing: grep the server log for the session uuid and look for Prompting → turn_complete pairs with sub-2ms deltas (an earlier incident had three swallowed sends within 8s).
Trigger hypothesis
The redirect path appears to be attempted unconditionally when a send races an agent turn, even when the agent advertised steering: advertised=false. Two candidate triggers, both observed today:
- Invisible live turn — a background-process notification was injected out-of-band into the session (user-role message, 00:30:00Z), so the agent was mid-turn while Codeg's session state said idle. The send became a "redirect" that could not be delivered.
- Stale session identity after compression rotation — Hermes rotated its session id mid-conversation (compression, 11:48Z); Codeg's row still maps the old id. Lineage resolution makes load_session work, but the conversational redirect/status logic may key on stale state.
Either way the agent cannot accept the correction, so an empty completion is synthesized and the row's status is flipped — which is the destructive part.
Suggested fixes (any one of these materially helps)
- When
steering: advertised=false, never attempt the redirect flow for mid-turn sends. Queue the prompt for the next turn, or surface a clear "agent is busy" state, instead of fabricating a completion. - Don't flip a regular conversation's status (
cancelled/pending_review) from an empty synthetic completion — keep the row sendable and the client-stream in sync. - If the prompt was passed to the agent anyway (as observed), match its eventual real
turn_completeto the conversation instead of discarding it (same direction as Complete grok turns when the session/prompt response is lost #553 for grok). #539(journal sent prompts) would at least make recovery lossless.
Environment
- codeg-server v0.28.1 native (
--supervise), agent Hermes 0.20.5 viahermes acp(steering: advertised=false,native=false) - Related: 任务会话把「截断的 tool call」写入历史后,每次点「继续」都被上游 400 拒绝并静默回到待审阅 #538 (same silent
pending_reviewend-state, different trigger), fix(acp): journal sent prompts so an interrupted chat cannot reopen blank #539, Complete grok turns when the session/prompt response is lost #553, [Bug] 旧 generation 的 cancelled 事件会清空新 generation 的待处理请求 #564/fix(tasks): preserve current waits after stale generation completion #567 (fixed)
Activity
Frank-zhu2000 commentedon Oct 14, 2026
Hi, I'd like to take a look at this one if nobody else is on it.
The part I'd focus on is the redirect path for agents that advertise
steering: advertised=false: a send that races a live turn should not synthesize an emptyturn_completeor flip the row tocancelled/pending_review.Proposed approach (suggested fixes 1 + 2 from the report):
I'd leave late-result reconciliation (#3) and prompt journaling (#539) out of scope. Does that match how you'd like it handled? If so, please assign it to me.
feat: support AIR sessionIndex extension (spacering-net#590) v2 (spac…
chore(main): release 2.3.0 (spacering-net#619)