agents 0.30.1 → 0.32.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 (182) hide show
  1. package/README.md +2 -1
  2. package/dist/{agent-routing-CDbpVjYq.d.ts → agent-routing-vVMA7m8Y.d.ts} +69 -608
  3. package/dist/agent-routing.d.ts +1 -1
  4. package/dist/agent-tool-types.d.ts +1 -1
  5. package/dist/{agent-tools-C8DH8Yii.d.ts → agent-tools-gGXXkf1z.d.ts} +2 -2
  6. package/dist/agent-tools.d.ts +1 -1
  7. package/dist/agent-tools.js +0 -1
  8. package/dist/agent-tools.js.map +1 -1
  9. package/dist/{ai-iXw0uOGj.d.ts → ai-CDn3Abax.d.ts} +30 -4
  10. package/dist/{ai-sdk-DRPh2rRa.js → ai-sdk-Kon40i0q.js} +2 -36
  11. package/dist/ai-sdk-Kon40i0q.js.map +1 -0
  12. package/dist/browser/ai.d.ts +1 -1
  13. package/dist/browser/ai.js +16 -3
  14. package/dist/browser/ai.js.map +1 -1
  15. package/dist/browser/index.d.ts +64 -16
  16. package/dist/browser/index.js +4 -4
  17. package/dist/browser/tools/ai-sdk.d.ts +7 -5
  18. package/dist/browser/tools/ai-sdk.js +4 -4
  19. package/dist/browser/tools/ai-sdk.js.map +1 -1
  20. package/dist/browser/tools/pi.d.ts +9 -15
  21. package/dist/browser/tools/pi.js +6 -14
  22. package/dist/browser/tools/pi.js.map +1 -1
  23. package/dist/browser/tools/tanstack-ai.d.ts +48 -25
  24. package/dist/browser/tools/tanstack-ai.js +8 -4
  25. package/dist/browser/tools/tanstack-ai.js.map +1 -1
  26. package/dist/{browser-DccPw_wW.js → browser-1eUbPvC2.js} +413 -14
  27. package/dist/browser-1eUbPvC2.js.map +1 -0
  28. package/dist/{browser-DlXhSAdf.d.ts → browser-D9RNpJuT.d.ts} +254 -20
  29. package/dist/browser-tool-BhjCcNVE.d.ts +71 -0
  30. package/dist/browser-tool-DRan7Lk6.js +497 -0
  31. package/dist/browser-tool-DRan7Lk6.js.map +1 -0
  32. package/dist/capability-id-B3J8Nok_.js +13 -0
  33. package/dist/capability-id-B3J8Nok_.js.map +1 -0
  34. package/dist/catalog-BpOejOsE.js +239 -0
  35. package/dist/catalog-BpOejOsE.js.map +1 -0
  36. package/dist/catalog-BpiVV1OF.d.ts +173 -0
  37. package/dist/chat/index.d.ts +2 -2
  38. package/dist/chat-sdk/index.d.ts +3 -3
  39. package/dist/chat-sdk/index.js +1 -1
  40. package/dist/client-4OlMIJx-.js.map +1 -1
  41. package/dist/{client-BnYbNT3z.js → client-DMWujQJ1.js} +68 -244
  42. package/dist/client-DMWujQJ1.js.map +1 -0
  43. package/dist/{client-C27CtRZr.d.ts → client-eSfgYvd3.d.ts} +9 -7
  44. package/dist/client.d.ts +1 -1
  45. package/dist/{cloudflare-Dzvc7V2N.js → cloudflare-CUUm_9q9.js} +26 -4
  46. package/dist/cloudflare-CUUm_9q9.js.map +1 -0
  47. package/dist/{connector-DAXBOIIi.js → connector-CM-uuxKM.js} +96 -8
  48. package/dist/connector-CM-uuxKM.js.map +1 -0
  49. package/dist/{connector-20DJ6p4k.d.ts → connector-DZ71EjE-.d.ts} +109 -9
  50. package/dist/{do-oauth-client-provider-DfsUcpc3.d.ts → do-oauth-client-provider-DtNqv7oj.d.ts} +26 -2
  51. package/dist/{index-YSKgfgg9.d.ts → emitter-BJZtCVjQ.d.ts} +6 -140
  52. package/dist/emitter-DPOVcCln.js +15 -0
  53. package/dist/emitter-DPOVcCln.js.map +1 -0
  54. package/dist/experimental/channels/web/index.js +1 -1
  55. package/dist/experimental/webmcp.js +1 -1
  56. package/dist/{handler-stateless-Bly_nSH3.js → handler-stateless-B5hU3dxK.js} +35 -2
  57. package/dist/handler-stateless-B5hU3dxK.js.map +1 -0
  58. package/dist/{handler-stateless-DxYpJ_XF.d.ts → handler-stateless-C90kD5_R.d.ts} +3 -47
  59. package/dist/harness/container/index.d.ts +15 -15
  60. package/dist/harness/container/index.js +0 -1
  61. package/dist/harness/container/index.js.map +1 -1
  62. package/dist/harness/container/runtime/index.js +2 -2
  63. package/dist/harness/opencode/index.js +0 -1
  64. package/dist/harness/opencode/index.js.map +1 -1
  65. package/dist/harness/pi/index.d.ts +15 -7
  66. package/dist/harness/pi/index.js +74 -151
  67. package/dist/harness/pi/index.js.map +1 -1
  68. package/dist/harness/think/index.d.ts +2 -1
  69. package/dist/{images-xaDv2Fqe.js → images-CfuRCjwq.js} +1 -5
  70. package/dist/images-CfuRCjwq.js.map +1 -0
  71. package/dist/{index-DCxk3OSJ.d.ts → index-8k4qsX1T.d.ts} +2 -28
  72. package/dist/index.d.ts +15 -16
  73. package/dist/index.js +3 -3
  74. package/dist/internal-hosts-Kfp5A392.d.ts +230 -0
  75. package/dist/internal-kmw70xwR.js.map +1 -1
  76. package/dist/invoker-BC1SZ3q1.js +26 -0
  77. package/dist/invoker-BC1SZ3q1.js.map +1 -0
  78. package/dist/invoker-DoLFPJZV.d.ts +21 -0
  79. package/dist/markdown-BZ-VLtA1.js +86 -0
  80. package/dist/markdown-BZ-VLtA1.js.map +1 -0
  81. package/dist/mcp/client/do-oauth-client-provider.d.ts +4 -2
  82. package/dist/mcp/client/do-oauth-client-provider.js +15 -1
  83. package/dist/mcp/client/do-oauth-client-provider.js.map +1 -1
  84. package/dist/mcp/client/index.d.ts +15 -1
  85. package/dist/mcp/client/index.js +2 -2
  86. package/dist/mcp/client/x402.d.ts +7 -5
  87. package/dist/mcp/client/x402.js +34 -1
  88. package/dist/mcp/client/x402.js.map +1 -1
  89. package/dist/mcp/index.d.ts +561 -27
  90. package/dist/mcp/index.js +180 -3
  91. package/dist/mcp/index.js.map +1 -1
  92. package/dist/mcp/server/index.d.ts +1 -1
  93. package/dist/mcp/server/index.js +1 -1
  94. package/dist/models/ai-sdk/index.d.ts +2 -2
  95. package/dist/models/ai-sdk/index.js +1 -1
  96. package/dist/models/opencode/index.d.ts +2 -2
  97. package/dist/models/opencode/index.js +1 -1
  98. package/dist/models/pi-ai/index.d.ts +3 -28
  99. package/dist/models/pi-ai/index.js +160 -18
  100. package/dist/models/pi-ai/index.js.map +1 -1
  101. package/dist/observability/ai/index.js +1 -2
  102. package/dist/observability/ai/index.js.map +1 -1
  103. package/dist/observability/index.d.ts +170 -9
  104. package/dist/observability/index.js +386 -11
  105. package/dist/observability/index.js.map +1 -1
  106. package/dist/protocol-BEgrJYC5.d.ts +29 -29
  107. package/dist/react.d.ts +2 -2
  108. package/dist/resource-B59OV2a2.js +325 -0
  109. package/dist/resource-B59OV2a2.js.map +1 -0
  110. package/dist/routing/index.d.ts +1 -1
  111. package/dist/script-CFDLTk5b.d.ts +91 -0
  112. package/dist/script-workspace-BpdAYq1R.d.ts +41 -0
  113. package/dist/serializable.d.ts +1 -1
  114. package/dist/{session-store-yvwq1f9x.js → session-7CDgVE0z.js} +455 -33
  115. package/dist/session-7CDgVE0z.js.map +1 -0
  116. package/dist/{session-store-_IK6dV-w.d.ts → session-store-B7Ca-u1t.d.ts} +8 -1
  117. package/dist/settings-Dk2il2Hf.js.map +1 -1
  118. package/dist/skill-DRBcIBjC.d.ts +221 -0
  119. package/dist/skills/index.d.ts +275 -36
  120. package/dist/skills/index.js +265 -1534
  121. package/dist/skills/index.js.map +1 -1
  122. package/dist/skills/runner.d.ts +37 -0
  123. package/dist/skills/runner.js +912 -0
  124. package/dist/skills/runner.js.map +1 -0
  125. package/dist/skills/tools/ai-sdk.d.ts +36 -0
  126. package/dist/skills/tools/ai-sdk.js +94 -0
  127. package/dist/skills/tools/ai-sdk.js.map +1 -0
  128. package/dist/source-9RQzlEhM.d.ts +112 -0
  129. package/dist/{src-DfLzdwlK.js → src-DoGNJ4Ud.js} +21 -6
  130. package/dist/src-DoGNJ4Ud.js.map +1 -0
  131. package/dist/sub-routing.d.ts +12 -12
  132. package/dist/tool-CTp72Jb_.d.ts +10 -10
  133. package/dist/tool-CxKsvxjm.js.map +1 -1
  134. package/dist/tool-DkT_DJBm.js.map +1 -1
  135. package/dist/tool-DoVberpN.d.ts +22 -22
  136. package/dist/tool-helpers-BI7ifO2S.js +255 -0
  137. package/dist/tool-helpers-BI7ifO2S.js.map +1 -0
  138. package/dist/{transport-CPh7YGiw.d.ts → transport-5moD2pW5.d.ts} +1 -43
  139. package/dist/types-D2naO3rA.d.ts +49 -0
  140. package/dist/vite.d.ts +2 -2
  141. package/dist/vite.js +10 -258
  142. package/dist/vite.js.map +1 -1
  143. package/dist/voice/index.d.ts +1 -1
  144. package/dist/websockets/index.js +1 -1
  145. package/dist/{websockets-CyoeosdO.js → websockets-C6SJtWCJ.js} +1 -2
  146. package/dist/{websockets-CyoeosdO.js.map → websockets-C6SJtWCJ.js.map} +1 -1
  147. package/dist/workflow-types.d.ts +1 -1
  148. package/dist/workflows.d.ts +1 -1
  149. package/dist/workflows.js +0 -1
  150. package/dist/workflows.js.map +1 -1
  151. package/docs/browse-the-web.md +247 -413
  152. package/docs/harnesses/pi.md +36 -30
  153. package/docs/index.md +4 -4
  154. package/docs/mcp-client.md +25 -1
  155. package/docs/mcp-servers.md +26 -7
  156. package/docs/models-pi-ai.md +9 -11
  157. package/docs/models.md +1 -8
  158. package/docs/observability.md +5 -3
  159. package/docs/securing-mcp-servers.md +113 -302
  160. package/package.json +31 -17
  161. package/dist/ai-sdk-DRPh2rRa.js.map +0 -1
  162. package/dist/browser-DccPw_wW.js.map +0 -1
  163. package/dist/browser-tool-B1mhifin.js +0 -616
  164. package/dist/browser-tool-B1mhifin.js.map +0 -1
  165. package/dist/browser-tool-Cxjk5sgG.d.ts +0 -92
  166. package/dist/client-BnYbNT3z.js.map +0 -1
  167. package/dist/cloudflare-Dzvc7V2N.js.map +0 -1
  168. package/dist/connector-DAXBOIIi.js.map +0 -1
  169. package/dist/handler-stateless-Bly_nSH3.js.map +0 -1
  170. package/dist/images-xaDv2Fqe.js.map +0 -1
  171. package/dist/index-iv9FTXX9.d.ts +0 -311
  172. package/dist/invoker-CG0_p_Wq.d.ts +0 -20
  173. package/dist/invoker-CHMnoxIA.js +0 -57
  174. package/dist/invoker-CHMnoxIA.js.map +0 -1
  175. package/dist/session-store-yvwq1f9x.js.map +0 -1
  176. package/dist/skills/compile.d.ts +0 -40
  177. package/dist/skills/compile.js +0 -65
  178. package/dist/skills/compile.js.map +0 -1
  179. package/dist/src-DfLzdwlK.js.map +0 -1
  180. package/dist/tool-helpers-O5BDVEAY.js +0 -58
  181. package/dist/tool-helpers-O5BDVEAY.js.map +0 -1
  182. package/skills-module.d.ts +0 -22
