agents 0.20.1 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/README.md +51 -12
  2. package/dist/{agent-tool-types-BC-WFlsz.d.ts → agent-routing-CnkaHb-v.d.ts} +3305 -3138
  3. package/dist/agent-routing.d.ts +14 -0
  4. package/dist/agent-routing.js +187 -0
  5. package/dist/agent-routing.js.map +1 -0
  6. package/dist/agent-tool-types.d.ts +26 -26
  7. package/dist/{agent-tools-DeHe9Xov.d.ts → agent-tools-C0XJqWzB.d.ts} +7 -7
  8. package/dist/agent-tools.d.ts +44 -28
  9. package/dist/agent-tools.js +16 -7
  10. package/dist/agent-tools.js.map +1 -1
  11. package/dist/browser/ai.d.ts +5 -3
  12. package/dist/browser/ai.js +88 -8
  13. package/dist/browser/ai.js.map +1 -1
  14. package/dist/browser/index.d.ts +1 -1
  15. package/dist/browser/index.js +1 -1
  16. package/dist/browser/tanstack-ai.js +13 -1
  17. package/dist/browser/tanstack-ai.js.map +1 -1
  18. package/dist/capability-BjSKYpzg.js +42 -0
  19. package/dist/capability-BjSKYpzg.js.map +1 -0
  20. package/dist/capability-runner-CvHGZqUu.d.ts +150 -0
  21. package/dist/chat/index.d.ts +51 -15
  22. package/dist/chat/index.js +59 -4
  23. package/dist/chat/index.js.map +1 -1
  24. package/dist/chat/react.d.ts +19 -180
  25. package/dist/chat/react.js +93 -600
  26. package/dist/chat/react.js.map +1 -1
  27. package/dist/chat/transport.d.ts +10 -0
  28. package/dist/chat/transport.js +2 -0
  29. package/dist/chat-sdk/index.d.ts +4 -4
  30. package/dist/chat-sdk/index.js +1 -1
  31. package/dist/{client-zqKcsyFa.js → client-jagG8a9_.js} +129 -37
  32. package/dist/client-jagG8a9_.js.map +1 -0
  33. package/dist/client.d.ts +1 -1
  34. package/dist/client.js +1 -1
  35. package/dist/{cloudflare-BduZwmYK.js → cloudflare-Dzvc7V2N.js} +10 -3
  36. package/dist/{cloudflare-BduZwmYK.js.map → cloudflare-Dzvc7V2N.js.map} +1 -1
  37. package/dist/{connector-v2M1zlZp.d.ts → connector-CkQD4MK3.d.ts} +20 -3
  38. package/dist/{connector-KEJnl6e5.js → connector-CptFKzRh.js} +158 -40
  39. package/dist/connector-CptFKzRh.js.map +1 -0
  40. package/dist/current-agent-CuMErtly.d.ts +283 -0
  41. package/dist/current-agent-DhoDkSnH.js +51 -0
  42. package/dist/current-agent-DhoDkSnH.js.map +1 -0
  43. package/dist/diagnostics-CaBjfz4J.js +41 -0
  44. package/dist/diagnostics-CaBjfz4J.js.map +1 -0
  45. package/dist/{do-oauth-client-provider-VTZj2VtM.d.ts → do-oauth-client-provider-Tmf1vgKz.d.ts} +2 -2
  46. package/dist/durable-object-lifecycle-D6nNQJJd.js +862 -0
  47. package/dist/durable-object-lifecycle-D6nNQJJd.js.map +1 -0
  48. package/dist/{email-CL27preh.d.ts → email-7TatiTnl.d.ts} +38 -9
  49. package/dist/email-send.d.ts +15 -0
  50. package/dist/email-send.js +32 -0
  51. package/dist/email-send.js.map +1 -0
  52. package/dist/email.d.ts +14 -10
  53. package/dist/email.js.map +1 -1
  54. package/dist/experimental/memory/session/index.d.ts +3 -2
  55. package/dist/experimental/memory/session/index.js +15 -10
  56. package/dist/experimental/memory/session/index.js.map +1 -1
  57. package/dist/{handler-stateless-C_bo-Ytq.d.ts → handler-stateless-DxYpJ_XF.d.ts} +3 -3
  58. package/dist/{handler-stateless-CIkKPETH.js → handler-stateless-VvrWSAVA.js} +5 -5
  59. package/dist/handler-stateless-VvrWSAVA.js.map +1 -0
  60. package/dist/{index-BRnybD6X.d.ts → index-DcSAZKsB.d.ts} +19 -31
  61. package/dist/index.d.ts +102 -91
  62. package/dist/index.js +12 -7235
  63. package/dist/internal_context-BlxFEWfn.d.ts +19 -0
  64. package/dist/internal_context.d.ts +10 -4
  65. package/dist/internal_context.js +1 -10
  66. package/dist/{client-invoker-BNSZxAkv.d.ts → invoker-CG0_p_Wq.d.ts} +2 -2
  67. package/dist/{client-invoker-VNZ7X0nn.js → invoker-CHMnoxIA.js} +2 -2
  68. package/dist/invoker-CHMnoxIA.js.map +1 -0
  69. package/dist/lifecycle/index.d.ts +48 -0
  70. package/dist/lifecycle/index.js +4 -0
  71. package/dist/mcp/{do-oauth-client-provider.d.ts → client/do-oauth-client-provider.d.ts} +1 -1
  72. package/dist/mcp/{do-oauth-client-provider.js → client/do-oauth-client-provider.js} +1 -1
  73. package/dist/mcp/client/do-oauth-client-provider.js.map +1 -0
  74. package/dist/mcp/client/index.d.ts +42 -0
  75. package/dist/mcp/{client.js → client/index.js} +1 -1
  76. package/dist/mcp/{x402.d.ts → client/x402.d.ts} +2 -2
  77. package/dist/mcp/{x402.js → client/x402.js} +2 -2
  78. package/dist/mcp/client/x402.js.map +1 -0
  79. package/dist/mcp/index.d.ts +36 -36
  80. package/dist/mcp/index.js +14 -16
  81. package/dist/mcp/index.js.map +1 -1
  82. package/dist/mcp/{server.d.ts → server/index.d.ts} +1 -1
  83. package/dist/mcp/{server.js → server/index.js} +1 -1
  84. package/dist/observability/ai/index.d.ts +1 -130
  85. package/dist/observability/ai/index.js +88 -433
  86. package/dist/observability/ai/index.js.map +1 -1
  87. package/dist/observability/index.d.ts +4 -4
  88. package/dist/observability/index.js +3 -50
  89. package/dist/observability/index.js.map +1 -1
  90. package/dist/{wire-types-CU9rLoeS.js → protocol-B0nh6KNf.js} +20 -66
  91. package/dist/protocol-B0nh6KNf.js.map +1 -0
  92. package/dist/react.d.ts +4 -4
  93. package/dist/react.js +6 -9
  94. package/dist/react.js.map +1 -1
  95. package/dist/schedule.d.ts +25 -94
  96. package/dist/schedule.js +1 -98
  97. package/dist/schedule.js.map +1 -1
  98. package/dist/scheduler-CR9RHGos.js +857 -0
  99. package/dist/scheduler-CR9RHGos.js.map +1 -0
  100. package/dist/scheduler-DQoTGoAW.d.ts +224 -0
  101. package/dist/schedules/index.d.ts +22 -0
  102. package/dist/schedules/index.js +2 -0
  103. package/dist/schedules/parser.d.ts +79 -0
  104. package/dist/schedules/parser.js +103 -0
  105. package/dist/schedules/parser.js.map +1 -0
  106. package/dist/serializable.d.ts +1 -1
  107. package/dist/sql-error-CPY-GXyI.d.ts +12 -0
  108. package/dist/sql-error.d.ts +2 -0
  109. package/dist/sql-error.js +16 -0
  110. package/dist/sql-error.js.map +1 -0
  111. package/dist/src-5W6JNKVb.js +6613 -0
  112. package/dist/src-5W6JNKVb.js.map +1 -0
  113. package/dist/sub-routing.d.ts +18 -6
  114. package/dist/sub-routing.js +92 -2
  115. package/dist/sub-routing.js.map +1 -1
  116. package/dist/wire-types-CnMt6_HR.js +47 -0
  117. package/dist/wire-types-CnMt6_HR.js.map +1 -0
  118. package/dist/workflow-types.d.ts +25 -25
  119. package/dist/workflow-types.js.map +1 -1
  120. package/dist/workflows.d.ts +22 -22
  121. package/dist/workflows.js +2 -1
  122. package/dist/workflows.js.map +1 -1
  123. package/dist/ws-chat-transport-UNRIS2xl.d.ts +184 -0
  124. package/dist/ws-chat-transport-rWwta645.js +698 -0
  125. package/dist/ws-chat-transport-rWwta645.js.map +1 -0
  126. package/docs/adding-to-existing-project.md +4 -2
  127. package/docs/agent-class.md +29 -87
  128. package/docs/agent-tools.md +29 -0
  129. package/docs/browse-the-web.md +16 -1
  130. package/docs/chat-agents.md +16 -13
  131. package/docs/client-sdk.md +12 -8
  132. package/docs/configuration.md +7 -1
  133. package/docs/cross-domain-authentication.md +7 -35
  134. package/docs/durable-execution.md +1 -1
  135. package/docs/email.md +2 -13
  136. package/docs/http-websockets.md +1 -11
  137. package/docs/human-in-the-loop.md +16 -13
  138. package/docs/index.md +1 -0
  139. package/docs/lifecycle.md +323 -0
  140. package/docs/long-running-agents.md +13 -15
  141. package/docs/mcp-client.md +55 -0
  142. package/docs/mcp-servers.md +6 -0
  143. package/docs/mcp-transports.md +18 -12
  144. package/docs/migration-to-ai-sdk-v5.md +2 -2
  145. package/docs/migration-to-ai-sdk-v6.md +5 -1
  146. package/docs/observability.md +33 -77
  147. package/docs/resumable-streaming.md +1 -1
  148. package/docs/routing.md +27 -0
  149. package/docs/scheduling.md +175 -15
  150. package/docs/server-driven-messages.md +1 -1
  151. package/docs/sub-agents.md +47 -2
  152. package/docs/webhooks.md +109 -136
  153. package/docs/workflows.md +9 -1
  154. package/package.json +41 -23
  155. package/dist/cli/index.d.ts +0 -1
  156. package/dist/cli/index.js +0 -26
  157. package/dist/cli/index.js.map +0 -1
  158. package/dist/client-invoker-VNZ7X0nn.js.map +0 -1
  159. package/dist/client-zqKcsyFa.js.map +0 -1
  160. package/dist/connector-KEJnl6e5.js.map +0 -1
  161. package/dist/handler-stateless-CIkKPETH.js.map +0 -1
  162. package/dist/index.js.map +0 -1
  163. package/dist/internal_context-Dg4Cgjcu.d.ts +0 -37
  164. package/dist/internal_context.js.map +0 -1
  165. package/dist/mcp/client.d.ts +0 -42
  166. package/dist/mcp/do-oauth-client-provider.js.map +0 -1
  167. package/dist/mcp/x402.js.map +0 -1
  168. package/dist/wire-types-CU9rLoeS.js.map +0 -1
  169. package/dist/workflow-types-Baz_PO5v.d.ts +0 -280
