Persistence (Advanced)
- Build your first harness
- Host options
- Send and show media
- Connect clients
- Write a client in any language
- Access and tenants
- Run in the terminal
- Deploy a harness
- Send inputs safely
- Run a tool without a model
- Durable sessions
- Run background work on workers
- List, rename, and fork sessions
- Lifecycle and retention
- Run side effects once
- Wait for a person
- Build on the session log
- Control how a turn ends
- Store settings per thread
- Compact a long session
- Fork and reset a thread
- One log for a session tree
- Write a plugin
- Track usage and cost
- Ask before risky tool calls
- Give your agent coding tools
- Sandbox and extend the coding tools
- Run the coding tools for many threads
- Undo a turn
- Build a coding agent
- Delegate to coding agents
- Switch between agent profiles
- Auth and connectors
- Share a thread between people
- Connect model providers
- Build your own UI
- Code mode in a harness
- Work until a goal is met
- Run agents from a harness
- Hand off a conversation
- Add skills
- Use MCP servers
- Give your agent a computer
- Use from any MCP client
- Use a harness from an editor (ACP)
- Overview
- Quick Start
- ProvidersUpdated
- Harnesses
- Harness Auth
- Workspace
- Tools
- Policy
- Lifecycle & Snapshots
- Portable Snapshots
- Keep Files After Reload
- Save a Named Version
- Branch From a Version
- Send a Frozen File
- Let the Agent Save and Fork
- Pick Which Files to Keep
- What a Snapshot Stores
- Instance Durability
- Durable Runs
- Events
Sandboxes (Advanced)
- Runtime Context
- Bring Your Own Key
- Keep Keys on the Server
- Debug Logging
- Multimodal Content
- Files API
- Per-Model Type Safety
- Runtime Adapter Switching
- Tree-Shaking
- Extend Adapter
- Extend Adapter: Advanced
- Typed Pre-Configured Options
- Test with a Fake Model
- Prompt Caching
- Mid-Conversation Changes
- Approval Flow Processing
Let the Agent Save and Fork
createSnapshotTools turns threadId and runId into host tools. Spread those tools into chat() so the agent can save or fork a snapshot.
The model can pass a label or a checkpoint id on this thread. It cannot pass a thread id. Your factory mints every new thread id.
import { chat } from '@tanstack/ai'
import { grokBuildText } from '@tanstack/ai-grok-build'
import { withPersistence } from '@tanstack/ai-persistence'
import { createSnapshotTools, withSandbox } from '@tanstack/ai-sandbox'
import { instances, sandbox, snapshots } from './sandbox-server'
export function POST(threadId: string, runId: string) {
return chat({
threadId,
runId,
adapter: grokBuildText('composer-2.5'),
messages: [{ role: 'user', content: 'Save this, then try a dark theme.' }],
tools: [
...createSnapshotTools(snapshots, {
threadId,
runId,
createThreadId: () => crypto.randomUUID(),
onForked({ destinationThreadId }) {
void destinationThreadId
},
}),
],
middleware: [
withPersistence(snapshots.persistence),
withSandbox(sandbox, { instances, snapshots }),
],
})
}The tools run on the server. In a sandbox chat they are bridged back to the host. See Tools.
What each tool does#
- save_sandbox_snapshot: saves the live sandbox for the bound thread. The model passes label only.
- fork_sandbox_snapshot: copies one checkpoint into a new empty thread. The model can pass checkpointId. When it omits that id, the tool copies the latest checkpoint. createThreadId() sets the destination thread id.
- read_sandbox_snapshot_artifact: returns artifact metadata for a checkpoint on this thread. It does not return file bytes. Serve bytes from Send a Frozen File.
onForked runs after a successful fork. If you want the new branch to work right away, start chat() on destinationThreadId in that callback.
When save and fork run#
save_sandbox_snapshot takes the writer lease for this thread. If this chat() already holds that lease through portable snapshots, the save fails with SANDBOX_SNAPSHOT_WRITER_CONFLICT. Save from a planner thread, or after this run ends.
fork_sandbox_snapshot takes the writer lease on the new thread. A fork during this run can succeed. It copies a saved checkpoint. It does not copy files the agent is still writing.
See Keep Files After Reload to create the snapshots object. See Save a Named Version and Branch From a Version for routes that you call yourself.