How checkpointing works
TheCheckpointer class persists AgentCheckpoint objects into any StateStore (in-memory for tests, SQLite for production). The agent runtime writes a checkpoint:
- After each completed iteration β capturing the current message history and accumulated usage.
- When the run suspends for HITL approval β storing the pending payload and the results of all tools that already ran.
resumeAgent loads the checkpoint, restores the message history and counters, and re-enters the loop at the point it left off. Tool calls recorded in the approvals map are replayed from their stored results rather than re-executed.
Checkpointer API
StateStore
required
The key-value store where checkpoints are persisted. Use
SqliteStateStore in production and MemoryStateStore in tests.string
required
A stable identifier for this run. Use a value you can look up later β a request ID, a database primary key, or a UUID generated at request time.
Methods
Promise<void>
Persist the current run state. Called automatically by the Anvil runtime when you pass
checkpoint to runAgent or streamAgent; call it manually only if you are driving the loop yourself.Promise<AgentCheckpoint | undefined>
Retrieve the most recently saved checkpoint. Returns
undefined if no checkpoint exists for this runId.Promise<void>
Remove the checkpoint after a run completes successfully. Call this in your cleanup logic to avoid unbounded growth of the checkpoint store.
AgentCheckpoint fields
'running' | 'suspended' | 'done'
Current lifecycle state.
'suspended' means the run is waiting for human approval; 'done' means it completed (successfully or by hitting the iteration cap).Record<string, unknown>
Tool call results already persisted. When the run resumes, any tool call whose
callId appears in this map is not re-executed β its stored output is used directly. This prevents side-effect tools from running twice.{ callId: string; payload: unknown }
Set when
status is 'suspended'. Contains the call ID and the payload the tool passed to ApprovalRequiredError, which the approver sees in the HITL queue.Marking tools as side effects
DeclaresideEffect: true on any tool that mutates external state β charges a card, sends an email, creates a database row. Anvil checkpoints after the tool completes and fences its result so a resume can never re-execute it:
Running an agent with a checkpoint
Passcheckpoint to runAgent or streamAgent. Anvil instantiates a Checkpointer internally and writes checkpoints throughout the run:
Resuming after a crash
If the process was interrupted, callresumeAgent with the same runId. It loads the checkpoint, restores message history and usage counters, and re-enters the loop from the last saved state:
resumeAgent returns immediately if the checkpoint has status: 'done' β handy for idempotent retry logic.
Listing all persisted runs
UselistRuns to retrieve every run ID that has an active checkpoint. This is useful for building an admin queue of in-progress or suspended runs:
listRuns returns only run IDs, not the full checkpoints. Load a specific checkpoint with new Checkpointer(store, runId).load() to inspect its state.The state store
Durable execution usesStateStore β a simple key-value interface separate from TraceStore. The production default is SqliteStateStore:
SqliteStateStore across multiple agent routes, since each run uses a unique runId-namespaced key.