Storage
How the agent persists sessions, messages, parts, and sandbox records. Bring your own database or use the built-in filesystem handler.
The agent uses storage to maintain conversation context, resume workflows, and correlate tool execution across requests.
How It Works
Storage is a function you pass to the agent. It receives a store object and dispatches it to your handlers via store.on().
import { agent } from "experimental-agent";
export const myAgent = agent("my-agent", {
model: "anthropic/claude-sonnet-4-6",
async storage(store) {
return await store.on({
"session.get": async ({ id }) => await db.sessions.findById(id),
"session.set": async ({ id, value }) => await db.sessions.upsert(id, value),
"session.update": async ({ id, updates }) => await db.sessions.update(id, updates),
"message.get": async ({ id }) => await db.messages.findById(id),
"message.set": async ({ id, value }) => await db.messages.upsert(id, value),
"message.update": async ({ id, updates }) => await db.messages.update(id, updates),
"message.listBySession": async ({ sessionId }) => await db.messages.findBySession(sessionId),
"part.get": async ({ id }) => await db.parts.findById(id),
"part.set": async ({ id, value }) => await db.parts.upsert(id, value),
"part.listBySession": async ({ sessionId }) => await db.parts.findBySession(sessionId),
"sandbox.get": async ({ id }) => await db.sandboxes.findById(id),
"sandbox.set": async ({ id, value }) => await db.sandboxes.upsert(id, value),
"sandbox.update": async ({ id, updates }) => await db.sandboxes.update(id, updates),
"setup.get": async ({ id }) => await db.setups.findById(id),
"setup.set": async ({ id, value }) => await db.setups.upsert(id, value),
});
},
});To make this work with Vercel Workflow, add "use step" at the top of the function:
export const myAgent = agent("my-agent", {
model: "anthropic/claude-sonnet-4-6",
async storage(store) {
"use step";
return await store.on({
"session.get": async ({ id }) => await db.sessions.findById(id),
// ... same handlers
});
},
});That's it. The "use step" directive tells the workflow bundler to extract this function as a retryable step, so storage calls survive workflow replay. Without it, everything works the same — just without durability.
Built-in: localStorage()
For development, the SDK includes localStorage() — a filesystem-backed storage function. Data is stored as JSON files in .agent/ by default.
import { localStorage } from "experimental-agent/storage";
agent("my-agent", {
storage: localStorage(),
})
agent("my-agent", {
storage: localStorage({ dir: ".my-data" }),
})If you omit storage entirely, the SDK falls back to localStorage() with a warning.
What Gets Stored
| Entity | Purpose |
|---|---|
| Session | Conversation identity, model config, system prompt, generation options, last message ID. |
| Message | User and assistant turns. Role, usage, timestamps, workflow run ID, optional app-defined metadata. |
| Part | Individual content parts: text chunks, tool calls, tool results. |
| Sandbox | Sandbox ID, config, provider metadata. |
Message Metadata
Messages support an optional metadata field for application-defined annotations that need to live alongside the message in storage. This is useful for tagging messages with a kind or type (e.g. synthetic context injections, imported messages, UI rendering hints) without encoding that information in the message content itself.
await session.send({
parts: [{ type: "text", text: "User's current project context..." }],
metadata: { type: "user-context" },
});Metadata is opaque to the agent — it round-trips through storage unchanged and is returned by session.history(). See Message Metadata for type-safe usage.
Design Philosophy
Storage holds what the agent needs to function, plus optional message-level metadata for your application. Broader concerns — user ownership, access control, titles — belong in your app's database. Use the session ID as the join key.
┌──────────────────┐ session ID ┌──────────────────┐
│ Your Database │◄──────────────────►│ Agent Storage │
│ │ │ │
│ userId │ │ Session │
│ title │ │ Message (+meta) │
│ access control │ │ Part │
│ │ │ Sandbox │
└──────────────────┘ └──────────────────┘Next Steps
- Custom Storage — Full guide with
@vercel/kv2example - Sessions — How sessions use storage for persistence
- Sandbox — Sandbox records and lifecycle