API Reference

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

ParameterTypeRequiredDescription
namestringYesAgent name. Used for workflow serialization and logging.
optionsAgentOptionsNoConfiguration options (see below).

AgentOptions

All options are optional except model, which must be set either on the agent or as a resolvable.

ParameterTypeDefaultDescription
modelResolvable<GatewayModelId>AI model to use. Required. Can be a value or a function.
systemResolvable<string | string[]>System prompt. Value, function, or function returning array. See System Prompt.
storageStorageInputlocalStorage()Storage backend. StorageHandlers or StorageStepFunction.
toolsToolSetCustom tools. Object mapping tool names to tool() definitions from the ai package.
hooksAgentHooksInterception hooks: tool.before, tool.after, status.
contextSchemaz.ZodType<TContext>Zod schema for transient per-request context. NOT persisted. Use for secrets.
stateSchemaz.ZodType<TState>Zod schema for mutable per-session state. Persisted across messages. See Session State.
messageMetadataSchemaz.ZodType<TMessageMetadata>Zod schema for typed message metadata. See Message Metadata.
needsApprovalNeedsApprovalMap<Tools>Approval rules for tools. Maps tool names to true, false, or a function.
sandboxSandboxBinding | SandboxBinding[]Sandbox binding(s).
skillsResolvable<SkillInput[]>Skill source entries. Value or function.
activeToolsResolvable<(keyof Tools | BuiltInToolName)[]>All toolsRestrict which tools are available. Value or function.
generationResolvable<GenerationOptions>Generation options (maxSteps, temperature, etc.). Value or function.
loggingLoggingConfigLogging 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 / MethodTypeDescription
session(id)(id: string) => SessionHandleGet a session handle. Synchronous. See Session API.
sandbox(id)(id: string) => SandboxHandleGet a sandbox handle.
namestringThe agent name.
toolsToolSetMerged built-in + custom tools object.
storageStorageResolved storage (internal).
$UIMessageType helpertypeof 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:

src/agent.ts
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.) receive Readonly<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().

src/agent.ts
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

src/agent.ts
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