agent()
API reference for the agent() function — the entry point for creating AI agents.
Returns an agent object with methods for sessions and sandboxes.
import { agent } from "experimental-agent";
const myAgent = agent("my-agent", options);Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Agent name. Used for workflow serialization and logging. |
options | AgentOptions | No | Configuration options (see below). |
AgentOptions
All options are optional except model, which must be set either on the agent or as a resolvable.
| Parameter | Type | Default | Description |
|---|---|---|---|
model | Resolvable<GatewayModelId> | — | AI model to use. Required. Can be a value or a function. |
system | Resolvable<string | string[]> | — | System prompt. Value, function, or function returning array. See System Prompt. |
storage | StorageInput | localStorage() | Storage backend. StorageHandlers or StorageStepFunction. |
tools | ToolSet | — | Custom tools. Object mapping tool names to tool() definitions from the ai package. |
hooks | AgentHooks | — | Interception hooks: tool.before, tool.after, status. |
contextSchema | z.ZodType<TContext> | — | Zod schema for transient per-request context. NOT persisted. Use for secrets. |
stateSchema | z.ZodType<TState> | — | Zod schema for mutable per-session state. Persisted across messages. See Session State. |
messageMetadataSchema | z.ZodType<TMessageMetadata> | — | Zod schema for typed message metadata. See Message Metadata. |
needsApproval | NeedsApprovalMap<Tools> | — | Approval rules for tools. Maps tool names to true, false, or a function. |
sandbox | SandboxBinding | SandboxBinding[] | — | Sandbox binding(s). |
skills | Resolvable<SkillInput[]> | — | Skill source entries. Value or function. |
activeTools | Resolvable<(keyof Tools | BuiltInToolName)[]> | All tools | Restrict which tools are available. Value or function. |
generation | Resolvable<GenerationOptions> | — | Generation options (maxSteps, temperature, etc.). Value or function. |
logging | LoggingConfig | — | Logging configuration. |
See Storage, Sandbox, and GenerationOptions for details.
Resolvable Options
Most agent options accept a Resolvable<T> — either a static value or a function that receives context and returns the value. Functions are evaluated fresh on every LLM step.
type ResolvableArgs<TContext, TState> = {
context: TContext;
state: Readonly<TState>;
sessionId: string;
sandbox: SandboxInstance;
messages: UIMessage[];
};State is read-only in resolvable options. Only tools can mutate state — see Session State.
Static value
const myAgent = agent("my-agent", {
model: "anthropic/claude-opus-4.6",
system: "You are a helpful coding assistant.",
activeTools: ["Read", "Write", "Edit"],
});Dynamic function
Functions receive the full conversation context and are evaluated on every step:
const myAgent = agent("my-agent", {
model: "anthropic/claude-opus-4.6",
system: async () => {
const { readFileSync } = await import("node:fs");
return readFileSync("./INSTRUCTIONS.md", "utf-8");
},
activeTools: ({ messages }) => {
// Derive active tools from conversation state
const hasEnterPlan = messages.some((m) =>
m.parts.some((p) => "type" in p && p.type === "tool-EnterPlanMode")
);
return hasEnterPlan ? ["Read", "Grep", "List"] : ["Read", "Write", "Edit", "Grep", "List", "Bash"];
},
});System prompt with arrays
The system option can return a string[] — elements are joined with newlines:
const myAgent = agent("my-agent", {
system: ({ context }) => [
"You are a helpful coding assistant.",
context.projectType === "react"
? "Focus on React best practices."
: undefined,
],
});AgentHooks
type AgentHooks = {
"tool.before"?: (opts: {
name: string;
input: unknown;
context: ToolContext;
}) => Promise<undefined | { input: unknown }>;
"tool.after"?: (opts: {
name: string;
input: unknown;
result: unknown;
context: ToolContext;
}) => Promise<undefined | { result: unknown }>;
status?: (status: AgentStatus) => void | Promise<void>;
};- tool.before — Called before a tool executes. Return
{ input: modifiedInput }to transform input, or throw to block execution. - tool.after — Called after a tool executes. Return
{ result: modifiedResult }to transform output. - status — Called when the agent emits a transient status (e.g.
thinking,loading-skills).
See Hooks for usage patterns.
NeedsApprovalMap
type NeedsApprovalMap<Tools> = {
[K in ToolName<Tools>]?:
| boolean
| ((
input: ToolInput,
options: {
toolCallId: string;
messages: ModelMessage[];
experimental_context: unknown;
}
) => boolean | Promise<boolean>);
};true— Always require approval before the tool runs.false— Never require approval.(input, options) => boolean | Promise<boolean>— Dynamic approval based on input and context.
See Approvals for frontend integration.
Return value
The agent object has the following properties and methods:
| Property / Method | Type | Description |
|---|---|---|
session(id) | (id: string) => SessionHandle | Get a session handle. Synchronous. See Session API. |
sandbox(id) | (id: string) => SandboxHandle | Get a sandbox handle. |
name | string | The agent name. |
tools | ToolSet | Merged built-in + custom tools object. |
storage | Storage | Resolved storage (internal). |
$UIMessage | Type helper | typeof myAgent.$UIMessage gives the typed UIMessage for this agent. See Message Metadata. |
Session State
Mutable per-session state that tools can read and write. Unlike context (transient, per-request), state persists across messages in a session. Define stateSchema on the agent:
import { agent, tool } from "experimental-agent";
import { z } from "zod";
const stateSchema = z.object({
filesEdited: z.array(z.string()).default([]),
deployCount: z.number().default(0),
});
const TrackDeploy = tool({
stateSchema,
inputSchema: z.object({ env: z.string() }),
execute: async ({ env }, { state }) => {
state.deployCount++;
return { deployed: true, totalDeploys: state.deployCount };
},
});
export const myAgent = agent("my-agent", {
model: "anthropic/claude-sonnet-4-6",
stateSchema,
tools: { TrackDeploy },
system: ({ state }) => {
// state is read-only in resolvable options
return `You have deployed ${state.deployCount} times this session.`;
},
});Key points:
- Tools receive mutable
state— mutate it directly (state.counter++) - Resolvable options (
system,activeTools, etc.) receiveReadonly<TState>— read-only - State is persisted to storage after each LLM step, alongside parts
- State defaults to
{}for new sessions
Message Metadata
You can attach typed, durable metadata to messages. The metadata is persisted through storage and returned by session.history().
import { agent } from "experimental-agent";
import { z } from "zod";
const messageMetadataSchema = z.union([
z.object({ type: z.literal("user-context") }),
z.object({ type: z.literal("imported"), source: z.string() }),
]);
export const myAgent = agent("my-agent", {
model: "anthropic/claude-sonnet-4-6",
messageMetadataSchema,
});
type MyMessage = typeof myAgent.$UIMessage;Send messages with metadata:
await session.send({
parts: [{ type: "text", text: "User's current project context..." }],
metadata: { type: "user-context" },
});Read it back from history:
const { messages } = await session.history();
for (const msg of messages) {
if (msg.metadata?.type === "user-context") {
// render differently, deduplicate, etc.
}
}Metadata is opaque to the agent runtime — it never interprets or modifies it. If you don't need metadata, omit the type parameter and everything works as before.
Example
import { agent } from "experimental-agent";
import { localStorage } from "experimental-agent/storage";
import { tool } from "ai";
import { z } from "zod";
export const myAgent = agent("coding-assistant", {
model: "anthropic/claude-opus-4.6",
system: "You are a helpful coding assistant.",
storage: localStorage(),
contextSchema: z.object({
authToken: z.string().optional(),
}),
tools: {
Deploy: tool({
description: "Deploy the project",
parameters: z.object({ env: z.enum(["preview", "production"]) }),
execute: async ({ env }) => ({ deployed: true, env }),
}),
},
hooks: {
"tool.before": async ({ name, input }) => {
if (name === "Deploy") console.log("Deploy requested:", input);
return undefined;
},
status: (status) => console.log("Agent status:", status),
},
needsApproval: {
Deploy: ({ env }) => env === "production",
},
skills: [{ type: "sandbox", path: ".agent/skills" }],
activeTools: ["Read", "Write", "Edit", "Grep", "List", "Deploy"],
generation: { maxSteps: 20, temperature: 0.7 },
});
type MyMessage = typeof myAgent.$UIMessage;See also
- Session API — Session methods and options
- ToolContext — Context passed to custom tools
- GenerationOptions — Model generation parameters
- Sessions — Conceptual overview
- Custom Tools — Building tools with
tool()fromai