diff --git a/src/content/changelog/agents/2025-03-18-npm-i-agents.mdx b/src/content/changelog/agents/2025-03-18-npm-i-agents.mdx
index eaead553cf1..3bd3242dead 100644
--- a/src/content/changelog/agents/2025-03-18-npm-i-agents.mdx
+++ b/src/content/changelog/agents/2025-03-18-npm-i-agents.mdx
@@ -67,7 +67,7 @@ export default {
#### Call Agent methods from your client code
-We've added a new [`@unstable_callable()`](/agents/runtime/agents-api/) decorator for defining methods that can be called directly from clients. This allows you call methods from within your client code: you can call methods (with arguments) and get native JavaScript objects back.
+We've added a new [`@unstable_callable()`](/agents/agent-class/) decorator for defining methods that can be called directly from clients. This allows you call methods from within your client code: you can call methods (with arguments) and get native JavaScript objects back.
@@ -127,8 +127,8 @@ You can use the starter as a template for your own Agents projects: open up `src
We've heard your feedback on the Agents SDK documentation, and we're shipping more API reference material and usage examples, including:
- Expanded [API reference documentation](/agents/runtime/), covering the methods and properties exposed by the Agents SDK, as well as more usage examples.
-- More [Client API](/agents/runtime/agents-api/#client-api) documentation that documents `useAgent`, `useAgentChat` and the new `@unstable_callable` RPC decorator exposed by the SDK.
-- New documentation on how to [route requests to agents](/agents/runtime/communication/routing/) and (optionally) authenticate clients before they connect to your Agents.
+- More [Client API](/agents/agent-class/#client-api) documentation that documents `useAgent`, `useAgentChat` and the new `@unstable_callable` RPC decorator exposed by the SDK.
+- New documentation on how to [route requests to agents](/agents/agent-class/routing/) and (optionally) authenticate clients before they connect to your Agents.
Note that the Agents SDK is continually growing: the type definitions included in the SDK will always include the latest APIs exposed by the `agents` package.
diff --git a/src/content/changelog/agents/2025-04-07-mcp-servers-agents-sdk-updates.mdx b/src/content/changelog/agents/2025-04-07-mcp-servers-agents-sdk-updates.mdx
index d2c50c347ac..1ef97d617d7 100644
--- a/src/content/changelog/agents/2025-04-07-mcp-servers-agents-sdk-updates.mdx
+++ b/src/content/changelog/agents/2025-04-07-mcp-servers-agents-sdk-updates.mdx
@@ -52,7 +52,7 @@ We've made a number of improvements to the [Agents SDK](/agents/), including:
- Support for building MCP servers with the new `MCPAgent` class.
- The ability to export the current agent, request and WebSocket connection context using `import { context } from "agents"`, allowing you to minimize or avoid direct dependency injection when calling tools.
- Fixed a bug that prevented query parameters from being sent to the Agent server from the `useAgent` React hook.
-- Automatically converting the `agent` name in `useAgent` or `useAgentChat` to kebab-case to ensure it matches the naming convention expected by [`routeAgentRequest`](/agents/runtime/communication/routing/).
+- Automatically converting the `agent` name in `useAgent` or `useAgentChat` to kebab-case to ensure it matches the naming convention expected by [`routeAgentRequest`](/agents/agent-class/routing/).
To install or update the Agents SDK, run `npm i agents@latest` in an existing project, or explore the `agents-starter` project:
diff --git a/src/content/changelog/agents/2025-11-26-agents-resumable-streaming.mdx b/src/content/changelog/agents/2025-11-26-agents-resumable-streaming.mdx
index 2416eca0da8..560b2eeac6d 100644
--- a/src/content/changelog/agents/2025-11-26-agents-resumable-streaming.mdx
+++ b/src/content/changelog/agents/2025-11-26-agents-resumable-streaming.mdx
@@ -24,7 +24,7 @@ Streams are maintained across page refreshes, broken connections, and syncing ac
### Other improvements
- Default JSON schema validator added to MCP client
-- [Schedules](https://developers.cloudflare.com/agents/runtime/execution/schedule-tasks/) can now safely destroy the agent
+- [Schedules](https://developers.cloudflare.com/agents/agent-class/schedule-tasks/) can now safely destroy the agent
### MCP client API improvements
@@ -60,7 +60,7 @@ MCP discovery fetches the available tools, prompts, and resources from an MCP se
### Bug fixes
-- Fixed a bug where [schedules](https://developers.cloudflare.com/agents/runtime/execution/schedule-tasks/) meant to fire immediately with this.schedule(0, ...) or `this.schedule(new Date(), ...)` would not fire
+- Fixed a bug where [schedules](https://developers.cloudflare.com/agents/agent-class/schedule-tasks/) meant to fire immediately with this.schedule(0, ...) or `this.schedule(new Date(), ...)` would not fire
- Fixed an issue where schedules that took longer than 30 seconds would occasionally time out
- Fixed SSE transport now properly forwards session IDs and request headers
- Fixed AI SDK stream events conversion to UIMessageStreamPart
diff --git a/src/content/changelog/agents/2026-02-03-agents-workflows-integration.mdx b/src/content/changelog/agents/2026-02-03-agents-workflows-integration.mdx
index 501893ccad7..7fd6157f3c0 100644
--- a/src/content/changelog/agents/2026-02-03-agents-workflows-integration.mdx
+++ b/src/content/changelog/agents/2026-02-03-agents-workflows-integration.mdx
@@ -175,4 +175,4 @@ To update to the latest version:
npm i agents@latest
```
-For the complete Workflows API reference and patterns, see [Run Workflows](/agents/runtime/execution/run-workflows/).
+For the complete Workflows API reference and patterns, see [Run Workflows](/agents/agent-class/run-workflows/).
diff --git a/src/content/changelog/agents/2026-02-17-agents-sdk-v0.5.0.mdx b/src/content/changelog/agents/2026-02-17-agents-sdk-v0.5.0.mdx
index 8ecfba40a59..01093055962 100644
--- a/src/content/changelog/agents/2026-02-17-agents-sdk-v0.5.0.mdx
+++ b/src/content/changelog/agents/2026-02-17-agents-sdk-v0.5.0.mdx
@@ -73,7 +73,7 @@ class MyAgent extends Agent {
Connections with protocol messages disabled still fully participate in RPC and regular messaging. Use `isConnectionProtocolEnabled(connection)` to check a connection's status at any time. The flag persists across Durable Object hibernation.
-See [Protocol messages](/agents/runtime/communication/protocol-messages/) for full documentation.
+See [Protocol messages](/agents/agent-class/protocol-messages/) for full documentation.
## `@cloudflare/ai-chat` v0.1.0
@@ -81,7 +81,7 @@ The first stable release of `@cloudflare/ai-chat` ships alongside this release w
Key new features:
-- **Data parts** — Attach typed JSON blobs (`data-*`) to messages alongside text. Supports reconciliation (type+id updates in-place), append, and transient parts (ephemeral via `onData` callback). See [Data parts](/agents/communication-channels/chat/chat-agents/#data-parts).
+- **Data parts** — Attach typed JSON blobs (`data-*`) to messages alongside text. Supports reconciliation (type+id updates in-place), append, and transient parts (ephemeral via `onData` callback). See [Data parts](/agents/agent-class/communication-channels/chat/chat-agents/#data-parts).
- **Tool approval persistence** — The `needsApproval` approval UI now survives page refresh and DO hibernation. The streaming message is persisted to SQLite when a tool enters `approval-requested` state.
- **`maxPersistedMessages`** — Cap SQLite message storage with automatic oldest-message deletion.
- **`body` option on `useAgentChat`** — Send custom data with every request (static or dynamic).
diff --git a/src/content/changelog/agents/2026-03-02-agents-sdk-v0.7.0.mdx b/src/content/changelog/agents/2026-03-02-agents-sdk-v0.7.0.mdx
index 1f34c6070b5..0c4750f999a 100644
--- a/src/content/changelog/agents/2026-03-02-agents-sdk-v0.7.0.mdx
+++ b/src/content/changelog/agents/2026-03-02-agents-sdk-v0.7.0.mdx
@@ -117,7 +117,7 @@ Key details:
`keepAlive()` is marked `@experimental` and may change between releases.
:::
-For the full API reference and when-to-use guidance, refer to [Schedule tasks — Keeping the agent alive](/agents/runtime/execution/schedule-tasks/#keeping-the-agent-alive).
+For the full API reference and when-to-use guidance, refer to [Schedule tasks — Keeping the agent alive](/agents/agent-class/schedule-tasks/#keeping-the-agent-alive).
## `waitForMcpConnections`
diff --git a/src/content/changelog/agents/2026-05-13-agents-sdk-v0.12.4.mdx b/src/content/changelog/agents/2026-05-13-agents-sdk-v0.12.4.mdx
index 7e7d969103c..6af6f993359 100644
--- a/src/content/changelog/agents/2026-05-13-agents-sdk-v0.12.4.mdx
+++ b/src/content/changelog/agents/2026-05-13-agents-sdk-v0.12.4.mdx
@@ -125,4 +125,4 @@ To update to the latest version:
npm i agents@latest @cloudflare/ai-chat@latest @cloudflare/think@latest @cloudflare/voice@latest
```
-Refer to the [Agents API reference](/agents/runtime/) and [Chat agents documentation](/agents/communication-channels/chat/chat-agents/) for more information.
+Refer to the [Agents API reference](/agents/runtime/) and [Chat agents documentation](/agents/agent-class/communication-channels/chat/chat-agents/) for more information.
diff --git a/src/content/changelog/agents/2026-06-02-agents-sdk-v0.14.0.mdx b/src/content/changelog/agents/2026-06-02-agents-sdk-v0.14.0.mdx
index 55a22f00a9d..78abe7f2913 100644
--- a/src/content/changelog/agents/2026-06-02-agents-sdk-v0.14.0.mdx
+++ b/src/content/changelog/agents/2026-06-02-agents-sdk-v0.14.0.mdx
@@ -160,4 +160,4 @@ To update to the latest version:
-Refer to the [Agents API reference](/agents/runtime/) and [Chat agents documentation](/agents/communication-channels/chat/chat-agents/) for more information.
+Refer to the [Agents API reference](/agents/runtime/) and [Chat agents documentation](/agents/agent-class/communication-channels/chat/chat-agents/) for more information.
diff --git a/src/content/changelog/agents/2026-06-16-agents-sdk-v0.16.1.mdx b/src/content/changelog/agents/2026-06-16-agents-sdk-v0.16.1.mdx
index daf03a0d6d5..3de364dca52 100644
--- a/src/content/changelog/agents/2026-06-16-agents-sdk-v0.16.1.mdx
+++ b/src/content/changelog/agents/2026-06-16-agents-sdk-v0.16.1.mdx
@@ -119,4 +119,4 @@ To update to the latest version:
-Refer to the [Code Mode documentation](/agents/tools/codemode/), [Browser tools documentation](/agents/tools/browser/), [Think tools documentation](/agents/harnesses/think/tools/), and [Voice documentation](/agents/communication-channels/voice/) for more information.
+Refer to the [Code Mode documentation](/agents/tools/codemode/), [Browser tools documentation](/agents/tools/browser/), [Think tools documentation](/agents/harnesses/think/tools/), and [Voice documentation](/agents/agent-class/communication-channels/voice/) for more information.
diff --git a/src/content/changelog/agents/2026-09-11-voice-diagnostics-turn-metrics.mdx b/src/content/changelog/agents/2026-09-11-voice-diagnostics-turn-metrics.mdx
index 57b60631e2b..01946d4df05 100644
--- a/src/content/changelog/agents/2026-09-11-voice-diagnostics-turn-metrics.mdx
+++ b/src/content/changelog/agents/2026-09-11-voice-diagnostics-turn-metrics.mdx
@@ -69,4 +69,4 @@ Install the release with a compatible Agents SDK version:
-Refer to the [Voice pipeline metrics](/agents/communication-channels/voice/#pipeline-metrics) and [Voice Agent example](https://github.com/cloudflare/agents/tree/main/examples/voice-agent) to get started.
+Refer to the [Voice pipeline metrics](/agents/agent-class/communication-channels/voice/#pipeline-metrics) and [Voice Agent example](https://github.com/cloudflare/agents/tree/main/examples/voice-agent) to get started.
diff --git a/src/content/docs/agents/runtime/execution/agent-tools.mdx b/src/content/docs/agents/agent-class/agent-tools.mdx
similarity index 99%
rename from src/content/docs/agents/runtime/execution/agent-tools.mdx
rename to src/content/docs/agents/agent-class/agent-tools.mdx
index 05fefda79f8..a60435e0d39 100644
--- a/src/content/docs/agents/runtime/execution/agent-tools.mdx
+++ b/src/content/docs/agents/agent-class/agent-tools.mdx
@@ -3,7 +3,7 @@ title: Agents as tools
description: Run Think and AIChatAgent sub-agents as retained, streaming tools from a parent agent.
pcx_content_type: reference
sidebar:
- order: 16
+ order: 15
products:
- agents
---
@@ -510,12 +510,12 @@ Raw `diagnostics_channel` subscribers should use the channel name `agents:agent_
diff --git a/src/content/docs/agents/runtime/lifecycle/callable-methods.mdx b/src/content/docs/agents/agent-class/callable-methods.mdx
similarity index 99%
rename from src/content/docs/agents/runtime/lifecycle/callable-methods.mdx
rename to src/content/docs/agents/agent-class/callable-methods.mdx
index 4a398a7706a..a4037002264 100644
--- a/src/content/docs/agents/runtime/lifecycle/callable-methods.mdx
+++ b/src/content/docs/agents/agent-class/callable-methods.mdx
@@ -3,7 +3,7 @@ title: Callable methods
description: Expose Agent methods to external clients over WebSocket RPC using the @callable() decorator.
pcx_content_type: concept
sidebar:
- order: 6
+ order: 3
products:
- agents
---
@@ -743,18 +743,18 @@ Do not set `"experimentalDecorators": true` in your `tsconfig.json`. The Agents
diff --git a/src/content/docs/agents/runtime/communication/chat-sdk.mdx b/src/content/docs/agents/agent-class/communication-channels/chat-sdk.mdx
similarity index 100%
rename from src/content/docs/agents/runtime/communication/chat-sdk.mdx
rename to src/content/docs/agents/agent-class/communication-channels/chat-sdk.mdx
diff --git a/src/content/docs/agents/communication-channels/chat/autonomous-responses.mdx b/src/content/docs/agents/agent-class/communication-channels/chat/autonomous-responses.mdx
similarity index 96%
rename from src/content/docs/agents/communication-channels/chat/autonomous-responses.mdx
rename to src/content/docs/agents/agent-class/communication-channels/chat/autonomous-responses.mdx
index 69eef060c82..52768f99752 100644
--- a/src/content/docs/agents/communication-channels/chat/autonomous-responses.mdx
+++ b/src/content/docs/agents/agent-class/communication-channels/chat/autonomous-responses.mdx
@@ -66,7 +66,7 @@ return Response.json({
`submitMessages()` stores pending work first and appends the messages to the conversation Session only when the submission starts executing. It accepts serializable `UIMessage[]` values, not the function form supported by `saveMessages((messages) => ...)`.
-Use [`startFiber()`](/agents/runtime/execution/durable-execution/#startfiber) outside Think when the durable unit is a surrounding application job, such as accepting a webhook once, restoring provider state, posting a visible reply, and recording recovery policy. `submitMessages()` owns Think's conversation admission; managed fibers own external side effects around that turn.
+Use [`startFiber()`](/agents/agent-class/durable-execution/#startfiber) outside Think when the durable unit is a surrounding application job, such as accepting a webhook once, restoring provider state, posting a visible reply, and recording recovery policy. `submitMessages()` owns Think's conversation admission; managed fibers own external side effects around that turn.
For the full Think API, refer to [`submitMessages()`](/agents/harnesses/think/programmatic-submissions/#submitmessages).
### When to use `saveMessages` vs `onChatResponse`
@@ -152,7 +152,7 @@ export class DigestAgent extends AIChatAgent {
-The function form of `saveMessages` — `saveMessages((messages) => [...])` — reads the latest persisted messages at execution time. This avoids stale baselines when multiple calls queue up (for example, rapid webhook arrivals). Refer to [Schedule tasks](/agents/runtime/execution/schedule-tasks/) for more on `schedule()` and cron syntax.
+The function form of `saveMessages` — `saveMessages((messages) => [...])` — reads the latest persisted messages at execution time. This avoids stale baselines when multiple calls queue up (for example, rapid webhook arrivals). Refer to [Schedule tasks](/agents/agent-class/schedule-tasks/) for more on `schedule()` and cron syntax.
### Processing a queue
@@ -528,24 +528,24 @@ Use `cancelFiber(fiberId)` when the durable unit was accepted with `startFiber()
diff --git a/src/content/docs/agents/communication-channels/chat/chat-agents.mdx b/src/content/docs/agents/agent-class/communication-channels/chat/chat-agents.mdx
similarity index 99%
rename from src/content/docs/agents/communication-channels/chat/chat-agents.mdx
rename to src/content/docs/agents/agent-class/communication-channels/chat/chat-agents.mdx
index 9d7cb6d392c..7484f56c58b 100644
--- a/src/content/docs/agents/communication-channels/chat/chat-agents.mdx
+++ b/src/content/docs/agents/agent-class/communication-channels/chat/chat-agents.mdx
@@ -579,7 +579,7 @@ Use `abortRequest()` when you know the request ID. Use `abortAllRequests()` for
Automatic stream resumption (the `resume` option on `useAgentChat`) is **client reconnect recovery** — it resumes an active stream when a client disconnects and reconnects. It does not cover Durable Object eviction. If the Worker process or Durable Object is evicted while the model call is in flight, the stream itself is gone. Durable chat recovery handles that case.
-A mid-stream Durable Object eviction permanently severs the LLM connection. Durable recovery wraps every `AIChatAgent` and [`Think`](/agents/harnesses/think/) chat turn in a [`runFiber()`](/agents/runtime/execution/durable-execution/). The fiber provides automatic `keepAlive` during streaming and a recovery hook on restart.
+A mid-stream Durable Object eviction permanently severs the LLM connection. Durable recovery wraps every `AIChatAgent` and [`Think`](/agents/harnesses/think/) chat turn in a [`runFiber()`](/agents/agent-class/durable-execution/). The fiber provides automatic `keepAlive` during streaming and a recovery hook on restart.
The fiber row survives in SQLite after an eviction. On the next activation, the framework detects the interrupted fiber. It reconstructs the partial response from buffered stream chunks and calls `onChatRecovery`.
@@ -833,7 +833,7 @@ While a turn is being recovered, the agent broadcasts a `cf_agent_chat_recoverin
Transcript repairs — healing orphaned tool calls (preserved as errored results rather than deleted, so the record survives and the model does not silently re-run the tool) and normalizing malformed or missing tool inputs before a provider call — are emitted on the `transcript` observability channel.
-For how chat recovery fits into the broader long-running agents story, refer to [Long-running agents: Recovering interrupted LLM streams](/agents/concepts/agentic-patterns/long-running-agents/#recovering-interrupted-llm-streams). For the underlying fiber API, refer to [Durable Execution](/agents/runtime/execution/durable-execution/).
+For how chat recovery fits into the broader long-running agents story, refer to [Long-running agents: Recovering interrupted LLM streams](/agents/concepts/agentic-patterns/long-running-agents/#recovering-interrupted-llm-streams). For the underlying fiber API, refer to [Durable Execution](/agents/agent-class/durable-execution/).
## Client API
@@ -1616,7 +1616,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 [Agents as 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/agent-class/sub-agents/). To run Think or `AIChatAgent` sub-agents as retained, streaming tools, refer to [Agents as tools](/agents/agent-class/agent-tools/).
:::
@@ -1778,7 +1778,7 @@ If you are upgrading from an earlier version, replace deprecated calls with thei
@@ -1796,7 +1796,7 @@ If you are upgrading from an earlier version, replace deprecated calls with thei
diff --git a/src/content/docs/agents/communication-channels/chat/client-sdk.mdx b/src/content/docs/agents/agent-class/communication-channels/chat/client-sdk.mdx
similarity index 97%
rename from src/content/docs/agents/communication-channels/chat/client-sdk.mdx
rename to src/content/docs/agents/agent-class/communication-channels/chat/client-sdk.mdx
index e061391c6b6..32340fabe03 100644
--- a/src/content/docs/agents/communication-channels/chat/client-sdk.mdx
+++ b/src/content/docs/agents/agent-class/communication-channels/chat/client-sdk.mdx
@@ -638,11 +638,11 @@ const transport = new WebSocketChatTransport({
-Refer to [Non-React clients](/agents/communication-channels/chat/chat-agents/#non-react-clients) for the behaviors this transport does and does not cover.
+Refer to [Non-React clients](/agents/agent-class/communication-channels/chat/chat-agents/#non-react-clients) for the behaviors this transport does and does not cover.
## Agent-tool events
-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.
+If your chat UI renders retained child runs from [Agents as tools](/agents/agent-class/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.
@@ -661,13 +661,13 @@ const agentTools = useAgentToolEvents({ agent });
diff --git a/src/content/docs/agents/communication-channels/chat/index.mdx b/src/content/docs/agents/agent-class/communication-channels/chat/index.mdx
similarity index 100%
rename from src/content/docs/agents/communication-channels/chat/index.mdx
rename to src/content/docs/agents/agent-class/communication-channels/chat/index.mdx
diff --git a/src/content/docs/agents/communication-channels/email.mdx b/src/content/docs/agents/agent-class/communication-channels/email.mdx
similarity index 100%
rename from src/content/docs/agents/communication-channels/email.mdx
rename to src/content/docs/agents/agent-class/communication-channels/email.mdx
diff --git a/src/content/docs/agents/communication-channels/index.mdx b/src/content/docs/agents/agent-class/communication-channels/index.mdx
similarity index 95%
rename from src/content/docs/agents/communication-channels/index.mdx
rename to src/content/docs/agents/agent-class/communication-channels/index.mdx
index 935c15443eb..c90e2973369 100644
--- a/src/content/docs/agents/communication-channels/index.mdx
+++ b/src/content/docs/agents/agent-class/communication-channels/index.mdx
@@ -2,7 +2,7 @@
title: Communication channels
pcx_content_type: navigation
sidebar:
- order: 3
+ order: 20
group:
hideIndex: true
---
diff --git a/src/content/docs/agents/communication-channels/slack.mdx b/src/content/docs/agents/agent-class/communication-channels/slack.mdx
similarity index 100%
rename from src/content/docs/agents/communication-channels/slack.mdx
rename to src/content/docs/agents/agent-class/communication-channels/slack.mdx
diff --git a/src/content/docs/agents/communication-channels/voice.mdx b/src/content/docs/agents/agent-class/communication-channels/voice.mdx
similarity index 100%
rename from src/content/docs/agents/communication-channels/voice.mdx
rename to src/content/docs/agents/agent-class/communication-channels/voice.mdx
diff --git a/src/content/docs/agents/communication-channels/webhooks/index.mdx b/src/content/docs/agents/agent-class/communication-channels/webhooks/index.mdx
similarity index 98%
rename from src/content/docs/agents/communication-channels/webhooks/index.mdx
rename to src/content/docs/agents/agent-class/communication-channels/webhooks/index.mdx
index ca43097d33f..794a82189bb 100644
--- a/src/content/docs/agents/communication-channels/webhooks/index.mdx
+++ b/src/content/docs/agents/agent-class/communication-channels/webhooks/index.mdx
@@ -518,7 +518,7 @@ return Response.json(
-If the webhook owns application side effects around a turn, such as restoring a provider thread and posting a visible reply, use [`startFiber()`](/agents/runtime/execution/durable-execution/#startfiber) around that job. Managed fibers retain status, dedupe provider retries, and let `onFiberRecovered()` or `resolveFiber()` record the app-level recovery outcome.
+If the webhook owns application side effects around a turn, such as restoring a provider thread and posting a visible reply, use [`startFiber()`](/agents/agent-class/durable-execution/#startfiber) around that job. Managed fibers retain status, dedupe provider retries, and let `onFiberRecovered()` or `resolveFiber()` record the app-level recovery outcome.
### Multi-provider routing
@@ -647,18 +647,18 @@ const secret = this.env.GITHUB_WEBHOOK_SECRET;
diff --git a/src/content/docs/agents/communication-channels/webhooks/push-notifications.mdx b/src/content/docs/agents/agent-class/communication-channels/webhooks/push-notifications.mdx
similarity index 98%
rename from src/content/docs/agents/communication-channels/webhooks/push-notifications.mdx
rename to src/content/docs/agents/agent-class/communication-channels/webhooks/push-notifications.mdx
index 30fd5bad7ab..5b79b0a329d 100644
--- a/src/content/docs/agents/communication-channels/webhooks/push-notifications.mdx
+++ b/src/content/docs/agents/agent-class/communication-channels/webhooks/push-notifications.mdx
@@ -405,18 +405,18 @@ try {
diff --git a/src/content/docs/agents/runtime/execution/durable-execution.mdx b/src/content/docs/agents/agent-class/durable-execution.mdx
similarity index 96%
rename from src/content/docs/agents/runtime/execution/durable-execution.mdx
rename to src/content/docs/agents/agent-class/durable-execution.mdx
index ee2ca4213a0..5d93b1eff4c 100644
--- a/src/content/docs/agents/runtime/execution/durable-execution.mdx
+++ b/src/content/docs/agents/agent-class/durable-execution.mdx
@@ -60,7 +60,7 @@ When eviction happens mid-work, the upstream HTTP connection (to an LLM provider
`keepAlive()` reduces the chance of eviction. `runFiber()` makes eviction survivable.
-For work that should run independently of the agent with per-step retries and multi-step orchestration, use [Workflows](/agents/runtime/execution/run-workflows/) instead. Fibers are for work that is part of the agent's own execution. Refer to [Long-running agents: Workflows vs agent-internal patterns](/agents/concepts/agentic-patterns/long-running-agents/#when-to-use-workflows-vs-agent-internal-patterns) for a comparison.
+For work that should run independently of the agent with per-step retries and multi-step orchestration, use [Workflows](/agents/agent-class/run-workflows/) instead. Fibers are for work that is part of the agent's own execution. Refer to [Long-running agents: Workflows vs agent-internal patterns](/agents/concepts/agentic-patterns/long-running-agents/#when-to-use-workflows-vs-agent-internal-patterns) for a comparison.
## keepAlive
@@ -495,7 +495,7 @@ Run an async function while keeping the DO alive. Heartbeat starts before `fn` a
## Related
- [Long-running agents](/agents/concepts/agentic-patterns/long-running-agents/) — how fibers compose with schedules, plans, and async operations
-- [Schedule tasks](/agents/runtime/execution/schedule-tasks/) — `keepAlive` details and the alarm system
-- [Sub-agents](/agents/runtime/execution/sub-agents/) — durable execution and schedules inside sub-agents
-- [Workflows](/agents/runtime/execution/run-workflows/) — durable multi-step execution outside the agent
-- [Chat agents](/agents/communication-channels/chat/chat-agents/) — `chatRecovery` and `onChatRecovery`
+- [Schedule tasks](/agents/agent-class/schedule-tasks/) — `keepAlive` details and the alarm system
+- [Sub-agents](/agents/agent-class/sub-agents/) — durable execution and schedules inside sub-agents
+- [Workflows](/agents/agent-class/run-workflows/) — durable multi-step execution outside the agent
+- [Chat agents](/agents/agent-class/communication-channels/chat/chat-agents/) — `chatRecovery` and `onChatRecovery`
diff --git a/src/content/docs/agents/runtime/communication/http-sse.mdx b/src/content/docs/agents/agent-class/http-sse.mdx
similarity index 92%
rename from src/content/docs/agents/runtime/communication/http-sse.mdx
rename to src/content/docs/agents/agent-class/http-sse.mdx
index 860023ce43c..f8a8daf0ced 100644
--- a/src/content/docs/agents/runtime/communication/http-sse.mdx
+++ b/src/content/docs/agents/agent-class/http-sse.mdx
@@ -3,7 +3,7 @@ title: HTTP and Server-Sent Events
description: Handle HTTP requests and stream responses with Server-Sent Events (SSE) from Cloudflare Agents.
pcx_content_type: reference
sidebar:
- order: 9
+ order: 5
products:
- agents
---
@@ -146,8 +146,8 @@ export class ChatAgent extends Agent {
SSE connections can be long-lived. Handle client disconnects gracefully:
-- **Persist progress** — Write to [agent state](/agents/runtime/lifecycle/state/) so clients can resume
-- **Use agent routing** — Clients can [reconnect to the same agent instance](/agents/runtime/communication/routing/) without session stores
+- **Persist progress** — Write to [agent state](/agents/agent-class/state/) so clients can resume
+- **Use agent routing** — Clients can [reconnect to the same agent instance](/agents/agent-class/routing/) without session stores
- **No timeout limits** — Cloudflare Workers have no effective limit on SSE response duration
@@ -190,19 +190,19 @@ export class ResumeAgent extends Agent {
**Recommendation:** Use WebSockets for interactive applications. Use SSE for streaming AI responses or server-push notifications.
-Refer to [WebSockets](/agents/runtime/communication/websockets/) for WebSocket documentation.
+Refer to [WebSockets](/agents/agent-class/websockets/) for WebSocket documentation.
## Next steps
diff --git a/src/content/docs/agents/runtime/agents-api.mdx b/src/content/docs/agents/agent-class/index.mdx
similarity index 84%
rename from src/content/docs/agents/runtime/agents-api.mdx
rename to src/content/docs/agents/agent-class/index.mdx
index bede1c78b73..bf96338ed85 100644
--- a/src/content/docs/agents/runtime/agents-api.mdx
+++ b/src/content/docs/agents/agent-class/index.mdx
@@ -1,9 +1,11 @@
---
-title: Agents API
-description: Reference for the Agent base class, lifecycle hooks, SQL storage, and error handling in the Agents SDK.
+title: Agent class
+description: Reference for the Agent base class, its hooks, SQL storage, and error handling in the Agents SDK.
pcx_content_type: reference
sidebar:
- order: 1
+ order: 8
+ group:
+ hideIndex: false
products:
- agents
---
@@ -65,7 +67,7 @@ flowchart TD
| 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`. |
+| `onStart(props?)` | When the instance starts, or wakes from hibernation. Receives optional [initialization props](/agents/agent-class/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 |
@@ -87,27 +89,27 @@ flowchart TD
| 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/) |
+| **State** | `setState()`, `onStateChanged()`, `initialState` | [Store and sync state](/agents/agent-class/state/) |
+| **Callable methods** | `@callable()` decorator | [Callable methods](/agents/agent-class/callable-methods/) |
+| **Scheduling** | `schedule()`, `scheduleEvery()`, `getScheduleById()`, `listSchedules()` | [Schedule tasks](/agents/agent-class/schedule-tasks/) |
+| **Durable execution** | `runFiber()`, `startFiber()`, `stash()`, `onFiberRecovered()`, `keepAlive()`, `keepAliveWhile()` | [Durable execution](/agents/agent-class/durable-execution/) |
+| **Queue** | `queue()`, `dequeue()`, `dequeueAll()`, `getQueue()` | [Queue tasks](/agents/agent-class/queue-tasks/) |
+| **WebSockets** | `onConnect()`, `onMessage()`, `onClose()`, `broadcast()` | [WebSockets](/agents/agent-class/websockets/) |
+| **HTTP/SSE** | `onRequest()` | [HTTP and SSE](/agents/agent-class/http-sse/) |
+| **Email** | `onEmail()`, `replyToEmail()` | [Email routing](/agents/agent-class/communication-channels/email/) |
+| **Workflows** | `runWorkflow()`, `waitForApproval()` | [Run Workflows](/agents/agent-class/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/) |
+| **AI Models** | Workers AI, OpenAI, Anthropic bindings | [Using AI models](/agents/agent-class/using-ai-models/) |
+| **Protocol messages** | `shouldSendProtocolMessages()`, `isConnectionProtocolEnabled()` | [Protocol messages](/agents/agent-class/protocol-messages/) |
| **Context** | `getCurrentAgent()` | [getCurrentAgent()](/agents/runtime/lifecycle/get-current-agent/) |
| **Tracing** | `wrapAISDK()` | [Tracing](/agents/runtime/operations/observability/tracing/) |
| **Diagnostics channels** | `subscribe()`, diagnostics channels | [Diagnostics channels](/agents/runtime/operations/observability/diagnostics-channels/) |
-| **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/) |
+| **Sub-agents** | `subAgent()`, `abortSubAgent()`, `deleteSubAgent()` | [Sub-agents](/agents/agent-class/sub-agents/) |
+| **Agents as tools** | `runAgentTool()`, `clearAgentToolRuns()`, `hasAgentToolRun()` | [Agents as tools](/agents/agent-class/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/) |
+| **Chat SDK** | `createChatSdkState()`, `ChatSdkStateAgent` | [Chat SDK](/agents/agent-class/communication-channels/chat-sdk/) |
## SQL API
@@ -124,17 +126,17 @@ this.sql`INSERT INTO users (id, name) VALUES (${id}, ${name})`;
const users = this.sql`SELECT * FROM users WHERE id = ${id}`;
```
-For state that needs to sync with clients, use the [State API](/agents/runtime/lifecycle/state/) instead.
+For state that needs to sync with clients, use the [State API](/agents/agent-class/state/) instead.
## 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()` | [Agents as tools](/agents/runtime/execution/agent-tools/#render-child-timelines-in-react) |
+| **WebSocket client** | `AgentClient` | [Client SDK](/agents/agent-class/communication-channels/chat/client-sdk/) |
+| **HTTP client** | `agentFetch()` | [Client SDK](/agents/agent-class/communication-channels/chat/client-sdk/#http-requests-with-agentfetch) |
+| **React hook** | `useAgent()` | [Client SDK](/agents/agent-class/communication-channels/chat/client-sdk/#react) |
+| **Chat hook** | `useAgentChat()` | [Client SDK](/agents/agent-class/communication-channels/chat/client-sdk/) |
+| **Agent tool events** | `useAgentToolEvents()` | [Agents as tools](/agents/agent-class/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.
@@ -204,7 +206,7 @@ export default {
} satisfies ExportedHandler;
```
-Refer to [Routing](/agents/runtime/communication/routing/) for custom paths, CORS, and instance naming patterns.
+Refer to [Routing](/agents/agent-class/routing/) for custom paths, CORS, and instance naming patterns.
## Next steps
@@ -222,7 +224,7 @@ Refer to [Routing](/agents/runtime/communication/routing/) for custom paths, COR
diff --git a/src/content/docs/agents/runtime/lifecycle/agent-class.mdx b/src/content/docs/agents/agent-class/internals.mdx
similarity index 97%
rename from src/content/docs/agents/runtime/lifecycle/agent-class.mdx
rename to src/content/docs/agents/agent-class/internals.mdx
index 21d34f97c5e..27bf5913a3a 100644
--- a/src/content/docs/agents/runtime/lifecycle/agent-class.mdx
+++ b/src/content/docs/agents/agent-class/internals.mdx
@@ -3,7 +3,7 @@ title: Agent class internals
description: Explore how the Agent class extends Durable Objects to provide state, WebSockets, scheduling, and RPC.
pcx_content_type: concept
sidebar:
- order: 5
+ order: 1
products:
- agents
---
@@ -210,7 +210,7 @@ class MyAgent extends Agent {
}
```
-State is stored in the `cf_agents_state` SQL table. State messages are sent with `type: "cf_agent_state"` (both from the client and the server). Since `agents` provides [JS and React clients](/agents/runtime/lifecycle/state/#synchronizing-state), real-time state updates are available out of the box.
+State is stored in the `cf_agents_state` SQL table. State messages are sent with `type: "cf_agent_state"` (both from the client and the server). Since `agents` provides [JS and React clients](/agents/agent-class/state/#synchronizing-state), real-time state updates are available out of the box.
### `this.sql`
@@ -507,7 +507,7 @@ class MyAgent extends Agent {
}
```
-`AIChatAgent` uses `keepAliveWhile` internally to keep the agent alive during streaming LLM responses. For more details, refer to [Schedule tasks — Keeping the agent alive](/agents/runtime/execution/schedule-tasks/#keeping-the-agent-alive).
+`AIChatAgent` uses `keepAliveWhile` internally to keep the agent alive during streaming LLM responses. For more details, refer to [Schedule tasks — Keeping the agent alive](/agents/agent-class/schedule-tasks/#keeping-the-agent-alive).
### Routing
@@ -525,7 +525,7 @@ return new Response("Not found", { status: 404 });
## Layer 3: `AIChatAgent`
-The [`AIChatAgent`](/agents/communication-channels/chat/chat-agents/) class from `@cloudflare/ai-chat` extends `Agent` with an opinionated layer for AI chat. It adds automatic message persistence to SQLite, resumable streaming, tool support (server-side, client-side, and human-in-the-loop), and a React hook (`useAgentChat`) for building chat UIs.
+The [`AIChatAgent`](/agents/agent-class/communication-channels/chat/chat-agents/) class from `@cloudflare/ai-chat` extends `Agent` with an opinionated layer for AI chat. It adds automatic message persistence to SQLite, resumable streaming, tool support (server-side, client-side, and human-in-the-loop), and a React hook (`useAgentChat`) for building chat UIs.
The full hierarchy is: **DurableObject** > **Server** > **Agent** > **AIChatAgent**.
diff --git a/src/content/docs/agents/runtime/communication/protocol-messages.mdx b/src/content/docs/agents/agent-class/protocol-messages.mdx
similarity index 89%
rename from src/content/docs/agents/runtime/communication/protocol-messages.mdx
rename to src/content/docs/agents/agent-class/protocol-messages.mdx
index 1097bf4606a..67ed33ead38 100644
--- a/src/content/docs/agents/runtime/communication/protocol-messages.mdx
+++ b/src/content/docs/agents/agent-class/protocol-messages.mdx
@@ -3,7 +3,7 @@ title: Protocol messages
description: Control the identity, state, and MCP protocol messages sent to WebSocket clients on Agent connect.
pcx_content_type: concept
sidebar:
- order: 27
+ order: 8
products:
- agents
---
@@ -24,7 +24,7 @@ On every new connection, the Agent sends three protocol messages:
State and MCP messages are also broadcast to all connections whenever they change.
-For most web clients this is fine — the [Client SDK](/agents/communication-channels/chat/client-sdk/) and `useAgent` hook consume these messages automatically. However, some clients cannot handle JSON text frames:
+For most web clients this is fine — the [Client SDK](/agents/agent-class/communication-channels/chat/client-sdk/) and `useAgent` hook consume these messages automatically. However, some clients cannot handle JSON text frames:
- **Binary-only clients** — MQTT devices, IoT sensors, custom binary protocols
- **Lightweight clients** — Embedded systems with minimal WebSocket stacks
@@ -173,7 +173,7 @@ An overridable hook that determines if a connection should receive protocol mess
Default: returns `true` (all connections receive protocol messages).
-This hook is evaluated once on connect. The result is persisted in the connection's WebSocket attachment and survives [hibernation](/agents/runtime/communication/websockets/#hibernation).
+This hook is evaluated once on connect. The result is persisted in the connection's WebSocket attachment and survives [hibernation](/agents/agent-class/websockets/#hibernation).
### `isConnectionProtocolEnabled`
@@ -188,18 +188,18 @@ Safe to call at any time, including after the agent wakes from hibernation.
## How it works
-Protocol status is stored as an internal flag in the connection's WebSocket attachment — the same mechanism used by [readonly connections](/agents/runtime/communication/readonly-connections/). This means:
+Protocol status is stored as an internal flag in the connection's WebSocket attachment — the same mechanism used by [readonly connections](/agents/agent-class/readonly-connections/). This means:
- **Survives hibernation** — the flag is serialized and restored when the agent wakes up
- **No cleanup needed** — connection state is automatically discarded when the connection closes
- **Zero overhead** — no database tables or queries, just the connection's built-in attachment
- **Safe from user code** — `connection.state` and `connection.setState()` never expose or overwrite the flag
-Unlike [readonly](/agents/runtime/communication/readonly-connections/) which can be toggled dynamically with `setConnectionReadonly()`, protocol status is set once on connect and cannot be changed afterward. To change a connection's protocol status, the client must disconnect and reconnect.
+Unlike [readonly](/agents/agent-class/readonly-connections/) which can be toggled dynamically with `setConnectionReadonly()`, protocol status is set once on connect and cannot be changed afterward. To change a connection's protocol status, the client must disconnect and reconnect.
## Related resources
-- [Readonly connections](/agents/runtime/communication/readonly-connections/)
-- [WebSockets](/agents/runtime/communication/websockets/)
-- [Store and sync state](/agents/runtime/lifecycle/state/)
+- [Readonly connections](/agents/agent-class/readonly-connections/)
+- [WebSockets](/agents/agent-class/websockets/)
+- [Store and sync state](/agents/agent-class/state/)
- [MCP Client API](/agents/model-context-protocol/apis/client-api/)
diff --git a/src/content/docs/agents/runtime/execution/queue-tasks.mdx b/src/content/docs/agents/agent-class/queue-tasks.mdx
similarity index 94%
rename from src/content/docs/agents/runtime/execution/queue-tasks.mdx
rename to src/content/docs/agents/agent-class/queue-tasks.mdx
index bedead7d6ac..ce6e7dfabc1 100644
--- a/src/content/docs/agents/runtime/execution/queue-tasks.mdx
+++ b/src/content/docs/agents/agent-class/queue-tasks.mdx
@@ -3,7 +3,7 @@ title: Queue tasks
description: Add background tasks to a built-in FIFO queue for asynchronous processing within Cloudflare Agents.
pcx_content_type: concept
sidebar:
- order: 12
+ order: 10
products:
- agents
---
@@ -47,7 +47,7 @@ async queue(
- `callback` - The name of the method to call when processing the task
- `payload` - Data to pass to the callback method
- `options` - Optional configuration:
- - `retry` - Retry options for the callback execution. If the callback throws, it is retried with exponential backoff. Refer to [Retries](/agents/runtime/execution/retries/) for details on `RetryOptions`
+ - `retry` - Retry options for the callback execution. If the callback throws, it is retried with exponential backoff. Refer to [Retries](/agents/agent-class/retries/) for details on `RetryOptions`
**Returns:** The unique ID of the queued task
@@ -338,7 +338,7 @@ class RobustAgent extends Agent {
-If no `retry` option is provided, the class-level defaults from `static options.retry` are used (3 attempts, 100ms base delay, 3s max delay). Refer to [Retries](/agents/runtime/execution/retries/) for full details.
+If no `retry` option is provided, the class-level defaults from `static options.retry` are used (3 attempts, 100ms base delay, 3s max delay). Refer to [Retries](/agents/agent-class/retries/) for full details.
## Best practices
@@ -353,7 +353,7 @@ If no `retry` option is provided, the class-level defaults from `static options.
The queue system works with other Agent SDK features:
- **State management**: Access agent state within queued callbacks.
-- **Scheduling**: Combine with [`schedule()`](/agents/runtime/execution/schedule-tasks/) for time-based queue processing.
+- **Scheduling**: Combine with [`schedule()`](/agents/agent-class/schedule-tasks/) for time-based queue processing.
- **Context**: Queued tasks maintain the original request context.
- **Database**: Uses the same database as other agent data.
@@ -365,7 +365,7 @@ The queue system works with other Agent SDK features:
## Queue vs Schedule
-Use **queue** when you want tasks to execute as soon as possible in order. Use [**schedule**](/agents/runtime/execution/schedule-tasks/) when you need tasks to run at specific times or on a recurring basis.
+Use **queue** when you want tasks to execute as soon as possible in order. Use [**schedule**](/agents/agent-class/schedule-tasks/) when you need tasks to run at specific times or on a recurring basis.
| Feature | Queue | Schedule |
| ---------------- | ------------------------ | --------------------------- |
@@ -377,18 +377,18 @@ Use **queue** when you want tasks to execute as soon as possible in order. Use [
diff --git a/src/content/docs/agents/runtime/communication/readonly-connections.mdx b/src/content/docs/agents/agent-class/readonly-connections.mdx
similarity index 97%
rename from src/content/docs/agents/runtime/communication/readonly-connections.mdx
rename to src/content/docs/agents/agent-class/readonly-connections.mdx
index 35e683a7e6f..7936d4b3a31 100644
--- a/src/content/docs/agents/runtime/communication/readonly-connections.mdx
+++ b/src/content/docs/agents/agent-class/readonly-connections.mdx
@@ -3,7 +3,7 @@ title: Readonly connections
description: Restrict WebSocket clients to view-only access so they receive state updates without modifying Agent state.
pcx_content_type: concept
sidebar:
- order: 28
+ order: 7
products:
- agents
---
@@ -455,7 +455,7 @@ function GameComponent() {
## How it works
-Readonly status is stored in the connection's WebSocket attachment, which persists through the WebSocket Hibernation API. The flag is namespaced internally so it cannot be accidentally overwritten by `connection.setState()`. The same mechanism is used by [protocol message control](/agents/runtime/communication/protocol-messages/) — both flag coexist safely in the attachment. This means:
+Readonly status is stored in the connection's WebSocket attachment, which persists through the WebSocket Hibernation API. The flag is namespaced internally so it cannot be accidentally overwritten by `connection.setState()`. The same mechanism is used by [protocol message control](/agents/agent-class/protocol-messages/) — both flag coexist safely in the attachment. This means:
- **Survives hibernation** — the flag is serialized and restored when the agent wakes up
- **No cleanup needed** — connection state is automatically discarded when the connection closes
@@ -627,7 +627,7 @@ export class AuditedAgent extends Agent {
## Related resources
-- [Store and sync state](/agents/runtime/lifecycle/state/)
-- [Protocol messages](/agents/runtime/communication/protocol-messages/) — suppress JSON protocol frames for binary-only clients (can be combined with readonly)
-- [WebSockets](/agents/runtime/communication/websockets/)
-- [Callable methods](/agents/runtime/lifecycle/callable-methods/)
+- [Store and sync state](/agents/agent-class/state/)
+- [Protocol messages](/agents/agent-class/protocol-messages/) — suppress JSON protocol frames for binary-only clients (can be combined with readonly)
+- [WebSockets](/agents/agent-class/websockets/)
+- [Callable methods](/agents/agent-class/callable-methods/)
diff --git a/src/content/docs/agents/runtime/execution/retries.mdx b/src/content/docs/agents/agent-class/retries.mdx
similarity index 98%
rename from src/content/docs/agents/runtime/execution/retries.mdx
rename to src/content/docs/agents/agent-class/retries.mdx
index 3cd350f13f8..b9ed5201c2f 100644
--- a/src/content/docs/agents/runtime/execution/retries.mdx
+++ b/src/content/docs/agents/agent-class/retries.mdx
@@ -3,7 +3,7 @@ title: Retries
description: Retry failed operations with exponential backoff and jitter using the built-in retry system in the Agents SDK.
pcx_content_type: reference
sidebar:
- order: 13
+ order: 12
products:
- agents
---
@@ -507,18 +507,18 @@ class MyAgent extends Agent {
diff --git a/src/content/docs/agents/runtime/communication/routing.mdx b/src/content/docs/agents/agent-class/routing.mdx
similarity index 99%
rename from src/content/docs/agents/runtime/communication/routing.mdx
rename to src/content/docs/agents/agent-class/routing.mdx
index 07756e8227b..231edc4fa6a 100644
--- a/src/content/docs/agents/runtime/communication/routing.mdx
+++ b/src/content/docs/agents/agent-class/routing.mdx
@@ -3,7 +3,7 @@ title: Routing
description: Route HTTP and WebSocket requests to Agents SDK instances using routeAgentRequest() and getAgentByName().
pcx_content_type: reference
sidebar:
- order: 2
+ order: 4
products:
- agents
---
@@ -100,7 +100,7 @@ buildAgentUrl("https://app.example.com", address, {
Inside an Agent, `this.selfPath` provides the required root-first identity. If the root Durable Object binding name differs from its class name, pass the binding name as `rootBinding`. The pathname supports both HTTP requests and WebSocket connections.
-For a custom route prefix, pass the same `prefix` to `buildAgentPath()` and `routeAgentRequest()`. Refer to [Sub-agents](/agents/runtime/execution/sub-agents/#direct-http-and-websocket-urls) for callback and webhook examples.
+For a custom route prefix, pass the same `prefix` to `buildAgentPath()` and `routeAgentRequest()`. Refer to [Sub-agents](/agents/agent-class/sub-agents/#direct-http-and-websocket-urls) for callback and webhook examples.
## Instance naming patterns
@@ -903,7 +903,7 @@ class SecureAgent extends Agent {
@@ -915,7 +915,7 @@ class SecureAgent extends Agent {
diff --git a/src/content/docs/agents/runtime/execution/run-workflows.mdx b/src/content/docs/agents/agent-class/run-workflows.mdx
similarity index 99%
rename from src/content/docs/agents/runtime/execution/run-workflows.mdx
rename to src/content/docs/agents/agent-class/run-workflows.mdx
index 69589f8ef15..04960a3c463 100644
--- a/src/content/docs/agents/runtime/execution/run-workflows.mdx
+++ b/src/content/docs/agents/agent-class/run-workflows.mdx
@@ -3,7 +3,7 @@ title: Run Workflows
description: Integrate Cloudflare Workflows with Agents for durable, multi-step background processing and failure recovery.
pcx_content_type: concept
sidebar:
- order: 14
+ order: 13
products:
- agents
---
@@ -902,13 +902,13 @@ Workflows cannot open WebSocket connections directly. Use `broadcastToClients()`
diff --git a/src/content/docs/agents/runtime/execution/schedule-tasks.mdx b/src/content/docs/agents/agent-class/schedule-tasks.mdx
similarity index 98%
rename from src/content/docs/agents/runtime/execution/schedule-tasks.mdx
rename to src/content/docs/agents/agent-class/schedule-tasks.mdx
index 94bb7158614..60d23b0f490 100644
--- a/src/content/docs/agents/runtime/execution/schedule-tasks.mdx
+++ b/src/content/docs/agents/agent-class/schedule-tasks.mdx
@@ -3,7 +3,7 @@ title: Schedule tasks
description: Schedule delayed, date-based, cron, and interval tasks on Agents with persistent SQLite-backed execution.
pcx_content_type: reference
sidebar:
- order: 10
+ order: 9
products:
- agents
---
@@ -704,7 +704,7 @@ Schedule a task for future execution.
- `when` - When to execute: `number` (seconds delay), `Date` (specific time), or `string` (cron expression)
- `callback` - Name of the method to call
- `payload` - Data to pass to the callback (must be JSON-serializable)
-- `options.retry` - Optional retry configuration. Refer to [Retries](/agents/runtime/execution/retries/) for details
+- `options.retry` - Optional retry configuration. Refer to [Retries](/agents/agent-class/retries/) for details
- `options.idempotent` - Deduplicate by callback + payload. Defaults to `true` for cron schedules, `false` for delayed and Date-based schedules
**Returns:** A `Schedule` object with the task details
@@ -752,7 +752,7 @@ Schedule a task to run repeatedly at a fixed interval.
- `intervalSeconds` - Number of seconds between executions (must be greater than 0)
- `callback` - Name of the method to call
- `payload` - Data to pass to the callback (must be JSON-serializable)
-- `options.retry` - Optional retry configuration. Refer to [Retries](/agents/runtime/execution/retries/) for details.
+- `options.retry` - Optional retry configuration. Refer to [Retries](/agents/agent-class/retries/) for details.
**Returns:** A `Schedule` object with `type: "interval"`
@@ -916,24 +916,24 @@ dispose2(); // Now the agent can go idle
diff --git a/src/content/docs/agents/runtime/lifecycle/state.mdx b/src/content/docs/agents/agent-class/state.mdx
similarity index 95%
rename from src/content/docs/agents/runtime/lifecycle/state.mdx
rename to src/content/docs/agents/agent-class/state.mdx
index 903b195108c..0936d83ba9e 100644
--- a/src/content/docs/agents/runtime/lifecycle/state.mdx
+++ b/src/content/docs/agents/agent-class/state.mdx
@@ -3,7 +3,7 @@ title: Store and sync state
description: Persist and sync Agent state across clients in real time using setState, SQL storage, and bidirectional updates.
pcx_content_type: concept
sidebar:
- order: 7
+ order: 2
products:
- agents
---
@@ -27,7 +27,7 @@ State within an Agent is:
Agent state is stored in a SQL database embedded within each individual Agent instance. You can interact with it using the higher-level `this.setState` API (recommended), which allows you to sync state and trigger events on state changes, or by directly querying the database with `this.sql`.
:::note[State vs Props]
-**State** is persistent data that survives restarts and syncs across clients. **[Props](/agents/runtime/communication/routing/#props)** are one-time initialization arguments passed when an agent is instantiated - use props for configuration that does not need to persist.
+**State** is persistent data that survives restarts and syncs across clients. **[Props](/agents/agent-class/routing/#props)** are one-time initialization arguments passed when an agent is instantiated - use props for configuration that does not need to persist.
:::
@@ -186,7 +186,7 @@ export class MinimalAgent extends Agent {
Use `setState()` to update state. This:
1. Saves to SQLite (persistent)
-2. Broadcasts to all connected clients (excluding connections where [`shouldSendProtocolMessages`](/agents/runtime/communication/protocol-messages/) returned `false`)
+2. Broadcasts to all connected clients (excluding connections where [`shouldSendProtocolMessages`](/agents/agent-class/protocol-messages/) returned `false`)
3. Triggers `onStateChanged()` (after broadcast; best-effort)
@@ -426,7 +426,7 @@ flowchart TD
## State from Workflows
-When using [Workflows](/agents/runtime/execution/run-workflows/), you can update agent state from workflow steps:
+When using [Workflows](/agents/agent-class/run-workflows/), you can update agent state from workflow steps:
@@ -642,7 +642,7 @@ onStateChanged(state: State, source: Connection | "server") {
## Use Agent state as model context
-You can combine the state and SQL APIs in your Agent with its ability to [call AI models](/agents/runtime/operations/using-ai-models/) to include historical context within your prompts to a model. Modern Large Language Models (LLMs) often have very large context windows (up to millions of tokens), which allows you to pull relevant context into your prompt directly.
+You can combine the state and SQL APIs in your Agent with its ability to [call AI models](/agents/agent-class/using-ai-models/) to include historical context within your prompts to a model. Modern Large Language Models (LLMs) often have very large context windows (up to millions of tokens), which allows you to pull relevant context into your prompt directly.
For example, you can use an Agent's built-in SQL database to pull history, query a model with it, and append to that history ahead of the next call to the model:
@@ -719,7 +719,7 @@ This works because each instance of an Agent has its own database, and the state
@@ -731,12 +731,12 @@ This works because each instance of an Agent has its own database, and the state
diff --git a/src/content/docs/agents/runtime/execution/sub-agents.mdx b/src/content/docs/agents/agent-class/sub-agents.mdx
similarity index 95%
rename from src/content/docs/agents/runtime/execution/sub-agents.mdx
rename to src/content/docs/agents/agent-class/sub-agents.mdx
index e17ef6cea9e..be931102bc2 100644
--- a/src/content/docs/agents/runtime/execution/sub-agents.mdx
+++ b/src/content/docs/agents/agent-class/sub-agents.mdx
@@ -5,7 +5,7 @@ description: Spawn child agents with isolated storage and typed RPC using subAge
tags:
- AI
sidebar:
- order: 5
+ order: 14
products:
- agents
---
@@ -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 [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.
+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/agent-class/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
@@ -561,7 +561,7 @@ Calling `this.destroy()` inside a sub-agent delegates cleanup to the parent. The
### Workflows from sub-agents
-Sub-agents can also start [Workflows](/agents/runtime/execution/run-workflows/) with `this.runWorkflow()`. Workflow tracking is local to the sub-agent's SQLite database, and `AgentWorkflow.agent` routes RPC, callbacks, state updates, and broadcasts back to the originating sub-agent. Parent agents do not automatically list or control child-started workflows.
+Sub-agents can also start [Workflows](/agents/agent-class/run-workflows/) with `this.runWorkflow()`. Workflow tracking is local to the sub-agent's SQLite database, and `AgentWorkflow.agent` routes RPC, callbacks, state updates, and broadcasts back to the originating sub-agent. Parent agents do not automatically list or control child-started workflows.
Because `SubAgentStub` only exposes user-defined child methods, add child wrapper methods for controls such as `getWorkflow()`, `approveWorkflow()`, or `terminateWorkflow()`, then call those wrappers through `await this.subAgent(Child, name)`. If you pass `runWorkflow(..., { agentBinding })` from a sub-agent, use the root Agent binding name, not a child binding name.
@@ -579,6 +579,6 @@ For sub-agent workflow origins, `AgentWorkflow.agent` is RPC-only. Use it to cal
- [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
-- [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
+- [Callable methods](/agents/agent-class/callable-methods/) — RPC via `@callable` and service bindings
+- [Agents as tools](/agents/agent-class/agent-tools/) — run Think or `AIChatAgent` sub-agents as retained, streaming tools
+- [Schedule tasks](/agents/agent-class/schedule-tasks/) — scheduling primitives for top-level agents and sub-agents
diff --git a/src/content/docs/agents/runtime/operations/using-ai-models.mdx b/src/content/docs/agents/agent-class/using-ai-models.mdx
similarity index 92%
rename from src/content/docs/agents/runtime/operations/using-ai-models.mdx
rename to src/content/docs/agents/agent-class/using-ai-models.mdx
index 9e5d8ff5066..411b32412c8 100644
--- a/src/content/docs/agents/runtime/operations/using-ai-models.mdx
+++ b/src/content/docs/agents/agent-class/using-ai-models.mdx
@@ -5,7 +5,7 @@ pcx_content_type: concept
tags:
- AI
sidebar:
- order: 19
+ order: 16
products:
- agents
---
@@ -26,13 +26,13 @@ The [AI SDK](https://ai-sdk.dev/docs/introduction) provides a unified interface
## Calling AI Models
-You can call models from any method within an Agent, including from HTTP requests using the [`onRequest`](/agents/runtime/agents-api/) handler, when a [scheduled task](/agents/runtime/execution/schedule-tasks/) runs, when handling a WebSocket message in the [`onMessage`](/agents/runtime/communication/websockets/) handler, or from any of your own methods.
+You can call models from any method within an Agent, including from HTTP requests using the [`onRequest`](/agents/agent-class/) handler, when a [scheduled task](/agents/agent-class/schedule-tasks/) runs, when handling a WebSocket message in the [`onMessage`](/agents/agent-class/websockets/) handler, or from any of your own methods.
Agents can call AI models on their own — autonomously — and can handle long-running responses that take minutes (or longer) to respond in full. If a client disconnects mid-stream, the Agent keeps running and can catch the client up when it reconnects.
### Streaming over WebSockets {/* long-running-model-requests */}
-Modern reasoning models can take some time to both generate a response _and_ stream the response back to the client. Instead of buffering the entire response, you can stream it back over [WebSockets](/agents/runtime/communication/websockets/).
+Modern reasoning models can take some time to both generate a response _and_ stream the response back to the client. Instead of buffering the entire response, you can stream it back over [WebSockets](/agents/agent-class/websockets/).
@@ -79,7 +79,7 @@ export class MyAgent extends Agent {
-You can also persist AI model responses back to [Agent state](/agents/runtime/lifecycle/state/) using `this.setState`. If a user disconnects, read the message history back and send it to the user when they reconnect.
+You can also persist AI model responses back to [Agent state](/agents/agent-class/state/) using `this.setState`. If a user disconnects, read the message history back and send it to the user when they reconnect.
## Workers AI
@@ -242,7 +242,7 @@ export class MyAgent extends Agent {
Agents can call models across any service that supports the OpenAI API. For example, you can use the OpenAI SDK to call one of [Google's Gemini models](https://ai.google.dev/gemini-api/docs/openai#node.js) directly from your Agent.
-Agents can stream responses back over HTTP using Server-Sent Events (SSE) from within an `onRequest` handler, or by using the native [WebSocket API](/agents/runtime/communication/websockets/) to stream responses back to a client.
+Agents can stream responses back over HTTP using Server-Sent Events (SSE) from within an `onRequest` handler, or by using the native [WebSocket API](/agents/agent-class/websockets/) to stream responses back to a client.
diff --git a/src/content/docs/agents/runtime/communication/websockets.mdx b/src/content/docs/agents/agent-class/websockets.mdx
similarity index 97%
rename from src/content/docs/agents/runtime/communication/websockets.mdx
rename to src/content/docs/agents/agent-class/websockets.mdx
index 077ab9f6687..8a71e008561 100644
--- a/src/content/docs/agents/runtime/communication/websockets.mdx
+++ b/src/content/docs/agents/agent-class/websockets.mdx
@@ -3,14 +3,14 @@ title: WebSockets
description: Handle real-time WebSocket connections, messages, broadcasts, and lifecycle hooks in the Agents SDK.
pcx_content_type: reference
sidebar:
- order: 8
+ order: 6
products:
- agents
---
import { TypeScriptExample, LinkCard } from "~/components";
-Agents support WebSocket connections for real-time, bi-directional communication. This page covers server-side WebSocket handling. For client-side connection, refer to the [Client SDK](/agents/communication-channels/chat/client-sdk/).
+Agents support WebSocket connections for real-time, bi-directional communication. This page covers server-side WebSocket handling. For client-side connection, refer to the [Client SDK](/agents/agent-class/communication-channels/chat/client-sdk/).
## Lifecycle hooks
@@ -224,7 +224,7 @@ export class ChatAgent extends Agent {
| `getConnection` | `(id: string) => Connection \| undefined` | Get connection by ID |
| `getConnectionTags` | `(connection, ctx) => string[]` | Override to tag connections |
| `broadcast` | `(message, without?: string[]) => void` | Send to all connections |
-| `isConnectionReadonly` | `(connection) => boolean` | Check if a connection is [readonly](/agents/runtime/communication/readonly-connections/) |
+| `isConnectionReadonly` | `(connection) => boolean` | Check if a connection is [readonly](/agents/agent-class/readonly-connections/) |
| `isConnectionProtocolEnabled` | `(connection) => boolean` | Check if protocol messages are enabled for this connection |
## Handling binary data
@@ -255,7 +255,7 @@ export class FileAgent extends Agent {
:::note
-Agents automatically send JSON text frames (identity, state, MCP servers) to every connection. If your client only handles binary data and cannot process these frames, use [`shouldSendProtocolMessages`](/agents/runtime/communication/protocol-messages/) to suppress them.
+Agents automatically send JSON text frames (identity, state, MCP servers) to every connection. If your client only handles binary data and cannot process these frames, use [`shouldSendProtocolMessages`](/agents/agent-class/protocol-messages/) to suppress them.
:::
## Error and close handling
@@ -554,19 +554,19 @@ For browser connections, use the Agents client SDK:
- **Vanilla JS**: `AgentClient` from `agents/client`
- **React**: `useAgent` hook from `agents/react`
-Refer to [Client SDK](/agents/communication-channels/chat/client-sdk/) for full documentation.
+Refer to [Client SDK](/agents/agent-class/communication-channels/chat/client-sdk/) for full documentation.
## Next steps
diff --git a/src/content/docs/agents/concepts/agentic-patterns/human-in-the-loop.mdx b/src/content/docs/agents/concepts/agentic-patterns/human-in-the-loop.mdx
index bddc68dd5f4..69499d5b5a4 100644
--- a/src/content/docs/agents/concepts/agentic-patterns/human-in-the-loop.mdx
+++ b/src/content/docs/agents/concepts/agentic-patterns/human-in-the-loop.mdx
@@ -576,7 +576,7 @@ class MultiApprovalAgent extends Agent {
diff --git a/src/content/docs/agents/concepts/agentic-patterns/long-running-agents.mdx b/src/content/docs/agents/concepts/agentic-patterns/long-running-agents.mdx
index 494be30db9e..32a33b9c9af 100644
--- a/src/content/docs/agents/concepts/agentic-patterns/long-running-agents.mdx
+++ b/src/content/docs/agents/concepts/agentic-patterns/long-running-agents.mdx
@@ -124,7 +124,7 @@ A hibernated agent can be woken by any of these sources:
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------- |
| **HTTP request** | Any request to the agent's URL triggers `onRequest()` | A webhook from GitHub |
| **WebSocket connection** | A client connects, triggering `onConnect()` | A team member opens the dashboard |
-| **RPC call** | Another Worker or agent calls a method via [service binding](/workers/runtime-apis/bindings/service-bindings/) or [`@callable`](/agents/runtime/lifecycle/callable-methods/) | A coordinator agent delegates a task |
+| **RPC call** | Another Worker or agent calls a method via [service binding](/workers/runtime-apis/bindings/service-bindings/) or [`@callable`](/agents/agent-class/callable-methods/) | A coordinator agent delegates a task |
| **Scheduled alarm** | A stored schedule fires, triggering your callback | Daily standup reminder at 9am |
| **Email** | An inbound email triggers `onEmail()` | A team member replies to a status email |
@@ -220,16 +220,16 @@ try {
| Seconds | Normal request handling |
| Minutes | `keepAlive()` / `keepAliveWhile()` |
| Minutes | `startFiber()` when retryable acceptance matters |
-| Minutes to hours | [Workflows](/agents/runtime/execution/run-workflows/) |
+| Minutes to hours | [Workflows](/agents/agent-class/run-workflows/) |
| Hours to days | Async pattern: start job, hibernate, wake on completion |
## Surviving crashes: fibers and recovery
An agent can be evicted at any time — a deploy, a platform restart, or hitting resource limits. If the agent was mid-task, that work is lost unless it was checkpointed.
-[`runFiber()`](/agents/runtime/execution/durable-execution/) provides crash-recoverable execution. It persists a row in SQLite for the duration of the work, and lets you `stash()` intermediate state. If the agent is evicted, the fiber row survives, and `onFiberRecovered()` is called on the next activation.
+[`runFiber()`](/agents/agent-class/durable-execution/) provides crash-recoverable execution. It persists a row in SQLite for the duration of the work, and lets you `stash()` intermediate state. If the agent is evicted, the fiber row survives, and `onFiberRecovered()` is called on the next activation.
-Use [`startFiber()`](/agents/runtime/execution/durable-execution/#startfiber) when the important boundary is durable acceptance. It adds an idempotency key, retained status records, inspection, cancellation, and cleanup on top of the same fiber machinery. By default it returns after acceptance; pass `waitForCompletion: true` when the request should stay open until the accepted job reaches a terminal status. This is a good fit for webhooks where the provider may retry delivery and the agent must avoid starting duplicate visible side effects.
+Use [`startFiber()`](/agents/agent-class/durable-execution/#startfiber) when the important boundary is durable acceptance. It adds an idempotency key, retained status records, inspection, cancellation, and cleanup on top of the same fiber machinery. By default it returns after acceptance; pass `waitForCompletion: true` when the request should stay open until the accepted job reaches a terminal status. This is a good fit for webhooks where the provider may retry delivery and the agent must avoid starting duplicate visible side effects.
```ts
export class ProjectManager extends Agent {
@@ -270,7 +270,7 @@ In `wrangler dev`, fiber recovery works identically to production. Kill the wran
:::
-For the full API reference — `FiberContext`, `FiberRecoveryContext`, concurrent fibers, inline vs fire-and-forget patterns — refer to [Durable Execution](/agents/runtime/execution/durable-execution/).
+For the full API reference — `FiberContext`, `FiberRecoveryContext`, concurrent fibers, inline vs fire-and-forget patterns — refer to [Durable Execution](/agents/agent-class/durable-execution/).
## Handling long async operations
@@ -362,7 +362,7 @@ export class ProjectManager extends Agent {
### Pattern: workflow delegation
-A production deployment involves multiple steps that must each retry independently — build, test, stage, promote. The project manager should not manage these steps internally; it delegates to a [Workflow](/agents/runtime/execution/run-workflows/) that handles retries and step sequencing:
+A production deployment involves multiple steps that must each retry independently — build, test, stage, promote. The project manager should not manage these steps internally; it delegates to a [Workflow](/agents/agent-class/run-workflows/) that handles retries and step sequencing:
```ts
export class ProjectManager extends Agent {
@@ -535,7 +535,7 @@ Sub-agents have their own state, schedules, durable fibers, and lifecycle. They
Because facets do not have independent alarm slots, the top-level parent owns the physical Durable Object alarm. The Agents SDK records which sub-agent owns each schedule or fiber recovery lease, wakes the parent, and routes the callback back into the child. The parent does not need to stay active while the sub-agent works — it can start the work, hibernate, and be woken by the child-owned schedule or recovery check.
-For the full `subAgent()` API — typed RPC stubs, client routing, access control, storage isolation, and alarm-backed APIs — refer to [Sub-agents](/agents/runtime/execution/sub-agents/). For AI-specific sub-agent streaming (running full LLM turns through a child agent), refer to [Think: Sub-agent RPC](/agents/harnesses/think/sub-agents/).
+For the full `subAgent()` API — typed RPC stubs, client routing, access control, storage isolation, and alarm-backed APIs — refer to [Sub-agents](/agents/agent-class/sub-agents/). For AI-specific sub-agent streaming (running full LLM turns through a child agent), refer to [Think: Sub-agent RPC](/agents/harnesses/think/sub-agents/).
## Recovering interrupted LLM streams
The patterns above handle the project manager's coordination work — scheduling, delegating, polling. But the project manager also uses an LLM directly: generating plans, summarizing progress, drafting status emails. Those LLM calls stream tokens over a connection that cannot be resumed if the agent is evicted mid-response.
@@ -695,13 +695,13 @@ The agent does not need to run continuously to do any of this. It just needs to
## Related
-- [Durable Execution](/agents/runtime/execution/durable-execution/) — `runFiber()`, `startFiber()`, `stash()`, and crash recovery
-- [Schedule tasks](/agents/runtime/execution/schedule-tasks/) — delayed, cron, and interval tasks
-- [Retries](/agents/runtime/execution/retries/) — retry options and patterns
-- [Workflows](/agents/runtime/execution/run-workflows/) — durable multi-step processing
-- [Store and sync state](/agents/runtime/lifecycle/state/) — `setState()` and persistence
-- [WebSockets](/agents/runtime/communication/websockets/) — lifecycle hooks and hibernation
-- [Callable methods](/agents/runtime/lifecycle/callable-methods/) — RPC via `@callable` and service bindings
-- [Email routing](/agents/communication-channels/email/) — receiving inbound email
-- [Webhooks](/agents/communication-channels/webhooks/) — receiving external events
+- [Durable Execution](/agents/agent-class/durable-execution/) — `runFiber()`, `startFiber()`, `stash()`, and crash recovery
+- [Schedule tasks](/agents/agent-class/schedule-tasks/) — delayed, cron, and interval tasks
+- [Retries](/agents/agent-class/retries/) — retry options and patterns
+- [Workflows](/agents/agent-class/run-workflows/) — durable multi-step processing
+- [Store and sync state](/agents/agent-class/state/) — `setState()` and persistence
+- [WebSockets](/agents/agent-class/websockets/) — lifecycle hooks and hibernation
+- [Callable methods](/agents/agent-class/callable-methods/) — RPC via `@callable` and service bindings
+- [Email routing](/agents/agent-class/communication-channels/email/) — receiving inbound email
+- [Webhooks](/agents/agent-class/communication-channels/webhooks/) — receiving external events
- [Human in the loop](/agents/concepts/agentic-patterns/human-in-the-loop/) — approval flows
diff --git a/src/content/docs/agents/concepts/calling-llms.mdx b/src/content/docs/agents/concepts/calling-llms.mdx
index 45d48faf6ac..c44b532585b 100644
--- a/src/content/docs/agents/concepts/calling-llms.mdx
+++ b/src/content/docs/agents/concepts/calling-llms.mdx
@@ -14,11 +14,11 @@ import { TypeScriptExample, LinkCard } from "~/components";
Agents change how you work with LLMs. In a stateless Worker, every request starts from scratch — you reconstruct context, call a model, return the response, and forget everything. An Agent keeps state between calls, stays connected to clients over WebSocket, and can call models on its own schedule without a user present.
-This page covers the patterns that become possible when your LLM calls happen inside a stateful Agent. For provider setup and code examples, refer to [Using AI Models](/agents/runtime/operations/using-ai-models/).
+This page covers the patterns that become possible when your LLM calls happen inside a stateful Agent. For provider setup and code examples, refer to [Using AI Models](/agents/agent-class/using-ai-models/).
## State as context
-Every Agent has a built-in [SQL database](/agents/runtime/lifecycle/state/) and key-value state. Instead of passing an entire conversation history from the client on every request, the Agent stores it and builds prompts from its own storage.
+Every Agent has a built-in [SQL database](/agents/agent-class/state/) and key-value state. Instead of passing an entire conversation history from the client on every request, the Agent stores it and builds prompts from its own storage.
@@ -82,7 +82,7 @@ export class MyAgent extends Agent {
-With [`AIChatAgent`](/agents/communication-channels/chat/chat-agents/), this is handled automatically — messages are persisted to SQLite and streams resume on reconnect.
+With [`AIChatAgent`](/agents/agent-class/communication-channels/chat/chat-agents/), this is handled automatically — messages are persisted to SQLite and streams resume on reconnect.
## Autonomous model calls
@@ -202,24 +202,24 @@ For provider-level caching and rate limit management across multiple agents, use
diff --git a/src/content/docs/agents/concepts/conversation-state-and-memory.mdx b/src/content/docs/agents/concepts/conversation-state-and-memory.mdx
index ecd002751b1..7c4c0220643 100644
--- a/src/content/docs/agents/concepts/conversation-state-and-memory.mdx
+++ b/src/content/docs/agents/concepts/conversation-state-and-memory.mdx
@@ -16,7 +16,7 @@ Agents need memory to be useful over time. Without it, every conversation starts
The [Session API](/agents/runtime/lifecycle/sessions/) provides the memory layer for agents built on the Cloudflare Agents SDK. It manages two kinds of memory: **conversation history** (the messages and tool calls that make up a session) and **context memory** (persistent blocks injected into the system prompt that the agent can read, write, search, and load).
-Use this page when you need more than simple synced state or flat chat history. For small UI state, use [Store and sync state](/agents/runtime/lifecycle/state/). For basic chat persistence, `AIChatAgent` stores messages for you. For opinionated long-term memory, Think builds on Session and context blocks.
+Use this page when you need more than simple synced state or flat chat history. For small UI state, use [Store and sync state](/agents/agent-class/state/). For basic chat persistence, `AIChatAgent` stores messages for you. For opinionated long-term memory, Think builds on Session and context blocks.
:::caution[Experimental]
@@ -562,7 +562,7 @@ const truncated = truncateOlderMessages(history);
diff --git a/src/content/docs/agents/concepts/tools.mdx b/src/content/docs/agents/concepts/tools.mdx
index 560a09fbd7f..f3e4adc0309 100644
--- a/src/content/docs/agents/concepts/tools.mdx
+++ b/src/content/docs/agents/concepts/tools.mdx
@@ -51,9 +51,9 @@ Execution location describes where a tool implementation runs. It is independent
| Location | Use when | Start here |
| ------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- |
-| Worker | The tool calls an API, queries SQL, or uses server-side bindings and secrets. | [Server-side tools](/agents/communication-channels/chat/chat-agents/#server-side-tools) |
-| Browser | The tool needs geolocation, clipboard, local storage, or other browser APIs. | [Client-side tools](/agents/communication-channels/chat/chat-agents/#client-side-tools) |
-| Another Agent | A chat-capable Agent should execute as a retained, streaming tool. | [Agents as tools](/agents/runtime/execution/agent-tools/) |
+| Worker | The tool calls an API, queries SQL, or uses server-side bindings and secrets. | [Server-side tools](/agents/agent-class/communication-channels/chat/chat-agents/#server-side-tools) |
+| Browser | The tool needs geolocation, clipboard, local storage, or other browser APIs. | [Client-side tools](/agents/agent-class/communication-channels/chat/chat-agents/#client-side-tools) |
+| Another Agent | A chat-capable Agent should execute as a retained, streaming tool. | [Agents as tools](/agents/agent-class/agent-tools/) |
With direct tool calls, the model calls the tool and the framework routes execution to the configured location.
diff --git a/src/content/docs/agents/concepts/what-are-agents.mdx b/src/content/docs/agents/concepts/what-are-agents.mdx
index 0e6ab0ca45f..2784b2271c9 100644
--- a/src/content/docs/agents/concepts/what-are-agents.mdx
+++ b/src/content/docs/agents/concepts/what-are-agents.mdx
@@ -97,12 +97,12 @@ The Cloudflare Agents SDK provides the infrastructure for building production ag
diff --git a/src/content/docs/agents/concepts/workflows.mdx b/src/content/docs/agents/concepts/workflows.mdx
index df4a47df04f..2a15c661ae2 100644
--- a/src/content/docs/agents/concepts/workflows.mdx
+++ b/src/content/docs/agents/concepts/workflows.mdx
@@ -187,7 +187,7 @@ A Workflow updates Agent state at key milestones using `step.updateAgentState()`
diff --git a/src/content/docs/agents/examples/browser-agent.mdx b/src/content/docs/agents/examples/browser-agent.mdx
index bfd90b3d310..aa453eb9e1c 100644
--- a/src/content/docs/agents/examples/browser-agent.mdx
+++ b/src/content/docs/agents/examples/browser-agent.mdx
@@ -144,7 +144,7 @@ async () => {
## Use with an Agent
-The typical pattern is to create browser tools inside an [`AIChatAgent`](/agents/communication-channels/chat/chat-agents/) message handler, which gives you message persistence and streaming:
+The typical pattern is to create browser tools inside an [`AIChatAgent`](/agents/agent-class/communication-channels/chat/chat-agents/) message handler, which gives you message persistence and streaming:
diff --git a/src/content/docs/agents/examples/chat-agent.mdx b/src/content/docs/agents/examples/chat-agent.mdx
index 32169801577..2bfe7039a1f 100644
--- a/src/content/docs/agents/examples/chat-agent.mdx
+++ b/src/content/docs/agents/examples/chat-agent.mdx
@@ -349,19 +349,19 @@ Your chat agent has:
diff --git a/src/content/docs/agents/examples/email-agent.mdx b/src/content/docs/agents/examples/email-agent.mdx
index b63d259cd59..c061438fa8f 100644
--- a/src/content/docs/agents/examples/email-agent.mdx
+++ b/src/content/docs/agents/examples/email-agent.mdx
@@ -748,18 +748,18 @@ Useful when sending emails through external services while maintaining secure re
diff --git a/src/content/docs/agents/examples/slack-agent.mdx b/src/content/docs/agents/examples/slack-agent.mdx
index 87bcfc9d366..9c90be9c365 100644
--- a/src/content/docs/agents/examples/slack-agent.mdx
+++ b/src/content/docs/agents/examples/slack-agent.mdx
@@ -461,7 +461,7 @@ export class MyAgent extends SlackAgent {
- Add rate limiting to prevent abuse
- Implement conversation state management
- Use [Workers Analytics Engine](/analytics/analytics-engine/) to track usage
-- Add [schedules](/agents/runtime/execution/schedule-tasks/) for scheduled tasks
+- Add [schedules](/agents/agent-class/schedule-tasks/) for scheduled tasks
## Related resources
diff --git a/src/content/docs/agents/examples/voice-agent.mdx b/src/content/docs/agents/examples/voice-agent.mdx
index 1a439fa4cd6..7be70b67871 100644
--- a/src/content/docs/agents/examples/voice-agent.mdx
+++ b/src/content/docs/agents/examples/voice-agent.mdx
@@ -291,18 +291,18 @@ export class MyVoiceAgent extends VoiceAgent {
diff --git a/src/content/docs/agents/harnesses/index.mdx b/src/content/docs/agents/harnesses/index.mdx
index ee5c55836dc..a711b50bcc5 100644
--- a/src/content/docs/agents/harnesses/index.mdx
+++ b/src/content/docs/agents/harnesses/index.mdx
@@ -12,13 +12,13 @@ A harness is the loop that makes an agent behave like an agent instead of a sing
It is responsible for the turn-by-turn work around the model: building the prompt, loading memory, selecting tools, handling tool results, streaming responses, persisting messages, and deciding whether the agent should continue or stop.
-To build a new agent, start with the [Pi harness](/agents/harnesses/pi/), which runs Pi Durable in a Durable Object. You can also run [OpenCode](/agents/harnesses/opencode/), or build the loop yourself on top of the [Agents SDK runtime](/agents/runtime/agents-api/).
+To build a new agent, start with the [Pi harness](/agents/harnesses/pi/), which runs Pi Durable in a Durable Object. You can also run [OpenCode](/agents/harnesses/opencode/), or build the loop yourself on top of the [Agents SDK runtime](/agents/agent-class/).
## How harnesses fit
Harnesses sit on top of the Agents SDK runtime:
-- **The runtime** gives the agent durable infrastructure: the [`Agent` class](/agents/runtime/lifecycle/agent-class/), [state](/agents/runtime/lifecycle/state/), [sessions](/agents/runtime/lifecycle/sessions/), [routing](/agents/runtime/communication/routing/), [WebSockets](/agents/runtime/communication/websockets/), [scheduling](/agents/runtime/execution/schedule-tasks/), [fibers](/agents/runtime/execution/durable-execution/), and [observability](/agents/runtime/operations/observability/).
+- **The runtime** gives the agent durable infrastructure: the [`Agent` class](/agents/agent-class/internals/), [state](/agents/agent-class/state/), [sessions](/agents/runtime/lifecycle/sessions/), [routing](/agents/agent-class/routing/), [WebSockets](/agents/agent-class/websockets/), [scheduling](/agents/agent-class/schedule-tasks/), [fibers](/agents/agent-class/durable-execution/), and [observability](/agents/runtime/operations/observability/).
- **The harness** gives the agent behavior: model calls, prompt construction, tool selection, stream handling, memory strategy, and lifecycle hooks.
The runtime answers “where does this agent live and how does it stay durable?” The harness answers “what does this agent do on each turn?”
@@ -60,7 +60,7 @@ A harness usually owns:
@@ -72,6 +72,6 @@ A harness usually owns:
diff --git a/src/content/docs/agents/harnesses/think/channels.mdx b/src/content/docs/agents/harnesses/think/channels.mdx
index 5667b653a6f..b6edc9e928f 100644
--- a/src/content/docs/agents/harnesses/think/channels.mdx
+++ b/src/content/docs/agents/harnesses/think/channels.mdx
@@ -183,4 +183,4 @@ const unsubscribe = subscribe("channel", (event) => {
- [Messengers](/agents/harnesses/think/messengers/) — Chat SDK webhook setup and delivery in depth.
- [Actions](/agents/harnesses/think/actions/) — record reply attachments for `renderAttachment()`.
-- [Voice](/agents/communication-channels/voice/) — real-time speech surfaces.
+- [Voice](/agents/agent-class/communication-channels/voice/) — real-time speech surfaces.
diff --git a/src/content/docs/agents/harnesses/think/client-tools.mdx b/src/content/docs/agents/harnesses/think/client-tools.mdx
index 4c3433368a1..eb76006bb6f 100644
--- a/src/content/docs/agents/harnesses/think/client-tools.mdx
+++ b/src/content/docs/agents/harnesses/think/client-tools.mdx
@@ -118,7 +118,7 @@ When a turn produces several client tool calls at once, Think waits for **all**
A Durable Object can be evicted at any time, including while a turn is paused on an approval prompt or a client-side tool call. [`Think` durable recovery](/agents/harnesses/think/recovery/) is always on. The SDK treats such a turn as waiting on the human, not stuck. It parks the turn instead of failing it. The user's eventual approval or tool result resumes the conversation.
-For which interactions are exempt from recovery budgets, refer to [Turns waiting on a human are not sealed](/agents/communication-channels/chat/chat-agents/#turns-waiting-on-a-human-are-not-sealed).
+For which interactions are exempt from recovery budgets, refer to [Turns waiting on a human are not sealed](/agents/agent-class/communication-channels/chat/chat-agents/#turns-waiting-on-a-human-are-not-sealed).
## Message concurrency
diff --git a/src/content/docs/agents/harnesses/think/index.mdx b/src/content/docs/agents/harnesses/think/index.mdx
index 84cdc0b0a9b..d8edd145de3 100644
--- a/src/content/docs/agents/harnesses/think/index.mdx
+++ b/src/content/docs/agents/harnesses/think/index.mdx
@@ -151,7 +151,7 @@ For span attributes, payload controls, exporting traces, and direct AI SDK v6 or
## Think vs AIChatAgent
-Both Think and [`AIChatAgent`](/agents/communication-channels/chat/chat-agents/) extend `Agent` and speak the same `cf_agent_chat_*` WebSocket protocol. They serve different goals.
+Both Think and [`AIChatAgent`](/agents/agent-class/communication-channels/chat/chat-agents/) extend `Agent` and speak the same `cf_agent_chat_*` WebSocket protocol. They serve different goals.
**AIChatAgent** is a protocol adapter. You override `onChatMessage` and are responsible for calling `streamText`, wiring tools, converting messages, and returning a `Response`. AIChatAgent handles the plumbing — message persistence, streaming, abort, resume — but the LLM call is entirely your concern.
@@ -300,9 +300,9 @@ export class Assistant extends Think {
The supported pattern is "add context, then run a turn": call `addMessages()`, then `runTurn()`.
-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 `chat()` for low-level parent-to-child streaming when your code owns forwarding, cancellation, and replay policy. Use [Agents as tools](/agents/agent-class/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.
+Use [`startFiber()`](/agents/agent-class/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.
## In this section
@@ -394,8 +394,8 @@ Think's design is inspired by [Pi](https://pi.dev).
## Related
- [Sessions](/agents/runtime/lifecycle/sessions/) — context blocks, compaction, search, multi-session (the storage layer Think builds on)
-- [Sub-agents](/agents/runtime/execution/sub-agents/) — `subAgent()`, `abortSubAgent()`, `deleteSubAgent()` (the base Agent methods for spawning children)
-- [Chat agents](/agents/communication-channels/chat/chat-agents/) — `AIChatAgent` for when you need full control over the LLM call
+- [Sub-agents](/agents/agent-class/sub-agents/) — `subAgent()`, `abortSubAgent()`, `deleteSubAgent()` (the base Agent methods for spawning children)
+- [Chat agents](/agents/agent-class/communication-channels/chat/chat-agents/) — `AIChatAgent` for when you need full control over the LLM call
- [Long-running agents](/agents/concepts/agentic-patterns/long-running-agents/) — sub-agent delegation patterns for multi-week agent lifetimes
-- [Durable execution](/agents/runtime/execution/durable-execution/) — `runFiber()` and crash recovery (used by `chatRecovery`)
+- [Durable execution](/agents/agent-class/durable-execution/) — `runFiber()` and crash recovery (used by `chatRecovery`)
- [Browse the web](/agents/tools/browser/) — full CDP helper API reference
diff --git a/src/content/docs/agents/harnesses/think/messengers.mdx b/src/content/docs/agents/harnesses/think/messengers.mdx
index ca99f07f7f6..882b3488411 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/communication/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/agent-class/communication-channels/chat-sdk/) for the underlying state adapter.
diff --git a/src/content/docs/agents/harnesses/think/recovery.mdx b/src/content/docs/agents/harnesses/think/recovery.mdx
index 79caf2d7a74..a4f034e5477 100644
--- a/src/content/docs/agents/harnesses/think/recovery.mdx
+++ b/src/content/docs/agents/harnesses/think/recovery.mdx
@@ -10,7 +10,7 @@ products:
import { TypeScriptExample } from "~/components";
-Think always wraps chat turns in recoverable [fibers](/agents/runtime/execution/durable-execution/). If the Durable Object is evicted mid-stream, Think reconstructs any buffered chunks. It persists partial output and schedules a continuation or retry.
+Think always wraps chat turns in recoverable [fibers](/agents/agent-class/durable-execution/). If the Durable Object is evicted mid-stream, Think reconstructs any buffered chunks. It persists partial output and schedules a continuation or retry.
:::note
Durable recovery works without configuration. Most apps never need to configure it. Use this page for provider-specific recovery, a stall watchdog, or terminal behavior.
@@ -116,7 +116,7 @@ onChatRecovery(ctx: ChatRecoveryContext): ChatRecoveryOptions {
Use `ctx.createdAt` to skip stale recoveries. For example, if the interrupted turn is older than a few minutes, return `{ continue: false }` so the partial response is preserved without starting an old continuation.
-Durable bookkeeping remains active when automatic continuation is not appropriate. Return `{ continue: false }` to prevent another model call. For cancellation, side-effect, and cost controls, refer to [Control automatic continuation](/agents/communication-channels/chat/chat-agents/#control-automatic-continuation).
+Durable bookkeeping remains active when automatic continuation is not appropriate. Return `{ continue: false }` to prevent another model call. For cancellation, side-effect, and cost controls, refer to [Control automatic continuation](/agents/agent-class/communication-channels/chat/chat-agents/#control-automatic-continuation).
### Recovery budgets and limits
@@ -159,7 +159,7 @@ export class MyAgent extends Think {
| `terminalMessage` | generic message | Message shown to the user when recovery is given up on. |
| `onExhausted` | — | Called once when recovery is given up on. Inspect `ctx.reason`. |
-`ctx.reason` on the exhausted hook is one of: `no_progress_timeout` (stuck), `max_attempts_exceeded` (no-progress alarm loop), `work_budget_exceeded` (runaway), `interrupted_attempts_exceeded` (the turn's attempts kept dying, for example in memory-limit resets), `out_of_memory` (sealed by the alarm memory-limit circuit breaker), `recovery_aborted` (your `shouldKeepRecovering` returned `false`), or `stable_timeout` (extreme churn). Refer to [Stream recovery](/agents/communication-channels/chat/chat-agents/#stream-recovery) for the full shared reference. Think and `@cloudflare/ai-chat` use the same recovery configuration.
+`ctx.reason` on the exhausted hook is one of: `no_progress_timeout` (stuck), `max_attempts_exceeded` (no-progress alarm loop), `work_budget_exceeded` (runaway), `interrupted_attempts_exceeded` (the turn's attempts kept dying, for example in memory-limit resets), `out_of_memory` (sealed by the alarm memory-limit circuit breaker), `recovery_aborted` (your `shouldKeepRecovering` returned `false`), or `stable_timeout` (extreme churn). Refer to [Stream recovery](/agents/agent-class/communication-channels/chat/chat-agents/#stream-recovery) for the full shared reference. Think and `@cloudflare/ai-chat` use the same recovery configuration.
## Repairing interrupted tool calls
diff --git a/src/content/docs/agents/harnesses/think/tools.mdx b/src/content/docs/agents/harnesses/think/tools.mdx
index 51bee943ad4..5328b49d3f6 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 when `includeMcpTools` is `true`
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 [Agents as 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/agent-class/agent-tools/) instead of passing one-off tools through `chat()`.
## Built-in workspace tools
diff --git a/src/content/docs/agents/harnesses/think/workflows.mdx b/src/content/docs/agents/harnesses/think/workflows.mdx
index c4d73e6ef7e..c410572beb4 100644
--- a/src/content/docs/agents/harnesses/think/workflows.mdx
+++ b/src/content/docs/agents/harnesses/think/workflows.mdx
@@ -175,6 +175,6 @@ getScheduledTasks() {
Use [`submitMessages()`](/agents/harnesses/think/programmatic-submissions/) for durable one-off turns where the caller can inspect submission status later.
-Use [`startFiber()`](/agents/runtime/execution/durable-execution/#startfiber) for app-owned idempotent Agent jobs that need recovery inside the Agent. Think's workflow notification delivery does not use fibers; it uses a private outbox because it needs to store an event until delivery succeeds.
+Use [`startFiber()`](/agents/agent-class/durable-execution/#startfiber) for app-owned idempotent Agent jobs that need recovery inside the Agent. Think's workflow notification delivery does not use fibers; it uses a private outbox because it needs to store an event until delivery succeeds.
Use Workflows when the process has multiple deterministic steps, long waits, or human approval.
diff --git a/src/content/docs/agents/index.mdx b/src/content/docs/agents/index.mdx
index 98d691c38c2..5c127c3d098 100644
--- a/src/content/docs/agents/index.mdx
+++ b/src/content/docs/agents/index.mdx
@@ -24,9 +24,9 @@ Deploy once and Cloudflare runs your agents across its global network, scaling t
Agents on Cloudflare are composed from four parts:
-- **Communication channels** define how users and systems reach your agent, such as [chat](/agents/communication-channels/chat/), [voice](/agents/communication-channels/voice/), [email](/agents/communication-channels/email/), [Slack](/agents/communication-channels/slack/), [webhooks](/agents/communication-channels/webhooks/), and other event sources.
-- **The agent harness** defines the loop: how the agent calls models, selects tools, handles tool results, streams responses, and decides whether to continue. Use the [Pi harness](/agents/harnesses/pi/) to run Pi Durable in a Durable Object, or build your own loop directly on the [Agents SDK runtime](/agents/runtime/agents-api/).
-- **The Agents SDK runtime** provides durable infrastructure: the [`Agent` class](/agents/runtime/lifecycle/agent-class/), [state](/agents/runtime/lifecycle/state/), [sessions](/agents/runtime/lifecycle/sessions/), [routing](/agents/runtime/communication/routing/), [WebSockets](/agents/runtime/communication/websockets/), [scheduling](/agents/runtime/execution/schedule-tasks/), [fibers](/agents/runtime/execution/durable-execution/), and [observability](/agents/runtime/operations/observability/).
+- **Communication channels** define how users and systems reach your agent, such as [chat](/agents/agent-class/communication-channels/chat/), [voice](/agents/agent-class/communication-channels/voice/), [email](/agents/agent-class/communication-channels/email/), [Slack](/agents/agent-class/communication-channels/slack/), [webhooks](/agents/agent-class/communication-channels/webhooks/), and other event sources.
+- **The agent harness** defines the loop: how the agent calls models, selects tools, handles tool results, streams responses, and decides whether to continue. Use the [Pi harness](/agents/harnesses/pi/) to run Pi Durable in a Durable Object, or build your own loop directly on the [Agents SDK runtime](/agents/agent-class/).
+- **The Agents SDK runtime** provides durable infrastructure: the [`Agent` class](/agents/agent-class/internals/), [state](/agents/agent-class/state/), [sessions](/agents/runtime/lifecycle/sessions/), [routing](/agents/agent-class/routing/), [WebSockets](/agents/agent-class/websockets/), [scheduling](/agents/agent-class/schedule-tasks/), [fibers](/agents/agent-class/durable-execution/), and [observability](/agents/runtime/operations/observability/).
- **Tools** give the agent capabilities: [browser automation](/agents/tools/browser/), [sandboxed code execution](/agents/tools/sandbox/), [AI Search](/agents/tools/ai-search/), [MCP tools](/agents/tools/mcp/), and [payments](/agents/tools/payments/). [Code Mode](/agents/tools/codemode/) lets models discover and orchestrate multiple tools by writing code.
### Get started
diff --git a/src/content/docs/agents/model-context-protocol/apis/agent-api.mdx b/src/content/docs/agents/model-context-protocol/apis/agent-api.mdx
index 7026eae9aaa..4600cac1fbf 100644
--- a/src/content/docs/agents/model-context-protocol/apis/agent-api.mdx
+++ b/src/content/docs/agents/model-context-protocol/apis/agent-api.mdx
@@ -50,7 +50,7 @@ export class MyMCP extends McpAgent {
-This means that each instance of your MCP server has its own durable state, backed by a [Durable Object](/durable-objects/), with its own [SQL database](/agents/runtime/lifecycle/state/).
+This means that each instance of your MCP server has its own durable state, backed by a [Durable Object](/durable-objects/), with its own [SQL database](/agents/agent-class/state/).
A stateless server can define [tools](/agents/model-context-protocol/protocol/tools/) with `@modelcontextprotocol/server` and serve them through `createMcpHandler`.
@@ -335,13 +335,13 @@ For a stateless server, read the same props with `getMcpAuthContext()`. Refer to
## State synchronization APIs
-The `McpAgent` class provides full access to the [Agent state APIs](/agents/runtime/lifecycle/state/):
+The `McpAgent` class provides full access to the [Agent state APIs](/agents/agent-class/state/):
-- [`state`](/agents/runtime/lifecycle/state/) — Current persisted state
-- [`initialState`](/agents/runtime/lifecycle/state/#set-the-initial-state-for-an-agent) — Default state when instance starts
-- [`setState`](/agents/runtime/lifecycle/state/) — Update and persist state
-- [`onStateChanged`](/agents/runtime/lifecycle/state/#synchronizing-state) — React to state changes
-- [`sql`](/agents/runtime/agents-api/#sql-api) — Execute SQL queries on embedded database
+- [`state`](/agents/agent-class/state/) — Current persisted state
+- [`initialState`](/agents/agent-class/state/#set-the-initial-state-for-an-agent) — Default state when instance starts
+- [`setState`](/agents/agent-class/state/) — Update and persist state
+- [`onStateChanged`](/agents/agent-class/state/#synchronizing-state) — React to state changes
+- [`sql`](/agents/agent-class/#sql-api) — Execute SQL queries on embedded database
:::note[State resets after the session ends]
Each client session is backed by an instance of the `McpAgent` class. This means that when the same client reconnects, they will start a new session, and the state will be reset.
diff --git a/src/content/docs/agents/model-context-protocol/apis/client-api.mdx b/src/content/docs/agents/model-context-protocol/apis/client-api.mdx
index c2c56eb2b9d..c58ec07c807 100644
--- a/src/content/docs/agents/model-context-protocol/apis/client-api.mdx
+++ b/src/content/docs/agents/model-context-protocol/apis/client-api.mdx
@@ -62,7 +62,7 @@ export class MyAgent extends Agent {
-Connections persist in the agent's [SQL storage](/agents/runtime/lifecycle/state/), and when an agent connects to an MCP server, all tools from that server become available automatically.
+Connections persist in the agent's [SQL storage](/agents/agent-class/state/), and when an agent connects to an MCP server, all tools from that server become available automatically.
## Adding MCP servers
@@ -751,7 +751,7 @@ async addMcpServer(
- `transport` — Transport layer configuration:
- `headers` — Custom HTTP headers for authentication
- `type` — Transport type: `"auto"` (default), `"streamable-http"`, or `"sse"`
- - `retry` — Retry options for connection and reconnection attempts. Persisted and used when restoring connections after hibernation or after OAuth completion. Default: 3 attempts, 500ms base delay, 5s max delay. Refer to [Retries](/agents/runtime/execution/retries/) for details on `RetryOptions`.
+ - `retry` — Retry options for connection and reconnection attempts. Persisted and used when restoring connections after hibernation or after OAuth completion. Default: 3 attempts, 500ms base delay, 5s max delay. Refer to [Retries](/agents/agent-class/retries/) for details on `RetryOptions`.
#### Parameters (RPC transport)
@@ -1091,7 +1091,7 @@ const disposable = this.mcp.onServerStateChanged(() => {
:::note
-MCP server list broadcasts (`cf_agent_mcp_servers`) are automatically filtered to exclude connections where [`shouldSendProtocolMessages`](/agents/runtime/communication/protocol-messages/) returned `false`.
+MCP server list broadcasts (`cf_agent_mcp_servers`) are automatically filtered to exclude connections where [`shouldSendProtocolMessages`](/agents/agent-class/protocol-messages/) returned `false`.
:::
### Lifecycle methods
@@ -1156,7 +1156,7 @@ await this.mcp.waitForConnections({ timeout: 10_000 });
```
:::note
-`AIChatAgent` calls this automatically via its [`waitForMcpConnections`](/agents/communication-channels/chat/chat-agents/#waitformcpconnections) property (defaults to `{ timeout: 10_000 }`). You only need `waitForConnections()` directly when using `Agent` with MCP, or when you want finer control inside `onChatMessage`.
+`AIChatAgent` calls this automatically via its [`waitForMcpConnections`](/agents/agent-class/communication-channels/chat/chat-agents/#waitformcpconnections) property (defaults to `{ timeout: 10_000 }`). You only need `waitForConnections()` directly when using `Agent` with MCP, or when you want finer control inside `onChatMessage`.
:::
#### `this.mcp.closeConnection()`
@@ -1268,12 +1268,12 @@ export class MyAgent extends Agent {
diff --git a/src/content/docs/agents/model-context-protocol/guides/connect-mcp-client.mdx b/src/content/docs/agents/model-context-protocol/guides/connect-mcp-client.mdx
index d01eff88b8b..d9cde1c94ee 100644
--- a/src/content/docs/agents/model-context-protocol/guides/connect-mcp-client.mdx
+++ b/src/content/docs/agents/model-context-protocol/guides/connect-mcp-client.mdx
@@ -230,7 +230,7 @@ You created an Agent that can:
- List all available tools from connected servers
- Monitor connection status
-Connections persist in the Agent's [SQL storage](/agents/runtime/lifecycle/state/), so they remain active across requests.
+Connections persist in the Agent's [SQL storage](/agents/agent-class/state/), so they remain active across requests.
## Next steps
diff --git a/src/content/docs/agents/platform/limits.mdx b/src/content/docs/agents/platform/limits.mdx
index b531c72fad9..7073926e31f 100644
--- a/src/content/docs/agents/platform/limits.mdx
+++ b/src/content/docs/agents/platform/limits.mdx
@@ -28,6 +28,6 @@ Many limits are inherited from those applied to Workers scripts and/or Durable O
[^2]: You can deploy up to [500 scripts per account](/workers/platform/limits/), but each script (project) can define multiple Agents. Each deployed script can be up to 10 MB on the [Workers Paid Plan](/workers/platform/pricing/#workers)
-[^3]: Compute (CPU) time per Agent is limited to 30 seconds, but this is refreshed when an Agent receives a new HTTP request, runs a [scheduled task](/agents/runtime/execution/schedule-tasks/), or an incoming WebSocket message.
+[^3]: Compute (CPU) time per Agent is limited to 30 seconds, but this is refreshed when an Agent receives a new HTTP request, runs a [scheduled task](/agents/agent-class/schedule-tasks/), or an incoming WebSocket message.
diff --git a/src/content/docs/agents/runtime/communication/index.mdx b/src/content/docs/agents/runtime/communication/index.mdx
deleted file mode 100644
index afc1b725017..00000000000
--- a/src/content/docs/agents/runtime/communication/index.mdx
+++ /dev/null
@@ -1,12 +0,0 @@
----
-title: Communication
-pcx_content_type: navigation
-sidebar:
- order: 2
- group:
- hideIndex: true
----
-
-import { DirectoryListing } from "~/components";
-
-
diff --git a/src/content/docs/agents/runtime/execution/agent-skills.mdx b/src/content/docs/agents/runtime/execution/agent-skills.mdx
index 580acff3445..7bef060ae58 100644
--- a/src/content/docs/agents/runtime/execution/agent-skills.mdx
+++ b/src/content/docs/agents/runtime/execution/agent-skills.mdx
@@ -16,7 +16,7 @@ Agent Skills are on-demand instructions, resources, and scripts. A skill source
Agent Skills are experimental, and script execution in particular is early. The API may change in a future release.
:::
-The skills engine lives in `agents/skills` and is framework-agnostic, so any agent (including a plain [`AIChatAgent`](/agents/communication-channels/chat/chat-agents/) `onChatMessage`) can build a `SkillRegistry`. [`@cloudflare/think`](/agents/harnesses/think/) re-exports it as the `skills` namespace and wires `getSkills()` into the turn automatically.
+The skills engine lives in `agents/skills` and is framework-agnostic, so any agent (including a plain [`AIChatAgent`](/agents/agent-class/communication-channels/chat/chat-agents/) `onChatMessage`) can build a `SkillRegistry`. [`@cloudflare/think`](/agents/harnesses/think/) re-exports it as the `skills` namespace and wires `getSkills()` into the turn automatically.
## Using skills with Think
diff --git a/src/content/docs/agents/runtime/lifecycle/get-current-agent.mdx b/src/content/docs/agents/runtime/lifecycle/get-current-agent.mdx
index 706eaf1b0fa..e33dd267c40 100644
--- a/src/content/docs/agents/runtime/lifecycle/get-current-agent.mdx
+++ b/src/content/docs/agents/runtime/lifecycle/get-current-agent.mdx
@@ -320,18 +320,18 @@ The context available depends on how the method was invoked:
diff --git a/src/content/docs/agents/runtime/lifecycle/sessions.mdx b/src/content/docs/agents/runtime/lifecycle/sessions.mdx
index 84967bb6bde..5bd33e614bf 100644
--- a/src/content/docs/agents/runtime/lifecycle/sessions.mdx
+++ b/src/content/docs/agents/runtime/lifecycle/sessions.mdx
@@ -906,5 +906,5 @@ By default, storage is in Durable Object SQLite and tables are created lazily on
## Related
- [Think](/agents/harnesses/think/) — opinionated chat agent that uses Session for conversation storage via `configureSession()`
-- [Chat agents](/agents/communication-channels/chat/chat-agents/) — `AIChatAgent` with its own message persistence layer
-- [Store and sync state](/agents/runtime/lifecycle/state/) — `setState()` for simpler key-value persistence
+- [Chat agents](/agents/agent-class/communication-channels/chat/chat-agents/) — `AIChatAgent` with its own message persistence layer
+- [Store and sync state](/agents/agent-class/state/) — `setState()` for simpler key-value persistence
diff --git a/src/content/docs/agents/runtime/operations/configuration.mdx b/src/content/docs/agents/runtime/operations/configuration.mdx
index 4fd49eefa80..ab9a592a4f1 100644
--- a/src/content/docs/agents/runtime/operations/configuration.mdx
+++ b/src/content/docs/agents/runtime/operations/configuration.mdx
@@ -330,7 +330,7 @@ export default defineConfig({
The `agents()` plugin is safe to include even if your project does not use decorators. It only runs the transform on files that contain `@` syntax.
-The starter template and all examples include this plugin by default. If you encounter `SyntaxError: Invalid or unexpected token` with decorators, refer to [Callable methods — Troubleshooting](/agents/runtime/lifecycle/callable-methods/#troubleshooting).
+The starter template and all examples include this plugin by default. If you encounter `SyntaxError: Invalid or unexpected token` with decorators, refer to [Callable methods — Troubleshooting](/agents/agent-class/callable-methods/#troubleshooting).
## Generating types
@@ -939,18 +939,18 @@ Consider converting to the declarative [`exports`](/durable-objects/reference/du
diff --git a/src/content/docs/agents/runtime/operations/cross-domain-authentication.mdx b/src/content/docs/agents/runtime/operations/cross-domain-authentication.mdx
index 12c801a1785..75270b95c4f 100644
--- a/src/content/docs/agents/runtime/operations/cross-domain-authentication.mdx
+++ b/src/content/docs/agents/runtime/operations/cross-domain-authentication.mdx
@@ -256,13 +256,13 @@ export class SecureAgent extends Agent {
@@ -274,6 +274,6 @@ export class SecureAgent extends Agent {
diff --git a/src/content/docs/agents/runtime/operations/observability/tracing.mdx b/src/content/docs/agents/runtime/operations/observability/tracing.mdx
index 3e26ae7050f..3ac85830243 100644
--- a/src/content/docs/agents/runtime/operations/observability/tracing.mdx
+++ b/src/content/docs/agents/runtime/operations/observability/tracing.mdx
@@ -279,6 +279,6 @@ Beginning December 1, 2026, agent traces contribute to account-level ingestion a
diff --git a/src/content/docs/agents/tools/ai-search.mdx b/src/content/docs/agents/tools/ai-search.mdx
index 964b74449d3..098d2ae90d1 100644
--- a/src/content/docs/agents/tools/ai-search.mdx
+++ b/src/content/docs/agents/tools/ai-search.mdx
@@ -10,7 +10,7 @@ products:
import { LinkCard, TypeScriptExample, WranglerConfig } from "~/components";
-Agents can use [AI Search](/ai-search/) to retrieve relevant information from indexed content and use it to augment [calls to AI models](/agents/runtime/operations/using-ai-models/). AI Search manages the retrieval pipeline for you, including indexing, search, and optional chat completions over your content.
+Agents can use [AI Search](/ai-search/) to retrieve relevant information from indexed content and use it to augment [calls to AI models](/agents/agent-class/using-ai-models/). AI Search manages the retrieval pipeline for you, including indexing, search, and optional chat completions over your content.
Use AI Search when you want an agent to:
diff --git a/src/content/docs/agents/tools/codemode/browser.mdx b/src/content/docs/agents/tools/codemode/browser.mdx
index 94d293b855a..8c3f055191f 100644
--- a/src/content/docs/agents/tools/codemode/browser.mdx
+++ b/src/content/docs/agents/tools/codemode/browser.mdx
@@ -215,4 +215,4 @@ The timeout cannot interrupt a tight synchronous loop such as `while (true) {}`.
`createBrowserCodeTool()` excludes any tool whose `needsApproval` value is `true` or a function. Code Mode does not pause iframe execution to request approval for those tools.
-Keep approval-gated actions outside the Code Mode descriptor. Register them as standard tools and use the [`useAgentChat()` approval flow](/agents/communication-channels/chat/chat-agents/#tool-approval-human-in-the-loop) instead.
+Keep approval-gated actions outside the Code Mode descriptor. Register them as standard tools and use the [`useAgentChat()` approval flow](/agents/agent-class/communication-channels/chat/chat-agents/#tool-approval-human-in-the-loop) instead.
diff --git a/src/content/docs/agents/tools/mcp.mdx b/src/content/docs/agents/tools/mcp.mdx
index 9f9e9904206..6c718620c09 100644
--- a/src/content/docs/agents/tools/mcp.mdx
+++ b/src/content/docs/agents/tools/mcp.mdx
@@ -53,7 +53,7 @@ export class ToolAgent extends Agent {
-If the server requires OAuth, `addMcpServer()` returns an authentication state and authorization URL. The connection is persisted in the agent's [SQL storage](/agents/runtime/lifecycle/state/).
+If the server requires OAuth, `addMcpServer()` returns an authentication state and authorization URL. The connection is persisted in the agent's [SQL storage](/agents/agent-class/state/).
## Configuration
diff --git a/src/content/docs/agents/tools/sandbox.mdx b/src/content/docs/agents/tools/sandbox.mdx
index 37b18717ff1..95cd36a48e8 100644
--- a/src/content/docs/agents/tools/sandbox.mdx
+++ b/src/content/docs/agents/tools/sandbox.mdx
@@ -196,7 +196,7 @@ Files in the sandbox last while the container runs. When the container stops, th
Use agent state for user-visible progress and small metadata, such as the ID of the latest snapshot.
-For long-running sandbox work, pair the sandbox with [durable execution with fibers](/agents/runtime/execution/durable-execution/) or [Workflows](/agents/runtime/execution/run-workflows/) so the agent can recover or report progress if work outlives a single request.
+For long-running sandbox work, pair the sandbox with [durable execution with fibers](/agents/agent-class/durable-execution/) or [Workflows](/agents/agent-class/run-workflows/) so the agent can recover or report progress if work outlives a single request.
## Security considerations
diff --git a/src/content/docs/ai-search/agent-sdks/agents-sdk.mdx b/src/content/docs/ai-search/agent-sdks/agents-sdk.mdx
index 1a4c84fd5df..53fb7a38a81 100644
--- a/src/content/docs/ai-search/agent-sdks/agents-sdk.mdx
+++ b/src/content/docs/ai-search/agent-sdks/agents-sdk.mdx
@@ -230,7 +230,7 @@ npx wrangler types
npm run dev
```
-`AIChatAgent` speaks its chat protocol over a WebSocket, so drive it from a chat client rather than a plain `curl` request. The quickest way is to point a UI built with the Agents SDK [`useAgentChat`](/agents/communication-channels/chat/chat-agents/) hook at your local server.
+`AIChatAgent` speaks its chat protocol over a WebSocket, so drive it from a chat client rather than a plain `curl` request. The quickest way is to point a UI built with the Agents SDK [`useAgentChat`](/agents/agent-class/communication-channels/chat/chat-agents/) hook at your local server.
Send a message such as `How do I get started?`. On the first message, the agent creates and seeds the instance, so the first response can take a minute or two while the seed document indexes.
diff --git a/src/content/docs/ai-search/how-to/human-in-the-loop-knowledge-base.mdx b/src/content/docs/ai-search/how-to/human-in-the-loop-knowledge-base.mdx
index 86b86805e66..aeadd67b3fa 100644
--- a/src/content/docs/ai-search/how-to/human-in-the-loop-knowledge-base.mdx
+++ b/src/content/docs/ai-search/how-to/human-in-the-loop-knowledge-base.mdx
@@ -366,7 +366,7 @@ async () => {
`aiSearch.search()` runs immediately. When the program reaches `aiSearch.saveDocument()`, the runtime records the call as pending and pauses the execution before the upload runs.
-Your client sends the chat message that starts the run, then drives the approval with the `@callable()` methods. The following script uses the [Agents SDK client](/agents/communication-channels/chat/client-sdk/) to do both. Save it as `client.mjs`, set `HOST` to your deployed Worker, and run it with `node client.mjs`:
+Your client sends the chat message that starts the run, then drives the approval with the `@callable()` methods. The following script uses the [Agents SDK client](/agents/agent-class/communication-channels/chat/client-sdk/) to do both. Save it as `client.mjs`, set `HOST` to your deployed Worker, and run it with `node client.mjs`:
diff --git a/src/content/docs/ai-search/how-to/talk-to-your-knowledge-base.mdx b/src/content/docs/ai-search/how-to/talk-to-your-knowledge-base.mdx
index 2059cb136fd..2832cc2ecb5 100644
--- a/src/content/docs/ai-search/how-to/talk-to-your-knowledge-base.mdx
+++ b/src/content/docs/ai-search/how-to/talk-to-your-knowledge-base.mdx
@@ -18,7 +18,7 @@ import {
} from "~/components";
import AiSearchVoiceDiagram from "~/components/AiSearchVoiceDiagram.astro";
-This tutorial builds a voice agent that you can talk to and that answers out loud from your [AI Search](/ai-search/) knowledge base. It uses the [Cloudflare Agents](/agents/) [`@cloudflare/voice`](/agents/communication-channels/voice/) package for the speech pipeline, and AI Search as the agent's knowledge base, exposed as a retrieval tool the agent's model calls.
+This tutorial builds a voice agent that you can talk to and that answers out loud from your [AI Search](/ai-search/) knowledge base. It uses the [Cloudflare Agents](/agents/) [`@cloudflare/voice`](/agents/agent-class/communication-channels/voice/) package for the speech pipeline, and AI Search as the agent's knowledge base, exposed as a retrieval tool the agent's model calls.
**What you will build:** A voice agent that transcribes your speech, calls AI Search to retrieve relevant content from your indexed knowledge base, generates a grounded answer, and speaks it back.
@@ -53,7 +53,7 @@ Install the voice package:
-The [`@cloudflare/voice`](/agents/communication-channels/voice/) package provides the `withVoice` mixin and the Workers AI providers (`WorkersAIFluxSTT` and `WorkersAITTS`). For a full walkthrough of the voice agent itself, including the browser client, refer to the [Voice agent example](/agents/examples/voice-agent/).
+The [`@cloudflare/voice`](/agents/agent-class/communication-channels/voice/) package provides the `withVoice` mixin and the Workers AI providers (`WorkersAIFluxSTT` and `WorkersAITTS`). For a full walkthrough of the voice agent itself, including the browser client, refer to the [Voice agent example](/agents/examples/voice-agent/).
## 2. Add the AI Search binding
@@ -199,7 +199,7 @@ Use a Workers AI model that supports function calling so the model can call the
## 4. Build the client
-Replace `src/client.tsx` with a React component that uses the [`useVoiceAgent`](/agents/communication-channels/voice/) hook. The hook manages the microphone, the WebSocket connection to your agent, audio playback, and interrupt detection, so the component only needs to render controls. Set `agent` to your agent class name, `TalkToDocs`.
+Replace `src/client.tsx` with a React component that uses the [`useVoiceAgent`](/agents/agent-class/communication-channels/voice/) hook. The hook manages the microphone, the WebSocket connection to your agent, audio playback, and interrupt detection, so the component only needs to render controls. Set `agent` to your agent class name, `TalkToDocs`.
```tsx title="src/client.tsx"
import { useVoiceAgent } from "@cloudflare/voice/react";
@@ -292,7 +292,7 @@ This tutorial builds a single-user voice agent. If you instead need several peop
diff --git a/src/content/docs/workers/demos/chatgpt-app.mdx b/src/content/docs/workers/demos/chatgpt-app.mdx
index e5ccd8ba0d7..9ea01e4e048 100644
--- a/src/content/docs/workers/demos/chatgpt-app.mdx
+++ b/src/content/docs/workers/demos/chatgpt-app.mdx
@@ -868,7 +868,7 @@ Now that you have a working ChatGPT App, you can:
diff --git a/src/content/docs/workflows/get-started/durable-agents.mdx b/src/content/docs/workflows/get-started/durable-agents.mdx
index 97efadb5912..56115326a7d 100644
--- a/src/content/docs/workflows/get-started/durable-agents.mdx
+++ b/src/content/docs/workflows/get-started/durable-agents.mdx
@@ -649,7 +649,7 @@ Agent class names are automatically converted to kebab-case for URLs (`ResearchA
diff --git a/src/content/partials/agents/common-integration-patterns.mdx b/src/content/partials/agents/common-integration-patterns.mdx
index c596443ec91..5ee7dd47d3f 100644
--- a/src/content/partials/agents/common-integration-patterns.mdx
+++ b/src/content/partials/agents/common-integration-patterns.mdx
@@ -49,7 +49,7 @@ const agentResponse = await routeAgentRequest(request, env, {
-Refer to [Routing](/agents/runtime/communication/routing/) for more options including CORS, custom instance naming, and location hints.
+Refer to [Routing](/agents/agent-class/routing/) for more options including CORS, custom instance naming, and location hints.
### Accessing agents from server code
diff --git a/src/content/partials/agents/next-steps.mdx b/src/content/partials/agents/next-steps.mdx
index b1a384f100f..42c4780fb04 100644
--- a/src/content/partials/agents/next-steps.mdx
+++ b/src/content/partials/agents/next-steps.mdx
@@ -12,46 +12,46 @@ Now that you have a working agent, explore these topics:
| Learn how to | Refer to |
| ------------------------ | --------------------------------------------------------- |
-| Add AI/LLM capabilities | [Using AI models](/agents/runtime/operations/using-ai-models/) |
+| Add AI/LLM capabilities | [Using AI models](/agents/agent-class/using-ai-models/) |
| Expose tools via MCP | [MCP servers](/agents/model-context-protocol/apis/handler-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/) |
+| Run background tasks | [Schedule tasks](/agents/agent-class/schedule-tasks/) |
+| Handle emails | [Email routing](/agents/agent-class/communication-channels/email/) |
+| Use Cloudflare Workflows | [Run Workflows](/agents/agent-class/run-workflows/) |
### Explore more
\ No newline at end of file
From c1c13d18aabd8d3c72181ea1f90bc3349a78ee39 Mon Sep 17 00:00:00 2001
From: Matt Carey
Date: Tue, 27 Oct 2026 11:14:35 +0000
Subject: [PATCH 02/11] [Agents] Address style review: relative links, refer
to, serial comma, split sentences
---
src/content/changelog/agents/2025-03-18-npm-i-agents.mdx | 2 +-
.../agents/2025-11-26-agents-resumable-streaming.mdx | 4 ++--
.../agents/2026-02-03-agents-workflows-integration.mdx | 2 +-
src/content/changelog/agents/2026-02-17-agents-sdk-v0.5.0.mdx | 4 ++--
.../communication-channels/chat/autonomous-responses.mdx | 2 +-
.../agents/concepts/agentic-patterns/long-running-agents.mdx | 2 +-
6 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/content/changelog/agents/2025-03-18-npm-i-agents.mdx b/src/content/changelog/agents/2025-03-18-npm-i-agents.mdx
index 3bd3242dead..b5b97ed1dae 100644
--- a/src/content/changelog/agents/2025-03-18-npm-i-agents.mdx
+++ b/src/content/changelog/agents/2025-03-18-npm-i-agents.mdx
@@ -127,7 +127,7 @@ You can use the starter as a template for your own Agents projects: open up `src
We've heard your feedback on the Agents SDK documentation, and we're shipping more API reference material and usage examples, including:
- Expanded [API reference documentation](/agents/runtime/), covering the methods and properties exposed by the Agents SDK, as well as more usage examples.
-- More [Client API](/agents/agent-class/#client-api) documentation that documents `useAgent`, `useAgentChat` and the new `@unstable_callable` RPC decorator exposed by the SDK.
+- More [Client API](/agents/agent-class/#client-api) documentation that documents `useAgent`, `useAgentChat`, and the new `@unstable_callable` RPC decorator exposed by the SDK.
- New documentation on how to [route requests to agents](/agents/agent-class/routing/) and (optionally) authenticate clients before they connect to your Agents.
Note that the Agents SDK is continually growing: the type definitions included in the SDK will always include the latest APIs exposed by the `agents` package.
diff --git a/src/content/changelog/agents/2025-11-26-agents-resumable-streaming.mdx b/src/content/changelog/agents/2025-11-26-agents-resumable-streaming.mdx
index 560b2eeac6d..4c19d25c918 100644
--- a/src/content/changelog/agents/2025-11-26-agents-resumable-streaming.mdx
+++ b/src/content/changelog/agents/2025-11-26-agents-resumable-streaming.mdx
@@ -24,7 +24,7 @@ Streams are maintained across page refreshes, broken connections, and syncing ac
### Other improvements
- Default JSON schema validator added to MCP client
-- [Schedules](https://developers.cloudflare.com/agents/agent-class/schedule-tasks/) can now safely destroy the agent
+- [Schedules](/agents/agent-class/schedule-tasks/) can now safely destroy the agent
### MCP client API improvements
@@ -60,7 +60,7 @@ MCP discovery fetches the available tools, prompts, and resources from an MCP se
### Bug fixes
-- Fixed a bug where [schedules](https://developers.cloudflare.com/agents/agent-class/schedule-tasks/) meant to fire immediately with this.schedule(0, ...) or `this.schedule(new Date(), ...)` would not fire
+- Fixed a bug where [schedules](/agents/agent-class/schedule-tasks/) meant to fire immediately with this.schedule(0, ...) or `this.schedule(new Date(), ...)` would not fire
- Fixed an issue where schedules that took longer than 30 seconds would occasionally time out
- Fixed SSE transport now properly forwards session IDs and request headers
- Fixed AI SDK stream events conversion to UIMessageStreamPart
diff --git a/src/content/changelog/agents/2026-02-03-agents-workflows-integration.mdx b/src/content/changelog/agents/2026-02-03-agents-workflows-integration.mdx
index 7fd6157f3c0..20fac12d9a9 100644
--- a/src/content/changelog/agents/2026-02-03-agents-workflows-integration.mdx
+++ b/src/content/changelog/agents/2026-02-03-agents-workflows-integration.mdx
@@ -175,4 +175,4 @@ To update to the latest version:
npm i agents@latest
```
-For the complete Workflows API reference and patterns, see [Run Workflows](/agents/agent-class/run-workflows/).
+For the complete Workflows API reference and patterns, refer to [Run Workflows](/agents/agent-class/run-workflows/).
diff --git a/src/content/changelog/agents/2026-02-17-agents-sdk-v0.5.0.mdx b/src/content/changelog/agents/2026-02-17-agents-sdk-v0.5.0.mdx
index 01093055962..abb8da7b93a 100644
--- a/src/content/changelog/agents/2026-02-17-agents-sdk-v0.5.0.mdx
+++ b/src/content/changelog/agents/2026-02-17-agents-sdk-v0.5.0.mdx
@@ -73,7 +73,7 @@ class MyAgent extends Agent {
Connections with protocol messages disabled still fully participate in RPC and regular messaging. Use `isConnectionProtocolEnabled(connection)` to check a connection's status at any time. The flag persists across Durable Object hibernation.
-See [Protocol messages](/agents/agent-class/protocol-messages/) for full documentation.
+Refer to [Protocol messages](/agents/agent-class/protocol-messages/) for full documentation.
## `@cloudflare/ai-chat` v0.1.0
@@ -81,7 +81,7 @@ The first stable release of `@cloudflare/ai-chat` ships alongside this release w
Key new features:
-- **Data parts** — Attach typed JSON blobs (`data-*`) to messages alongside text. Supports reconciliation (type+id updates in-place), append, and transient parts (ephemeral via `onData` callback). See [Data parts](/agents/agent-class/communication-channels/chat/chat-agents/#data-parts).
+- **Data parts** — Attach typed JSON blobs (`data-*`) to messages alongside text. Supports reconciliation (type+id updates in-place), append, and transient parts (ephemeral via `onData` callback). Refer to [Data parts](/agents/agent-class/communication-channels/chat/chat-agents/#data-parts).
- **Tool approval persistence** — The `needsApproval` approval UI now survives page refresh and DO hibernation. The streaming message is persisted to SQLite when a tool enters `approval-requested` state.
- **`maxPersistedMessages`** — Cap SQLite message storage with automatic oldest-message deletion.
- **`body` option on `useAgentChat`** — Send custom data with every request (static or dynamic).
diff --git a/src/content/docs/agents/agent-class/communication-channels/chat/autonomous-responses.mdx b/src/content/docs/agents/agent-class/communication-channels/chat/autonomous-responses.mdx
index 52768f99752..cb344e872e0 100644
--- a/src/content/docs/agents/agent-class/communication-channels/chat/autonomous-responses.mdx
+++ b/src/content/docs/agents/agent-class/communication-channels/chat/autonomous-responses.mdx
@@ -66,7 +66,7 @@ return Response.json({
`submitMessages()` stores pending work first and appends the messages to the conversation Session only when the submission starts executing. It accepts serializable `UIMessage[]` values, not the function form supported by `saveMessages((messages) => ...)`.
-Use [`startFiber()`](/agents/agent-class/durable-execution/#startfiber) outside Think when the durable unit is a surrounding application job, such as accepting a webhook once, restoring provider state, posting a visible reply, and recording recovery policy. `submitMessages()` owns Think's conversation admission; managed fibers own external side effects around that turn.
+Use [`startFiber()`](/agents/agent-class/durable-execution/#startfiber) outside Think when the durable unit is a surrounding application job, such as accepting a webhook once, restoring provider state, posting a visible reply, and recording recovery policy. `submitMessages()` owns Think's conversation admission. Managed fibers own external side effects around that turn.
For the full Think API, refer to [`submitMessages()`](/agents/harnesses/think/programmatic-submissions/#submitmessages).
### When to use `saveMessages` vs `onChatResponse`
diff --git a/src/content/docs/agents/concepts/agentic-patterns/long-running-agents.mdx b/src/content/docs/agents/concepts/agentic-patterns/long-running-agents.mdx
index 32a33b9c9af..8e99398d8fc 100644
--- a/src/content/docs/agents/concepts/agentic-patterns/long-running-agents.mdx
+++ b/src/content/docs/agents/concepts/agentic-patterns/long-running-agents.mdx
@@ -229,7 +229,7 @@ An agent can be evicted at any time — a deploy, a platform restart, or hitting
[`runFiber()`](/agents/agent-class/durable-execution/) provides crash-recoverable execution. It persists a row in SQLite for the duration of the work, and lets you `stash()` intermediate state. If the agent is evicted, the fiber row survives, and `onFiberRecovered()` is called on the next activation.
-Use [`startFiber()`](/agents/agent-class/durable-execution/#startfiber) when the important boundary is durable acceptance. It adds an idempotency key, retained status records, inspection, cancellation, and cleanup on top of the same fiber machinery. By default it returns after acceptance; pass `waitForCompletion: true` when the request should stay open until the accepted job reaches a terminal status. This is a good fit for webhooks where the provider may retry delivery and the agent must avoid starting duplicate visible side effects.
+Use [`startFiber()`](/agents/agent-class/durable-execution/#startfiber) when the important boundary is durable acceptance. It adds an idempotency key, retained status records, inspection, cancellation, and cleanup on top of the same fiber machinery. By default it returns after acceptance. Pass `waitForCompletion: true` when the request should stay open until the accepted job reaches a terminal status. This is a good fit for webhooks where the provider may retry delivery and the agent must avoid starting duplicate visible side effects.
```ts
export class ProjectManager extends Agent {
From efae3578af97788ba3b13e1de8a04ebf907e8790 Mon Sep 17 00:00:00 2001
From: Matt Carey
Date: Tue, 27 Oct 2026 11:10:55 +0000
Subject: [PATCH 03/11] [Agents] Rewrite the core Agents docs around harnesses
---
public/__redirects | 24 +-
src/components/cf/AgentsPlatformDiagram.astro | 38 +-
src/content/agent-setup/tracing.md | 2 +-
.../agents/2025-03-18-npm-i-agents.mdx | 2 +-
.../agents/2026-03-02-agents-sdk-v0.7.0.mdx | 2 +-
.../agents/2026-05-13-agents-sdk-v0.12.4.mdx | 2 +-
.../agents/2026-06-02-agents-sdk-v0.14.0.mdx | 2 +-
.../agents/2026-08-04-agent-tracing.mdx | 2 +-
.../agents/2026-10-13-persistent-browser.mdx | 2 +-
src/content/docs/agent-memory/get-started.mdx | 4 +-
.../agent-class/add-to-existing-project.mdx | 352 +++++++++++
.../chat/client-sdk.mdx | 2 +-
.../get-current-agent.mdx | 4 +-
.../docs/agents/agent-class/get-started.mdx | 245 ++++++++
src/content/docs/agents/agent-class/index.mdx | 71 ++-
.../docs/agents/agent-class/internals.mdx | 150 ++---
.../docs/agents/agent-class/routing.mdx | 8 +-
.../docs/agents/agent-class/websockets.mdx | 2 +-
.../docs/agents/capabilities/index.mdx | 344 +++++++++++
.../lifecycle => capabilities}/sessions.mdx | 0
.../conversation-state-and-memory.mdx | 580 +++---------------
.../docs/agents/concepts/what-are-agents.mdx | 18 +-
src/content/docs/agents/examples/index.mdx | 4 +-
.../add-to-existing-project.mdx | 380 ++++--------
.../docs/agents/getting-started/index.mdx | 20 +-
.../agents/getting-started/quick-start.mdx | 307 +++++----
src/content/docs/agents/harnesses/ai-sdk.mdx | 106 ++++
.../docs/agents/harnesses/container.mdx | 144 +++++
src/content/docs/agents/harnesses/index.mdx | 127 ++--
.../docs/agents/harnesses/opencode.mdx | 7 +-
.../docs/agents/harnesses/pi/extensions.mdx | 2 +-
.../docs/agents/harnesses/pi/index.mdx | 2 +-
.../agents/harnesses/think/configuration.mdx | 10 +-
.../harnesses/think/getting-started.mdx | 4 +-
.../docs/agents/harnesses/think/index.mdx | 10 +-
.../docs/agents/harnesses/think/recovery.mdx | 8 +-
.../docs/agents/harnesses/think/tools.mdx | 2 +-
src/content/docs/agents/index.mdx | 57 +-
.../agents/lifecycle/build-a-capability.mdx | 208 +++++++
.../agents/lifecycle/get-current-agent.mdx | 76 +++
src/content/docs/agents/lifecycle/index.mdx | 213 +++++++
.../docs/agents/lifecycle/job-queue.mdx | 126 ++++
.../model-context-protocol/apis/agent-api.mdx | 4 +-
src/content/docs/agents/models/index.mdx | 2 +-
.../operations => platform}/configuration.mdx | 2 +-
.../cross-domain-authentication.mdx | 2 +-
src/content/docs/agents/platform/index.mdx | 4 +-
.../observability/diagnostics-channels.mdx | 4 +-
.../observability/index.mdx | 2 +-
.../observability/tracing.mdx | 14 +-
.../docs/agents/runtime/execution/index.mdx | 12 -
src/content/docs/agents/runtime/index.mdx | 14 -
.../docs/agents/runtime/lifecycle/index.mdx | 12 -
.../docs/agents/runtime/operations/index.mdx | 12 -
.../execution => tools}/agent-skills.mdx | 2 +-
src/content/docs/agents/tools/browser.mdx | 2 +-
src/content/docs/agents/tools/index.mdx | 2 +-
.../partials/agents/agent-class-legacy.mdx | 9 +
58 files changed, 2525 insertions(+), 1243 deletions(-)
create mode 100644 src/content/docs/agents/agent-class/add-to-existing-project.mdx
rename src/content/docs/agents/{runtime/lifecycle => agent-class}/get-current-agent.mdx (99%)
create mode 100644 src/content/docs/agents/agent-class/get-started.mdx
create mode 100644 src/content/docs/agents/capabilities/index.mdx
rename src/content/docs/agents/{runtime/lifecycle => capabilities}/sessions.mdx (100%)
create mode 100644 src/content/docs/agents/harnesses/ai-sdk.mdx
create mode 100644 src/content/docs/agents/harnesses/container.mdx
create mode 100644 src/content/docs/agents/lifecycle/build-a-capability.mdx
create mode 100644 src/content/docs/agents/lifecycle/get-current-agent.mdx
create mode 100644 src/content/docs/agents/lifecycle/index.mdx
create mode 100644 src/content/docs/agents/lifecycle/job-queue.mdx
rename src/content/docs/agents/{runtime/operations => platform}/configuration.mdx (99%)
rename src/content/docs/agents/{runtime/operations => platform}/cross-domain-authentication.mdx (99%)
rename src/content/docs/agents/{runtime/operations => platform}/observability/diagnostics-channels.mdx (99%)
rename src/content/docs/agents/{runtime/operations => platform}/observability/index.mdx (93%)
rename src/content/docs/agents/{runtime/operations => platform}/observability/tracing.mdx (94%)
delete mode 100644 src/content/docs/agents/runtime/execution/index.mdx
delete mode 100644 src/content/docs/agents/runtime/index.mdx
delete mode 100644 src/content/docs/agents/runtime/lifecycle/index.mdx
delete mode 100644 src/content/docs/agents/runtime/operations/index.mdx
rename src/content/docs/agents/{runtime/execution => tools}/agent-skills.mdx (99%)
create mode 100644 src/content/partials/agents/agent-class-legacy.mdx
diff --git a/public/__redirects b/public/__redirects
index fae3f3de5b3..39c4396c09e 100644
--- a/public/__redirects
+++ b/public/__redirects
@@ -240,6 +240,18 @@
/aegis/analytics/ /smart-shield/configuration/dedicated-egress-ips/ips-utilization/ 301
# agents
+/agents/runtime/ /agents/lifecycle/ 301
+/agents/runtime/execution/ /agents/agent-class/ 301
+/agents/runtime/execution/agent-skills/ /agents/tools/agent-skills/ 301
+/agents/runtime/lifecycle/ /agents/lifecycle/ 301
+/agents/runtime/lifecycle/get-current-agent/ /agents/lifecycle/get-current-agent/ 301
+/agents/runtime/lifecycle/sessions/ /agents/capabilities/sessions/ 301
+/agents/runtime/operations/ /agents/platform/ 301
+/agents/runtime/operations/configuration/ /agents/platform/configuration/ 301
+/agents/runtime/operations/cross-domain-authentication/ /agents/platform/cross-domain-authentication/ 301
+/agents/runtime/operations/observability/ /agents/platform/observability/ 301
+/agents/runtime/operations/observability/diagnostics-channels/ /agents/platform/observability/diagnostics-channels/ 301
+/agents/runtime/operations/observability/tracing/ /agents/platform/observability/tracing/ 301
/agents/communication-channels/ /agents/agent-class/communication-channels/ 301
/agents/communication-channels/chat/ /agents/agent-class/communication-channels/chat/ 301
/agents/communication-channels/chat/autonomous-responses/ /agents/agent-class/communication-channels/chat/autonomous-responses/ 301
@@ -298,7 +310,7 @@
/agents/concepts/human-in-the-loop/ /agents/concepts/agentic-patterns/human-in-the-loop/ 301
/agents/concepts/long-running-agents/ /agents/concepts/agentic-patterns/long-running-agents/ 301
/agents/api-reference/agents-api/ /agents/agent-class/ 301
-/agents/api-reference/ /agents/runtime/ 301
+/agents/api-reference/ /agents/lifecycle/ 301
/agents/api-reference/chat-sdk/ /agents/agent-class/communication-channels/chat-sdk/ 301
/agents/runtime/execution/chat-sdk/ /agents/agent-class/communication-channels/chat-sdk/ 301
/agents/api-reference/agent-tools/ /agents/agent-class/agent-tools/ 301
@@ -307,15 +319,15 @@
/agents/api-reference/chat-agents/ /agents/agent-class/communication-channels/chat/chat-agents/ 301
/agents/api-reference/client-sdk/ /agents/agent-class/communication-channels/chat/client-sdk/ 301
/agents/api-reference/codemode/ /agents/tools/codemode/api-reference/ 301
-/agents/api-reference/configuration/ /agents/runtime/operations/configuration/ 301
+/agents/api-reference/configuration/ /agents/platform/configuration/ 301
/agents/api-reference/durable-execution/ /agents/agent-class/durable-execution/ 301
/agents/api-reference/email/ /agents/agent-class/communication-channels/email/ 301
-/agents/api-reference/get-current-agent/ /agents/runtime/lifecycle/get-current-agent/ 301
+/agents/api-reference/get-current-agent/ /agents/lifecycle/get-current-agent/ 301
/agents/api-reference/http-sse/ /agents/agent-class/http-sse/ 301
/agents/api-reference/mcp-agent-api/ /agents/model-context-protocol/apis/agent-api/ 301
/agents/api-reference/mcp-client-api/ /agents/model-context-protocol/apis/client-api/ 301
/agents/api-reference/mcp-handler-api/ /agents/model-context-protocol/apis/handler-api/ 301
-/agents/api-reference/observability/ /agents/runtime/operations/observability/ 301
+/agents/api-reference/observability/ /agents/platform/observability/ 301
/agents/api-reference/protocol-messages/ /agents/agent-class/protocol-messages/ 301
/agents/api-reference/queue-tasks/ /agents/agent-class/queue-tasks/ 301
/agents/api-reference/rag/ /agents/tools/ai-search/ 301
@@ -324,7 +336,7 @@
/agents/api-reference/routing/ /agents/agent-class/routing/ 301
/agents/api-reference/run-workflows/ /agents/agent-class/run-workflows/ 301
/agents/api-reference/schedule-tasks/ /agents/agent-class/schedule-tasks/ 301
-/agents/api-reference/sessions/ /agents/runtime/lifecycle/sessions/ 301
+/agents/api-reference/sessions/ /agents/capabilities/sessions/ 301
/agents/api-reference/store-and-sync-state/ /agents/agent-class/state/ 301
/agents/api-reference/sub-agents/ /agents/agent-class/sub-agents/ 301
/agents/api-reference/using-ai-models/ /agents/agent-class/using-ai-models/ 301
@@ -337,7 +349,7 @@
/agents/guides/chatgpt-app/ /workers/demos/chatgpt-app/ 301
/agents/communication-channels/chat/chatgpt-app/ /workers/demos/chatgpt-app/ 301
/agents/guides/connect-mcp-client/ /agents/model-context-protocol/guides/connect-mcp-client/ 301
-/agents/guides/cross-domain-authentication/ /agents/runtime/operations/cross-domain-authentication/ 301
+/agents/guides/cross-domain-authentication/ /agents/platform/cross-domain-authentication/ 301
/agents/guides/human-in-the-loop/ /agents/concepts/agentic-patterns/human-in-the-loop/ 301
/agents/guides/oauth-mcp-client/ /agents/model-context-protocol/guides/oauth-mcp-client/ 301
/agents/guides/push-notifications/ /agents/agent-class/communication-channels/webhooks/push-notifications/ 301
diff --git a/src/components/cf/AgentsPlatformDiagram.astro b/src/components/cf/AgentsPlatformDiagram.astro
index 44394f8359f..ec81fc77e4b 100644
--- a/src/components/cf/AgentsPlatformDiagram.astro
+++ b/src/components/cf/AgentsPlatformDiagram.astro
@@ -28,12 +28,12 @@ const channels = [
];
const runtime = [
- { label: "State", href: "/agents/agent-class/state/" },
- { label: "Sessions", href: "/agents/runtime/lifecycle/sessions/" },
- { label: "Routing", href: "/agents/agent-class/routing/" },
- { label: "WebSockets", href: "/agents/agent-class/websockets/" },
- { label: "Scheduling", href: "/agents/agent-class/schedule-tasks/" },
- { label: "Fibers", href: "/agents/agent-class/durable-execution/" },
+ { label: "MCP client", href: "/agents/capabilities/#mcp-client" },
+ { label: "Scheduler", href: "/agents/capabilities/#scheduler" },
+ { label: "Queue", href: "/agents/capabilities/#queue" },
+ { label: "WebSockets", href: "/agents/capabilities/#websockets" },
+ { label: "Sessions", href: "/agents/capabilities/#sessions" },
+ { label: "Tasks", href: "/agents/capabilities/#tasks" },
];
const tools = [
@@ -94,20 +94,27 @@ const tools = [
-
+ ObservabilityLogs · metrics · traces
@@ -387,7 +391,7 @@ const tools = [
.section-row {
display: grid;
- grid-template-columns: 1fr;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 6px;
margin-top: 9px;
}
diff --git a/src/content/agent-setup/tracing.md b/src/content/agent-setup/tracing.md
index de3aacbb049..0bb863bca58 100644
--- a/src/content/agent-setup/tracing.md
+++ b/src/content/agent-setup/tracing.md
@@ -167,7 +167,7 @@ State any validation command that could not run and why.
## Resources
-- Agent tracing and framework setup: `https://developers.cloudflare.com/agents/runtime/operations/observability/tracing/`
+- Agent tracing and framework setup: `https://developers.cloudflare.com/agents/platform/observability/tracing/`
- Workers tracing and sampling: `https://developers.cloudflare.com/workers/observability/traces/`
These instructions are published at `https://developers.cloudflare.com/agent-setup/tracing.md` so you can re-verify their authenticity at any time.
diff --git a/src/content/changelog/agents/2025-03-18-npm-i-agents.mdx b/src/content/changelog/agents/2025-03-18-npm-i-agents.mdx
index b5b97ed1dae..05cab32d9a8 100644
--- a/src/content/changelog/agents/2025-03-18-npm-i-agents.mdx
+++ b/src/content/changelog/agents/2025-03-18-npm-i-agents.mdx
@@ -126,7 +126,7 @@ You can use the starter as a template for your own Agents projects: open up `src
We've heard your feedback on the Agents SDK documentation, and we're shipping more API reference material and usage examples, including:
-- Expanded [API reference documentation](/agents/runtime/), covering the methods and properties exposed by the Agents SDK, as well as more usage examples.
+- Expanded [API reference documentation](/agents/agent-class/), covering the methods and properties exposed by the Agents SDK, as well as more usage examples.
- More [Client API](/agents/agent-class/#client-api) documentation that documents `useAgent`, `useAgentChat`, and the new `@unstable_callable` RPC decorator exposed by the SDK.
- New documentation on how to [route requests to agents](/agents/agent-class/routing/) and (optionally) authenticate clients before they connect to your Agents.
diff --git a/src/content/changelog/agents/2026-03-02-agents-sdk-v0.7.0.mdx b/src/content/changelog/agents/2026-03-02-agents-sdk-v0.7.0.mdx
index 0c4750f999a..065e9391311 100644
--- a/src/content/changelog/agents/2026-03-02-agents-sdk-v0.7.0.mdx
+++ b/src/content/changelog/agents/2026-03-02-agents-sdk-v0.7.0.mdx
@@ -72,7 +72,7 @@ export default {
The custom `Observability` override interface is still supported for users who need to filter or forward events to external services.
-For the full event reference, refer to the [Diagnostics channels documentation](/agents/runtime/operations/observability/diagnostics-channels/).
+For the full event reference, refer to the [Diagnostics channels documentation](/agents/platform/observability/diagnostics-channels/).
## `keepAlive()` and `keepAliveWhile()`
diff --git a/src/content/changelog/agents/2026-05-13-agents-sdk-v0.12.4.mdx b/src/content/changelog/agents/2026-05-13-agents-sdk-v0.12.4.mdx
index 6af6f993359..2a546261bc2 100644
--- a/src/content/changelog/agents/2026-05-13-agents-sdk-v0.12.4.mdx
+++ b/src/content/changelog/agents/2026-05-13-agents-sdk-v0.12.4.mdx
@@ -125,4 +125,4 @@ To update to the latest version:
npm i agents@latest @cloudflare/ai-chat@latest @cloudflare/think@latest @cloudflare/voice@latest
```
-Refer to the [Agents API reference](/agents/runtime/) and [Chat agents documentation](/agents/agent-class/communication-channels/chat/chat-agents/) for more information.
+Refer to the [Agents API reference](/agents/lifecycle/) and [Chat agents documentation](/agents/agent-class/communication-channels/chat/chat-agents/) for more information.
diff --git a/src/content/changelog/agents/2026-06-02-agents-sdk-v0.14.0.mdx b/src/content/changelog/agents/2026-06-02-agents-sdk-v0.14.0.mdx
index 78abe7f2913..71a610a2f2a 100644
--- a/src/content/changelog/agents/2026-06-02-agents-sdk-v0.14.0.mdx
+++ b/src/content/changelog/agents/2026-06-02-agents-sdk-v0.14.0.mdx
@@ -160,4 +160,4 @@ To update to the latest version:
-Refer to the [Agents API reference](/agents/runtime/) and [Chat agents documentation](/agents/agent-class/communication-channels/chat/chat-agents/) for more information.
+Refer to the [Agents API reference](/agents/lifecycle/) and [Chat agents documentation](/agents/agent-class/communication-channels/chat/chat-agents/) for more information.
diff --git a/src/content/changelog/agents/2026-08-04-agent-tracing.mdx b/src/content/changelog/agents/2026-08-04-agent-tracing.mdx
index 8973f10fc2f..59ef0618d69 100644
--- a/src/content/changelog/agents/2026-08-04-agent-tracing.mdx
+++ b/src/content/changelog/agents/2026-08-04-agent-tracing.mdx
@@ -46,4 +46,4 @@ Think and Flue agents are instrumented automatically. For direct AI SDK calls, w
Check out the full blog post: [Introducing: Cloudflare Agents](https://blog.cloudflare.com/agents-on-cloudflare).
-For setup details, refer to the [Agent tracing documentation](/agents/runtime/operations/observability/tracing/).
+For setup details, refer to the [Agent tracing documentation](/agents/platform/observability/tracing/).
diff --git a/src/content/changelog/agents/2026-10-13-persistent-browser.mdx b/src/content/changelog/agents/2026-10-13-persistent-browser.mdx
index f17c9ccb6c6..8c778e827a9 100644
--- a/src/content/changelog/agents/2026-10-13-persistent-browser.mdx
+++ b/src/content/changelog/agents/2026-10-13-persistent-browser.mdx
@@ -10,7 +10,7 @@ The [Cloudflare Agents SDK](/agents/) now provides a `browserTool` that gives yo
When you give `browserTool` to your agent, the model writes code that drives the browser through the [Chrome DevTools Protocol (CDP)](/browser-run/cdp/). Screenshots return as images that an image-capable model can inspect and your UI can display.
-To use `browserTool`, make sure you have [Agents SDK v0.28.0](https://github.com/cloudflare/agents/releases/tag/agents%400.28.0) or greater installed. Create a `Browser`, register it with your object's [Lifecycle](/agents/runtime/lifecycle/), and add the tool to a [Pi Durable](/agents/harnesses/pi/) [extension registry](/agents/harnesses/pi/extensions/) before opening the harness:
+To use `browserTool`, make sure you have [Agents SDK v0.28.0](https://github.com/cloudflare/agents/releases/tag/agents%400.28.0) or greater installed. Create a `Browser`, register it with your object's [Lifecycle](/agents/lifecycle/), and add the tool to a [Pi Durable](/agents/harnesses/pi/) [extension registry](/agents/harnesses/pi/extensions/) before opening the harness:
diff --git a/src/content/docs/agent-memory/get-started.mdx b/src/content/docs/agent-memory/get-started.mdx
index ac18713e71f..0e98992aebf 100644
--- a/src/content/docs/agent-memory/get-started.mdx
+++ b/src/content/docs/agent-memory/get-started.mdx
@@ -18,7 +18,7 @@ import {
Add Agent Memory to an agent so it can recall durable context across conversations.
-This guide uses the [Agents SDK](/agents/) and its [Session API](/agents/runtime/lifecycle/sessions/) to expose memory recall as a model-callable tool. The same pattern applies if you use another agent framework: store memories with `ingest()` or `remember()`, expose `recall()` through one of your agent's tools, and use the system prompt to tell the model when to search memory.
+This guide uses the [Agents SDK](/agents/) and its [Session API](/agents/capabilities/sessions/) to expose memory recall as a model-callable tool. The same pattern applies if you use another agent framework: store memories with `ingest()` or `remember()`, expose `recall()` through one of your agent's tools, and use the system prompt to tell the model when to search memory.
## Prerequisites
@@ -104,7 +104,7 @@ Generate local TypeScript types for your bindings:
The model cannot use memory just because your application has a memory binding. You need to expose recall through a tool and instruct the model when to call it.
-With the Agents SDK [Session API](/agents/runtime/lifecycle/sessions/), add a searchable context provider. Session turns the provider's `search()` method into a `search_context` tool for the model.
+With the Agents SDK [Session API](/agents/capabilities/sessions/), add a searchable context provider. Session turns the provider's `search()` method into a `search_context` tool for the model.
Create `src/server.ts` and add the recall setup:
diff --git a/src/content/docs/agents/agent-class/add-to-existing-project.mdx b/src/content/docs/agents/agent-class/add-to-existing-project.mdx
new file mode 100644
index 00000000000..2e0b9cf9528
--- /dev/null
+++ b/src/content/docs/agents/agent-class/add-to-existing-project.mdx
@@ -0,0 +1,352 @@
+---
+title: Add an Agent to an existing project
+description: Add the Agent class to an existing Cloudflare Workers project with state management and real-time connections.
+pcx_content_type: how-to
+sidebar:
+ order: 0.5
+ label: Add to an existing project
+products:
+ - agents
+---
+
+import {
+ PackageManagers,
+ TypeScriptExample,
+ WranglerConfig,
+ 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.
+
+## Prerequisites
+
+- An existing Cloudflare Workers project with a Wrangler configuration file
+- Node.js 18 or newer
+
+## 1. Install the package
+
+
+
+For React applications, no additional packages are needed — React bindings are included.
+
+For Hono applications:
+
+
+
+## 2. Create an Agent
+
+Create a new file for your agent (for example, `src/agents/counter.ts`):
+
+
+
+```ts
+import { Agent, callable } from "agents";
+
+export type CounterState = {
+ count: number;
+};
+
+export class CounterAgent extends Agent {
+ initialState: CounterState = { count: 0 };
+
+ @callable()
+ increment() {
+ this.setState({ count: this.state.count + 1 });
+ return this.state.count;
+ }
+
+ @callable()
+ decrement() {
+ this.setState({ count: this.state.count - 1 });
+ return this.state.count;
+ }
+}
+```
+
+
+
+## 3. Update Wrangler configuration
+
+Add the Durable Object binding and migration:
+
+
+
+```jsonc
+{
+ "name": "my-existing-project",
+ "main": "src/index.ts",
+ "upload_source_maps": true,
+ "compatibility_date": "$today",
+ "compatibility_flags": ["nodejs_compat"],
+
+ "durable_objects": {
+ "bindings": [
+ {
+ "name": "CounterAgent",
+ "class_name": "CounterAgent",
+ },
+ ],
+ },
+
+ "migrations": [
+ {
+ "tag": "v1",
+ "new_sqlite_classes": ["CounterAgent"],
+ },
+ ],
+}
+```
+
+
+
+**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
+
+## 4. Configure TypeScript and Vite
+
+If you use `@callable()` decorators (as in the example above), you need two build configurations.
+
+**tsconfig.json** — extend `agents/tsconfig` (or set `"target": "ES2021"` manually):
+
+```json
+{
+ "extends": "agents/tsconfig"
+}
+```
+
+If you have an existing `tsconfig.json` with custom settings, you can extend and override:
+
+```json
+{
+ "extends": "agents/tsconfig",
+ "compilerOptions": {
+ "paths": { "~/*": ["./src/*"] }
+ }
+}
+```
+
+**vite.config.ts** — add the `agents()` plugin (handles TC39 decorator transforms for Vite 8):
+
+
+
+```ts
+import agents from "agents/vite";
+
+export default defineConfig({
+ plugins: [
+ agents(),
+ // ... your existing plugins
+ ],
+});
+```
+
+
+
+If your project does not use Vite, the `tsconfig.json` change alone is sufficient — your bundler must support TC39 decorators (stage 3, version `2023-11`).
+
+For more details, refer to the [TypeScript configuration](/agents/platform/configuration/#typescript-configuration) and [Vite configuration](/agents/platform/configuration/#vite-configuration) reference.
+
+## 5. Export the Agent class
+
+Your agent class must be exported from your main entry point. Update your `src/index.ts`:
+
+
+
+```ts
+// Export the agent class (required for Durable Objects)
+export { CounterAgent } from "./agents/counter";
+
+// Your existing exports...
+export default {
+ // ...
+} satisfies ExportedHandler;
+```
+
+
+
+## 6. Wire up routing
+
+Choose the approach that matches your project structure:
+
+### Plain Workers (fetch handler)
+
+
+
+```ts
+import { routeAgentRequest } from "agents";
+export { CounterAgent } from "./agents/counter";
+
+export default {
+ async fetch(request: Request, env: Env, ctx: ExecutionContext) {
+ // Try agent routing first
+ const agentResponse = await routeAgentRequest(request, env);
+ if (agentResponse) return agentResponse;
+
+ // Your existing routing logic
+ const url = new URL(request.url);
+ if (url.pathname === "/api/hello") {
+ return Response.json({ message: "Hello!" });
+ }
+
+ return new Response("Not found", { status: 404 });
+ },
+} satisfies ExportedHandler;
+```
+
+
+
+### Hono
+
+
+
+```ts
+import { Hono } from "hono";
+import { agentsMiddleware } from "hono-agents";
+export { CounterAgent } from "./agents/counter";
+
+const app = new Hono<{ Bindings: Env }>();
+
+// Add agents middleware - handles WebSocket upgrades and agent HTTP requests
+app.use("*", agentsMiddleware());
+
+// Your existing routes continue to work
+app.get("/api/hello", (c) => c.json({ message: "Hello!" }));
+
+export default app;
+```
+
+
+
+### With static assets
+
+If you are serving static assets alongside agents, static assets are served first by default. Your Worker code only runs for paths that do not match a static asset:
+
+
+
+```ts
+import { routeAgentRequest } from "agents";
+export { CounterAgent } from "./agents/counter";
+
+export default {
+ async fetch(request: Request, env: Env, ctx: ExecutionContext) {
+ // Static assets are served automatically before this runs
+ // This only handles non-asset requests
+
+ // Route to agents
+ const agentResponse = await routeAgentRequest(request, env);
+ if (agentResponse) return agentResponse;
+
+ return new Response("Not found", { status: 404 });
+ },
+} satisfies ExportedHandler;
+```
+
+
+
+Configure assets in the Wrangler configuration file:
+
+
+
+```jsonc
+{
+ "assets": {
+ "directory": "./public",
+ },
+}
+```
+
+
+
+## 7. Generate TypeScript types
+
+Do not hand-write your `Env` interface. Run [`wrangler types`](/workers/wrangler/commands/general/#types) to generate a type definition file that matches your Wrangler configuration. This catches mismatches between your config and code at compile time instead of at deploy time.
+
+Re-run `wrangler types` whenever you add or rename a binding.
+
+```sh
+npx wrangler types
+```
+
+This creates a type definition file with all your bindings typed, including your agent Durable Object namespaces. The `Agent` class defaults to using the generated `Env` type, so you do not need to pass it as a type parameter — `extends Agent` is sufficient unless you need to pass a second type parameter for state (for example, `Agent`).
+
+Refer to [Configuration](/agents/platform/configuration/#generating-types) for more details on type generation.
+
+## 8. Connect from the frontend
+
+### React
+
+
+
+```tsx
+import { useState } from "react";
+import { useAgent } from "agents/react";
+import type { CounterAgent, CounterState } from "./agents/counter";
+
+function CounterWidget() {
+ const [count, setCount] = useState(0);
+
+ const agent = useAgent({
+ agent: "CounterAgent",
+ onStateUpdate: (state) => setCount(state.count),
+ });
+
+ return (
+ <>
+ {count}
+
+
+ >
+ );
+}
+```
+
+
+
+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
+
+
+
+```ts
+import { AgentClient } from "agents/client";
+
+const agent = new AgentClient({
+ agent: "CounterAgent",
+ name: "user-123", // Optional: unique instance name
+ onStateUpdate: (state) => {
+ document.getElementById("count").textContent = state.count;
+ },
+});
+
+// Call methods
+document.getElementById("increment").onclick = () => agent.call("increment");
+```
+
+
+
+
+
+## Deploy to Cloudflare
+
+```sh
+npm run deploy
+```
+
+Your agent is now live on Cloudflare's global network, running close to your users.
+
+
+
+
+
+
diff --git a/src/content/docs/agents/agent-class/communication-channels/chat/client-sdk.mdx b/src/content/docs/agents/agent-class/communication-channels/chat/client-sdk.mdx
index 32340fabe03..915f3b7292e 100644
--- a/src/content/docs/agents/agent-class/communication-channels/chat/client-sdk.mdx
+++ b/src/content/docs/agents/agent-class/communication-channels/chat/client-sdk.mdx
@@ -673,7 +673,7 @@ const agentTools = useAgentToolEvents({ agent });
diff --git a/src/content/docs/agents/runtime/lifecycle/get-current-agent.mdx b/src/content/docs/agents/agent-class/get-current-agent.mdx
similarity index 99%
rename from src/content/docs/agents/runtime/lifecycle/get-current-agent.mdx
rename to src/content/docs/agents/agent-class/get-current-agent.mdx
index e33dd267c40..96a3a1402a5 100644
--- a/src/content/docs/agents/runtime/lifecycle/get-current-agent.mdx
+++ b/src/content/docs/agents/agent-class/get-current-agent.mdx
@@ -3,7 +3,7 @@ title: getCurrentAgent()
description: Access the current Agent context from external utility functions using getCurrentAgent() in the Agents SDK.
pcx_content_type: concept
sidebar:
- order: 29
+ order: 17
products:
- agents
---
@@ -283,7 +283,7 @@ The context available depends on how the method was invoked:
| `onEmail()` | Yes | No | No | Yes |
| Custom method (via RPC) | Yes | Yes | No | No |
| Scheduled task | Yes | No | No | No |
-| Queue callback | Yes | Depends | Depends | Depends |
+| Queue callback | Yes | No | No | No |
## Best practices
diff --git a/src/content/docs/agents/agent-class/get-started.mdx b/src/content/docs/agents/agent-class/get-started.mdx
new file mode 100644
index 00000000000..34278b59f3a
--- /dev/null
+++ b/src/content/docs/agents/agent-class/get-started.mdx
@@ -0,0 +1,245 @@
+---
+title: Get started with the Agent class
+pcx_content_type: get-started
+sidebar:
+ order: 0
+ label: Get started
+description: Build a counter with the Agent class, with persistent state that syncs to a React frontend in real time.
+products:
+ - agents
+---
+
+import {
+ TypeScriptExample,
+ WranglerConfig,
+ PackageManagers,
+ 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.
+
+**What you will build:** A counter agent with persistent state that syncs to a React frontend in real-time.
+
+**Time:** ~10 minutes
+
+## Create a new project
+
+
+
+Then install dependencies and start the dev server:
+
+```sh
+cd agents-starter
+npm install
+npm run dev
+```
+
+This creates a project with:
+
+- `src/server.ts` — Your agent code
+- `src/client.tsx` — React frontend
+- `wrangler.jsonc` — Cloudflare configuration
+- `tsconfig.json` — Extends `agents/tsconfig` for correct decorator and module settings
+- `vite.config.ts` — Includes the `agents/vite` plugin for decorator support
+
+The starter template includes two important SDK integrations. If you are setting up a project manually, add both:
+
+**tsconfig.json** — extends `agents/tsconfig`, which sets `target: "ES2021"` and other recommended options:
+
+```json
+{
+ "extends": "agents/tsconfig"
+}
+```
+
+**vite.config.ts** — includes the `agents()` plugin, which handles TC39 decorator transforms (required for `@callable()` in Vite 8):
+
+```ts
+import { cloudflare } from "@cloudflare/vite-plugin";
+import react from "@vitejs/plugin-react";
+import agents from "agents/vite";
+import { defineConfig } from "vite";
+
+export default defineConfig({
+ plugins: [agents(), react(), cloudflare()],
+});
+```
+
+Open [http://localhost:5173](http://localhost:5173) to see your agent in action.
+
+## Your first agent
+
+Build a simple counter agent from scratch. Replace `src/server.ts`:
+
+
+
+```ts title="src/server.ts"
+import { Agent, routeAgentRequest, callable } from "agents";
+
+// Define the state shape
+export type CounterState = {
+ count: number;
+};
+
+// Create the agent
+export class CounterAgent extends Agent {
+ // Initial state for new instances
+ initialState: CounterState = { count: 0 };
+
+ // Methods marked with @callable can be called from the client
+ @callable()
+ increment() {
+ this.setState({ count: this.state.count + 1 });
+ return this.state.count;
+ }
+
+ @callable()
+ decrement() {
+ this.setState({ count: this.state.count - 1 });
+ return this.state.count;
+ }
+
+ @callable()
+ reset() {
+ this.setState({ count: 0 });
+ }
+}
+
+// Route requests to agents
+export default {
+ async fetch(request: Request, env: Env, ctx: ExecutionContext) {
+ return (
+ (await routeAgentRequest(request, env)) ??
+ new Response("Not found", { status: 404 })
+ );
+ },
+} satisfies ExportedHandler;
+```
+
+
+
+Update `wrangler.jsonc` to register the agent:
+
+
+
+```jsonc
+{
+ "name": "my-agent",
+ "main": "src/server.ts",
+ "upload_source_maps": true,
+ "compatibility_date": "$today",
+ "compatibility_flags": ["nodejs_compat"],
+ "durable_objects": {
+ "bindings": [
+ {
+ "name": "CounterAgent",
+ "class_name": "CounterAgent",
+ },
+ ],
+ },
+ "migrations": [
+ {
+ "tag": "v1",
+ "new_sqlite_classes": ["CounterAgent"],
+ },
+ ],
+}
+```
+
+
+
+**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`:
+
+```tsx title="src/client.tsx"
+import "./styles.css";
+import { createRoot } from "react-dom/client";
+import { useState } from "react";
+import { useAgent } from "agents/react";
+import type { CounterAgent, CounterState } from "./server";
+
+export default function App() {
+ const [count, setCount] = useState(0);
+
+ // Connect to the Counter agent
+ const agent = useAgent({
+ agent: "CounterAgent",
+ onStateUpdate: (state) => setCount(state.count),
+ });
+
+ return (
+
+
Counter Agent
+
{count}
+
+
+
+
+
+
+ );
+}
+
+const root = createRoot(document.getElementById("root")!);
+root.render();
+```
+
+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
+
+
+
+## Connect from vanilla JavaScript
+
+If you are not using React:
+
+
+
+```ts
+import { AgentClient } from "agents/client";
+
+const agent = new AgentClient({
+ agent: "CounterAgent",
+ name: "my-counter", // optional, defaults to "default"
+ onStateUpdate: (state) => {
+ console.log("New count:", state.count);
+ },
+});
+
+// Call methods
+await agent.call("increment");
+await agent.call("reset");
+```
+
+
+
+## Deploy to Cloudflare
+
+```sh
+npm run deploy
+```
+
+Your agent is now live on Cloudflare's global network, running close to your users.
+
+
+
+
+
+
diff --git a/src/content/docs/agents/agent-class/index.mdx b/src/content/docs/agents/agent-class/index.mdx
index bf96338ed85..f19a485b5b9 100644
--- a/src/content/docs/agents/agent-class/index.mdx
+++ b/src/content/docs/agents/agent-class/index.mdx
@@ -1,22 +1,61 @@
---
title: Agent class
-description: Reference for the Agent base class, its hooks, SQL storage, and error handling in the Agents SDK.
+description: Reference for the legacy Agent base class, its hooks, SQL storage, and error handling in the Agents SDK. New agents should start with a harness on a Durable Object.
pcx_content_type: reference
sidebar:
- order: 8
+ order: 11
group:
hideIndex: false
+ badge: Legacy
products:
- agents
---
import { Render, LinkCard } from "~/components";
-This page provides an overview of the Agents SDK. For detailed documentation on each feature, refer to the linked reference pages.
+
-## Overview
+The `Agent` class is a [Durable Object](/durable-objects/) with the [Lifecycle](/agents/lifecycle/) and a fixed set of [capabilities](/agents/capabilities/) already installed. Extend it when you need a feature that only the `Agent` class has today:
-The Agents SDK provides two main APIs:
+- `@callable()` methods and the `useAgent` client
+- [Chat agents](/agents/agent-class/communication-channels/chat/) with `AIChatAgent` and `useAgentChat`
+- [Voice](/agents/agent-class/communication-channels/voice/), [email](/agents/agent-class/communication-channels/email/), and the other [communication channels](/agents/agent-class/communication-channels/)
+- [Project Think](/agents/harnesses/think/), which extends `Agent`.
+
+The pages in this section document the `Agent` class and the classes that extend it.
+
+## Move from the Agent class to a harness
+
+Each `Agent` API calls a capability underneath. To move an agent off the `Agent` class, extend `DurableObject`, install the Lifecycle, and use the capability from this table directly. For how request, alarm, and WebSocket handling work on the Lifecycle, refer to [Move an existing Durable Object onto the Lifecycle](/agents/getting-started/add-to-existing-project/#move-an-existing-durable-object-onto-the-lifecycle).
+
+| Agent API | Capability |
+| ---------------------------------------------------------------------- | ------------------------------------------------ |
+| `this.schedule()`, `this.scheduleEvery()`, `this.cancelSchedule()` | [`Scheduler`](/agents/capabilities/#scheduler) |
+| `this.queue()`, `this.dequeue()`, `this.getQueue()` | [`Queue`](/agents/capabilities/#queue) |
+| `this.addMcpServer()`, `this.removeMcpServer()`, `this.mcp` | [`MCPClientManager`](/agents/capabilities/#mcp-client) |
+| `this.state`, `this.setState()`, `onStateChanged()` | [`State`](/agents/capabilities/#state) |
+| `onConnect()`, `onMessage()`, `this.broadcast()`, `getConnections()` | [`WebSockets`](/agents/capabilities/#websockets) |
+| `this.tasks` | [`Tasks`](/agents/capabilities/#tasks) |
+
+To stay on the `Agent` class and add another capability, such as the [Pi harness](/agents/harnesses/pi/), install it on `this.lifecycle` in the constructor:
+
+```ts
+import { Agent } from "agents";
+import { PiHarness } from "agents/harness/pi";
+
+export class Assistant extends Agent {
+ harness = new PiHarness({
+ /* model and tools */
+ });
+
+ constructor(ctx: DurableObjectState, env: Env) {
+ super(ctx, env);
+ this.lifecycle.use(this.harness);
+ }
+}
+```
+
+The Agents SDK provides two main APIs for the Agent class:
| API | Description |
| ----------------------------- | -------------------------------------------------------------------------------- |
@@ -25,7 +64,7 @@ The Agents SDK provides two main APIs:
:::note
-Agents require [Cloudflare Durable Objects](/durable-objects/). Refer to [Configuration](/agents/runtime/operations/configuration/) to learn how to add the required bindings.
+Agents require [Cloudflare Durable Objects](/durable-objects/). Refer to [Configuration](/agents/platform/configuration/) to learn how to add the required bindings.
:::
@@ -54,7 +93,7 @@ Each Agent can have millions of instances. Each instance is a separate micro-ser
-## Lifecycle
+## Hooks
```mermaid
flowchart TD
@@ -101,13 +140,13 @@ flowchart TD
| **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/agent-class/using-ai-models/) |
| **Protocol messages** | `shouldSendProtocolMessages()`, `isConnectionProtocolEnabled()` | [Protocol messages](/agents/agent-class/protocol-messages/) |
-| **Context** | `getCurrentAgent()` | [getCurrentAgent()](/agents/runtime/lifecycle/get-current-agent/) |
-| **Tracing** | `wrapAISDK()` | [Tracing](/agents/runtime/operations/observability/tracing/) |
-| **Diagnostics channels** | `subscribe()`, diagnostics channels | [Diagnostics channels](/agents/runtime/operations/observability/diagnostics-channels/) |
+| **Context** | `getCurrentAgent()` | [getCurrentAgent()](/agents/lifecycle/get-current-agent/) |
+| **Tracing** | `wrapAISDK()` | [Tracing](/agents/platform/observability/tracing/) |
+| **Diagnostics channels** | `subscribe()`, diagnostics channels | [Diagnostics channels](/agents/platform/observability/diagnostics-channels/) |
| **Sub-agents** | `subAgent()`, `abortSubAgent()`, `deleteSubAgent()` | [Sub-agents](/agents/agent-class/sub-agents/) |
| **Agents as tools** | `runAgentTool()`, `clearAgentToolRuns()`, `hasAgentToolRun()` | [Agents as tools](/agents/agent-class/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/) |
+| **Agent Skills** | `skills` registry, bundled skill sources, script runners | [Agent Skills](/agents/tools/agent-skills/) |
+| **Sessions** | `Session.create()`, context blocks, compaction, search | [Sessions](/agents/capabilities/sessions/) |
| **Think** | `Think` base class, workspace tools, lifecycle hooks, extensions | [Think](/agents/harnesses/think/) |
| **Chat SDK** | `createChatSdkState()`, `ChatSdkStateAgent` | [Chat SDK](/agents/agent-class/communication-channels/chat-sdk/) |
@@ -211,14 +250,14 @@ Refer to [Routing](/agents/agent-class/routing/) for custom paths, CORS, and ins
## Next steps
diff --git a/src/content/docs/agents/agent-class/internals.mdx b/src/content/docs/agents/agent-class/internals.mdx
index 27bf5913a3a..4bce519f0be 100644
--- a/src/content/docs/agents/agent-class/internals.mdx
+++ b/src/content/docs/agents/agent-class/internals.mdx
@@ -1,6 +1,6 @@
---
title: Agent class internals
-description: Explore how the Agent class extends Durable Objects to provide state, WebSockets, scheduling, and RPC.
+description: Explore how the Agent class composes a Durable Object lifecycle and capabilities for state, WebSockets, scheduling, and RPC.
pcx_content_type: concept
sidebar:
order: 1
@@ -10,13 +10,20 @@ products:
The core of the `agents` library is the `Agent` class. You extend it, override a few methods, and get state management, WebSockets, scheduling, RPC, and more for free. This page explains how `Agent` is built, layer by layer, so you understand what is happening under the hood.
-The snippets shown here are illustrative and do not necessarily represent best practices. For the full API, refer to the [API reference](/agents/runtime/) and the [source code](https://github.com/cloudflare/agents/blob/main/packages/agents/src/index.ts).
+The snippets shown here are illustrative and do not necessarily represent best practices. For the full API, refer to the [API reference](/agents/agent-class/) and the [source code](https://github.com/cloudflare/agents/blob/main/packages/agents/src/index.ts).
## What is the Agent?
-The `Agent` class is an extension of `DurableObject` — agents _are_ Durable Objects. If you are not familiar with Durable Objects, read [What are Durable Objects](/durable-objects/) first. At their core, Durable Objects are globally addressable (each instance has a unique ID), single-threaded compute instances with long-term storage (key-value and SQLite).
+`Agent` directly extends the platform `DurableObject` class. Each Agent is a globally addressable compute instance with durable key-value and SQLite storage. For more information, refer to [What are Durable Objects](/durable-objects/).
-`Agent` does not extend `DurableObject` directly. It extends `Server` from the [`partyserver`](https://github.com/cloudflare/partykit/tree/main/packages/partyserver) package, which extends `DurableObject`. Think of it as layers: **DurableObject** > **Server** > **Agent**.
+Each Agent also installs a [`Lifecycle`](/agents/lifecycle/) instance. The lifecycle connects the Agent to reusable [capabilities](/agents/capabilities/).
+
+```txt
+DurableObject
+└── Agent
+ └── Lifecycle
+ └── capabilities
+```
## Layer 0: Durable Object
@@ -81,9 +88,9 @@ export class MyDurableObject extends DurableObject {
### `alarm()`
-HTTP and RPC requests are not the only entrypoints for a Durable Object. Alarms allow developers to schedule an event to trigger at a later time. Whenever the next alarm is due, the runtime will call the `alarm()` method, which is left to the developer to implement.
+HTTP and RPC requests are not the only Durable Object entry points. A plain Durable Object can schedule one alarm with `ctx.storage.setAlarm()` and handle it through `alarm()`. For more information, refer to [Alarms](/durable-objects/api/alarms/).
-To schedule an alarm, you can use the `this.ctx.storage.setAlarm()` method. For more information, refer to [Alarms](/durable-objects/api/alarms/).
+Lifecycle owns the physical alarm for an Agent. Do not override `alarm()` or call `ctx.storage.setAlarm()` from Agent code. Use `this.schedule()` for Agent callbacks. A reusable capability can push durable jobs through its scoped Lifecycle job queue.
### `this.ctx`
@@ -107,94 +114,25 @@ const token = this.ctx.storage.get("someToken");
Lastly, it is worth mentioning that the Durable Object also has the Worker `Env` in `this.env`. Learn more in [Bindings](/workers/runtime-apis/bindings).
-## Layer 1: `Server` (partyserver)
-
-Now that you have seen what Durable Objects provide out of the box, the `Server` class from [`partyserver`](https://github.com/cloudflare/partykit/tree/main/packages/partyserver) will make more sense. It is an opinionated `DurableObject` wrapper that replaces low-level primitives with developer-friendly callbacks.
-
-`Server` does not add any storage operations of its own — it only wraps the Durable Object lifecycle.
-
-### Addressing
-
-`partyserver` exposes helpers to address Durable Objects by name instead of going through bindings manually. This includes a URL routing scheme (`/servers/:durableClass/:durableName`) that the Agent layer builds on.
-
-```ts
-// Note the await here!
-const stub = await getServerByName(env.MY_DO, "foo");
-
-// We can still call RPC methods.
-await stub.bar();
-```
-
-The URL scheme also enables a request router. In the Agent layer, this is re-exported as `routeAgentRequest`:
-
-```ts
- async fetch(request: Request, env: Env, ctx: ExecutionContext) {
- const res = await routeAgentRequest(request, env);
-
- if (res) return res;
-
- return new Response("Not found", { status: 404 });
- }
-```
+## Layer 1: lifecycle composition
-### `onStart`
+`Agent` uses `Lifecycle.install(this)` to install its platform-facing request, alarm, and hibernating WebSocket event handlers. Agent subclasses implement semantic callbacks such as `onStart()` and `onRequest()` instead of forwarding those platform events.
-The addressing layer allows `Server` to expose an `onStart` callback that runs every time the Durable Object starts up (after eviction, hibernation, or first creation) and before any `fetch` or RPC call.
+The `Agent` class installs `State`, `Queue`, `Scheduler`, `Tasks`, `MCPClientManager`, and `WebSockets`, plus its internal dynamic-agent capability. Capabilities start before Agent startup. Selective capabilities run before catch-alls, such as `WebSockets`. Queues, schedules, and tasks share one Lifecycle-managed Durable Object alarm.
-```ts
-class MyServer extends Server {
- onStart() {
- // Some initialization logic that you wish
- // to run every time the DO is started up.
- const sql = this.ctx.storage.sql;
- sql.exec(`...`);
- }
-}
-```
+You can install additional capabilities through `this.lifecycle.use(capability)`. The same capability can also run on a class that directly extends `DurableObject`.
-### `onRequest` and `onConnect`
-
-`Server` already implements `fetch` for the underlying Durable Object and exposes two different callbacks that developers can make use of, `onRequest` and `onConnect` for HTTP requests and incoming WS connections, respectively (WebSocket connections are accepted by default).
-
-```ts
-class MyServer extends Server {
- async onRequest(request: Request) {
- const url = new URL(request.url);
-
- return new Response(`Hello from ${url.origin}!`);
- }
-
- async onConnect(conn, ctx) {
- const { request } = ctx;
- const url = new URL(request.url);
-
- // Connections are a WebSocket wrapper
- conn.send(`Hello from ${url.origin}!`);
- }
-}
-```
-
-### WebSockets
-
-Just as `onConnect` is the callback for every new connection, `Server` also provides wrappers on top of the default callbacks from the `DurableObject` class: `onMessage`, `onClose` and `onError`.
-
-There's also `this.broadcast` that sends a WS message to all connected clients (no magic, just a loop over `this.getConnections()`!).
-
-### `this.name`
-
-It is hard to get a Durable Object's `name` from within it. `partyserver` tries to make it available in `this.name` but it is not a perfect solution. Learn more about it in [this GitHub issue](https://github.com/cloudflare/workerd/issues/2240).
+Lifecycle reads names assigned through `idFromName()` or `getByName()` from `ctx.id.name`. The Agent exposes this value as `this.name`.
## Layer 2: Agent
-Now finally, the `Agent` class. `Agent` extends `Server` and provides opinionated primitives for stateful, schedulable, and observable agents that can communicate via RPC, WebSockets, and (even!) email.
+The `Agent` class directly extends `DurableObject`. It adds state, scheduling, RPC, WebSockets, MCP clients, email, and other agent-focused APIs.
### `this.state` and `this.setState()`
-One of the core features of `Agent` is **automatic state persistence**. Developers define the shape of their state via the generic parameter and `initialState` (which is only used if no state exists in storage), and the Agent handles loading, saving, and broadcasting state changes (check `Server`'s `this.broadcast()` above).
+Developers define the state shape through a generic parameter and `initialState`. The `State` capability owns lazy loading, caching, validation, and persistence. The `WebSockets` capability owns state frames, broadcasting, and connection flags.
-`this.state` is a getter that lazily loads state from storage (SQL). State is persisted across Durable Object evictions when it is updated with `this.setState()`, which automatically serializes the state and writes it back to storage.
-
-There's also `this.onStateChanged` that you can override to react to state changes.
+`this.state` reads the stored value, while `this.setState()` validates and persists changes. `initialState` seeds a value only when none exists. Override `this.onStateChanged` to react after a change.
```ts
class MyAgent extends Agent {
@@ -210,7 +148,7 @@ class MyAgent extends Agent {
}
```
-State is stored in the `cf_agents_state` SQL table. State messages are sent with `type: "cf_agent_state"` (both from the client and the server). Since `agents` provides [JS and React clients](/agents/agent-class/state/#synchronizing-state), real-time state updates are available out of the box.
+State is stored in the `cf_agents_state` SQL table. State messages are sent with `type: "cf_agent_state"` (both from the client and the server). Since `agents` provides [JS and React clients](/agents/agent-class/state/#client-side-state-sync), real-time state updates are available out of the box.
### `this.sql`
@@ -251,7 +189,9 @@ class MyAgent extends Agent {
}
```
-Clients can invoke this method by sending a WebSocket message:
+The default `"cf-websocket"` transport invokes decorated methods through JSON RPC frames. Decorators do not populate the native Cap'n Web interface. Plain Durable Objects expose their interface through `WebSockets({ callables: new MyCalls() })`, where `MyCalls` extends `RpcTarget`.
+
+Clients can invoke the decorated method by sending a WebSocket message:
```json
{
@@ -272,7 +212,9 @@ console.log(result); // 5
### `this.queue` and friends
-Agents include a built-in task queue for deferred execution. This is useful for offloading work or retrying operations. The available methods are `this.queue`, `this.dequeue`, `this.dequeueAll`, `this.dequeueAllByCallback`, `this.getQueue`, and `this.getQueues`.
+Agent queue methods delegate to the `Queue` capability. `this.queue()` durably accepts a callback and returns its item identifier. Queue callbacks run from the alarm loop, one at a time in push order.
+
+The methods `this.dequeue`, `this.dequeueAll`, `this.dequeueAllByCallback`, `this.getQueue`, and `this.getQueues` return promises. Await inspection and removal operations. Queue items expose `createdAt` as a Unix timestamp in seconds, replacing `created_at`.
```ts
class MyAgent extends Agent {
@@ -287,7 +229,9 @@ class MyAgent extends Agent {
}
```
-Tasks are stored in the `cf_agents_queues` SQL table and are automatically flushed in sequence. If a task succeeds, it is automatically dequeued.
+Each queued item is a job in `cf_agents_jobs`. Startup migrates the legacy `cf_agents_queues` rows and drops that table. Callbacks no longer inherit the enqueuing request or connection through `getCurrentAgent()`; only the host Agent remains in scope.
+
+A callback can run again after interruption, so external effects must be idempotent. An item that exhausts its application retries is removed after error reporting.
### `this.schedule` and friends
@@ -324,7 +268,7 @@ class MyAgent extends Agent {
}
```
-Schedules are stored in the `cf_agents_schedules` SQL table. Cron schedules automatically reschedule themselves after execution, while one-time schedules are deleted.
+The `Scheduler` stores schedules in the Lifecycle-owned `cf_agents_jobs` queue. Startup migrates legacy `cf_agents_schedules` rows and drops that table. Cron schedules reschedule after execution, while one-time schedules complete and are deleted.
### `this.mcp` and friends
@@ -402,7 +346,7 @@ function someUtilityFunction() {
### `this.onError`
-`Agent` extends `Server`'s `onError` so it can be used to handle errors that are not necessarily WebSocket errors. It is called with a `Connection` or `unknown` error.
+`Agent` exposes `onError()` for WebSocket and other Agent errors. It receives either a `Connection` with an error, or an error value.
```ts
class MyAgent extends Agent {
@@ -411,12 +355,12 @@ class MyAgent extends Agent {
// WebSocket connection error
console.error("Connection error:", error);
} else {
- // Server error
- console.error("Server error:", connectionOrError);
+ // Non-WebSocket error
+ console.error("Agent error:", connectionOrError);
}
// Optionally throw to propagate the error
- throw connectionOrError;
+ throw error ?? connectionOrError;
}
}
```
@@ -467,20 +411,22 @@ Configure agent behavior by overriding `static options` on your class. All field
```ts
export class MyAgent extends Agent {
static options = {
- hibernate: true,
sendIdentityOnConnect: false,
retry: { maxAttempts: 5, baseDelayMs: 200, maxDelayMs: 5000 },
+ maxAlarmMemoryLimitStrikes: 3,
};
}
```
-| Option | Type | Default | Description |
-| --- | --- | --- | --- |
-| `hibernate` | `boolean` | `true` | Whether the agent hibernates when inactive. WebSocket connections stay open while the DO sleeps |
-| `sendIdentityOnConnect` | `boolean` | `true` | Send identity (agent name, instance name) to clients on WebSocket connect. Set to `false` to hide sensitive instance names |
-| `hungScheduleTimeoutSeconds` | `number` | `30` | Timeout before a running interval schedule is considered hung and force-reset. Increase for long-running callbacks |
-| `keepAliveIntervalMs` | `number` | `30000` | Interval in milliseconds for `keepAlive()` alarm heartbeats. Lower values mean faster recovery but more frequent alarms |
-| `retry` | `RetryOptions` | `{ maxAttempts: 3, baseDelayMs: 100, maxDelayMs: 3000 }` | Default retry options for `schedule()`, `queue()`, and `this.retry()`. Per-task options override these defaults |
+The default `"cf-websocket"` transport uses the WebSocket Hibernation API. A client can select `transport: "capnweb"` instead, which keeps the object in memory while the connection is open. There is no server-side `hibernate` option.
+
+| Option | Type | Default | Description |
+| ---------------------------- | -------------- | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
+| `sendIdentityOnConnect` | `boolean` | `true` | Send identity (agent name, instance name) to clients on WebSocket connect. Set to `false` to hide sensitive instance names |
+| `hungScheduleTimeoutSeconds` | `number` | `30` | Timeout before a running interval schedule is considered hung and force-reset. Increase for long-running callbacks |
+| `keepAliveIntervalMs` | `number` | `30000` | Interval in milliseconds for `keepAlive()` alarm heartbeats. Lower values mean faster recovery but more frequent alarms |
+| `maxAlarmMemoryLimitStrikes` | `number` | `3` | Consecutive alarm memory-limit resets allowed before Lifecycle seals recovery-loop work |
+| `retry` | `RetryOptions` | `{ maxAttempts: 3, baseDelayMs: 100, maxDelayMs: 3000 }` | Default retry options for `schedule()`, `queue()`, and `this.retry()`. Per-task options override these defaults |
### `this.keepAlive()` and `this.keepAliveWhile()`
@@ -511,7 +457,7 @@ class MyAgent extends Agent {
### Routing
-The `Agent` class re-exports the [addressing helpers](#addressing) as `getAgentByName` and `routeAgentRequest`.
+The Agents SDK exports `getAgentByName` and `routeAgentRequest` for named Agent routing.
```ts
const stub = await getAgentByName(env.MY_DO, "foo");
@@ -527,6 +473,6 @@ return new Response("Not found", { status: 404 });
The [`AIChatAgent`](/agents/agent-class/communication-channels/chat/chat-agents/) class from `@cloudflare/ai-chat` extends `Agent` with an opinionated layer for AI chat. It adds automatic message persistence to SQLite, resumable streaming, tool support (server-side, client-side, and human-in-the-loop), and a React hook (`useAgentChat`) for building chat UIs.
-The full hierarchy is: **DurableObject** > **Server** > **Agent** > **AIChatAgent**.
+The full class hierarchy is **DurableObject** > **Agent** > **AIChatAgent**. `Agent` composes Lifecycle and its capabilities instead of adding another base class.
If you are building a chat agent, start with `AIChatAgent`. If you need lower-level control or are not building a chat interface, use `Agent` directly.
diff --git a/src/content/docs/agents/agent-class/routing.mdx b/src/content/docs/agents/agent-class/routing.mdx
index 231edc4fa6a..2a27d860f16 100644
--- a/src/content/docs/agents/agent-class/routing.mdx
+++ b/src/content/docs/agents/agent-class/routing.mdx
@@ -539,7 +539,7 @@ const response = await routeAgentRequest(request, env, {
-These hooks are useful for authentication and validation. Refer to [Cross-domain authentication](/agents/runtime/operations/cross-domain-authentication/) for detailed examples.
+These hooks are useful for authentication and validation. Refer to [Cross-domain authentication](/agents/platform/cross-domain-authentication/) for detailed examples.
## Server-side agent access
@@ -792,7 +792,7 @@ export default app;
-For WebSocket authentication patterns (tokens in URLs, JWT refresh), refer to [Cross-domain authentication](/agents/runtime/operations/cross-domain-authentication/).
+For WebSocket authentication patterns (tokens in URLs, JWT refresh), refer to [Cross-domain authentication](/agents/platform/cross-domain-authentication/).
## Troubleshooting
@@ -909,7 +909,7 @@ class SecureAgent extends Agent {
@@ -921,6 +921,6 @@ class SecureAgent extends Agent {
diff --git a/src/content/docs/agents/agent-class/websockets.mdx b/src/content/docs/agents/agent-class/websockets.mdx
index 8a71e008561..ac51d4a34ef 100644
--- a/src/content/docs/agents/agent-class/websockets.mdx
+++ b/src/content/docs/agents/agent-class/websockets.mdx
@@ -572,6 +572,6 @@ Refer to [Client SDK](/agents/agent-class/communication-channels/chat/client-sdk
diff --git a/src/content/docs/agents/capabilities/index.mdx b/src/content/docs/agents/capabilities/index.mdx
new file mode 100644
index 00000000000..3114a7a8e44
--- /dev/null
+++ b/src/content/docs/agents/capabilities/index.mdx
@@ -0,0 +1,344 @@
+---
+title: Capabilities
+description: Add a harness, MCP client, scheduling, queues, WebSockets, state, sessions, streams, and durable tasks to a Durable Object with Lifecycle capabilities.
+pcx_content_type: concept
+sidebar:
+ order: 5
+ group:
+ hideIndex: false
+products:
+ - agents
+---
+
+import { TypeScriptExample } from "~/components";
+
+A capability is a reusable piece of a Durable Object. Each one owns its tables and behavior, exposes a focused API, and implements only the [Lifecycle](/agents/lifecycle/) hooks it needs. You build an agent by installing the capabilities you want on one Durable Object:
+
+
+
+```ts
+import { DurableObject } from "cloudflare:workers";
+import { Lifecycle } from "agents/lifecycle";
+import { MCPClientManager } from "agents/mcp/client";
+import { Scheduler } from "agents/schedules";
+import { WebSockets } from "agents/websockets";
+
+export class MyAgent extends DurableObject {
+ readonly mcp = new MCPClientManager("my-agent", "1.0.0", { env: this.env });
+ readonly scheduler = new Scheduler({ callbacks: {} });
+ readonly webSockets = new WebSockets();
+
+ readonly lifecycle = Lifecycle.install(this)
+ .use(this.mcp)
+ .use(this.scheduler)
+ .use(this.webSockets);
+}
+```
+
+
+
+Capabilities share the object's storage and its alarm, but they do not depend on each other. Install each one once. Bindings and callbacks a capability needs go in its constructor.
+
+## Available capabilities
+
+The following capabilities ship with the `agents` package. Capabilities marked Experimental may change between releases.
+
+| Capability | Import | Status | What it does |
+| ------------------------------------- | ------------------- | ------------ | -------------------------------------------------------------------- |
+| [`PiHarness`](/agents/harnesses/pi/) | `agents/harness/pi` | Beta | Runs the Pi agent loop, with recovery after eviction |
+| [`MCPClientManager`](#mcp-client) | `agents/mcp/client` | Stable | Connects to MCP servers and keeps the connections across restarts |
+| [`Scheduler`](#scheduler) | `agents/schedules` | Stable | Runs callbacks after a delay, at a date, on a cron, or on an interval |
+| [`WebSockets`](#websockets) | `agents/websockets` | Stable | Accepts hibernating connections that `useAgent` can reach |
+| [`State`](#state) | `agents/state` | Stable | Stores one validated value, and syncs it to clients with `WebSockets` |
+| [`Queue`](#queue) | `agents/queue` | Experimental | Runs background callbacks one at a time, in push order |
+| [`RoutedAgents`](#routed-agents) | `agents/routing` | Experimental | Keeps a catalog of independent agents and forwards requests to them |
+| [`Browser`](#browser) | `agents/browser` | Experimental | Keeps one persistent browser session open for the agent |
+| [`Sessions`](#sessions) | `agents/sessions` | Experimental | Stores conversation history as a tree of messages |
+| [`Streams`](#streams) | `agents/streams` | Experimental | Stores incremental output that clients can replay and follow |
+| [`Tasks`](#tasks) | `agents/tasks` | Experimental | Runs replayable background work with journaled steps |
+
+`PiHarness`, `MCPClientManager`, `RoutedAgents`, and `Browser` give an agent its behavior. `Scheduler`, `Queue`, `WebSockets`, and `State` cover background work and connections. `Sessions`, `Streams`, and `Tasks` are lower-level pieces for [building your own harness](#build-your-own-harness).
+
+To write a capability of your own, refer to [Build a capability](/agents/lifecycle/build-a-capability/).
+
+## MCP client
+
+`MCPClientManager` connects your agent to [Model Context Protocol (MCP)](/agents/model-context-protocol/) servers. It stores the servers you add, reconnects to them on startup, and handles OAuth callbacks before your `onRequest()` runs:
+
+
+
+```ts
+import { DurableObject } from "cloudflare:workers";
+import { Lifecycle } from "agents/lifecycle";
+import { MCPClientManager } from "agents/mcp/client";
+
+export class ToolAgent extends DurableObject {
+ readonly mcp = new MCPClientManager("tool-agent", "1.0.0", {
+ env: this.env,
+ });
+ readonly lifecycle = Lifecycle.install(this).use(this.mcp);
+}
+```
+
+
+
+Pass `env` when you connect to MCP servers over Durable Object RPC. Use `listTools()` for the raw tool list or `getAITools()` for AI SDK tools. For more information, refer to the [MCP client API](/agents/model-context-protocol/apis/client-api/).
+
+## Scheduler
+
+`Scheduler` runs registered callbacks after a delay, at a date, from a cron expression, or at a fixed interval:
+
+
+
+```ts
+import { DurableObject } from "cloudflare:workers";
+import { Lifecycle } from "agents/lifecycle";
+import { Scheduler } from "agents/schedules";
+
+export class ReminderAgent extends DurableObject {
+ readonly scheduler = new Scheduler({
+ callbacks: {
+ sendReminder: (payload: { message: string }) => {
+ console.log(payload.message);
+ },
+ },
+ });
+ readonly lifecycle = Lifecycle.install(this).use(this.scheduler);
+
+ async onRequest(): Promise {
+ const schedule = await this.scheduler.set(60, "sendReminder", {
+ message: "One minute has passed",
+ });
+ return Response.json(schedule, { status: 201 });
+ }
+}
+```
+
+
+
+Register callbacks in the constructor so that every wake rebuilds the same map. `set()` and `every()` check the callback name and payload type against it. Cron and interval schedules deduplicate, so you can create them in `onStart()`.
+
+## WebSockets
+
+`WebSockets` accepts WebSocket upgrades and speaks the Agents protocol, so a plain Durable Object works with `useAgent` and `AgentClient`:
+
+
+
+```ts
+import { DurableObject, RpcTarget } from "cloudflare:workers";
+import { Lifecycle } from "agents/lifecycle";
+import { WebSockets } from "agents/websockets";
+
+class RoomMethods extends RpcTarget {
+ ping(): string {
+ return "pong";
+ }
+}
+
+export class RoomAgent extends DurableObject {
+ readonly webSockets = new WebSockets({
+ callables: new RoomMethods(),
+ handlers: {
+ onMessage: (connection, message) => connection.send(message),
+ },
+ });
+ readonly lifecycle = Lifecycle.install(this).use(this.webSockets);
+}
+```
+
+
+
+Clients call the methods on `callables` with `agent.stub.ping()` or `agent.call("ping")`. Connections hibernate by default, so idle clients stay connected while the object leaves memory.
+
+## State
+
+`State` stores one validated value in the object's storage. Pass it to `WebSockets` to sync it with `useAgent().state`:
+
+
+
+```ts
+import { DurableObject } from "cloudflare:workers";
+import { Lifecycle } from "agents/lifecycle";
+import { State } from "agents/state";
+import { WebSockets } from "agents/websockets";
+
+export class CounterAgent extends DurableObject {
+ readonly state: State<{ count: number }> = new State({
+ initialState: { count: 0 },
+ onChanged: (_state, source) => this.webSockets.broadcastState(source),
+ });
+ readonly webSockets: WebSockets = new WebSockets({ state: this.state });
+ readonly lifecycle = Lifecycle.install(this)
+ .use(this.state)
+ .use(this.webSockets);
+}
+```
+
+
+
+`state.get()` reads the value and `state.set(next)` validates and stores it. Pass `validateStateChange` to reject a change by throwing.
+
+## Queue
+
+`Queue` runs background callbacks one at a time, in push order, from the object's alarm:
+
+
+
+```ts
+import { DurableObject } from "cloudflare:workers";
+import { Lifecycle } from "agents/lifecycle";
+import { Queue } from "agents/queue";
+
+export class UploadAgent extends DurableObject {
+ readonly queue = new Queue({
+ callbacks: {
+ processUpload: (payload: { fileName: string }) => {
+ console.log(`Processing ${payload.fileName}`);
+ },
+ },
+ retry: { maxAttempts: 3 },
+ });
+ readonly lifecycle = Lifecycle.install(this).use(this.queue);
+
+ async onRequest(): Promise {
+ const item = await this.queue.push("processUpload", {
+ fileName: "report.pdf",
+ });
+ return Response.json(item, { status: 202 });
+ }
+}
+```
+
+
+
+`push()` returns once the item is stored, without waiting for the callback. An interrupted callback can run again, so make external effects idempotent.
+
+## Routed agents
+
+`RoutedAgents` lets one hub Durable Object own many independent agents, such as one agent per chat. It stores each agent's ID and metadata, and forwards requests under `/{route}/{id}` to the right agent:
+
+
+
+```ts
+import { DurableObject } from "cloudflare:workers";
+import { Lifecycle } from "agents/lifecycle";
+import { RoutedAgents } from "agents/routing";
+import type { ChatAgent } from "./chat-agent";
+
+export class UserHub extends DurableObject {
+ readonly chats = new RoutedAgents({
+ namespace: this.env.ChatAgent,
+ route: "chats",
+ });
+ readonly lifecycle = Lifecycle.install(this).use(this.chats);
+}
+```
+
+
+
+The hub can be a plain Durable Object. Each routed agent must extend the [`Agent` class](/agents/agent-class/), because deletion uses its teardown. Manage the catalog with `create()`, `list()`, `get()`, `setMetadata()`, and `delete()`.
+
+## Browser
+
+`Browser` keeps one [Browser Run](/browser-run/) session open across requests, so tabs, cookies, and logins carry over:
+
+
+
+```ts
+import { DurableObject } from "cloudflare:workers";
+import { Browser, browserRun } from "agents/browser";
+import { Lifecycle } from "agents/lifecycle";
+
+export class ResearchAgent extends DurableObject {
+ readonly browser = new Browser({ provider: browserRun(this.env.BROWSER) });
+ readonly lifecycle = Lifecycle.install(this).use(this.browser);
+}
+```
+
+
+
+Give the browser to a model with `browserTool()`. For more information, refer to [Browser](/agents/tools/browser/).
+
+## Build your own harness
+
+A harness is the loop around the model. [`PiHarness`](/agents/harnesses/pi/) is one you can install today. To write your own, combine these three capabilities:
+
+- `Sessions` stores the conversation.
+- `Streams` stores the model's output as it arrives.
+- `Tasks` runs each turn as replayable background work.
+
+### Sessions
+
+`Sessions` stores conversation messages as a tree, with branches, compaction, full-text search, and attachments:
+
+
+
+```ts
+import { DurableObject } from "cloudflare:workers";
+import { Lifecycle } from "agents/lifecycle";
+import { Sessions } from "agents/sessions";
+
+export class ChatAgent extends DurableObject {
+ readonly sessions = new Sessions();
+ readonly lifecycle = Lifecycle.install(this).use(this.sessions);
+ readonly session = this.sessions.session();
+}
+```
+
+
+
+One `Sessions` capability holds many sessions by ID. Call `session()` with no ID when the object represents a single conversation.
+
+### Streams
+
+`Streams` stores incremental output as numbered chunks. A client can reconnect, replay what it missed, and follow new chunks:
+
+
+
+```ts
+import { DurableObject } from "cloudflare:workers";
+import { Lifecycle } from "agents/lifecycle";
+import { Streams } from "agents/streams";
+
+export class OutputAgent extends DurableObject {
+ readonly streams = new Streams();
+ readonly lifecycle = Lifecycle.install(this).use(this.streams);
+}
+```
+
+
+
+Producers call `open()`, append JSON chunks, and finish with `close()` or `error()`. `sseResponse()` serves a stream as resumable Server-Sent Events (SSE).
+
+### Tasks
+
+`Tasks` runs background work as named steps. After an interruption, completed steps return their stored results and the run continues from the first unfinished step:
+
+
+
+```ts
+import { DurableObject } from "cloudflare:workers";
+import { Lifecycle } from "agents/lifecycle";
+import { Tasks, type TaskStep } from "agents/tasks";
+
+export class ReportAgent extends DurableObject {
+ readonly tasks = new Tasks({
+ definitions: {
+ "publish-report@v1": async (input: { topic: string }, step: TaskStep) => {
+ const draft = await step.do("draft", () => `Draft about ${input.topic}`);
+ await step.sleep("review", "10 minutes");
+ return { draft };
+ },
+ },
+ });
+ readonly lifecycle = Lifecycle.install(this).use(this.tasks);
+}
+```
+
+
+
+`tasks.run(name, input)` returns a durable receipt. A durable sleep parks the run without keeping the object in memory.
+
+## Capabilities on the Agent class
+
+The [`Agent` class](/agents/agent-class/) installs `Scheduler`, `Queue`, `MCPClientManager`, `State`, `WebSockets`, and `Tasks` for you. Its methods, such as `this.schedule()`, `this.queue()`, `this.addMcpServer()`, and `this.setState()`, call those capabilities. Install any other capability on an Agent with `this.lifecycle.use()` in its constructor.
diff --git a/src/content/docs/agents/runtime/lifecycle/sessions.mdx b/src/content/docs/agents/capabilities/sessions.mdx
similarity index 100%
rename from src/content/docs/agents/runtime/lifecycle/sessions.mdx
rename to src/content/docs/agents/capabilities/sessions.mdx
diff --git a/src/content/docs/agents/concepts/conversation-state-and-memory.mdx b/src/content/docs/agents/concepts/conversation-state-and-memory.mdx
index 7c4c0220643..1925dd23835 100644
--- a/src/content/docs/agents/concepts/conversation-state-and-memory.mdx
+++ b/src/content/docs/agents/concepts/conversation-state-and-memory.mdx
@@ -1,7 +1,7 @@
---
title: Conversation state and memory
pcx_content_type: concept
-description: How agents store and recall information, including read-only context, writable short-form memory, searchable knowledge, and on-demand skills.
+description: How agents store and recall information, including conversation history in Sessions, context blocks in the system prompt, frozen prompts for caching, and compaction.
tags:
- AI
sidebar:
@@ -10,563 +10,135 @@ products:
- agents
---
-import { TypeScriptExample, LinkCard, WranglerConfig } from "~/components";
+import { LinkCard, TypeScriptExample } from "~/components";
-Agents need memory to be useful over time. Without it, every conversation starts from zero. The agent forgets who the user is, what it learned, and what it was doing. Memory is what turns a stateless LLM call into a persistent, context-aware agent.
+Agents need memory to be useful over time. Without it, every conversation starts from zero. The agent forgets who the user is, what it learned, and what it was doing.
-The [Session API](/agents/runtime/lifecycle/sessions/) provides the memory layer for agents built on the Cloudflare Agents SDK. It manages two kinds of memory: **conversation history** (the messages and tool calls that make up a session) and **context memory** (persistent blocks injected into the system prompt that the agent can read, write, search, and load).
+An agent on Cloudflare has two kinds of memory, and both live in its Durable Object's SQLite database:
-Use this page when you need more than simple synced state or flat chat history. For small UI state, use [Store and sync state](/agents/agent-class/state/). For basic chat persistence, `AIChatAgent` stores messages for you. For opinionated long-term memory, Think builds on Session and context blocks.
+- **Conversation history** is the messages and tool calls that make up a conversation. [Harnesses](/agents/harnesses/) store it for you. To store it yourself, use the [`Sessions`](/agents/capabilities/#sessions) capability from `agents/sessions`.
+- **Context** is what the model reads in its system prompt on every turn: its identity, facts it learned, and knowledge it can search. Assemble it with `ContextBlocks` from `agents/context`.
-:::caution[Experimental]
-
-The Session memory APIs currently use `agents/experimental/memory/session`. The concepts are stable, but import paths and details may change before graduation.
+The two are separate on purpose, so an agent can have a prompt without a transcript, or a transcript without a prompt.
+:::caution[Experimental]
+`agents/sessions` and `agents/context` are experimental. Their APIs may change between releases.
:::
-## Conversation history
-
-The most fundamental type of memory is the conversation itself: the messages between the user and the agent, the tool calls the agent made, and the results it received. The Session stores all of this in a tree-structured message history backed by a Session Provider, defaulting to SQLite.
-
-
-
-```ts
-import { Session } from "agents/experimental/memory/session";
-
-// Append messages as the conversation progresses
-await session.appendMessage({
- id: `user-${crypto.randomUUID()}`,
- role: "user",
- parts: [{ type: "text", text: "What's the status of the deployment?" }],
-});
-
-// Read the full conversation history
-const history = await session.getHistory();
-```
-
-
-
-Conversation history persists across Durable Object hibernation and eviction. When the agent wakes up, the full history is available in SQLite. It does not need to be replayed or reconstructed.
-
-Messages are stored in a tree structure via `parent_id`, which enables branching conversations. When you `appendMessage` with a `parentId` that already has children, you create a branch, useful for features like response regeneration. `getHistory(leafId)` walks any chosen path through the tree.
-
-The Session also provides full-text search across the conversation history:
-
-
-
-```ts
-const results = await session.search("deployment Friday", { limit: 10 });
-```
-
-
-
-As conversations grow long, [compaction](#compaction) summarizes older messages to keep the context window manageable without losing the underlying data.
-
-## Context memory
-
-Context memory is persistent information injected into the system prompt, separate from the conversation history. It gives the agent access to identity, instructions, learned facts, knowledge bases, and reference material across every turn.
-
-The Session API supports four types of context memory, each suited to different kinds of information. The type is determined by the **provider** backing the context block. The Session detects the provider's capabilities automatically.
-
-### Read-only context
-
-This is your traditional system prompt: the agent's identity, personality, and instructions. You might write it directly in your codebase, load it from a `SOUL.md` file in R2, or fetch it from an API. The content is injected into the system prompt and the agent cannot modify it.
-
-A coding assistant might have a soul that defines its personality and constraints:
-
-
-
-```ts
-import { Session } from "agents/experimental/memory/session";
-
-const session = Session.create(this).withContext("soul", {
- provider: {
- get: async () =>
- "You are a senior TypeScript engineer. You write concise, " +
- "well-tested code. You prefer composition over inheritance. " +
- "When you are unsure, you say so rather than guessing.",
- },
-});
-```
-
-
-
-Or load it from R2 so you can update the agent's personality without redeploying:
-
-
-
-```ts
-const session = Session.create(this).withContext("soul", {
- provider: {
- get: async () => {
- const obj = await env.CONFIG_BUCKET.get("soul.md");
- return obj ? obj.text() : "You are a helpful assistant.";
- },
- },
-});
-```
-
-
-
-Read-only blocks are defined by providing an object with only a `get()` method. No tools are generated. The content appears in the system prompt and the agent has no way to change it.
+For small UI state that syncs to clients, use the [`State`](/agents/capabilities/#state) capability instead.
-### Writable short-form context
-
-Think of this as a scratchpad the agent maintains for itself, a place to jot down things it needs to remember. Like how Claude Code keeps a todo list of tasks to work through, or how a customer support agent might track what it has learned about the user during the conversation.
-
-
-
-```ts
-const session = Session.create(this)
- .withContext("memory", {
- description: "Important facts learned during conversation",
- maxTokens: 1100,
- })
- .withContext("todos", {
- description: "Task list, track what needs to be done and what is complete",
- maxTokens: 2000,
- });
-```
-
-
-
-When you omit the `provider` option in the builder, the Session auto-wires to a SQLite-backed writable provider. The agent gets a `set_context` tool that lets it replace or append content to these blocks. Token limits are enforced, so the agent cannot write more than the `maxTokens` budget allows.
-
-The system prompt renders writable blocks with a token usage indicator so the agent knows how much space it has left:
-
-```txt
-══════════════════════════════════════════════
-MEMORY (Important facts learned during conversation) [45% — 495/1100 tokens] [writable]
-══════════════════════════════════════════════
-User prefers dark mode.
-User's project uses React and TypeScript.
-Deployment target is Cloudflare Workers.
-
-══════════════════════════════════════════════
-TODOS (Task list) [12% — 240/2000 tokens] [writable]
-══════════════════════════════════════════════
-- [x] Set up project scaffolding
-- [ ] Add authentication middleware
-- [ ] Write integration tests
-```
-
-The content persists across messages and survives hibernation. It is always visible in the system prompt, so the agent sees it on every turn without needing to fetch anything.
-
-### Searchable context
-
-When you have a large body of information (a knowledge base, documentation, logs, accumulated notes) you do not want to stuff it all into the system prompt. Searchable context keeps a summary in the system prompt (for example, "42 entries indexed") and lets the agent retrieve specific entries when it needs them.
-
-You provide a provider with a `search()` method. How that search works is entirely up to you: full-text search, vector search via [Vectorize](/vectorize/), a call to an external API, or anything else. The Session does not care about the implementation, only that the provider has a `search()` method.
-
-The built-in `AgentSearchProvider` uses Durable Object SQLite with FTS5 as default:
-
-
-
-```ts
-import { AgentSearchProvider } from "agents/experimental/memory/session";
-
-const session = Session.create(this).withContext("knowledge", {
- description:
- "Searchable knowledge base, search for relevant information before answering",
- provider: new AgentSearchProvider(this),
-});
-```
-
-
-
-But you can implement your own provider backed by any search mechanism:
-
-
-
-```ts
-const session = Session.create(this).withContext("knowledge", {
- description: "Searchable knowledge base",
- provider: {
- get: async () => "Product documentation and FAQs",
- search: async (query) => {
- // Use Vectorize, an external API, whatever you need
- const results = await env.VECTORIZE_INDEX.query(
- await generateEmbedding(query),
- { topK: 5 },
- );
- return results.matches.map((m) => m.metadata.text).join("\n\n");
- },
- set: async (key, content) => {
- // Index new content
- },
- },
-});
-```
-
-
-
-The agent gets a `search_context` tool for querying and a `set_context` tool for indexing new entries. It decides what to search for, and you decide how the search works.
-
-This is the right choice when the agent needs to find specific pieces of information from a large collection, rather than loading entire documents.
-
-### Loadable context (Skills)
-
-Skills are large pieces of context (complete documents, reference guides, runbooks, templates) that the agent can discover and load on demand. Think of them as reference material on a shelf: the agent sees a list of titles and descriptions, picks what is relevant to the current task, loads it, uses it, and unloads it when done.
-
-Unlike searchable context where the agent retrieves small chunks from a larger collection, skills are designed to be loaded whole. When an agent loads a skill, it gets the entire document in its context window.
-
-Skills are backed by the `SkillProvider` interface. A skill provider has three methods:
-
-- **`get()`** returns a metadata listing (titles and descriptions) that appears in the system prompt
-- **`load(key)`** fetches the full content of a specific skill
-- **`set(key, content, description?)`** writes or updates a skill entry (optional)
-
-The system prompt shows available skills as a listing. The `[loadable]` tag tells the LLM that these entries are not inline. It needs to use a tool to access the full content:
-
-```txt
-══════════════════════════════════════════════
-SKILLS [loadable]
-══════════════════════════════════════════════
-- api-ref: API Reference documentation
-- style-guide: Company style guide
-- deploy-checklist: Production deployment checklist
-```
-
-The agent sees the titles, decides which skill is relevant to the current task, and uses `load_context` to pull the full content into its working context. When it is done, it uses `unload_context` to free the space. When the skill provider implements `set()`, the agent can also write back, updating existing skills or creating new ones.
-
-```txt
-Agent sees: "- deploy-checklist: Production deployment checklist"
-User asks: "Walk me through a production deployment"
-Agent calls: load_context({ block: "skills", key: "deploy-checklist" })
-→ Full checklist content is loaded into the agent's working context
-```
-
-#### R2-backed skills
-
-The built-in `R2SkillProvider` stores skills in a Cloudflare R2 bucket. Each skill is an R2 object with optional custom metadata for descriptions.
-
-
-
-```ts
-import { Session, R2SkillProvider } from "agents/experimental/memory/session";
-
-const session = Session.create(this)
- .withContext("soul", {
- provider: {
- get: async () =>
- [
- "You are a helpful assistant with access to skills.",
- "When a user asks you to do something, check the SKILLS section",
- "for a relevant skill and use load_context to load it.",
- ].join("\n"),
- },
- })
- .withContext("memory", {
- description: "Learned facts",
- maxTokens: 1100,
- })
- .withContext("skills", {
- provider: new R2SkillProvider(env.SKILLS_BUCKET, { prefix: "skills/" }),
- })
- .withCachedPrompt();
-```
-
-
-
-The `prefix` option scopes the provider to a subdirectory in the bucket. Skill keys in the metadata listing are shown without the prefix, so `skills/api-ref` becomes `api-ref` in the system prompt.
-
-Use `keys` to allowlist specific prefix-relative skills for `get()` and `load()`:
-
-
-
-```ts
-new R2SkillProvider(env.SKILLS_BUCKET, {
- prefix: "skills/",
- keys: ["deploy-checklist", "api-ref"],
-});
-```
-
-
-
-Add an R2 bucket binding to your Wrangler configuration:
-
-
-
-```jsonc
-{
- "r2_buckets": [
- {
- "binding": "SKILLS_BUCKET",
- "bucket_name": "my-agent-skills"
- }
- ]
-}
-```
-
-
-
-Skills are regular R2 objects. Upload them through any R2 interface (the Wrangler CLI, the dashboard, or the Workers API):
-
-```sh
-# Upload a skill from a file
-wrangler r2 object put my-agent-skills/skills/style-guide --file ./docs/style-guide.md --content-type text/markdown
-```
-
-To add descriptions (shown in the metadata listing), set custom metadata on the R2 object:
-
-
-
-```ts
-await env.SKILLS_BUCKET.put("skills/api-ref", content, {
- customMetadata: { description: "API Reference documentation" },
-});
-```
-
-
+## Conversation history
-#### Custom skill providers
+Each harness keeps its own transcript. For example, the [Pi harness](/agents/harnesses/pi/) stores Pi's transcript in the object's database. You read history through the harness, for example with `harness.messages()`.
-You can back skills with any storage by implementing the `SkillProvider` interface:
+When you build your own loop, install `Sessions` and store messages in it:
```ts
-import type { SkillProvider } from "agents/experimental/memory/session";
-
-class DatabaseSkillProvider implements SkillProvider {
- private db: D1Database;
-
- constructor(db: D1Database) {
- this.db = db;
- }
-
- async get(): Promise {
- const rows = await this.db
- .prepare("SELECT key, description FROM skills ORDER BY key")
- .all();
- if (rows.results.length === 0) return null;
- return rows.results
- .map((r) => `- ${r.key}${r.description ? `: ${r.description}` : ""}`)
- .join("\n");
- }
-
- async load(key: string): Promise {
- const row = await this.db
- .prepare("SELECT content FROM skills WHERE key = ?")
- .bind(key)
- .first();
- return row ? (row.content as string) : null;
- }
-
- async set(key: string, content: string, description?: string): Promise {
- await this.db
- .prepare(
- "INSERT INTO skills (key, content, description) VALUES (?, ?, ?) " +
- "ON CONFLICT(key) DO UPDATE SET content = ?, description = ?",
- )
- .bind(key, content, description ?? null, content, description ?? null)
- .run();
+import { DurableObject } from "cloudflare:workers";
+import { Lifecycle } from "agents/lifecycle";
+import { Sessions } from "agents/sessions";
+
+export class ChatAgent extends DurableObject {
+ readonly sessions = new Sessions();
+ readonly lifecycle = Lifecycle.install(this).use(this.sessions);
+
+ async remember(text: string) {
+ const session = this.sessions.session();
+ await session.appendMessage({
+ id: crypto.randomUUID(),
+ role: "user",
+ parts: [{ type: "text", text }],
+ });
+ return session.getRecentHistory(100_000);
}
}
```
-The Session detects the `load()` method via duck-typing and generates the appropriate tools automatically.
-
-#### Skills vs other memory types
+Messages form a tree, so a conversation can branch when a user edits a message or regenerates an answer. `Sessions` also stores image and file attachments, searches message text, and reads long histories as a stream or in byte-budgeted windows.
-| Aspect | Skills | Writable context | Searchable context |
-| -------------------- | ----------------------------------- | ------------------------ | ---------------------------------- |
-| **In system prompt** | Metadata listing only | Full content | Summary count |
-| **Access pattern** | Load whole document by key | Always visible | Search by query |
-| **Best for** | Large documents, reference material | Short notes, preferences | Large collections of small entries |
-| **Context cost** | Low (until loaded) | Proportional to content | Low (until searched) |
-| **Agent writes?** | Optional (if `set` implemented) | Yes (via `set_context`) | Yes (via `set_context`) |
+## Context blocks
-The key distinction: skills are **lazy**. They cost nearly nothing in the system prompt until the agent decides it needs one. This makes them ideal for large reference material where only a subset is relevant to any given conversation.
+A context block is a labelled section of the system prompt with a provider behind it. What the provider can do decides how the block behaves, and which tools the model gets for it:
-## How agents interact with memory
-
-The Session automatically generates tools based on the provider types of your context blocks. You pass these tools to your LLM alongside your own application-specific tools:
-
-
+| Provider | Block behavior | Tool for the model |
+| ----------------------- | -------------------------------------------------------- | ------------------ |
+| `get()` | Read-only text, such as the agent's identity | None |
+| `get()` and `set()` | Writable memory the model updates, such as user facts | `set_context` |
+| `get()` and `search()` | A summary in the prompt, and full-text search on request | `search_context` |
```ts
-const sessionTools = await session.tools();
-const allTools = { ...sessionTools, ...myApplicationTools };
-
-const result = streamText({
- model: myModel,
- system: await session.freezeSystemPrompt(),
- messages: await convertToModelMessages(await session.getHistory()),
- tools: allTools,
-});
-```
-
-
-
-### Generated tools
-
-The Session generates tools dynamically based on what provider types are present:
-
-| Tool | Generated when | What it does |
-| -------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
-| **`set_context`** | Any writable, skill, or search block exists | Writes content to a named block. For writable blocks, replaces or appends. For skill/search blocks, writes a keyed entry. |
-| **`load_context`** | Any skill block exists | Loads full content of a document by key into the agent's context. |
-| **`unload_context`** | Any skill block exists | Frees context space by removing a previously loaded document. The document remains available for re-loading. |
-| **`search_context`** | Any search block exists | Full-text search within a searchable block. Returns the top results ranked by relevance. |
-| **`session_search`** | Using `SessionManager` | Searches across all sessions (cross-conversation search). |
-
-The tools include descriptions and parameter schemas that tell the LLM which blocks are available and what they are for. The agent decides when and how to use them based on the conversation.
-
-For the full tool signatures and all Session methods, refer to the [Session API reference](/agents/runtime/lifecycle/sessions/).
-
-## The system prompt
-
-Context blocks are assembled into a structured system prompt with clear headers and metadata. Each block gets a labeled section with tags indicating its type and capacity:
-
-```txt
-══════════════════════════════════════════════
-SOUL (Identity) [readonly]
-══════════════════════════════════════════════
-You are a helpful coding assistant who speaks concisely.
-
-══════════════════════════════════════════════
-MEMORY (Important facts) [45% — 495/1100 tokens] [writable]
-══════════════════════════════════════════════
-User prefers dark mode.
-User's project uses React and TypeScript.
-
-══════════════════════════════════════════════
-KNOWLEDGE (Searchable knowledge base) [searchable]
-══════════════════════════════════════════════
-12 entries indexed.
-
-══════════════════════════════════════════════
-SKILLS [loadable]
-══════════════════════════════════════════════
-- api-ref: API Reference documentation
-- style-guide: Company style guide
-```
-
-The tags (`[readonly]`, `[writable]`, `[searchable]`, `[loadable]`) tell the LLM what kind of interaction is possible with each block. Token budgets show the agent how much space remains in writable blocks, helping it manage its own memory.
-
-## Gotchas
-
-### Prompt caching
-
-LLM providers (Anthropic, OpenAI, and others) cache the system prompt prefix. When consecutive requests share the same system prompt, the provider can skip re-processing that prefix, reducing both latency and cost. Breaking the cache (by changing the system prompt) throws away this benefit.
-
-The Session API is designed to work with prompt caching:
-
-- **`freezeSystemPrompt()`** renders the system prompt from all context blocks on the first call, then returns the cached value on subsequent calls. The prompt does not change between turns, even when the agent writes to memory via `set_context`.
-- **`withCachedPrompt()`** persists the frozen prompt to storage, so it survives Durable Object hibernation and eviction. When the agent wakes up, it loads the same prompt without re-fetching from all providers.
-
-When the agent uses `set_context` to update a writable block, the underlying provider is updated immediately (the data is saved), but the frozen system prompt is **not** re-rendered. The LLM sees the update on its next turn only if you explicitly call `refreshSystemPrompt()`, which you typically do between conversation turns, not mid-turn.
-
-This means the system prompt stays stable throughout a multi-step tool-use turn, preserving the provider's prefix cache across every step.
-
-
+import { ContextBlocks } from "agents/context";
-```ts
-const session = Session.create(this)
- .withContext("soul", {
+const context = new ContextBlocks([
+ {
+ label: "soul",
provider: { get: async () => "You are a helpful assistant." },
- })
- .withContext("memory", { description: "Learned facts", maxTokens: 1100 })
- .withCachedPrompt(); // Persist the frozen prompt across hibernation
-
-// During a conversation turn:
-const system = await session.freezeSystemPrompt(); // Same value every call
-const tools = await session.tools();
-
-// ... agent calls set_context to update memory ...
-// The frozen prompt is NOT changed, prefix cache stays warm
+ },
+ {
+ label: "memory",
+ description: "Facts learned about the user",
+ maxTokens: 1_100,
+ provider: memoryProvider,
+ },
+]);
-// Between turns (optional, if you want the agent to see its own updates):
-await session.refreshSystemPrompt();
+const system = await context.freezeSystemPrompt();
+const tools = await context.tools();
```
-
-
-### Compaction
-
-Long conversations eventually exceed the LLM's context window. Compaction addresses this at two levels: **macro-compaction** summarizes ranges of older messages, and **micro-compaction** truncates individual messages that are too large.
+Each block renders with a header that carries its label, its description, how much of its `maxTokens` budget it uses, and a marker such as `[readonly]`, `[writable]`, or `[searchable]`. The markers and budgets tell the model what it can do with each block, so it can manage its own memory.
-#### Macro-compaction
+`agents/context` includes two durable providers. `AgentContextProvider` stores one writable block per row in SQLite, and `AgentSearchProvider` backs a searchable block with a full-text index. Both take anything with a tagged-template `sql` method, such as an `Agent`.
-Macro-compaction summarizes older messages, but it never deletes the originals.
+## Frozen prompts and caching
-It uses **overlays**: a summary is stored in a separate table, keyed by the message range it covers. When `getHistory()` is called, overlays are applied transparently at read time. The compacted range is replaced by a synthetic summary message. The underlying messages remain in SQLite, preserving the full conversation for audit, search, and branching.
+Model providers cache the prefix of a request. If the system prompt changes between turns, the cache misses and every turn pays for the full prompt again.
-```txt
-Messages: [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]
- ↓ compaction ↓
-Overlay: [1] [2] [SUMMARY of 3-7] [8] [9] [10]
- ↑ tail protected
-```
+`freezeSystemPrompt()` renders the prompt once and returns the same string on every later call. A write to a block goes to its provider immediately, but it does not change the frozen prompt until you call `refreshSystemPrompt()`. Pass a writable provider as the second argument to `ContextBlocks` to store the frozen prompt, so a restarted agent reuses the exact prompt the model already cached.
-The key points:
+For a block that changes while the prompt is frozen, such as the current date, set `whenChanged: "remind"`. `reminder()` then returns the block's new value for you to send after the cached prefix, for example at the end of the latest user message, until the next refresh.
-- **Non-destructive**, original messages are never deleted. The full conversation is always available in the database.
-- **Iterative**, when the conversation grows again and triggers another compaction, the existing summary is passed to the LLM to update, not replaced from scratch.
-- **Boundary-aware**, compaction boundaries are shifted to avoid splitting tool call / tool result pairs.
-- **Configurable**, `protectHead` preserves the first N messages (usually the system context), and `tailTokenBudget` keeps the most recent messages intact.
+## Compaction
-
+Long conversations eventually exceed the model's context window. Compaction replaces a range of older messages with a summary when you read history. The original messages stay in storage:
```ts
-import { createCompactFunction } from "agents/experimental/memory/utils/compaction-helpers";
+import { createCompactFunction } from "agents/sessions";
-const session = Session.create(this)
- .withContext("memory", { maxTokens: 1100 })
+session
.onCompaction(
createCompactFunction({
- summarize: (prompt) =>
- generateText({ model: myModel, prompt }).then((r) => r.text),
- protectHead: 3,
- tailTokenBudget: 20000,
- minTailMessages: 2,
+ summarize: async (prompt) => summarizeWithModel(prompt),
+ keepRecentTokens: 20_000,
}),
)
- .compactAfter(100_000); // Auto-compact when token estimate exceeds threshold
-```
-
-
-
-Auto-compaction triggers after `appendMessage()` when the estimated token count exceeds the threshold. Compaction failure is non-fatal, the message is already saved.
-
-#### Micro-compaction
-
-Micro-compaction works at the individual message level rather than across ranges. It handles two problems:
-
-**Read-time truncation**: `truncateOlderMessages()` shortens tool outputs and long text in older messages before sending them to the LLM. Recent messages (last 4 by default) are kept intact. This operates on a copy, stored messages are not mutated.
-
-
-
-```ts
-import { truncateOlderMessages } from "agents/experimental/memory/utils";
-
-const history = await session.getHistory();
-const truncated = truncateOlderMessages(history);
-// Pass truncated history to the LLM
+ .compactAfter(80_000);
```
-
+`compactAfter()` compacts once the session's estimated token count passes the limit.
-**Row size enforcement**: when a message is persisted (typically an assistant message with large tool outputs), it is checked against the SQLite row size limit. Oversized tool outputs are replaced with a preview and a note suggesting the tool be re-run. This prevents individual messages from exceeding storage limits while preserving the conversation flow.
+Refresh the frozen system prompt after a compaction, so the prompt and the summarized history agree.
-## Related
+## Related resources
diff --git a/src/content/docs/agents/concepts/what-are-agents.mdx b/src/content/docs/agents/concepts/what-are-agents.mdx
index 2784b2271c9..81fe348108d 100644
--- a/src/content/docs/agents/concepts/what-are-agents.mdx
+++ b/src/content/docs/agents/concepts/what-are-agents.mdx
@@ -79,13 +79,13 @@ Agents operate in a continuous loop of:
## Building agents on Cloudflare
-The Cloudflare Agents SDK provides the infrastructure for building production agents:
+On Cloudflare, an agent is a [harness](/agents/harnesses/) running on a [Durable Object](/durable-objects/), with the [capabilities](/agents/capabilities/) you plug in:
-- **Persistent state** — Each agent instance has its own SQLite database for storing context and memory
-- **Real-time sync** — State changes automatically broadcast to all connected clients via WebSockets
-- **Hibernation** — Agents sleep when idle and wake on demand, so you only pay for what you use
-- **Global edge deployment** — Agents run close to your users on Cloudflare's network
-- **Built-in capabilities** — Scheduling, task queues, workflows, email handling, and more
+- **A harness**: the [Pi harness](/agents/harnesses/pi/) runs the observe, plan, and execute loop for you.
+- **Persistent state**: each agent instance has its own SQLite database for context and memory.
+- **Durable work**: the [Lifecycle](/agents/lifecycle/) keeps a job queue that wakes the agent after eviction, deploys, and crashes.
+- **Capabilities**: MCP clients, schedules, queues, and real-time WebSocket clients plug in on the same object.
+- **Hibernation**: agents sleep when idle and wake on demand, so you only pay for what you use.
## Next steps
@@ -96,9 +96,9 @@ The Cloudflare Agents SDK provides the infrastructure for building production ag
/>
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 db87dd45b23..553b4987bed 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
@@ -1,6 +1,6 @@
---
-title: Add to existing project
-description: Add the Agents SDK to an existing Cloudflare Workers project with state management and real-time connections.
+title: Add to an existing project
+description: Run an agent in an existing Worker or Durable Object. Install the Lifecycle, add a harness and capabilities, and move fetch, alarm, and WebSocket handlers onto the Lifecycle.
pcx_content_type: how-to
sidebar:
order: 2
@@ -10,340 +10,212 @@ products:
import {
PackageManagers,
+ Steps,
TypeScriptExample,
WranglerConfig,
- 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.
+An agent is a [harness](/agents/harnesses/) running on a Durable Object. Any Durable Object can run one once it installs the [Lifecycle](/agents/lifecycle/). This guide covers adding a new agent to an existing Worker, and moving an existing Durable Object onto the Lifecycle so it can run a harness and plug in [capabilities](/agents/capabilities/).
-## Prerequisites
-
-- An existing Cloudflare Workers project with a Wrangler configuration file
-- Node.js 18 or newer
-
-## 1. Install the package
+## Install the package
-For React applications, no additional packages are needed — React bindings are included.
-
-For Hono applications:
-
-
-
-## 2. Create an Agent
-
-Create a new file for your agent (for example, `src/agents/counter.ts`):
-
-
-
-```ts
-import { Agent, callable } from "agents";
-
-export type CounterState = {
- count: number;
-};
-
-export class CounterAgent extends Agent {
- initialState: CounterState = { count: 0 };
-
- @callable()
- increment() {
- this.setState({ count: this.state.count + 1 });
- return this.state.count;
- }
-
- @callable()
- decrement() {
- this.setState({ count: this.state.count - 1 });
- return this.state.count;
- }
-}
-```
-
-
-
-## 3. Update Wrangler configuration
-
-Add the Durable Object binding and migration:
+The `agents` package requires the `nodejs_compat` compatibility flag:
```jsonc
{
- "name": "my-existing-project",
- "main": "src/index.ts",
- "upload_source_maps": true,
- "compatibility_date": "$today",
"compatibility_flags": ["nodejs_compat"],
-
- "durable_objects": {
- "bindings": [
- {
- "name": "CounterAgent",
- "class_name": "CounterAgent",
- },
- ],
- },
-
- "migrations": [
- {
- "tag": "v1",
- "new_sqlite_classes": ["CounterAgent"],
- },
- ],
}
```
-**Key points:**
+## Add a new agent to a Worker
-- `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
+
-## 4. Configure TypeScript and Vite
+1. Add a SQLite-backed Durable Object binding and migration to your Wrangler configuration file:
-If you use `@callable()` decorators (as in the example above), you need two build configurations.
+
-**tsconfig.json** — extend `agents/tsconfig` (or set `"target": "ES2021"` manually):
+ ```jsonc
+ {
+ "durable_objects": {
+ "bindings": [{ "name": "SupportAgent", "class_name": "SupportAgent" }],
+ },
+ "migrations": [{ "tag": "v1", "new_sqlite_classes": ["SupportAgent"] }],
+ }
+ ```
-```json
-{
- "extends": "agents/tsconfig"
-}
-```
+
-If you have an existing `tsconfig.json` with custom settings, you can extend and override:
+ If your project already has migrations, add a new tag instead of reusing `v1`.
-```json
-{
- "extends": "agents/tsconfig",
- "compilerOptions": {
- "paths": { "~/*": ["./src/*"] }
- }
-}
-```
+2. Create the agent and export it from your Worker's entry point:
-**vite.config.ts** — add the `agents()` plugin (handles TC39 decorator transforms for Vite 8):
+
-
+ ```ts
+ import { DurableObject } from "cloudflare:workers";
+ import { Lifecycle } from "agents/lifecycle";
-```ts
-import agents from "agents/vite";
-
-export default defineConfig({
- plugins: [
- agents(),
- // ... your existing plugins
- ],
-});
-```
+ export class SupportAgent extends DurableObject {
+ readonly lifecycle = Lifecycle.install(this);
-
+ onRequest(request: Request): Response {
+ return new Response(`Support agent ${this.lifecycle.name}`);
+ }
+ }
+ ```
-If your project does not use Vite, the `tsconfig.json` change alone is sufficient — your bundler must support TC39 decorators (stage 3, version `2023-11`).
+
-For more details, refer to the [TypeScript configuration](/agents/runtime/operations/configuration/#typescript-configuration) and [Vite configuration](/agents/runtime/operations/configuration/#vite-configuration) reference.
+ ```ts title="src/index.ts"
+ export { SupportAgent } from "./support-agent";
+ ```
-## 5. Export the Agent class
+3. Route requests to the agent from your Worker's `fetch` handler. `routeAgentRequest()` returns `null` for paths it does not handle, so your existing routes keep working:
-Your agent class must be exported from your main entry point. Update your `src/index.ts`:
+
-
+ ```ts
+ import { routeAgentRequest } from "agents";
-```ts
-// Export the agent class (required for Durable Objects)
-export { CounterAgent } from "./agents/counter";
+ export { SupportAgent } from "./support-agent";
-// Your existing exports...
-export default {
- // ...
-} satisfies ExportedHandler;
-```
+ export default {
+ async fetch(request: Request, env: Env): Promise {
+ const agentResponse = await routeAgentRequest(request, env);
+ if (agentResponse) return agentResponse;
-
+ // Your existing routing.
+ return new Response("Not found", { status: 404 });
+ },
+ } satisfies ExportedHandler;
+ ```
-## 6. Wire up routing
+
-Choose the approach that matches your project structure:
+ Requests to `/agents/support-agent/:name` now reach the agent. To call it from your own routes instead, use `env.SupportAgent.getByName(name).fetch(request)`.
-### Plain Workers (fetch handler)
+4. Generate types for the new binding:
-
+ ```sh
+ npx wrangler types
+ ```
-```ts
-import { routeAgentRequest } from "agents";
-export { CounterAgent } from "./agents/counter";
-
-export default {
- async fetch(request: Request, env: Env, ctx: ExecutionContext) {
- // Try agent routing first
- const agentResponse = await routeAgentRequest(request, env);
- if (agentResponse) return agentResponse;
-
- // Your existing routing logic
- const url = new URL(request.url);
- if (url.pathname === "/api/hello") {
- return Response.json({ message: "Hello!" });
- }
-
- return new Response("Not found", { status: 404 });
- },
-} satisfies ExportedHandler;
-```
-
-
+
-### Hono
+The object is ready to run an agent. Install a harness with `.use()`, as in the [Quick start](/agents/getting-started/quick-start/#run-a-harness-on-a-durable-object), then plug in the [capabilities](/agents/capabilities/) you need.
-
-
-```ts
-import { Hono } from "hono";
-import { agentsMiddleware } from "hono-agents";
-export { CounterAgent } from "./agents/counter";
+## Move an existing Durable Object onto the Lifecycle
-const app = new Hono<{ Bindings: Env }>();
+`Lifecycle.install(this)` installs the `fetch`, `alarm`, `webSocketMessage`, `webSocketClose`, and `webSocketError` handlers. Move the logic out of those methods before you install it.
-// Add agents middleware - handles WebSocket upgrades and agent HTTP requests
-app.use("*", agentsMiddleware());
-
-// Your existing routes continue to work
-app.get("/api/hello", (c) => c.json({ message: "Hello!" }));
-
-export default app;
-```
-
-
+### Move `fetch()` to `onRequest()`
-### With static assets
-
-If you are serving static assets alongside agents, static assets are served first by default. Your Worker code only runs for paths that do not match a static asset:
+Rename `fetch()` to `onRequest()`. Lifecycle calls it for every request that no capability handles:
```ts
-import { routeAgentRequest } from "agents";
-export { CounterAgent } from "./agents/counter";
-
-export default {
- async fetch(request: Request, env: Env, ctx: ExecutionContext) {
- // Static assets are served automatically before this runs
- // This only handles non-asset requests
-
- // Route to agents
- const agentResponse = await routeAgentRequest(request, env);
- if (agentResponse) return agentResponse;
-
- return new Response("Not found", { status: 404 });
- },
-} satisfies ExportedHandler;
-```
+import { DurableObject } from "cloudflare:workers";
+import { Lifecycle } from "agents/lifecycle";
-
-
-Configure assets in the Wrangler configuration file:
+export class Counter extends DurableObject {
+ readonly lifecycle = Lifecycle.install(this);
-
-
-```jsonc
-{
- "assets": {
- "directory": "./public",
- },
+ // Previously: async fetch(request: Request)
+ async onRequest(request: Request): Promise {
+ const count = ((await this.ctx.storage.get("count")) ?? 0) + 1;
+ await this.ctx.storage.put("count", count);
+ return Response.json({ count });
+ }
}
```
-
-
-## 7. Generate TypeScript types
-
-Do not hand-write your `Env` interface. Run [`wrangler types`](/workers/wrangler/commands/general/#types) to generate a type definition file that matches your Wrangler configuration. This catches mismatches between your config and code at compile time instead of at deploy time.
-
-Re-run `wrangler types` whenever you add or rename a binding.
-
-```sh
-npx wrangler types
-```
-
-This creates a type definition file with all your bindings typed, including your agent Durable Object namespaces. The `Agent` class defaults to using the generated `Env` type, so you do not need to pass it as a type parameter — `extends Agent` is sufficient unless you need to pass a second type parameter for state (for example, `Agent`).
+
-Refer to [Configuration](/agents/runtime/operations/configuration/#generating-types) for more details on type generation.
+Your existing storage stays where it is. Capabilities create their own tables, with prefixes such as `cf_agents_` and `pi_`.
-## 8. Connect from the frontend
+### Replace `alarm()` with jobs
-### React
+Lifecycle owns the object's single alarm. Replace `ctx.storage.setAlarm()` with a job, and move the alarm logic to `onJob()`:
-```tsx
-import { useState } from "react";
-import { useAgent } from "agents/react";
-import type { CounterAgent, CounterState } from "./agents/counter";
-
-function CounterWidget() {
- const [count, setCount] = useState(0);
-
- const agent = useAgent({
- agent: "CounterAgent",
- onStateUpdate: (state) => setCount(state.count),
- });
+```ts
+import { DurableObject } from "cloudflare:workers";
+import { Lifecycle, type LifecycleJobContext } from "agents/lifecycle";
+
+export class Counter extends DurableObject {
+ readonly lifecycle = Lifecycle.install(this);
+
+ async onRequest(): Promise {
+ // Previously: await this.ctx.storage.setAlarm(Date.now() + 60_000);
+ await this.lifecycle.jobs.push({
+ id: "reset",
+ fn: "reset",
+ time: Date.now() + 60_000,
+ });
+ return new Response("Reset scheduled", { status: 202 });
+ }
- return (
- <>
- {count}
-
-
- >
- );
+ // Previously: async alarm()
+ async onJob({ job }: LifecycleJobContext): Promise {
+ if (job.fn === "reset") await this.ctx.storage.delete("count");
+ }
}
```
-Key points:
+For recurring or cron work, install the [`Scheduler`](/agents/capabilities/#scheduler) capability instead. For more information, refer to [Jobs and alarms](/agents/lifecycle/job-queue/).
-- `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
+### Move WebSocket handlers to the `WebSockets` capability
-### Vanilla JavaScript
+Lifecycle ignores sockets you accept yourself with `ctx.acceptWebSocket()`. Install the `WebSockets` capability and move your handlers into it:
```ts
-import { AgentClient } from "agents/client";
-
-const agent = new AgentClient({
- agent: "CounterAgent",
- name: "user-123", // Optional: unique instance name
- onStateUpdate: (state) => {
- document.getElementById("count").textContent = state.count;
- },
-});
-
-// Call methods
-document.getElementById("increment").onclick = () => agent.call("increment");
+import { DurableObject } from "cloudflare:workers";
+import { Lifecycle } from "agents/lifecycle";
+import { WebSockets } from "agents/websockets";
+
+export class ChatRoom extends DurableObject {
+ readonly webSockets = new WebSockets({
+ handlers: {
+ // Previously: webSocketMessage(ws, message)
+ onMessage: (_connection, message) => {
+ for (const connection of this.webSockets.getConnections()) {
+ connection.send(message);
+ }
+ },
+ },
+ });
+ readonly lifecycle = Lifecycle.install(this).use(this.webSockets);
+}
```
-
+Connections still hibernate. Clients can connect with a plain `WebSocket` or with `useAgent` from `agents/react`.
-## Deploy to Cloudflare
+### Start the Lifecycle in RPC methods
-```sh
-npm run deploy
-```
+RPC methods do not pass through `fetch`. Call `await this.lifecycle.start()` at the top of any RPC method that uses a capability before the object has started.
-Your agent is now live on Cloudflare's global network, running close to your users.
+### Address objects by name
-
+Lifecycle requires a named object. If your code creates IDs with `newUniqueId()`, switch to `getByName()` or `idFromName()`. Objects created with unique IDs cannot move onto the Lifecycle, because their IDs carry no name.
-
+## Next steps
-
+- [Harnesses](/agents/harnesses/) compares the harnesses you can run.
+- [Capabilities](/agents/capabilities/) lists the capabilities you can plug in.
+- [Quick start](/agents/getting-started/quick-start/) builds an agent with the Pi harness.
+- [Add an Agent to an existing project](/agents/agent-class/add-to-existing-project/) covers the same steps for the legacy `Agent` class.
diff --git a/src/content/docs/agents/getting-started/index.mdx b/src/content/docs/agents/getting-started/index.mdx
index 68b3d6adae4..c036bb238b8 100644
--- a/src/content/docs/agents/getting-started/index.mdx
+++ b/src/content/docs/agents/getting-started/index.mdx
@@ -1,6 +1,6 @@
---
title: Getting started
-description: Start building Agents on Cloudflare that maintain state, make decisions, and execute workflows.
+description: Start running agents on Cloudflare. Pick a harness, run it on a Durable Object, and plug in the capabilities you need.
pcx_content_type: overview
sidebar:
order: 1
@@ -12,26 +12,26 @@ products:
import { DirectoryListing, LinkCard } from "~/components";
-Start building agents that can remember context, communicate with users, and act on their own. Pick the path that matches what you want to build first.
+An agent on Cloudflare is a [harness](/agents/harnesses/) running on a Durable Object, with the [capabilities](/agents/capabilities/) you plug in. Pick the path that matches what you want to build first.
## Choose a path
+To build on the legacy `Agent` class instead, refer to [Get started with the Agent class](/agents/agent-class/get-started/).
+
## All getting started pages
diff --git a/src/content/docs/agents/getting-started/quick-start.mdx b/src/content/docs/agents/getting-started/quick-start.mdx
index e19e76425c9..b05b08f3723 100644
--- a/src/content/docs/agents/getting-started/quick-start.mdx
+++ b/src/content/docs/agents/getting-started/quick-start.mdx
@@ -3,114 +3,120 @@ title: Quick start
pcx_content_type: get-started
sidebar:
order: 1
-description: Build your first agent in 10 minutes — a counter with persistent state that syncs to a React frontend in real-time.
+description: Run your first agent on Cloudflare. Install the Pi harness on a Durable Object, plug in a scheduler, and deploy it.
products:
- agents
---
import {
+ PackageManagers,
+ Steps,
TypeScriptExample,
WranglerConfig,
- PackageManagers,
- 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.
+An agent on Cloudflare is a [harness](/agents/harnesses/) running on a [Durable Object](/durable-objects/). The [Lifecycle](/agents/lifecycle/) keeps the harness running, and you plug in the [capabilities](/agents/capabilities/) it needs. In this guide, you run the [Pi harness](/agents/harnesses/pi/) on Workers AI, answer prompts over HTTP, and plug in a scheduler that asks for a plan every morning.
-**What you will build:** A counter agent with persistent state that syncs to a React frontend in real-time.
+You do not need an API key. The agent uses [Workers AI](/workers-ai/) through the `AI` binding.
-**Time:** ~10 minutes
+## Prerequisites
-## Create a new project
+- A [Cloudflare account](https://dash.cloudflare.com/sign-up)
+- Node.js 24 or later
-
+## Create a project
-Then install dependencies and start the dev server:
+
-```sh
-cd agents-starter
-npm install
-npm run dev
-```
+1. Create a Worker project:
-This creates a project with:
+
-- `src/server.ts` — Your agent code
-- `src/client.tsx` — React frontend
-- `wrangler.jsonc` — Cloudflare configuration
-- `tsconfig.json` — Extends `agents/tsconfig` for correct decorator and module settings
-- `vite.config.ts` — Includes the `agents/vite` plugin for decorator support
+2. Go to the project directory and install the Agents SDK and the Pi packages:
-The starter template includes two important SDK integrations. If you are setting up a project manually, add both:
+ ```sh
+ cd my-agent
+ ```
-**tsconfig.json** — extends `agents/tsconfig`, which sets `target: "ES2021"` and other recommended options:
+
-```json
-{
- "extends": "agents/tsconfig"
-}
-```
+
-**vite.config.ts** — includes the `agents()` plugin, which handles TC39 decorator transforms (required for `@callable()` in Vite 8):
+## Configure Wrangler
-```ts
-import { cloudflare } from "@cloudflare/vite-plugin";
-import react from "@vitejs/plugin-react";
-import agents from "agents/vite";
-import { defineConfig } from "vite";
-
-export default defineConfig({
- plugins: [agents(), react(), cloudflare()],
-});
-```
+Add the `AI` binding and a SQLite-backed Durable Object to your Wrangler configuration file:
-Open [http://localhost:5173](http://localhost:5173) to see your agent in action.
+
-## Your first agent
+```jsonc
+{
+ "name": "my-agent",
+ "main": "src/index.ts",
+ "upload_source_maps": true,
+ "compatibility_date": "$today",
+ "compatibility_flags": ["nodejs_compat"],
+ "ai": {
+ "binding": "AI",
+ },
+ "durable_objects": {
+ "bindings": [{ "name": "Assistant", "class_name": "Assistant" }],
+ },
+ "migrations": [{ "tag": "v1", "new_sqlite_classes": ["Assistant"] }],
+}
+```
-Build a simple counter agent from scratch. Replace `src/server.ts`:
+
-
+The `nodejs_compat` flag is required by the `agents` package. `new_sqlite_classes` gives the Durable Object the SQLite storage that the Lifecycle and the harness use.
-```ts title="src/server.ts"
-import { Agent, routeAgentRequest, callable } from "agents";
+## Run a harness on a Durable Object
-// Define the state shape
-export type CounterState = {
- count: number;
-};
+Replace `src/index.ts` with an `Assistant` Durable Object that runs the Pi harness:
-// Create the agent
-export class CounterAgent extends Agent {
- // Initial state for new instances
- initialState: CounterState = { count: 0 };
+
- // Methods marked with @callable can be called from the client
- @callable()
- increment() {
- this.setState({ count: this.state.count + 1 });
- return this.state.count;
- }
+```ts
+import { DurableObject } from "cloudflare:workers";
+import { createModels } from "@earendil-works/pi-ai/models";
+import { createRegistry, Harness } from "@earendil-works/pi-durable";
+import { routeAgentRequest } from "agents";
+import { PiHarness } from "agents/harness/pi";
+import { Lifecycle } from "agents/lifecycle";
+import { createAI } from "agents/models/pi-ai";
+
+export class Assistant extends DurableObject {
+ readonly ai = createAI({ binding: this.env.AI });
+ readonly registry = createRegistry();
+
+ readonly harness = new PiHarness({
+ harness: ({ storage, context }) => {
+ const models = createModels();
+ models.setProvider(this.ai.provider);
+ return Harness.open(storage, { models, registry: this.registry }, context);
+ },
+ defaults: {
+ model: this.ai("@cf/zai-org/glm-4.7-flash"),
+ thinkingLevel: "low",
+ },
+ });
- @callable()
- decrement() {
- this.setState({ count: this.state.count - 1 });
- return this.state.count;
- }
+ readonly lifecycle = Lifecycle.install(this).use(this.harness);
- @callable()
- reset() {
- this.setState({ count: 0 });
+ async onRequest(request: Request): Promise {
+ if (request.method !== "POST") {
+ return Response.json(await this.harness.messages());
+ }
+ const { status, text } = await this.harness.prompt(await request.text());
+ return Response.json({ status, text });
}
}
-// Route requests to agents
export default {
- async fetch(request: Request, env: Env, ctx: ExecutionContext) {
+ async fetch(request: Request, env: Env): Promise {
return (
(await routeAgentRequest(request, env)) ??
new Response("Not found", { status: 404 })
@@ -121,122 +127,105 @@ export default {
-Update `wrangler.jsonc` to register the agent:
+Here is what each part does:
-
+- `Assistant` extends the platform `DurableObject`. Each named instance is one agent with its own SQLite database.
+- `PiHarness` is the harness. It runs the [Pi](https://pi.dev/) agent loop, stores the transcript in the object's database, and keeps runs going after the object is evicted.
+- `Lifecycle.install(this)` takes over the object's `fetch` and `alarm` handlers. `.use(this.harness)` runs the harness on the object.
+- `onRequest()` handles requests that no capability claimed. A `POST` sends a prompt, and a `GET` returns the transcript.
+- `routeAgentRequest()` sends `/agents/assistant/:name` to the `Assistant` instance called `:name`.
-```jsonc
-{
- "name": "my-agent",
- "main": "src/server.ts",
- "upload_source_maps": true,
- "compatibility_date": "$today",
- "compatibility_flags": ["nodejs_compat"],
- "durable_objects": {
- "bindings": [
- {
- "name": "CounterAgent",
- "class_name": "CounterAgent",
- },
- ],
- },
- "migrations": [
- {
- "tag": "v1",
- "new_sqlite_classes": ["CounterAgent"],
- },
- ],
-}
-```
+Generate types for the `Env` interface:
-
+```sh
+npx wrangler types
+```
-**Key points:**
+## Run the agent locally
-- `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
+1. Start the development server:
-Replace `src/client.tsx`:
+ ```sh
+ npx wrangler dev
+ ```
-```tsx title="src/client.tsx"
-import "./styles.css";
-import { createRoot } from "react-dom/client";
-import { useState } from "react";
-import { useAgent } from "agents/react";
-import type { CounterAgent, CounterState } from "./server";
+ The `AI` binding always runs remotely, so Wrangler asks you to log in the first time.
-export default function App() {
- const [count, setCount] = useState(0);
+2. In another terminal, send the agent a prompt:
- // Connect to the Counter agent
- const agent = useAgent({
- agent: "CounterAgent",
- onStateUpdate: (state) => setCount(state.count),
- });
+ ```sh
+ curl -X POST http://localhost:8787/agents/assistant/demo \
+ -d "What is 47 times 19? Reply with the number only."
+ ```
- return (
-
-
Counter Agent
-
{count}
-
-
-
-
-
-
- );
-}
+ ```json output
+ { "status": "done", "text": "893" }
+ ```
-const root = createRoot(document.getElementById("root")!);
-root.render();
-```
+3. Read the transcript:
-Key points:
+ ```sh
+ curl http://localhost:8787/agents/assistant/demo
+ ```
-- `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
+
-
+The transcript lives in the `demo` instance's database. A request to `/agents/assistant/another-name` reaches a different agent with an empty transcript.
-## Connect from vanilla JavaScript
+## Plug in a capability
-If you are not using React:
+The harness and its capabilities share the same Lifecycle, so plugging one in is a new field and one more `.use()` call. Add a `Scheduler` that asks the agent for a plan every morning:
-
+
```ts
-import { AgentClient } from "agents/client";
+// ...
+import { Scheduler } from "agents/schedules";
+
+export class Assistant extends DurableObject {
+ readonly ai = createAI({ binding: this.env.AI });
+ readonly registry = createRegistry();
+ readonly harness = new PiHarness({ /* ... */ });
+
+ readonly scheduler = new Scheduler({
+ callbacks: {
+ morningPlan: async () => {
+ await this.harness.submit("Write a two-line plan for today.");
+ },
+ },
+ });
-const agent = new AgentClient({
- agent: "CounterAgent",
- name: "my-counter", // optional, defaults to "default"
- onStateUpdate: (state) => {
- console.log("New count:", state.count);
- },
-});
+ readonly lifecycle = Lifecycle.install(this).use(this.harness).use(this.scheduler);
+
+ async onStart(): Promise {
+ await this.scheduler.set("0 8 * * *", "morningPlan");
+ }
-// Call methods
-await agent.call("increment");
-await agent.call("reset");
+ // onRequest() is unchanged.
+}
```
-## Deploy to Cloudflare
+- `onStart()` runs once each time the object starts. A cron schedule with the same callback and payload already exists after the first start, so `set()` does not add a second one.
+- `harness.submit()` returns once Pi has stored the prompt, before the model runs. The harness finishes the run in the background and wakes the object if it is evicted.
+- `Scheduler` and `PiHarness` both store their future work in the Lifecycle's job queue, so they share the object's single alarm.
+
+## Deploy
+
+Deploy the Worker to Cloudflare:
```sh
-npm run deploy
+npx wrangler deploy
```
-Your agent is now live on Cloudflare's global network, running close to your users.
-
-
+Your agent is now live at `https://my-agent..workers.dev/agents/assistant/demo`.
-
+## Next steps
-
+- To stream Pi sessions to a browser over WebSockets, refer to [Pi harness](/agents/harnesses/pi/).
+- To plug in MCP servers, queues, or connected clients, refer to [Capabilities](/agents/capabilities/).
+- To compare Pi with Project Think or a harness of your own, refer to [Harnesses](/agents/harnesses/).
+- To understand startup, request handling, and jobs, refer to [Lifecycle](/agents/lifecycle/).
diff --git a/src/content/docs/agents/harnesses/ai-sdk.mdx b/src/content/docs/agents/harnesses/ai-sdk.mdx
new file mode 100644
index 00000000000..8700328fe08
--- /dev/null
+++ b/src/content/docs/agents/harnesses/ai-sdk.mdx
@@ -0,0 +1,106 @@
+---
+title: AI SDK harness
+pcx_content_type: how-to
+description: Run an AI SDK streamText loop on a Durable Object with the experimental AiSdkHarness capability, with sessions, transcripts, and a queue of operations in the object's storage.
+sidebar:
+ order: 3
+ badge: Experimental
+products:
+ - agents
+---
+
+import { PackageManagers, TypeScriptExample } from "~/components";
+
+`AiSdkHarness` from `agents/harness/ai-sdk` runs the [AI SDK](https://ai-sdk.dev/)'s `streamText` as a harness on a Durable Object. It keeps sessions, their transcripts, and a queue of operations in the object's storage. One operation runs at a time per session, and each run is one `streamText` call over the session's transcript.
+
+:::caution[Experimental]
+`AiSdkHarness` is experimental. The API may change in any release.
+:::
+
+Use it when you want a thin, AI SDK-native loop. `AiSdkHarness` implements the shared harness interface that the experimental Channels API serves, so the same harness can answer browsers, terminals, and messaging platforms.
+
+## Install
+
+
+
+The example uses [Workers AI](/workers-ai/) through the `AI` binding and `createAI` from `agents/models/ai-sdk`. To use other models, refer to [AI SDK models](/agents/models/ai-sdk/).
+
+## Create the harness
+
+
+
+```ts
+import { DurableObject } from "cloudflare:workers";
+import { stepCountIs, tool } from "ai";
+import { z } from "zod";
+import { AiSdkHarness } from "agents/harness/ai-sdk";
+import { Lifecycle } from "agents/lifecycle";
+import { createAI } from "agents/models/ai-sdk";
+
+export class Assistant extends DurableObject {
+ readonly ai = createAI({ binding: this.env.AI });
+
+ readonly harness = new AiSdkHarness({
+ model: this.ai("@cf/moonshotai/kimi-k2.7-code"),
+ system: "You are a helpful assistant.",
+ tools: {
+ flipCoin: tool({
+ description: "Flip a coin, once the user approves",
+ inputSchema: z.object({}),
+ needsApproval: true,
+ execute: async () => (Math.random() < 0.5 ? "Heads" : "Tails"),
+ }),
+ },
+ stopWhen: stepCountIs(5),
+ });
+
+ readonly lifecycle = Lifecycle.install(this).use(this.harness);
+
+ async onRequest(request: Request): Promise {
+ const session = this.harness.session();
+ const { operationId } = await session.submit({
+ parts: [{ type: "text", text: await request.text() }],
+ });
+ return Response.json(await session.wait(operationId, request.signal));
+ }
+}
+```
+
+
+
+| Option | Description |
+| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
+| `model` | Required. An AI SDK `LanguageModel`. |
+| `tools` | An AI SDK `ToolSet`. Tools without `execute` run on the client of the participant whose message started the turn. |
+| `system` | The system prompt. |
+| `stopWhen` | When the run stops calling the model. Defaults to `stepCountIs(5)`. |
+| `heartbeatMs` | How often a running session's wake job checks in. Defaults to 30 seconds. |
+
+## Work with sessions
+
+`harness.session(id)` returns a handle on one session. Without an id, it returns the default session. Each session has:
+
+| Method | Description |
+| ---------------------------- | ------------------------------------------------------------------------------------------------------- |
+| `submit(input, options)` | Queue a message or a tool answer. Pass `operationId` to make the call idempotent. |
+| `wait(operationId, signal?)` | Resolve when the operation settles, as `done` with its `text` or `unanswered` with a `reason`. |
+| `abort(operationId?)` | Stop one operation, or everything in the session. |
+| `reset(handoff?)` | Start a new context, optionally from a handoff note. |
+| `watch()` | The current state, then every change in order: operation status, run start and end, chunks, and saved messages. |
+
+`harness.sessions` has `create()`, `fork(from)`, and `list()`.
+
+A tool call that needs approval settles the operation. Submit the answer as a new operation to continue:
+
+```ts
+await session.submit({ type: "approval", approvalId, approved: true });
+```
+
+## Recovery
+
+The AI SDK keeps no state of its own. If an eviction cuts a run short, the harness starts the run again from the saved transcript, and the run's unsaved output is lost. Steering is not supported: `whenBusy: "steer"` queues the input as a follow-up.
+
+## Related resources
+
+- [Harnesses](/agents/harnesses/) compares the harnesses you can run.
+- [AI SDK models](/agents/models/ai-sdk/) configures Workers AI and third-party models.
diff --git a/src/content/docs/agents/harnesses/container.mdx b/src/content/docs/agents/harnesses/container.mdx
new file mode 100644
index 00000000000..e44d50b9cd5
--- /dev/null
+++ b/src/content/docs/agents/harnesses/container.mdx
@@ -0,0 +1,144 @@
+---
+title: Container harness
+pcx_content_type: how-to
+description: Run a coding agent CLI such as Claude Code or Codex in a Cloudflare Container, driven from a Durable Object with the experimental ContainerHarness capability.
+sidebar:
+ order: 4
+ badge: Experimental
+products:
+ - agents
+ - containers
+---
+
+import { TypeScriptExample, WranglerConfig } from "~/components";
+
+`ContainerHarness` from `agents/harness/container` runs an agent CLI, such as Claude Code or Codex, in a [Cloudflare Container](/containers/) and drives it from a Durable Object. It has the same interface as the [Pi harness](/agents/harnesses/pi/): `prompt()`, `submit()`, `wait()`, `abort()`, `messages()`, `pending()`, `session(id)`, and `sessions`.
+
+:::caution[Experimental]
+`ContainerHarness` is experimental. The API may change before it stabilizes. It uses the Containers [`durable_object` scheduling policy](/containers/configuration/scheduling-policy/), which is in beta.
+:::
+
+The Durable Object keeps everything durable: sessions, operations, the transcript, and the CLI's own session files. The container is disposable. It stops after an idle timeout, and the next prompt starts a new container in which the CLI resumes its session.
+
+## Configure Wrangler
+
+Each agent object gets its own container through `ctx.container`. Use the `durable_object` scheduling policy. You do not need an image: the harness builds the container from the [`cloudflare/debian-trixie`](/containers/guides/image-management/#use-the-cloudflare-managed-image) image at runtime.
+
+
+
+```jsonc
+{
+ "name": "coding-agent",
+ "main": "src/index.ts",
+ "upload_source_maps": true,
+ "compatibility_date": "$today",
+ "compatibility_flags": ["nodejs_compat"],
+ "durable_objects": {
+ "bindings": [{ "name": "CodingAgent", "class_name": "CodingAgent" }],
+ },
+ "migrations": [{ "tag": "v1", "new_sqlite_classes": ["CodingAgent"] }],
+ "containers": [
+ { "class_name": "CodingAgent", "scheduling_policy": "durable_object" },
+ ],
+ "vars": {
+ "AI_GATEWAY_URL": "https://gateway.ai.cloudflare.com/v1//",
+ },
+}
+```
+
+
+
+Store your [AI Gateway](/ai-gateway/) token as a secret:
+
+```sh
+npx wrangler secret put AI_GATEWAY_TOKEN
+```
+
+## Create the harness
+
+Pick what runs in the container with a preset, `claudeCode()` or `codex()`:
+
+
+
+```ts
+import { DurableObject } from "cloudflare:workers";
+import { routeAgentRequest } from "agents";
+import { ContainerHarness, claudeCode } from "agents/harness/container";
+import { Lifecycle } from "agents/lifecycle";
+
+// Adds the credentials outside the container. Export it from the main module.
+export { ContainerEgress } from "agents/harness/container";
+
+export class CodingAgent extends DurableObject {
+ readonly harness = new ContainerHarness({
+ container: this.ctx.container!,
+ egress: this.ctx.exports.ContainerEgress,
+ agent: claudeCode({
+ baseUrl: `${this.env.AI_GATEWAY_URL}/anthropic`,
+ apiKey: this.env.AI_GATEWAY_TOKEN,
+ }),
+ });
+
+ readonly lifecycle = Lifecycle.install(this).use(this.harness);
+
+ async onRequest(request: Request): Promise {
+ const { status, text } = await this.harness.prompt(await request.text());
+ return Response.json({ status, text });
+ }
+}
+
+export default {
+ async fetch(request: Request, env: Env): Promise {
+ return (
+ (await routeAgentRequest(request, env)) ??
+ new Response("Not found", { status: 404 })
+ );
+ },
+} satisfies ExportedHandler;
+```
+
+
+
+To run Codex instead, replace `claudeCode()` with `codex()` and point `baseUrl` at `${this.env.AI_GATEWAY_URL}/openai`. Leave out `baseUrl` to call the provider directly with its own key, and add `headers` for anything else a request needs.
+
+The routes in this example have no authentication. Each request drives an agent that runs commands and spends model credit, so put your own authentication in front of it before you deploy.
+
+## How it works
+
+- **First start**: the harness starts `cloudflare/debian-trixie`, creates an unprivileged `agent` user, installs the CLI, and snapshots the filesystem. Later starts restore the snapshot. Changing the preset, such as a new CLI version or extra `setup` steps, sets the container up again.
+- **Each turn** is one CLI process. The harness turns its output into messages and events, and copies the CLI's session files to the Durable Object as the turn runs.
+- **Credentials**: the CLI only sees a placeholder key and a placeholder host. The harness intercepts that host, and `ContainerEgress`, running in your Worker, adds the real credentials and forwards the request to `baseUrl`.
+- **Idle and restart**: five minutes after the last prompt, the harness snapshots the container and stops it. The next prompt starts a new container from the snapshot, and the CLI resumes its own session. Call `harness.stop()` to stop the container sooner.
+- **Lost containers**: if the container stops mid-run, that run settles as `unanswered` with the reason `container_lost`. Edits to the workspace since the last snapshot are lost. If the Durable Object is evicted mid-run, the container keeps working, and the object reattaches and replays what it missed.
+
+## Customize the CLI
+
+Pass `setup` steps to the preset to install plugins, skills, or settings. Steps run once, on the first start, and their result is kept in the snapshot. A step with `user: "agent"` runs as the user the CLI runs as:
+
+```ts
+claudeCode({
+ baseUrl: `${this.env.AI_GATEWAY_URL}/anthropic`,
+ apiKey: this.env.AI_GATEWAY_TOKEN,
+ setup: [
+ {
+ name: "my plugins",
+ user: "agent",
+ command: [
+ "sh",
+ "-c",
+ "claude plugin marketplace add you/my-plugins && claude plugin install my-plugin@my-plugins",
+ ],
+ },
+ ],
+});
+```
+
+A token that a setup step uses stays in the snapshot, where the agent can read it. Prefer public sources or short-lived URLs.
+
+To run another CLI, build an image whose entrypoint serves the harness daemon with `cliAdapter` from `agents/harness/container/runtime`, and pass it to `containerAgent()`.
+
+## Related resources
+
+- [Harnesses](/agents/harnesses/) compares the harnesses you can run.
+- [Scheduling policies](/containers/configuration/scheduling-policy/) describes the `durable_object` policy.
+- [Example on GitHub](https://github.com/cloudflare/agents/tree/main/examples/next/harnesses/container)
diff --git a/src/content/docs/agents/harnesses/index.mdx b/src/content/docs/agents/harnesses/index.mdx
index a711b50bcc5..cd8be57dd94 100644
--- a/src/content/docs/agents/harnesses/index.mdx
+++ b/src/content/docs/agents/harnesses/index.mdx
@@ -1,77 +1,114 @@
---
title: Harnesses
-description: Understand agent harnesses — the loop that controls planning, tool use, and response flow.
+description: A harness is the agent loop around the model. Pick a harness, run it on a Durable Object with the Lifecycle, and plug in the capabilities it needs.
pcx_content_type: concept
sidebar:
- order: 5
+ order: 3
---
-import { LinkCard } from "~/components";
+import { LinkCard, TypeScriptExample } from "~/components";
-A harness is the loop that makes an agent behave like an agent instead of a single model call.
+A harness is the loop that makes an agent behave like an agent instead of a single model call. On Cloudflare, the harness is the agent: you pick one, and it runs inside a [Durable Object](/durable-objects/).
-It is responsible for the turn-by-turn work around the model: building the prompt, loading memory, selecting tools, handling tool results, streaming responses, persisting messages, and deciding whether the agent should continue or stop.
+The harness does the turn-by-turn work around the model: building the prompt, loading memory, selecting tools, handling tool results, streaming responses, persisting messages, and deciding whether the agent should continue or stop.
-To build a new agent, start with the [Pi harness](/agents/harnesses/pi/), which runs Pi Durable in a Durable Object. You can also run [OpenCode](/agents/harnesses/opencode/), or build the loop yourself on top of the [Agents SDK runtime](/agents/agent-class/).
+## Run a harness on a Durable Object
-## How harnesses fit
+Running an agent on Cloudflare takes three steps:
-Harnesses sit on top of the Agents SDK runtime:
+1. **Pick a harness.** Install one of the [harnesses below](#choose-a-harness), such as the [Pi harness](/agents/harnesses/pi/), or [build your own](#build-your-own-harness).
+2. **Run it on a Durable Object.** Install the harness with the [Lifecycle](/agents/lifecycle/). The Lifecycle runs startup, routes requests, and owns the object's job queue and alarm, so the harness wakes and carries on after eviction, deploys, and crashes.
+3. **Plug in capabilities.** Add an [MCP client](/agents/capabilities/#mcp-client), a [scheduler](/agents/capabilities/#scheduler), [WebSockets](/agents/capabilities/#websockets), or any other [capability](/agents/capabilities/) the agent needs. Each one is a field and one more `.use()` call.
-- **The runtime** gives the agent durable infrastructure: the [`Agent` class](/agents/agent-class/internals/), [state](/agents/agent-class/state/), [sessions](/agents/runtime/lifecycle/sessions/), [routing](/agents/agent-class/routing/), [WebSockets](/agents/agent-class/websockets/), [scheduling](/agents/agent-class/schedule-tasks/), [fibers](/agents/agent-class/durable-execution/), and [observability](/agents/runtime/operations/observability/).
-- **The harness** gives the agent behavior: model calls, prompt construction, tool selection, stream handling, memory strategy, and lifecycle hooks.
+
-The runtime answers “where does this agent live and how does it stay durable?” The harness answers “what does this agent do on each turn?”
+```ts
+import { DurableObject } from "cloudflare:workers";
+import { PiHarness } from "agents/harness/pi";
+import { Lifecycle } from "agents/lifecycle";
+import { Scheduler } from "agents/schedules";
-## Choose an approach
+export class Assistant extends DurableObject {
+ // The harness: the agent loop.
+ readonly harness = new PiHarness({
+ /* model and tools */
+ });
-Use Pi when you want [Pi Durable](https://earendil.com/posts/pi-durable/) to own the agent loop, transcript, and recovery. This is the recommended starting point for a new agent. `PiHarness` runs Pi in a Durable Object and wakes it after eviction. You choose the transport.
+ // A capability plugged in next to it.
+ readonly scheduler = new Scheduler({ callbacks: {} });
-Use a build-your-own harness when you need full control over the model call, message format, tool loop, or UI protocol. This is the right approach when you want to compose low-level APIs directly from the Agents SDK.
+ // The Lifecycle runs both on the Durable Object.
+ readonly lifecycle = Lifecycle.install(this)
+ .use(this.harness)
+ .use(this.scheduler);
+}
+```
-Use OpenCode (experimental) when you want OpenCode 2.0's sessions, tools, and plugin system. `OpenCodeHarness` runs [OpenCode's Workerd SDK](https://opencode.ai/v2/docs/build/sdk/cloudflare/) inside a plain Durable Object.
+
-## Current harnesses
+For a complete, runnable agent, refer to the [Quick start](/agents/getting-started/quick-start/).
-
+## Choose a harness
-
+Except for Project Think, every harness below is a capability: it installs on a plain Durable Object with `Lifecycle.use()`, next to any other capability, and keeps many sessions per object through `sessions` and `session(id)`. Each session takes durable, idempotent submissions that you can wait on or abort.
+
+| Harness | Import | Status | What runs the loop |
+| ----------------------------------------------------- | -------------------------- | ------------ | ------------------------------------------------------------------------ |
+| [Pi](/agents/harnesses/pi/) | `agents/harness/pi` | Beta | [Pi Durable](https://earendil.com/posts/pi-durable/) |
+| [AI SDK harness](/agents/harnesses/ai-sdk/) | `agents/harness/ai-sdk` | Experimental | The AI SDK's `streamText` |
+| [Container harness](/agents/harnesses/container/) | `agents/harness/container` | Experimental | Claude Code, Codex, or another agent CLI in a Cloudflare Container |
+| [OpenCode](/agents/harnesses/opencode/) | `agents/harness/opencode` | Experimental | [OpenCode](https://opencode.ai/) 2.0, inside the Durable Object |
+| [Project Think](/agents/harnesses/think/) | `@cloudflare/think` | Experimental | Think's agent loop, as a class that extends the `Agent` class |
+
+Use **Pi** when you want Pi Durable to own the agent loop, transcript, and recovery, including steering a running turn. You choose how clients reach it, such as HTTP, WebSockets, or RPC. Start here if you are not sure.
+
+Use the **AI SDK harness** when you want a thin `streamText` loop with sessions and a queue of operations, and nothing more.
+
+Use the **Container harness** when you want a coding agent CLI with a real filesystem and shell. The CLI runs in a container that stops when idle, while the Durable Object keeps the sessions.
+
+Use **OpenCode** when you want OpenCode 2.0's sessions, tools, and plugin system, running inside the Durable Object itself.
+
+Use **Project Think** when you want every default built in: memory, workspace tools, actions, extensions, messengers, scheduled tasks, and sub-agent RPC. Think is a class: your agent extends `Think`, which builds on the legacy [`Agent` class](/agents/agent-class/).
-## What a harness usually owns
+## Build your own harness
+
+When you need full control over the model call, message format, tool loop, or UI protocol, write your own harness as a [capability](/agents/lifecycle/build-a-capability/):
+
+- Combine [Sessions, Streams, and Tasks](/agents/capabilities/#build-your-own-harness) to store the conversation, stream output, and run turns as replayable work.
+- If your agent runs somewhere other than the Durable Object, such as a container or a remote process, keep its durable record in `HarnessStore` from `agents/harness/store` (experimental). It stores sessions, operations with idempotency keys, and named logs of JSON entries per session, in whatever format your agent speaks. The Container harness uses it.
+
+```ts
+import { openHarnessStore } from "agents/harness/store";
+
+const store = openHarnessStore(this.ctx.storage, { prefix: "my_harness_" });
+store.createSession({ id: "main" });
+store.enqueue({ session: "main", id: operationId, input: { text } });
+store.append("main", "messages", [{ data: message }]);
+```
+
+## What a harness owns
A harness usually owns:
-- **Prompt construction** — system prompts, memory, retrieved context, and per-turn instructions.
-- **Model execution** — the call to Workers AI, OpenAI, Anthropic, Gemini, or another provider.
-- **Tool orchestration** — server tools, client tools, MCP tools, approval flows, and continuation after tool results.
-- **Message persistence** — how user, assistant, and tool messages are saved and replayed.
-- **Streaming and recovery** — how responses stream to clients and resume after disconnects or Durable Object eviction.
-- **Extension points** — hooks before and after turns, steps, tool calls, and recovery events.
+- **Prompt construction**: system prompts, memory, retrieved context, and per-turn instructions.
+- **Model execution**: the call to Workers AI, OpenAI, Anthropic, Gemini, or another provider. For model setup, refer to [Models](/agents/models/).
+- **Tool orchestration**: server tools, client tools, MCP tools, approval flows, and continuation after tool results.
+- **Message persistence**: how user, assistant, and tool messages are saved and replayed.
+- **Streaming and recovery**: how responses stream to clients and resume after disconnects or Durable Object eviction.
+- **Extension points**: hooks before and after turns, steps, tool calls, and recovery events.
-## Related resources
+The harness does not own where the agent lives or how it wakes up. The Durable Object and the Lifecycle handle that, and capabilities add the rest.
-
+## Related resources
diff --git a/src/content/docs/agents/harnesses/opencode.mdx b/src/content/docs/agents/harnesses/opencode.mdx
index d5f84525fde..a92fc18b319 100644
--- a/src/content/docs/agents/harnesses/opencode.mdx
+++ b/src/content/docs/agents/harnesses/opencode.mdx
@@ -3,7 +3,8 @@ title: OpenCode
description: Run OpenCode 2.0 in a plain Durable Object with the Agents SDK, Workers AI and AI Gateway models, bundled plugins, and durable sessions.
pcx_content_type: overview
sidebar:
- order: 10
+ order: 5
+ badge: Experimental
products:
- agents
reviewed: 2026-10-12
@@ -29,7 +30,7 @@ OpenCode owns the agent loop, transcript, tools, and session recovery. `OpenCode
## How it works
-`OpenCodeHarness` is a [Lifecycle capability](https://github.com/cloudflare/agents/blob/main/docs/agents/lifecycle.md). The Lifecycle starts the capability and provides storage and a durable job queue.
+`OpenCodeHarness` is a [Lifecycle capability](/agents/lifecycle/build-a-capability/). The Lifecycle starts the capability and provides storage and a durable job queue.
```mermaid
flowchart LR
@@ -339,4 +340,4 @@ export default Plugin.define({
-Import this plugin and add it to `plugins`. The `content` supplies instructions without reading a local file at runtime. Refer to [OpenCode skills](https://opencode.ai/v2/docs/build/plugins/#skills) for registration and [Agent Skills](/agents/runtime/execution/agent-skills/) for authoring guidance.
+Import this plugin and add it to `plugins`. The `content` supplies instructions without reading a local file at runtime. Refer to [OpenCode skills](https://opencode.ai/v2/docs/build/plugins/#skills) for registration and [Agent Skills](/agents/tools/agent-skills/) for authoring guidance.
diff --git a/src/content/docs/agents/harnesses/pi/extensions.mdx b/src/content/docs/agents/harnesses/pi/extensions.mdx
index c5bda34f4e8..45f7327d2bf 100644
--- a/src/content/docs/agents/harnesses/pi/extensions.mdx
+++ b/src/content/docs/agents/harnesses/pi/extensions.mdx
@@ -129,7 +129,7 @@ this.registry.install({
## Add skills
-`skills(sources)` from `agents/harness/pi` turns [Agent Skills](/agents/runtime/execution/agent-skills/) sources from `agents/skills` into a Pi extension named `agents.skills`. It has two tools, `activate_skill` and `read_skill_resource`, and a `skills` section that lists the skills. It reads the sources, so install it in an `async` factory:
+`skills(sources)` from `agents/harness/pi` turns [Agent Skills](/agents/tools/agent-skills/) sources from `agents/skills` into a Pi extension named `agents.skills`. It has two tools, `activate_skill` and `read_skill_resource`, and a `skills` section that lists the skills. It reads the sources, so install it in an `async` factory:
```ts
import { skills } from "agents/harness/pi";
diff --git a/src/content/docs/agents/harnesses/pi/index.mdx b/src/content/docs/agents/harnesses/pi/index.mdx
index cb592077ce7..b70452f0541 100644
--- a/src/content/docs/agents/harnesses/pi/index.mdx
+++ b/src/content/docs/agents/harnesses/pi/index.mdx
@@ -5,7 +5,7 @@ description: Run the Pi Durable harness on Cloudflare with the Agents SDK. PiHar
tags:
- AI
sidebar:
- order: 9
+ order: 1
group:
hideIndex: false
products:
diff --git a/src/content/docs/agents/harnesses/think/configuration.mdx b/src/content/docs/agents/harnesses/think/configuration.mdx
index f6e15ba066c..20e484c8cf4 100644
--- a/src/content/docs/agents/harnesses/think/configuration.mdx
+++ b/src/content/docs/agents/harnesses/think/configuration.mdx
@@ -24,8 +24,8 @@ Think is configured by overriding methods and properties on your `Think` subclas
| `getMessengers()` | `{}` | Messenger ingress and delivery declarations — refer to [Messengers](/agents/harnesses/think/messengers/) |
| `maxSteps` | `10` | Max tool-call rounds per turn |
| `sendReasoning` | `true` | Send reasoning chunks to chat clients |
-| `configureSession()` | identity | Add context blocks, compaction, search, skills — refer to [Sessions](/agents/runtime/lifecycle/sessions/) |
-| `getSkills()` | `[]` | Return Agent Skills sources for on-demand skill activation — refer to [Agent Skills](/agents/runtime/execution/agent-skills/) |
+| `configureSession()` | identity | Add context blocks, compaction, search, skills — refer to [Sessions](/agents/capabilities/sessions/) |
+| `getSkills()` | `[]` | Return Agent Skills sources for on-demand skill activation — refer to [Agent Skills](/agents/tools/agent-skills/) |
| `getSkillScriptRunner()` | `null` | Enable the optional `run_skill_script` tool |
| `messageConcurrency` | `"queue"` | How overlapping submits behave — refer to [Client tools](/agents/harnesses/think/client-tools/#message-concurrency) |
| `includeMcpTools` | `true` | Convert connected MCP tools to AI SDK tools and add them to model turns. Refer to [MCP tools](/agents/harnesses/think/tools/#mcp-tools) |
@@ -87,7 +87,7 @@ export class MyAgent extends Think {
## Session integration
-Think stores conversations in a [Session](/agents/runtime/lifecycle/sessions/) — the storage layer that holds your messages and gives the model writable memory. Two concepts come up here: **context blocks** are labelled sections of the system prompt the model can read and update (for example, a `memory` block of facts about the user), and **compaction** summarizes older messages so long conversations stay within the model's context window. Override `configureSession` to add persistent memory, compaction, search, and skills:
+Think stores conversations in a [Session](/agents/capabilities/sessions/) — the storage layer that holds your messages and gives the model writable memory. Two concepts come up here: **context blocks** are labelled sections of the system prompt the model can read and update (for example, a `memory` block of facts about the user), and **compaction** summarizes older messages so long conversations stay within the model's context window. Override `configureSession` to add persistent memory, compaction, search, and skills:
@@ -117,7 +117,7 @@ export class MyAgent extends Think {
When `configureSession` adds context blocks, Think builds the system prompt from those blocks instead of using `getSystemPrompt()`. Think's `this.messages` getter reads directly from Session's tree-structured storage.
-For the full Session API — context blocks, compaction, search, skills, and multi-session support — refer to the [Sessions documentation](/agents/runtime/lifecycle/sessions/).
+For the full Session API — context blocks, compaction, search, skills, and multi-session support — refer to the [Sessions documentation](/agents/capabilities/sessions/).
## Package exports
@@ -150,4 +150,4 @@ Bundled with `@cloudflare/think`:
| ---------------------- | ---------------------------------------- |
| `@cloudflare/codemode` | Code execution for `createExecuteTool()` |
-The Agent Skills engine and its script runner live in [`agents/skills`](/agents/runtime/execution/agent-skills/), so skill scripts pull `@cloudflare/worker-bundler` and `just-bash` through `agents`, not Think.
+The Agent Skills engine and its script runner live in [`agents/skills`](/agents/tools/agent-skills/), so skill scripts pull `@cloudflare/worker-bundler` and `just-bash` through `agents`, not Think.
diff --git a/src/content/docs/agents/harnesses/think/getting-started.mdx b/src/content/docs/agents/harnesses/think/getting-started.mdx
index 065acce96db..8bf3453a9b7 100644
--- a/src/content/docs/agents/harnesses/think/getting-started.mdx
+++ b/src/content/docs/agents/harnesses/think/getting-started.mdx
@@ -254,7 +254,7 @@ Now the model sees a `MEMORY` section in its system prompt and gets a `set_conte
When you use `configureSession`, the system prompt is built from context blocks rather than `getSystemPrompt()`. The `"soul"` block above acts as the system identity — it is read-only and always appears first. The `"memory"` block is writable, and the model proactively updates it when it learns something useful.
-Refer to the [Sessions documentation](/agents/runtime/lifecycle/sessions/) for context blocks, compaction, search, skills, and multi-session support.
+Refer to the [Sessions documentation](/agents/capabilities/sessions/) for context blocks, compaction, search, skills, and multi-session support.
## 7. Add custom tools
@@ -345,4 +345,4 @@ Refer to [Lifecycle hooks](/agents/harnesses/think/lifecycle-hooks/) for the ful
- [Tools](/agents/harnesses/think/tools/) — workspace tools, code execution, extensions
- [Client tools](/agents/harnesses/think/client-tools/) — browser-side tools, approval flows, concurrency
- [Sub-agent RPC and programmatic turns](/agents/harnesses/think/sub-agents/) — RPC streaming, scheduled turns, recovery
-- [Sessions](/agents/runtime/lifecycle/sessions/) — context blocks, compaction, search, multi-session
+- [Sessions](/agents/capabilities/sessions/) — context blocks, compaction, search, multi-session
diff --git a/src/content/docs/agents/harnesses/think/index.mdx b/src/content/docs/agents/harnesses/think/index.mdx
index d8edd145de3..4c8d4929d78 100644
--- a/src/content/docs/agents/harnesses/think/index.mdx
+++ b/src/content/docs/agents/harnesses/think/index.mdx
@@ -5,7 +5,7 @@ description: Opinionated chat agent framework with built-in tools, persistent me
tags:
- AI
sidebar:
- order: 11
+ order: 6
group:
hideIndex: false
products:
@@ -24,6 +24,8 @@ import {
Think works as both a **top-level agent** (WebSocket chat to browser clients via `useAgentChat`) and a **sub-agent** (a child agent that another agent drives over RPC via `chat()`).
+The `Think` class extends the legacy [`Agent` class](/agents/agent-class/).
+
:::note[New to Cloudflare Agents?]
If this is your first agent, start with the [Getting started tutorial](/agents/harnesses/think/getting-started/) for a guided build. For the bigger picture of what agents are and how they run, read [What are agents?](/agents/concepts/what-are-agents/). Think builds on two Cloudflare primitives worth a quick look: [Workers AI](/workers-ai/) provides the model, and each agent instance is a [Durable Object](/durable-objects/) that stores its state. The rest of this section is reference material you can dip into as you need it.
:::
@@ -147,7 +149,7 @@ enabled = true
Traces appear in the Agents view in the Cloudflare Dashboard. You can inspect conversations and trace timelines. To turn tracing off, set `observability.traces.enabled` to `false`. You do not need to change the Think agent class.
-For span attributes, payload controls, exporting traces, and direct AI SDK v6 or v7 setup, refer to [Tracing](/agents/runtime/operations/observability/tracing/).
+For span attributes, payload controls, exporting traces, and direct AI SDK v6 or v7 setup, refer to [Tracing](/agents/platform/observability/tracing/).
## Think vs AIChatAgent
@@ -374,7 +376,7 @@ Use [`startFiber()`](/agents/agent-class/durable-execution/#startfiber) outside
/>
@@ -393,7 +395,7 @@ Think's design is inspired by [Pi](https://pi.dev).
## Related
-- [Sessions](/agents/runtime/lifecycle/sessions/) — context blocks, compaction, search, multi-session (the storage layer Think builds on)
+- [Sessions](/agents/capabilities/sessions/) — context blocks, compaction, search, multi-session (the storage layer Think builds on)
- [Sub-agents](/agents/agent-class/sub-agents/) — `subAgent()`, `abortSubAgent()`, `deleteSubAgent()` (the base Agent methods for spawning children)
- [Chat agents](/agents/agent-class/communication-channels/chat/chat-agents/) — `AIChatAgent` for when you need full control over the LLM call
- [Long-running agents](/agents/concepts/agentic-patterns/long-running-agents/) — sub-agent delegation patterns for multi-week agent lifetimes
diff --git a/src/content/docs/agents/harnesses/think/recovery.mdx b/src/content/docs/agents/harnesses/think/recovery.mdx
index a4f034e5477..366f73c0f7f 100644
--- a/src/content/docs/agents/harnesses/think/recovery.mdx
+++ b/src/content/docs/agents/harnesses/think/recovery.mdx
@@ -45,7 +45,7 @@ export class MyAgent extends Think {
-The same recovery events are available through `agents/observability` on the `chat` channel; transcript repairs are emitted on the `transcript` channel. Refer to [Diagnostics channels](/agents/runtime/operations/observability/diagnostics-channels/#chat-recovery-events).
+The same recovery events are available through `agents/observability` on the `chat` channel; transcript repairs are emitted on the `transcript` channel. Refer to [Diagnostics channels](/agents/platform/observability/diagnostics-channels/#chat-recovery-events).
## onChatRecovery
@@ -194,7 +194,7 @@ This runs during transcript repair — before the repaired transcript is persist
## Context-window overflow recovery
-[Compaction](/agents/runtime/lifecycle/sessions/#compaction) is checked **between turns** — `compactAfter()` runs after each `appendMessage()`. But a single long, tool-heavy turn grows the prompt step by step inside one `streamText` loop and can exceed the model context window **mid-turn**, before the next pre-turn check. The provider then rejects the request (`"prompt is too long"`, `context_length_exceeded`), and the turn would otherwise die terminally.
+[Compaction](/agents/capabilities/sessions/#compaction) is checked **between turns** — `compactAfter()` runs after each `appendMessage()`. But a single long, tool-heavy turn grows the prompt step by step inside one `streamText` loop and can exceed the model context window **mid-turn**, before the next pre-turn check. The provider then rejects the request (`"prompt is too long"`, `context_length_exceeded`), and the turn would otherwise die terminally.
Think recovers from this with two opt-in, provider-agnostic layers, both configured through the `contextOverflow` property. Both are off by default, so existing behavior is unchanged. Both reuse your session's compaction function, so they require a `configureSession()` with `onCompaction()` configured. Both require [`classifyChatError`](/agents/harnesses/think/lifecycle-hooks/#classifychaterror) to tell Think which errors are overflows — Think ships no provider-specific matching in core.
@@ -236,10 +236,10 @@ export class MyAgent extends Think {
-Use either layer alone, or both together: the proactive guard avoids most overflows, and the reactive backstop catches any that still slip through (for example, a turn that starts already over budget, or a single tool result so large that compaction cannot help — in which case it terminalizes cleanly). Both apply to every turn entry path (WebSocket, sub-agent `chat()`, and programmatic `saveMessages()` / `submitMessages()`), and both emit a `chat:context:compacted` [diagnostics channel event](/agents/runtime/operations/observability/diagnostics-channels/#chat-context-events).
+Use either layer alone, or both together: the proactive guard avoids most overflows, and the reactive backstop catches any that still slip through (for example, a turn that starts already over budget, or a single tool result so large that compaction cannot help — in which case it terminalizes cleanly). Both apply to every turn entry path (WebSocket, sub-agent `chat()`, and programmatic `saveMessages()` / `submitMessages()`), and both emit a `chat:context:compacted` [diagnostics channel event](/agents/platform/observability/diagnostics-channels/#chat-context-events).
:::note
-A no-op compaction cannot rescue an over-budget turn, so recovery is only as effective as your compaction configuration. For tool-heavy histories, configure a `tokenCounter` on `compactAfter()` (refer to [Sessions](/agents/runtime/lifecycle/sessions/#auto-compaction)).
+A no-op compaction cannot rescue an over-budget turn, so recovery is only as effective as your compaction configuration. For tool-heavy histories, configure a `tokenCounter` on `compactAfter()` (refer to [Sessions](/agents/capabilities/sessions/#auto-compaction)).
:::
For a runnable demo against a real Workers AI model, refer to the [`context-overflow-recovery` example](https://github.com/cloudflare/agents/tree/main/examples/context-overflow-recovery).
diff --git a/src/content/docs/agents/harnesses/think/tools.mdx b/src/content/docs/agents/harnesses/think/tools.mdx
index 5328b49d3f6..1a7b26b04ea 100644
--- a/src/content/docs/agents/harnesses/think/tools.mdx
+++ b/src/content/docs/agents/harnesses/think/tools.mdx
@@ -20,7 +20,7 @@ On every turn, Think merges tools from multiple sources. Later sources override
2. **`getTools()`** — your custom server-side tools
3. **Extension tools** — tools from loaded extensions (prefixed by extension name)
4. **Session tools** — `set_context`, `load_context`, `search_context` (from `configureSession`)
-5. **Skill tools** — `activate_skill`, `read_skill_resource`, `run_skill_script` (from `getSkills()`, refer to [Agent Skills](/agents/runtime/execution/agent-skills/))
+5. **Skill tools** — `activate_skill`, `read_skill_resource`, `run_skill_script` (from `getSkills()`, refer to [Agent Skills](/agents/tools/agent-skills/))
6. **MCP tools** — from connected MCP servers when `includeMcpTools` is `true`
7. **Client tools** — from the browser (refer to [Client tools](/agents/harnesses/think/client-tools/))
diff --git a/src/content/docs/agents/index.mdx b/src/content/docs/agents/index.mdx
index 5c127c3d098..87d4e77f1e2 100644
--- a/src/content/docs/agents/index.mdx
+++ b/src/content/docs/agents/index.mdx
@@ -1,6 +1,6 @@
---
title: Build Agents on Cloudflare
-description: Build and host AI agents on Cloudflare. Start with the Pi harness, which runs a durable model loop with tools in a Durable Object, and add state, real-time connections, and scheduled tasks with the Agents SDK.
+description: Run an AI agent on a Cloudflare Durable Object. Start with the Pi harness, let the Lifecycle keep it running, and plug in capabilities for MCP, scheduling, and real-time clients.
pcx_content_type: overview
tags:
- AI
@@ -11,31 +11,62 @@ head:
content: Agents
---
-import { PackageManagers, RelatedProduct } from "~/components";
+import { PackageManagers, RelatedProduct, TypeScriptExample } from "~/components";
import AgentsPlatformDiagram from "~/components/cf/AgentsPlatformDiagram.astro";
-Build and host Agents on Cloudflare, connect chat, voice, email, Slack, and webhooks to a durable agent runtime with Browser, Sandbox, AI Search, MCP, Payments, and other MCP tools.
+Run your agent on Cloudflare. An agent is a [harness](/agents/harnesses/), the loop around the model, running inside a [Durable Object](/durable-objects/): an addressable instance with its own SQLite database, alarms, and WebSocket connections. You can run one agent per user, conversation, or task, and an idle agent costs close to nothing.
-When you host agents on Cloudflare, each agent session has a durable identity, local SQL storage, real-time connections, scheduled work, and recoverable execution.
+To run an agent, pick a harness and install it on a Durable Object with the [Lifecycle](/agents/lifecycle/). Then plug in the [capabilities](/agents/capabilities/) your agent needs:
-Deploy once and Cloudflare runs your agents across its global network, scaling to tens of millions of instances. No infrastructure to manage, no sessions to reconstruct, no state to externalize.
+
+
+```ts
+import { DurableObject } from "cloudflare:workers";
+import { PiHarness } from "agents/harness/pi";
+import { Lifecycle } from "agents/lifecycle";
+import { MCPClientManager } from "agents/mcp/client";
+import { Scheduler } from "agents/schedules";
+
+export class Assistant extends DurableObject {
+ readonly harness = new PiHarness({
+ /* model and tools */
+ });
+ readonly mcp = new MCPClientManager("assistant", "1.0.0", { env: this.env });
+ readonly scheduler = new Scheduler({ callbacks: {} });
+
+ readonly lifecycle = Lifecycle.install(this)
+ .use(this.harness)
+ .use(this.mcp)
+ .use(this.scheduler);
+}
+```
+
+
+
+Deploy once, and Cloudflare runs your agents across its global network, scaling to tens of millions of instances.
-Agents on Cloudflare are composed from four parts:
+An agent on Cloudflare has these parts:
+
+- **A harness** is the agent. It runs the loop around the model: prompts, tool calls, retries, and recovery. Start with the [Pi harness](/agents/harnesses/pi/), or one of the experimental harnesses: the [AI SDK harness](/agents/harnesses/ai-sdk/), [OpenCode](/agents/harnesses/opencode/), or the [Container harness](/agents/harnesses/container/), which runs Claude Code or Codex. You can also extend [Project Think](/agents/harnesses/think/), or [build your own](/agents/harnesses/#build-your-own-harness).
+- **A Durable Object** is where the agent lives. Each instance has its own storage and address, so each user or task gets its own agent.
+- **The Lifecycle** keeps the harness running on the Durable Object. It runs startup, routes requests, and owns the object's durable job queue and its single alarm, so work continues across eviction, deploys, and crashes.
+- **Capabilities** plug in what else the agent needs: an [MCP client](/agents/capabilities/#mcp-client), [schedules](/agents/capabilities/#scheduler), [queues](/agents/capabilities/#queue), [WebSockets](/agents/capabilities/#websockets) and [state](/agents/capabilities/#state) for clients, and [routed agents](/agents/capabilities/#routed-agents).
+- **Tools** let the model act: [browser automation](/agents/tools/browser/), [sandboxed code execution](/agents/tools/sandbox/), [AI Search](/agents/tools/ai-search/), [MCP tools](/agents/tools/mcp/), and [payments](/agents/tools/payments/). [Code Mode](/agents/tools/codemode/) lets models orchestrate tools by writing code.
-- **Communication channels** define how users and systems reach your agent, such as [chat](/agents/agent-class/communication-channels/chat/), [voice](/agents/agent-class/communication-channels/voice/), [email](/agents/agent-class/communication-channels/email/), [Slack](/agents/agent-class/communication-channels/slack/), [webhooks](/agents/agent-class/communication-channels/webhooks/), and other event sources.
-- **The agent harness** defines the loop: how the agent calls models, selects tools, handles tool results, streams responses, and decides whether to continue. Use the [Pi harness](/agents/harnesses/pi/) to run Pi Durable in a Durable Object, or build your own loop directly on the [Agents SDK runtime](/agents/agent-class/).
-- **The Agents SDK runtime** provides durable infrastructure: the [`Agent` class](/agents/agent-class/internals/), [state](/agents/agent-class/state/), [sessions](/agents/runtime/lifecycle/sessions/), [routing](/agents/agent-class/routing/), [WebSockets](/agents/agent-class/websockets/), [scheduling](/agents/agent-class/schedule-tasks/), [fibers](/agents/agent-class/durable-execution/), and [observability](/agents/runtime/operations/observability/).
-- **Tools** give the agent capabilities: [browser automation](/agents/tools/browser/), [sandboxed code execution](/agents/tools/sandbox/), [AI Search](/agents/tools/ai-search/), [MCP tools](/agents/tools/mcp/), and [payments](/agents/tools/payments/). [Code Mode](/agents/tools/codemode/) lets models discover and orchestrate multiple tools by writing code.
+Before harnesses, you built an agent by extending the [`Agent` class](/agents/agent-class/). The `Agent` class is still supported. It is a Durable Object with the Lifecycle and a fixed set of capabilities already installed, and it is the base for `@callable()` methods, [chat](/agents/agent-class/communication-channels/chat/), [voice](/agents/agent-class/communication-channels/voice/), [email](/agents/agent-class/communication-channels/email/), and other [communication channels](/agents/agent-class/communication-channels/). For a new agent, start with a harness.
### Get started
-Build your agent with the [Pi harness](/agents/harnesses/pi/). [Pi Durable](https://earendil.com/posts/pi-durable/) runs the agent loop, including the transcript, model calls, tools, and retries, inside a Durable Object. `PiHarness` from the Agents SDK stores Pi's state in the object's SQLite database and resumes the run after an eviction. No API keys are required. The examples use [Workers AI](/workers-ai/) through the `AI` binding.
+Build your agent with the [Pi harness](/agents/harnesses/pi/). [Pi Durable](https://earendil.com/posts/pi-durable/) runs the agent loop, including the transcript, model calls, tools, and retries, inside a Durable Object. `PiHarness` stores Pi's state in the object's SQLite database and resumes the run after an eviction. No API keys are required, because the examples use [Workers AI](/workers-ai/) through the `AI` binding.
-The [Pi harness guide](/agents/harnesses/pi/) builds a first agent in one file. To start from a complete app with a browser UI, a durable filesystem, and code execution, deploy the Pi harness example:
+- [Quick start](/agents/getting-started/quick-start/) builds the agent step by step.
+- [Add to an existing project](/agents/getting-started/add-to-existing-project/) adds an agent to a Worker you already have.
+
+You can also deploy the Pi harness example, which streams sessions to a browser over WebSockets:
[](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/agents/tree/main/examples/next/harnesses/pi)
@@ -47,6 +78,8 @@ Build an agent on the Pi harness, with tools, sessions, and recovery after evict
+The remaining examples build on the legacy `Agent` class.
+
Build a streaming AI chat agent with tools and human-in-the-loop approvals.
diff --git a/src/content/docs/agents/lifecycle/build-a-capability.mdx b/src/content/docs/agents/lifecycle/build-a-capability.mdx
new file mode 100644
index 00000000000..dba1d9c0ec9
--- /dev/null
+++ b/src/content/docs/agents/lifecycle/build-a-capability.mdx
@@ -0,0 +1,208 @@
+---
+title: Build a capability
+description: Write your own reusable Lifecycle capability with LifecycleCapability, including storage, request middleware, durable jobs, events, and user callbacks.
+pcx_content_type: how-to
+sidebar:
+ order: 3
+products:
+ - agents
+---
+
+import { TypeScriptExample } from "~/components";
+
+A capability is a class that the [Lifecycle](/agents/lifecycle/) starts and calls. It owns its own tables, its own jobs, and the routes it handles. Every capability in the Agents SDK, from `Scheduler` to `PiHarness`, is built this way, and you can write your own with the same API.
+
+This guide builds an `UptimeMonitor` capability. It checks a URL on an interval, stores each result, serves the history over HTTP, and calls back into the host when the URL is down.
+
+## Extend `LifecycleCapability`
+
+Extend `LifecycleCapability` and pass a stable, non-empty ID to `super()`. Lifecycle uses the ID to route jobs and events back to the capability, so do not change it after you deploy:
+
+
+
+```ts
+import { LifecycleCapability } from "agents/lifecycle";
+
+type UptimeMonitorOptions = {
+ url: string;
+ intervalMs: number;
+ onDown?: (status: number) => void | Promise;
+};
+
+class UptimeMonitor extends LifecycleCapability {
+ readonly options: UptimeMonitorOptions;
+
+ constructor(options: UptimeMonitorOptions) {
+ super("uptime-monitor");
+ this.options = options;
+ }
+}
+```
+
+
+
+After you install the capability, `this.lifecycle` gives it these services:
+
+| Service | Description |
+| --------------------------- | ----------------------------------------------------------------------------------- |
+| `storage` | The Durable Object's storage, including `storage.sql` |
+| `jobs` | The capability's own view of the [job queue](/agents/lifecycle/job-queue/) |
+| `ready()` | Resolves once startup has finished, starting the object if needed |
+| `status()` | `"zero"`, `"starting"`, or `"started"` |
+| `name`, `className` | The object's name and its exported class name |
+| `runInHostContext(fn)` | Runs a user callback inside the host's invocation context |
+| `events.emit(type, data)` | Publishes a best-effort event to the `agents:*` diagnostics channels |
+| `sockets`, `routes` | WebSocket access and routing between parent and child objects |
+
+The capability never receives the host object itself. Pass bindings and callbacks it needs through its constructor.
+
+## Create tables on startup
+
+Lifecycle calls `onStart()` on every capability before the host's `onStart()`. Use it to create or migrate the capability's tables. Prefix table names so they do not collide with the host's tables:
+
+
+
+```ts
+class UptimeMonitor extends LifecycleCapability {
+ // ...
+
+ override onStart(): void {
+ this.lifecycle.storage.sql.exec(`
+ CREATE TABLE IF NOT EXISTS uptime_checks (
+ checked_at INTEGER NOT NULL,
+ status INTEGER NOT NULL
+ )
+ `);
+ }
+}
+```
+
+
+
+## Run work on a schedule
+
+Push a job to wake the capability later, and handle it in `onJob()`. Return `{ rescheduleAt }` to run it again:
+
+
+
+```ts
+import {
+ LifecycleCapability,
+ type LifecycleJobContext,
+ type LifecycleJobOutcome,
+} from "agents/lifecycle";
+
+class UptimeMonitor extends LifecycleCapability {
+ // ...
+
+ async startMonitoring(): Promise {
+ await this.lifecycle.ready();
+ await this.lifecycle.jobs.push({
+ id: "check",
+ fn: "check",
+ time: Date.now(),
+ });
+ }
+
+ async onJob({ job }: LifecycleJobContext): Promise {
+ if (job.fn !== "check") return;
+ const response = await fetch(this.options.url, { method: "HEAD" });
+ this.lifecycle.storage.sql.exec(
+ "INSERT INTO uptime_checks (checked_at, status) VALUES (?, ?)",
+ Date.now(),
+ response.status,
+ );
+ this.lifecycle.events.emit("uptime:check", { status: response.status });
+
+ const { onDown } = this.options;
+ if (!response.ok && onDown) {
+ await this.lifecycle.runInHostContext(() => onDown(response.status));
+ }
+ return { rescheduleAt: Date.now() + this.options.intervalMs };
+ }
+}
+```
+
+
+
+The job has a fixed ID, so calling `startMonitoring()` again replaces the pending check instead of adding a second one.
+
+`startMonitoring()` awaits `this.lifecycle.ready()` first. A public method on a capability can run before startup, because [RPC calls](/agents/lifecycle/#call-your-agent-over-rpc) do not start the object the way requests and alarms do. Await `ready()` at the top of any method that uses storage, jobs, or work that `onStart()` also starts. `ready()` resolves immediately inside startup, so `onStart()` can call the same method.
+
+Capability hooks run outside the host's invocation context. Call user code through `runInHostContext()` so that [`getCurrentAgent()`](/agents/lifecycle/get-current-agent/) returns the host inside it. `Scheduler` and `Queue` call their registered callbacks the same way.
+
+## Handle requests
+
+Implement `onRequest()` to act as middleware in front of the host. Return a `Response` to handle the request, or `undefined` to pass it on:
+
+
+
+```ts
+import {
+ LifecycleCapability,
+ type CapabilityRequestContext,
+} from "agents/lifecycle";
+
+class UptimeMonitor extends LifecycleCapability {
+ // ...
+
+ onRequest({ request }: CapabilityRequestContext): Response | undefined {
+ if (!new URL(request.url).pathname.endsWith("/uptime")) return;
+ const checks = this.lifecycle.storage.sql
+ .exec(
+ "SELECT checked_at, status FROM uptime_checks ORDER BY checked_at DESC LIMIT 20",
+ )
+ .toArray();
+ return Response.json(checks);
+ }
+}
+```
+
+
+
+Match only the routes the capability owns. A capability that handles every request should set `claims = "catch-all"`, so that Lifecycle runs it after all other capabilities.
+
+## Install the capability
+
+Install the capability like any other:
+
+
+
+```ts
+import { DurableObject } from "cloudflare:workers";
+import { Lifecycle } from "agents/lifecycle";
+
+export class SiteAgent extends DurableObject {
+ readonly uptime = new UptimeMonitor({
+ url: "https://example.com/",
+ intervalMs: 5 * 60_000,
+ onDown: (status) => console.warn(`example.com returned ${status}`),
+ });
+ readonly lifecycle = Lifecycle.install(this).use(this.uptime);
+
+ async onRequest(): Promise {
+ await this.uptime.startMonitoring();
+ return new Response("Monitoring started", { status: 202 });
+ }
+}
+```
+
+
+
+A request to `/agents/site-agent/example/uptime` returns the stored checks. Every other request reaches the host's `onRequest()`.
+
+The same capability works on the [`Agent` class](/agents/agent-class/). Install it with `this.lifecycle.use(this.uptime)` in the Agent's constructor.
+
+## Other hooks
+
+A capability can also implement these hooks:
+
+| Hook | Purpose |
+| ------------------------------------------------------------- | -------------------------------------------------------------------- |
+| `onWebSocketUpgrade(context)` | Claim a WebSocket upgrade by returning a `Response` |
+| `onWebSocketMessage`, `onWebSocketClose`, `onWebSocketError` | Handle events for sockets the capability accepted |
+| `onJobError(context, error)` | Decide what happens after a job fails its last retry |
+| `onMemoryLimit(context)` | Apply policy after an alarm ends in a memory-limit reset |
+| `dispose()` | Release live resources during explicit teardown |
+
+Events are best-effort telemetry, not a durable outbox. If delivery is part of your business logic, store an outbox in the capability's own table.
diff --git a/src/content/docs/agents/lifecycle/get-current-agent.mdx b/src/content/docs/agents/lifecycle/get-current-agent.mdx
new file mode 100644
index 00000000000..7178c9ce359
--- /dev/null
+++ b/src/content/docs/agents/lifecycle/get-current-agent.mdx
@@ -0,0 +1,76 @@
+---
+title: getCurrentAgent()
+description: Read the current Durable Object, request, and WebSocket connection from shared code with getCurrentAgent() from agents/lifecycle.
+pcx_content_type: reference
+sidebar:
+ order: 4
+products:
+ - agents
+---
+
+import { TypeScriptExample } from "~/components";
+
+`getCurrentAgent()` returns the Durable Object that is handling the current invocation, along with its request or WebSocket connection. Use it in shared code that does not have access to `this`, such as an AI SDK tool or a logging helper.
+
+
+
+```ts
+import { DurableObject } from "cloudflare:workers";
+import { getCurrentAgent, Lifecycle } from "agents/lifecycle";
+
+function requestOrigin(): string | undefined {
+ const { request } = getCurrentAgent();
+ return request ? new URL(request.url).origin : undefined;
+}
+
+export class MyAgent extends DurableObject {
+ readonly lifecycle = Lifecycle.install(this);
+
+ onRequest(): Response {
+ return Response.json({ origin: requestOrigin() });
+ }
+}
+```
+
+
+
+Pass your class as a type parameter when shared code needs its methods:
+
+```ts
+const { agent } = getCurrentAgent();
+```
+
+## Return value
+
+| Property | Type | Description |
+| ------------ | ------------------------- | ------------------------------------------------------------------ |
+| `agent` | `T \| undefined` | The Durable Object handling the invocation |
+| `connection` | `Connection \| undefined` | The WebSocket connection, inside `WebSockets` capability handlers |
+| `request` | `Request \| undefined` | The HTTP request, inside `onRequest()` or a connect handler |
+| `email` | `unknown` | The email, inside the `Agent` class email handler |
+
+Every value is `undefined` outside an invocation that Lifecycle started.
+
+## Context availability
+
+Lifecycle sets the context while it calls host hooks and user callbacks:
+
+| Invocation | `agent` | `connection` | `request` |
+| ------------------------------------------ | ------- | ------------ | --------- |
+| `onStart()`, `onAlarm()`, `onJob()` | Yes | No | No |
+| `onRequest()` | Yes | No | Yes |
+| `WebSockets` `onConnect` handler | Yes | Yes | Yes |
+| `WebSockets` `onMessage`, `onClose`, `onError` handlers | Yes | Yes | No |
+| `Scheduler` and `Queue` callbacks | Yes | No | No |
+
+Capability hooks run outside the host context. A capability that calls user code wraps the call in `this.lifecycle.runInHostContext()`. For more information, refer to [Build a capability](/agents/lifecycle/build-a-capability/).
+
+`Scheduler` and `Queue` callbacks run from the alarm, in a fresh invocation. They do not see the request or connection that created the schedule or queue item.
+
+## Native RPC
+
+Lifecycle does not wrap native Durable Object RPC methods, so `getCurrentAgent()` returns `undefined` values inside them. The [`Agent` class](/agents/agent-class/get-current-agent/) wraps its methods for you.
+
+## The `Agent` class
+
+`getCurrentAgent()` from `agents` is the same function, typed for the `Agent` class. It also returns the current email inside `onEmail()`. For more information, refer to [`getCurrentAgent()` on the Agent class](/agents/agent-class/get-current-agent/).
diff --git a/src/content/docs/agents/lifecycle/index.mdx b/src/content/docs/agents/lifecycle/index.mdx
new file mode 100644
index 00000000000..ce9e38df249
--- /dev/null
+++ b/src/content/docs/agents/lifecycle/index.mdx
@@ -0,0 +1,213 @@
+---
+title: Lifecycle
+description: The Lifecycle keeps a harness and its capabilities running on a Durable Object. It runs startup, routes requests, and shares one durable job queue and alarm.
+pcx_content_type: concept
+sidebar:
+ order: 7
+ group:
+ hideIndex: false
+products:
+ - agents
+---
+
+import { PackageManagers, TypeScriptExample } from "~/components";
+
+Every agent on Cloudflare is a [harness](/agents/harnesses/) running on a [Durable Object](/durable-objects/). `Lifecycle` from `agents/lifecycle` is what keeps it running there. It runs startup, offers each request to the harness and the other installed [capabilities](/agents/capabilities/), and owns the object's durable job queue and its single alarm. That is how a harness wakes and finishes its work after eviction, deploys, and crashes.
+
+`agents/lifecycle` is stable. You install the Lifecycle on a class that extends `DurableObject`. Most agents only need `Lifecycle.install(this)` and one `.use()` call for each capability. This section covers what the Lifecycle does underneath, and how to [build your own capability](/agents/lifecycle/build-a-capability/). The legacy [`Agent` class](/agents/agent-class/) installs the same Lifecycle for you.
+
+## Install the Lifecycle
+
+
+
+Construct the Lifecycle as a field on your Durable Object:
+
+
+
+```ts
+import { DurableObject } from "cloudflare:workers";
+import { Lifecycle } from "agents/lifecycle";
+
+export class MyAgent extends DurableObject {
+ readonly lifecycle = Lifecycle.install(this);
+
+ onStart(): void {
+ // Runs once for each in-memory object lifetime, before any work.
+ }
+
+ onRequest(request: Request): Response {
+ return new Response(`Hello from ${this.lifecycle.name}`);
+ }
+
+ onAlarm(): void {
+ // Runs once for each alarm, after due jobs.
+ }
+}
+```
+
+
+
+`Lifecycle.install(this)` installs the `fetch`, `alarm`, `webSocketMessage`, `webSocketClose`, and `webSocketError` handlers on your class. Do not define those methods yourself. Implement the hooks Lifecycle calls instead:
+
+| Hook | When it runs |
+| -------------------- | ------------------------------------------------------------------------------- |
+| `onStart(props)` | Once per in-memory lifetime, after every capability has started |
+| `onRequest(request)` | For each HTTP request that no capability handled |
+| `onAlarm()` | Once per alarm, after Lifecycle has run the due jobs |
+| `onJob(context)` | When a job the host pushed through `lifecycle.jobs` comes due |
+
+The expanded form separates construction from handler installation:
+
+
+
+```ts
+import { DurableObject } from "cloudflare:workers";
+import { Lifecycle } from "agents/lifecycle";
+
+export class MyAgent extends DurableObject {
+ readonly lifecycle = new Lifecycle(this);
+
+ constructor(ctx: DurableObjectState, env: Env) {
+ super(ctx, env);
+ this.lifecycle.installHandlers();
+ }
+}
+```
+
+
+
+Calling `installHandlers()` more than once throws an error.
+
+## Add capabilities
+
+A capability is a reusable piece of a Durable Object. It owns its own tables and behavior, and implements only the Lifecycle hooks it needs. Construct each capability as a field, then register it with `use()`:
+
+
+
+```ts
+import { DurableObject } from "cloudflare:workers";
+import { Lifecycle } from "agents/lifecycle";
+import { MCPClientManager } from "agents/mcp/client";
+import { Scheduler } from "agents/schedules";
+
+export class MyAgent extends DurableObject {
+ readonly mcp = new MCPClientManager("my-agent", "1.0.0", { env: this.env });
+ readonly scheduler = new Scheduler({
+ callbacks: {
+ sendReminder: (payload: { message: string }) => {
+ console.log(payload.message);
+ },
+ },
+ });
+
+ readonly lifecycle = Lifecycle.install(this)
+ .use(this.mcp)
+ .use(this.scheduler);
+}
+```
+
+
+
+Declare capability fields before the `lifecycle` field. Class fields initialize in order, so a capability declared after `lifecycle` is still `undefined` when `use()` runs.
+
+Register every capability before startup. `use()` throws after startup begins, or when a capability with the same identifier is already installed. For the list of capabilities, refer to [Capabilities](/agents/capabilities/).
+
+## Route requests to your agent
+
+Use `routeAgentRequest()` in the Worker's `fetch` handler to reach a named Durable Object:
+
+
+
+```ts
+import { routeAgentRequest } from "agents";
+
+export default {
+ async fetch(request: Request, env: Env): Promise {
+ return (
+ (await routeAgentRequest(request, env)) ??
+ new Response("Not found", { status: 404 })
+ );
+ },
+} satisfies ExportedHandler;
+```
+
+
+
+The default URL shape is `/agents/:binding/:name`, where `:binding` is the kebab-case Durable Object binding name. A request to `/agents/my-agent/alice` reaches the `MyAgent` instance named `alice`.
+
+You can also call the object directly with `env.MyAgent.getByName(name).fetch(request)`.
+
+### Use named objects
+
+Lifecycle reads the object's name from `ctx.id.name` and exposes it as `lifecycle.name`. Address objects with `getByName()` or `idFromName()`. Startup fails for an object addressed with an ID from `newUniqueId()` or `idFromString()`, because those IDs carry no name.
+
+## Startup and request handling
+
+Lifecycle starts on the first request, alarm, or WebSocket event of each in-memory lifetime:
+
+1. Each capability runs its `onStart()` hook, in registration order.
+2. The host runs `onStart()`.
+3. Lifecycle handles the event that woke the object.
+
+A warm object skips startup. If startup fails, the event fails and the next event tries again.
+
+For an HTTP request, Lifecycle offers the request to each capability in registration order. The first capability that returns a `Response` handles it. A capability that returns `undefined` passes the request on. A request that no capability handles reaches the host's `onRequest()`.
+
+```txt
+routeAgentRequest(request)
+└─ stub.fetch(request)
+ └─ Lifecycle fetch
+ ├─ startup (cold objects only)
+ ├─ capabilities, in registration order
+ │ └─ the first Response handles the request
+ └─ host onRequest()
+```
+
+A capability declares `claims: "catch-all"` when it handles everything offered to a hook. Lifecycle always runs catch-alls after every selective capability. The `WebSockets` capability is a catch-all for upgrades, so other capabilities can claim their own upgrade routes first. Without a capability that accepts upgrades, Lifecycle declines WebSocket upgrades.
+
+There is no `next()` callback. A capability either handles a request or declines it.
+
+## Call your agent over RPC
+
+Public methods on a Durable Object are [RPC methods](/durable-objects/best-practices/create-durable-object-stubs-and-send-requests/). RPC calls do not pass through `fetch`, so Lifecycle does not start the object for them. Call `lifecycle.start()` first in a method that needs started capabilities:
+
+
+
+```ts
+import { DurableObject } from "cloudflare:workers";
+import { Lifecycle } from "agents/lifecycle";
+import { Scheduler } from "agents/schedules";
+
+export class MyAgent extends DurableObject {
+ readonly scheduler = new Scheduler({ callbacks: {} });
+ readonly lifecycle = Lifecycle.install(this).use(this.scheduler);
+
+ async pendingSchedules(): Promise {
+ await this.lifecycle.start();
+ return (await this.scheduler.list()).length;
+ }
+}
+```
+
+
+
+Concurrent calls to `start()` share one startup. `lifecycle.isStarted()` returns `true` once every capability and the host `onStart()` have finished. Many capability methods, such as `scheduler.set()` and `harness.prompt()`, wait for startup on their own.
+
+## Jobs and alarms
+
+A Durable Object has one alarm. Lifecycle owns it and schedules it from a durable queue of jobs, so capabilities such as `Scheduler`, `Queue`, `Tasks`, and `PiHarness` never overwrite each other's alarms.
+
+Do not call `ctx.storage.setAlarm()` on an object that uses Lifecycle. Push a job through `lifecycle.jobs` instead. For more information, refer to [Jobs and alarms](/agents/lifecycle/job-queue/).
+
+## Clean up
+
+`lifecycle.dispose()` calls each capability's `dispose()` hook in reverse registration order. Disposal releases live resources, such as MCP transports and listeners. It does not delete stored data.
+
+Eviction does not call `dispose()`. Call it during explicit teardown, before you delete the object's storage with `ctx.storage.deleteAll()`.
+
+## Related resources
+
+- [Capabilities](/agents/capabilities/) lists the capabilities you can install.
+- [Jobs and alarms](/agents/lifecycle/job-queue/) describes the durable job queue.
+- [Build a capability](/agents/lifecycle/build-a-capability/) shows how to write your own.
+- [`getCurrentAgent()`](/agents/lifecycle/get-current-agent/) reads the current object, request, and connection.
diff --git a/src/content/docs/agents/lifecycle/job-queue.mdx b/src/content/docs/agents/lifecycle/job-queue.mdx
new file mode 100644
index 00000000000..d7d10b3c376
--- /dev/null
+++ b/src/content/docs/agents/lifecycle/job-queue.mdx
@@ -0,0 +1,126 @@
+---
+title: Jobs and alarms
+description: Push durable jobs onto the Lifecycle job queue, handle them with onJob(), and control retries, rescheduling, and the single Durable Object alarm.
+pcx_content_type: reference
+sidebar:
+ order: 2
+products:
+ - agents
+---
+
+import { TypeScriptExample } from "~/components";
+
+A Durable Object has one [alarm](/durable-objects/api/alarms/). The [Lifecycle](/agents/lifecycle/) owns that alarm and schedules it from a durable queue of jobs stored in the object's SQLite database. `Scheduler`, `Queue`, `Tasks`, and `PiHarness` all store their future work as jobs, so they share the alarm without overwriting each other.
+
+You can push jobs from the host Durable Object or from your own [capability](/agents/lifecycle/build-a-capability/).
+
+## Push a job from the host
+
+A job names a function, a due time in Unix milliseconds, and an optional JSON payload. Push it through `lifecycle.jobs` and handle it in `onJob()`:
+
+
+
+```ts
+import { DurableObject } from "cloudflare:workers";
+import {
+ Lifecycle,
+ type LifecycleJobContext,
+ type LifecycleJobOutcome,
+} from "agents/lifecycle";
+
+export class CleanupAgent extends DurableObject {
+ readonly lifecycle = Lifecycle.install(this);
+
+ async onRequest(): Promise {
+ await this.lifecycle.jobs.push({
+ id: "cleanup",
+ fn: "deleteExpired",
+ time: Date.now() + 60_000,
+ retry: { maxAttempts: 3 },
+ });
+ return new Response("Cleanup scheduled", { status: 202 });
+ }
+
+ async onJob({ job }: LifecycleJobContext): Promise {
+ if (job.fn !== "deleteExpired") return;
+ this.ctx.storage.sql.exec(
+ "DELETE FROM sessions WHERE expires_at < ?",
+ Date.now(),
+ );
+ }
+}
+```
+
+
+
+The job is durable once `push()` resolves. If the object is evicted, the alarm wakes it when the job is due.
+
+## Job options
+
+`push()` accepts these options:
+
+| Option | Description |
+| -------------------- | --------------------------------------------------------------------------------------------------------------- |
+| `fn` | Required. The function name your `onJob()` dispatches on. |
+| `time` | Required. Due time in Unix milliseconds. |
+| `payload` | Optional JSON-serializable value passed to `onJob()` as `job.payload`. |
+| `id` | Stable job ID. Pushing an existing ID replaces that job. Lifecycle generates an ID when you omit it. |
+| `retry` | Retry policy for failed dispatches: `maxAttempts`, `baseDelayMs`, and `maxDelayMs`. |
+| `singleflight` | Skip the job while a previous run of it is still in progress. |
+| `hungTimeoutSeconds` | Seconds before an in-progress single-flight run counts as hung. Defaults to `30`. |
+| `exclusive` | Hold back other alarm work while this job is pending. The `Agent` class uses this for deferred destruction. |
+
+Job IDs are scoped to their owner. The host and each capability can use the same ID without conflict. A capability cannot replace or cancel a job that another owner pushed.
+
+`lifecycle.jobs` also has `cancel(id)`, `reschedule(id, time)`, `get(id)`, and `list()`.
+
+## Control what happens after a job runs
+
+The value `onJob()` returns decides what happens to the job:
+
+| Return value | Result |
+| ------------------------ | ---------------------------------------------------------- |
+| Nothing | The job completes and Lifecycle deletes it. |
+| `{ rescheduleAt: time }` | The job stays in the queue until `time`. |
+| `"yield"` | The job stays due, and the object wakes again immediately. |
+
+Return `{ rescheduleAt }` for recurring work, such as a poll every five minutes. Return `"yield"` to split a long job into smaller pieces.
+
+## Retries and failures
+
+When `onJob()` throws, Lifecycle retries the job in the same invocation, up to the job's `retry.maxAttempts`. Platform failures, such as a deploy that replaces the isolate or a memory-limit reset, keep the job for a fresh invocation instead of using up its attempts.
+
+A capability can implement `onJobError(context, error)` to decide what happens after the last attempt fails. A host job that fails every attempt completes.
+
+Before it runs any job, Lifecycle sets a backup alarm. If the isolate dies mid-run, that alarm wakes the object to resume the work.
+
+## How the alarm runs
+
+When the alarm fires, Lifecycle:
+
+1. Runs every due job in due-time order.
+2. Runs the host's `onAlarm()`.
+3. Sets the alarm for the next due job.
+
+Every change to the queue resets the alarm, so you never set it yourself. Do not call `ctx.storage.setAlarm()` on an object that uses Lifecycle, because that alarm would replace the one Lifecycle manages.
+
+Keep each `onJob()` call short. Lifecycle awaits jobs one at a time, so a slow job delays every other job on the object. For long work, start it, record its progress in storage, and return. [`Tasks`](/agents/capabilities/#tasks) does this for you with replayable steps.
+
+## Memory-limit protection
+
+A job that exhausts the isolate's memory can crash every alarm that retries it. Lifecycle counts consecutive alarm invocations that end in a memory-limit reset. Each one delays the next alarm with a growing backoff, up to five minutes. After three in a row, Lifecycle stops the retry loop and removes the jobs that capabilities marked as recovery loops. Set the limit with the `maxAlarmMemoryLimitStrikes` option:
+
+
+
+```ts
+import { DurableObject } from "cloudflare:workers";
+import { Lifecycle } from "agents/lifecycle";
+
+export class MyAgent extends DurableObject {
+ readonly lifecycle = Lifecycle.install(this, {
+ maxAlarmMemoryLimitStrikes: 5,
+ });
+}
+```
+
+
diff --git a/src/content/docs/agents/model-context-protocol/apis/agent-api.mdx b/src/content/docs/agents/model-context-protocol/apis/agent-api.mdx
index 4600cac1fbf..5c28166e880 100644
--- a/src/content/docs/agents/model-context-protocol/apis/agent-api.mdx
+++ b/src/content/docs/agents/model-context-protocol/apis/agent-api.mdx
@@ -338,9 +338,9 @@ For a stateless server, read the same props with `getMcpAuthContext()`. Refer to
The `McpAgent` class provides full access to the [Agent state APIs](/agents/agent-class/state/):
- [`state`](/agents/agent-class/state/) — Current persisted state
-- [`initialState`](/agents/agent-class/state/#set-the-initial-state-for-an-agent) — Default state when instance starts
+- [`initialState`](/agents/agent-class/state/#defining-initial-state) — Default state when instance starts
- [`setState`](/agents/agent-class/state/) — Update and persist state
-- [`onStateChanged`](/agents/agent-class/state/#synchronizing-state) — React to state changes
+- [`onStateChanged`](/agents/agent-class/state/#client-side-state-sync) — React to state changes
- [`sql`](/agents/agent-class/#sql-api) — Execute SQL queries on embedded database
:::note[State resets after the session ends]
diff --git a/src/content/docs/agents/models/index.mdx b/src/content/docs/agents/models/index.mdx
index 62e6bf1159d..46e982d9ea0 100644
--- a/src/content/docs/agents/models/index.mdx
+++ b/src/content/docs/agents/models/index.mdx
@@ -5,7 +5,7 @@ description: Call Workers AI, AI Gateway catalog models, dynamic routes, and ven
tags:
- AI
sidebar:
- order: 4.5
+ order: 4
badge: Beta
group:
hideIndex: false
diff --git a/src/content/docs/agents/runtime/operations/configuration.mdx b/src/content/docs/agents/platform/configuration.mdx
similarity index 99%
rename from src/content/docs/agents/runtime/operations/configuration.mdx
rename to src/content/docs/agents/platform/configuration.mdx
index ab9a592a4f1..25add28a335 100644
--- a/src/content/docs/agents/runtime/operations/configuration.mdx
+++ b/src/content/docs/agents/platform/configuration.mdx
@@ -3,7 +3,7 @@ title: Configuration
description: Configure Wrangler bindings, environment variables, and type generation for a project using the Agents SDK.
pcx_content_type: concept
sidebar:
- order: 3
+ order: 2
products:
- agents
---
diff --git a/src/content/docs/agents/runtime/operations/cross-domain-authentication.mdx b/src/content/docs/agents/platform/cross-domain-authentication.mdx
similarity index 99%
rename from src/content/docs/agents/runtime/operations/cross-domain-authentication.mdx
rename to src/content/docs/agents/platform/cross-domain-authentication.mdx
index 75270b95c4f..7dd53a36574 100644
--- a/src/content/docs/agents/runtime/operations/cross-domain-authentication.mdx
+++ b/src/content/docs/agents/platform/cross-domain-authentication.mdx
@@ -3,7 +3,7 @@ title: Cross-domain authentication
description: Authenticate WebSocket connections to Cloudflare Agents across domains using signed tokens.
pcx_content_type: how-to
sidebar:
- order: 13
+ order: 4
products:
- agents
---
diff --git a/src/content/docs/agents/platform/index.mdx b/src/content/docs/agents/platform/index.mdx
index 4dd2f7adb1e..a1aa3cacdf7 100644
--- a/src/content/docs/agents/platform/index.mdx
+++ b/src/content/docs/agents/platform/index.mdx
@@ -1,9 +1,9 @@
---
pcx_content_type: reference
title: Platform
-description: Review platform details for Cloudflare Agents, including limits and configuration options.
+description: Review platform details for Cloudflare Agents, including limits, configuration, observability, and authentication.
sidebar:
- order: 9
+ order: 10
group:
hideIndex: true
products:
diff --git a/src/content/docs/agents/runtime/operations/observability/diagnostics-channels.mdx b/src/content/docs/agents/platform/observability/diagnostics-channels.mdx
similarity index 99%
rename from src/content/docs/agents/runtime/operations/observability/diagnostics-channels.mdx
rename to src/content/docs/agents/platform/observability/diagnostics-channels.mdx
index 514d08fc8f2..45fdc05d387 100644
--- a/src/content/docs/agents/runtime/operations/observability/diagnostics-channels.mdx
+++ b/src/content/docs/agents/platform/observability/diagnostics-channels.mdx
@@ -299,13 +299,13 @@ These events track chat message lifecycle, client-side tool interactions, and Th
diff --git a/src/content/docs/agents/runtime/operations/observability/index.mdx b/src/content/docs/agents/platform/observability/index.mdx
similarity index 93%
rename from src/content/docs/agents/runtime/operations/observability/index.mdx
rename to src/content/docs/agents/platform/observability/index.mdx
index 59aca38fbbb..57aef9f4766 100644
--- a/src/content/docs/agents/runtime/operations/observability/index.mdx
+++ b/src/content/docs/agents/platform/observability/index.mdx
@@ -2,7 +2,7 @@
title: Observability
pcx_content_type: navigation
sidebar:
- order: 30
+ order: 3
group:
hideIndex: true
---
diff --git a/src/content/docs/agents/runtime/operations/observability/tracing.mdx b/src/content/docs/agents/platform/observability/tracing.mdx
similarity index 94%
rename from src/content/docs/agents/runtime/operations/observability/tracing.mdx
rename to src/content/docs/agents/platform/observability/tracing.mdx
index 3ac85830243..298efa9a560 100644
--- a/src/content/docs/agents/runtime/operations/observability/tracing.mdx
+++ b/src/content/docs/agents/platform/observability/tracing.mdx
@@ -41,7 +41,7 @@ Select an agent to see its traces. Each trace includes its duration, token break
There are two ways to follow what an agent did: **Session replay** and **Trace**.
-**Session replay** shows the recorded conversation across turns, including messages, reasoning, tool calls, and subagent activity. What appears depends on your [payload recording settings](/agents/runtime/operations/observability/tracing/#payload-privacy).
+**Session replay** shows the recorded conversation across turns, including messages, reasoning, tool calls, and subagent activity. What appears depends on your [payload recording settings](/agents/platform/observability/tracing/#payload-privacy).

@@ -78,7 +78,7 @@ Do not derive the agent name from a request, conversation, or user identifier. T
### Payload privacy
-Message and tool payloads can contain personally identifiable information. Only record payloads that are safe to store. Each integration controls payload recording differently, so configure it in the relevant [Framework setup](/agents/runtime/operations/observability/tracing/#framework-setup) section.
+Message and tool payloads can contain personally identifiable information. Only record payloads that are safe to store. Each integration controls payload recording differently, so configure it in the relevant [Framework setup](/agents/platform/observability/tracing/#framework-setup) section.
Think and `wrapAISDK()` use `storeMessages` to record input and output messages on `chat` spans. They use `storeTools` to record arguments and results on `execute_tool` spans.
@@ -88,7 +88,7 @@ Tracing and payload controls depend on the integration. Think and Flue instrumen
### Think
-Think automatically instruments your agent and emits the [standard span structure](/agents/runtime/operations/observability/tracing/#trace-structure). No additional tracing setup is required.
+Think automatically instruments your agent and emits the [standard span structure](/agents/platform/observability/tracing/#trace-structure). No additional tracing setup is required.
#### Store payloads
@@ -109,7 +109,7 @@ export class MyAgent extends Think {
### Flue
-[Flue v2+](https://flueframework.com/blog/flue-2/) also automatically instruments your agent and emits the [standard span structure](/agents/runtime/operations/observability/tracing/#trace-structure). No additional tracing setup is required.
+[Flue v2+](https://flueframework.com/blog/flue-2/) also automatically instruments your agent and emits the [standard span structure](/agents/platform/observability/tracing/#trace-structure). No additional tracing setup is required.
#### Exclude payloads
@@ -143,7 +143,7 @@ const tracedAI = wrapAISDK(ai);
`wrapAISDK()` supports AI SDK v6 and v7. It instruments `generateText`, `streamText`, `generateObject`, and `streamObject`, creating the `invoke_agent` parent before model and tool work begins.
-Unlike Think, a direct AI SDK call has no Agent instance from which to infer dashboard identity. Supply the [agent identity fields](/agents/runtime/operations/observability/tracing/#agent-identity) on each call.
+Unlike Think, a direct AI SDK call has no Agent instance from which to infer dashboard identity. Supply the [agent identity fields](/agents/platform/observability/tracing/#agent-identity) on each call.
Only include context that is safe to store. Do not include credentials, tokens, user input, or other secrets.
@@ -267,13 +267,13 @@ Beginning December 1, 2026, agent traces contribute to account-level ingestion a
diff --git a/src/content/docs/agents/runtime/execution/index.mdx b/src/content/docs/agents/runtime/execution/index.mdx
deleted file mode 100644
index 6014c2a070b..00000000000
--- a/src/content/docs/agents/runtime/execution/index.mdx
+++ /dev/null
@@ -1,12 +0,0 @@
----
-title: Execution
-pcx_content_type: navigation
-sidebar:
- order: 3
- group:
- hideIndex: true
----
-
-import { DirectoryListing } from "~/components";
-
-
diff --git a/src/content/docs/agents/runtime/index.mdx b/src/content/docs/agents/runtime/index.mdx
deleted file mode 100644
index af883fedb83..00000000000
--- a/src/content/docs/agents/runtime/index.mdx
+++ /dev/null
@@ -1,14 +0,0 @@
----
-title: Runtime
-pcx_content_type: navigation
-sidebar:
- order: 6
- group:
- hideIndex: true
----
-
-import { DirectoryListing } from "~/components";
-
-The runtime powers your agent — state, communication, execution, and operations.
-
-
diff --git a/src/content/docs/agents/runtime/lifecycle/index.mdx b/src/content/docs/agents/runtime/lifecycle/index.mdx
deleted file mode 100644
index cc12db0abd2..00000000000
--- a/src/content/docs/agents/runtime/lifecycle/index.mdx
+++ /dev/null
@@ -1,12 +0,0 @@
----
-title: Lifecycle
-pcx_content_type: navigation
-sidebar:
- order: 1
- group:
- hideIndex: true
----
-
-import { DirectoryListing } from "~/components";
-
-
diff --git a/src/content/docs/agents/runtime/operations/index.mdx b/src/content/docs/agents/runtime/operations/index.mdx
deleted file mode 100644
index a451c862df9..00000000000
--- a/src/content/docs/agents/runtime/operations/index.mdx
+++ /dev/null
@@ -1,12 +0,0 @@
----
-title: Operations
-pcx_content_type: navigation
-sidebar:
- order: 4
- group:
- hideIndex: true
----
-
-import { DirectoryListing } from "~/components";
-
-
diff --git a/src/content/docs/agents/runtime/execution/agent-skills.mdx b/src/content/docs/agents/tools/agent-skills.mdx
similarity index 99%
rename from src/content/docs/agents/runtime/execution/agent-skills.mdx
rename to src/content/docs/agents/tools/agent-skills.mdx
index 7bef060ae58..265bac3e4b1 100644
--- a/src/content/docs/agents/runtime/execution/agent-skills.mdx
+++ b/src/content/docs/agents/tools/agent-skills.mdx
@@ -3,7 +3,7 @@ title: Agent Skills
pcx_content_type: reference
description: Give an agent a catalog of on-demand instructions, resources, and scripts with agents/skills, activated by the model only when a task matches.
sidebar:
- order: 17
+ order: 8
products:
- agents
---
diff --git a/src/content/docs/agents/tools/browser.mdx b/src/content/docs/agents/tools/browser.mdx
index 35e4b2e06f2..c8f4f35cdfa 100644
--- a/src/content/docs/agents/tools/browser.mdx
+++ b/src/content/docs/agents/tools/browser.mdx
@@ -31,7 +31,7 @@ Start with a [Pi Durable agent](/agents/harnesses/pi/). Install the browser runt
-This example installs a `Browser` and `PiHarness` on a plain Durable Object. Pi chooses when to call the browser tool. [Lifecycle](/agents/runtime/lifecycle/) starts both components and provides their storage.
+This example installs a `Browser` and `PiHarness` on a plain Durable Object. Pi chooses when to call the browser tool. [Lifecycle](/agents/lifecycle/) starts both components and provides their storage.
diff --git a/src/content/docs/agents/tools/index.mdx b/src/content/docs/agents/tools/index.mdx
index 56e6419587c..9464e5c5256 100644
--- a/src/content/docs/agents/tools/index.mdx
+++ b/src/content/docs/agents/tools/index.mdx
@@ -2,7 +2,7 @@
title: Tools
pcx_content_type: navigation
sidebar:
- order: 4
+ order: 6
group:
hideIndex: true
---
diff --git a/src/content/partials/agents/agent-class-legacy.mdx b/src/content/partials/agents/agent-class-legacy.mdx
new file mode 100644
index 00000000000..2806bd5746d
--- /dev/null
+++ b/src/content/partials/agents/agent-class-legacy.mdx
@@ -0,0 +1,9 @@
+---
+{}
+---
+
+:::note[Start new agents with a harness]
+
+The `Agent` class is the original way to build agents with the Agents SDK. It is still supported, but new agents should run a [harness](/agents/harnesses/) on a plain Durable Object and plug in only the [capabilities](/agents/capabilities/) they need. To get started that way, refer to the [Quick start](/agents/getting-started/quick-start/).
+
+:::
From e9668103a79f3a205b4a7ecf861b4a264e01405f Mon Sep 17 00:00:00 2001
From: Matt Carey
Date: Tue, 27 Oct 2026 11:31:50 +0000
Subject: [PATCH 04/11] [Agents] Update harness docs for Pi 1.4 and
agents@0.30, link the Codex example, and address review
---
.../agents/2026-08-04-agent-tracing.mdx | 2 +-
.../2026-10-20-think-computer-workspace.mdx | 2 +-
.../agent-class/add-to-existing-project.mdx | 2 +-
src/content/docs/agents/agent-class/index.mdx | 17 +++++++++++++-
.../docs/agents/capabilities/index.mdx | 6 ++---
.../agents/getting-started/quick-start.mdx | 15 ++++++------
src/content/docs/agents/harnesses/index.mdx | 23 ++++++++++++++++---
src/content/docs/agents/index.mdx | 19 +++++++++++++--
src/content/docs/agents/lifecycle/index.mdx | 23 +++++++++++++++++--
.../docs/agents/lifecycle/job-queue.mdx | 2 ++
10 files changed, 89 insertions(+), 22 deletions(-)
diff --git a/src/content/changelog/agents/2026-08-04-agent-tracing.mdx b/src/content/changelog/agents/2026-08-04-agent-tracing.mdx
index 59ef0618d69..8636c2710c2 100644
--- a/src/content/changelog/agents/2026-08-04-agent-tracing.mdx
+++ b/src/content/changelog/agents/2026-08-04-agent-tracing.mdx
@@ -40,7 +40,7 @@ Enable tracing in your Wrangler configuration:
## Framework support
-Think and Flue agents are instrumented automatically. For direct AI SDK calls, wrap the namespace with [`wrapAISDK()`](/agents/runtime/operations/observability/tracing/#ai-sdk). Custom harnesses can use the [Workers custom spans API](/workers/observability/traces/custom-spans/) with [OpenTelemetry Generative AI semantic conventions](https://opentelemetry.io/docs/specs/semconv/registry/attributes/gen-ai/).
+Think and Flue agents are instrumented automatically. For direct AI SDK calls, wrap the namespace with [`wrapAISDK()`](/agents/platform/observability/tracing/#ai-sdk). Custom harnesses can use the [Workers custom spans API](/workers/observability/traces/custom-spans/) with [OpenTelemetry Generative AI semantic conventions](https://opentelemetry.io/docs/specs/semconv/registry/attributes/gen-ai/).
## Get Started
diff --git a/src/content/changelog/agents/2026-10-20-think-computer-workspace.mdx b/src/content/changelog/agents/2026-10-20-think-computer-workspace.mdx
index d130c8e2703..31babb4f206 100644
--- a/src/content/changelog/agents/2026-10-20-think-computer-workspace.mdx
+++ b/src/content/changelog/agents/2026-10-20-think-computer-workspace.mdx
@@ -9,7 +9,7 @@ date: 2026-10-20
import { TypeScriptExample } from "~/components";
-[Think](/agents/harnesses/think/) and [Agent Skills](/agents/runtime/execution/agent-skills/) now use the workspace from [`@cloudflare/computer`](https://github.com/cloudflare/computer) as well as the filesystem tools. A JavaScript execution environment can be configured by adding the `WorkerJavaScriptBackend`.
+[Think](/agents/harnesses/think/) and [Agent Skills](/agents/tools/agent-skills/) now use the workspace from [`@cloudflare/computer`](https://github.com/cloudflare/computer) as well as the filesystem tools. A JavaScript execution environment can be configured by adding the `WorkerJavaScriptBackend`.
`@cloudflare/shell` is deprecated, and so is Think's `createExecuteTool`. These will no longer be developed nor supported from today onwards.
diff --git a/src/content/docs/agents/agent-class/add-to-existing-project.mdx b/src/content/docs/agents/agent-class/add-to-existing-project.mdx
index 2e0b9cf9528..4a49c9fb5e3 100644
--- a/src/content/docs/agents/agent-class/add-to-existing-project.mdx
+++ b/src/content/docs/agents/agent-class/add-to-existing-project.mdx
@@ -110,7 +110,7 @@ Add the Durable Object binding and migration:
## 4. Configure TypeScript and Vite
-If you use `@callable()` decorators (as in the example above), you need two build configurations.
+If you use `@callable()` decorators, as `CounterAgent` does, you need two build configurations.
**tsconfig.json** — extend `agents/tsconfig` (or set `"target": "ES2021"` manually):
diff --git a/src/content/docs/agents/agent-class/index.mdx b/src/content/docs/agents/agent-class/index.mdx
index f19a485b5b9..6897b8e3102 100644
--- a/src/content/docs/agents/agent-class/index.mdx
+++ b/src/content/docs/agents/agent-class/index.mdx
@@ -41,11 +41,26 @@ To stay on the `Agent` class and add another capability, such as the [Pi harness
```ts
import { Agent } from "agents";
+import { createModels } from "@earendil-works/pi-ai/models";
+import { createRegistry, Harness } from "@earendil-works/pi-durable";
import { PiHarness } from "agents/harness/pi";
+import { createAI } from "agents/models/pi-ai";
export class Assistant extends Agent {
+ ai = createAI({ binding: this.env.AI });
+ registry = createRegistry();
+
harness = new PiHarness({
- /* model and tools */
+ harness: ({ storage, context, onWake }) => {
+ const models = createModels();
+ models.setProvider(this.ai.provider);
+ return Harness.open(
+ storage,
+ { models, registry: this.registry, onWake },
+ context,
+ );
+ },
+ defaults: { model: this.ai("@cf/zai-org/glm-5.3-flash") },
});
constructor(ctx: DurableObjectState, env: Env) {
diff --git a/src/content/docs/agents/capabilities/index.mdx b/src/content/docs/agents/capabilities/index.mdx
index 3114a7a8e44..e5072996584 100644
--- a/src/content/docs/agents/capabilities/index.mdx
+++ b/src/content/docs/agents/capabilities/index.mdx
@@ -24,7 +24,7 @@ import { Scheduler } from "agents/schedules";
import { WebSockets } from "agents/websockets";
export class MyAgent extends DurableObject {
- readonly mcp = new MCPClientManager("my-agent", "1.0.0", { env: this.env });
+ readonly mcp = new MCPClientManager("my-agent", "1.0.0");
readonly scheduler = new Scheduler({ callbacks: {} });
readonly webSockets = new WebSockets();
@@ -73,9 +73,7 @@ import { Lifecycle } from "agents/lifecycle";
import { MCPClientManager } from "agents/mcp/client";
export class ToolAgent extends DurableObject {
- readonly mcp = new MCPClientManager("tool-agent", "1.0.0", {
- env: this.env,
- });
+ readonly mcp = new MCPClientManager("tool-agent", "1.0.0");
readonly lifecycle = Lifecycle.install(this).use(this.mcp);
}
```
diff --git a/src/content/docs/agents/getting-started/quick-start.mdx b/src/content/docs/agents/getting-started/quick-start.mdx
index b05b08f3723..f9dba73d867 100644
--- a/src/content/docs/agents/getting-started/quick-start.mdx
+++ b/src/content/docs/agents/getting-started/quick-start.mdx
@@ -42,7 +42,7 @@ You do not need an API key. The agent uses [Workers AI](/workers-ai/) through th
cd my-agent
```
-
+
@@ -93,15 +93,16 @@ export class Assistant extends DurableObject {
readonly registry = createRegistry();
readonly harness = new PiHarness({
- harness: ({ storage, context }) => {
+ harness: ({ storage, context, onWake }) => {
const models = createModels();
models.setProvider(this.ai.provider);
- return Harness.open(storage, { models, registry: this.registry }, context);
- },
- defaults: {
- model: this.ai("@cf/zai-org/glm-4.7-flash"),
- thinkingLevel: "low",
+ return Harness.open(
+ storage,
+ { models, registry: this.registry, onWake },
+ context,
+ );
},
+ defaults: { model: this.ai("@cf/zai-org/glm-5.3-flash") },
});
readonly lifecycle = Lifecycle.install(this).use(this.harness);
diff --git a/src/content/docs/agents/harnesses/index.mdx b/src/content/docs/agents/harnesses/index.mdx
index cd8be57dd94..4f03fa86c1c 100644
--- a/src/content/docs/agents/harnesses/index.mdx
+++ b/src/content/docs/agents/harnesses/index.mdx
@@ -16,7 +16,7 @@ The harness does the turn-by-turn work around the model: building the prompt, lo
Running an agent on Cloudflare takes three steps:
-1. **Pick a harness.** Install one of the [harnesses below](#choose-a-harness), such as the [Pi harness](/agents/harnesses/pi/), or [build your own](#build-your-own-harness).
+1. **Pick a harness.** Install a [harness](#choose-a-harness), such as the [Pi harness](/agents/harnesses/pi/), or [build your own](#build-your-own-harness).
2. **Run it on a Durable Object.** Install the harness with the [Lifecycle](/agents/lifecycle/). The Lifecycle runs startup, routes requests, and owns the object's job queue and alarm, so the harness wakes and carries on after eviction, deploys, and crashes.
3. **Plug in capabilities.** Add an [MCP client](/agents/capabilities/#mcp-client), a [scheduler](/agents/capabilities/#scheduler), [WebSockets](/agents/capabilities/#websockets), or any other [capability](/agents/capabilities/) the agent needs. Each one is a field and one more `.use()` call.
@@ -24,14 +24,29 @@ Running an agent on Cloudflare takes three steps:
```ts
import { DurableObject } from "cloudflare:workers";
+import { createModels } from "@earendil-works/pi-ai/models";
+import { createRegistry, Harness } from "@earendil-works/pi-durable";
import { PiHarness } from "agents/harness/pi";
import { Lifecycle } from "agents/lifecycle";
+import { createAI } from "agents/models/pi-ai";
import { Scheduler } from "agents/schedules";
export class Assistant extends DurableObject {
+ readonly ai = createAI({ binding: this.env.AI });
+ readonly registry = createRegistry();
+
// The harness: the agent loop.
readonly harness = new PiHarness({
- /* model and tools */
+ harness: ({ storage, context, onWake }) => {
+ const models = createModels();
+ models.setProvider(this.ai.provider);
+ return Harness.open(
+ storage,
+ { models, registry: this.registry, onWake },
+ context,
+ );
+ },
+ defaults: { model: this.ai("@cf/zai-org/glm-5.3-flash") },
});
// A capability plugged in next to it.
@@ -50,7 +65,7 @@ For a complete, runnable agent, refer to the [Quick start](/agents/getting-start
## Choose a harness
-Except for Project Think, every harness below is a capability: it installs on a plain Durable Object with `Lifecycle.use()`, next to any other capability, and keeps many sessions per object through `sessions` and `session(id)`. Each session takes durable, idempotent submissions that you can wait on or abort.
+Except for Project Think, every harness in this table is a capability: it installs on a plain Durable Object with `Lifecycle.use()`, next to any other capability, and keeps many sessions per object through `sessions` and `session(id)`. Each session takes durable, idempotent submissions that you can wait on or abort.
| Harness | Import | Status | What runs the loop |
| ----------------------------------------------------- | -------------------------- | ------------ | ------------------------------------------------------------------------ |
@@ -86,6 +101,8 @@ store.enqueue({ session: "main", id: operationId, input: { text } });
store.append("main", "messages", [{ data: message }]);
```
+For a complete harness built this way, refer to the [Codex harness example](https://github.com/cloudflare/agents/tree/main/examples/next/harnesses/codex). It runs a turn loop derived from Codex, compiled to WebAssembly, as a capability on a plain Durable Object. The transcript lives in Sessions, operation events go to Streams, model and tool steps run as Tasks, and WebSockets serves the events to the browser.
+
## What a harness owns
A harness usually owns:
diff --git a/src/content/docs/agents/index.mdx b/src/content/docs/agents/index.mdx
index 87d4e77f1e2..704551f95e7 100644
--- a/src/content/docs/agents/index.mdx
+++ b/src/content/docs/agents/index.mdx
@@ -22,16 +22,31 @@ To run an agent, pick a harness and install it on a Durable Object with the [Lif
```ts
import { DurableObject } from "cloudflare:workers";
+import { createModels } from "@earendil-works/pi-ai/models";
+import { createRegistry, Harness } from "@earendil-works/pi-durable";
import { PiHarness } from "agents/harness/pi";
import { Lifecycle } from "agents/lifecycle";
import { MCPClientManager } from "agents/mcp/client";
+import { createAI } from "agents/models/pi-ai";
import { Scheduler } from "agents/schedules";
export class Assistant extends DurableObject {
+ readonly ai = createAI({ binding: this.env.AI });
+ readonly registry = createRegistry();
+
readonly harness = new PiHarness({
- /* model and tools */
+ harness: ({ storage, context, onWake }) => {
+ const models = createModels();
+ models.setProvider(this.ai.provider);
+ return Harness.open(
+ storage,
+ { models, registry: this.registry, onWake },
+ context,
+ );
+ },
+ defaults: { model: this.ai("@cf/zai-org/glm-5.3-flash") },
});
- readonly mcp = new MCPClientManager("assistant", "1.0.0", { env: this.env });
+ readonly mcp = new MCPClientManager("assistant", "1.0.0");
readonly scheduler = new Scheduler({ callbacks: {} });
readonly lifecycle = Lifecycle.install(this)
diff --git a/src/content/docs/agents/lifecycle/index.mdx b/src/content/docs/agents/lifecycle/index.mdx
index ce9e38df249..7fcd0197dc5 100644
--- a/src/content/docs/agents/lifecycle/index.mdx
+++ b/src/content/docs/agents/lifecycle/index.mdx
@@ -14,6 +14,25 @@ import { PackageManagers, TypeScriptExample } from "~/components";
Every agent on Cloudflare is a [harness](/agents/harnesses/) running on a [Durable Object](/durable-objects/). `Lifecycle` from `agents/lifecycle` is what keeps it running there. It runs startup, offers each request to the harness and the other installed [capabilities](/agents/capabilities/), and owns the object's durable job queue and its single alarm. That is how a harness wakes and finishes its work after eviction, deploys, and crashes.
+```mermaid
+flowchart TB
+ R["Request, WebSocket message, or alarm"] --> L
+ subgraph DO["Durable Object: one per user, conversation, or task"]
+ L["Lifecycle runs startup, routes each event, owns the job queue and the one alarm"]
+ L -->|"onStart, onRequest, onJob"| H["Harness, such as PiHarness runs the agent loop"]
+ L -->|"onStart, onRequest, onJob"| C["Capabilities, such as Scheduler, MCPClientManager, and WebSockets"]
+ H --- DB[("SQLite storage")]
+ C --- DB
+ end
+```
+
+The roles split like this:
+
+- **The Durable Object** is where the agent lives. It gives each agent an address, its own SQLite database, and one alarm.
+- **The Lifecycle** decides when code runs. It starts every installed capability once per wake, offers each request and WebSocket event to them in order, and runs due jobs when the alarm fires.
+- **The harness** runs the agent loop: the model calls, tool calls, and transcript. It stores its work in the object's database and queues jobs so the Lifecycle can wake it to finish.
+- **Capabilities** add everything else, such as schedules, MCP connections, and real-time clients. Each one is independent of the others and of the harness.
+
`agents/lifecycle` is stable. You install the Lifecycle on a class that extends `DurableObject`. Most agents only need `Lifecycle.install(this)` and one `.use()` call for each capability. This section covers what the Lifecycle does underneath, and how to [build your own capability](/agents/lifecycle/build-a-capability/). The legacy [`Agent` class](/agents/agent-class/) installs the same Lifecycle for you.
## Install the Lifecycle
@@ -91,7 +110,7 @@ import { MCPClientManager } from "agents/mcp/client";
import { Scheduler } from "agents/schedules";
export class MyAgent extends DurableObject {
- readonly mcp = new MCPClientManager("my-agent", "1.0.0", { env: this.env });
+ readonly mcp = new MCPClientManager("my-agent", "1.0.0");
readonly scheduler = new Scheduler({
callbacks: {
sendReminder: (payload: { message: string }) => {
@@ -149,7 +168,7 @@ Lifecycle starts on the first request, alarm, or WebSocket event of each in-memo
2. The host runs `onStart()`.
3. Lifecycle handles the event that woke the object.
-A warm object skips startup. If startup fails, the event fails and the next event tries again.
+A warm object skips startup. If startup fails, the event fails and the next event tries again. If the job queue holds jobs, a fallback alarm also retries startup, so queued work does not wait for a new request. For more information, refer to [Jobs and alarms](/agents/lifecycle/job-queue/#retries-and-failures).
For an HTTP request, Lifecycle offers the request to each capability in registration order. The first capability that returns a `Response` handles it. A capability that returns `undefined` passes the request on. A request that no capability handles reaches the host's `onRequest()`.
diff --git a/src/content/docs/agents/lifecycle/job-queue.mdx b/src/content/docs/agents/lifecycle/job-queue.mdx
index d7d10b3c376..f746013a288 100644
--- a/src/content/docs/agents/lifecycle/job-queue.mdx
+++ b/src/content/docs/agents/lifecycle/job-queue.mdx
@@ -94,6 +94,8 @@ A capability can implement `onJobError(context, error)` to decide what happens a
Before it runs any job, Lifecycle sets a backup alarm. If the isolate dies mid-run, that alarm wakes the object to resume the work.
+While the queue holds jobs, Lifecycle also sets a fallback alarm before startup runs, at the next job's time but at least 30 seconds out. If startup throws, that alarm retries it, so queued jobs are not stranded by a failed startup.
+
## How the alarm runs
When the alarm fires, Lifecycle:
From 09c61e102ca2d6f3ea52d8b93b580677c4e50498 Mon Sep 17 00:00:00 2001
From: Matt Carey
Date: Tue, 27 Oct 2026 12:02:47 +0000
Subject: [PATCH 05/11] [Agents] Remove the AI SDK harness docs
---
src/components/cf/AgentsPlatformDiagram.astro | 5 +-
src/content/docs/agents/harnesses/ai-sdk.mdx | 106 ------------------
src/content/docs/agents/harnesses/index.mdx | 3 -
src/content/docs/agents/index.mdx | 2 +-
4 files changed, 5 insertions(+), 111 deletions(-)
delete mode 100644 src/content/docs/agents/harnesses/ai-sdk.mdx
diff --git a/src/components/cf/AgentsPlatformDiagram.astro b/src/components/cf/AgentsPlatformDiagram.astro
index ec81fc77e4b..c9e41871904 100644
--- a/src/components/cf/AgentsPlatformDiagram.astro
+++ b/src/components/cf/AgentsPlatformDiagram.astro
@@ -96,7 +96,6 @@ const tools = [