Resumable runs
resumeAgentStream()/resumeAgent() pick a run back up after a crash, an uncaught error, or a process restart — instead of starting over from the original input.
A RunCheckpoint is saved after every completed turn (model call + any tool execution or handoff).
resume.ts
import {
createClient, anthropic, defineAgent, runAgent, resumeAgent, FileCheckpointStore,
} from "samai-sdk";
const client = createClient({ provider: anthropic({ apiKey: "..." }) });
const agent = defineAgent({ name: "worker", instructions: "...", model: "claude-sonnet-4-6", tools: [] });
const checkpointStore = new FileCheckpointStore("./checkpoints"); // survives a real process restart
const runId = "run-" + Date.now();
try {
await runAgent(client, agent, "Do a multi-step task", { checkpoint: { store: checkpointStore, runId } });
} catch (err) {
// Resume with the SAME root agent — its handoffs tree is walked by name to find
// whichever agent was active when the checkpoint was saved.
const result = await resumeAgent(client, agent, { checkpoint: { store: checkpointStore, runId } });
console.log(result.output);
}Nothing gets re-run
Already-executed tool calls are never re-run on resume — the checkpoint carries the full message history, so the resumed run's first action is a fresh model call continuing the conversation, not a repeat of completed work. The checkpoint is deleted automatically on successful completion; it's left in place on failure so you can inspect or resume past it.
Checkpoint stores
| Store | Notes |
|---|---|
InMemoryCheckpointStore | Only survives within the same process — good for resuming after a caught error mid-request. |
FileCheckpointStore(dir) | One JSON file per run — survives a real process restart/crash. |
Agent definitions (instructions, tools, code) aren't part of a checkpoint — only the run's accumulated state is. Resuming a runId with no saved checkpoint throws CheckpointNotFoundError rather than silently starting fresh.