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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-1eUbPvC2.js","names":["loopback"],"sources":["../src/browser/internal-hosts.ts","../src/browser/providers/provider-adapter.ts","../src/browser/providers/browser-run.ts","../src/browser/browser.ts"],"sourcesContent":["/**\n * Internal hostnames: sites only this agent's browser can open.\n *\n * A host maps a hostname such as `preview.internal` to code in your agent\n * (`fetch`) or to a deployed Worker (`worker`). Browser Run sends the\n * browser's requests for that hostname to it instead of the internet, so a\n * page can be served from a sandbox port or a workspace folder, without exposing anything publicly.\n *\n * How a request travels: Browser Run accepts only entrypoints of your own\n * Worker as routes, and keeps them for the whole browser session (observed\n * across redeploys and Durable Object eviction; Browser Run doesn't document\n * this yet). So every `fetch` host routes to {@link InternalHostRouter}, which\n * you export from your main module. Its props say which agent and browser\n * own the host, and nothing else. It calls the agent's `fetchInternalHost()`\n * over RPC, and the agent's `Browser` runs the host's `fetch`. `Agent` has\n * that method built in; a plain Durable Object adds it (see\n * {@link InternalHostMessage}). A `worker` host goes straight to the Worker\n * you gave it.\n */\n\nimport { Validator, type OutputUnit, type Schema } from \"@cfworker/json-schema\";\nimport { WorkerEntrypoint } from \"cloudflare:workers\";\n\n/** The most hostnames one browser can have. A Browser Run limit. */\nexport const MAX_INTERNAL_HOSTS = 8;\n\n// ── Schemas ────────────────────────────────────────────────────────────────\n//\n// Standard Schema and Standard JSON Schema (standardschema.dev) are small\n// interfaces meant to be copied rather than depended on. zod 4.2+, ArkType,\n// and Valibot (through its adapter) implement both.\n\n/** A Standard Schema: something that validates a value. */\nexport interface StandardSchemaV1<Input = unknown, Output = Input> {\n readonly \"~standard\": {\n readonly version: 1;\n readonly vendor: string;\n readonly validate: (\n value: unknown\n ) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;\n readonly types?:\n | { readonly input: Input; readonly output: Output }\n | undefined;\n };\n}\n\ntype StandardSchemaResult<Output> =\n | { readonly value: Output; readonly issues?: undefined }\n | {\n readonly issues: ReadonlyArray<{\n readonly message: string;\n readonly path?: ReadonlyArray<\n PropertyKey | { readonly key: PropertyKey }\n >;\n }>;\n };\n\n/** A Standard JSON Schema: something that can describe itself as JSON Schema. */\nexport interface StandardJSONSchemaV1 {\n readonly \"~standard\": {\n readonly jsonSchema: {\n readonly input: (options: {\n readonly target: string;\n }) => Record<string, unknown>;\n };\n };\n}\n\n/** A typed schema: validates (Standard Schema) and describes itself (Standard JSON Schema). */\nexport type TypedTargetSchema = StandardSchemaV1 & StandardJSONSchemaV1;\n\n/** A plain JSON Schema object, such as TypeBox output or a hand-written one. */\nexport type JsonSchemaObject = { readonly [key: string]: unknown };\n\n/**\n * What the model passes to `browser.assignHost()` for a host. A typed schema (zod\n * 4.2+, ArkType, Valibot) types `fetch`'s `target`; a plain JSON Schema\n * leaves it `unknown`.\n */\nexport type TargetSchema = TypedTargetSchema | JsonSchemaObject;\n\n/** The value a schema validates to: typed for a Standard Schema, else `unknown`. */\nexport type TargetOf<S> =\n S extends StandardSchemaV1<unknown, infer Output> ? Output : unknown;\n\n// ── Hosts ──────────────────────────────────────────────────────────────────\n\n/** A host served by code in your agent. */\nexport interface InternalHost<S = unknown> {\n /** Shown to the model next to the hostname. */\n description?: string;\n /**\n * What the model passes to `browser.assignHost()` to point this host somewhere,\n * as a schema. Omit it for a host that always serves the same thing.\n *\n * The model picks this value, so if part of it becomes a path, URL or\n * command in `fetch`, limit it in the schema (an enum, a pattern, a range)\n * rather than trusting it.\n */\n target?: S & TargetSchema;\n /**\n * Serves every browser request to this hostname. Runs in your agent, with\n * the target the model last assigned (validated by `target`), or\n * `undefined` when the host has no `target` schema.\n *\n * Any http page open in the browser can send requests here, not only\n * pages on this host: a page on another internal hostname, or an http\n * site the guardrails allow, can POST to `http://preview.internal`. Chrome\n * doesn't send `Sec-Fetch-*` headers to http hosts, so if a request has\n * side effects, check that its `Origin` is this host.\n *\n * If it throws, the browser gets a 502 and the error is logged, not shown\n * to the page.\n */\n fetch: (request: Request, target: TargetOf<S>) => Promise<Response>;\n worker?: never;\n}\n\n/**\n * A host served by a deployed Worker directly, without going through your\n * agent: a service binding, or an entrypoint of your own Worker. Not a\n * Dynamic Worker or Durable Object stub — Browser Run can't hold those.\n */\nexport interface InternalWorkerHost {\n /** Shown to the model next to the hostname. */\n description?: string;\n /**\n * The Worker that serves this hostname: a service binding, or an\n * entrypoint of your own Worker. Requests go straight to it, without\n * waking your agent.\n *\n * An entrypoint passed as is (`this.ctx.exports.DocsSite`) is called with\n * empty props; to give it props, call it yourself\n * (`this.ctx.exports.DocsSite({ props })`). A Durable Object stub doesn't\n * type-check: Browser Run accepts one, then resets every request.\n */\n worker: Fetcher & { id?: never };\n target?: never;\n fetch?: never;\n}\n\n/** The entrypoint loopback for {@link InternalHostRouter}: `this.ctx.exports.InternalHostRouter`. */\nexport type InternalHostRouterLoopback = (init: {\n props: InternalHostRouterProps;\n}) => Fetcher;\n\n/**\n * `browserRun()`'s `internalHosts`: hostnames only this browser can open.\n *\n * Fixed when a browser starts. Adding or removing a hostname replaces the\n * browser (the model sees `restarted`); changing a host's `fetch`,\n * `description` or `target` doesn't. Nor does pointing a `worker` host at\n * a different Worker: the running browser keeps the old one until it is\n * replaced, so call `browser.close()` after such a change.\n */\nexport interface InternalHostsOptions<S = Record<string, unknown>> {\n /**\n * Your Worker's {@link InternalHostRouter} export, as\n * `this.ctx.exports.InternalHostRouter`. Needed for `fetch` hosts.\n */\n router?: InternalHostRouterLoopback;\n /**\n * At most {@link MAX_INTERNAL_HOSTS} hostnames: bare and lowercase, no\n * scheme, port, or path, ending in a suffix that can't be a real site:\n * `.internal`, `.test`, `.example` or `.invalid`, like `preview.internal`.\n */\n hosts: { [K in keyof S]: InternalHost<S[K]> | InternalWorkerHost };\n}\n\n/** A host as erased internally, once its schema type no longer matters. */\nexport type AnyInternalHost = InternalHost<unknown> | InternalWorkerHost;\n\n/** One host, as the host API and the model see it. */\nexport interface InternalHostInfo {\n /** The hostname, such as `preview.internal`. */\n host: string;\n /** Where the browser opens it: `http://<host>/`. */\n url: string;\n description?: string;\n /** Whether the model can point it somewhere with `browser.assignHost()`. */\n assignable: boolean;\n /** The JSON Schema `browser.assignHost()` checks the target against. */\n targetSchema?: Record<string, unknown>;\n /** The target last assigned, as assigned (before the schema transforms it). */\n target?: unknown;\n}\n\nconst HOSTNAME =\n /^(?=.{1,253}$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/;\n\n/**\n * Suffixes reserved so they never resolve on the internet. A real domain\n * would be served by the host over http but reach the real site over https\n * (and Chrome tries https first), so internal hostnames must use one.\n * Single labels like `preview` would work, but Browser Run doesn't route\n * labels that are real TLDs (`docs`, `app`) and gives no error for them, so\n * they're rejected rather than checked against a TLD list.\n */\nconst RESERVED_SUFFIXES = [\".internal\", \".test\", \".example\", \".invalid\"];\n\n/** Why `host` can't be an internal hostname, or undefined when it can. */\nfunction hostnameProblem(host: string): string | undefined {\n if (host.includes(\"://\")) return \"drop the scheme\";\n if (host.includes(\"/\")) return \"drop the path\";\n if (host.includes(\":\")) return \"drop the port (only port 80 is routed)\";\n if (host !== host.toLowerCase()) return \"use lowercase\";\n // HOSTNAME alone would accept this: its labels may be all digits.\n if (/^\\d+(\\.\\d+){3}$/.test(host)) return \"use a name, not an IP address\";\n if (host === \"localhost\" || host.endsWith(\".localhost\")) {\n return \"Chrome sends localhost to the browser's own machine\";\n }\n if (!HOSTNAME.test(host)) return \"not a valid hostname\";\n if (!RESERVED_SUFFIXES.some((suffix) => host.endsWith(suffix))) {\n return `use a name that can't be a real site: end it in ${RESERVED_SUFFIXES.join(\", \")}`;\n }\n return undefined;\n}\n\n/**\n * Check `internalHosts` when the provider is created, so a mistake fails at\n * startup instead of at the first browser launch.\n */\nexport function validateInternalHosts(options: InternalHostsOptions): void {\n const hosts = Object.entries(options.hosts) as [string, AnyInternalHost][];\n if (hosts.length === 0) {\n throw new Error(\"internalHosts.hosts needs at least one hostname\");\n }\n if (hosts.length > MAX_INTERNAL_HOSTS) {\n throw new Error(\n `internalHosts has ${hosts.length} hostnames; Browser Run allows at most ${MAX_INTERNAL_HOSTS} per browser`\n );\n }\n for (const [host, config] of hosts) {\n const problem = hostnameProblem(host);\n if (problem) {\n throw new Error(`Internal hostname \"${host}\": ${problem}`);\n }\n const hasFetch = typeof config.fetch === \"function\";\n const hasWorker = config.worker !== undefined;\n if (hasFetch === hasWorker) {\n throw new Error(\n `Internal hostname \"${host}\" needs exactly one of fetch or worker`\n );\n }\n if (hasWorker && config.target !== undefined) {\n throw new Error(\n `Internal hostname \"${host}\": target needs fetch; a worker host can't be assigned`\n );\n }\n if (hasFetch && typeof options.router !== \"function\") {\n throw new Error(\n `Internal hostname \"${host}\" uses fetch, which needs internalHosts.router: this.ctx.exports.InternalHostRouter (and \\`export { InternalHostRouter } from \"agents/browser\"\\` in your main module)`\n );\n }\n if (config.target !== undefined) targetJsonSchema(host, config.target);\n }\n}\n\n/**\n * Identifies the set of hostnames and how each is routed. A browser started\n * with a different set is replaced: Browser Run fixes routes at launch.\n */\nexport function internalHostsSignature(\n options: InternalHostsOptions | undefined\n): string | undefined {\n if (!options) return undefined;\n const hosts = Object.entries(options.hosts) as [string, AnyInternalHost][];\n return hosts\n .map(([host, config]) => `${host}=${config.worker ? \"worker\" : \"fetch\"}`)\n .sort()\n .join(\",\");\n}\n\n/**\n * The route Browser Run gets for a `worker` host. A `ctx.exports`\n * entrypoint is a loopback stub that Browser Run can't take as is, so it's\n * called (with empty props) to get a Fetcher it can.\n */\nexport function workerRoute(worker: Fetcher): Fetcher {\n if (typeof worker !== \"function\") return worker;\n const loopback = worker as unknown as (init: { props: object }) => Fetcher;\n return loopback({ props: {} });\n}\n\n// ── Targets ────────────────────────────────────────────────────────────────\n\nfunction isStandardSchema(schema: TargetSchema): schema is TypedTargetSchema {\n return typeof schema === \"object\" && schema !== null && \"~standard\" in schema;\n}\n\n/** A host's target schema as JSON Schema, for the model and for plain validation. */\nexport function targetJsonSchema(\n host: string,\n schema: TargetSchema\n): Record<string, unknown> {\n if (!isStandardSchema(schema)) {\n if (typeof schema !== \"object\" || schema === null) {\n throw new Error(`Internal hostname \"${host}\": target must be a schema`);\n }\n return schema as Record<string, unknown>;\n }\n const { jsonSchema } = schema[\"~standard\"];\n if (!jsonSchema?.input) {\n throw new Error(\n `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`\n );\n }\n try {\n return jsonSchema.input({ target: \"draft-07\" });\n } catch {\n return jsonSchema.input({ target: \"draft-2020-12\" });\n }\n}\n\nconst plainValidators = new WeakMap<object, Validator>();\n\nfunction formatIssues(\n issues: ReadonlyArray<{ message: string; path?: string }>\n): string {\n return issues\n .map((issue) =>\n issue.path ? `${issue.path}: ${issue.message}` : issue.message\n )\n .join(\"; \");\n}\n\nfunction pathString(\n path: ReadonlyArray<PropertyKey | { readonly key: PropertyKey }> | undefined\n): string | undefined {\n if (!path?.length) return undefined;\n return path\n .map((part) => String(typeof part === \"object\" ? part.key : part))\n .join(\".\");\n}\n\nfunction plainIssues(errors: OutputUnit[]) {\n const specific = errors.filter((error) => error.keyword !== \"properties\");\n return (specific.length > 0 ? specific : errors).map((error) => ({\n message: error.error,\n path:\n error.instanceLocation.replace(/^#\\/?/, \"\").replaceAll(\"/\", \".\") ||\n undefined\n }));\n}\n\n/**\n * Validate a target against a host's schema. Returns the validated value\n * (a Standard Schema's output, which may differ from its input), or a\n * message saying what's wrong.\n */\nexport async function validateTarget(\n schema: TargetSchema,\n value: unknown\n): Promise<{ ok: true; value: unknown } | { ok: false; error: string }> {\n if (isStandardSchema(schema)) {\n const result = await schema[\"~standard\"].validate(value);\n if (result.issues) {\n return {\n ok: false,\n error: formatIssues(\n result.issues.map((issue) => ({\n message: issue.message,\n path: pathString(issue.path)\n }))\n )\n };\n }\n return { ok: true, value: result.value };\n }\n let validator = plainValidators.get(schema);\n if (!validator) {\n validator = new Validator(schema as unknown as Schema, \"7\", false);\n plainValidators.set(schema, validator);\n }\n const result = validator.validate(value);\n return result.valid\n ? { ok: true, value }\n : { ok: false, error: formatIssues(plainIssues(result.errors)) };\n}\n\n// ── Router ─────────────────────────────────────────────────────────────────\n\n/**\n * What {@link InternalHostRouter} is given for one hostname: which agent and\n * browser own it. Browser Run keeps these for the whole session, across\n * redeploys, so they hold names only, never secrets or config.\n */\nexport type InternalHostRouterProps = {\n readonly v: 1;\n /** The agent's class name, as exported from the Worker. */\n readonly className: string;\n /** The agent's name. */\n readonly name: string;\n /** The name of the `Browser` that owns the host. */\n readonly browser: string;\n /** The hostname this route serves. */\n readonly host: string;\n};\n\n/**\n * One browser request to an internal hostname, as {@link InternalHostRouter}\n * hands it to the agent's `fetchInternalHost()`.\n *\n * `Agent` has `fetchInternalHost()` built in. A plain Durable Object adds it\n * and passes the message to its `Browser`:\n *\n * ```ts\n * fetchInternalHost(message: InternalHostMessage) {\n * return this.browser.fetchInternalHost(message);\n * }\n * ```\n */\nexport type InternalHostMessage = {\n /** The name of the `Browser` that owns the host. */\n readonly browser: string;\n /** The hostname, such as `preview.internal`. */\n readonly host: string;\n /** The browser's request. */\n readonly request: Request;\n};\n\nexport function isInternalHostMessage(\n value: unknown\n): value is InternalHostMessage {\n if (typeof value !== \"object\" || value === null) return false;\n const message = value as Partial<InternalHostMessage>;\n return (\n typeof message.browser === \"string\" &&\n typeof message.host === \"string\" &&\n message.request instanceof Request\n );\n}\n\n/** A plain-text response the browser shows for a failed internal request. */\nexport function internalHostError(status: number, message: string): Response {\n return new Response(message, {\n status,\n headers: { \"content-type\": \"text/plain; charset=utf-8\" }\n });\n}\n\n/** What {@link InternalHostRouter} calls on the agent that owns a host. */\ntype InternalHostTarget = {\n fetchInternalHost(message: InternalHostMessage): Promise<unknown>;\n};\n\ntype OwnerNamespace = {\n getByName(name: string): InternalHostTarget;\n};\n\n/**\n * Whether an RPC call failed because the agent has no `fetchInternalHost()`.\n * workerd says: `The RPC receiver does not implement the method\n * \"fetchInternalHost\".` If that wording changes, the router falls back to its\n * generic 502, which names the method too.\n */\nfunction isMissingMethod(error: unknown): boolean {\n return (\n error instanceof Error &&\n error.message.includes(\"fetchInternalHost\") &&\n /does not implement|is not a function/.test(error.message)\n );\n}\n\n/**\n * Carries a browser's requests for an internal hostname to the agent that\n * owns it. Export it from your Worker's main module, so\n * `this.ctx.exports.InternalHostRouter` exists:\n *\n * ```ts\n * export { InternalHostRouter } from \"agents/browser\";\n * ```\n *\n * It trusts its props: Browser Run sets them from your routes, but another\n * Worker in your account with a service binding to this entrypoint could\n * set its own and reach any agent's hosts. That Worker could already call\n * your agents directly, so this grants nothing new.\n *\n * It holds no logic of its own: it calls the agent's `fetchInternalHost()`\n * with an {@link InternalHostMessage}, and the agent's `Browser` runs the\n * host's `fetch`. `Agent` has `fetchInternalHost()` built in; a plain\n * Durable Object adds it (see {@link InternalHostMessage}).\n *\n * @experimental The API may change before it stabilizes.\n */\nexport class InternalHostRouter extends WorkerEntrypoint<\n unknown,\n InternalHostRouterProps\n> {\n override async fetch(request: Request): Promise<Response> {\n const props = this.ctx.props as\n | Partial<InternalHostRouterProps>\n | undefined;\n if (!isRouterProps(props)) {\n return internalHostError(\n 502,\n \"InternalHostRouter was called without route props. Only Browser Run calls it, with the routes browserRun({ internalHosts }) gives it.\"\n );\n }\n const exports = (\n this.ctx as unknown as { exports?: Record<string, unknown> }\n ).exports;\n const namespace = exports?.[props.className] as OwnerNamespace | undefined;\n if (!namespace?.getByName) {\n return internalHostError(\n 502,\n `${props.host}: the agent class \"${props.className}\" isn't exported from this Worker`\n );\n }\n try {\n const response = await namespace.getByName(props.name).fetchInternalHost({\n browser: props.browser,\n host: props.host,\n request\n });\n if (response instanceof Response) return response;\n return internalHostError(\n 502,\n `${props.host}: the agent didn't answer with a Response`\n );\n } catch (error) {\n if (isMissingMethod(error)) {\n return internalHostError(\n 502,\n `${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); }`\n );\n }\n console.error(\n `[agents/browser] ${props.className} failed to serve ${props.host}`,\n error\n );\n return internalHostError(\n 502,\n `${props.host}: the agent failed to serve this request`\n );\n }\n }\n}\n\nfunction isRouterProps(\n props: Partial<InternalHostRouterProps> | undefined\n): props is InternalHostRouterProps {\n return (\n props?.v === 1 &&\n typeof props.className === \"string\" &&\n typeof props.name === \"string\" &&\n typeof props.browser === \"string\" &&\n typeof props.host === \"string\"\n );\n}\n","/**\n * The internal seam between {@link Browser} and the service that runs its\n * browser. A provider is the public, plain-data config a host passes\n * (`browserRun(env.BROWSER)`); its adapter is the code that does the work.\n * `Browser` keeps everything that doesn't depend on the provider — the\n * durable record, locks, restart detection, scopes, and activity touches —\n * and calls the adapter for the rest.\n *\n * Not exported from `agents/browser`: the provider set is closed, and this\n * interface may change as providers are added.\n */\n\nimport type { CdpConnection } from \"../cdp-connection\";\nimport type { InternalHostsOptions } from \"../internal-hosts\";\nimport type { BrowserLiveView, LiveViewMode } from \"../live-view\";\nimport type { SearchableCdpSpec } from \"../spec\";\n\n/**\n * Thrown by {@link BrowserProviderAdapter.connect} when the session no longer\n * exists upstream (closed, expired, or reclaimed). `Browser` treats it as\n * \"this browser is lost\" and replaces it; any other error is passed on.\n */\nexport class BrowserSessionGoneError extends Error {\n constructor(\n readonly sessionId: string,\n options?: { cause?: unknown }\n ) {\n super(`Browser session gone: ${sessionId}`, options);\n this.name = \"BrowserSessionGoneError\";\n }\n}\n\n/** Which agent and browser own a session's internal hostnames. */\nexport interface InternalHostOwner {\n /** The agent's class name, as exported from the Worker. */\n readonly className: string;\n /** The agent's name. */\n readonly name: string;\n /** The `Browser`'s name. */\n readonly browser: string;\n}\n\nexport interface BrowserProviderAdapter {\n /**\n * Start a new browser session. `owner` is set when the provider has\n * internal hostnames, so their routes can find the `Browser` again.\n */\n create(context?: { owner?: InternalHostOwner }): Promise<{\n sessionId: string;\n }>;\n /** The provider's internal hostnames, when it has any. */\n readonly internalHosts?: InternalHostsOptions;\n /**\n * Identifies the hostnames and how each is routed. A stored session made\n * with a different signature is replaced, since routes are fixed when a\n * session starts.\n */\n readonly internalHostsSignature?: string;\n /**\n * Whether the session still exists. `true` means alive and `false` means\n * definitely gone. Throw when the answer is unknown (a 5xx, a network\n * error): `Browser` discards the browser on `false`, so a flaky probe must\n * never report it.\n */\n isAlive(sessionId: string): Promise<boolean>;\n /**\n * Open a CDP connection to the session. Throws\n * {@link BrowserSessionGoneError} when the session is gone.\n */\n connect(\n sessionId: string,\n options: { timeoutMs?: number; onActivity?: () => void }\n ): Promise<CdpConnection>;\n /** End the session. A session that's already gone counts as closed. */\n close(sessionId: string): Promise<void>;\n /** The Chrome DevTools Protocol description the session's browser serves. */\n spec(sessionId: string): Promise<SearchableCdpSpec>;\n /**\n * Live View URLs for the session's open tabs, or `undefined` when the\n * session is gone.\n */\n liveView(\n sessionId: string,\n mode?: LiveViewMode\n ): Promise<BrowserLiveView | undefined>;\n /**\n * How long the provider keeps an idle session before reclaiming it.\n * `Browser` refreshes the record's `updatedAt` at most every half window.\n * `undefined` when idle sessions are never reclaimed.\n */\n readonly keepAliveMs?: number;\n}\n","/**\n * The Browser Run provider: `browserRun(env.BROWSER, options)` and the\n * adapter that runs a {@link Browser}'s browser on Cloudflare Browser Run.\n */\n\nimport {\n acquireBrowserSession,\n type BrowserBinding,\n type BrowserSessionGuardrails,\n closeBrowserSession,\n connectBrowserSession,\n isMissingBrowserSession,\n listBrowserTargets\n} from \"../browser-run\";\nimport {\n workerRoute,\n internalHostsSignature,\n validateInternalHosts,\n type AnyInternalHost,\n type InternalHostsOptions\n} from \"../internal-hosts\";\nimport { createLiveView } from \"../live-view\";\nimport { loadCdpSpec } from \"../spec\";\nimport {\n BrowserSessionGoneError,\n type BrowserProviderAdapter,\n type InternalHostOwner\n} from \"./provider-adapter\";\n\n/**\n * Default `keep_alive` for a `Browser` on Browser Run: 10 minutes. Long\n * enough to survive quiet spells between agent turns, while an abandoned\n * browser bills for at most 10 idle minutes (a whole day of Workers Free\n * browser time).\n */\nexport const BROWSER_SESSION_KEEP_ALIVE_DEFAULT_MS = 600_000;\n\n/**\n * Browser Run's `keep_alive` maximum: 20 minutes. Browser Run rejects\n * anything longer.\n */\nexport const BROWSER_SESSION_KEEP_ALIVE_MAX_MS = 1_200_000;\n\n/**\n * Browser Run settings applied every time a browser is created — including\n * when a lost browser is replaced — so options like guardrails survive a\n * restart.\n */\nexport interface BrowserRunOptions<S = Record<string, unknown>> {\n /**\n * Platform `keep_alive` in milliseconds: how long an idle browser lives.\n * Defaults to {@link BROWSER_SESSION_KEEP_ALIVE_DEFAULT_MS} (10 minutes).\n * Browser Run accepts up to {@link BROWSER_SESSION_KEEP_ALIVE_MAX_MS}\n * (20 minutes).\n */\n keepAliveMs?: number;\n /** Opt into Browser Run session recording (rrweb capture). */\n recording?: boolean;\n /**\n * Hostname guardrails, fixed at launch for every connection. They don't\n * apply to `internalHosts`: `allowedDomains: []` leaves the browser able\n * to open internal hostnames only.\n */\n guardrails?: BrowserSessionGuardrails;\n /**\n * Hostnames only this browser can open, served by your agent or a Worker\n * instead of the internet. Plain http on port 80; no HTTPS or WebSockets.\n * Deployed Workers only: in local dev the browser starts without them.\n *\n * Any http page open in the browser can send requests to these hosts, so\n * a host whose requests have side effects should check `Origin` (see\n * `InternalHost.fetch`).\n *\n * @example\n * ```ts\n * browserRun(this.env.BROWSER, {\n * internalHosts: {\n * router: this.ctx.exports.InternalHostRouter,\n * hosts: {\n * \"preview.internal\": {\n * description: \"A dev server in your sandbox\",\n * target: z.object({ port: z.number().int() }),\n * fetch: (request, { port }) => this.sandboxPort(port, request)\n * }\n * }\n * }\n * });\n * ```\n */\n internalHosts?: InternalHostsOptions<S>;\n}\n\n/** A {@link Browser} provider backed by Cloudflare Browser Run. */\nexport interface BrowserRunProvider {\n readonly kind: \"browser-run\";\n readonly binding: BrowserBinding;\n readonly options: BrowserRunOptions;\n}\n\n/**\n * Run the browser on Cloudflare Browser Run.\n *\n * @example\n * ```ts\n * new Browser({ provider: browserRun(env.BROWSER, { recording: true }) });\n * ```\n */\nexport function browserRun<const S extends Record<string, unknown> = {}>(\n binding: BrowserBinding,\n options: BrowserRunOptions<S> = {}\n): BrowserRunProvider {\n const erased = options as BrowserRunOptions;\n if (erased.internalHosts) validateInternalHosts(erased.internalHosts);\n return { kind: \"browser-run\", binding, options: erased };\n}\n\nfunction isDataCloneError(error: unknown): boolean {\n return error instanceof Error && error.name === \"DataCloneError\";\n}\n\n/** One `outboundByHost` route per internal hostname. */\nfunction internalHostRoutes(\n internalHosts: InternalHostsOptions,\n owner: InternalHostOwner\n): Record<string, Fetcher> {\n const routes: Record<string, Fetcher> = {};\n const hosts = Object.entries(internalHosts.hosts) as [\n string,\n AnyInternalHost\n ][];\n for (const [host, config] of hosts) {\n if (config.worker) {\n routes[host] = workerRoute(config.worker);\n continue;\n }\n // validateInternalHosts made sure a fetch host has a router.\n routes[host] = internalHosts.router!({\n props: { v: 1, ...owner, host }\n });\n }\n return routes;\n}\n\n/** The code behind {@link browserRun}. */\nexport function browserRunAdapter(\n provider: BrowserRunProvider\n): BrowserProviderAdapter {\n const { binding, options } = provider;\n // Options are read on every use, like `recording` and `guardrails` below.\n const keepAliveMs = () =>\n options.keepAliveMs ?? BROWSER_SESSION_KEEP_ALIVE_DEFAULT_MS;\n\n return {\n get keepAliveMs() {\n return keepAliveMs();\n },\n\n internalHosts: options.internalHosts,\n internalHostsSignature: internalHostsSignature(options.internalHosts),\n\n async create(context) {\n const { internalHosts } = options;\n if (internalHosts && !context?.owner) {\n throw new Error(\n \"Internal hostnames need the Browser installed with Lifecycle.use() on a top-level agent\"\n );\n }\n try {\n return await acquireBrowserSession(binding, {\n keepAliveMs: keepAliveMs(),\n recording: options.recording,\n guardrails: options.guardrails,\n ...(internalHosts && context?.owner\n ? {\n outboundByHost: internalHostRoutes(internalHosts, context.owner)\n }\n : {})\n });\n } catch (error) {\n if (internalHosts && isDataCloneError(error)) {\n throw new Error(\n \"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\",\n { cause: error }\n );\n }\n throw error;\n }\n },\n\n async isAlive(sessionId) {\n try {\n await listBrowserTargets(binding, sessionId);\n return true;\n } catch (error) {\n if (isMissingBrowserSession(error)) return false;\n throw error;\n }\n },\n\n async connect(sessionId, connectOptions) {\n try {\n return await connectBrowserSession(binding, sessionId, connectOptions);\n } catch (error) {\n if (isMissingBrowserSession(error)) {\n throw new BrowserSessionGoneError(sessionId, { cause: error });\n }\n throw error;\n }\n },\n\n async close(sessionId) {\n try {\n await closeBrowserSession(binding, sessionId);\n } catch (error) {\n if (!isMissingBrowserSession(error)) throw error;\n }\n },\n\n spec(sessionId) {\n return loadCdpSpec({ browser: binding, sessionId });\n },\n\n async liveView(sessionId, mode) {\n try {\n const targets = await listBrowserTargets(binding, sessionId);\n return createLiveView(sessionId, targets, mode);\n } catch (error) {\n if (isMissingBrowserSession(error)) return undefined;\n throw error;\n }\n }\n };\n}\n","/**\n * `Browser`: one named, persistent browser for an agent or Durable Object.\n *\n * Its tabs and logins survive between agent runs. When the browser is lost\n * (closed, idle past `keep_alive`, or crashed), the next use starts a fresh\n * one and reports `restarted: true` so the model knows its earlier state is\n * gone.\n *\n * Where the browser runs is its provider: Browser Run is the only one today\n * (`browserRun` in ./providers/browser-run.ts). `Browser` holds everything\n * that doesn't depend on the provider and calls the provider's adapter for\n * the rest (see ./providers/provider-adapter.ts).\n */\n\nimport {\n LifecycleCapability,\n type LifecycleRouteContext\n} from \"../lifecycle/capability\";\nimport { browserCapabilityId } from \"./capability-id\";\nimport type { CdpConnection } from \"./cdp-connection\";\nimport {\n internalHostError,\n isInternalHostMessage,\n targetJsonSchema,\n validateTarget,\n type AnyInternalHost,\n type InternalHostInfo,\n type InternalHostMessage\n} from \"./internal-hosts\";\nimport type { BrowserLiveView, LiveViewMode } from \"./live-view\";\nimport {\n browserRunAdapter,\n type BrowserRunProvider\n} from \"./providers/browser-run\";\nimport {\n BrowserSessionGoneError,\n type BrowserProviderAdapter,\n type InternalHostOwner\n} from \"./providers/provider-adapter\";\nimport { BrowserSession, type BrowserSessionOptions } from \"./session\";\nimport {\n DurableBrowserSessionStore,\n type ListableBrowserSessionStore,\n type StoredBrowserSession\n} from \"./session-store\";\nimport type { SearchableCdpSpec } from \"./spec\";\n\n/**\n * Minimum interval between store `updatedAt` refreshes driven by CDP traffic\n * on connections returned by {@link Browser.connect}.\n */\nexport const SESSION_TOUCH_INTERVAL_MS = 60_000;\n\n/** The browser name used when a host doesn't pick one. */\nexport const DEFAULT_BROWSER_NAME = \"default\";\n\n/**\n * The scope used when a caller doesn't pick one: every caller that leaves\n * `scope` out shares this one active tab. See {@link BrowserConnectOptions}.\n */\nexport const DEFAULT_BROWSER_SCOPE = \"shared\";\n\n/**\n * Scope records kept per browser. Writing a new one past the cap deletes the\n * least recently used; a scope that comes back afterwards starts fresh.\n */\nexport const MAX_BROWSER_SCOPES = 100;\n\nconst NAMED_SESSION_KEY_PREFIX = \"browser:session:\";\n\n/** The store key holding the named browser's current record. */\nexport function namedBrowserSessionKey(name: string): string {\n return `${NAMED_SESSION_KEY_PREFIX}${name}`;\n}\n\n/**\n * Where a closed or lost browser's record moves: permanent evidence that the\n * name once owned a browser, so a later resolve reports `restarted: true`.\n * Kept outside {@link NAMED_SESSION_KEY_PREFIX} so listing that prefix\n * yields only names that currently own a browser. Grows with the number of\n * distinct names the host has ever used.\n */\nconst RETIRED_SESSION_KEY_PREFIX = \"browser:retired:\";\n\nfunction retiredBrowserSessionKey(name: string): string {\n return `${RETIRED_SESSION_KEY_PREFIX}${name}`;\n}\n\n/**\n * Where a browser's scope records live. The name's length is part of the\n * prefix, so one browser's prefix never matches another name's (`a` with\n * scope `b:c` vs. `a:b` with scope `c`).\n */\nfunction browserScopeKeyPrefix(name: string): string {\n return `browser:scope:${name.length}:${name}:`;\n}\n\n/**\n * Where a browser's internal-host targets live: one key per host, under a\n * prefix per browser (length-prefixed like scope records, so one browser's\n * prefix never matches another's). Kept apart from the session record, so\n * targets survive the browser being replaced.\n */\nfunction internalHostTargetsPrefix(name: string): string {\n return `browser:hosts:${name.length}:${name}:`;\n}\n\n/** Where a {@link Browser} gets its browser from. */\nexport type BrowserProvider = BrowserRunProvider;\n\n/** The adapter behind a provider: one explicit case per provider kind. */\nfunction providerAdapterFor(provider: BrowserProvider): BrowserProviderAdapter {\n switch (provider.kind) {\n case \"browser-run\":\n return browserRunAdapter(provider);\n }\n}\n\nexport interface BrowserOptions {\n /** Where the browser runs. Create one with `browserRun()`. */\n provider: BrowserProvider;\n /**\n * Which browser this is. Two `Browser` objects on the same Durable Object\n * need different names. Defaults to {@link DEFAULT_BROWSER_NAME}.\n */\n name?: string;\n /**\n * Where the browser's record lives. Defaults to a\n * {@link DurableBrowserSessionStore} over the host object's storage, which\n * requires installing the `Browser` with `Lifecycle.use()`. Passing a store\n * lets the `Browser` work without Lifecycle. It must implement `list`.\n */\n store?: ListableBrowserSessionStore;\n /** Default CDP command timeout for {@link Browser.connect}. */\n timeoutMs?: number;\n /**\n * Minimum interval between activity-driven `updatedAt` refreshes.\n * Defaults to {@link SESSION_TOUCH_INTERVAL_MS}, capped at half the\n * keep-alive window.\n *\n * @internal For tests.\n */\n touchIntervalMs?: number;\n}\n\nexport interface ResolvedBrowser {\n name: string;\n /** The provider's session id — host-side only, never model-visible. */\n sessionId: string;\n /**\n * `true` when this resolution had to create a fresh browser to replace one\n * that previously existed (was closed, or expired or died upstream). Page\n * state from the prior browser is gone; surface this loudly to the model.\n * `false` only on first-ever use of the name — nothing was lost.\n */\n restarted: boolean;\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface BrowserConnectOptions {\n /**\n * Who is driving. Connections with the same scope share an active tab, and\n * each scope keeps its own, so two conversations on one browser don't\n * drive each other's page. Cookies, logins, and the tabs themselves are\n * shared by every scope: a scope splits \"which tab is mine\", not the\n * browser. Defaults to {@link DEFAULT_BROWSER_SCOPE}.\n */\n scope?: string;\n}\n\nexport interface BrowserConnection {\n name: string;\n sessionId: string;\n /** The scope this connection works in; see {@link BrowserConnectOptions}. */\n scope: string;\n /**\n * `true` when this scope worked in an earlier browser that has since been\n * replaced, whichever scope's connection replaced it. Its tabs and page\n * state are gone; surface this loudly to the model. `false` on a scope's\n * first connection, even one that replaced a lost browser: the scope had\n * nothing to lose. This is per scope, not a browser-wide signal.\n */\n restarted: boolean;\n /**\n * The tab this scope last worked in, when it recorded one in this\n * browser.\n */\n activeTargetId?: string;\n /** Closing this connection does NOT close the browser. */\n cdp: CdpConnection;\n /**\n * Record the tab this scope is working in (or clear it with `undefined`).\n * Never resurrects: returns `false` when the browser was closed or\n * replaced since this connection resolved it.\n */\n setActiveTarget(targetId: string | undefined): Promise<boolean>;\n /** The tabs other scopes are working in, in this browser. */\n targetsInOtherScopes(): Promise<Set<string>>;\n /**\n * The Chrome DevTools Protocol description this browser serves, read from\n * the browser itself (cached).\n */\n spec(): Promise<SearchableCdpSpec>;\n}\n\n/**\n * A browser's internal hostnames (`browserRun({ internalHosts })`), for the\n * host and the browser tool. Assignments are stored with the agent, so they\n * survive the browser being replaced, eviction, and redeploys.\n */\nexport interface BrowserInternalHosts {\n /** Every hostname with its description, schema, and current target. */\n list(): Promise<InternalHostInfo[]>;\n /**\n * Point an assignable host somewhere: `target` is checked against the\n * host's `target` schema and stored. The browser's next request to the\n * host uses it. Throws with what's wrong when the target doesn't match.\n */\n assign(host: string, target: unknown): Promise<{ url: string }>;\n /** Forget a host's target; its requests answer 502 until assigned again. */\n clear(host: string): Promise<boolean>;\n /** The hostnames with their descriptions and schemas, without reading storage. */\n describe(): InternalHostInfo[];\n}\n\n/**\n * One named, persistent browser.\n *\n * Install it with `Lifecycle.use()` — on an Agent subclass or any Durable\n * Object that composes `Lifecycle` — and it stores its record in the\n * object's own storage. It schedules nothing: the provider reclaims idle\n * browsers itself (Browser Run once `keep_alive` elapses), and the next use\n * replaces the lost browser with `restarted: true`.\n *\n * Everything here is host-side. Models never see browser names, provider\n * session ids, or Live View URLs through this surface.\n *\n * Store discipline: locks wrap storage operations only — liveness probes and\n * provider create/close calls always happen outside any lock, with a commit\n * re-check to detect concurrent swaps.\n *\n * @experimental The API surface may change before stabilizing.\n */\nexport class Browser extends LifecycleCapability {\n /** Which browser this is; see {@link BrowserOptions.name}. */\n readonly name: string;\n readonly #options: BrowserOptions;\n readonly #key: string;\n readonly #adapter: BrowserProviderAdapter;\n #store?: ListableBrowserSessionStore;\n\n constructor(options: BrowserOptions) {\n const name = options.name ?? DEFAULT_BROWSER_NAME;\n super(browserCapabilityId(name));\n if (name.trim() === \"\") {\n throw new Error(\"Browser names must be non-empty\");\n }\n this.name = name;\n this.#options = options;\n this.#key = namedBrowserSessionKey(name);\n this.#adapter = providerAdapterFor(options.provider);\n }\n\n /**\n * Reattach to the browser when it is still alive, otherwise create a fresh\n * one. See {@link ResolvedBrowser.restarted} for the mortality signal.\n */\n async resolve(): Promise<ResolvedBrowser> {\n const { name } = this;\n const key = this.#key;\n\n // Dead-browser recovery consumes two attempts (retire, then create).\n for (let attempt = 0; attempt < 4; attempt++) {\n const existing = await this.#readStored();\n\n if (existing === undefined) {\n // No browser on record: create one. The commit reports whether the\n // name was ever used before (see #createAndCommit).\n const { session, restarted } = await this.#createAndCommit();\n return { name, restarted, ...resolvedFields(session) };\n }\n\n // Live entry on record — probe it outside any lock. A session started\n // with other internal hostnames counts as lost: its routes can't change.\n const routesMatch =\n existing.internalHosts === this.#adapter.internalHostsSignature;\n const alive =\n routesMatch && (await this.#adapter.isAlive(existing.sessionId));\n const lock = await this.#sessionStore.acquireLock(key);\n try {\n const current = await this.#sessionStore.get(key);\n if (current?.sessionId !== existing.sessionId) {\n continue; // swapped or retired while we probed — revalidate\n }\n if (alive) {\n const refreshed = { ...current, updatedAt: Date.now() };\n await this.#sessionStore.set(key, refreshed);\n return { name, restarted: false, ...resolvedFields(refreshed) };\n }\n // The browser died upstream (expired or reclaimed), or has stale\n // routes. Retire the record under this lock, so any resolver that\n // reads it during the replacement window sees the marker and\n // reports restarted: true too.\n await this.#retire(current);\n } finally {\n await lock.release();\n }\n if (!routesMatch) await this.#closeQuietly(existing.sessionId);\n // Re-enter the loop: the next attempt takes the create path.\n }\n\n throw new Error(`Browser \"${name}\" kept changing concurrently — retry`);\n }\n\n /**\n * Resolve the browser and open a CDP connection to it for one scope (see\n * {@link BrowserConnectOptions.scope}). Commands sent over the connection\n * refresh the record's `updatedAt` (throttled to\n * {@link SESSION_TOUCH_INTERVAL_MS}), so hosts can tell an actively used\n * browser from one the platform has likely reclaimed.\n *\n * If the browser expires between the liveness probe and the WebSocket\n * upgrade, the record is retired and the browser resolved once more, so\n * the caller gets a fresh browser instead of an error.\n */\n async connect(\n options: BrowserConnectOptions = {}\n ): Promise<BrowserConnection> {\n const scope = options.scope ?? DEFAULT_BROWSER_SCOPE;\n if (scope.trim() === \"\") {\n throw new Error(\"Browser scopes must be non-empty\");\n }\n let resolved = await this.resolve();\n let cdp: CdpConnection;\n try {\n cdp = await this.#open(resolved);\n } catch (error) {\n if (!(error instanceof BrowserSessionGoneError)) throw error;\n await this.#retireIfCurrent(resolved.sessionId);\n resolved = await this.resolve();\n cdp = await this.#open(resolved);\n }\n try {\n const entered = await this.#enterScope(scope, resolved.sessionId);\n return this.#connection(resolved, scope, cdp, entered);\n } catch (error) {\n cdp.disconnect();\n throw error;\n }\n }\n\n /**\n * Start a session on this browser for one run: connects on its first\n * call, resolves `sessionId: \"active\"` to the scope's tab, and reports\n * what happened to the browser when you close it. `browserTool` opens\n * one per run.\n */\n session(options?: BrowserSessionOptions): BrowserSession {\n return new BrowserSession(this, options);\n }\n\n #connection(\n resolved: ResolvedBrowser,\n scope: string,\n cdp: CdpConnection,\n entered: { restarted: boolean; activeTargetId?: string }\n ): BrowserConnection {\n const { sessionId } = resolved;\n return {\n name: resolved.name,\n sessionId,\n scope,\n restarted: entered.restarted,\n activeTargetId: entered.activeTargetId,\n cdp,\n setActiveTarget: (targetId) =>\n this.#updateScope(scope, sessionId, targetId),\n targetsInOtherScopes: async () =>\n activeTargets(await this.#listScopes(), scope, sessionId),\n spec: () => this.#adapter.spec(sessionId)\n };\n }\n\n async #open(resolved: ResolvedBrowser): Promise<CdpConnection> {\n let lastTouchAt = Date.now();\n let touchInFlight = false;\n return this.#adapter.connect(resolved.sessionId, {\n timeoutMs: this.#options.timeoutMs,\n onActivity: () => {\n const now = Date.now();\n if (touchInFlight || now - lastTouchAt < this.#touchIntervalMs) {\n return;\n }\n touchInFlight = true;\n lastTouchAt = now;\n void this.#touch(resolved.sessionId)\n .catch((error: unknown) => {\n console.warn(\n `[agents/browser] Failed to refresh activity for browser \"${this.name}\"`,\n error\n );\n })\n .finally(() => {\n touchInFlight = false;\n });\n }\n });\n }\n\n /**\n * Close the browser: retire the record (so the next resolve reports\n * `restarted: true`) and close its provider session. Returns `false` when\n * there was nothing to close.\n *\n * The provider close is best-effort: the retired record is the durable\n * outcome, and a browser whose close failed is unreachable through this\n * store, so the provider reclaims it once idle (Browser Run: after\n * `keepAliveMs`, at most 20 minutes).\n */\n async close(): Promise<boolean> {\n let stored: StoredBrowserSession | undefined;\n const lock = await this.#sessionStore.acquireLock(this.#key);\n try {\n const current = await this.#sessionStore.get(this.#key);\n if (!current) return false;\n stored = current;\n await this.#retire(current);\n } finally {\n await lock.release();\n }\n try {\n await this.#adapter.close(stored.sessionId);\n } catch (error) {\n console.warn(\n `[agents/browser] Failed to close browser session ${stored.sessionId}`,\n error\n );\n }\n return true;\n }\n\n /**\n * Mint Live View URLs for the browser's open tabs, fresh from a target\n * listing. `undefined` when the browser was never created, is closed, or\n * is gone. The URLs are bearer credentials with a short connect window\n * (~5 min) — hand them to a trusted human, never to the model, and never\n * store them: re-mint instead.\n */\n async liveView(options?: {\n mode?: LiveViewMode;\n }): Promise<BrowserLiveView | undefined> {\n const entry = await this.#sessionStore.get(this.#key);\n if (!entry) return undefined;\n const view = await this.#adapter.liveView(entry.sessionId, options?.mode);\n if (!view) return undefined;\n // A human is about to look: minting counts as activity. Target listing\n // yields to the network, so a concurrent close or replacement can retire\n // the browser mid-mint — the touch and the retire serialize on the key\n // lock, and a lost touch means the listed targets are already dead:\n // report the browser gone rather than handing out doomed links. Touch\n // *errors* stay best effort — a store blip must not break minting.\n try {\n if (!(await this.#touch(entry.sessionId))) return undefined;\n } catch (error) {\n console.warn(\n `[agents/browser] Failed to refresh activity for browser \"${this.name}\"`,\n error\n );\n }\n return view;\n }\n\n // ── Internal hostnames ───────────────────────────────────────────────────\n\n /**\n * The browser's internal hostnames, or `undefined` when its provider has\n * none. See {@link BrowserInternalHosts}.\n */\n get internalHosts(): BrowserInternalHosts | undefined {\n const config = this.#adapter.internalHosts;\n if (!config) return undefined;\n const hosts = config.hosts as Record<string, AnyInternalHost>;\n const describe = (): InternalHostInfo[] =>\n Object.entries(hosts).map(([host, entry]) => ({\n host,\n url: `http://${host}/`,\n ...(entry.description ? { description: entry.description } : {}),\n assignable: entry.target !== undefined,\n ...(entry.target !== undefined\n ? { targetSchema: targetJsonSchema(host, entry.target) }\n : {})\n }));\n const assignable = (host: string) => {\n const entry = hosts[host];\n if (!entry || !Object.hasOwn(hosts, host)) {\n throw new Error(\n `\"${host}\" isn't an internal hostname. The hostnames are: ${Object.keys(hosts).join(\", \")}`\n );\n }\n if (entry.target === undefined) {\n throw new Error(\n `\"${host}\" always serves the same thing; it has no target to assign`\n );\n }\n return entry.target;\n };\n return {\n describe,\n list: async () => {\n const prefix = internalHostTargetsPrefix(this.name);\n const stored = await this.lifecycle.storage.list({ prefix });\n return describe().map((info) => {\n const key = prefix + info.host;\n return stored.has(key) ? { ...info, target: stored.get(key) } : info;\n });\n },\n assign: async (host, target) => {\n const schema = assignable(host);\n if (target === undefined) {\n throw new Error(\n `browser.assignHost needs a target for ${host}; use browser.clearHost to unassign it`\n );\n }\n // Stored as JSON, so check what will be stored.\n const json: unknown = JSON.parse(JSON.stringify(target));\n const checked = await validateTarget(schema, json);\n if (!checked.ok) {\n throw new Error(`Invalid target for ${host}: ${checked.error}`);\n }\n await this.lifecycle.storage.put(this.#targetKey(host), json);\n return { url: `http://${host}/` };\n },\n clear: async (host) => {\n assignable(host);\n return this.lifecycle.storage.delete(this.#targetKey(host));\n }\n };\n }\n\n /**\n * Serve one browser request to an internal hostname. `InternalHostRouter`\n * calls the agent's `fetchInternalHost()` with the message; `Agent` passes\n * it here on its own. A plain Durable Object passes it on itself:\n *\n * ```ts\n * fetchInternalHost(message: InternalHostMessage) {\n * return this.browser.fetchInternalHost(message);\n * }\n * ```\n *\n * Answers 502 with the reason when this browser doesn't serve the host.\n */\n async fetchInternalHost(message: InternalHostMessage): Promise<Response> {\n if (message.browser !== this.name) {\n return internalHostError(\n 502,\n `${message.host} belongs to browser \"${message.browser}\", but was passed to browser \"${this.name}\"`\n );\n }\n return this.#serveInternalHost(message.host, message.request);\n }\n\n /**\n * `Agent.fetchInternalHost()` routes here through Lifecycle, so `Agent`\n * needn't import the browser.\n *\n * @internal Called by Lifecycle; not part of the host API.\n */\n async onRoute(context: LifecycleRouteContext): Promise<unknown> {\n const { payload } = context;\n if (!isInternalHostMessage(payload)) {\n throw new Error(`Browser \"${this.name}\" got an unknown routed message`);\n }\n return this.fetchInternalHost(payload);\n }\n\n async #serveInternalHost(host: string, request: Request): Promise<Response> {\n const hosts = this.#adapter.internalHosts?.hosts as\n | Record<string, AnyInternalHost>\n | undefined;\n const entry = hosts && Object.hasOwn(hosts, host) ? hosts[host] : undefined;\n if (!entry?.fetch) {\n return internalHostError(\n 502,\n `${host} is no longer an internal hostname of browser \"${this.name}\"`\n );\n }\n let target: unknown;\n if (entry.target !== undefined) {\n const assigned = await this.lifecycle.storage.get(this.#targetKey(host));\n if (assigned === undefined) {\n return internalHostError(\n 502,\n `${host} isn't pointed anywhere yet. Call browser.assignHost({ host: \"${host}\", target }) first.`\n );\n }\n // Validate again: the schema may have changed since the assignment.\n const checked = await validateTarget(entry.target, assigned);\n if (!checked.ok) {\n return internalHostError(\n 502,\n `${host}'s target no longer matches its schema (${checked.error}). Call browser.assignHost again.`\n );\n }\n target = checked.value;\n }\n const fetch = entry.fetch;\n try {\n const response = await this.lifecycle.runInHostContext(\n () => fetch(request, target),\n { request }\n );\n if (response instanceof Response) return response;\n return internalHostError(\n 502,\n `${host}: its fetch didn't return a Response`\n );\n } catch (error) {\n // Logged, not shown: the page on this host can read the response.\n console.error(`[agents/browser] ${host}: its fetch threw`, error);\n return internalHostError(502, `${host}: its fetch failed`);\n }\n }\n\n /**\n * Which agent and browser the session's routes lead back to. `undefined`\n * without internal hostnames.\n */\n #internalHostOwner(): InternalHostOwner | undefined {\n if (!this.#adapter.internalHosts) return undefined;\n const services = this.lifecycleServices;\n if (!services) {\n throw new Error(\n `Browser \"${this.name}\" has internal hostnames, which need it installed with Lifecycle.use()`\n );\n }\n if (services.routes.source !== undefined) {\n throw new Error(\n `Browser \"${this.name}\" has internal hostnames, which sub-agents don't support yet`\n );\n }\n return {\n className: services.className,\n name: services.name,\n browser: this.name\n };\n }\n\n /** The storage key of one host's target. */\n #targetKey(host: string): string {\n return internalHostTargetsPrefix(this.name) + host;\n }\n\n async #closeQuietly(sessionId: string): Promise<void> {\n try {\n await this.#adapter.close(sessionId);\n } catch (error) {\n console.warn(\n `[agents/browser] Failed to close browser session ${sessionId}`,\n error\n );\n }\n }\n\n // ── Internals ────────────────────────────────────────────────────────────\n\n /**\n * Capped at half the provider's keep-alive window: a browser with\n * continuous CDP traffic must never look older than the window the\n * provider uses to reclaim it.\n */\n get #touchIntervalMs(): number {\n const interval = this.#options.touchIntervalMs ?? SESSION_TOUCH_INTERVAL_MS;\n const { keepAliveMs } = this.#adapter;\n return keepAliveMs === undefined\n ? interval\n : Math.min(interval, Math.floor(keepAliveMs / 2));\n }\n\n /** Lazy: `lifecycle.storage` exists only once `Lifecycle.use()` ran. */\n get #sessionStore(): ListableBrowserSessionStore {\n this.#store ??=\n this.#options.store ??\n new DurableBrowserSessionStore(this.lifecycle.storage);\n return this.#store;\n }\n\n /**\n * Refresh `updatedAt` while the record still holds `sessionId`. Returns\n * false when a concurrent close or replacement already retired it.\n */\n #touch(sessionId: string): Promise<boolean> {\n return this.#update(sessionId, (current) => ({\n ...current,\n updatedAt: Date.now()\n }));\n }\n\n /**\n * Rewrite the record only while it still holds `sessionId`: a replaced or\n * retired record is never resurrected.\n */\n async #update(\n sessionId: string,\n change: (current: StoredBrowserSession) => StoredBrowserSession\n ): Promise<boolean> {\n const lock = await this.#sessionStore.acquireLock(this.#key);\n try {\n const current = await this.#sessionStore.get(this.#key);\n if (current?.sessionId !== sessionId) {\n return false; // replaced or gone — the caller's view is stale\n }\n await this.#sessionStore.set(this.#key, change(current));\n return true;\n } finally {\n await lock.release();\n }\n }\n\n // ── Scopes ───────────────────────────────────────────────────────────────\n //\n // Each scope has a small record next to the browser's: the browser it last\n // worked in (`sessionId`) and its tab there. All of a browser's scope\n // records share one lock, so claiming a tab can check the others.\n\n #scopeKey(scope: string): string {\n return `${browserScopeKeyPrefix(this.name)}${scope}`;\n }\n\n /** The lock every scope record of this browser is written under. */\n get #scopesLockKey(): string {\n return browserScopeKeyPrefix(this.name);\n }\n\n /** Every scope record of this browser, by scope. */\n async #listScopes(): Promise<Map<string, StoredBrowserSession>> {\n const prefix = browserScopeKeyPrefix(this.name);\n const entries = await this.#sessionStore.list(prefix);\n const scopes = new Map<string, StoredBrowserSession>();\n for (const [key, entry] of entries) {\n scopes.set(key.slice(prefix.length), entry);\n }\n return scopes;\n }\n\n /**\n * Start a connection's scope: a scope whose record names an earlier\n * browser was restarted, and its record moves to this one. A new record\n * past {@link MAX_BROWSER_SCOPES} evicts the least recently used.\n */\n async #enterScope(\n scope: string,\n sessionId: string\n ): Promise<{ restarted: boolean; activeTargetId?: string }> {\n const key = this.#scopeKey(scope);\n const lock = await this.#sessionStore.acquireLock(this.#scopesLockKey);\n try {\n const entry = await this.#sessionStore.get(key);\n const now = Date.now();\n if (entry?.sessionId === sessionId) {\n await this.#sessionStore.set(key, { ...entry, updatedAt: now });\n return { restarted: false, activeTargetId: entry.activeTargetId };\n }\n await this.#sessionStore.set(key, {\n sessionId,\n createdAt: now,\n updatedAt: now\n });\n if (!entry) await this.#evictScopes();\n return { restarted: entry !== undefined };\n } finally {\n await lock.release();\n }\n }\n\n /** Delete the least recently used scope records past the cap. */\n async #evictScopes(): Promise<void> {\n const scopes = await this.#listScopes();\n if (scopes.size <= MAX_BROWSER_SCOPES) return;\n const oldest = [...scopes]\n .sort(([, a], [, b]) => a.updatedAt - b.updatedAt)\n .slice(0, scopes.size - MAX_BROWSER_SCOPES);\n for (const [scope] of oldest) {\n await this.#sessionStore.delete(this.#scopeKey(scope));\n }\n }\n\n /**\n * Set a scope's tab (or clear it with `undefined`). Never resurrects:\n * does nothing (and returns `false`) once the browser this connection\n * resolved was closed or replaced.\n */\n async #updateScope(\n scope: string,\n sessionId: string,\n targetId: string | undefined\n ): Promise<boolean> {\n const key = this.#scopeKey(scope);\n const lock = await this.#sessionStore.acquireLock(this.#scopesLockKey);\n try {\n const current = await this.#sessionStore.get(this.#key);\n const entry = await this.#sessionStore.get(key);\n if (current?.sessionId !== sessionId || entry?.sessionId !== sessionId) {\n return false;\n }\n await this.#sessionStore.set(key, {\n ...entry,\n activeTargetId: targetId,\n updatedAt: Date.now()\n });\n return true;\n } finally {\n await lock.release();\n }\n }\n\n /** Retire the record if it still holds `sessionId`. */\n async #retireIfCurrent(sessionId: string): Promise<void> {\n const lock = await this.#sessionStore.acquireLock(this.#key);\n try {\n const current = await this.#sessionStore.get(this.#key);\n if (current?.sessionId === sessionId) await this.#retire(current);\n } finally {\n await lock.release();\n }\n }\n\n /**\n * Move the record to its retired marker. Callers hold the key lock, so a\n * resolver always sees either the record or the marker.\n */\n async #retire(current: StoredBrowserSession): Promise<void> {\n await this.#sessionStore.set(retiredBrowserSessionKey(this.name), {\n ...current,\n closedAt: Date.now()\n });\n await this.#sessionStore.delete(this.#key);\n }\n\n /**\n * Create a provider session (outside any lock) and commit it. The adapter\n * applies the provider's creation options. If a concurrent caller\n * committed an entry first, theirs wins and the redundant session is\n * closed best-effort.\n *\n * `restarted` is read under the commit lock: a retired marker — left by\n * close() or a dead-browser recovery — is evidence a prior browser\n * existed, and only first-ever use of the name is not a restart. Markers\n * are permanent, so reading at commit time also catches a browser that\n * was created and closed while this create was in flight.\n */\n async #createAndCommit(): Promise<{\n session: StoredBrowserSession;\n restarted: boolean;\n }> {\n const info = await this.#adapter.create({\n owner: this.#internalHostOwner()\n });\n const now = Date.now();\n const signature = this.#adapter.internalHostsSignature;\n const stored: StoredBrowserSession = {\n sessionId: info.sessionId,\n createdAt: now,\n updatedAt: now,\n ...(signature !== undefined ? { internalHosts: signature } : {})\n };\n\n let winner: StoredBrowserSession | undefined;\n let restarted: boolean;\n const lock = await this.#sessionStore.acquireLock(this.#key);\n try {\n restarted =\n (await this.#sessionStore.get(retiredBrowserSessionKey(this.name))) !==\n undefined;\n const current = await this.#sessionStore.get(this.#key);\n if (current === undefined) {\n await this.#sessionStore.set(this.#key, stored);\n } else {\n winner = current;\n }\n } finally {\n await lock.release();\n }\n\n if (winner) {\n try {\n await this.#adapter.close(stored.sessionId);\n } catch (error) {\n console.warn(\n `[agents/browser] Failed to close redundant browser session ${stored.sessionId}`,\n error\n );\n }\n return { session: winner, restarted };\n }\n return { session: stored, restarted };\n }\n\n async #readStored(): Promise<StoredBrowserSession | undefined> {\n const lock = await this.#sessionStore.acquireLock(this.#key);\n try {\n return await this.#sessionStore.get(this.#key);\n } finally {\n await lock.release();\n }\n }\n}\n\n/** A store record without fields that belong to scope records. */\nfunction resolvedFields(session: StoredBrowserSession) {\n const { sessionId, createdAt, updatedAt } = session;\n return { sessionId, createdAt, updatedAt };\n}\n\n/** The tabs scopes other than `scope` are working in, in `sessionId`. */\nfunction activeTargets(\n scopes: Map<string, StoredBrowserSession>,\n scope: string,\n sessionId: string\n): Set<string> {\n const targets = new Set<string>();\n for (const [other, entry] of scopes) {\n if (other === scope || entry.sessionId !== sessionId) continue;\n if (entry.activeTargetId) targets.add(entry.activeTargetId);\n }\n return targets;\n}\n"],"mappings":"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"}
@@ -1,4 +1,12 @@
1
- import { s as LifecycleCapability } from "./capability-runner-BiVUtjqC.js";
1
+ import {
2
+ d as LifecycleRouteContext,
3
+ s as LifecycleCapability
4
+ } from "./capability-runner-BiVUtjqC.js";
5
+ import {
6
+ n as InternalHostInfo,
7
+ r as InternalHostMessage,
8
+ s as InternalHostsOptions
9
+ } from "./internal-hosts-Kfp5A392.js";
2
10
  import {
3
11
  O as CdpConnection,
4
12
  a as ListableBrowserSessionStore,
@@ -6,7 +14,7 @@ import {
6
14
  m as BrowserSessionGuardrails,
7
15
  s as BrowserLiveView,
8
16
  u as LiveViewMode
9
- } from "./session-store-_IK6dV-w.js";
17
+ } from "./session-store-B7Ca-u1t.js";
10
18
 