@@ -2,13 +2,18 @@
2
2
 
3
3
  This guide explains the different transport options for connecting to MCP servers with the Agents SDK.
4
4
 
5
- For a primer on MCP servers and how they are implemented in the Agents SDK with `McpAgent`, see [Creating MCP Servers](./mcp-servers.md).
5
+ For a primer on MCP servers, see [Creating MCP Servers](./mcp-servers.md).
6
6
 
7
7
  ## Streamable HTTP Transport (Recommended)
8
8
 
9
- The **Streamable HTTP** transport is the recommended way to connect to MCP servers.
9
+ The Agents SDK has two server paths:
10
10
 
11
- ### How it works
11
+ - Current SDK v2 servers use `createMcpHandler`, a per-request stateless Worker handler with no Durable Object or WebSocket bridge.
12
+ - Retained SDK v1 servers use the deprecated, feature-frozen `McpAgent`, which provides sessionful transports through a Durable Object and internal WebSocket bridge.
13
+
14
+ The current stateless handler also accepts stateless 2025-era Streamable HTTP requests by default. It does not expose standalone legacy SSE.
15
+
16
+ ### Legacy `McpAgent` architecture
12
17
 
13
18
  When a client connects to your MCP server:
14
19
 
@@ -44,13 +49,13 @@ The `serve()` method returns a Worker with a `fetch` handler that:
44
49
 
