diff --git a/public/__redirects b/public/__redirects index d0b1f57af1c..b2becb07f1c 100644 --- a/public/__redirects +++ b/public/__redirects @@ -228,7 +228,8 @@ /agents/concepts/long-running-agents/ /agents/concepts/agentic-patterns/long-running-agents/ 301 /agents/api-reference/agents-api/ /agents/runtime/agents-api/ 301 /agents/api-reference/ /agents/runtime/ 301 -/agents/api-reference/chat-sdk/ /agents/runtime/execution/chat-sdk/ 301 +/agents/api-reference/chat-sdk/ /agents/runtime/communication/chat-sdk/ 301 +/agents/runtime/execution/chat-sdk/ /agents/runtime/communication/chat-sdk/ 301 /agents/api-reference/agent-tools/ /agents/runtime/execution/agent-tools/ 301 /agents/api-reference/browse-the-web/ /agents/tools/browser/ 301 /agents/api-reference/callable-methods/ /agents/runtime/lifecycle/callable-methods/ 301 diff --git a/src/content/docs/agents/communication-channels/chat/chat-agents.mdx b/src/content/docs/agents/communication-channels/chat/chat-agents.mdx index fc19650f6fd..7f7667ab400 100644 --- a/src/content/docs/agents/communication-channels/chat/chat-agents.mdx +++ b/src/content/docs/agents/communication-channels/chat/chat-agents.mdx @@ -1582,7 +1582,7 @@ export class ChatAgent extends AIChatAgent { :::note -This section covers **in-process** subagents using the AI SDK's `ToolLoopAgent`. For **Durable Object sub-agents** with their own isolated storage and typed RPC, refer to [Sub-agents](/agents/runtime/execution/sub-agents/). To run Think or `AIChatAgent` sub-agents as retained, streaming tools, refer to [Agent tools](/agents/runtime/execution/agent-tools/). +This section covers **in-process** subagents using the AI SDK's `ToolLoopAgent`. For **Durable Object sub-agents** with their own isolated storage and typed RPC, refer to [Sub-agents](/agents/runtime/execution/sub-agents/). To run Think or `AIChatAgent` sub-agents as retained, streaming tools, refer to [Agents as tools](/agents/runtime/execution/agent-tools/). ::: diff --git a/src/content/docs/agents/communication-channels/chat/client-sdk.mdx b/src/content/docs/agents/communication-channels/chat/client-sdk.mdx index 7fe18dd4742..ff7b3e47114 100644 --- a/src/content/docs/agents/communication-channels/chat/client-sdk.mdx +++ b/src/content/docs/agents/communication-channels/chat/client-sdk.mdx @@ -616,7 +616,7 @@ client.addEventListener("message", () => {}); ## Agent-tool events -If your chat UI renders retained child runs from [Agent tools](/agents/runtime/execution/agent-tools/), use `useAgentToolEvents()` alongside `useAgent()` and `useAgentChat()`. The hook subscribes to the parent connection, replays retained child timelines, and groups runs by parent tool call ID. +If your chat UI renders retained child runs from [Agents as tools](/agents/runtime/execution/agent-tools/), use `useAgentToolEvents()` alongside `useAgent()` and `useAgentChat()`. The hook subscribes to the parent connection, replays retained child timelines, and groups runs by parent tool call ID. diff --git a/src/content/docs/agents/concepts/tools.mdx b/src/content/docs/agents/concepts/tools.mdx index 3ffba4ef643..caa6ee330f0 100644 --- a/src/content/docs/agents/concepts/tools.mdx +++ b/src/content/docs/agents/concepts/tools.mdx @@ -16,13 +16,13 @@ Tools enable AI systems to interact with external services and perform actions. Cloudflare Agents support several tool patterns. Choose the smallest one that fits the job: -| Pattern | Use when | Start here | -| ----------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | +| Pattern | Use when | Start here | +| ----------------- | ----------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | | Server-side tools | The tool can run entirely in the Worker, such as fetching an API or querying SQL | [Chat agents](/agents/communication-channels/chat/chat-agents/#server-side-tools) | | Client-side tools | The tool needs browser APIs such as geolocation, clipboard, or local storage | [Chat agents](/agents/communication-channels/chat/chat-agents/#client-side-tools) | -| Human approvals | The tool is sensitive and needs a user decision before it runs | [Human-in-the-loop](/agents/concepts/agentic-patterns/human-in-the-loop/) | -| MCP tools | You want to expose or consume tools through the Model Context Protocol | [Model Context Protocol](/agents/model-context-protocol/) | -| Agent tools | You want a chat agent to run another chat-capable sub-agent as a retained, streaming tool | [Agent tools](/agents/runtime/execution/agent-tools/) | +| Human approvals | The tool is sensitive and needs a user decision before it runs | [Human-in-the-loop](/agents/concepts/agentic-patterns/human-in-the-loop/) | +| MCP tools | You want to expose or consume tools through the Model Context Protocol | [Model Context Protocol](/agents/model-context-protocol/) | +| Agents as tools | You want a chat agent to run another chat-capable sub-agent as a retained, streaming tool | [Agents as tools](/agents/runtime/execution/agent-tools/) | ### Understanding tools diff --git a/src/content/docs/agents/getting-started/add-to-existing-project.mdx b/src/content/docs/agents/getting-started/add-to-existing-project.mdx index 326728cf0e8..5b42e32a303 100644 --- a/src/content/docs/agents/getting-started/add-to-existing-project.mdx +++ b/src/content/docs/agents/getting-started/add-to-existing-project.mdx @@ -12,7 +12,7 @@ import { PackageManagers, TypeScriptExample, WranglerConfig, - LinkCard, + Render, } from "~/components"; This guide shows how to add agents to an existing Cloudflare Workers project. If you are starting fresh, refer to [Building a chat agent](/agents/examples/chat-agent/) instead. @@ -304,6 +304,12 @@ function CounterWidget() { +Key points: + +- `useAgent` connects to your agent via WebSocket +- `onStateUpdate` fires whenever the agent's state changes +- `agent.stub.methodName()` calls methods marked with `@callable()` on your agent + ### Vanilla JavaScript @@ -325,207 +331,18 @@ document.getElementById("increment").onclick = () => agent.call("increment"); -## Adding multiple agents - -Add more agents by extending the configuration: - - - -```ts -// src/agents/chat.ts -export class Chat extends Agent { - // ... -} - -// src/agents/scheduler.ts -export class Scheduler extends Agent { - // ... -} -``` - - - -Update the Wrangler configuration file: - - - -```jsonc -{ - "durable_objects": { - "bindings": [ - { "name": "CounterAgent", "class_name": "CounterAgent" }, - { "name": "Chat", "class_name": "Chat" }, - { "name": "Scheduler", "class_name": "Scheduler" }, - ], - }, - "migrations": [ - { - "tag": "v1", - "new_sqlite_classes": ["CounterAgent", "Chat", "Scheduler"], - }, - ], -} -``` - - - -Export all agents from your entry point: - - - -```ts -export { CounterAgent } from "./agents/counter"; -export { Chat } from "./agents/chat"; -export { Scheduler } from "./agents/scheduler"; -``` - - - -## Common integration patterns - -### Agents behind authentication + -Check auth before routing to agents: - - - -```ts -export default { - async fetch(request: Request, env: Env) { - // Check auth for agent routes - if (request.url.includes("/agents/")) { - const authResult = await checkAuth(request, env); - if (!authResult.valid) { - return new Response("Unauthorized", { status: 401 }); - } - } - - const agentResponse = await routeAgentRequest(request, env); - if (agentResponse) return agentResponse; - - // ... rest of routing - }, -} satisfies ExportedHandler; -``` - - +## Deploy to Cloudflare -### Custom agent path prefix - -By default, agents are routed at `/agents/{agent-name}/{instance-name}`. You can customize this: - - - -```ts -import { routeAgentRequest } from "agents"; - -const agentResponse = await routeAgentRequest(request, env, { - prefix: "/api/agents", // Now routes at /api/agents/{agent-name}/{instance-name} -}); -``` - - - -Refer to [Routing](/agents/runtime/communication/routing/) for more options including CORS, custom instance naming, and location hints. - -### Accessing agents from server code - -You can interact with agents directly from your Worker code: - - - -```ts -import { getAgentByName } from "agents"; - -export default { - async fetch(request: Request, env: Env) { - if (request.url.endsWith("/api/increment")) { - // Get a specific agent instance - const counter = await getAgentByName(env.CounterAgent, "shared-counter"); - const newCount = await counter.increment(); - return Response.json({ count: newCount }); - } - // ... - }, -} satisfies ExportedHandler; -``` - - - -## Troubleshooting - -### Agent not found, or 404 errors - -1. **Check the export** - Agent class must be exported from your main entry point. -2. **Check the binding** - `class_name` in the Wrangler configuration file must exactly match the exported class name. -3. **Check the route** - Default route is `/agents/{agent-name}/{instance-name}`. - -### No such Durable Object class error - -Add the migration to the Wrangler configuration file: - - - -```jsonc -{ - "migrations": [ - { - "tag": "v1", - "new_sqlite_classes": ["YourAgentClass"], - }, - ], -} -``` - - - -### WebSocket connection fails - -Ensure your routing passes the response unchanged: - - - -```ts -// Correct - return the response directly -const agentResponse = await routeAgentRequest(request, env); -if (agentResponse) return agentResponse; - -// Wrong - this breaks WebSocket connections -if (agentResponse) return new Response(agentResponse.body); +```sh +npm run deploy ``` - - -### State not persisting - -Check that: - -1. You are using `this.setState()`, not mutating `this.state` directly. -2. The agent class is in `new_sqlite_classes` in migrations. -3. You are connecting to the same agent instance name. - -## Next steps - - +Your agent is now live on Cloudflare's global network, running close to your users. - + - + - + diff --git a/src/content/docs/agents/getting-started/quick-start.mdx b/src/content/docs/agents/getting-started/quick-start.mdx index e1ff81f5caf..64344a67415 100644 --- a/src/content/docs/agents/getting-started/quick-start.mdx +++ b/src/content/docs/agents/getting-started/quick-start.mdx @@ -12,7 +12,7 @@ import { TypeScriptExample, WranglerConfig, PackageManagers, - LinkCard, + Render, } from "~/components"; Build AI agents that persist, think, and act. Agents run on Cloudflare's global network, maintain state across requests, and connect to clients in real-time via WebSockets. @@ -150,6 +150,13 @@ Update `wrangler.jsonc` to register the agent: +**Key points:** + +- `name` in bindings becomes the property on `env` (for example, `env.CounterAgent`) +- `class_name` must exactly match your exported class name +- `new_sqlite_classes` enables SQLite storage for state persistence +- `nodejs_compat` flag is required for the agents package + ## Connect from React Replace `src/client.tsx`: @@ -193,30 +200,7 @@ Key points: - `onStateUpdate` fires whenever the agent's state changes - `agent.stub.methodName()` calls methods marked with `@callable()` on your agent -## What just happened? - -When you clicked the button: - -1. **Client** called `agent.stub.increment()` over WebSocket -2. **Agent** ran `increment()`, updated state with `setState()` -3. **State** persisted to SQLite automatically -4. **Broadcast** sent to all connected clients -5. **React** updated via `onStateUpdate` - -```mermaid -flowchart LR - A["Browser
(React)"] <-->|WebSocket| B["Agent
(Counter)"] - B --> C["SQLite
(State)"] -``` - -### Key concepts - -| Concept | What it means | -| -------------------- | ----------------------------------------------------------------------------------------------------- | -| **Agent instance** | Each unique name gets its own agent. `CounterAgent:user-123` is separate from `CounterAgent:user-456` | -| **Persistent state** | State survives restarts, deploys, and hibernation. It is stored in SQLite | -| **Real-time sync** | All clients connected to the same agent receive state updates instantly | -| **Hibernation** | When no clients are connected, the agent hibernates (no cost). It wakes on the next request | + ## Connect from vanilla JavaScript @@ -250,117 +234,8 @@ npm run deploy Your agent is now live on Cloudflare's global network, running close to your users. -## Troubleshooting - -### "Agent not found" or 404 errors - -Make sure: - -1. Agent class is exported from your server file -2. `wrangler.jsonc` has the binding and migration -3. Agent name in client matches the class name (case-insensitive) - -### State not syncing - -Check that: - -1. You are calling `this.setState()`, not mutating `this.state` directly -2. The `onStateUpdate` callback is wired up in your client -3. WebSocket connection is established (check browser dev tools) - -### "Method X is not callable" errors - -Make sure your methods are decorated with `@callable()`: + - - -```ts -import { Agent, callable } from "agents"; - -export class MyAgent extends Agent { - @callable() - increment() { - // ... - } -} -``` - - - -### Type errors with `agent.stub` + -Add the agent and state type parameters: - - - -```ts -import { useAgent } from "agents/react"; -import type { CounterAgent, CounterState } from "./server"; - -// Pass the agent and state types to useAgent -const agent = useAgent({ - agent: "CounterAgent", - onStateUpdate: (state) => setCount(state.count), -}); - -// Now agent.stub is fully typed -agent.stub.increment(); -``` - - - -### `SyntaxError: Invalid or unexpected token` with `@callable()` - -If your dev server fails with `SyntaxError: Invalid or unexpected token`, set `"target": "ES2021"` in your `tsconfig.json`. This ensures that Vite's esbuild transpiler downlevels TC39 decorators instead of passing them through as native syntax. - -```json -{ - "compilerOptions": { - "target": "ES2021" - } -} -``` - -:::caution -Do not set `"experimentalDecorators": true` in your `tsconfig.json`. The Agents SDK uses [TC39 standard decorators](https://github.com/tc39/proposal-decorators), not TypeScript legacy decorators. Enabling `experimentalDecorators` applies an incompatible transform that silently breaks `@callable()` at runtime. -::: - -## Next steps - -Now that you have a working agent, explore these topics: - -### Common patterns - -| Learn how to | Refer to | -| ------------------------ | --------------------------------------------------------- | -| Add AI/LLM capabilities | [Using AI models](/agents/runtime/operations/using-ai-models/) | -| Expose tools via MCP | [MCP servers](/agents/model-context-protocol/apis/agent-api/) | -| Run background tasks | [Schedule tasks](/agents/runtime/execution/schedule-tasks/) | -| Handle emails | [Email routing](/agents/communication-channels/email/) | -| Use Cloudflare Workflows | [Run Workflows](/agents/runtime/execution/run-workflows/) | - -### Explore more - - - - - - - - + diff --git a/src/content/docs/agents/harnesses/think/index.mdx b/src/content/docs/agents/harnesses/think/index.mdx index 55c1ddab27c..701f56467fe 100644 --- a/src/content/docs/agents/harnesses/think/index.mdx +++ b/src/content/docs/agents/harnesses/think/index.mdx @@ -185,7 +185,7 @@ Think has several ways to start or continue a turn. Choose based on who starts t Use `saveMessages()` when the caller owns the trigger and can wait for the turn to finish. Use [`submitMessages()`](/agents/harnesses/think/programmatic-submissions/) when timeout ambiguity would make retries unsafe. -Use `chat()` for low-level parent-to-child streaming when your code owns forwarding, cancellation, and replay policy. Use [Agent tools](/agents/runtime/execution/agent-tools/) when a parent model or workflow delegates to a child agent and you want retained child runs, event replay, abort bridging, and UI drill-in. +Use `chat()` for low-level parent-to-child streaming when your code owns forwarding, cancellation, and replay policy. Use [Agents as tools](/agents/runtime/execution/agent-tools/) when a parent model or workflow delegates to a child agent and you want retained child runs, event replay, abort bridging, and UI drill-in. Use [`startFiber()`](/agents/runtime/execution/durable-execution/#startfiber) outside Think when the durable unit is an application job around a turn: accepting a webhook once, restoring a serialized channel or thread target, posting a visible reply, or recording app-level recovery policy. Think submissions own conversation admission and turn serialization; managed fibers own external job acceptance, idempotent side effects, and application recovery. diff --git a/src/content/docs/agents/harnesses/think/messengers.mdx b/src/content/docs/agents/harnesses/think/messengers.mdx index 601af04155f..ca99f07f7f6 100644 --- a/src/content/docs/agents/harnesses/think/messengers.mdx +++ b/src/content/docs/agents/harnesses/think/messengers.mdx @@ -196,4 +196,4 @@ Every custom messenger must provide `verifyWebhook` or explicitly use `verifyWeb The `examples/think-chat-sdk` example demonstrates the Think-native `getMessengers()` path with a small Vite dashboard that inspects the root Think conversation over the Agent WebSocket. -The `examples/chat-sdk-messenger` example demonstrates a larger manual ingress agent with an admin dashboard, menu handling, and application-owned reply fibers. Use `getMessengers()` for the simple Think-native path. Use the example when you need to own the Chat SDK runtime and control-plane UI yourself. Refer to [Chat SDK state](/agents/runtime/execution/chat-sdk/) for the underlying state adapter. +The `examples/chat-sdk-messenger` example demonstrates a larger manual ingress agent with an admin dashboard, menu handling, and application-owned reply fibers. Use `getMessengers()` for the simple Think-native path. Use the example when you need to own the Chat SDK runtime and control-plane UI yourself. Refer to [Chat SDK state](/agents/runtime/communication/chat-sdk/) for the underlying state adapter. diff --git a/src/content/docs/agents/harnesses/think/tools.mdx b/src/content/docs/agents/harnesses/think/tools.mdx index ea6a65e54f5..feb5df869c5 100644 --- a/src/content/docs/agents/harnesses/think/tools.mdx +++ b/src/content/docs/agents/harnesses/think/tools.mdx @@ -24,7 +24,7 @@ On every turn, Think merges tools from multiple sources. Later sources override 6. **MCP tools** — from connected MCP servers 7. **Client tools** — from the browser (refer to [Client tools](/agents/harnesses/think/client-tools/)) -Tools belong to the agent running the turn. For parent-child orchestration, use [Agent tools](/agents/runtime/execution/agent-tools/) instead of passing one-off tools through `chat()`. +Tools belong to the agent running the turn. For parent-child orchestration, use [Agents as tools](/agents/runtime/execution/agent-tools/) instead of passing one-off tools through `chat()`. ## Built-in workspace tools diff --git a/src/content/docs/agents/runtime/agents-api.mdx b/src/content/docs/agents/runtime/agents-api.mdx index ad7907fd3d8..8347424197c 100644 --- a/src/content/docs/agents/runtime/agents-api.mdx +++ b/src/content/docs/agents/runtime/agents-api.mdx @@ -63,16 +63,16 @@ flowchart TD E --> F["onClose"] ``` -| Method | When it runs | -| --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Method | When it runs | +| --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `onStart(props?)` | When the instance starts, or wakes from hibernation. Receives optional [initialization props](/agents/runtime/communication/routing/#props) passed via `getAgentByName` or `routeAgentRequest`. | -| `onRequest(request)` | For each HTTP request to the instance | -| `onConnect(connection, ctx)` | When a WebSocket connection is established | -| `onMessage(connection, message)` | For each WebSocket message received | -| `onError(connection, error)` | When a WebSocket error occurs | -| `onClose(connection, code, reason, wasClean)` | When a WebSocket connection closes | -| `onEmail(email)` | When an email is routed to the instance | -| `onStateChanged(state, source)` | When state changes (from server or client) | +| `onRequest(request)` | For each HTTP request to the instance | +| `onConnect(connection, ctx)` | When a WebSocket connection is established | +| `onMessage(connection, message)` | For each WebSocket message received | +| `onError(connection, error)` | When a WebSocket error occurs | +| `onClose(connection, code, reason, wasClean)` | When a WebSocket connection closes | +| `onEmail(email)` | When an email is routed to the instance | +| `onStateChanged(state, source)` | When state changes (from server or client) | ## Core properties @@ -85,28 +85,28 @@ flowchart TD ## Server-side API reference -| Feature | Methods | Documentation | -| --------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------- | -| **State** | `setState()`, `onStateChanged()`, `initialState` | [Store and sync state](/agents/runtime/lifecycle/state/) | -| **Callable methods** | `@callable()` decorator | [Callable methods](/agents/runtime/lifecycle/callable-methods/) | -| **Scheduling** | `schedule()`, `scheduleEvery()`, `getScheduleById()`, `listSchedules()` | [Schedule tasks](/agents/runtime/execution/schedule-tasks/) | -| **Durable execution** | `runFiber()`, `startFiber()`, `stash()`, `onFiberRecovered()`, `keepAlive()`, `keepAliveWhile()` | [Durable execution](/agents/runtime/execution/durable-execution/) | -| **Queue** | `queue()`, `dequeue()`, `dequeueAll()`, `getQueue()` | [Queue tasks](/agents/runtime/execution/queue-tasks/) | -| **WebSockets** | `onConnect()`, `onMessage()`, `onClose()`, `broadcast()` | [WebSockets](/agents/runtime/communication/websockets/) | -| **HTTP/SSE** | `onRequest()` | [HTTP and SSE](/agents/runtime/communication/http-sse/) | -| **Email** | `onEmail()`, `replyToEmail()` | [Email routing](/agents/communication-channels/email/) | -| **Workflows** | `runWorkflow()`, `waitForApproval()` | [Run Workflows](/agents/runtime/execution/run-workflows/) | -| **MCP Client** | `addMcpServer()`, `removeMcpServer()`, `getMcpServers()` | [MCP Client API](/agents/model-context-protocol/apis/client-api/) | -| **AI Models** | Workers AI, OpenAI, Anthropic bindings | [Using AI models](/agents/runtime/operations/using-ai-models/) | -| **Protocol messages** | `shouldSendProtocolMessages()`, `isConnectionProtocolEnabled()` | [Protocol messages](/agents/runtime/communication/protocol-messages/) | -| **Context** | `getCurrentAgent()` | [getCurrentAgent()](/agents/runtime/lifecycle/get-current-agent/) | -| **Observability** | `subscribe()`, diagnostics channels, Tail Workers | [Observability](/agents/runtime/operations/observability/) | -| **Sub-agents** | `subAgent()`, `abortSubAgent()`, `deleteSubAgent()` | [Sub-agents](/agents/runtime/execution/sub-agents/) | -| **Agent tools** | `runAgentTool()`, `clearAgentToolRuns()`, `hasAgentToolRun()` | [Agent tools](/agents/runtime/execution/agent-tools/) | -| **Agent Skills** | `skills` registry, bundled skill sources, script runners | [Agent Skills](/agents/runtime/execution/agent-skills/) | -| **Sessions** | `Session.create()`, context blocks, compaction, search | [Sessions](/agents/runtime/lifecycle/sessions/) | -| **Think** | `Think` base class, workspace tools, lifecycle hooks, extensions | [Think](/agents/harnesses/think/) | -| **Chat SDK** | `createChatSdkState()`, `ChatSdkStateAgent` | [Chat SDK](/agents/runtime/execution/chat-sdk/) | +| Feature | Methods | Documentation | +| --------------------- | ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------- | +| **State** | `setState()`, `onStateChanged()`, `initialState` | [Store and sync state](/agents/runtime/lifecycle/state/) | +| **Callable methods** | `@callable()` decorator | [Callable methods](/agents/runtime/lifecycle/callable-methods/) | +| **Scheduling** | `schedule()`, `scheduleEvery()`, `getScheduleById()`, `listSchedules()` | [Schedule tasks](/agents/runtime/execution/schedule-tasks/) | +| **Durable execution** | `runFiber()`, `startFiber()`, `stash()`, `onFiberRecovered()`, `keepAlive()`, `keepAliveWhile()` | [Durable execution](/agents/runtime/execution/durable-execution/) | +| **Queue** | `queue()`, `dequeue()`, `dequeueAll()`, `getQueue()` | [Queue tasks](/agents/runtime/execution/queue-tasks/) | +| **WebSockets** | `onConnect()`, `onMessage()`, `onClose()`, `broadcast()` | [WebSockets](/agents/runtime/communication/websockets/) | +| **HTTP/SSE** | `onRequest()` | [HTTP and SSE](/agents/runtime/communication/http-sse/) | +| **Email** | `onEmail()`, `replyToEmail()` | [Email routing](/agents/communication-channels/email/) | +| **Workflows** | `runWorkflow()`, `waitForApproval()` | [Run Workflows](/agents/runtime/execution/run-workflows/) | +| **MCP Client** | `addMcpServer()`, `removeMcpServer()`, `getMcpServers()` | [MCP Client API](/agents/model-context-protocol/apis/client-api/) | +| **AI Models** | Workers AI, OpenAI, Anthropic bindings | [Using AI models](/agents/runtime/operations/using-ai-models/) | +| **Protocol messages** | `shouldSendProtocolMessages()`, `isConnectionProtocolEnabled()` | [Protocol messages](/agents/runtime/communication/protocol-messages/) | +| **Context** | `getCurrentAgent()` | [getCurrentAgent()](/agents/runtime/lifecycle/get-current-agent/) | +| **Observability** | `subscribe()`, diagnostics channels, Tail Workers | [Observability](/agents/runtime/operations/observability/) | +| **Sub-agents** | `subAgent()`, `abortSubAgent()`, `deleteSubAgent()` | [Sub-agents](/agents/runtime/execution/sub-agents/) | +| **Agents as tools** | `runAgentTool()`, `clearAgentToolRuns()`, `hasAgentToolRun()` | [Agents as tools](/agents/runtime/execution/agent-tools/) | +| **Agent Skills** | `skills` registry, bundled skill sources, script runners | [Agent Skills](/agents/runtime/execution/agent-skills/) | +| **Sessions** | `Session.create()`, context blocks, compaction, search | [Sessions](/agents/runtime/lifecycle/sessions/) | +| **Think** | `Think` base class, workspace tools, lifecycle hooks, extensions | [Think](/agents/harnesses/think/) | +| **Chat SDK** | `createChatSdkState()`, `ChatSdkStateAgent` | [Chat SDK](/agents/runtime/communication/chat-sdk/) | ## SQL API @@ -127,13 +127,13 @@ For state that needs to sync with clients, use the [State API](/agents/runtime/l ## Client-side API reference -| Feature | Methods | Documentation | -| --------------------- | ---------------------- | --------------------------------------------------------------------------------- | -| **WebSocket client** | `AgentClient` | [Client SDK](/agents/communication-channels/chat/client-sdk/) | -| **HTTP client** | `agentFetch()` | [Client SDK](/agents/communication-channels/chat/client-sdk/#http-requests-with-agentfetch) | -| **React hook** | `useAgent()` | [Client SDK](/agents/communication-channels/chat/client-sdk/#react) | -| **Chat hook** | `useAgentChat()` | [Client SDK](/agents/communication-channels/chat/client-sdk/) | -| **Agent tool events** | `useAgentToolEvents()` | [Agent tools](/agents/runtime/execution/agent-tools/#render-child-timelines-in-react) | +| Feature | Methods | Documentation | +| --------------------- | ---------------------- | ------------------------------------------------------------------------------------------- | +| **WebSocket client** | `AgentClient` | [Client SDK](/agents/communication-channels/chat/client-sdk/) | +| **HTTP client** | `agentFetch()` | [Client SDK](/agents/communication-channels/chat/client-sdk/#http-requests-with-agentfetch) | +| **React hook** | `useAgent()` | [Client SDK](/agents/communication-channels/chat/client-sdk/#react) | +| **Chat hook** | `useAgentChat()` | [Client SDK](/agents/communication-channels/chat/client-sdk/) | +| **Agent tool events** | `useAgentToolEvents()` | [Agents as tools](/agents/runtime/execution/agent-tools/#render-child-timelines-in-react) | Module-level helper exports include `agentTool()` from `agents/agent-tools`, which converts a Think or `AIChatAgent` subclass into an AI SDK tool definition. diff --git a/src/content/docs/agents/runtime/execution/chat-sdk.mdx b/src/content/docs/agents/runtime/communication/chat-sdk.mdx similarity index 99% rename from src/content/docs/agents/runtime/execution/chat-sdk.mdx rename to src/content/docs/agents/runtime/communication/chat-sdk.mdx index 9cfbb129eec..cbbfa760e8e 100644 --- a/src/content/docs/agents/runtime/execution/chat-sdk.mdx +++ b/src/content/docs/agents/runtime/communication/chat-sdk.mdx @@ -3,7 +3,7 @@ title: Chat SDK description: Integrate Chat SDK with Agents, including durable state for subscriptions, locks, queues, and message history. pcx_content_type: reference sidebar: - order: 23 + order: 10 products: - agents --- diff --git a/src/content/docs/agents/runtime/execution/agent-tools.mdx b/src/content/docs/agents/runtime/execution/agent-tools.mdx index f79da2f7b1b..ada19fb9bd0 100644 --- a/src/content/docs/agents/runtime/execution/agent-tools.mdx +++ b/src/content/docs/agents/runtime/execution/agent-tools.mdx @@ -1,5 +1,5 @@ --- -title: Agent tools +title: Agents as tools description: Run Think and AIChatAgent sub-agents as retained, streaming tools from a parent agent. pcx_content_type: reference sidebar: @@ -10,11 +10,11 @@ products: import { TypeScriptExample, LinkCard } from "~/components"; -Agent tools let one chat agent dispatch another chat-capable sub-agent as part of its work. The child is a real sub-agent with its own Durable Object storage, messages, tools, resumable stream, and drill-in URL. The parent keeps a small run registry so clients can render the child timeline, replay it after refresh, and clean it up later. +Agents as tools let one chat agent dispatch another chat-capable sub-agent as part of its work. The child is a real sub-agent with its own Durable Object storage, messages, tools, resumable stream, and drill-in URL. The parent keeps a small run registry so clients can render the child timeline, replay it after refresh, and clean it up later. -Agent tools support `@cloudflare/think` agents and `AIChatAgent` subclasses. `AIChatAgent` children run headlessly through `saveMessages()`, so they should use server-side tools. Browser-provided client tools are not available during an agent-tool turn unless you model that interaction as server-side state or a separate parent-mediated workflow. +Agents as tools support `@cloudflare/think` agents and `AIChatAgent` subclasses. `AIChatAgent` children run headlessly through `saveMessages()`, so they should use server-side tools. Browser-provided client tools are not available during an agent-tool turn unless you model that interaction as server-side state or a separate parent-mediated workflow. -## Agent tools vs sub-agent RPC +## Agents as tools vs sub-agent RPC Use `subAgent(...).chat()` when parent code needs direct streaming RPC to a specific child and your code owns forwarding, cancellation, and replay policy. @@ -207,7 +207,7 @@ Imperative runs without a parent tool call are available as `agentTools.unboundR ## Drill in and gate access -Agent tools are normal sub-agents. Connect to a retained child through the parent route: +Agents as tools are normal sub-agents. Connect to a retained child through the parent route: diff --git a/src/content/docs/agents/runtime/execution/sub-agents.mdx b/src/content/docs/agents/runtime/execution/sub-agents.mdx index 1b173a5d06f..9dd53b2f787 100644 --- a/src/content/docs/agents/runtime/execution/sub-agents.mdx +++ b/src/content/docs/agents/runtime/execution/sub-agents.mdx @@ -16,7 +16,7 @@ Spawn child agents as co-located Durable Objects with their own isolated SQLite Use sub-agents when a single user or entity owns an open-ended set of long-lived agents, such as chats, documents, sessions, shards, or projects. Each sub-agent runs in parallel with its own state while the parent coordinates discovery, access control, and lifecycle. -If you want a parent chat agent to dispatch another chat-capable agent during a single turn and render that child's progress inline, use [Agent tools](/agents/runtime/execution/agent-tools/). Agent tools are built on sub-agents, but add a parent-side run registry, streaming `agent-tool-event` frames, replay, cancellation, and cleanup. +If you want a parent chat agent to dispatch another chat-capable agent during a single turn and render that child's progress inline, use [Agents as tools](/agents/runtime/execution/agent-tools/). Agents as tools are built on sub-agents, but add a parent-side run registry, streaming `agent-tool-event` frames, replay, cancellation, and cleanup. ## Quick start @@ -519,5 +519,5 @@ Calling `this.destroy()` inside a sub-agent delegates cleanup to the parent. The - [Think](/agents/harnesses/think/) — `chat()` method for streaming AI turns through sub-agents - [Long-running agents](/agents/concepts/agentic-patterns/long-running-agents/) — sub-agent delegation in the context of multi-week agent lifetimes - [Callable methods](/agents/runtime/lifecycle/callable-methods/) — RPC via `@callable` and service bindings -- [Agent tools](/agents/runtime/execution/agent-tools/) — run Think or `AIChatAgent` sub-agents as retained, streaming tools -- [Schedule tasks](/agents/runtime/execution/schedule-tasks/) — scheduling primitives for top-level agents and sub-agents \ No newline at end of file +- [Agents as tools](/agents/runtime/execution/agent-tools/) — run Think or `AIChatAgent` sub-agents as retained, streaming tools +- [Schedule tasks](/agents/runtime/execution/schedule-tasks/) — scheduling primitives for top-level agents and sub-agents diff --git a/src/content/partials/agents/common-integration-patterns.mdx b/src/content/partials/agents/common-integration-patterns.mdx new file mode 100644 index 00000000000..c596443ec91 --- /dev/null +++ b/src/content/partials/agents/common-integration-patterns.mdx @@ -0,0 +1,132 @@ +--- +{} +--- + +import { TypeScriptExample, WranglerConfig } from "~/components"; + +## Common integration patterns + +### Agents behind authentication + +Check auth before routing to agents: + + + +```ts +export default { + async fetch(request: Request, env: Env) { + // Check auth for agent routes + if (request.url.includes("/agents/")) { + const authResult = await checkAuth(request, env); + if (!authResult.valid) { + return new Response("Unauthorized", { status: 401 }); + } + } + + const agentResponse = await routeAgentRequest(request, env); + if (agentResponse) return agentResponse; + + // ... rest of routing + }, +} satisfies ExportedHandler; +``` + + + +### Custom agent path prefix + +By default, agents are routed at `/agents/{agent-name}/{instance-name}`. You can customize this: + + + +```ts +import { routeAgentRequest } from "agents"; + +const agentResponse = await routeAgentRequest(request, env, { + prefix: "/api/agents", // Now routes at /api/agents/{agent-name}/{instance-name} +}); +``` + + + +Refer to [Routing](/agents/runtime/communication/routing/) for more options including CORS, custom instance naming, and location hints. + +### Accessing agents from server code + +You can interact with agents directly from your Worker code: + + + +```ts +import { getAgentByName } from "agents"; + +export default { + async fetch(request: Request, env: Env) { + if (request.url.endsWith("/api/increment")) { + // Get a specific agent instance + const counter = await getAgentByName(env.CounterAgent, "shared-counter"); + const newCount = await counter.increment(); + return Response.json({ count: newCount }); + } + // ... + }, +} satisfies ExportedHandler; +``` + + + +### Adding multiple agents + +Add more agents by extending the configuration: + + + +```ts +// src/agents/chat.ts +export class Chat extends Agent { + // ... +} + +// src/agents/scheduler.ts +export class Scheduler extends Agent { + // ... +} +``` + + + +Update the Wrangler configuration file: + + + +```toml +[[durable_objects.bindings]] +name = "CounterAgent" +class_name = "CounterAgent" + +[[durable_objects.bindings]] +name = "Chat" +class_name = "Chat" + +[[durable_objects.bindings]] +name = "Scheduler" +class_name = "Scheduler" + +[[migrations]] +tag = "v1" +new_sqlite_classes = ["CounterAgent", "Chat", "Scheduler"] +``` + + + +Export all agents from your entry point: + + + +```ts +export { CounterAgent } from "./agents/counter"; +export { Chat } from "./agents/chat"; +export { Scheduler } from "./agents/scheduler"; +``` + + diff --git a/src/content/partials/agents/next-steps.mdx b/src/content/partials/agents/next-steps.mdx new file mode 100644 index 00000000000..5b5c8fba6aa --- /dev/null +++ b/src/content/partials/agents/next-steps.mdx @@ -0,0 +1,57 @@ +--- +{} +--- + +import { LinkCard } from "~/components"; + +## Next steps + +Now that you have a working agent, explore these topics: + +### Common next steps + +| Learn how to | Refer to | +| ------------------------ | --------------------------------------------------------- | +| Add AI/LLM capabilities | [Using AI models](/agents/runtime/operations/using-ai-models/) | +| Expose tools via MCP | [MCP servers](/agents/model-context-protocol/apis/agent-api/) | +| Run background tasks | [Schedule tasks](/agents/runtime/execution/schedule-tasks/) | +| Handle emails | [Email routing](/agents/communication-channels/email/) | +| Use Cloudflare Workflows | [Run Workflows](/agents/runtime/execution/run-workflows/) | + +### Explore more + + + + + + + + + + + + \ No newline at end of file diff --git a/src/content/partials/agents/troubleshooting.mdx b/src/content/partials/agents/troubleshooting.mdx new file mode 100644 index 00000000000..de27431633f --- /dev/null +++ b/src/content/partials/agents/troubleshooting.mdx @@ -0,0 +1,111 @@ +--- +{} +--- + +import { TypeScriptExample, WranglerConfig } from "~/components"; + +## Troubleshooting + +### Agent not found, or 404 errors + +1. **Check the export** - Agent class must be exported from your main entry point. +2. **Check the binding** - `class_name` in the Wrangler configuration file must exactly match the exported class name. +3. **Check the route** - Default route is `/agents/{'{agent-name}'}/{'{instance-name}'}`. Agent name in client matches the class name (case-insensitive). + +### No such Durable Object class error + +Add the migration to the Wrangler configuration file: + + + +```toml +[[migrations]] +tag = "v1" +new_sqlite_classes = ["YourAgentClass"] +``` + + + +### WebSocket connection fails + +Ensure your routing passes the response unchanged: + + + +```ts +// Correct - return the response directly +const agentResponse = await routeAgentRequest(request, env); +if (agentResponse) return agentResponse; + +// Wrong - this breaks WebSocket connections +if (agentResponse) return new Response(agentResponse.body); +``` + + + +### State not persisting + +Check that: + +1. You are calling `this.setState()`, not mutating `this.state` directly. +2. The agent class is in `new_sqlite_classes` in migrations. +3. You are connecting to the same agent instance name. +4. The `onStateUpdate` callback is wired up in your client. +5. WebSocket connection is established (check browser dev tools). + +### "Method X is not callable" errors + +Make sure your methods are decorated with `@callable()`: + + + +```ts +import { Agent, callable } from "agents"; + +export class MyAgent extends Agent { + @callable() + increment() { + // ... + } +} +``` + + + +### Type errors with `agent.stub` + +Add the agent and state type parameters: + + + +```ts +import { useAgent } from "agents/react"; +import type { CounterAgent, CounterState } from "./server"; + +// Pass the agent and state types to useAgent +const agent = useAgent({ + agent: "CounterAgent", + onStateUpdate: (state) => setCount(state.count), +}); + +// Now agent.stub is fully typed +agent.stub.increment(); +``` + + + +### `SyntaxError: Invalid or unexpected token` with `@callable()` + +If your dev server fails with `SyntaxError: Invalid or unexpected token`, set `"target": "ES2021"` in your `tsconfig.json`. This ensures that Vite's esbuild transpiler downlevels TC39 decorators instead of passing them through as native syntax. + +```json +{ + "compilerOptions": { + "target": "ES2021" + } +} +``` + +:::caution +Do not set `"experimentalDecorators": true` in your `tsconfig.json`. The Agents SDK uses [TC39 standard decorators](https://github.com/tc39/proposal-decorators), not TypeScript legacy decorators. Enabling `experimentalDecorators` applies an incompatible transform that silently breaks `@callable()` at runtime. +::: \ No newline at end of file diff --git a/src/content/partials/agents/what-just-happened.mdx b/src/content/partials/agents/what-just-happened.mdx new file mode 100644 index 00000000000..dbcd1da0988 --- /dev/null +++ b/src/content/partials/agents/what-just-happened.mdx @@ -0,0 +1,28 @@ +--- +{} +--- + +## How it works + +When you clicked the button: + +1. **Client** called `agent.stub.increment()` over WebSocket +2. **Agent** ran `increment()`, updated state with `setState()` +3. **State** persisted to SQLite automatically +4. **Broadcast** sent to all connected clients +5. **React** updated via `onStateUpdate` + +```mermaid +flowchart LR + A["Browser
(React)"] <-->|WebSocket| B["Agent
(Counter)"] + B --> C["SQLite
(State)"] +``` + +### Key concepts + +| Concept | What it means | +| -------------------- | ----------------------------------------------------------------------------------------------------- | +| **Agent instance** | Each unique name gets its own agent. `CounterAgent:user-123` is separate from `CounterAgent:user-456` | +| **Persistent state** | State survives restarts, deploys, and hibernation. It is stored in SQLite | +| **Real-time sync** | All clients connected to the same agent receive state updates instantly | +| **Hibernation** | When no clients are connected, the agent hibernates (no cost). It wakes on the next request |