11
19
  //#region src/browser/spec.d.ts
12
20
  /**
@@ -92,7 +100,7 @@ declare function loadCdpSpec(source: CdpSpecSource): Promise<SearchableCdpSpec>;
92
100
  * when a lost browser is replaced — so options like guardrails survive a
93
101
  * restart.
94
102
  */
95
- interface BrowserRunOptions {
103
+ interface BrowserRunOptions<S = Record<string, unknown>> {
96
104
  /**
97
105
  * Platform `keep_alive` in milliseconds: how long an idle browser lives.
98
106
  * Defaults to {@link BROWSER_SESSION_KEEP_ALIVE_DEFAULT_MS} (10 minutes).
@@ -102,8 +110,38 @@ interface BrowserRunOptions {
102
110
  keepAliveMs?: number;
103
111
  /** Opt into Browser Run session recording (rrweb capture). */
104
112
  recording?: boolean;
105
- /** Hostname guardrails, fixed at launch for every connection. */
113
+ /**
114
+ * Hostname guardrails, fixed at launch for every connection. They don't
115
+ * apply to `internalHosts`: `allowedDomains: []` leaves the browser able
116
+ * to open internal hostnames only.
117
+ */
106
118
  guardrails?: BrowserSessionGuardrails;
119
+ /**
120
+ * Hostnames only this browser can open, served by your agent or a Worker
121
+ * instead of the internet. Plain http on port 80; no HTTPS or WebSockets.
122
+ * Deployed Workers only: in local dev the browser starts without them.
123
+ *
124
+ * Any http page open in the browser can send requests to these hosts, so
125
+ * a host whose requests have side effects should check `Origin` (see
126
+ * `InternalHost.fetch`).
127
+ *
128
+ * @example
129
+ * ```ts
130
+ * browserRun(this.env.BROWSER, {
131
+ * internalHosts: {
132
+ * router: this.ctx.exports.InternalHostRouter,
133
+ * hosts: {
134
+ * "preview.internal": {
135
+ * description: "A dev server in your sandbox",
136
+ * target: z.object({ port: z.number().int() }),
137
+ * fetch: (request, { port }) => this.sandboxPort(port, request)
138
+ * }
139
+ * }
140
+ * }
141
+ * });
142
+ * ```
143
+ */
144
+ internalHosts?: InternalHostsOptions<S>;
107
145
  }
108
146
  /** A {@link Browser} provider backed by Cloudflare Browser Run. */
109
147
  interface BrowserRunProvider {
@@ -119,11 +157,140 @@ interface BrowserRunProvider {
119
157
  * new Browser({ provider: browserRun(env.BROWSER, { recording: true }) });
120
158
  * ```
121
159
  */
122
- declare function browserRun(
160
+ declare function browserRun<const S extends Record<string, unknown> = {}>(
123
161
  binding: BrowserBinding,
124
- options?: BrowserRunOptions
162
+ options?: BrowserRunOptions<S>
125
163
  ): BrowserRunProvider;
126
164
  //#endregion
165
+ //#region src/browser/session.d.ts
166
+ /**
167
+ * What a {@link BrowserSession} connects to. Usually a `Browser` installed on
168
+ * the host's Lifecycle.
169
+ */
170
+ interface BrowserSource {
171
+ /** The browser's name, for log messages. */
172
+ readonly name: string;
173
+ /**
174
+ * Reattach to the browser (or replace it) and open a CDP connection for
175
+ * a scope. A source that ignores `scope` shares one active tab between
176
+ * every caller.
177
+ */
178
+ connect(options?: BrowserConnectOptions): Promise<BrowserConnection>;
179
+ /**
180
+ * The browser's internal hostnames, when it has any. `browserTool` lists
181
+ * them and adds the hosts functions to `browser`.
182
+ */
183
+ readonly internalHosts?: BrowserInternalHosts;
184
+ }
185
+ interface BrowserSessionOptions {
186
+ /**
187
+ * Who is driving: sessions with the same scope share an active tab, and
188
+ * each scope keeps its own. Cookies and logins are shared by every scope.
189
+ * Defaults to the browser's default scope.
190
+ */
191
+ scope?: string;
192
+ /**
193
+ * The largest `Page.captureScreenshot` result `send` returns, measured as
194
+ * JSON. Defaults to 1,000,000 bytes.
195
+ */
196
+ maxResultBytes?: number;
197
+ }
198
+ /** `send`'s input. */
199
+ interface BrowserSendInput {
200
+ /** CDP method, e.g. `"Page.navigate"`. */
201
+ method: string;
202
+ /** CDP command parameters. */
203
+ params?: Record<string, unknown>;
204
+ /**
205
+ * `"active"` for the current tab, or a handle from `attachToTarget`. Omit
206
+ * for `Browser.*` and `Target.*` commands.
207
+ */
208
+ sessionId?: string;
209
+ /** Per-command timeout in milliseconds. */
210
+ timeoutMs?: number;
211
+ }
212
+ /** `attachToTarget`'s input. */
213
+ interface BrowserAttachInput {
214
+ /** Target id from `Target.getTargets` or `Target.createTarget`. */
215
+ targetId: string;
216
+ timeoutMs?: number;
217
+ }
218
+ /** `getDebugLog`'s input. */
219
+ interface BrowserDebugLogInput {
220
+ /** Most entries to return, newest last. Defaults to 50; 400 are kept. */
221
+ limit?: number;
222
+ }
223
+ /** A tab a page opened on its own during a session. */
224
+ interface BrowserNewTab {
225
+ targetId: string;
226
+ url?: string;
227
+ title?: string;
228
+ }
229
+ /**
230
+ * What happened to the browser during one session, beyond the results of its
231
+ * calls. {@link BrowserSession.close} returns it.
232
+ */
233
+ interface BrowserSessionReport {
234
+ /** The browser was replaced before this session; earlier page state is gone. */
235
+ restarted: boolean;
236
+ /** Tabs the page opened itself (popups, `target=_blank`). */
237
+ newTabs: BrowserNewTab[];
238
+ /**
239
+ * The tab this scope was working in was closed by someone else (another
240
+ * scope, or a person in Live View), so `"active"` will be a new blank tab.
241
+ */
242
+ tabClosed: boolean;
243
+ /** Another scope is working in this scope's active tab too. */
244
+ tabShared: boolean;
245
+ }
246
+ /** `sessionId` value that addresses the tab the agent is working in. */
247
+ declare const ACTIVE_PAGE_SESSION = "active";
248
+ /**
249
+ * One run's connection to a persistent browser, for one scope.
250
+ *
251
+ * It connects on its first call and stays connected until {@link close}.
252
+ * Concurrent first calls share one connect. Every method takes one options
253
+ * object, validated before anything touches the browser, because the input
254
+ * usually comes from model-written code.
255
+ *
256
+ * Create one per run with `browser.session({ scope })`, and close it when
257
+ * the run ends. The browser itself stays open.
258
+ *
259
+ * @experimental The API surface may change before stabilizing.
260
+ */
261
+ declare class BrowserSession {
262
+ #private;
263
+ constructor(browser: BrowserSource, options?: BrowserSessionOptions);
264
+ /**
265
+ * Send a CDP command and resolve with its result, without the JSON-RPC
266
+ * envelope. `sessionId: "active"` runs it in the scope's tab.
267
+ */
268
+ send(input: BrowserSendInput): Promise<unknown>;
269
+ /**
270
+ * Make an open tab the scope's active tab, and resolve with a `sessionId`
271
+ * handle for it that keeps working in later runs.
272
+ */
273
+ attachToTarget(input: BrowserAttachInput): Promise<{
274
+ sessionId: string;
275
+ }>;
276
+ /** The Chrome DevTools Protocol description the browser serves. */
277
+ spec(): Promise<SearchableCdpSpec>;
278
+ /** Recent CDP traffic on this session's connection, newest last. */
279
+ getDebugLog(input?: BrowserDebugLogInput): Promise<unknown[]>;
280
+ /** Forget the CDP traffic recorded so far. */
281
+ clearDebugLog(): Promise<null>;
282
+ /**
283
+ * End the session: save the scope's active tab, work out what happened to
284
+ * the browser, and close the connection. The browser stays open.
285
+ *
286
+ * Resolves with the report, or `undefined` when the session never
287
+ * connected. Never rejects: failures to read the browser's state are
288
+ * logged and leave the report incomplete. Later calls resolve with
289
+ * `undefined`, and other methods reject.
290
+ */
291
+ close(): Promise<BrowserSessionReport | undefined>;
292
+ }
293
+ //#endregion
127
294
  //#region src/browser/browser.d.ts
128
295
  /**
129
296
  * The scope used when a caller doesn't pick one: every caller that leaves
@@ -221,6 +388,30 @@ interface BrowserConnection {
221
388
  */
222
389
  spec(): Promise<SearchableCdpSpec>;
223
390
  }
391
+ /**
392
+ * A browser's internal hostnames (`browserRun({ internalHosts })`), for the
393
+ * host and the browser tool. Assignments are stored with the agent, so they
394
+ * survive the browser being replaced, eviction, and redeploys.
395
+ */
396
+ interface BrowserInternalHosts {
397
+ /** Every hostname with its description, schema, and current target. */
398
+ list(): Promise<InternalHostInfo[]>;
399
+ /**
400
+ * Point an assignable host somewhere: `target` is checked against the
401
+ * host's `target` schema and stored. The browser's next request to the
402
+ * host uses it. Throws with what's wrong when the target doesn't match.
403
+ */
404
+ assign(
405
+ host: string,
406
+ target: unknown
407
+ ): Promise<{
408
+ url: string;
409
+ }>;
410
+ /** Forget a host's target; its requests answer 502 until assigned again. */
411
+ clear(host: string): Promise<boolean>;
412
+ /** The hostnames with their descriptions and schemas, without reading storage. */
413
+ describe(): InternalHostInfo[];
414
+ }
224
415
  /**
225
416
  * One named, persistent browser.
226
417
  *
@@ -261,6 +452,13 @@ declare class Browser extends LifecycleCapability {
261
452
  * the caller gets a fresh browser instead of an error.
262
453
  */
263
454
  connect(options?: BrowserConnectOptions): Promise<BrowserConnection>;
455
+ /**
456
+ * Start a session on this browser for one run: connects on its first
457
+ * call, resolves `sessionId: "active"` to the scope's tab, and reports
458
+ * what happened to the browser when you close it. `browserTool` opens
459
+ * one per run.
460
+ */
461
+ session(options?: BrowserSessionOptions): BrowserSession;
264
462
  /**
265
463
  * Close the browser: retire the record (so the next resolve reports
266
464
  * `restarted: true`) and close its provider session. Returns `false` when
@@ -282,24 +480,60 @@ declare class Browser extends LifecycleCapability {
282
480
  liveView(options?: {
283
481
  mode?: LiveViewMode;
284
482
  }): Promise<BrowserLiveView | undefined>;
483
+ /**
484
+ * The browser's internal hostnames, or `undefined` when its provider has
485
+ * none. See {@link BrowserInternalHosts}.
486
+ */
487
+ get internalHosts(): BrowserInternalHosts | undefined;
488
+ /**
489
+ * Serve one browser request to an internal hostname. `InternalHostRouter`
490
+ * calls the agent's `fetchInternalHost()` with the message; `Agent` passes
491
+ * it here on its own. A plain Durable Object passes it on itself:
492
+ *
493
+ * ```ts
494
+ * fetchInternalHost(message: InternalHostMessage) {
495
+ * return this.browser.fetchInternalHost(message);
496
+ * }
497
+ * ```
498
+ *
499
+ * Answers 502 with the reason when this browser doesn't serve the host.
500
+ */
501
+ fetchInternalHost(message: InternalHostMessage): Promise<Response>;
502
+ /**
503
+ * `Agent.fetchInternalHost()` routes here through Lifecycle, so `Agent`
504
+ * needn't import the browser.
505
+ *
506
+ * @internal Called by Lifecycle; not part of the host API.
507
+ */
508
+ onRoute(context: LifecycleRouteContext): Promise<unknown>;
285
509
  }
286
510
  //#endregion
287
511
  export {
288
- BrowserProvider as a,
289
- ResolvedBrowser as c,
290
- browserRun as d,
291
- CdpField as f,
292
- loadCdpSpec as g,
293
- SearchableCdpSpec as h,
294
- BrowserOptions as i,
295
- BrowserRunOptions as l,
296
- CdpSpecSource as m,
512
+ CdpItems as C,
513
+ loadCdpSpec as E,
514
+ CdpField as S,
515
+ SearchableCdpSpec as T,
516
+ BrowserSessionReport as _,
517
+ BrowserOptions as a,
518
+ BrowserRunProvider as b,
519
+ MAX_BROWSER_SCOPES as c,
520
+ BrowserAttachInput as d,
521
+ BrowserDebugLogInput as f,
522
+ BrowserSessionOptions as g,
523
+ BrowserSession as h,
524
+ BrowserInternalHosts as i,
525
+ ResolvedBrowser as l,
526
+ BrowserSendInput as m,
297
527
  BrowserConnectOptions as n,
298
- DEFAULT_BROWSER_SCOPE as o,
299
- CdpItems as p,
528
+ BrowserProvider as o,
529
+ BrowserNewTab as p,
300
530
  BrowserConnection as r,
301
- MAX_BROWSER_SCOPES as s,
531
+ DEFAULT_BROWSER_SCOPE as s,
302
532
  Browser as t,
303
- BrowserRunProvider as u
533
+ ACTIVE_PAGE_SESSION as u,
534
+ BrowserSource as v,
535
+ CdpSpecSource as w,
536
+ browserRun as x,
537
+ BrowserRunOptions as y
304
538
  };
305
- //# sourceMappingURL=browser-DlXhSAdf.d.ts.map
539
+ //# sourceMappingURL=browser-D9RNpJuT.d.ts.map
@@ -0,0 +1,71 @@
1
+ import { v as BrowserSource } from "./browser-D9RNpJuT.js";
2
+
3
+ //#region src/browser/browser-tool.d.ts
4
+ interface BrowserToolOptions {
5
+ /**
6
+ * The browser the tool drives — usually a `Browser` installed on the
7
+ * host's Lifecycle. Tools built from the same `Browser` share it.
8
+ */
9
+ browser: BrowserSource;
10
+ /**
11
+ * Worker Loader binding the model's code runs in. Each run gets a fresh
12
+ * Dynamic Worker with no network access.
13
+ *
14
+ * Requires `"worker_loaders": [{ "binding": "LOADER" }]` in wrangler.jsonc.
15
+ */
16
+ loader: WorkerLoader;
17
+ /** How long one run may take, in milliseconds. Defaults to 60000 (60s). */
18
+ timeoutMs?: number;
19
+ /**
20
+ * Who is driving, when several chats or users share one `Browser`: runs
21
+ * with the same scope share an active tab, and each scope keeps its own.
22
+ * Cookies and logins are shared by every scope. Defaults to `"shared"`,
23
+ * one active tab for every tool that leaves it out.
24
+ */
25
+ scope?: string;
26
+ /**
27
+ * @deprecated Not needed any more: the tool no longer runs on a codemode
28
+ * facet. Ignored.
29
+ */
30
+ ctx?: DurableObjectState;
31
+ }
32
+ /** The model's input to `browserTool`: JavaScript to run. */
33
+ interface BrowserToolInput {
34
+ code: string;
35
+ }
36
+ /** What happened to the browser during a run, added to its result. */
37
+ interface BrowserReportFields {
38
+ /**
39
+ * Sentences telling the model what happened to its browser: it was
40
+ * restarted, its tab was closed by someone else, another scope is working
41
+ * in its tab too, or the page opened new tabs.
42
+ */
43
+ notice?: string;
44
+ }
45
+ /**
46
+ * The result of one `browserTool` run: the code's result, or the
47
+ * error it threw, plus what happened to the browser. Errors are returned,
48
+ * not thrown, so the model can read them and try again.
49
+ */
50
+ type BrowserToolOutput = (
51
+ | {
52
+ status: "completed" /** Identifies the run, for logs and UIs. */;
53
+ executionId: string;
54
+ result: unknown /** What the code wrote to the console. */;
55
+ logs?: string[];
56
+ }
57
+ | {
58
+ status: "error";
59
+ executionId: string;
60
+ error: string;
61
+ logs?: string[];
62
+ }
63
+ ) &
64
+ BrowserReportFields;
65
+ //#endregion
66
+ export {
67
+ BrowserToolOptions as n,
68
+ BrowserToolOutput as r,
69
+ BrowserToolInput as t
70
+ };
71
+ //# sourceMappingURL=browser-tool-BhjCcNVE.d.ts.map