45
50
  ### Connection from clients
46
51
 
47
- Clients connect using the `streamable-http` transport:
52
+ The Agents MCP client uses `"auto"` by default. It tries Streamable HTTP first and falls back to legacy SSE only when the endpoint reports that Streamable HTTP is unsupported:
48
53
 
49
54
  ```typescript
50
55
  await agent.addMcpServer("my-server", "https://your-worker.workers.dev/mcp");
51
56
  ```
52
57
 
53
- ## Auto Transport
58
+ ## Legacy `McpAgent` Server Auto Transport
54
59
 
55
60
  The **auto** transport serves both Streamable HTTP and legacy SSE on the same endpoint. Capable clients use Streamable HTTP automatically, while older SSE-only clients continue to work.
56
61
 
@@ -60,7 +65,7 @@ export default MyMCP.serve("/mcp", { transport: "auto" });
60
65
 
61
66
  The handler distinguishes between the two protocols based on the request shape — no configuration or content negotiation is required from clients. This is useful when migrating from SSE to Streamable HTTP without breaking existing clients.
62
67
 
63
- ## SSE Transport (Deprecated)
68
+ ## Legacy `McpAgent` SSE Transport (Deprecated)
64
69
 
65
70
  We also support the legacy **SSE (Server-Sent Events)** transport, but it is deprecated in favor of Streamable HTTP.
66
71
 
