retry a turn the agent restart cut off
the cut-off notice is its own event now rather than an error: nothing is broken and nothing is lost but the turn, so the row says what happened and offers the one action that fixes it. the conversation is already durable — the claude session id is written through to disk and passed back as resume: — so retry just resends the prompt on a session the fresh agent picks up with full context. read back out of the transcript, so a second window on the same session can offer it too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -550,15 +550,37 @@ Two paths, because the tab can be in two states:
|
||||
from its own memory, which died with the process, so it asks the agent over a new `claude:is-generating`
|
||||
command. The agent is the only party that knows.
|
||||
|
||||
Either way you get a line saying the agent restarted and the turn was cut off, and it's written to
|
||||
`chat_session_events` — so a reload afterwards shows the same explanation rather than a conversation that
|
||||
trails off mid-tool-call.
|
||||
Either way you get a seam across the transcript — **AGENT RESTARTED — TURN CUT OFF** — with a **Retry**
|
||||
button beside it that sends the same prompt again.
|
||||
|
||||
The retry is the whole reason this is worth having, and it works because most of what looked like the
|
||||
hard problem is already solved. `sessionKey → claude session_id` is written through to
|
||||
`data/<email>/sidecar/claude-state.json` on every change — not just at shutdown, so it survives a
|
||||
`SIGKILL` — and `createSession` passes it back as `resume:`. **A restarted agent costs you the turn, not
|
||||
the conversation:** the next prompt picks the thread up from the transcript on disk with full context.
|
||||
Retry just spares you scrolling up to copy what you'd said.
|
||||
|
||||
It's deliberately a seam and not a red error bubble. Nothing is broken and nothing is lost but the turn,
|
||||
so the row's job is to say what happened and offer the one action that fixes it. The prompt is read back
|
||||
out of the transcript rather than remembered separately, because this can land in a second window on the
|
||||
same session — one that never sent it.
|
||||
|
||||
Making the turn _itself_ survive is the part that stays unsolved, and deliberately so. The Agent SDK
|
||||
spawns `claude` as a child with piped stdio; re-adopting it after the sidecar dies would mean the CLI
|
||||
becoming a detached grandchild talking over a socket, i.e. not using the SDK's process management at all.
|
||||
That is a large, risky rewrite that buys exactly one turn — and Retry buys most of it for thirty lines.
|
||||
|
||||
The liveness check **fails toward alive**: a timeout, or no answer, is read as "still running". Telling
|
||||
you a turn died while it is quietly typing would be a worse lie than a spinner that stays up a bit
|
||||
longer. Only a registered agent answering "no", or no agent at all, counts as dead. OpenCode sessions are
|
||||
left alone — that harness runs a turn per invocation and has no equivalent question.
|
||||
|
||||
The notice is also appended to `chat_session_events`, but **don't count on it surviving a refresh**. That
|
||||
table is keyed by officer's own session id, while a session reopened from history is addressed by
|
||||
Claude's transcript uuid; the two converge once a conversation has been resumed at least once, and don't
|
||||
before that. The live case is the one that matters here and it is unaffected. Untangling those two ids is
|
||||
a separate job.
|
||||
|
||||
**Not verified:** the browser, and the restart itself. The mechanism is reasoned from the code plus the
|
||||
`pm2` logs that pinned the cause; typecheck and the full 365-test suite are clean.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user