Changelog
Breaking changes, new APIs, and migration guide for the latest release.
v0.5.0
Resolvable agent options
Agent options (model, system, activeTools, generation, skills) now accept Resolvable<T> — a static value or a function evaluated fresh on every LLM step. This replaces send-level overrides and session-stored config.
const myAgent = agent("my-agent", {
model: "anthropic/claude-sonnet-4-6",
system: async ({ context }) => {
const rules = await fetchRules(context.projectId);
return `You are a coding assistant.\n${rules}`;
},
activeTools: ({ messages }) => {
const inPlanMode = messages.some(m =>
m.parts.some(p => "type" in p && p.type === "tool-EnterPlanMode")
);
return inPlanMode ? ["Read", "Grep", "List"] : ["Read", "Write", "Edit", "Grep", "List", "Bash"];
},
});Resolvable functions receive { context, sessionId, sandbox, messages } — the same shape as ToolContext.
Simplified session model
Session is now minimal: { id, sandboxId, lastMessageId, createdAt, updatedAt }. Runtime config (model, system, activeTools, generation, skills) is no longer stored on the session — it's resolved from agent options each step.
session.update()is scoped to{ sandboxId?, lastMessageId? }. To change model, system, or tools dynamically, use resolvable agent options with context.SendOptionsno longer accepts model, system, activeTools, generation, or skills overrides. Pass context instead and use resolvable agent options to derive config.
Before
await session.send("hello", {
model: "anthropic/claude-opus-4.6",
system: "Be concise.",
activeTools: ["Read", "Write"],
});After
const myAgent = agent("my-agent", {
model: ({ context }) => context.useAdvanced
? "anthropic/claude-opus-4.6"
: "anthropic/claude-sonnet-4-6",
system: ({ context }) => context.systemPrompt,
activeTools: ({ context }) => context.tools,
});
await session.send("hello", {
context: { useAdvanced: true, systemPrompt: "Be concise.", tools: ["Read", "Write"] },
});ToolContext changes
ToolContext is now ResolvableArgs — the same shape used by resolvable option functions:
| Before | After |
|---|---|
context.session | context.sessionId |
context.storage | (removed) |
context.sandbox | context.sandbox (unchanged) |
context.context | context.context (unchanged) |
| — | context.messages (new) |
Other changes
Resolvable,ResolvableArgsexports — New types for building resolvable option patterns.activeToolsacceptsreadonlyarrays —as consttool lists work without casts.localStorage()auto-gitignore — Emits a.gitignorein the storage directory on first write to prevent session data from being tracked by git.SystemPromptFn,SystemPromptInputremoved — System is nowResolvable<string | string[] | undefined>. Arrays are joined with newlines; falsy values are filtered.- Default sandbox setup options — Sandbox bindings accept default
setupoptions applied on first sandbox creation. session.sandboxhandle — Access the session's sandbox outside of tool execution without knowing the sandbox ID.- Durable message metadata — Type-safe metadata on messages via
agent<Metadata>()generic.
v0.4.0
KV2 storage auto-fallback
When no storage is configured, the agent automatically falls back to KV2-backed storage when available.
// explicit storage — nothing changes
const myAgent = agent("my-agent", {
storage: myCustomStorage(),
});
// no storage — auto-falls back to KV2
const myAgent = agent("my-agent", {});BLOB_READ_WRITE_TOKENmust be present in the environment for it to fallback to KV2- Keys are namespaced under the agent name (e.g.
"my-agent/") @vercel/kv2is dynamically imported on first storage access- Uses
"use step"for workflow durability - Falls back to
localStorage()when the token is not set (e.g. local dev)
v0.3.0
Redesigned storage and workflow. Storage is now a handler map you provide directly. Workflow is fully opt-in.
Storage
Storage backends ({ type: "vercel" | "local" | "custom" }) are replaced by a storage() function with inline handlers.
Before
agent({ storage: { type: "vercel" } })After
Storage is a function that receives a store and dispatches to your handlers. To add workflow durability, add "use step" at the top — that's the only difference.
agent("my-agent", {
async storage(store) {
"use step"; // remove this line if you don't use workflow
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, part, sandbox handlers
});
},
})For development, use the built-in localStorage(). For Vercel deployments, use @vercel/kv2 (see the vade app for a full example).
Session
session() is now synchronous. send() accepts the message directly and returns a SendResult.
Before
const session = await myAgent.session(chatId);
await session.send({ input: message });
const stream = await session.stream();After
const session = myAgent.session(chatId);
await session.send(message, opts);
const stream = await session.stream();send() returns SendResult { assistantMessageId, done } instead of void | Error.
Workflow
Workflow is no longer always-on. Everything works without it. To add durability, define a workflow function and start it yourself.
Before
The withAgent Next.js plugin in next.config.ts automatically wired workflow durability behind the scenes. You didn't opt in — it was always on.
// next.config.ts
import { withAgent } from "experimental-agent/next";
export default withAgent(nextConfig);
// route.ts — workflow was implicit, handled by the plugin
const session = await myAgent.session(chatId);
await session.send({ input: message });
const stream = await session.stream();After
// workflow.ts — you define the workflow function
export async function agentWorkflow(
sessionId: string,
...args: SessionSendArgs<typeof myAgent>
) {
"use workflow";
return await myAgent.session(sessionId).send(...args);
}
// route.ts — you start it and stream from the result
import { start } from "workflow/api";
const result = await start(agentWorkflow, [sessionId, message, opts]);
const stream = await session.stream(result);
return createUIMessageStreamResponse({ stream });send() uses "use step" internally, which is a no-op outside a workflow. No code changes needed in the agent itself.
Streaming
session.stream() now accepts an optional WorkflowRunLike to stream from a workflow result.
Before
const stream = await session.stream();After
// In-memory reconnect (no workflow)
const stream = await session.stream();
// Stream from a workflow result
const result = await start(agentWorkflow, [...]);
const stream = await session.stream(result);All changes
New APIs:
StorageHandlers— flat map of handler functions ("session.get","message.set", etc.) (27ca29d,0033ac2)StorageStepFunction—(store: StorageStep) => Promise<any>with"use step"for workflow (748b912)StorageStepclass — serializable across workflow boundary, dispatches viastore.on(handlers)(748b912)localStorage()— built-in filesystem-backedStorageHandlers(0033ac2)SendResult { assistantMessageId, done }— return type ofsend()(27ca29d)session.stream(result?)— acceptsWorkflowRunLikefor workflow streaming (7b2e370,5ff9743)session.history()— returns conversation history from storage (27ca29d,3827005)agent(name, opts)— name is now a required first positional argument (27ca29d)
Changed APIs:
session()is synchronous (noawait) (27ca29d)send(input, opts?)— input is a string, UIMessage, parts array, or{ type: "approval" | "message", ... }; options includeinterruptIfStreaming,context, and session updates (27ca29d,3827005)session.stream()returnsReadableStream<UIMessageChunk>(notAgentStream) (7b2e370)- Vercel storage: use
@vercel/kv2to implement your handlers (0033ac2)
Removed APIs:
StorageConfigtype ({ type: "vercel" | "local" | "custom" }) (0033ac2)handleStorageRpcandHandlers(old RPC type) (0033ac2)session.ui()— usesession.history()instead (3827005)session.tag.*andtagsSchema(27ca29d)session.resolveApproval()— usesend({ type: "approval", ... })instead (3827005)myAgent.storage.*— direct storage access from the agent (0033ac2)myAgent.handler()andmyAgent.rpc(0033ac2)workflowSend()andworkflowInput()on session (e643d88)withAgentnext.config.ts wrapper (27ca29d)apps/storage(Vercel hosted storage service) (0033ac2)- RPC dispatch layer (
0033ac2)