@@ -294,12 +299,13 @@ export class MyMCP extends McpAgent<Env, State> {
294
299
 
295
300
  ## Choosing a transport
296
301
 
297
- | Transport | Use when | Pros | Cons |
298
- | ------------------- | ---------------------------------------- | ---------------------------------------- | ------------------------------- |
299
- | **Streamable HTTP** | External MCP servers, production apps | Standard protocol, secure, supports auth | Slight network overhead |
300
- | **Auto** | Migrating from SSE, mixed client support | Serves both protocols on one endpoint | Reserves `{path}/message` route |
301
- | **RPC** | Internal agents | Fastest, simplest setup | No auth, Service Bindings only |
302
- | **SSE** | Legacy compatibility | Backwards compatible | Deprecated, use Streamable HTTP |
302
+ | Transport | Use when | Pros | Cons |
303
+ | --------------------------------- | --------------------------------------------------- | ------------------------------------------ | -------------------------------------- |
304
+ | **Streamable HTTP** | New servers and external MCP connections | Current standard transport; supports OAuth | Slight network overhead |
305
+ | **Client `auto`** | The remote server's HTTP transport is not known | Tries Streamable HTTP, then legacy SSE | May make a fallback connection attempt |
306
+ | **Legacy `McpAgent` server auto** | An existing server must accept both HTTP transports | Serves both protocols on one endpoint | Uses the deprecated server path |
307
+ | **RPC** | Internal agents | Fastest, simplest setup | No auth, Service Bindings only |
308
+ | **SSE** | Legacy compatibility | Backwards compatible | Deprecated, use Streamable HTTP |
303
309
 
304
310
  ## Examples
305
311
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  This guide covers the changes needed when upgrading from AI SDK v4 to v5 with `@cloudflare/ai-chat`.
4
4
 
5
- > If you are on AI SDK v5 and upgrading to v6, see the [v6 migration guide](./migration-to-ai-sdk-v6.md) instead.
5
+ > The current `agents` and `@cloudflare/ai-chat` packages support AI SDK v6 and v7, not v5. If an application is still on v4, apply the intermediate changes on this page and then continue through the [v6 migration guide](./migration-to-ai-sdk-v6.md). Do not install AI SDK v5 alongside the current Cloudflare packages.
6
6
 
7
7
  ## Message format: `content` to `parts`
8
8
 
@@ -71,7 +71,7 @@ chunk.type === "text-delta" && chunk.delta;
71
71
 
72
72
  ## Migration checklist
73
73
 
74
- 1. Update dependencies: `npm update agents ai`
74
+ 1. Apply this intermediate checklist without installing v5, then continue through the [v6 migration guide](./migration-to-ai-sdk-v6.md) and install the supported package majors shown there
75
75
  2. Replace `import type { Message }` with `import type { UIMessage }`
76
76
  3. Replace `"ai/react"` imports with `"@ai-sdk/react"`
77
77
  4. Rename `parameters` to `inputSchema` in tool definitions
@@ -4,10 +4,14 @@ This guide covers the changes needed when upgrading from AI SDK v5 to v6 with `@
4
4
 
5
5
  ## Installation
6
6
 
7
+ Pin the v6-compatible majors so this migration does not also install AI SDK v7:
8
+
7
9
  ```bash
8
- npm install ai@latest @ai-sdk/react@latest @ai-sdk/openai@latest
10
+ npm install ai@^6 @ai-sdk/react@^3 @ai-sdk/openai@^3
9
11
  ```
10
12
 
13
+ The current `agents` and `@cloudflare/ai-chat` releases support both AI SDK v6 and v7. This guide covers v6 only.
14
+
11
15
  ## Breaking changes
12
16
 
13
17
  ### 1. `convertToModelMessages()` is now async
@@ -158,17 +158,17 @@ These events are emitted by `AIChatAgent` from `@cloudflare/ai-chat`. They track
158
158
 
159
159
  ### Chat recovery events
160
160
 
161
- | Type | Payload | When |
162
- | ------------------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
163
- | `chat:request:failed` | `{ requestId?, stage, messagesPersisted?, error }` | A Think chat request fails while parsing, persisting, running, or streaming |
164
- | `chat:recovery:detected` | `{ incidentId, requestId, attempt, maxAttempts, recoveryKind }` | An interrupted chat fiber is first observed |
165
- | `chat:recovery:attempt` | `{ incidentId, requestId, attempt, maxAttempts, recoveryKind }` | The framework begins a recovery attempt |
166
- | `chat:recovery:scheduled` | `{ incidentId, requestId, attempt, maxAttempts, recoveryKind }` | A retry or continuation callback is scheduled |
167
- | `chat:recovery:completed` | `{ incidentId, requestId, attempt, maxAttempts, recoveryKind }` | Recovery completed successfully |
168
- | `chat:recovery:skipped` | `{ incidentId, requestId, attempt, maxAttempts, recoveryKind, reason? }` | Recovery was skipped because the conversation changed or was no longer recoverable |
169
- | `chat:recovery:failed` | `{ incidentId, requestId, attempt, maxAttempts, recoveryKind, reason? }` | Recovery ran but failed |
170
- | `chat:recovery:exhausted` | `{ incidentId, requestId, attempt, maxAttempts, recoveryKind, reason }` | Recovery exceeded its configured attempt budget |
171
- | `chat:stream:stalled` | `{ requestId, timeoutMs }` | The inactivity watchdog fired — no stream chunk arrived within `chatStreamStallTimeoutMs`. With `chatRecovery` on (the default) the turn then routes into bounded recovery (look for `chat:recovery:*`); with recovery off it terminalizes. See [Think configuration](https://github.com/cloudflare/agents/blob/main/docs/think/index.md) |
161
+ | Type | Payload | When |
162
+ | ------------------------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
163
+ | `chat:request:failed` | `{ requestId?, stage, messagesPersisted?, error }` | A Think chat request fails while parsing, persisting, running, or streaming |
164
+ | `chat:recovery:detected` | `{ incidentId, requestId, attempt, maxAttempts, recoveryKind }` | An interrupted chat fiber is first observed |
165
+ | `chat:recovery:attempt` | `{ incidentId, requestId, attempt, maxAttempts, recoveryKind }` | The framework begins a recovery attempt |
166
+ | `chat:recovery:scheduled` | `{ incidentId, requestId, attempt, maxAttempts, recoveryKind }` | A retry or continuation callback is scheduled |
167
+ | `chat:recovery:completed` | `{ incidentId, requestId, attempt, maxAttempts, recoveryKind }` | Recovery completed successfully |
168
+ | `chat:recovery:skipped` | `{ incidentId, requestId, attempt, maxAttempts, recoveryKind, reason? }` | Recovery was skipped because the conversation changed or was no longer recoverable |
169
+ | `chat:recovery:failed` | `{ incidentId, requestId, attempt, maxAttempts, recoveryKind, reason? }` | Recovery ran but failed |
170
+ | `chat:recovery:exhausted` | `{ incidentId, requestId, attempt, maxAttempts, recoveryKind, reason }` | Recovery exceeded its configured attempt budget |
171
+ | `chat:stream:stalled` | `{ requestId, timeoutMs }` | The inactivity watchdog fired — no stream chunk arrived within `chatStreamStallTimeoutMs`. The turn then routes into bounded recovery; look for `chat:recovery:*`. See [Think configuration](https://github.com/cloudflare/agents/blob/main/docs/think/index.md) |
172
172
 
173
173
  `recoveryKind` is `"retry"` when recovery replays an unanswered user turn and `"continue"` when it continues a partial assistant turn.
174
174
 
@@ -292,9 +292,9 @@ metadata fields for applications with a different identity model. Payload
292
292
  storage is off by default. Set `storeMessages` and/or `storeTools` on the Think
293
293
  agent to opt in; these are wrapper settings, not span attributes.
294
294
 
295
- ### AI SDK v6
295
+ ### AI SDK v6 and v7
296
296
 
297
- Wrap the SDK namespace:
297
+ Wrap the SDK namespace and call the wrapped functions:
298
298
 
299
299
  ```ts
300
300
  import * as ai from "ai";
@@ -303,14 +303,17 @@ import { wrapAISDK } from "agents/observability/ai";
303
303
  const { generateText, streamText } = wrapAISDK(ai);
304
304
  ```
305
305
 
306
- `wrapAISDK` instruments `generateText`, `streamText`, `generateObject`, and
307
- `streamObject`. Span names use `{operation} {target}` and fall back to the bare
308
- operation past 64 UTF-8 bytes; the full target remains on its semantic
309
- attribute. A model object is wrapped with the SDK's `wrapLanguageModel` helper,
310
- so provider work is a `chat {model}` child of the operation span. Tool
311
- execution is wrapped as `execute_tool {tool}`. AI SDK v6 approval lifecycle
312
- segments appear as bounded `tool_approval {tool}` children of an
313
- `execute_tool {tool}` span, correlated by `gen_ai.tool.call.id` and carrying
306
+ `wrapAISDK` supports AI SDK v6 and v7. It instruments `generateText`,
307
+ `streamText`, `generateObject`, and `streamObject`. You do not need to register a
308
+ telemetry integration for Agents tracing on AI SDK v7.
309
+
310
+ Span names use `{operation} {target}` and fall back to the bare operation past
311
+ 64 UTF-8 bytes; the full target remains on its semantic attribute. A model
312
+ object is wrapped with the SDK's `wrapLanguageModel` helper, so provider work is
313
+ a `chat {model}` child of the `invoke_agent {agent}` operation span. Tool
314
+ execution is wrapped as `execute_tool {tool}`. Approval lifecycle segments
315
+ appear as bounded `tool_approval {tool}` children of an `execute_tool {tool}`
316
+ span, correlated by `gen_ai.tool.call.id` and carrying
314
317
  `cloudflare.agents.tool.approval.state` (`requested`, `approved`, or `denied`).
315
318
  They never remain open while waiting for a human across invocations. Stream
316
319
  spans close on completion, cancellation, an in-band error, or early consumer
@@ -329,46 +332,10 @@ const traced = wrapAISDK(ai, {
329
332
  });
330
333
  ```
331
334
 
332
- ### AI SDK v7
333
-
334
- AI SDK v7 ships a first-class telemetry lifecycle. Register the adapter once and
335
- every `generateText`, `streamText`, `generateObject`, and `streamObject` call is
336
- instrumented:
337
-
338
- ```ts
339
- import { registerTelemetry } from "ai";
340
- import { createAISDKTelemetry } from "agents/observability/ai";
341
-
342
- registerTelemetry(
343
- createAISDKTelemetry({ storeMessages: true, storeTools: true })
344
- );
345
- ```
346
-
347
- Or scope it to a single call through `experimental_telemetry`:
348
-
349
- ```ts
350
- import { createAISDKTelemetry } from "agents/observability/ai";
351
-
352
- await generateText({
353
- model,
354
- prompt: "...",
355
- experimental_telemetry: {
356
- integrations: [createAISDKTelemetry()]
357
- }
358
- });
359
- ```
360
-
361
- The v7 adapter uses `cloudflare.agents.call.id` to correlate operation, model,
362
- and tool spans. Its execution hooks keep provider work under the `chat` span and
363
- nested work performed by a tool under the `execute_tool` span. It handles both
364
- `onEnd` and `onAbort` terminal paths. `wrapAISDK` and `createAISDKTelemetry`
365
- project into the same span schema, so the two SDK versions are dashboard-
366
- compatible.
367
-
368
335
  When a gateway-backed provider exposes its AI Gateway log ID through response
369
336
  headers, provider metadata, or the Workers AI binding, the corresponding
370
337
  `chat` span includes `cloudflare.ai_gateway.log.id`. The attribute is omitted
371
- when no actual response exposes an ID; the adapter does not infer one or make an
338
+ when no actual response exposes an ID; the wrapper does not infer one or make an
372
339
  extra request.
373
340
 
374
341
  ### Identity
@@ -445,13 +412,13 @@ await generateText({
445
412
  | `gen_ai.usage.cache_creation.input_tokens`, `gen_ai.usage.cache_read.input_tokens` | Provider cache usage when reported |
446
413
  | `gen_ai.usage.reasoning.output_tokens` | Reasoning output usage when reported |
447
414
  | `gen_ai.tool.name`, `gen_ai.tool.type`, `gen_ai.tool.call.id` | Tool identity; call ID also correlates approval lifecycle segments |
448
- | `cloudflare.agents.tool.approval.state` | AI SDK v6 approval lifecycle segment: `requested`, `approved`, or `denied` |
415
+ | `cloudflare.agents.tool.approval.state` | Approval lifecycle segment: `requested`, `approved`, or `denied` |
449
416
  | `gen_ai.input.messages`, `gen_ai.output.messages` | Opt-in OTel-schema model messages on `chat`, including tool parts and output finish reasons |
450
417
  | `gen_ai.tool.call.arguments`, `gen_ai.tool.call.result` | Opt-in tool arguments/results on `execute_tool` |
451
418
  | `user.id` | Explicit v6 metadata key `user.id` |
452
419
  | `error.type` | Low-cardinality error class; raw error messages are never recorded |
453
420
 
454
- The adapter also emits a small vendor namespace where no standard equivalent
421
+ The wrapper also emits a small vendor namespace where no standard equivalent
455
422
  exists:
456
423
 
457
424
  | Attribute | Meaning |
@@ -459,12 +426,10 @@ exists:
459
426
  | `cloudflare.ai_gateway.log.id` | AI Gateway log reference on `chat`, when exposed by the actual response |
460
427
  | `cloudflare.agents.integration.name` | Instrumentation source (`ai-sdk`) |
461
428
  | `cloudflare.agents.operation.name` | Original SDK operation (`streamText`, `doStream`, `tool.execute`, etc.) |
462
- | `cloudflare.agents.call.id` | AI SDK v7 callback correlation ID |
463
429
  | `cloudflare.agents.response.finish_reason` | One finish reason as a scalar |
464
430
  | `cloudflare.agents.tool.count` | Precomputed tool-call count for dashboards |
465
431
  | `cloudflare.agents.usage.total_tokens` | Provider total, or input plus output when both are known |
466
432
  | `cloudflare.agents.runtime_context.{key}` | Explicitly included scalar runtime context |
467
- | `cloudflare.agents.tool_context.{tool}.{key}` | Explicitly included scalar context on the executed tool span |
468
433
  | `cloudflare.agents.metadata.{key}` | Other scalar v6 telemetry metadata |
469
434
  | `cloudflare.agents.turn.{request_id,trigger,admission,channel,continuation,generation}` | Think turn context, from v6 metadata or v7 runtime context |
470
435
  | `cloudflare.agents.canceled` | Recognized cancellation, not a failure |
@@ -472,16 +437,16 @@ exists:
472
437
 
473
438
  `gen_ai.response.finish_reasons` and `gen_ai.request.stop_sequences` are arrays
474
439
  in OTel. Workers' custom `Span.setAttribute` currently accepts only a string,
475
- number, or boolean, so the adapter omits those attributes rather than placing
440
+ number, or boolean, so the wrapper omits those attributes rather than placing
476
441
  JSON text under an array-typed key. Similarly, span status is state rather than
477
- an attribute: failures emit `error.type`, but the adapter does not invent an
442
+ an attribute: failures emit `error.type`, but the wrapper does not invent an
478
443
  `otel.status_code` attribute when Workers exposes no custom-span status setter.
479
444
 
480
445
  ### Context and safety
481
446
 
482
447
  Payload storage is explicit and off by default. `storeMessages` writes only
483
448
  `gen_ai.input.messages` / `gen_ai.output.messages` on `chat`; when the message
484
- attribute exceeds its budget, the adapter repeatedly drops the oldest
449
+ attribute exceeds its budget, the wrapper repeatedly drops the oldest
485
450
  unprotected message (index 2), preserving the first two messages and newest
486
451
  tail. `storeTools` writes only `gen_ai.tool.call.arguments` /
487
452
  `gen_ai.tool.call.result` on `execute_tool`. The flags themselves are never
@@ -489,7 +454,7 @@ written to telemetry metadata or spans.
489
454
 
490
455
  System instructions that the AI SDK presents to the model as a system-role chat
491
456
  message remain in `gen_ai.input.messages`, which OTel explicitly permits for
492
- instructions that are part of chat history. The adapter does not separately
457
+ instructions that are part of chat history. The wrapper does not separately
493
458
  copy the raw `system` parameter into `gen_ai.system_instructions`. Schemas,
494
459
  request headers, provider options, and raw error messages are never recorded.
495
460
  The optional AI Gateway reference is a bounded opaque log ID; response headers
@@ -514,27 +479,18 @@ await traced.generateText({
514
479
  });
515
480
  ```
516
481
 
517
- For v7, the AI SDK filters runtime and per-tool context before the adapter sees
518
- it. Its allowlists are boolean maps, not arrays:
482
+ For v7, put application context in `runtimeContext` and select telemetry-visible
483
+ keys with the AI SDK's boolean-map allowlist:
519
484
 
520
485
  ```ts
521
486
  await generateText({
522
487
  model,
523
488
  prompt: "Will I need an umbrella?",
524
489
  runtimeContext: { requestId: "req-123", tenantId: "tenant-42" },
525
- toolsContext: {
526
- weather: { defaultUnit: "celsius", cacheHit: true }
527
- },
528
490
  telemetry: {
529
491
  includeRuntimeContext: {
530
492
  requestId: true,
531
493
  tenantId: true
532
- },
533
- includeToolsContext: {
534
- weather: {
535
- defaultUnit: true,
536
- cacheHit: true
537
- }
538
494
  }
539
495
  }
540
496
  });
@@ -2,7 +2,7 @@
2
2
 
3
3
  The `AIChatAgent` class provides **automatic resumable streaming** out of the box. When a client disconnects and reconnects during an active stream, the response automatically resumes from where it left off.
4
4
 
5
- This is client reconnect recovery, not Durable Object eviction recovery. If the Worker process or Durable Object is evicted while the model call is in flight, enable `chatRecovery` so the turn runs inside a recoverable fiber. `Think` enables `chatRecovery` by default; plain `AIChatAgent` subclasses opt in with `override chatRecovery = true`.
5
+ This is client reconnect recovery, not Durable Object eviction recovery. Durable Object eviction recovery is also always enabled for `AIChatAgent` and `Think`: every turn runs inside a recoverable fiber. Use `chatRecovery` only to tune its budgets and terminal behavior.
6
6
 
7
7
  ## How It Works
8
8
 
package/docs/routing.md CHANGED
@@ -96,6 +96,33 @@ export default {
96
96
 
97
97
  ---
98
98
 
99
+ ## Building Agent URLs
100
+
101
+ Use `buildAgentPath()` when a server, external provider, or client needs the canonical pathname for a known root-first Agent identity. It handles the top-level route and every nested `/sub/...` hop:
102
+
103
+ ```typescript
104
+ import { buildAgentPath, buildAgentUrl } from "agents";
105
+
106
+ const address = [
107
+ { className: "Inbox", name: userId },
108
+ { className: "Chat", name: chatId }
109
+ ];
110
+
111
+ buildAgentPath(address, { leafPath: "/callbacks/job" });
112
+ // /agents/inbox/{userId}/sub/chat/{chatId}/callbacks/job
113
+
114
+ buildAgentUrl("https://app.example.com", address, {
115
+ leafPath: "/callbacks/job"
116
+ });
117
+ // URL("https://app.example.com/agents/inbox/...")
118
+ ```
119
+
120
+ Inside an Agent, `this.selfPath` has the required root-first shape. If the root Durable Object binding name differs from its class name, pass that binding name as `rootBinding`. The pathname works for both HTTP and WebSocket traffic. For a custom top-level prefix, pass the same `prefix` to `buildAgentPath()` and `routeAgentRequest()`.
121
+
122
+ See [Sub-agents](./sub-agents.md#direct-http-and-websocket-urls) for callbacks, webhooks, custom routing, and name-encoding details.
123
+
124
+ ---
125
+
99
126
  ## Instance Naming Patterns
100
127
 
101
128
  The instance name (the last part of the URL) determines which agent instance handles the request. Each unique name gets its own isolated agent with its own state.
@@ -15,6 +15,91 @@ The scheduling system supports four modes:
15
15
 
16
16
  Under the hood, scheduling uses [Durable Object alarms](https://developers.cloudflare.com/durable-objects/api/alarms/) to wake the agent at the right time. Tasks are stored in a SQLite table and executed in order.
17
17
 
18
+ ## Scheduler Lifecycle primitive
19
+
20
+ > **Experimental.** The `Scheduler` primitive and the `agents/lifecycle`
21
+ > surface it builds on may change between releases. Agent's established
22
+ > scheduling methods (`this.schedule()` and friends) are stable.
23
+
24
+ `Scheduler` is a reusable Lifecycle capability. A plain Lifecycle Object can
25
+ install it without extending `Agent`:
26
+
27
+ ```typescript
28
+ import { DurableObject } from "cloudflare:workers";
29
+ import { Lifecycle } from "agents/lifecycle";
30
+ import { Scheduler, type Schedule } from "agents/schedules";
31
+
32
+ export class ReminderObject extends DurableObject<Env> {
33
+ readonly scheduler = new Scheduler({
34
+ callbacks: {
35
+ sendReminder: (
36
+ payload: { message: string },
37
+ schedule: Schedule<{ message: string }>
38
+ ) => {
39
+ console.log(schedule.id, payload.message);
40
+ }
41
+ }
42
+ });
43
+
44
+ readonly lifecycle = Lifecycle.install(this).use(this.scheduler);
45
+
46
+ async createReminder(message: string): Promise<string> {
47
+ const schedule = await this.scheduler.set(300, "sendReminder", {
48
+ message
49
+ });
50
+ return schedule.id;
51
+ }
52
+ }
53
+ ```
54
+
55
+ Lifecycle owns the physical Durable Object alarm. Scheduler contributes its
56
+ earliest pending task or hung-interval recheck. Lifecycle selects the earliest
57
+ contribution from Scheduler, other capabilities, and the host, then rearms after
58
+ every alarm phase. A future Fiber or MCP capability can contribute its own wake
59
+ time without storing work in Scheduler or depending on it.
60
+
61
+ Scheduler's primary API is small: callbacks are registered by name in the
62
+ constructor, `set()` and `every()` create schedules typed against that
63
+ registration, and `get()`, `list()`, and `cancel()` manage them. All of these
64
+ are asynchronous and work inside routed sub-agents.
65
+
66
+ Scheduler Lifecycle hooks run without ambient host context. Registered
67
+ callbacks are user code, so they run inside the host invocation context with
68
+ the Lifecycle Object available through `getCurrentAgent()`.
69
+
70
+ ## Using Scheduler through Agent
71
+
72
+ Every `Agent` constructs and installs the same primitive at `this.scheduler`.
73
+ Existing Agent applications continue to use the established methods:
74
+
75
+ - `this.schedule()` and `this.scheduleEvery()` create schedules.
76
+ - `this.getScheduleById()` and `this.listSchedules()` read schedules.
77
+ - `this.cancelSchedule()` removes a schedule.
78
+
79
+ These methods delegate to `this.scheduler`; no setup or migration is required.
80
+ Agent registers no callbacks map — a composition-root resolver keeps
81
+ `this.schedule(60, "methodName")` dispatching to Agent methods. Agent passes
82
+ only policy options (retry defaults, hung-interval timeout, error routing) and
83
+ adapts Lifecycle's event sink, facet transport, and host invocation boundary
84
+ at its composition root — there is no Agent-specific Scheduler adapter. Scheduler contributes its next wake time to the same Lifecycle alarm
85
+ selection as Agent keep-alive, fibers, sub-agent work, and deferred
86
+ destruction.
87
+
88
+ Import `Scheduler` and runtime schedule types from the dependency-light entry
89
+ point:
90
+
91
+ ```typescript
92
+ import {
93
+ Scheduler,
94
+ type Schedule,
95
+ type ScheduleCriteria
96
+ } from "agents/schedules";
97
+ ```
98
+
99
+ Natural-language parsing helpers use Zod and live under
100
+ `agents/schedules/parser`. The previous `agents/schedule` path remains as a
101
+ deprecated compatibility alias.
102
+
18
103
  ## Quick Start
19
104
 
20
105
  ```typescript
@@ -299,9 +384,9 @@ This is the recommended approach since you cannot forget to dispose the heartbea
299
384
 
300
385
  ### How it works
301
386
 
302
- `keepAlive()` uses an in-memory reference count and the Durable Object alarm system directly. Each call increments the count; the disposer decrements it. While the count is above zero, `_scheduleNextAlarm()` ensures an alarm fires every 30 seconds, which resets the inactivity timer. No schedule rows are created and no observability events are emitted the heartbeat is invisible to `listSchedules()` and the `agents:schedule` diagnostics channel.
387
+ `keepAlive()` uses an in-memory reference count. Each call increments the count; the disposer decrements it. While the count is above zero, Agent contributes a wake time every 30 seconds to Lifecycle. No schedule rows are created and no observability events are emitted, so the heartbeat is invisible to `listSchedules()` and the scheduling diagnostics channel.
303
388
 
304
- The heartbeat does not conflict with your own schedules the alarm system multiplexes all schedules and the keepAlive heartbeat through a single alarm slot.
389
+ The heartbeat does not conflict with scheduled work. Lifecycle selects one physical alarm from both contributions.
305
390
 
306
391
  Inside sub-agents, `keepAlive()` delegates that heartbeat ref to the top-level parent because facets do not have independent alarm slots. `keepAliveWhile()` works the same way because it calls `keepAlive()` and automatically disposes the delegated ref when the scoped work completes.
307
392
 
@@ -642,14 +727,14 @@ class TimezoneAgent extends Agent {
642
727
 
643
728
  ## AI-Assisted Scheduling
644
729
 
645
- The SDK includes utilities for parsing natural language scheduling requests with AI.
730
+ The SDK includes utilities for parsing natural language scheduling requests with AI. Import them from `agents/schedules/parser`.
646
731
 
647
732
  ### getSchedulePrompt()
648
733
 
649
734
  Returns a system prompt for parsing natural language into scheduling parameters:
650
735
 
651
736
  ```typescript
652
- import { getSchedulePrompt, scheduleSchema } from "agents";
737
+ import { getSchedulePrompt, scheduleSchema } from "agents/schedules/parser";
653
738
  import { generateObject } from "ai";
654
739
  import { openai } from "@ai-sdk/openai";
655
740
 
@@ -703,7 +788,7 @@ class SmartScheduler extends Agent {
703
788
  A Zod schema for validating parsed scheduling data:
704
789
 
705
790
  ```typescript
706
- import { scheduleSchema } from "agents";
791
+ import { scheduleSchema } from "agents/schedules/parser";
707
792
 
708
793
  // The schema uses a discriminated union on `when.type`:
709
794
  // {
@@ -751,7 +836,81 @@ When using this schema with OpenAI models via the AI SDK, you must pass `provide
751
836
 
752
837
  ## API Reference
753
838
 
754
- ### schedule()
839
+ Two surfaces share these semantics: the experimental `Scheduler` primitive
840
+ (`agents/schedules`, methods on the scheduler instance) and the stable `Agent`
841
+ methods (on the Agent class, delegating to `this.scheduler`).
842
+
843
+ ### Scheduler primitive
844
+
845
+ #### `new Scheduler(options?)`
846
+
847
+ ```typescript
848
+ new Scheduler({
849
+ callbacks?,
850
+ retry?,
851
+ hungScheduleTimeoutSeconds?,
852
+ onError?
853
+ });
854
+ ```
855
+
856
+ - `callbacks` registers scheduled callbacks by name. `set()` and `every()`
857
+ type both the name and the payload against this map, and dispatch runs the
858
+ registered function — the typed scheduling surface and the runtime dispatch
859
+ target are the same object.
860
+ - Lifecycle supplies storage, readiness, startup state, alarm coordination,
861
+ the host invocation boundary, events, and routing.
862
+ - `retry` supplies callback retry defaults. The defaults are three attempts,
863
+ 100 ms base delay, and 3,000 ms maximum delay.
864
+ - `hungScheduleTimeoutSeconds` defaults to 30 seconds.
865
+ - `onError` observes terminal callback failures outside ambient host context.
866
+
867
+ Install the constructed object with `Lifecycle.use()` before use. Scheduler
868
+ starts Lifecycle automatically when its asynchronous API is entered. It
869
+ publishes `schedule:*` events through Lifecycle's best-effort event bus. A plain
870
+ Lifecycle Object writes them to the existing `agents:schedule` diagnostics
871
+ channel; `Agent` sends them through its existing observability implementation.
872
+
873
+ #### set()
874
+
875
+ ```typescript
876
+ async set(when, callback, payload?, options?): Promise<Schedule<Payload>>
877
+ ```
878
+
879
+ Create a one-shot or cron schedule. `when`, `options`, idempotency, and return
880
+ value follow [`schedule()`](#schedule) below. `callback` must be a name
881
+ registered in the constructor's `callbacks` map, and `payload` is typed
882
+ against that callback's first parameter.
883
+
884
+ #### every()
885
+
886
+ ```typescript
887
+ async every(intervalSeconds, callback, payload?, options?): Promise<Schedule<Payload>>
888
+ ```
889
+
890
+ Create a fixed-interval schedule. Semantics follow
891
+ [`scheduleEvery()`](#scheduleevery) below, and `every()` also accepts
892
+ `options.idempotent` to opt out of interval dedup.
893
+
894
+ #### get() / list() / cancel()
895
+
896
+ ```typescript
897
+ async get(id): Promise<Schedule<unknown> | undefined>
898
+ async list(criteria?): Promise<Schedule<unknown>[]>
899
+ async cancel(id): Promise<boolean>
900
+ ```
901
+
902
+ Read and cancel schedules — the primitive's equivalents of
903
+ [`getScheduleById()`](#getschedulebyid), [`listSchedules()`](#listschedules),
904
+ and [`cancelSchedule()`](#cancelschedule) below. (Internal synchronous
905
+ variants on the Scheduler back Agent's deprecated `getSchedule()` and
906
+ `getSchedules()`; they are not part of the primitive's contract.)
907
+
908
+ ### Agent methods
909
+
910
+ Methods on the `Agent` class, delegating to `this.scheduler`. Callback names
911
+ resolve to methods on the Agent.
912
+
913
+ #### schedule()
755
914
 
756
915
  ```typescript
757
916
  async schedule<T = string>(
@@ -789,7 +948,7 @@ class MyAgent extends Agent {
789
948
  }
790
949
  ```
791
950
 
792
- ### scheduleEvery()
951
+ #### scheduleEvery()
793
952
 
794
953
  ```typescript
795
954
  async scheduleEvery<T = string>(
@@ -800,7 +959,8 @@ async scheduleEvery<T = string>(
800
959
  ): Promise<Schedule<T>>
801
960
  ```
802
961
 
803
- Schedule a task to run repeatedly at a fixed interval.
962
+ Schedule a task to run repeatedly at a fixed interval. Idempotent by design
963
+ (the primitive's `every()` accepts `options.idempotent` to opt out).
804
964
 
805
965
  **Parameters:**
806
966
 
@@ -819,7 +979,7 @@ Schedule a task to run repeatedly at a fixed interval.
819
979
  - If callback throws an error, the interval continues
820
980
  - Cancel with `cancelSchedule(id)` to stop the entire interval
821
981
 
822
- ### getScheduleById()
982
+ #### getScheduleById()
823
983
 
824
984
  ```typescript
825
985
  async getScheduleById(id: string): Promise<Schedule<unknown> | undefined>
@@ -827,7 +987,7 @@ async getScheduleById(id: string): Promise<Schedule<unknown> | undefined>
827
987
 
828
988
  Get a scheduled task by ID. This method works in both top-level agents and sub-agents.
829
989
 
830
- ### listSchedules()
990
+ #### listSchedules()
831
991
 
832
992
  ```typescript
833
993
  async listSchedules(criteria?: {
@@ -839,7 +999,7 @@ async listSchedules(criteria?: {
839
999
 
840
1000
  Get scheduled tasks matching the criteria. This method works in both top-level agents and sub-agents.
841
1001
 
842
- ### getSchedule()
1002
+ #### getSchedule()
843
1003
 
844
1004
  ```typescript
845
1005
  getSchedule<T = string>(id: string): Schedule<T> | undefined
@@ -847,7 +1007,7 @@ getSchedule<T = string>(id: string): Schedule<T> | undefined
847
1007
 
848
1008
  Deprecated. Get a scheduled task by ID synchronously. This method only works in top-level agents; use `await this.getScheduleById(id)` instead.
849
1009
 
850
- ### getSchedules()
1010
+ #### getSchedules()
851
1011
 
852
1012
  ```typescript
853
1013
  getSchedules<T = string>(criteria?: {
@@ -859,7 +1019,7 @@ getSchedules<T = string>(criteria?: {
859
1019
 
860
1020
  Deprecated. Get scheduled tasks matching the criteria synchronously. This method only works in top-level agents; use `await this.listSchedules(criteria)` instead.
861
1021
 
862
- ### cancelSchedule()
1022
+ #### cancelSchedule()
863
1023
 
864
1024
  ```typescript
865
1025
  async cancelSchedule(id: string): Promise<boolean>
@@ -867,7 +1027,7 @@ async cancelSchedule(id: string): Promise<boolean>
867
1027
 
868
1028
  Cancel a scheduled task. Returns `true` if cancelled, `false` if not found.
869
1029
 
870
- ### keepAlive()
1030
+ #### keepAlive()
871
1031
 
872
1032
  ```typescript
873
1033
  async keepAlive(): Promise<() => void>
@@ -877,7 +1037,7 @@ Create an alarm-backed heartbeat that prevents the Durable Object from being evi
877
1037
 
878
1038
  See [Keeping the Agent Alive](#keeping-the-agent-alive) for usage details.
879
1039
 
880
- ### keepAliveWhile()
1040
+ #### keepAliveWhile()
881
1041
 
882
1042
  ```typescript
883
1043
  async keepAliveWhile<T>(fn: () => Promise<T>): Promise<T>