@@ -1,6 +1,6 @@
1
- import { n as browserScreenshotOutput, t as browserExecuteModelOutput } from "../../tool-helpers-O5BDVEAY.js";
1
+ import { n as browserScreenshotOutput, t as browserExecuteModelOutput } from "../../tool-helpers-BI7ifO2S.js";
2
2
  import { createBrowserRuntime } from "../ai.js";
3
- import { n as createBrowserToolCore } from "../../browser-tool-B1mhifin.js";
3
+ import { t as createBrowserToolCore } from "../../browser-tool-DRan7Lk6.js";
4
4
  import { z } from "zod";
5
5
  import { toolDefinition } from "@tanstack/ai";
6
6
  //#region src/browser/tanstack-ai.ts
@@ -38,6 +38,10 @@ import { toolDefinition } from "@tanstack/ai";
38
38
  * messages,
39
39
  * });
40
40
  * ```
41
+ *
42
+ * @deprecated Use `browserTool` from `agents/browser/ai-sdk`,
43
+ * `agents/browser/tanstack-ai` or `agents/browser/pi`. See
44
+ * https://github.com/cloudflare/agents/blob/main/docs/agents/browse-the-web.md#persistent-browser
41
45
  */
42
46
  function createBrowserTools(options) {
43
47
  const { tools } = createBrowserRuntime({
@@ -99,8 +103,8 @@ function browserToolModelResult(output) {
99
103
  *
100
104
  * Works like `browserTool` in `agents/browser/ai-sdk`: tabs, cookies, and
101
105
  * logins carry over between runs, `sessionId: "active"` addresses the tab the
102
- * model last worked in, and a replaced browser is reported as
103
- * `restarted: true`. The tool is named `browser` unless you pass `name`.
106
+ * model last worked in, and a replaced browser is reported in the
107
+ * result's `notice`. The tool is named `browser` unless you pass `name`.
104
108
  *
105
109
  * Unlike the AI SDK tool, the host gets the same output as the model, and
106
110
  * screenshots aren't supported: a returned screenshot is replaced by a
@@ -1 +1 @@
1
- {"version":3,"file":"tanstack-ai.js","names":[],"sources":["../../../src/browser/tanstack-ai.ts","../../../src/browser/tools/tanstack-ai.ts"],"sourcesContent":["/**\n * The legacy TanStack AI `createBrowserTools`. `agents/browser/tanstack-ai`\n * (`tools/tanstack-ai.ts`) re-exports it until it's retired.\n */\nimport { toolDefinition } from \"@tanstack/ai\";\nimport type { ServerTool } from \"@tanstack/ai\";\nimport type { ProxyToolOutput } from \"@cloudflare/codemode\";\nimport { z } from \"zod\";\nimport { createBrowserRuntime, type CreateBrowserToolsOptions } from \"./ai\";\n\nexport type { CreateBrowserToolsOptions } from \"./ai\";\n\n/**\n * Create TanStack AI tools for browser automation via CDP code mode.\n *\n * Returns an array with a single durable `browser_execute` `ServerTool`\n * backed by the same codemode runtime as `agents/browser/ai` — the model\n * writes TypeScript against the `cdp` connector and browser sessions\n * survive pauses.\n *\n * The stateless Quick Action tools are not surfaced through this TanStack\n * wrapper (it exposes only `browser_execute`); use `createQuickActionTools`\n * from `agents/browser/ai` if you want them.\n *\n * @example\n * ```ts\n * import { createBrowserTools } from \"agents/browser/tanstack-ai\";\n * import { chat } from \"@tanstack/ai\";\n *\n * // inside a Durable Object / Agent:\n * const browserTools = createBrowserTools({\n * ctx: this.ctx,\n * browser: this.env.BROWSER,\n * loader: this.env.LOADER,\n * });\n *\n * const stream = chat({\n * adapter: openaiText(\"gpt-4o\"),\n * tools: [...browserTools, ...otherTools],\n * messages,\n * });\n * ```\n */\nexport function createBrowserTools(\n options: CreateBrowserToolsOptions\n): ServerTool[] {\n // This wrapper only surfaces `browser_execute`, so don't build the default-on\n // Quick Action tools just to discard them.\n const { tools } = createBrowserRuntime({ ...options, quickActions: false });\n const executeTool = tools.browser_execute;\n\n const execute = toolDefinition({\n name: \"browser_execute\" as const,\n description:\n typeof executeTool.description === \"function\"\n ? \"\"\n : (executeTool.description ?? \"\"),\n inputSchema: z.object({\n code: z.string().meta({\n description:\n \"TypeScript async arrow function that uses the cdp connector\"\n })\n })\n }).server(async ({ code }) => {\n if (!executeTool.execute) {\n throw new Error(\"browser_execute tool is not executable\");\n }\n const result = (await executeTool.execute({ code }, {\n toolCallId: crypto.randomUUID(),\n messages: [],\n context: {}\n } as never)) as ProxyToolOutput;\n // TanStack has a single return channel, so what the host sees is what the\n // model sees: apply the AI SDK path's `toModelOutput` projection so a\n // screenshot's base64 cannot reach the model and the durable `calls` log\n // stays out of its context. The execution envelope (status, executionId)\n // is preserved either way.\n const modelOutput = await executeTool.toModelOutput?.({\n toolCallId: crypto.randomUUID(),\n input: { code },\n output: result\n });\n if (modelOutput?.type === \"error-text\") {\n throw new Error(modelOutput.value);\n }\n if (modelOutput?.type === \"text\") {\n return { ...result, result: modelOutput.value };\n }\n if (modelOutput?.type === \"json\") {\n return modelOutput.value;\n }\n return result;\n });\n\n return [execute];\n}\n","/**\n * `agents/browser/tanstack-ai` — the TanStack AI tool for a persistent\n * `Browser`.\n *\n * The older `createBrowserTools` lives in `../tanstack-ai.ts` and is\n * re-exported here until it's retired.\n */\nimport { toolDefinition } from \"@tanstack/ai\";\nimport {\n createBrowserToolCore,\n type BrowserToolOptions,\n type BrowserToolOutput\n} from \"../browser-tool\";\nimport {\n browserExecuteModelOutput,\n browserScreenshotOutput\n} from \"../tool-helpers\";\n\nexport type {\n BrowserToolInput,\n BrowserToolOptions,\n BrowserToolOutput\n} from \"../browser-tool\";\nexport type { BrowserNewTab, BrowserSource } from \"../session-connector\";\n\nexport interface TanStackBrowserToolOptions<\n TName extends string = \"browser\"\n> extends BrowserToolOptions {\n /** The tool's name. TanStack AI tools carry it in the definition. */\n name?: TName;\n}\n\n/**\n * What the model sees from one run. TanStack AI has one return channel, so\n * the host gets this too: no `calls` log, bounded `logs`, and a screenshot\n * replaced by a sentence saying it was left out.\n */\nfunction browserToolModelResult(output: BrowserToolOutput): unknown {\n const screenshot = browserScreenshotOutput(output);\n if (!screenshot) return browserExecuteModelOutput(output).value;\n const bytes = Math.floor((screenshot.data.length * 3) / 4);\n // Keep the rest of the result (status, restarted, notice, newTabs).\n return browserExecuteModelOutput({\n ...output,\n result: `Screenshot captured (${screenshot.mediaType}, approximately ${bytes.toLocaleString()} bytes), but this tool can't return images, so neither you nor the user can see it. Read the page with Runtime.evaluate instead.`\n }).value;\n}\n\n/**\n * Create a TanStack AI tool that lets the model drive a persistent browser\n * with JavaScript and the Chrome DevTools Protocol.\n *\n * Works like `browserTool` in `agents/browser/ai-sdk`: tabs, cookies, and\n * logins carry over between runs, `sessionId: \"active\"` addresses the tab the\n * model last worked in, and a replaced browser is reported as\n * `restarted: true`. The tool is named `browser` unless you pass `name`.\n *\n * Unlike the AI SDK tool, the host gets the same output as the model, and\n * screenshots aren't supported: a returned screenshot is replaced by a\n * sentence saying it was left out.\n *\n * @example\n * ```ts\n * import { Browser, browserRun } from \"agents/browser\";\n * import { browserTool } from \"agents/browser/tanstack-ai\";\n * import { chat } from \"@tanstack/ai\";\n *\n * export class MyAgent extends Agent<Env> {\n * browser = new Browser({ provider: browserRun(this.env.BROWSER) });\n *\n * constructor(ctx: AgentContext, env: Env) {\n * super(ctx, env);\n * this.lifecycle.use(this.browser);\n * }\n *\n * async onChatMessage() {\n * const stream = chat({\n * adapter,\n * tools: [browserTool({ browser: this.browser, loader: this.env.LOADER })],\n * messages\n * });\n * }\n * }\n * ```\n */\nexport function browserTool<TName extends string = \"browser\">(\n options: TanStackBrowserToolOptions<TName>\n) {\n const core = createBrowserToolCore(options, {\n screenshotHint:\n \"This tool can't return images, so don't take screenshots; read the page with Runtime.evaluate (for example document.body.innerText) instead.\"\n });\n return toolDefinition({\n name: options.name ?? (\"browser\" as TName),\n description: core.description,\n inputSchema: core.inputSchema\n }).server(async (input) => browserToolModelResult(await core.execute(input)));\n}\n\nexport {\n createBrowserTools,\n type CreateBrowserToolsOptions\n} from \"../tanstack-ai\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,SAAgB,mBACd,SACc;CAGd,MAAM,EAAE,UAAU,qBAAqB;EAAE,GAAG;EAAS,cAAc;CAAM,CAAC;CAC1E,MAAM,cAAc,MAAM;CA6C1B,OAAO,CA3CS,eAAe;EAC7B,MAAM;EACN,aACE,OAAO,YAAY,gBAAgB,aAC/B,KACC,YAAY,eAAe;EAClC,aAAa,EAAE,OAAO,EACpB,MAAM,EAAE,OAAO,CAAC,CAAC,KAAK,EACpB,aACE,8DACJ,CAAC,EACH,CAAC;CACH,CAAC,CAAC,CAAC,OAAO,OAAO,EAAE,WAAW;EAC5B,IAAI,CAAC,YAAY,SACf,MAAM,IAAI,MAAM,wCAAwC;EAE1D,MAAM,SAAU,MAAM,YAAY,QAAQ,EAAE,KAAK,GAAG;GAClD,YAAY,OAAO,WAAW;GAC9B,UAAU,CAAC;GACX,SAAS,CAAC;EACZ,CAAU;EAMV,MAAM,cAAc,MAAM,YAAY,gBAAgB;GACpD,YAAY,OAAO,WAAW;GAC9B,OAAO,EAAE,KAAK;GACd,QAAQ;EACV,CAAC;EACD,IAAI,aAAa,SAAS,cACxB,MAAM,IAAI,MAAM,YAAY,KAAK;EAEnC,IAAI,aAAa,SAAS,QACxB,OAAO;GAAE,GAAG;GAAQ,QAAQ,YAAY;EAAM;EAEhD,IAAI,aAAa,SAAS,QACxB,OAAO,YAAY;EAErB,OAAO;CACT,CAEc,CAAC;AACjB;;;;;;;;;;;;;;;AC1DA,SAAS,uBAAuB,QAAoC;CAClE,MAAM,aAAa,wBAAwB,MAAM;CACjD,IAAI,CAAC,YAAY,OAAO,0BAA0B,MAAM,CAAC,CAAC;CAC1D,MAAM,QAAQ,KAAK,MAAO,WAAW,KAAK,SAAS,IAAK,CAAC;CAEzD,OAAO,0BAA0B;EAC/B,GAAG;EACH,QAAQ,wBAAwB,WAAW,UAAU,kBAAkB,MAAM,eAAe,EAAE;CAChG,CAAC,CAAC,CAAC;AACL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCA,SAAgB,YACd,SACA;CACA,MAAM,OAAO,sBAAsB,SAAS,EAC1C,gBACE,+IACJ,CAAC;CACD,OAAO,eAAe;EACpB,MAAM,QAAQ,QAAS;EACvB,aAAa,KAAK;EAClB,aAAa,KAAK;CACpB,CAAC,CAAC,CAAC,OAAO,OAAO,UAAU,uBAAuB,MAAM,KAAK,QAAQ,KAAK,CAAC,CAAC;AAC9E"}
1
+ {"version":3,"file":"tanstack-ai.js","names":[],"sources":["../../../src/browser/tanstack-ai.ts","../../../src/browser/tools/tanstack-ai.ts"],"sourcesContent":["/**\n * The legacy TanStack AI `createBrowserTools`. `agents/browser/tanstack-ai`\n * (`tools/tanstack-ai.ts`) re-exports it until it's retired.\n */\nimport { toolDefinition } from \"@tanstack/ai\";\nimport type { SchemaInput, ServerTool } from \"@tanstack/ai\";\nimport type { ProxyToolOutput } from \"@cloudflare/codemode\";\nimport { z } from \"zod\";\nimport { createBrowserRuntime, type CreateBrowserToolsOptions } from \"./ai\";\n\nexport type { CreateBrowserToolsOptions } from \"./ai\";\n\n/**\n * Create TanStack AI tools for browser automation via CDP code mode.\n *\n * Returns an array with a single durable `browser_execute` `ServerTool`\n * backed by the same codemode runtime as `agents/browser/ai` — the model\n * writes TypeScript against the `cdp` connector and browser sessions\n * survive pauses.\n *\n * The stateless Quick Action tools are not surfaced through this TanStack\n * wrapper (it exposes only `browser_execute`); use `createQuickActionTools`\n * from `agents/browser/ai` if you want them.\n *\n * @example\n * ```ts\n * import { createBrowserTools } from \"agents/browser/tanstack-ai\";\n * import { chat } from \"@tanstack/ai\";\n *\n * // inside a Durable Object / Agent:\n * const browserTools = createBrowserTools({\n * ctx: this.ctx,\n * browser: this.env.BROWSER,\n * loader: this.env.LOADER,\n * });\n *\n * const stream = chat({\n * adapter: openaiText(\"gpt-4o\"),\n * tools: [...browserTools, ...otherTools],\n * messages,\n * });\n * ```\n *\n * @deprecated Use `browserTool` from `agents/browser/ai-sdk`,\n * `agents/browser/tanstack-ai` or `agents/browser/pi`. See\n * https://github.com/cloudflare/agents/blob/main/docs/agents/browse-the-web.md#persistent-browser\n */\nexport function createBrowserTools(\n options: CreateBrowserToolsOptions\n): ServerTool<SchemaInput, SchemaInput>[] {\n // This wrapper only surfaces `browser_execute`, so don't build the default-on\n // Quick Action tools just to discard them.\n const { tools } = createBrowserRuntime({ ...options, quickActions: false });\n const executeTool = tools.browser_execute;\n\n const execute = toolDefinition({\n name: \"browser_execute\" as const,\n description:\n typeof executeTool.description === \"function\"\n ? \"\"\n : (executeTool.description ?? \"\"),\n inputSchema: z.object({\n code: z.string().meta({\n description:\n \"TypeScript async arrow function that uses the cdp connector\"\n })\n })\n }).server(async ({ code }) => {\n if (!executeTool.execute) {\n throw new Error(\"browser_execute tool is not executable\");\n }\n const result = (await executeTool.execute({ code }, {\n toolCallId: crypto.randomUUID(),\n messages: [],\n context: {}\n } as never)) as ProxyToolOutput;\n // TanStack has a single return channel, so what the host sees is what the\n // model sees: apply the AI SDK path's `toModelOutput` projection so a\n // screenshot's base64 cannot reach the model and the durable `calls` log\n // stays out of its context. The execution envelope (status, executionId)\n // is preserved either way.\n const modelOutput = await executeTool.toModelOutput?.({\n toolCallId: crypto.randomUUID(),\n input: { code },\n output: result\n });\n if (modelOutput?.type === \"error-text\") {\n throw new Error(modelOutput.value);\n }\n if (modelOutput?.type === \"text\") {\n return { ...result, result: modelOutput.value };\n }\n if (modelOutput?.type === \"json\") {\n return modelOutput.value;\n }\n return result;\n });\n\n return [execute];\n}\n","/**\n * `agents/browser/tanstack-ai` — the TanStack AI tool for a persistent\n * `Browser`.\n *\n * The older `createBrowserTools` lives in `../tanstack-ai.ts` and is\n * re-exported here until it's retired.\n */\nimport { toolDefinition } from \"@tanstack/ai\";\nimport {\n createBrowserToolCore,\n type BrowserToolOptions,\n type BrowserToolOutput\n} from \"../browser-tool\";\nimport {\n browserExecuteModelOutput,\n browserScreenshotOutput\n} from \"../tool-helpers\";\n\nexport type {\n BrowserToolInput,\n BrowserToolOptions,\n BrowserToolOutput\n} from \"../browser-tool\";\nexport type { BrowserNewTab, BrowserSource } from \"../session\";\n\nexport interface TanStackBrowserToolOptions<\n TName extends string = \"browser\"\n> extends BrowserToolOptions {\n /** The tool's name. TanStack AI tools carry it in the definition. */\n name?: TName;\n}\n\n/**\n * What the model sees from one run. TanStack AI has one return channel, so\n * the host gets this too: no `calls` log, bounded `logs`, and a screenshot\n * replaced by a sentence saying it was left out.\n */\nfunction browserToolModelResult(output: BrowserToolOutput): unknown {\n const screenshot = browserScreenshotOutput(output);\n if (!screenshot) return browserExecuteModelOutput(output).value;\n const bytes = Math.floor((screenshot.data.length * 3) / 4);\n // Keep the rest of the result (status, notice).\n return browserExecuteModelOutput({\n ...output,\n result: `Screenshot captured (${screenshot.mediaType}, approximately ${bytes.toLocaleString()} bytes), but this tool can't return images, so neither you nor the user can see it. Read the page with Runtime.evaluate instead.`\n }).value;\n}\n\n/**\n * Create a TanStack AI tool that lets the model drive a persistent browser\n * with JavaScript and the Chrome DevTools Protocol.\n *\n * Works like `browserTool` in `agents/browser/ai-sdk`: tabs, cookies, and\n * logins carry over between runs, `sessionId: \"active\"` addresses the tab the\n * model last worked in, and a replaced browser is reported in the\n * result's `notice`. The tool is named `browser` unless you pass `name`.\n *\n * Unlike the AI SDK tool, the host gets the same output as the model, and\n * screenshots aren't supported: a returned screenshot is replaced by a\n * sentence saying it was left out.\n *\n * @example\n * ```ts\n * import { Browser, browserRun } from \"agents/browser\";\n * import { browserTool } from \"agents/browser/tanstack-ai\";\n * import { chat } from \"@tanstack/ai\";\n *\n * export class MyAgent extends Agent<Env> {\n * browser = new Browser({ provider: browserRun(this.env.BROWSER) });\n *\n * constructor(ctx: AgentContext, env: Env) {\n * super(ctx, env);\n * this.lifecycle.use(this.browser);\n * }\n *\n * async onChatMessage() {\n * const stream = chat({\n * adapter,\n * tools: [browserTool({ browser: this.browser, loader: this.env.LOADER })],\n * messages\n * });\n * }\n * }\n * ```\n */\nexport function browserTool<TName extends string = \"browser\">(\n options: TanStackBrowserToolOptions<TName>\n) {\n const core = createBrowserToolCore(options, {\n screenshotHint:\n \"This tool can't return images, so don't take screenshots; read the page with Runtime.evaluate (for example document.body.innerText) instead.\"\n });\n return toolDefinition({\n name: options.name ?? (\"browser\" as TName),\n description: core.description,\n inputSchema: core.inputSchema\n }).server(async (input) => browserToolModelResult(await core.execute(input)));\n}\n\nexport {\n createBrowserTools,\n type CreateBrowserToolsOptions\n} from \"../tanstack-ai\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA,SAAgB,mBACd,SACwC;CAGxC,MAAM,EAAE,UAAU,qBAAqB;EAAE,GAAG;EAAS,cAAc;CAAM,CAAC;CAC1E,MAAM,cAAc,MAAM;CA6C1B,OAAO,CA3CS,eAAe;EAC7B,MAAM;EACN,aACE,OAAO,YAAY,gBAAgB,aAC/B,KACC,YAAY,eAAe;EAClC,aAAa,EAAE,OAAO,EACpB,MAAM,EAAE,OAAO,CAAC,CAAC,KAAK,EACpB,aACE,8DACJ,CAAC,EACH,CAAC;CACH,CAAC,CAAC,CAAC,OAAO,OAAO,EAAE,WAAW;EAC5B,IAAI,CAAC,YAAY,SACf,MAAM,IAAI,MAAM,wCAAwC;EAE1D,MAAM,SAAU,MAAM,YAAY,QAAQ,EAAE,KAAK,GAAG;GAClD,YAAY,OAAO,WAAW;GAC9B,UAAU,CAAC;GACX,SAAS,CAAC;EACZ,CAAU;EAMV,MAAM,cAAc,MAAM,YAAY,gBAAgB;GACpD,YAAY,OAAO,WAAW;GAC9B,OAAO,EAAE,KAAK;GACd,QAAQ;EACV,CAAC;EACD,IAAI,aAAa,SAAS,cACxB,MAAM,IAAI,MAAM,YAAY,KAAK;EAEnC,IAAI,aAAa,SAAS,QACxB,OAAO;GAAE,GAAG;GAAQ,QAAQ,YAAY;EAAM;EAEhD,IAAI,aAAa,SAAS,QACxB,OAAO,YAAY;EAErB,OAAO;CACT,CAEc,CAAC;AACjB;;;;;;;;;;;;;;;AC9DA,SAAS,uBAAuB,QAAoC;CAClE,MAAM,aAAa,wBAAwB,MAAM;CACjD,IAAI,CAAC,YAAY,OAAO,0BAA0B,MAAM,CAAC,CAAC;CAC1D,MAAM,QAAQ,KAAK,MAAO,WAAW,KAAK,SAAS,IAAK,CAAC;CAEzD,OAAO,0BAA0B;EAC/B,GAAG;EACH,QAAQ,wBAAwB,WAAW,UAAU,kBAAkB,MAAM,eAAe,EAAE;CAChG,CAAC,CAAC,CAAC;AACL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCA,SAAgB,YACd,SACA;CACA,MAAM,OAAO,sBAAsB,SAAS,EAC1C,gBACE,+IACJ,CAAC;CACD,OAAO,eAAe;EACpB,MAAM,QAAQ,QAAS;EACvB,aAAa,KAAK;EAClB,aAAa,KAAK;CACpB,CAAC,CAAC,CAAC,OAAO,OAAO,UAAU,uBAAuB,MAAM,KAAK,QAAQ,KAAK,CAAC,CAAC;AAC9E"}
@@ -1,7 +1,227 @@
1
1
  import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, r as _assertClassBrand, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-DZBYAB34.js";
2
2
  import { t as _classPrivateMethodInitSpec } from "./classPrivateMethodInitSpec-qMjJ6sHQ.js";
3
3
  import { t as LifecycleCapability } from "./capability-DBAKc6UZ.js";
4
- import { _ as listBrowserTargets, f as connectBrowserSession, g as isMissingBrowserSession, l as acquireBrowserSession, n as DurableBrowserSessionStore, o as createLiveView, r as loadCdpSpec, u as closeBrowserSession } from "./session-store-yvwq1f9x.js";
4
+ import { t as browserCapabilityId } from "./capability-id-B3J8Nok_.js";
5
+ import { b as listBrowserTargets, f as acquireBrowserSession, h as connectBrowserSession, n as BrowserSession, o as DurableBrowserSessionStore, p as closeBrowserSession, s as loadCdpSpec, u as createLiveView, y as isMissingBrowserSession } from "./session-7CDgVE0z.js";
6
+ import { WorkerEntrypoint } from "cloudflare:workers";
7
+ import { Validator } from "@cfworker/json-schema";
8
+ //#region src/browser/internal-hosts.ts
9
+ /**
10
+ * Internal hostnames: sites only this agent's browser can open.
11
+ *
12
+ * A host maps a hostname such as `preview.internal` to code in your agent
13
+ * (`fetch`) or to a deployed Worker (`worker`). Browser Run sends the
14
+ * browser's requests for that hostname to it instead of the internet, so a
15
+ * page can be served from a sandbox port or a workspace folder, without exposing anything publicly.
16
+ *
17
+ * How a request travels: Browser Run accepts only entrypoints of your own
18
+ * Worker as routes, and keeps them for the whole browser session (observed
19
+ * across redeploys and Durable Object eviction; Browser Run doesn't document
20
+ * this yet). So every `fetch` host routes to {@link InternalHostRouter}, which
21
+ * you export from your main module. Its props say which agent and browser
22
+ * own the host, and nothing else. It calls the agent's `fetchInternalHost()`
23
+ * over RPC, and the agent's `Browser` runs the host's `fetch`. `Agent` has
24
+ * that method built in; a plain Durable Object adds it (see
25
+ * {@link InternalHostMessage}). A `worker` host goes straight to the Worker
26
+ * you gave it.
27
+ */
28
+ /** The most hostnames one browser can have. A Browser Run limit. */
29
+ const MAX_INTERNAL_HOSTS = 8;
30
+ const HOSTNAME = /^(?=.{1,253}$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/;
31
+ /**
32
+ * Suffixes reserved so they never resolve on the internet. A real domain
33
+ * would be served by the host over http but reach the real site over https
34
+ * (and Chrome tries https first), so internal hostnames must use one.
35
+ * Single labels like `preview` would work, but Browser Run doesn't route
36
+ * labels that are real TLDs (`docs`, `app`) and gives no error for them, so
37
+ * they're rejected rather than checked against a TLD list.
38
+ */
39
+ const RESERVED_SUFFIXES = [
40
+ ".internal",
41
+ ".test",
42
+ ".example",
43
+ ".invalid"
44
+ ];
45
+ /** Why `host` can't be an internal hostname, or undefined when it can. */
46
+ function hostnameProblem(host) {
47
+ if (host.includes("://")) return "drop the scheme";
48
+ if (host.includes("/")) return "drop the path";
49
+ if (host.includes(":")) return "drop the port (only port 80 is routed)";
50
+ if (host !== host.toLowerCase()) return "use lowercase";
51
+ if (/^\d+(\.\d+){3}$/.test(host)) return "use a name, not an IP address";
52
+ if (host === "localhost" || host.endsWith(".localhost")) return "Chrome sends localhost to the browser's own machine";
53
+ if (!HOSTNAME.test(host)) return "not a valid hostname";
54
+ if (!RESERVED_SUFFIXES.some((suffix) => host.endsWith(suffix))) return `use a name that can't be a real site: end it in ${RESERVED_SUFFIXES.join(", ")}`;
55
+ }
56
+ /**
57
+ * Check `internalHosts` when the provider is created, so a mistake fails at
58
+ * startup instead of at the first browser launch.
59
+ */
60
+ function validateInternalHosts(options) {
61
+ const hosts = Object.entries(options.hosts);
62
+ if (hosts.length === 0) throw new Error("internalHosts.hosts needs at least one hostname");
63
+ if (hosts.length > 8) throw new Error(`internalHosts has ${hosts.length} hostnames; Browser Run allows at most 8 per browser`);
64
+ for (const [host, config] of hosts) {
65
+ const problem = hostnameProblem(host);
66
+ if (problem) throw new Error(`Internal hostname "${host}": ${problem}`);
67
+ const hasFetch = typeof config.fetch === "function";
68
+ const hasWorker = config.worker !== void 0;
69
+ if (hasFetch === hasWorker) throw new Error(`Internal hostname "${host}" needs exactly one of fetch or worker`);
70
+ if (hasWorker && config.target !== void 0) throw new Error(`Internal hostname "${host}": target needs fetch; a worker host can't be assigned`);
71
+ if (hasFetch && typeof options.router !== "function") throw new Error(`Internal hostname "${host}" uses fetch, which needs internalHosts.router: this.ctx.exports.InternalHostRouter (and \`export { InternalHostRouter } from "agents/browser"\` in your main module)`);
72
+ if (config.target !== void 0) targetJsonSchema(host, config.target);
73
+ }
74
+ }
75
+ /**
76
+ * Identifies the set of hostnames and how each is routed. A browser started
77
+ * with a different set is replaced: Browser Run fixes routes at launch.
78
+ */
79
+ function internalHostsSignature(options) {
80
+ if (!options) return void 0;
81
+ return Object.entries(options.hosts).map(([host, config]) => `${host}=${config.worker ? "worker" : "fetch"}`).sort().join(",");
82
+ }
83
+ /**
84
+ * The route Browser Run gets for a `worker` host. A `ctx.exports`
85
+ * entrypoint is a loopback stub that Browser Run can't take as is, so it's
86
+ * called (with empty props) to get a Fetcher it can.
87
+ */
88
+ function workerRoute(worker) {
89
+ if (typeof worker !== "function") return worker;
90
+ return worker({ props: {} });
91
+ }
92
+ function isStandardSchema(schema) {
93
+ return typeof schema === "object" && schema !== null && "~standard" in schema;
94
+ }
95
+ /** A host's target schema as JSON Schema, for the model and for plain validation. */
96
+ function targetJsonSchema(host, schema) {
97
+ if (!isStandardSchema(schema)) {
98
+ if (typeof schema !== "object" || schema === null) throw new Error(`Internal hostname "${host}": target must be a schema`);
99
+ return schema;
100
+ }
101
+ const { jsonSchema } = schema["~standard"];
102
+ if (!jsonSchema?.input) throw new Error(`Internal hostname "${host}": target needs a schema that can describe itself as JSON Schema (Standard JSON Schema, such as zod 4.2+), or a plain JSON Schema object`);
103
+ try {
104
+ return jsonSchema.input({ target: "draft-07" });
105
+ } catch {
106
+ return jsonSchema.input({ target: "draft-2020-12" });
107
+ }
108
+ }
109
+ const plainValidators = /* @__PURE__ */ new WeakMap();
110
+ function formatIssues(issues) {
111
+ return issues.map((issue) => issue.path ? `${issue.path}: ${issue.message}` : issue.message).join("; ");
112
+ }
113
+ function pathString(path) {
114
+ if (!path?.length) return void 0;
115
+ return path.map((part) => String(typeof part === "object" ? part.key : part)).join(".");
116
+ }
117
+ function plainIssues(errors) {
118
+ const specific = errors.filter((error) => error.keyword !== "properties");
119
+ return (specific.length > 0 ? specific : errors).map((error) => ({
120
+ message: error.error,
121
+ path: error.instanceLocation.replace(/^#\/?/, "").replaceAll("/", ".") || void 0
122
+ }));
123
+ }
124
+ /**
125
+ * Validate a target against a host's schema. Returns the validated value
126
+ * (a Standard Schema's output, which may differ from its input), or a
127
+ * message saying what's wrong.
128
+ */
129
+ async function validateTarget(schema, value) {
130
+ if (isStandardSchema(schema)) {
131
+ const result = await schema["~standard"].validate(value);
132
+ if (result.issues) return {
133
+ ok: false,
134
+ error: formatIssues(result.issues.map((issue) => ({
135
+ message: issue.message,
136
+ path: pathString(issue.path)
137
+ })))
138
+ };
139
+ return {
140
+ ok: true,
141
+ value: result.value
142
+ };
143
+ }
144
+ let validator = plainValidators.get(schema);
145
+ if (!validator) {
146
+ validator = new Validator(schema, "7", false);
147
+ plainValidators.set(schema, validator);
148
+ }
149
+ const result = validator.validate(value);
150
+ return result.valid ? {
151
+ ok: true,
152
+ value
153
+ } : {
154
+ ok: false,
155
+ error: formatIssues(plainIssues(result.errors))
156
+ };
157
+ }
158
+ function isInternalHostMessage(value) {
159
+ if (typeof value !== "object" || value === null) return false;
160
+ const message = value;
161
+ return typeof message.browser === "string" && typeof message.host === "string" && message.request instanceof Request;
162
+ }
163
+ /** A plain-text response the browser shows for a failed internal request. */
164
+ function internalHostError(status, message) {
165
+ return new Response(message, {
166
+ status,
167
+ headers: { "content-type": "text/plain; charset=utf-8" }
168
+ });
169
+ }
170
+ /**
171
+ * Whether an RPC call failed because the agent has no `fetchInternalHost()`.
172
+ * workerd says: `The RPC receiver does not implement the method
173
+ * "fetchInternalHost".` If that wording changes, the router falls back to its
174
+ * generic 502, which names the method too.
175
+ */
176
+ function isMissingMethod(error) {
177
+ return error instanceof Error && error.message.includes("fetchInternalHost") && /does not implement|is not a function/.test(error.message);
178
+ }
179
+ /**
180
+ * Carries a browser's requests for an internal hostname to the agent that
181
+ * owns it. Export it from your Worker's main module, so
182
+ * `this.ctx.exports.InternalHostRouter` exists:
183
+ *
184
+ * ```ts
185
+ * export { InternalHostRouter } from "agents/browser";
186
+ * ```
187
+ *
188
+ * It trusts its props: Browser Run sets them from your routes, but another
189
+ * Worker in your account with a service binding to this entrypoint could
190
+ * set its own and reach any agent's hosts. That Worker could already call
191
+ * your agents directly, so this grants nothing new.
192
+ *
193
+ * It holds no logic of its own: it calls the agent's `fetchInternalHost()`
194
+ * with an {@link InternalHostMessage}, and the agent's `Browser` runs the
195
+ * host's `fetch`. `Agent` has `fetchInternalHost()` built in; a plain
196
+ * Durable Object adds it (see {@link InternalHostMessage}).
197
+ *
198
+ * @experimental The API may change before it stabilizes.
199
+ */
200
+ var InternalHostRouter = class extends WorkerEntrypoint {
201
+ async fetch(request) {
202
+ const props = this.ctx.props;
203
+ if (!isRouterProps(props)) return internalHostError(502, "InternalHostRouter was called without route props. Only Browser Run calls it, with the routes browserRun({ internalHosts }) gives it.");
204
+ const namespace = this.ctx.exports?.[props.className];
205
+ if (!namespace?.getByName) return internalHostError(502, `${props.host}: the agent class "${props.className}" isn't exported from this Worker`);
206
+ try {
207
+ const response = await namespace.getByName(props.name).fetchInternalHost({
208
+ browser: props.browser,
209
+ host: props.host,
210
+ request
211
+ });
212
+ if (response instanceof Response) return response;
213
+ return internalHostError(502, `${props.host}: the agent didn't answer with a Response`);
214
+ } catch (error) {
215
+ if (isMissingMethod(error)) return internalHostError(502, `${props.host}: ${props.className} has no fetchInternalHost() method. Agent has it built in; on a plain Durable Object add: fetchInternalHost(message) { return this.browser.fetchInternalHost(message); }`);
216
+ console.error(`[agents/browser] ${props.className} failed to serve ${props.host}`, error);
217
+ return internalHostError(502, `${props.host}: the agent failed to serve this request`);
218
+ }
219
+ }
220
+ };
221
+ function isRouterProps(props) {
222
+ return props?.v === 1 && typeof props.className === "string" && typeof props.name === "string" && typeof props.browser === "string" && typeof props.host === "string";
223
+ }
224
+ //#endregion
5
225
  //#region src/browser/providers/provider-adapter.ts
6
226
  /**
7
227
  * Thrown by {@link BrowserProviderAdapter.connect} when the session no longer
@@ -24,12 +244,34 @@ var BrowserSessionGoneError = class extends Error {
24
244
  * ```
25
245
  */
26
246
  function browserRun(binding, options = {}) {
247
+ const erased = options;
248
+ if (erased.internalHosts) validateInternalHosts(erased.internalHosts);
27
249
  return {
28
250
  kind: "browser-run",
29
251
  binding,
30
- options
252
+ options: erased
31
253
  };
32
254
  }
255
+ function isDataCloneError(error) {
256
+ return error instanceof Error && error.name === "DataCloneError";
257
+ }
258
+ /** One `outboundByHost` route per internal hostname. */
259
+ function internalHostRoutes(internalHosts, owner) {
260
+ const routes = {};
261
+ const hosts = Object.entries(internalHosts.hosts);
262
+ for (const [host, config] of hosts) {
263
+ if (config.worker) {
264
+ routes[host] = workerRoute(config.worker);
265
+ continue;
266
+ }
267
+ routes[host] = internalHosts.router({ props: {
268
+ v: 1,
269
+ ...owner,
270
+ host
271
+ } });
272
+ }
273
+ return routes;
274
+ }
33
275
  /** The code behind {@link browserRun}. */
34
276
  function browserRunAdapter(provider) {
35
277
  const { binding, options } = provider;
@@ -38,12 +280,22 @@ function browserRunAdapter(provider) {
38
280
  get keepAliveMs() {
39
281
  return keepAliveMs();
40
282
  },
41
- async create() {
42
- return acquireBrowserSession(binding, {
43
- keepAliveMs: keepAliveMs(),
44
- recording: options.recording,
45
- guardrails: options.guardrails
46
- });
283
+ internalHosts: options.internalHosts,
284
+ internalHostsSignature: internalHostsSignature(options.internalHosts),
285
+ async create(context) {
286
+ const { internalHosts } = options;
287
+ if (internalHosts && !context?.owner) throw new Error("Internal hostnames need the Browser installed with Lifecycle.use() on a top-level agent");
288
+ try {
289
+ return await acquireBrowserSession(binding, {
290
+ keepAliveMs: keepAliveMs(),
291
+ recording: options.recording,
292
+ guardrails: options.guardrails,
293
+ ...internalHosts && context?.owner ? { outboundByHost: internalHostRoutes(internalHosts, context.owner) } : {}
294
+ });
295
+ } catch (error) {
296
+ if (internalHosts && isDataCloneError(error)) throw new Error("Browser Run couldn't take the internal hostname routes: a worker host must be a service binding or an entrypoint of this Worker (this.ctx.exports.Name), not a Dynamic Worker or another object it can't hold", { cause: error });
297
+ throw error;
298
+ }
47
299
  },
48
300
  async isAlive(sessionId) {
49
301
  try {
@@ -119,6 +371,15 @@ function retiredBrowserSessionKey(name) {
119
371
  function browserScopeKeyPrefix(name) {
120
372
  return `browser:scope:${name.length}:${name}:`;
121
373
  }
374
+ /**
375
+ * Where a browser's internal-host targets live: one key per host, under a
376
+ * prefix per browser (length-prefixed like scope records, so one browser's
377
+ * prefix never matches another's). Kept apart from the session record, so
378
+ * targets survive the browser being replaced.
379
+ */
380
+ function internalHostTargetsPrefix(name) {
381
+ return `browser:hosts:${name.length}:${name}:`;
382
+ }
122
383
  /** The adapter behind a provider: one explicit case per provider kind. */
123
384
  function providerAdapterFor(provider) {
124
385
  switch (provider.kind) {
@@ -151,7 +412,7 @@ var _Browser_brand = /* @__PURE__ */ new WeakSet();
151
412
  var Browser = class extends LifecycleCapability {
152
413
  constructor(options) {
153
414
  const name = options.name ?? "default";
154
- super(`browser:${name}`);
415
+ super(browserCapabilityId(name));
155
416
  _classPrivateMethodInitSpec(this, _Browser_brand);
156
417
  _classPrivateFieldInitSpec(this, _options, void 0);
157
418
  _classPrivateFieldInitSpec(this, _key, void 0);
@@ -180,7 +441,8 @@ var Browser = class extends LifecycleCapability {
180
441
  ...resolvedFields(session)
181
442
  };
182
443
  }
183
- const alive = await _classPrivateFieldGet2(_adapter, this).isAlive(existing.sessionId);
444
+ const routesMatch = existing.internalHosts === _classPrivateFieldGet2(_adapter, this).internalHostsSignature;
445
+ const alive = routesMatch && await _classPrivateFieldGet2(_adapter, this).isAlive(existing.sessionId);
184
446
  const lock = await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).acquireLock(key);
185
447
  try {
186
448
  const current = await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).get(key);
@@ -201,6 +463,7 @@ var Browser = class extends LifecycleCapability {
201
463
  } finally {
202
464
  await lock.release();
203
465
  }
466
+ if (!routesMatch) await _assertClassBrand(_Browser_brand, this, _closeQuietly).call(this, existing.sessionId);
204
467
  }
205
468
  throw new Error(`Browser "${name}" kept changing concurrently — retry`);
206
469
  }
@@ -237,6 +500,15 @@ var Browser = class extends LifecycleCapability {
237
500
  }
238
501
  }
239
502
  /**
503
+ * Start a session on this browser for one run: connects on its first
504
+ * call, resolves `sessionId: "active"` to the scope's tab, and reports
505
+ * what happened to the browser when you close it. `browserTool` opens
506
+ * one per run.
507
+ */
508
+ session(options) {
509
+ return new BrowserSession(this, options);
510
+ }
511
+ /**
240
512
  * Close the browser: retire the record (so the next resolve reports
241
513
  * `restarted: true`) and close its provider session. Returns `false` when
242
514
  * there was nothing to close.
@@ -283,6 +555,83 @@ var Browser = class extends LifecycleCapability {
283
555
  }
284
556
  return view;
285
557
  }
558
+ /**
559
+ * The browser's internal hostnames, or `undefined` when its provider has
560
+ * none. See {@link BrowserInternalHosts}.
561
+ */
562
+ get internalHosts() {
563
+ const config = _classPrivateFieldGet2(_adapter, this).internalHosts;
564
+ if (!config) return void 0;
565
+ const hosts = config.hosts;
566
+ const describe = () => Object.entries(hosts).map(([host, entry]) => ({
567
+ host,
568
+ url: `http://${host}/`,
569
+ ...entry.description ? { description: entry.description } : {},
570
+ assignable: entry.target !== void 0,
571
+ ...entry.target !== void 0 ? { targetSchema: targetJsonSchema(host, entry.target) } : {}
572
+ }));
573
+ const assignable = (host) => {
574
+ const entry = hosts[host];
575
+ if (!entry || !Object.hasOwn(hosts, host)) throw new Error(`"${host}" isn't an internal hostname. The hostnames are: ${Object.keys(hosts).join(", ")}`);
576
+ if (entry.target === void 0) throw new Error(`"${host}" always serves the same thing; it has no target to assign`);
577
+ return entry.target;
578
+ };
579
+ return {
580
+ describe,
581
+ list: async () => {
582
+ const prefix = internalHostTargetsPrefix(this.name);
583
+ const stored = await this.lifecycle.storage.list({ prefix });
584
+ return describe().map((info) => {
585
+ const key = prefix + info.host;
586
+ return stored.has(key) ? {
587
+ ...info,
588
+ target: stored.get(key)
589
+ } : info;
590
+ });
591
+ },
592
+ assign: async (host, target) => {
593
+ const schema = assignable(host);
594
+ if (target === void 0) throw new Error(`browser.assignHost needs a target for ${host}; use browser.clearHost to unassign it`);
595
+ const json = JSON.parse(JSON.stringify(target));
596
+ const checked = await validateTarget(schema, json);
597
+ if (!checked.ok) throw new Error(`Invalid target for ${host}: ${checked.error}`);
598
+ await this.lifecycle.storage.put(_assertClassBrand(_Browser_brand, this, _targetKey).call(this, host), json);
599
+ return { url: `http://${host}/` };
600
+ },
601
+ clear: async (host) => {
602
+ assignable(host);
603
+ return this.lifecycle.storage.delete(_assertClassBrand(_Browser_brand, this, _targetKey).call(this, host));
604
+ }
605
+ };
606
+ }
607
+ /**
608
+ * Serve one browser request to an internal hostname. `InternalHostRouter`
609
+ * calls the agent's `fetchInternalHost()` with the message; `Agent` passes
610
+ * it here on its own. A plain Durable Object passes it on itself:
611
+ *
612
+ * ```ts
613
+ * fetchInternalHost(message: InternalHostMessage) {
614
+ * return this.browser.fetchInternalHost(message);
615
+ * }
616
+ * ```
617
+ *
618
+ * Answers 502 with the reason when this browser doesn't serve the host.
619
+ */
620
+ async fetchInternalHost(message) {
621
+ if (message.browser !== this.name) return internalHostError(502, `${message.host} belongs to browser "${message.browser}", but was passed to browser "${this.name}"`);
622
+ return _assertClassBrand(_Browser_brand, this, _serveInternalHost).call(this, message.host, message.request);
623
+ }
624
+ /**
625
+ * `Agent.fetchInternalHost()` routes here through Lifecycle, so `Agent`
626
+ * needn't import the browser.
627
+ *
628
+ * @internal Called by Lifecycle; not part of the host API.
629
+ */
630
+ async onRoute(context) {
631
+ const { payload } = context;
632
+ if (!isInternalHostMessage(payload)) throw new Error(`Browser "${this.name}" got an unknown routed message`);
633
+ return this.fetchInternalHost(payload);
634
+ }
286
635
  };
287
636
  function _connection(resolved, scope, cdp, entered) {
288
637
  const { sessionId } = resolved;
@@ -316,6 +665,54 @@ async function _open(resolved) {
316
665
  }
317
666
  });
318
667
  }
668
+ async function _serveInternalHost(host, request) {
669
+ const hosts = _classPrivateFieldGet2(_adapter, this).internalHosts?.hosts;
670
+ const entry = hosts && Object.hasOwn(hosts, host) ? hosts[host] : void 0;
671
+ if (!entry?.fetch) return internalHostError(502, `${host} is no longer an internal hostname of browser "${this.name}"`);
672
+ let target;
673
+ if (entry.target !== void 0) {
674
+ const assigned = await this.lifecycle.storage.get(_assertClassBrand(_Browser_brand, this, _targetKey).call(this, host));
675
+ if (assigned === void 0) return internalHostError(502, `${host} isn't pointed anywhere yet. Call browser.assignHost({ host: "${host}", target }) first.`);
676
+ const checked = await validateTarget(entry.target, assigned);
677
+ if (!checked.ok) return internalHostError(502, `${host}'s target no longer matches its schema (${checked.error}). Call browser.assignHost again.`);
678
+ target = checked.value;
679
+ }
680
+ const fetch = entry.fetch;
681
+ try {
682
+ const response = await this.lifecycle.runInHostContext(() => fetch(request, target), { request });
683
+ if (response instanceof Response) return response;
684
+ return internalHostError(502, `${host}: its fetch didn't return a Response`);
685
+ } catch (error) {
686
+ console.error(`[agents/browser] ${host}: its fetch threw`, error);
687
+ return internalHostError(502, `${host}: its fetch failed`);
688
+ }
689
+ }
690
+ /**
691
+ * Which agent and browser the session's routes lead back to. `undefined`
692
+ * without internal hostnames.
693
+ */
694
+ function _internalHostOwner() {
695
+ if (!_classPrivateFieldGet2(_adapter, this).internalHosts) return void 0;
696
+ const services = this.lifecycleServices;
697
+ if (!services) throw new Error(`Browser "${this.name}" has internal hostnames, which need it installed with Lifecycle.use()`);
698
+ if (services.routes.source !== void 0) throw new Error(`Browser "${this.name}" has internal hostnames, which sub-agents don't support yet`);
699
+ return {
700
+ className: services.className,
701
+ name: services.name,
702
+ browser: this.name
703
+ };
704
+ }
705
+ /** The storage key of one host's target. */
706
+ function _targetKey(host) {
707
+ return internalHostTargetsPrefix(this.name) + host;
708
+ }
709
+ async function _closeQuietly(sessionId) {
710
+ try {
711
+ await _classPrivateFieldGet2(_adapter, this).close(sessionId);
712
+ } catch (error) {
713
+ console.warn(`[agents/browser] Failed to close browser session ${sessionId}`, error);
714
+ }
715
+ }
319
716
  /**
320
717
  * Capped at half the provider's keep-alive window: a browser with
321
718
  * continuous CDP traffic must never look older than the window the
@@ -466,12 +863,14 @@ async function _retire(current) {
466
863
  * was created and closed while this create was in flight.
467
864
  */
468
865
  async function _createAndCommit() {
469
- const info = await _classPrivateFieldGet2(_adapter, this).create();
866
+ const info = await _classPrivateFieldGet2(_adapter, this).create({ owner: _assertClassBrand(_Browser_brand, this, _internalHostOwner).call(this) });
470
867
  const now = Date.now();
868
+ const signature = _classPrivateFieldGet2(_adapter, this).internalHostsSignature;
471
869
  const stored = {
472
870
  sessionId: info.sessionId,
473
871
  createdAt: now,
474
- updatedAt: now
872
+ updatedAt: now,
873
+ ...signature !== void 0 ? { internalHosts: signature } : {}
475
874
  };
476
875
  let winner;
477
876
  let restarted;
@@ -527,6 +926,6 @@ function activeTargets(scopes, scope, sessionId) {
527
926
  return targets;
528
927
  }
529
928
  //#endregion
530
- export { browserRun as i, DEFAULT_BROWSER_SCOPE as n, MAX_BROWSER_SCOPES as r, Browser as t };
929
+ export { InternalHostRouter as a, browserRun as i, DEFAULT_BROWSER_SCOPE as n, MAX_INTERNAL_HOSTS as o, MAX_BROWSER_SCOPES as r, Browser as t };
531
930
 
532
- //# sourceMappingURL=browser-DccPw_wW.js.map
931
+ //# sourceMappingURL=browser-1eUbPvC2.js.map