agents 0.27.0 → 0.28.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 (193) hide show
  1. package/dist/abortable-C1SpfVoi.js +21 -0
  2. package/dist/abortable-C1SpfVoi.js.map +1 -0
  3. package/dist/{agent-routing-Bhdbvf7s.d.ts → agent-routing-QDcchcQP.d.ts} +10 -15
  4. package/dist/agent-routing.d.ts +1 -1
  5. package/dist/agent-tool-types.d.ts +1 -1
  6. package/dist/{agent-tools-bJZwBMfq.d.ts → agent-tools-DHL2XI7u.d.ts} +2 -2
  7. package/dist/agent-tools.d.ts +1 -1
  8. package/dist/ai-ahruGw7e.d.ts +239 -0
  9. package/dist/{ai-sdk-OoGkFy3s.js → ai-sdk-Bdsf6mSA.js} +2 -2
  10. package/dist/{ai-sdk-OoGkFy3s.js.map → ai-sdk-Bdsf6mSA.js.map} +1 -1
  11. package/dist/browser/ai.d.ts +8 -227
  12. package/dist/browser/ai.js +259 -1
  13. package/dist/browser/ai.js.map +1 -0
  14. package/dist/browser/index.d.ts +56 -46
  15. package/dist/browser/index.js +4 -361
  16. package/dist/browser/{ai-sdk.d.ts → tools/ai-sdk.d.ts} +7 -7
  17. package/dist/browser/{ai-sdk.js → tools/ai-sdk.js} +5 -4
  18. package/dist/browser/tools/ai-sdk.js.map +1 -0
  19. package/dist/browser/tools/pi.d.ts +111 -0
  20. package/dist/browser/tools/pi.js +119 -0
  21. package/dist/browser/tools/pi.js.map +1 -0
  22. package/dist/browser/{tanstack-ai.d.ts → tools/tanstack-ai.d.ts} +39 -37
  23. package/dist/browser/{tanstack-ai.js → tools/tanstack-ai.js} +73 -59
  24. package/dist/browser/tools/tanstack-ai.js.map +1 -0
  25. package/dist/browser-CEdNlsaD.js +481 -0
  26. package/dist/browser-CEdNlsaD.js.map +1 -0
  27. package/dist/{browser-tmkcAAI3.d.ts → browser-D30VlpcA.d.ts} +63 -37
  28. package/dist/{browser-tool-CBqs6hqa.js → browser-tool-4iomavs_.js} +145 -49
  29. package/dist/browser-tool-4iomavs_.js.map +1 -0
  30. package/dist/{browser-tool-Bn-nzJQI.d.ts → browser-tool-vtrPOV8e.d.ts} +24 -5
  31. package/dist/{capability-B4WbF81e.js → capability-DBAKc6UZ.js} +1 -3
  32. package/dist/capability-DBAKc6UZ.js.map +1 -0
  33. package/dist/{capability-runner-O5VbQzqg.d.ts → capability-runner-BiVUtjqC.d.ts} +1 -7
  34. package/dist/chat/index.d.ts +4 -4
  35. package/dist/chat/index.js +1 -1
  36. package/dist/chat-sdk/index.d.ts +1 -1
  37. package/dist/chat-sdk/index.js +1 -1
  38. package/dist/{client-DS3Dz9l0.d.ts → client-BGIQKJhX.d.ts} +2 -2
  39. package/dist/{client-1tEQ1Oqz.js → client-BnYbNT3z.js} +3 -4
  40. package/dist/client-BnYbNT3z.js.map +1 -0
  41. package/dist/client.d.ts +1 -1
  42. package/dist/{connector-58XDGvc3.js → connector-CtZylXdk.js} +3 -603
  43. package/dist/connector-CtZylXdk.js.map +1 -0
  44. package/dist/{connector-Cl-VqPmA.d.ts → connector-DEXzVoaY.d.ts} +9 -374
  45. package/dist/{conversations-DH0ut_8n.d.ts → conversations-FjHDRZyD.d.ts} +4 -4
  46. package/dist/{current-agent-C07C5o5Y.d.ts → current-agent-CKgT5d1O.d.ts} +3 -5
  47. package/dist/current-agent-DhoDkSnH.js.map +1 -1
  48. package/dist/{durable-object-lifecycle-DRqp475_.d.ts → durable-object-lifecycle-DOI_bzCH.d.ts} +2 -2
  49. package/dist/experimental/channels/index.d.ts +2 -2
  50. package/dist/experimental/channels/index.js +2 -2
  51. package/dist/experimental/channels/web/index.d.ts +2 -2
  52. package/dist/experimental/channels/web/index.js +1 -1
  53. package/dist/{gateway-BDC07gYO.d.ts → gateway-BFO6vRDn.d.ts} +2 -2
  54. package/dist/harness/ai-sdk/index.d.ts +2 -2
  55. package/dist/harness/ai-sdk/index.js +1 -1
  56. package/dist/harness/container/index.d.ts +2 -2
  57. package/dist/harness/container/index.js +3 -3
  58. package/dist/harness/opencode/index.d.ts +1 -1
  59. package/dist/harness/opencode/index.js +2 -2
  60. package/dist/harness/pi/index.d.ts +2 -2
  61. package/dist/harness/pi/index.js +3 -3
  62. package/dist/harness/store/index.d.ts +1 -1
  63. package/dist/harness/store/index.js +1 -1
  64. package/dist/harness/think/index.d.ts +2 -2
  65. package/dist/harness/think/index.js +363 -171
  66. package/dist/harness/think/index.js.map +1 -1
  67. package/dist/{index-D0A5tbWu.d.ts → index-BEL3ZV-s.d.ts} +2 -6
  68. package/dist/{index-Ib_4dwOo.d.ts → index-CBt8oRJt.d.ts} +3 -5
  69. package/dist/index.d.ts +4 -4
  70. package/dist/index.js +2 -2
  71. package/dist/input-schema-BQykTEp_.js +20 -0
  72. package/dist/input-schema-BQykTEp_.js.map +1 -0
  73. package/dist/{input-schema-DNrpTu49.js → input-schema-D2QAtHZH.js} +3 -3
  74. package/dist/input-schema-D2QAtHZH.js.map +1 -0
  75. package/dist/internal_context.d.ts +1 -1
  76. package/dist/lifecycle/index.d.ts +3 -3
  77. package/dist/lifecycle/index.js +2 -2
  78. package/dist/{lifecycle-CFu6OV03.js → lifecycle-DGorpAmA.js} +2 -4
  79. package/dist/lifecycle-DGorpAmA.js.map +1 -0
  80. package/dist/mcp/client/index.d.ts +1 -1
  81. package/dist/mcp/client/index.js +1 -1
  82. package/dist/mcp/index.d.ts +1 -1
  83. package/dist/mcp/index.js +2 -2
  84. package/dist/models/ai-sdk/index.js +1 -1
  85. package/dist/models/opencode/index.js +1 -1
  86. package/dist/models/pi-ai/index.js +1 -1
  87. package/dist/{options-pwTNq9z6.d.ts → options-CiIsJHXR.d.ts} +2 -6
  88. package/dist/queue/index.d.ts +1 -1
  89. package/dist/queue/index.js +1 -1
  90. package/dist/{queue-BB-zfIYF.js → queue-CcIjVYTE.js} +2 -2
  91. package/dist/{queue-BB-zfIYF.js.map → queue-CcIjVYTE.js.map} +1 -1
  92. package/dist/react.d.ts +2 -2
  93. package/dist/routing/index.d.ts +2 -2
  94. package/dist/routing/index.js +1 -1
  95. package/dist/{scheduler-CrFCnv_J.d.ts → scheduler-BCLESHSC.d.ts} +2 -12
  96. package/dist/{scheduler-DZ3xQfeK.js → scheduler-FIHv2-kJ.js} +2 -4
  97. package/dist/scheduler-FIHv2-kJ.js.map +1 -0
  98. package/dist/schedules/index.d.ts +1 -1
  99. package/dist/schedules/index.js +1 -1
  100. package/dist/serializable.d.ts +1 -1
  101. package/dist/session-store-Do4xkDcf.js +606 -0
  102. package/dist/session-store-Do4xkDcf.js.map +1 -0
  103. package/dist/session-store-DoPY12xM.d.ts +383 -0
  104. package/dist/sessions/index.d.ts +1 -1
  105. package/dist/sessions/index.js +1 -1
  106. package/dist/{sessions-B9pNuxdT.js → sessions-_vN5BEZX.js} +2 -2
  107. package/dist/{sessions-B9pNuxdT.js.map → sessions-_vN5BEZX.js.map} +1 -1
  108. package/dist/{src-DgCbZtIR.js → src-CQxJFv4l.js} +8 -8
  109. package/dist/src-CQxJFv4l.js.map +1 -0
  110. package/dist/state/index.d.ts +1 -1
  111. package/dist/state/index.js +1 -3
  112. package/dist/state/index.js.map +1 -1
  113. package/dist/{store-DLeqKuj3.d.ts → store-BKM4Xm9M.d.ts} +23 -1
  114. package/dist/{store-vgcPYo8K.js → store-CslVzfN7.js} +28 -2
  115. package/dist/store-CslVzfN7.js.map +1 -0
  116. package/dist/streams/index.d.ts +1 -1
  117. package/dist/streams/index.js +1 -1
  118. package/dist/{streams-CITClVer.js → streams-lsHMJhRW.js} +2 -2
  119. package/dist/{streams-CITClVer.js.map → streams-lsHMJhRW.js.map} +1 -1
  120. package/dist/{streams-fbG61qM8.d.ts → streams-vrCtbUIn.d.ts} +2 -2
  121. package/dist/sub-routing.d.ts +1 -1
  122. package/dist/tasks/index.d.ts +1 -1
  123. package/dist/tasks/index.js +1 -1
  124. package/dist/{tasks-CkTZNaV1.js → tasks-BN_5AWqI.js} +2 -2
  125. package/dist/{tasks-CkTZNaV1.js.map → tasks-BN_5AWqI.js.map} +1 -1
  126. package/dist/{tasks-PtlB4Uyn.d.ts → tasks-E89KdwkS.d.ts} +2 -2
  127. package/dist/{tool-BPJEbwaX.d.ts → tool-CTp72Jb_.d.ts} +4 -4
  128. package/dist/tool-CxKsvxjm.js +1126 -0
  129. package/dist/tool-CxKsvxjm.js.map +1 -0
  130. package/dist/tool-DoVberpN.d.ts +301 -0
  131. package/dist/{tool-3PA8xjB_.js → tool-cQRkwSZZ.js} +6 -17
  132. package/dist/tool-cQRkwSZZ.js.map +1 -0
  133. package/dist/tool-helpers-O5BDVEAY.js +58 -0
  134. package/dist/tool-helpers-O5BDVEAY.js.map +1 -0
  135. package/dist/voice/index.d.ts +2 -2
  136. package/dist/web/fetch/index.d.ts +89 -0
  137. package/dist/web/fetch/index.js +20 -0
  138. package/dist/web/fetch/index.js.map +1 -0
  139. package/dist/web/fetch/tools/ai-sdk.d.ts +80 -0
  140. package/dist/web/fetch/tools/ai-sdk.js +47 -0
  141. package/dist/web/fetch/tools/ai-sdk.js.map +1 -0
  142. package/dist/web/fetch/tools/pi.d.ts +84 -0
  143. package/dist/web/fetch/tools/pi.js +88 -0
  144. package/dist/web/fetch/tools/pi.js.map +1 -0
  145. package/dist/web/fetch/tools/tanstack-ai.d.ts +78 -0
  146. package/dist/web/fetch/tools/tanstack-ai.js +45 -0
  147. package/dist/web/fetch/tools/tanstack-ai.js.map +1 -0
  148. package/dist/{websearch → web/search}/index.d.ts +1 -1
  149. package/dist/{websearch → web/search}/index.js +1 -1
  150. package/dist/{websearch → web/search}/tools/ai-sdk.d.ts +2 -2
  151. package/dist/{websearch → web/search}/tools/ai-sdk.js +3 -3
  152. package/dist/web/search/tools/ai-sdk.js.map +1 -0
  153. package/dist/{websearch → web/search}/tools/pi.d.ts +3 -3
  154. package/dist/{websearch → web/search}/tools/pi.js +2 -2
  155. package/dist/web/search/tools/pi.js.map +1 -0
  156. package/dist/{websearch → web/search}/tools/tanstack-ai.d.ts +2 -2
  157. package/dist/{websearch → web/search}/tools/tanstack-ai.js +3 -3
  158. package/dist/web/search/tools/tanstack-ai.js.map +1 -0
  159. package/dist/websockets/index.d.ts +2 -2
  160. package/dist/websockets/index.js +1 -1
  161. package/dist/{websockets-CmENGb-Z.js → websockets-C3zy8YR0.js} +3 -5
  162. package/dist/websockets-C3zy8YR0.js.map +1 -0
  163. package/dist/workflow-types.d.ts +1 -1
  164. package/dist/workflows.d.ts +1 -1
  165. package/dist/workflows.js +1 -1
  166. package/docs/browse-the-web.md +26 -1
  167. package/docs/fetch-the-web.md +276 -0
  168. package/docs/harnesses/pi.md +25 -0
  169. package/docs/index.md +99 -76
  170. package/docs/lifecycle.md +2 -3
  171. package/docs/queue.md +2 -3
  172. package/docs/scheduling.md +2 -6
  173. package/docs/search-the-web.md +2 -0
  174. package/package.json +44 -19
  175. package/dist/ai-fm7xx1BK.js +0 -312
  176. package/dist/ai-fm7xx1BK.js.map +0 -1
  177. package/dist/browser/ai-sdk.js.map +0 -1
  178. package/dist/browser/index.js.map +0 -1
  179. package/dist/browser/tanstack-ai.js.map +0 -1
  180. package/dist/browser-tool-CBqs6hqa.js.map +0 -1
  181. package/dist/capability-B4WbF81e.js.map +0 -1
  182. package/dist/client-1tEQ1Oqz.js.map +0 -1
  183. package/dist/connector-58XDGvc3.js.map +0 -1
  184. package/dist/input-schema-DNrpTu49.js.map +0 -1
  185. package/dist/lifecycle-CFu6OV03.js.map +0 -1
  186. package/dist/scheduler-DZ3xQfeK.js.map +0 -1
  187. package/dist/src-DgCbZtIR.js.map +0 -1
  188. package/dist/store-vgcPYo8K.js.map +0 -1
  189. package/dist/tool-3PA8xjB_.js.map +0 -1
  190. package/dist/websearch/tools/ai-sdk.js.map +0 -1
  191. package/dist/websearch/tools/pi.js.map +0 -1
  192. package/dist/websearch/tools/tanstack-ai.js.map +0 -1
  193. package/dist/websockets-CmENGb-Z.js.map +0 -1
