Snapshotter Module
The Snapshotter interface lets your agent capture workspace state as a serializable handle, then restore or branch from that handle. Useful for "explore a hypothesis, roll back if it doesn't pan out" patterns and Jupyter-style checkpointing.
Interface
interface SnapshotRef {
readonly providerId: string;
readonly ref: unknown; // provider-specific opaque payload
}
interface Snapshotter {
snapshot(): Promise<SnapshotRef>;
restore(ref: SnapshotRef): Promise<WorkspaceRef>;
branch?(ref: SnapshotRef): Promise<WorkspaceRef>;
}"Restore returns a NEW WorkspaceRef" — read this carefully
Pattern 3e treats snapshots as forks, not mutations. Both restore(ref) and branch(ref):
- Do NOT mutate the current workspace.
- Create a NEW workspace identity (the provider's choice — typically a new id like
{originId}-restored-{shortId}). - Apply the snapshot to that new workspace.
- Return a fresh
WorkspaceRefpointing at the new workspace.
This means after a restore(ref):
- The current workspace is unchanged.
- You have a new ref. To start using the restored state, your code or your LLM must switch to operating against the new ref.
The framework's tool layer surfaces the new ref via the tool result — the LLM sees the ref it can pass to subsequent calls.
branch? is optional
Some providers (CodeSandbox SDK, etc.) distinguish "branch" (intentional fork) from "restore" (recover prior state) at the API layer. Pattern 3e codifies this distinction at the interface level. For providers where the implementation is identical (like CloudflareSandboxSnapshotter), branch is implemented as a semantic alias of restore — same behavior, different id suffix.
The auto-injected workspace__<name>__branch tool is only present when the provider populates branch?.
Auto-injected tools
For a workspace named <name> with snapshot: true:
| Tool | Schema | Returns | When |
|---|---|---|---|
workspace__<name>__snapshot | {} | { ref: SnapshotRef } | Always (when snapshot declared) |
workspace__<name>__restore | { ref: SnapshotRef } | { ref: WorkspaceRef } (new) | Always |
workspace__<name>__branch | { ref: SnapshotRef } | { ref: WorkspaceRef } (new) | Only when provider implements branch? |
Capability config
// boolean — no policy options in v1
snapshot: boolean;Snapshot has no fine-grained config in v1. Either you declare it or you don't.
Provider-specific snapshot configuration (R2 binding for Cloudflare Sandbox, etc.) lives on the PROVIDER config, not the capability config. See per-provider pages.
Provider-specific behavior
CloudflareSandboxSnapshotter
snapshot()callssandbox.createBackup({ dir: snapshotDir }). Backup is uploaded to R2 (provider'sbackupR2Bindingmust be configured).restore(ref)andbranch(ref)generate a new sandbox ID and callgetSandbox(namespace, newId)to obtain a stub for the new sandbox, then apply the backup viarestoreBackup(payload.backup).- The mount is FUSE overlay — ephemeral. After the new sandbox sleeps + wakes, you must re-restore from the same
SnapshotRefto recover. - Without
backupR2Bindingconfigured,snapshot()throws at call time with a clear error.
Provider support matrix
| Provider | snapshot supported |
|---|---|
| In-Memory | ❌ |
| Local Bash | ❌ |
| Cloudflare Filestore | ❌ (could be added — R2-backed sqlite dump — but not in v1) |
| Cloudflare Sandbox | ✅ (R2-backed; restore returns NEW sandbox; branch implemented) |
Source
- Interface:
packages/core/src/workspace/types/modules/snapshot.ts - Tool injection:
packages/core/src/workspace/tool-injection.ts(search formakeSnapshotTools) - Reference implementation:
CloudflareSandboxSnapshotter