agents: replay event log from Run.conversation() on worker crash (cursor only) #956

Closed
opened 2026-05-08 12:06:07 +00:00 by claude-desktop · 3 comments
Collaborator

User story

As an operator whose service occasionally hard-restarts mid-task, I want cursor-backed sessions to recover their event timeline from Run.conversation() rather than being a black hole between restart and the next operator action, so I don't lose visibility into what the agent already did.

Context

@cursor/sdk exposes Run.conversation(): Promise<ConversationTurn[]> — the structured per-turn history of a run. Today after a crash + restart, redispatch-interrupted re-queues the task, but the old event log is gone (in-memory ring buffer + nothing rebuilt). For claude-code there's no equivalent fallback (the SDK doesn't expose a transcript replay endpoint), so this issue scopes to cursor only.

Acceptance criteria

Cursor adapter

  • On Agent.resume(...) — when there is a stored cursor session — call Run.conversation() against the most recent run for that agent and translate each ConversationTurn into the existing TaskEvent types: assistant, user, tool_progress, tool_summary. Yield them at the head of the run's event stream so the operator immediately sees the prior context.
  • Mark the replayed events with a flag (replayed: true) so the event-log + UI can render them differently (faded / dimmed / "from previous run" divider).
  • Never re-execute side effects — replay is read-only; the runner skips PR-URL extraction (already extracted last time) and skips session-id capture for replayed events.

Event log

  • New TaskRecord.events[i].replayed: boolean field. Default false.
  • SSE envelope carries the flag.

Frontend

  • Replayed events render with reduced opacity + a single "Replayed from session X" divider above the run.

Tests

  • Unit test: feed a fake Run.conversation() result; assert correct event-stream prefix.
  • Integration test against a real cursor SDK using a recorded fixture run (no live API calls in CI).
  • Idempotence test: replay called twice does not duplicate events in the record.

Out of scope

  • Claude Code replay — no SDK support.
  • Replay for completed (non-resumed) runs — only fires when an agent is resumed mid-flight.

References

  • Parent: #950
  • Cursor SDK: Run.conversation() in node_modules/.bun/@cursor+sdk@1.0.12/node_modules/@cursor/sdk/dist/cjs/run.d.ts
  • Existing redispatch path: just redispatch-interrupted
## User story As an operator whose service occasionally hard-restarts mid-task, I want cursor-backed sessions to recover their event timeline from `Run.conversation()` rather than being a black hole between restart and the next operator action, so I don't lose visibility into what the agent already did. ## Context `@cursor/sdk` exposes `Run.conversation(): Promise<ConversationTurn[]>` — the structured per-turn history of a run. Today after a crash + restart, `redispatch-interrupted` re-queues the task, but the old event log is gone (in-memory ring buffer + nothing rebuilt). For claude-code there's no equivalent fallback (the SDK doesn't expose a transcript replay endpoint), so this issue scopes to cursor only. ## Acceptance criteria ### Cursor adapter - [ ] On `Agent.resume(...)` — when there is a stored cursor session — call `Run.conversation()` against the most recent run for that agent and translate each `ConversationTurn` into the existing `TaskEvent` types: `assistant`, `user`, `tool_progress`, `tool_summary`. Yield them at the head of the run's event stream so the operator immediately sees the prior context. - [ ] Mark the replayed events with a flag (`replayed: true`) so the event-log + UI can render them differently (faded / dimmed / "from previous run" divider). - [ ] Never re-execute side effects — replay is read-only; the runner skips PR-URL extraction (already extracted last time) and skips session-id capture for replayed events. ### Event log - [ ] New `TaskRecord.events[i].replayed: boolean` field. Default `false`. - [ ] SSE envelope carries the flag. ### Frontend - [ ] Replayed events render with reduced opacity + a single "Replayed from session X" divider above the run. ### Tests - [ ] Unit test: feed a fake `Run.conversation()` result; assert correct event-stream prefix. - [ ] Integration test against a real cursor SDK using a recorded fixture run (no live API calls in CI). - [ ] Idempotence test: replay called twice does not duplicate events in the record. ## Out of scope - Claude Code replay — no SDK support. - Replay for completed (non-resumed) runs — only fires when an agent is resumed mid-flight. ## References - Parent: #950 - Cursor SDK: `Run.conversation()` in `node_modules/.bun/@cursor+sdk@1.0.12/node_modules/@cursor/sdk/dist/cjs/run.d.ts` - Existing redispatch path: `just redispatch-interrupted`
Collaborator

🤖 Auto-assigned to code-lead (heuristic: area:agents → code-lead (architecture-touching)). Reply /unassign to reroute.

🤖 Auto-assigned to **code-lead** (heuristic: area:agents → code-lead (architecture-touching)). Reply `/unassign` to reroute.
Collaborator

🧹 janitor: this ticket has been idle-assigned since 2026-05-08T15:36:14.000Z. Re-dispatching.

🧹 janitor: this ticket has been idle-assigned since 2026-05-08T15:36:14.000Z. Re-dispatching.
Collaborator

🦵 @charles kicked the queue — re-running implement on @code-lead.

🦵 @charles kicked the queue — re-running implement on @code-lead.
Sign in to join this conversation.
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
charles/claude-hooks#956
No description provided.