@@ -0,0 +1,276 @@
1
+ # Fetch the Web (Beta)
2
+
3
+ `agents/webfetch` gives a model a `web_fetch` tool that reads one URL and returns the most useful text form of it: Markdown for HTML pages, PDFs, and Office documents, pretty-printed JSON, or plain text as served. Long content is windowed, so the model reads a page in parts. The same tool is available for the pi harness, the AI SDK, and TanStack AI.
4
+
5
+ The host decides which URLs can be fetched, how much is read, and how long a fetch may take. The model only chooses the URL, where to continue reading, and whether to get the body converted or raw.
6
+
7
+ > **Beta** — this feature may have breaking changes in future releases.
8
+
9
+ ## Quick Start
10
+
11
+ You need an `AI` binding in `wrangler.jsonc`. The tool fetches pages with the Worker's own `fetch` and uses `env.AI.toMarkdown()` from [Workers AI Markdown Conversion](https://developers.cloudflare.com/workers-ai/features/markdown-conversion/) to turn HTML and documents into Markdown. There is no API token to set up.
12
+
13
+ ```jsonc
14
+ {
15
+ // No local implementation: under `wrangler dev` or `vite dev`, conversions
16
+ // run against the account you are logged in to.
17
+ "ai": { "binding": "AI", "remote": true }
18
+ }
19
+ ```
20
+
21
+ Pages that are already Markdown, JSON, or plain text never reach the binding. The tool asks for `text/markdown` first, so sites with [Markdown for Agents](https://developers.cloudflare.com/fundamentals/reference/markdown-for-agents/) enabled answer in Markdown and skip conversion.
22
+
23
+ Then add the tool to your harness. Every adapter takes the same options; the TanStack AI adapter also takes `name`, because TanStack AI tools carry their name in the definition.
24
+
25
+ Pi harness:
26
+
27
+ ```ts
28
+ import { webFetchTool } from "agents/webfetch/pi";
29
+
30
+ this.registry.install({
31
+ name: "tools",
32
+ tools: [webFetchTool({ binding: this.env.AI })]
33
+ });
34
+ ```
35
+
36
+ AI SDK:
37
+
38
+ ```ts
39
+ import { webFetchTool } from "agents/webfetch/ai-sdk";
40
+
41
+ const result = streamText({
42
+ model,
43
+ tools: { web_fetch: webFetchTool({ binding: this.env.AI }) },
44
+ messages
45
+ });
46
+ ```
47
+
48
+ If you convert UI messages yourself, pass the same tools to `convertToModelMessages(messages, { tools })`. Without them, the AI SDK sends earlier fetches back to the model as the JSON output: the same window, but without the header line or the `<untrusted_web_content>` wrapper. `AiSdkHarness` does this for you.
49
+
50
+ TanStack AI:
51
+
52
+ ```ts
53
+ import { webFetchTool } from "agents/webfetch/tanstack-ai";
54
+
55
+ const tools = [webFetchTool({ binding: this.env.AI })];
56
+ ```
57
+
58
+ ## Options
59
+
60
+ An invalid value throws a `RangeError` when the tool is created.
61
+
62
+ | Option | Default | Notes |
63
+ | ------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
64
+ | `binding` | — | The `AI` binding, for converting HTML and documents. Or pass `source` instead (see [Other sources](#other-sources)). |
65
+ | `pageChars` | `20000` | Characters the model reads per call. It continues with `offset`. |
66
+ | `maxBytes` | `5000000` | Stop reading a response body past this many bytes, as a `web_fetch_too_large` failure. Bodies are streamed, so a large one is never buffered. |
67
+ | `timeoutMs` | `30000` | Give up on a fetch after this long, redirects and conversion included, as a retryable `web_fetch_timeout` failure. |
68
+ | `maxRedirects` | `5` | Redirects to follow, from 0 to 20. |
69
+ | `allowedHosts` | any public host | Host patterns URLs must match: `example.com`, `*.example.com` (subdomains, not the apex), or `*`. |
70
+ | `blockedHosts` | — | Host patterns never fetched, in the same syntax. Checked first. |
71
+ | `allowPrivateHosts` | `false` | Allow loopback, private, and local-only hosts. For local development against `localhost`; see [URL policy](#url-policy). |
72
+ | `userAgent` | `Cloudflare-Agents (…)` | The `User-Agent` header. |
73
+ | `description` | built-in description | Replaces the tool description the model sees. |
74
+
75
+ With `source`, only `pageChars`, `timeoutMs`, and `description` apply: the URL policy and limits belong to the source.
76
+
77
+ ## Model Interface
78
+
79
+ The model sends:
80
+
81
+ | Field | Notes |
82
+ | -------- | -------------------------------------------------------------------------------------------------------------------- |
83
+ | `url` | The http(s) URL to read, at most 2,000 characters. |
84
+ | `offset` | Character offset to continue a long page from, as the previous result says. Default `0`. |
85
+ | `format` | `"auto"` (default) converts to the most readable form; `"raw"` returns a textual body exactly as the server sent it. |
86
+
87
+ The model gets text: a header line, then one window of the content inside `<untrusted_web_content>`:
88
+
89
+ ```
90
+ web_fetch: https://developers.cloudflare.com/agents/ · 200 · text/html → markdown · chars 0–19873 of 81200 · continue with offset=19873
91
+ <untrusted_web_content>
92
+ # Agents
93
+ …
94
+ </untrusted_web_content>
95
+ ```
96
+
97
+ The header line names the final URL after redirects, the status, the content type and how it was read, and where the window sits. A window ends on a line break when one is close to its end, so `offset` values are rarely round numbers; the model continues from the offset the header gives. When the window reaches the end, the header has no `continue with`. An error status reads `404 Not Found` rather than `404`, so the model cannot miss it.
98
+
99
+ The wrapper and the tool description tell the model to treat the content as data, not instructions. A page cannot close the wrapper early: a closing tag inside the content is escaped.
100
+
101
+ `renderWebFetchPage(output)` from `agents/webfetch` is the renderer, if you want the same text elsewhere. It formats a stored output as is, so re-rendering an old result gives the text the model read.
102
+
103
+ ## Host Output
104
+
105
+ The host gets `WebFetchToolOutput`: the window the model read and the page's metadata. The rest of the content is not stored, so a long page does not bloat the conversation history:
106
+
107
+ | Field | Notes |
108
+ | ----------------------- | -------------------------------------------------------------------------------------------------------------- |
109
+ | `url`, `finalUrl` | The URL as requested, and after redirects. |
110
+ | `status` | The final response's HTTP status. |
111
+ | `contentType` | The media type, lowercased, without parameters. |
112
+ | `via` | How the content was produced; see [Conversion](#conversion). |
113
+ | `content`, `totalChars` | The window of converted content the model read, and the whole page's length. |
114
+ | `offset`, `nextOffset` | The window the model read, and where the next one starts (`null` at the end). |
115
+ | `title` | The page title, from `<title>` for HTML or the first `# ` heading for Markdown, when there is one. |
116
+ | `headers` | `content-type`, `content-length`, `last-modified`, `etag`, `x-markdown-tokens`, and `cf-mitigated`, when sent. |
117
+ | `bytes` | Body bytes read. |
118
+ | `redirects` | The URLs that redirected, in order; `finalUrl` is not included. |
119
+
120
+ Where it arrives depends on the harness:
121
+
122
+ - **Pi**: in the tool result's `details`, as `{ ok: true, output }`. The tool is `replay: "safe"`: if a fetch is interrupted mid-call, for example by an eviction, pi runs it again when the session recovers. Completed results are stored and not fetched again.
123
+ - **AI SDK**: as the return value of `execute`, so `onFinish`, UI message parts, and logs see it. `toModelOutput` renders it for the model.
124
+ - **TanStack AI**: the server tool returns the rendered window, so the host gets the same text the model does.
125
+
126
+ Each call fetches the page again; there is no cache between calls, so reading a long page in five windows fetches it five times.
127
+
128
+ ## Conversion
129
+
130
+ With `format: "auto"`, the content type decides what the model reads:
131
+
132
+ | Content type | Handling | `via` |
133
+ | -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | --------------------- |
134
+ | `text/markdown` | Passed through | `markdown-negotiated` |
135
+ | `text/html`, `application/xhtml+xml` | Converted with `env.AI.toMarkdown()` | `converted` |
136
+ | `application/pdf`, Word (`.docx`), Excel (`.xlsx`, `.xlsm`, `.xlsb`, `.xls`), OpenDocument (`.odt`, `.ods`), Numbers | Converted with `env.AI.toMarkdown()` | `converted` |
137
+ | `application/json`, `*+json` | Pretty-printed; invalid JSON is passed through as text | `json` |
138
+ | Other `text/*`, `application/xml`, `*+xml`, JavaScript, YAML, TOML, NDJSON, SQL, GraphQL, shell | Passed through | `text` |
139
+ | Images, audio, video, fonts, archives, binary `application/octet-stream`, anything else | Rejected as `web_fetch_unsupported_content_type` | — |
140
+
141
+ Bodies are decoded with the charset from a byte-order mark, the `Content-Type` header, or an HTML `<meta charset>`, falling back to UTF-8. A response without a `Content-Type`, or sent as `application/octet-stream` (common for `.md`, `.yaml`, and source files), is sniffed as HTML, PDF, binary, or plain text.
142
+
143
+ With `format: "raw"`, textual bodies, HTML included, are returned exactly as served with `via: "raw"`. Documents and binary bodies are rejected, because their raw bytes are not text.
144
+
145
+ A 4xx or 5xx response with a readable body is returned like any page, with its status in the header line: error pages often explain what went wrong. Only an empty or non-textual error body becomes a `web_fetch_http_error` failure. A bot-management challenge page (sent with `cf-mitigated: challenge`) is reported as `web_fetch_blocked` rather than returned as content, because a plain fetch cannot pass it; the model is told to use a browser tool or another source.
146
+
147
+ ## URL Policy
148
+
149
+ Every URL is checked before it is fetched, and every redirect target is checked again before it is followed. A redirect from `https:` down to `http:` is refused. Requests are GET only, with no cookies, credentials, or host-supplied headers.
150
+
151
+ Always rejected:
152
+
153
+ - Schemes other than `http:` and `https:`.
154
+ - URLs with a username or password.
155
+ - URLs longer than 2,000 characters.
156
+
157
+ Rejected unless `allowPrivateHosts` is `true`:
158
+
159
+ - Loopback, private, CGNAT, link-local (including cloud metadata at `169.254.169.254`), multicast, and reserved addresses, in IPv4 or IPv6, including IPv4-mapped, NAT64, and 6to4 forms. The list is a subset of the IANA special-purpose address registries ([RFC 6890](https://www.rfc-editor.org/rfc/rfc6890)).
160
+ - `localhost` and names ending in `.localhost` or `.internal`.
161
+
162
+ Hosts are checked after the URL parser has canonicalised them, so a private address cannot hide behind an unusual spelling such as `2130706433`, `0x7f.1`, `0177.0.0.1`, or `[::ffff:127.0.0.1]`. Names are not resolved: a public name that points at a private address, such as `127.0.0.1.nip.io`, passes the policy. In production, Workers egress cannot reach private ranges anyway; the policy is for local development, other runtimes, and defence in depth. The fragment is dropped from every URL.
163
+
164
+ `blockedHosts` is checked first, then `allowedHosts`, then the private-host rule. To keep the model on your documentation:
165
+
166
+ ```ts
167
+ webFetchTool({
168
+ binding: this.env.AI,
169
+ allowedHosts: ["developers.cloudflare.com", "*.cloudflare.com"]
170
+ });
171
+ ```
172
+
173
+ No DNS lookup is done: a public name that resolves to a private address is the runtime's to refuse. Deployed Workers cannot reach private networks. Under `wrangler dev` they can, which is why the private-host rule exists, and why `allowPrivateHosts` is the switch for testing against a server on `localhost`:
174
+
175
+ ```ts
176
+ webFetchTool({
177
+ binding: this.env.AI,
178
+ // Local development only: lets the model reach http://localhost:8787.
179
+ allowPrivateHosts: true
180
+ });
181
+ ```
182
+
183
+ ## Failures
184
+
185
+ A failed fetch becomes a `WebFetchError` with `status`, `code`, `retryable`, and, where there is one, `url`, `contentType`, and (for a timeout) `timeoutMs`. Every error has a `code`:
186
+
187
+ | Code | Retryable | When |
188
+ | ------------------------------------ | ---------------------- | ------------------------------------------------------------------------ |
189
+ | `invalid_web_fetch_input` | No | The URL, `offset`, or `format` is malformed, or the URL is too long. |
190
+ | `web_fetch_disallowed_url` | No | The URL's scheme, credentials, or host is not allowed by the URL policy. |
191
+ | `web_fetch_disallowed_redirect` | No | A redirect pointed at a URL the policy does not allow, or https to http. |
192
+ | `web_fetch_too_many_redirects` | No | More than `maxRedirects` redirects, or a redirect loop. |
193
+ | `web_fetch_timeout` | Yes | The fetch took longer than `timeoutMs`. |
194
+ | `web_fetch_too_large` | No | The body is larger than `maxBytes`. |
195
+ | `web_fetch_unsupported_content_type` | No | The body is binary, or a document requested with `format: "raw"`. |
196
+ | `web_fetch_http_error` | 5xx, 408, and 429 only | A 4xx or 5xx response with an empty or non-textual body. |
197
+ | `web_fetch_blocked` | No | The site answered with a bot challenge (`cf-mitigated: challenge`). |
198
+ | `web_fetch_conversion_failed` | Yes | Workers AI could not convert the body to Markdown. |
199
+ | `web_fetch_unavailable` | Yes | The network failed: DNS, TLS, the connection, or reading the body. |
200
+
201
+ The error's `message` is written for you. The model gets different text that tells it what to do next: fix the input, retry once, retry with `format: "raw"` after a failed HTML conversion (a document that fails to convert is not retried), or move on.
202
+
203
+ - **Pi**: the tool returns an error result with the model's text, and `details` is `{ ok: false, message, status, code, retryable, url?, contentType? }`.
204
+ - **AI SDK** and **TanStack AI**: the tool throws a `WebFetchError`, which is how those frameworks report tool errors. Its `message` is the model's text, and its `cause` is the original error with the detail.
205
+
206
+ Every adapter entry point re-exports `WebFetchError`, so `instanceof` checks do not need a second import.
207
+
208
+ If the harness cancels the call, the fetch is aborted and the abort propagates instead of becoming a failed result.
209
+
210
+ ## With Web Search
211
+
212
+ `web_fetch` pairs with [`web_search`](./search-the-web.md): search finds pages, fetch reads them. Each tool's description names the other, so a model with both searches, then fetches the results it wants to read.
213
+
214
+ ```ts
215
+ import { webFetchTool } from "agents/webfetch/ai-sdk";
216
+ import { webSearchTool } from "agents/websearch/ai-sdk";
217
+
218
+ const tools = {
219
+ web_search: webSearchTool({ binding: this.env.AI }),
220
+ web_fetch: webFetchTool({ binding: this.env.AI })
221
+ };
222
+ ```
223
+
224
+ ## What It Is Not
225
+
226
+ - **Not an HTTP client.** There is no POST, no request body, and no custom headers, cookies, or credentials. For calls to your own APIs, write a tool for them.
227
+ - **Not a browser.** Pages are not rendered, so content that only appears after JavaScript runs is missing. For rendered pages, screenshots, or interaction, see [Browse the Web](./browse-the-web.md).
228
+ - **Not a summariser.** The model reads the page itself, a window at a time.
229
+
230
+ ## Other Sources
231
+
232
+ `fetchWeb` from `agents/webfetch` fetches one URL with no model involved: the same pipeline, URL policy, limits, and `timeoutMs`. It returns the whole page as a `WebFetchPage`: the fields of `WebFetchToolOutput` with all of `content`, and no `offset` or `nextOffset`. Use it from scheduled jobs, callables, or tests:
233
+
234
+ ```ts
235
+ import { WebFetchError, fetchWeb } from "agents/webfetch";
236
+
237
+ try {
238
+ const page = await fetchWeb(
239
+ { url: "https://developers.cloudflare.com/agents/" },
240
+ { binding: this.env.AI, maxBytes: 1_000_000 }
241
+ );
242
+ console.log(page.title, page.via, page.totalChars);
243
+ } catch (error) {
244
+ if (error instanceof WebFetchError) console.log(error.code, error.retryable);
245
+ }
246
+ ```
247
+
248
+ To show a page a window at a time without a model, for example in a UI that pages through what the model would read, cut it with `windowWebFetchPage` and render each window with `renderWebFetchPage`. This is exactly what the tool does, and it does not fetch again:
249
+
250
+ ```ts
251
+ import { renderWebFetchPage, windowWebFetchPage } from "agents/webfetch";
252
+
253
+ const first = windowWebFetchPage(page, { offset: 0, pageChars: 20_000 });
254
+ console.log(renderWebFetchPage(first));
255
+ // Next window: windowWebFetchPage(page, { offset: first.nextOffset, … }),
256
+ // until nextOffset is null.
257
+ ```
258
+
259
+ `createDirectWebFetch` from `agents/webfetch` returns a `WebFetchSource`: an object whose `fetch({ url, format? }, { signal? })` returns a `WebFetchPage`. A source validates `url` and `format` itself (the tool checks only `offset`), throwing `invalid_web_fetch_input` for a malformed request. Any `WebFetchSource` can be passed to a tool as `source` instead of `binding`. That is how tests substitute a fake, and how you wrap a source with caching or logging:
260
+
261
+ ```ts
262
+ import { createDirectWebFetch } from "agents/webfetch";
263
+ import { webFetchTool } from "agents/webfetch/ai-sdk";
264
+
265
+ const direct = createDirectWebFetch({ binding: this.env.AI });
266
+
267
+ webFetchTool({
268
+ source: {
269
+ async fetch(request, options) {
270
+ const page = await direct.fetch(request, options);
271
+ console.log(request.url, page.status, page.bytes);
272
+ return page;
273
+ }
274
+ }
275
+ });
276
+ ```
@@ -125,6 +125,31 @@ harness: async ({ storage, context }) => {
125
125
 
126
126
  The existing `@cloudflare/think` skills work out-of-the-box.
127
127
 
128
+ ### Browser
129
+
130
+ `browserTool` from `agents/browser/pi` gives the model a persistent browser it drives with the Chrome DevTools Protocol. Put a `Browser` on the object's Lifecycle and install the tool in an extension:
131
+
132
+ ```ts
133
+ import { Browser, browserRun } from "agents/browser";
134
+ import { browserTool } from "agents/browser/pi";
135
+
136
+ readonly browser = new Browser({ provider: browserRun(this.env.BROWSER) });
137
+
138
+ harness: async ({ storage, context }) => {
139
+ this.registry.install({
140
+ name: "browser",
141
+ tools: [
142
+ browserTool({ ctx: this.ctx, browser: this.browser, loader: this.env.LOADER })
143
+ ]
144
+ });
145
+ return Harness.open(storage, { models, registry: this.registry }, context);
146
+ };
147
+
148
+ readonly lifecycle = Lifecycle.install(this).use(this.browser).use(this.harness);
149
+ ```
150
+
151
+ It needs a `browser` binding, a `LOADER` Worker Loader binding, and `export { CodemodeRuntime } from "@cloudflare/codemode"` from the Worker entry. Screenshots come back as images, and each conversation works in its own tab. Refer to [Persistent browser](../browse-the-web.md#persistent-browser) for how it behaves.
152
+
128
153
  ## Work with sessions
129
154
 
130
155
  A session is a Pi conversation. Each has its own transcript, inbox, model and run, and they can run at the same time.
package/docs/index.md CHANGED
@@ -1,6 +1,25 @@
1
1
  # Agents Documentation
2
2
 
3
- Build stateful AI agents on Cloudflare Workers. Every agent is a Durable Object — an addressable, hibernatable actor with its own SQLite database, WebSockets, and scheduling — so you can afford one durable agent per user, account, task, or conversation, with near-zero cost while idle.
3
+ Build stateful AI agents on Cloudflare Workers. An agent is a Durable Object: an addressable actor with its own SQLite database, alarms, and WebSockets. You can run one durable agent per user, account, task, or conversation, and pay close to nothing while it is idle.
4
+
5
+ Every agent starts the same way. Extend `DurableObject`, install a [Lifecycle](./lifecycle.md), and add the capabilities you need:
6
+
7
+ ```ts
8
+ import { DurableObject } from "cloudflare:workers";
9
+ import { Lifecycle } from "agents/lifecycle";
10
+ import { MCPClientManager } from "agents/mcp/client";
11
+ import { Scheduler } from "agents/schedules";
12
+
13
+ export class Assistant extends DurableObject<Env> {
14
+ readonly mcp = new MCPClientManager("assistant", "1.0.0");
15
+ readonly scheduler = new Scheduler({ callbacks: {} });
16
+ readonly lifecycle = Lifecycle.install(this)
17
+ .use(this.mcp)
18
+ .use(this.scheduler);
19
+ }
20
+ ```
21
+
22
+ The Lifecycle runs startup, routes requests to capabilities, and owns the object's durable job queue and its single alarm. Capabilities are independent pieces that share it.
4
23
 
5
24
  ## Related package documentation
6
25
 
@@ -9,49 +28,76 @@ Build stateful AI agents on Cloudflare Workers. Every agent is a Durable Object
9
28
 
10
29
  ## Choose your path
11
30
 
12
- Pick the base class that matches what you are building. They share the same Durable Object foundation, so you can start small and move up without re-platforming.
31
+ | You are building... | Use |
32
+ | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
33
+ | An agent that runs a model loop with tools and recovery | [Pi harness](./harnesses/pi.md) on a Durable Object |
34
+ | Your own agent loop | [Sessions](./sessions.md), [Streams](./streams.md), and [Tasks](./tasks.md) on a Durable Object |
35
+ | A chat or reasoning agent with every default built in | [`Think`](https://github.com/cloudflare/agents/blob/main/docs/think/index.md) |
36
+ | A chat UI that owns the loop and the stream | [`AIChatAgent`](./chat-agents.md), built on the `Agent` class |
37
+ | Real-time state sync, `@callable` methods, email, voice | The [`Agent` class](./agent-class.md), which installs a preset of capabilities for you |
38
+ | Durable multi-step processes (not chat) | [Workflows](./workflows.md) |
13
39
 
14
- | You are building... | Use | Why |
15
- | -------------------------------------------------------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
16
- | Stateful backend logic, real-time sync, custom protocols | [`Agent`](./agent-class.md) | The core class: state, WebSockets, scheduling, SQL, and sub-agents. No opinions about chat or LLMs. |
17
- | A chat UI where you own the loop, the stream, and the response | [`AIChatAgent`](./chat-agents.md) | A thin chat-protocol adapter for `useAgentChat`. Bring your own agentic loop and custom streaming. |
18
- | A durable, general-purpose reasoning agent | [`Think`](https://github.com/cloudflare/agents/blob/main/docs/think/index.md) | Opinionated runtime: agentic loop, sessions, tools, memory, compaction, recovery, and multi-channel delivery built in. |
19
- | A voice agent (speech in, speech out) | [Voice mixins](./voice.md) | `withVoice` adds real-time STT/TTS, interruption and barge-in, and conversation persistence to an agent. |
20
- | Durable multi-step processes (not chat) | [Workflows](./workflows.md) | Long-running, retryable step orchestration with Cloudflare Workflows. |
40
+ ## Getting started
21
41
 
22
- Not sure? Start with [`Agent`](./agent-class.md) for raw building blocks, or [`Think`](https://github.com/cloudflare/agents/blob/main/docs/think/index.md) if you want a chat or reasoning agent that already handles the hard parts.
42
+ - [Getting Started](./getting-started.md) - Quick start guide for new users
43
+ - [Adding to an Existing Project](./adding-to-existing-project.md) - Integrate agents into your app
23
44
 
24
- ## What makes these production-grade
45
+ ## Lifecycle
25
46
 
26
- The differentiator is not "we have durable state" — it is what happens when a turn is interrupted. Agents built on this SDK keep their promises across Durable Object eviction, deploys, client disconnects, and human waits:
47
+ - [Durable Object Lifecycle](./lifecycle.md) - Install a Lifecycle on a Durable Object, write capabilities, and use the job queue
48
+ - [getCurrentAgent()](./get-current-agent.md) - Accessing the current object, request, and connection across async calls
27
49
 
28
- - **Turn recovery** — an in-flight LLM turn survives Durable Object eviction and resumes instead of silently dying. See [Chat & Fiber Recovery](./chat-agents.md#stream-recovery) and [Durable Execution](./durable-execution.md).
29
- - **Resumable streams** — a disconnected client rejoins the same stream rather than losing the response. See [Resumable Streaming](./resumable-streaming.md).
30
- - **Recovery-aware delivery** — Think snapshots channel delivery as `accepted`, `streaming`, or `completed`, so a restart replays a not-yet-streamed answer but posts a safe interruption notice rather than risking a duplicate partial reply. See [Messengers — Delivery and Recovery](https://github.com/cloudflare/agents/blob/main/docs/think/messengers.md#delivery-and-recovery).
31
- - **Durable submissions** — webhooks and RPC callers submit a turn with an idempotency key and check status later, instead of holding a request open. See [Programmatic Submissions](https://github.com/cloudflare/agents/blob/main/docs/think/programmatic-submissions.md).
32
- - **Human-in-the-loop without hangs** — a turn can pause for approval and resume later. A human wait is a first-class state, not a stuck request. See [Human in the Loop](./human-in-the-loop.md).
50
+ ## Harnesses
33
51
 
34
- ## Getting Started
52
+ - [Pi harness (Beta)](./harnesses/pi.md) - Host pi-durable sessions in a Durable Object with durable storage and lifecycle wakeups
53
+ - [Think harness (Experimental)](./harnesses/think.md) - Run Think's agent loop as a Lifecycle capability, with transcripts in Sessions, output in Streams, and turns that survive eviction
54
+ - [Think (Experimental)](https://github.com/cloudflare/agents/blob/main/docs/think/index.md) - Opinionated chat agent with built-in memory, tools, and streaming. Extends `Think`, which builds on `Agent`.
35
55
 
36
- - [Getting Started](./getting-started.md) - Quick start guide for new users
37
- - [Adding to an Existing Project](./adding-to-existing-project.md) - Integrate agents into your app
38
- - [Understanding the Agent Class](./agent-class.md) - Deep dive into the Agent class architecture
56
+ ## Capabilities
39
57
 
40
- ## Core Concepts
58
+ Install each capability with `lifecycle.use()`. Capabilities marked experimental may change between releases.
41
59
 
42
- - [State Management](./state.md) - Managing agent state with `setState()`, `initialState`, and `onStateChanged()`
60
+ | Capability | Import | Status | Docs |
61
+ | ------------------ | ------------------- | ------------ | --------------------------------------------------------------- |
62
+ | `PiHarness` | `agents/harness/pi` | Beta | [Pi harness](./harnesses/pi.md) |
63
+ | `MCPClientManager` | `agents/mcp/client` | Stable | [MCP client](./mcp-client.md) |
64
+ | `Scheduler` | `agents/schedules` | Stable | [Scheduling](./scheduling.md) |
65
+ | `State` | `agents/state` | Stable | [Lifecycle](./lifecycle.md#websockets-are-an-opt-in-capability) |
66
+ | `WebSockets` | `agents/websockets` | Stable | [Lifecycle](./lifecycle.md#websockets-are-an-opt-in-capability) |
67
+ | `Queue` | `agents/queue` | Experimental | [Queue](./queue.md) |
68
+ | `RoutedAgents` | `agents/routing` | Experimental | [Routing](./routing.md) |
69
+ | `Browser` | `agents/browser` | Experimental | [Browse the Web](./browse-the-web.md) |
70
+ | `Sessions` | `agents/sessions` | Experimental | [Sessions](./sessions.md) |
71
+ | `Streams` | `agents/streams` | Experimental | [Streams](./streams.md) |
72
+ | `Tasks` | `agents/tasks` | Experimental | [Tasks](./tasks.md) |
73
+
74
+ Building your own harness? Sessions stores the conversation, Streams holds durable incremental output, and Tasks runs replayable background work. [Models](./models.md) and [Models for pi-ai](./models-pi-ai.md) give you one provider for Workers AI and third-party models. [Context](./context.md) and [Workspace](https://github.com/cloudflare/agents/blob/main/docs/shell/index.md) are also available.
75
+
76
+ ## Agent class
77
+
78
+ `Agent` extends `DurableObject`, installs a Lifecycle, and adds `Scheduler`, `Queue`, `MCPClientManager`, `State`, `WebSockets`, `Tasks`, and dynamic agents for you. Its methods (`this.schedule()`, `this.queue()`, `this.setState()`) delegate to those capabilities. Features below are documented for `Agent` and its subclasses.
79
+
80
+ ### Core
81
+
82
+ - [Understanding the Agent Class](./agent-class.md) - How the Agent class is built on the Lifecycle
83
+ - [State Management](./state.md) - `setState()`, `initialState`, and `onStateChanged()`
43
84
  - [Routing](./routing.md) - How `routeAgentRequest()` and agent naming works, plus `RoutedAgents` for a hub that routes to many independent Agents
44
85
  - [Dynamic agents](./sub-agents.md) - Facet-backed child agents for code the parent supervises (dynamic/generated code, per-run tool agents, sandboxes) — not the recommended primitive for many independent peers like chats
45
86
  - [HTTP & WebSockets](./http-websockets.md) - Request handling and real-time connections
46
87
  - [Callable Methods](./callable-methods.md) - The `@callable` decorator and client-server method calls
47
88
  - [Readonly Connections](./readonly-connections.md) - Restricting which connections can modify state
48
- - [getCurrentAgent()](./get-current-agent.md) - Accessing agent context across async calls
89
+ - [Client SDK](./client-sdk.md) - Connecting from React (`useAgent`) and vanilla JS (`AgentClient`), state sync, and RPC calls
49
90
 
50
- ## Client SDK
91
+ ### Background processing
51
92
 
52
- - [Client SDK](./client-sdk.md) - Connecting from React (`useAgent`) and vanilla JS (`AgentClient`), state sync, and RPC calls
93
+ - [Queue](./queue.md) - Durable background task execution
94
+ - [Scheduling](./scheduling.md) - Delayed, scheduled, and cron-based tasks
95
+ - [Retries](./retries.md) - Automatic retries with exponential backoff and jitter
96
+ - [Durable Execution](./durable-execution.md) - `runFiber()`, `startFiber()`, `stash()`, and crash recovery for long tasks
97
+ - [Workflows](./workflows.md) - Durable multi-step processing with Cloudflare Workflows
98
+ - [Human in the Loop](./human-in-the-loop.md) - Approval flows and manual intervention
53
99
 
54
- ## Communication Channels
100
+ ### Communication channels
55
101
 
56
102
  - [Email Service](./email.md) - Sending, receiving, and replying to emails
57
103
  - [Webhooks](./webhooks.md) - Receiving and sending webhook events
@@ -60,35 +106,17 @@ The differentiator is not "we have durable state" — it is what happens when a
60
106
  - [Voice Agents](./voice.md) - Build voice agents with real-time speech-to-text, text-to-speech, and conversation persistence
61
107
  - [Chat SDK State](./chat-sdk.md) - Store Chat SDK subscriptions, locks, queues, and history in Agents sub-agents
62
108
 
63
- ## Background Processing
64
-
65
- - [Queue](./queue.md) - Durable background task execution
66
- - [Scheduling](./scheduling.md) - Delayed, scheduled, and cron-based tasks
67
- - [Retries](./retries.md) - Automatic retries with exponential backoff and jitter
68
- - [Durable Execution](./durable-execution.md) - `runFiber()`, `startFiber()`, `stash()`, and crash recovery for long tasks
69
- - [Workflows](./workflows.md) - Durable multi-step processing with Cloudflare Workflows
70
- - [Human in the Loop](./human-in-the-loop.md) - Approval flows and manual intervention
109
+ ### Chat and AI
71
110
 
72
- ## AI Integration
73
-
74
- - TODO: [AI SDK Integration](./ai-sdk.md) - Using Vercel AI SDK with agents
75
- - TODO: [TanStack Integration](./tanstack.md) - Using TanStack AI with agents
76
111
  - [Chat Agents](./chat-agents.md) - `AIChatAgent` class and `useAgentChat` React hook
77
112
  - [Chat & Fiber Recovery](./chat-agents.md#stream-recovery) - Recover LLM turns after Durable Object eviction
78
113
  - [Agent Tools](./agent-tools.md) - Run chat-capable sub-agents as tools with streaming child timelines
79
114
  - [Server-Driven Messages](./server-driven-messages.md) - Autonomous agent workflows: scheduled follow-ups, queue processing, webhooks, chained reasoning
80
- - TODO: [Using AI Models](./using-ai-models.md) - OpenAI, Anthropic, Workers AI, and other providers
81
- - [Models (Experimental)](./models.md) - `createAI` — one AI SDK provider for Workers AI and third-party catalog models, same string space
82
- - [Models for pi-ai (Beta)](./models-pi-ai.md) - `createAI` for pi-ai: Workers AI ids and third-party models through AI Gateway, as a pi-ai provider
83
- - [Pi harness (Beta)](./harnesses/pi.md) - Host pi-durable sessions in a Durable Object with durable storage and lifecycle wakeups
84
- - [Think harness (Experimental)](./harnesses/think.md) - Run Think's agent loop as a Lifecycle capability, with transcripts in Sessions, output in Streams, and turns that survive eviction
85
- - TODO: [RAG (Retrieval Augmented Generation)](./rag.md) - Vector search with Vectorize
86
- - [Sessions (Experimental)](./sessions.md) - Durable message trees, streamed history, compaction, search, and lossless attachment offload
87
- - [Context (Experimental)](./context.md) - System-prompt blocks, frozen prompts, writable/searchable/loadable providers, and their tools
88
- - [Workspace (Experimental)](https://github.com/cloudflare/agents/blob/main/docs/shell/index.md) - Durable virtual filesystem backed by SQLite + R2
89
- - [Codemode (Experimental)](https://github.com/cloudflare/agents/blob/main/docs/agents/codemode.md) - LLM-generated executable code for tool orchestration
90
115
  - [Client Tools Continuation](./client-tools-continuation.md) - Handling tool calls across client/server
91
116
  - [Resumable Streaming](./resumable-streaming.md) - Automatic stream resumption on client disconnect
117
+ - [Long-Running Agents](./long-running-agents.md) - Building agents that persist for weeks or months: lifecycle, recovery, async operations, and planning
118
+ - TODO: [SQL API](./sql.md) - Using `this.sql` for direct database queries
119
+ - TODO: [Memory & Persistence](./memory.md) - Long-term storage patterns
92
120
 
93
121
  ## Think (Experimental)
94
122
 
@@ -103,51 +131,46 @@ The differentiator is not "we have durable state" — it is what happens when a
103
131
  - [Sub-agents and Programmatic Turns](https://github.com/cloudflare/agents/blob/main/docs/think/sub-agents.md) - RPC streaming, `saveMessages`, recovery
104
132
  - [Programmatic Submissions](https://github.com/cloudflare/agents/blob/main/docs/think/programmatic-submissions.md) - Durable Think turn admission for webhooks and RPC callers
105
133
 
134
+ ## Models and tools
135
+
136
+ - TODO: [AI SDK Integration](./ai-sdk.md) - Using Vercel AI SDK with agents
137
+ - TODO: [TanStack Integration](./tanstack.md) - Using TanStack AI with agents
138
+ - TODO: [Using AI Models](./using-ai-models.md) - OpenAI, Anthropic, Workers AI, and other providers
139
+ - [Models (Experimental)](./models.md) - `createAI` — one AI SDK provider for Workers AI and third-party catalog models, same string space
140
+ - [Models for pi-ai (Beta)](./models-pi-ai.md) - `createAI` for pi-ai: Workers AI ids and third-party models through AI Gateway, as a pi-ai provider
141
+ - [Context (Experimental)](./context.md) - System-prompt blocks, frozen prompts, writable/searchable/loadable providers, and their tools
142
+ - [Workspace (Experimental)](https://github.com/cloudflare/agents/blob/main/docs/shell/index.md) - Durable virtual filesystem backed by SQLite + R2
143
+ - [Codemode (Experimental)](./codemode.md) - LLM-generated executable code for tool orchestration
144
+ - [Browse the Web (Experimental)](./browse-the-web.md) - Full CDP access for web inspection, scraping, and debugging
145
+ - [Search the Web (Beta)](./search-the-web.md) - A `websearch` tool over Cloudflare's Web Search API for the pi harness, the AI SDK, and TanStack AI
146
+ - [Fetch the Web (Beta)](./fetch-the-web.md) - A `web_fetch` tool that reads a URL as Markdown, JSON, or text, with a host-controlled URL policy, for the pi harness, the AI SDK, TanStack AI, and Think
147
+ - TODO: [Cloudflare Sandboxes](./sandboxes.md) - Isolated environments for coding agents, ffmpeg, and heavy compute
148
+ - TODO: [RAG (Retrieval Augmented Generation)](./rag.md) - Vector search with Vectorize
149
+
106
150
  ## MCP (Model Context Protocol)
107
151
 
108
152
  - [Creating MCP Servers](./mcp-servers.md) - Build MCP servers with `McpAgent`
109
153
  - [Securing MCP Servers](./securing-mcp-servers.md) - OAuth and authentication for MCP
110
- - [Connecting to MCP Servers](./mcp-client.md) - `addMcpServer()` and consuming external MCP tools
154
+ - [Connecting to MCP Servers](./mcp-client.md) - `MCPClientManager` on a Durable Object, or `addMcpServer()` on an Agent
111
155
  - [MCP Transports](./mcp-transports.md) - Transport options: Streamable HTTP, SSE, and RPC
112
156
 
113
- ## Authentication & Security
114
-
115
- - TODO: [Securing your Agents](./securing-agents.md) - Authentication, authorization, and access control
116
- - [Cross-Domain Authentication](./cross-domain-authentication.md) - Auth across different domains
117
-
118
- ## Observability & Debugging
157
+ ## Operations
119
158
 
159
+ - [Configuration](./configuration.md) - wrangler.jsonc setup, types, secrets, and deployment
120
160
  - [Observability](./observability.md) - Monitoring and tracing agent activity
161
+ - [Cross-Domain Authentication](./cross-domain-authentication.md) - Auth across different domains
162
+ - TODO: [Securing your Agents](./securing-agents.md) - Authentication, authorization, and access control
121
163
  - TODO: [Testing](./testing.md) - Unit tests, integration tests, mocking agents
122
164
  - TODO: [Evals](./evals.md) - Evaluating AI agent quality and behavior
123
-
124
- ## Agent Studio
125
-
126
165
  - TODO: [Agent Studio](./agent-studio.md) - Local dev tool for inspecting and interacting with agent instances
127
166
 
128
- ## Compute Environments
129
-
130
- - [Browse the Web (Experimental)](./browse-the-web.md) - Full CDP access for web inspection, scraping, and debugging
131
- - [Search the Web (Beta)](./search-the-web.md) - A `websearch` tool over Cloudflare's Web Search API for the pi harness, the AI SDK, and TanStack AI
132
- - TODO: [Cloudflare Sandboxes](./sandboxes.md) - Isolated environments for coding agents, ffmpeg, and heavy compute
133
-
134
- ## Advanced Topics
135
-
136
- - [Long-Running Agents](./long-running-agents.md) - Building agents that persist for weeks or months: lifecycle, recovery, async operations, and planning
137
- - TODO: [SQL API](./sql.md) - Using `this.sql` for direct database queries
138
- - TODO: [Memory & Persistence](./memory.md) - Long-term storage patterns
139
- - [Configuration](./configuration.md) - wrangler.jsonc setup, types, secrets, and deployment
140
-
141
- ## Migration Guides
167
+ ## Migration guides
142
168
 
143
169
  - [Migration to AI SDK v5](./migration-to-ai-sdk-v5.md)
144
170
  - [Migration to AI SDK v6](./migration-to-ai-sdk-v6.md)
145
171
 
146
172
  ## Reference
147
173
 
148
- - [Durable Object Lifecycle](./lifecycle.md) - Compose reusable durable components outside the Agent base class
149
- - [Tasks](./tasks.md) - Durable, replayable background work with journaled steps and durable sleeps (experimental)
150
- - [Streams](./streams.md) - Durable incremental output: chunk log, cursor, replay-then-tail reads (experimental)
151
174
  - TODO: [API Reference](./api-reference.md) - Complete API documentation
152
175
  - TODO: [FAQ / How is this different from Durable Objects?](./faq.md)
153
176
  - TODO: [Resources & Further Reading](./resources.md)
package/docs/lifecycle.md CHANGED
@@ -1,8 +1,7 @@
1
1
  # Durable Object lifecycle
2
2
 
3
- > **Experimental.** Everything exported from `agents/lifecycle` — and the
4
- > capabilities built on it, including `Scheduler` — may change between
5
- > releases while the composition surface stabilizes.
3
+ `agents/lifecycle` is stable. Most capabilities built on it are still
4
+ experimental; each capability's page says which.
6
5
 
7
6
  `agents/lifecycle` lets reusable durable capabilities work in both `Agent` and a
8
7
  plain Cloudflare Durable Object. It uses composition: your class extends the
package/docs/queue.md CHANGED
@@ -7,9 +7,8 @@ survives the object leaving memory.
7
7
 
8
8
  ## Queue Lifecycle capability
9
9
 
10
- > **Experimental.** The `Queue` capability and the `agents/lifecycle` surface
11
- > it builds on may change between releases. Agent's established queue methods
12
- > (`this.queue()` and friends) are stable.
10
+ > **Experimental.** The `Queue` capability may change between releases.
11
+ > Agent's queue methods (`this.queue()` and friends) are stable.
13
12
 
14
13
  `Queue` is a reusable Lifecycle capability. A plain Lifecycle Object can
15
14
  install it without extending `Agent`:
@@ -17,10 +17,6 @@ Under the hood, scheduling uses [Durable Object alarms](https://developers.cloud
17
17
 
18
18
  ## Scheduler Lifecycle primitive
19
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
20
  `Scheduler` is a reusable Lifecycle capability. A plain Lifecycle Object can
25
21
  install it without extending `Agent`:
26
22
 
@@ -836,8 +832,8 @@ When using this schema with OpenAI models via the AI SDK, you must pass `provide
836
832
 
837
833
  ## API Reference
838
834
 
839
- Two surfaces share these semantics: the experimental `Scheduler` primitive
840
- (`agents/schedules`, methods on the scheduler instance) and the stable `Agent`
835
+ Two surfaces share these semantics: the `Scheduler` capability
836
+ (`agents/schedules`, methods on the scheduler instance) and the `Agent`
841
837
  methods (on the Agent class, delegating to `this.scheduler`).
842
838
 
843
839
  ### Scheduler primitive
@@ -88,6 +88,8 @@ The host gets the full API response, without trimming, as `WebSearchToolOutput`:
88
88
 
89
89
  The API has no pagination. The tool description tells the model to search again with a rephrased query when it wants more or different results.
90
90
 
91
+ Results are for discovery. To let the model read the pages it finds, add the [`web_fetch` tool](./fetch-the-web.md) beside `web_search`; the `web_search` description tells the model to fetch result URLs with `web_fetch` when it is available.
92
+
91
93
  ## Failures
92
94
 
93
95
  A failed search becomes a `WebSearchError` with `status`, `code`, `retryable`, and `requestId` (AI Gateway's id for the request, for the gateway log). Every error has a `code`: the API's own when it sends one, for example `web_search_payment_required`, otherwise one derived from the HTTP status, such as `web_search_rate_limited` or `web_search_unavailable`. On a runtime older than the one above, the binding has no `websearch()` and the search fails with code `web_search_unsupported_runtime`.