Repository navigation
[EPIC-W][LOU-W9] Sessions checkpoint every step and resume a crashed turn - #89
Merged
Merged
Conversation
agent.session({ id, store, checkpointStore }) (or a { sessions, checkpoints }
store such as SqliteStore) runs each turn with sessionId '<id>.turn-<n>' and
that CheckpointStore, so the turn is checkpointed after every model response
and tool result. session.resume() finishes an interrupted turn through the
executor's resume path (input: []) without re-running recorded steps, adds it
to the transcript as send() would and returns its result (null when nothing
is pending); pending() reports an unfinished turn and discardPending() drops
it. send()/stream() resume a pending turn first. A turn paused for approval
stays in its checkpoint until it finishes; resume()/send() throw
SessionAwaitingApprovalError and agent.approvals.resolve() continues the turn
in the session, passing the session's checkpointStore to
resumeAfterApproval().
Docs: "Durable sessions" in docs/sessions.md, cross-link from
docs/durable-execution.md, shorter README SQLite example, CHANGELOG, llms.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bY29ibQgv3MDdjAbUT4YR
…point # Conflicts: # CHANGELOG.md # src/createAgent.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
agent.session({ id, store, checkpointStore? }): with acheckpointStore, or astorethat carries both stores ({ sessions, checkpoints }, whichSqliteStorematches), every turn runs withsessionId: '<id>.turn-<n>'(n= transcript length when the turn started) and that store, so it is checkpointed after each model response and tool result.stream()does the same.session.resume()finishes an interrupted turn through the executor's existing resume path (input: []on the samesessionId). Recorded tool calls and model responses are not run again. The finished turn is added to the transcript exactly assend()adds it, and the turn's checkpoint is deleted. It returns the result, ornullwhen nothing is pending.session.pending()returns{ status: 'in-progress' | 'awaiting-approval', approvalId? }ornull.session.discardPending()drops an unfinished turn.send()/stream()while a turn is pending: the pending turn is resumed first, then the new message is sent.resume()/send()throwSessionAwaitingApprovalErrorand bind the approval to the session.agent.approvals.resolve()then continues the turn in that session and passes the session'scheckpointStoretoresumeAfterApproval().resume()/send()/pending()adds that turn to the transcript instead of running it again. An aborted turn deletes its checkpoint, so abort semantics are unchanged.Why
Epic W, AUDIT §7 "Durability / resume" row and differentiator 1 (host-agnostic durable sessions). Sessions saved the transcript only at the end of a turn, and durable execution was not connected to them, so a crash in the middle of a turn inside a session lost that turn.
Acceptance criteria
agent.session({ id, store, checkpointStore? }), plus{ sessions, checkpoints }-shapedstore(SqliteStore), runs every turn with a derived per-turnsessionId+checkpointStoresession.resume()continues an in-progress turn viainput: []without re-running finished tools, appends the completed turn likesend()and returns its result. It throwsSessionAwaitingApprovalErrorwhen the turn is awaiting approval (resolvable withagent.approvals.resolve()) and returnsnullwhen nothing is pending. Asession.pending()helper is added.send()/stream()while a turn is pending resumes it first, then runs the new input (documented)PropagatingToolErroron the second step and a failing checkpoint write, each resumed in a freshcreateAgent+ session;model.calls.lengthshows finished steps are not re-called; the session store holds the full turn once; an approval pause resolves throughapprovals.resolve()after a restart; thestream()path checkpoints toosession()wiring and theresumelambda in src/createAgent.ts, src/createAgentApprovals.ts, docs. The executor, resume.ts, subagents, security, utils and package.json are untouched. Non-test source diff: +202/-30.How verified
npx tsc --noEmit: no errorsnpm run lint: 0 errors, 420 warnings. The changed files have 0 warnings, so the total is unchanged from main.npm run test:types: no type errorsnpx vitest run: 147 test files passed; 2104 tests passed, 3 skippednpm run build && npm run build --workspace=packages/create-loushy-agent: both succeednpm run docs:verify-snippets -- --skip-build: all 103 snippets type-check; 6 also run cleanlynpm run docs:llms:check: up to datenpm run test:coverage: passes (147 files, 2104 tests).npm run fallow: "No issues found". The one duplicate it lists (AnthropicProvider / OpenAIProvider) was already on main.Left for W9.2: fingerprint-on-resume (detecting that the agent config changed between the crash and the resume).
🤖 Generated with Claude Code
https://claude.ai/code/session_016bY29ibQgv3MDdjAbUT4YR
Generated by Claude Code