agents 0.27.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/dist/abortable-C1SpfVoi.js +21 -0
  2. package/dist/abortable-C1SpfVoi.js.map +1 -0
  3. package/dist/{agent-routing-Bhdbvf7s.d.ts → agent-routing-QDcchcQP.d.ts} +10 -15
  4. package/dist/agent-routing.d.ts +1 -1
  5. package/dist/agent-tool-types.d.ts +1 -1
  6. package/dist/{agent-tools-bJZwBMfq.d.ts → agent-tools-DHL2XI7u.d.ts} +2 -2
  7. package/dist/agent-tools.d.ts +1 -1
  8. package/dist/ai-ahruGw7e.d.ts +239 -0
  9. package/dist/{ai-sdk-OoGkFy3s.js → ai-sdk-Bdsf6mSA.js} +2 -2
  10. package/dist/{ai-sdk-OoGkFy3s.js.map → ai-sdk-Bdsf6mSA.js.map} +1 -1
  11. package/dist/browser/ai.d.ts +8 -227
  12. package/dist/browser/ai.js +259 -1
  13. package/dist/browser/ai.js.map +1 -0
  14. package/dist/browser/index.d.ts +56 -46
  15. package/dist/browser/index.js +4 -361
  16. package/dist/browser/{ai-sdk.d.ts → tools/ai-sdk.d.ts} +7 -7
  17. package/dist/browser/{ai-sdk.js → tools/ai-sdk.js} +5 -4
  18. package/dist/browser/tools/ai-sdk.js.map +1 -0
  19. package/dist/browser/tools/pi.d.ts +111 -0
  20. package/dist/browser/tools/pi.js +119 -0
  21. package/dist/browser/tools/pi.js.map +1 -0
  22. package/dist/browser/{tanstack-ai.d.ts → tools/tanstack-ai.d.ts} +39 -37
  23. package/dist/browser/{tanstack-ai.js → tools/tanstack-ai.js} +73 -59
  24. package/dist/browser/tools/tanstack-ai.js.map +1 -0
  25. package/dist/browser-CEdNlsaD.js +481 -0
  26. package/dist/browser-CEdNlsaD.js.map +1 -0
  27. package/dist/{browser-tmkcAAI3.d.ts → browser-D30VlpcA.d.ts} +63 -37
  28. package/dist/{browser-tool-CBqs6hqa.js → browser-tool-4iomavs_.js} +145 -49
  29. package/dist/browser-tool-4iomavs_.js.map +1 -0
  30. package/dist/{browser-tool-Bn-nzJQI.d.ts → browser-tool-vtrPOV8e.d.ts} +24 -5
  31. package/dist/{capability-B4WbF81e.js → capability-DBAKc6UZ.js} +1 -3
  32. package/dist/capability-DBAKc6UZ.js.map +1 -0
  33. package/dist/{capability-runner-O5VbQzqg.d.ts → capability-runner-BiVUtjqC.d.ts} +1 -7
  34. package/dist/chat/index.d.ts +4 -4
  35. package/dist/chat/index.js +1 -1
  36. package/dist/chat-sdk/index.d.ts +1 -1
  37. package/dist/chat-sdk/index.js +1 -1
  38. package/dist/{client-DS3Dz9l0.d.ts → client-BGIQKJhX.d.ts} +2 -2
  39. package/dist/{client-1tEQ1Oqz.js → client-BnYbNT3z.js} +3 -4
  40. package/dist/client-BnYbNT3z.js.map +1 -0
  41. package/dist/client.d.ts +1 -1
  42. package/dist/{connector-58XDGvc3.js → connector-CtZylXdk.js} +3 -603
  43. package/dist/connector-CtZylXdk.js.map +1 -0
  44. package/dist/{connector-Cl-VqPmA.d.ts → connector-DEXzVoaY.d.ts} +9 -374
  45. package/dist/{conversations-DH0ut_8n.d.ts → conversations-FjHDRZyD.d.ts} +4 -4
  46. package/dist/{current-agent-C07C5o5Y.d.ts → current-agent-CKgT5d1O.d.ts} +3 -5
  47. package/dist/current-agent-DhoDkSnH.js.map +1 -1
  48. package/dist/{durable-object-lifecycle-DRqp475_.d.ts → durable-object-lifecycle-DOI_bzCH.d.ts} +2 -2
  49. package/dist/experimental/channels/index.d.ts +2 -2
  50. package/dist/experimental/channels/index.js +2 -2
  51. package/dist/experimental/channels/web/index.d.ts +2 -2
  52. package/dist/experimental/channels/web/index.js +1 -1
  53. package/dist/{gateway-BDC07gYO.d.ts → gateway-BFO6vRDn.d.ts} +2 -2
  54. package/dist/harness/ai-sdk/index.d.ts +2 -2
  55. package/dist/harness/ai-sdk/index.js +1 -1
  56. package/dist/harness/container/index.d.ts +2 -2
  57. package/dist/harness/container/index.js +3 -3
  58. package/dist/harness/opencode/index.d.ts +1 -1
  59. package/dist/harness/opencode/index.js +2 -2
  60. package/dist/harness/pi/index.d.ts +2 -2
  61. package/dist/harness/pi/index.js +3 -3
  62. package/dist/harness/store/index.d.ts +1 -1
  63. package/dist/harness/store/index.js +1 -1
  64. package/dist/harness/think/index.d.ts +2 -2
  65. package/dist/harness/think/index.js +363 -171
  66. package/dist/harness/think/index.js.map +1 -1
  67. package/dist/{index-D0A5tbWu.d.ts → index-BEL3ZV-s.d.ts} +2 -6
  68. package/dist/{index-Ib_4dwOo.d.ts → index-CBt8oRJt.d.ts} +3 -5
  69. package/dist/index.d.ts +4 -4
  70. package/dist/index.js +2 -2
  71. package/dist/input-schema-BQykTEp_.js +20 -0
  72. package/dist/input-schema-BQykTEp_.js.map +1 -0
  73. package/dist/{input-schema-DNrpTu49.js → input-schema-D2QAtHZH.js} +3 -3
  74. package/dist/input-schema-D2QAtHZH.js.map +1 -0
  75. package/dist/internal_context.d.ts +1 -1
  76. package/dist/lifecycle/index.d.ts +3 -3
  77. package/dist/lifecycle/index.js +2 -2
  78. package/dist/{lifecycle-CFu6OV03.js → lifecycle-DGorpAmA.js} +2 -4
  79. package/dist/lifecycle-DGorpAmA.js.map +1 -0
  80. package/dist/mcp/client/index.d.ts +1 -1
  81. package/dist/mcp/client/index.js +1 -1
  82. package/dist/mcp/index.d.ts +1 -1
  83. package/dist/mcp/index.js +2 -2
  84. package/dist/models/ai-sdk/index.js +1 -1
  85. package/dist/models/opencode/index.js +1 -1
  86. package/dist/models/pi-ai/index.js +1 -1
  87. package/dist/{options-pwTNq9z6.d.ts → options-CiIsJHXR.d.ts} +2 -6
  88. package/dist/queue/index.d.ts +1 -1
  89. package/dist/queue/index.js +1 -1
  90. package/dist/{queue-BB-zfIYF.js → queue-CcIjVYTE.js} +2 -2
  91. package/dist/{queue-BB-zfIYF.js.map → queue-CcIjVYTE.js.map} +1 -1
  92. package/dist/react.d.ts +2 -2
  93. package/dist/routing/index.d.ts +2 -2
  94. package/dist/routing/index.js +1 -1
  95. package/dist/{scheduler-CrFCnv_J.d.ts → scheduler-BCLESHSC.d.ts} +2 -12
  96. package/dist/{scheduler-DZ3xQfeK.js → scheduler-FIHv2-kJ.js} +2 -4
  97. package/dist/scheduler-FIHv2-kJ.js.map +1 -0
  98. package/dist/schedules/index.d.ts +1 -1
  99. package/dist/schedules/index.js +1 -1
  100. package/dist/serializable.d.ts +1 -1
  101. package/dist/session-store-Do4xkDcf.js +606 -0
  102. package/dist/session-store-Do4xkDcf.js.map +1 -0
  103. package/dist/session-store-DoPY12xM.d.ts +383 -0
  104. package/dist/sessions/index.d.ts +1 -1
  105. package/dist/sessions/index.js +1 -1
  106. package/dist/{sessions-B9pNuxdT.js → sessions-_vN5BEZX.js} +2 -2
  107. package/dist/{sessions-B9pNuxdT.js.map → sessions-_vN5BEZX.js.map} +1 -1
  108. package/dist/{src-DgCbZtIR.js → src-CQxJFv4l.js} +8 -8
  109. package/dist/src-CQxJFv4l.js.map +1 -0
  110. package/dist/state/index.d.ts +1 -1
  111. package/dist/state/index.js +1 -3
  112. package/dist/state/index.js.map +1 -1
  113. package/dist/{store-DLeqKuj3.d.ts → store-BKM4Xm9M.d.ts} +23 -1
  114. package/dist/{store-vgcPYo8K.js → store-CslVzfN7.js} +28 -2
  115. package/dist/store-CslVzfN7.js.map +1 -0
  116. package/dist/streams/index.d.ts +1 -1
  117. package/dist/streams/index.js +1 -1
  118. package/dist/{streams-CITClVer.js → streams-lsHMJhRW.js} +2 -2
  119. package/dist/{streams-CITClVer.js.map → streams-lsHMJhRW.js.map} +1 -1
  120. package/dist/{streams-fbG61qM8.d.ts → streams-vrCtbUIn.d.ts} +2 -2
  121. package/dist/sub-routing.d.ts +1 -1
  122. package/dist/tasks/index.d.ts +1 -1
  123. package/dist/tasks/index.js +1 -1
  124. package/dist/{tasks-CkTZNaV1.js → tasks-BN_5AWqI.js} +2 -2
  125. package/dist/{tasks-CkTZNaV1.js.map → tasks-BN_5AWqI.js.map} +1 -1
  126. package/dist/{tasks-PtlB4Uyn.d.ts → tasks-E89KdwkS.d.ts} +2 -2
  127. package/dist/{tool-BPJEbwaX.d.ts → tool-CTp72Jb_.d.ts} +4 -4
  128. package/dist/tool-CxKsvxjm.js +1126 -0
  129. package/dist/tool-CxKsvxjm.js.map +1 -0
  130. package/dist/tool-DoVberpN.d.ts +301 -0
  131. package/dist/{tool-3PA8xjB_.js → tool-cQRkwSZZ.js} +6 -17
  132. package/dist/tool-cQRkwSZZ.js.map +1 -0
  133. package/dist/tool-helpers-O5BDVEAY.js +58 -0
  134. package/dist/tool-helpers-O5BDVEAY.js.map +1 -0
  135. package/dist/voice/index.d.ts +2 -2
  136. package/dist/web/fetch/index.d.ts +89 -0
  137. package/dist/web/fetch/index.js +20 -0
  138. package/dist/web/fetch/index.js.map +1 -0
  139. package/dist/web/fetch/tools/ai-sdk.d.ts +80 -0
  140. package/dist/web/fetch/tools/ai-sdk.js +47 -0
  141. package/dist/web/fetch/tools/ai-sdk.js.map +1 -0
  142. package/dist/web/fetch/tools/pi.d.ts +84 -0
  143. package/dist/web/fetch/tools/pi.js +88 -0
  144. package/dist/web/fetch/tools/pi.js.map +1 -0
  145. package/dist/web/fetch/tools/tanstack-ai.d.ts +78 -0
  146. package/dist/web/fetch/tools/tanstack-ai.js +45 -0
  147. package/dist/web/fetch/tools/tanstack-ai.js.map +1 -0
  148. package/dist/{websearch → web/search}/index.d.ts +1 -1
  149. package/dist/{websearch → web/search}/index.js +1 -1
  150. package/dist/{websearch → web/search}/tools/ai-sdk.d.ts +2 -2
  151. package/dist/{websearch → web/search}/tools/ai-sdk.js +3 -3
  152. package/dist/web/search/tools/ai-sdk.js.map +1 -0
  153. package/dist/{websearch → web/search}/tools/pi.d.ts +3 -3
  154. package/dist/{websearch → web/search}/tools/pi.js +2 -2
  155. package/dist/web/search/tools/pi.js.map +1 -0
  156. package/dist/{websearch → web/search}/tools/tanstack-ai.d.ts +2 -2
  157. package/dist/{websearch → web/search}/tools/tanstack-ai.js +3 -3
  158. package/dist/web/search/tools/tanstack-ai.js.map +1 -0
  159. package/dist/websockets/index.d.ts +2 -2
  160. package/dist/websockets/index.js +1 -1
  161. package/dist/{websockets-CmENGb-Z.js → websockets-C3zy8YR0.js} +3 -5
  162. package/dist/websockets-C3zy8YR0.js.map +1 -0
  163. package/dist/workflow-types.d.ts +1 -1
  164. package/dist/workflows.d.ts +1 -1
  165. package/dist/workflows.js +1 -1
  166. package/docs/browse-the-web.md +26 -1
  167. package/docs/fetch-the-web.md +276 -0
  168. package/docs/harnesses/pi.md +25 -0
  169. package/docs/index.md +99 -76
  170. package/docs/lifecycle.md +2 -3
  171. package/docs/queue.md +2 -3
  172. package/docs/scheduling.md +2 -6
  173. package/docs/search-the-web.md +2 -0
  174. package/package.json +44 -19
  175. package/dist/ai-fm7xx1BK.js +0 -312
  176. package/dist/ai-fm7xx1BK.js.map +0 -1
  177. package/dist/browser/ai-sdk.js.map +0 -1
  178. package/dist/browser/index.js.map +0 -1
  179. package/dist/browser/tanstack-ai.js.map +0 -1
  180. package/dist/browser-tool-CBqs6hqa.js.map +0 -1
  181. package/dist/capability-B4WbF81e.js.map +0 -1
  182. package/dist/client-1tEQ1Oqz.js.map +0 -1
  183. package/dist/connector-58XDGvc3.js.map +0 -1
  184. package/dist/input-schema-DNrpTu49.js.map +0 -1
  185. package/dist/lifecycle-CFu6OV03.js.map +0 -1
  186. package/dist/scheduler-DZ3xQfeK.js.map +0 -1
  187. package/dist/src-DgCbZtIR.js.map +0 -1
  188. package/dist/store-vgcPYo8K.js.map +0 -1
  189. package/dist/tool-3PA8xjB_.js.map +0 -1
  190. package/dist/websearch/tools/ai-sdk.js.map +0 -1
  191. package/dist/websearch/tools/pi.js.map +0 -1
  192. package/dist/websearch/tools/tanstack-ai.js.map +0 -1
  193. package/dist/websockets-CmENGb-Z.js.map +0 -1
package/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "bin": {
13
13
  "agents": "./dist/cli.js"
14
14
  },
15
- "version": "0.27.0",
15
+ "version": "0.28.0",
16
16
  "license": "MIT",
17
17
  "repository": {
18
18
  "directory": "packages/agents",
@@ -383,34 +383,59 @@
383
383
  "require": "./dist/browser/ai.js"
384
384
  },
385
385
  "./browser/ai-sdk": {
386
- "types": "./dist/browser/ai-sdk.d.ts",
387
- "import": "./dist/browser/ai-sdk.js",
388
- "require": "./dist/browser/ai-sdk.js"
386
+ "types": "./dist/browser/tools/ai-sdk.d.ts",
387
+ "import": "./dist/browser/tools/ai-sdk.js",
388
+ "require": "./dist/browser/tools/ai-sdk.js"
389
389
  },
390
390
  "./browser/tanstack-ai": {
391
- "types": "./dist/browser/tanstack-ai.d.ts",
392
- "import": "./dist/browser/tanstack-ai.js",
393
- "require": "./dist/browser/tanstack-ai.js"
391
+ "types": "./dist/browser/tools/tanstack-ai.d.ts",
392
+ "import": "./dist/browser/tools/tanstack-ai.js",
393
+ "require": "./dist/browser/tools/tanstack-ai.js"
394
394
  },
395
395
  "./websearch": {
396
- "types": "./dist/websearch/index.d.ts",
397
- "import": "./dist/websearch/index.js",
398
- "require": "./dist/websearch/index.js"
396
+ "types": "./dist/web/search/index.d.ts",
397
+ "import": "./dist/web/search/index.js",
398
+ "require": "./dist/web/search/index.js"
399
399
  },
400
400
  "./websearch/pi": {
401
- "types": "./dist/websearch/tools/pi.d.ts",
402
- "import": "./dist/websearch/tools/pi.js",
403
- "require": "./dist/websearch/tools/pi.js"
401
+ "types": "./dist/web/search/tools/pi.d.ts",
402
+ "import": "./dist/web/search/tools/pi.js",
403
+ "require": "./dist/web/search/tools/pi.js"
404
404
  },
405
405
  "./websearch/ai-sdk": {
406
- "types": "./dist/websearch/tools/ai-sdk.d.ts",
407
- "import": "./dist/websearch/tools/ai-sdk.js",
408
- "require": "./dist/websearch/tools/ai-sdk.js"
406
+ "types": "./dist/web/search/tools/ai-sdk.d.ts",
407
+ "import": "./dist/web/search/tools/ai-sdk.js",
408
+ "require": "./dist/web/search/tools/ai-sdk.js"
409
409
  },
410
410
  "./websearch/tanstack-ai": {
411
- "types": "./dist/websearch/tools/tanstack-ai.d.ts",
412
- "import": "./dist/websearch/tools/tanstack-ai.js",
413
- "require": "./dist/websearch/tools/tanstack-ai.js"
411
+ "types": "./dist/web/search/tools/tanstack-ai.d.ts",
412
+ "import": "./dist/web/search/tools/tanstack-ai.js",
413
+ "require": "./dist/web/search/tools/tanstack-ai.js"
414
+ },
415
+ "./browser/pi": {
416
+ "types": "./dist/browser/tools/pi.d.ts",
417
+ "import": "./dist/browser/tools/pi.js",
418
+ "require": "./dist/browser/tools/pi.js"
419
+ },
420
+ "./webfetch": {
421
+ "types": "./dist/web/fetch/index.d.ts",
422
+ "import": "./dist/web/fetch/index.js",
423
+ "require": "./dist/web/fetch/index.js"
424
+ },
425
+ "./webfetch/pi": {
426
+ "types": "./dist/web/fetch/tools/pi.d.ts",
427
+ "import": "./dist/web/fetch/tools/pi.js",
428
+ "require": "./dist/web/fetch/tools/pi.js"
429
+ },
430
+ "./webfetch/ai-sdk": {
431
+ "types": "./dist/web/fetch/tools/ai-sdk.d.ts",
432
+ "import": "./dist/web/fetch/tools/ai-sdk.js",
433
+ "require": "./dist/web/fetch/tools/ai-sdk.js"
434
+ },
435
+ "./webfetch/tanstack-ai": {
436
+ "types": "./dist/web/fetch/tools/tanstack-ai.d.ts",
437
+ "import": "./dist/web/fetch/tools/tanstack-ai.js",
438
+ "require": "./dist/web/fetch/tools/tanstack-ai.js"
414
439
  },
415
440
  "./voice": {
416
441
  "types": "./dist/voice/index.d.ts",
@@ -1,312 +0,0 @@
1
- import { t as __DO_NOT_USE_WILL_BREAK__agentContext } from "./current-agent-DhoDkSnH.js";
2
- import "./internal_context.js";
3
- import { c as browserExtract, f as browserScrape, i as DurableBrowserSessionStore, l as browserLinks, s as browserContent, t as BrowserConnector, u as browserMarkdown } from "./connector-58XDGvc3.js";
4
- import { t as redactBase64Payloads } from "./base64-redaction-C1Uu29t5.js";
5
- import { tool } from "ai";
6
- import { z } from "zod";
7
- import { DynamicWorkerExecutor, createCodemodeRuntime, truncateResult } from "@cloudflare/codemode";
8
- //#region src/browser/tool-helpers.ts
9
- function browserScreenshotOutput(value) {
10
- const outer = typeof value === "object" && value !== null ? value : null;
11
- const result = outer && typeof outer.result === "object" && outer.result !== null ? outer.result : outer;
12
- if (result?.type !== "browser_screenshot" || typeof result.mediaType !== "string" || typeof result.data !== "string") return null;
13
- return result;
14
- }
15
- function transformBrowserResult(value) {
16
- return browserScreenshotOutput(value) ? value : truncateResult(redactBase64Payloads(value));
17
- }
18
- /**
19
- * @param notes Sentences appended when a screenshot collapses the output to
20
- * text, so notices on the output aren't lost with it.
21
- */
22
- function browserExecuteModelOutput(output, notes = []) {
23
- const screenshot = browserScreenshotOutput(output);
24
- if (screenshot) {
25
- const approximateBytes = Math.floor(screenshot.data.length * 3 / 4);
26
- return {
27
- type: "text",
28
- value: [`Screenshot captured successfully (${screenshot.mediaType}, approximately ${approximateBytes.toLocaleString()} bytes); the image is kept for the UI and omitted here.`, ...notes].join(" ")
29
- };
30
- }
31
- const redacted = redactBase64Payloads(typeof output === "object" && output !== null && !Array.isArray(output) ? (({ calls: _calls, ...rest }) => ({
32
- ...rest,
33
- ...Array.isArray(rest.logs) ? { logs: truncateResult(rest.logs) } : {}
34
- }))(output) : output);
35
- try {
36
- const serialized = JSON.stringify(redacted);
37
- return {
38
- type: "json",
39
- value: serialized === void 0 ? null : JSON.parse(serialized)
40
- };
41
- } catch {
42
- return {
43
- type: "text",
44
- value: "Browser execution completed, but its result could not be serialized for model context."
45
- };
46
- }
47
- }
48
- /**
49
- * The Durable Object state to build the runtime in: the explicit `ctx` if
50
- * given, otherwise the current Agent's `ctx` (via `getCurrentAgent()`), so
51
- * `createBrowserRuntime` can be called from an Agent method without threading
52
- * `this.ctx` through.
53
- */
54
- function resolveCtx(options) {
55
- if (options.ctx) return options.ctx;
56
- return (__DO_NOT_USE_WILL_BREAK__agentContext.getStore()?.agent)?.ctx;
57
- }
58
- //#endregion
59
- //#region src/browser/ai.ts
60
- let didWarnExperimental = false;
61
- let didDebugQuickActionSkip = false;
62
- function connectorOptions(options, ctx) {
63
- if (options.cdpUrl) return {
64
- cdpUrl: options.cdpUrl,
65
- cdpHeaders: options.cdpHeaders,
66
- timeout: options.timeout
67
- };
68
- if (!options.browser) throw new Error("Either 'browser' (Fetcher binding) or 'cdpUrl' must be provided");
69
- return {
70
- browser: options.browser,
71
- store: options.store ?? new DurableBrowserSessionStore(ctx.storage),
72
- session: options.session,
73
- timeout: options.timeout
74
- };
75
- }
76
- /**
77
- * Create the browser codemode runtime: the `browser_execute` tool plus the
78
- * runtime handle and connector for host-side wiring (approvals, session info,
79
- * sweeps).
80
- *
81
- * @example
82
- * ```ts
83
- * export class MyAgent extends Agent<Env> {
84
- * get browser() {
85
- * return createBrowserRuntime({
86
- * ctx: this.ctx,
87
- * browser: this.env.BROWSER,
88
- * loader: this.env.LOADER,
89
- * session: { mode: "dynamic" }
90
- * });
91
- * }
92
- *
93
- * @callable()
94
- * async closeBrowserSession() {
95
- * await this.browser.connector.closeSession();
96
- * }
97
- * }
98
- * ```
99
- */
100
- function createBrowserRuntime(options) {
101
- if (!didWarnExperimental) {
102
- didWarnExperimental = true;
103
- console.warn("[agents/browser] Browser tools are experimental and may change in a future release.");
104
- }
105
- const ctx = resolveCtx(options);
106
- if (!ctx) throw new Error("createBrowserRuntime requires a Durable Object 'ctx' — pass it explicitly, or call from within an Agent so it can be resolved via getCurrentAgent()");
107
- const connector = new BrowserConnector(ctx, connectorOptions(options, ctx));
108
- const runtime = createCodemodeRuntime({
109
- ctx,
110
- executor: new DynamicWorkerExecutor({
111
- loader: options.loader,
112
- timeout: options.timeout
113
- }),
114
- connectors: [connector],
115
- name: options.name ?? "browser",
116
- transformResult: transformBrowserResult
117
- });
118
- const isKitesurf = !options.cdpUrl && options.session?.browser === "kitesurf";
119
- const tools = { browser_execute: {
120
- ...runtime.tool({ connectorHints: { cdp: isKitesurf ? "Kitesurf one-shot Browser CDP. Call codemode.describe(\"cdp\") for connector types and Kitesurf execution rules. codemode.search indexes connector methods and snippets, not underlying CDP commands. Complete the task in one execution and do not pause. Return screenshots as { type: \"browser_screenshot\", mediaType: \"image/png\", data: screenshot.data }." : "Browser CDP. Return screenshots as { type: 'browser_screenshot', mediaType: 'image/png', data: screenshot.data }; the UI keeps the image while the model receives a compact summary." } }),
121
- toModelOutput: ({ output }) => browserExecuteModelOutput(output)
122
- } };
123
- if (options.quickActions !== false && (!isKitesurf || options.quickActions !== void 0)) {
124
- const qa = typeof options.quickActions === "object" ? options.quickActions : {};
125
- const quickActionBrowser = qa.browser ?? options.browser;
126
- if (quickActionBrowser) Object.assign(tools, createQuickActionTools({
127
- browser: quickActionBrowser,
128
- actions: qa.actions,
129
- maxChars: qa.maxChars,
130
- options: qa.options
131
- }));
132
- else if (options.quickActions) throw new Error("quickActions requires a Browser Run binding — set 'browser' (env.BROWSER) or 'quickActions.browser'");
133
- else if (!didDebugQuickActionSkip) {
134
- didDebugQuickActionSkip = true;
135
- console.debug("[agents/browser] Quick Action tools skipped — no Browser Run binding (only 'cdpUrl' is set). Pass 'browser' or 'quickActions.browser' to enable them.");
136
- }
137
- }
138
- return {
139
- runtime,
140
- connector,
141
- tools
142
- };
143
- }
144
- /**
145
- * Create AI SDK tools for browser automation via CDP code mode.
146
- *
147
- * Returns a `ToolSet` with a single durable `browser_execute` tool backed by
148
- * a codemode runtime: the model writes TypeScript against the `cdp` connector
149
- * (`cdp.send`, `cdp.attachToTarget`, `cdp.spec`, …), executions are recorded
150
- * for abort-and-replay, and browser sessions survive pauses.
151
- *
152
- * @example
153
- * ```ts
154
- * import { createBrowserTools } from "agents/browser/ai";
155
- * import { generateText } from "ai";
156
- *
157
- * // inside a Durable Object / Agent:
158
- * const browserTools = createBrowserTools({
159
- * ctx: this.ctx,
160
- * browser: this.env.BROWSER,
161
- * loader: this.env.LOADER,
162
- * });
163
- *
164
- * const result = await generateText({
165
- * model,
166
- * tools: { ...browserTools, ...otherTools },
167
- * messages,
168
- * });
169
- * ```
170
- */
171
- function createBrowserTools(options) {
172
- return createBrowserRuntime(options).tools;
173
- }
174
- const DEFAULT_QUICK_ACTION_TOOLS = [
175
- "markdown",
176
- "extract",
177
- "links",
178
- "scrape"
179
- ];
180
- const DEFAULT_QUICK_ACTION_MAX_CHARS = 5e4;
181
- const pageInputSchema = z.object({
182
- url: z.string().url().optional().describe("URL of the page to load"),
183
- html: z.string().optional().describe("Raw HTML to render instead of loading a URL")
184
- }).refine((value) => Boolean(value.url) || Boolean(value.html), { message: "Provide either 'url' or 'html'" });
185
- function toPage(input, options) {
186
- const page = input.url ? { url: input.url } : { html: input.html };
187
- return {
188
- ...options,
189
- ...page
190
- };
191
- }
192
- function truncate(text, maxChars) {
193
- if (maxChars <= 0 || text.length <= maxChars) return text;
194
- return `${text.slice(0, maxChars)}\n\n[truncated ${text.length - maxChars} characters]`;
195
- }
196
- /**
197
- * Keep a tool result within a rough character budget so a single browse cannot
198
- * blow the model's context window — while preserving the result's shape so the
199
- * model sees a consistent type across calls:
200
- *
201
- * - strings (markdown/content) are truncated to a string;
202
- * - arrays (links/scrape) are trimmed from the end but stay arrays;
203
- * - only an opaque oversized object (e.g. a sprawling `extract`) degrades to a
204
- * `{ truncated, note, preview }` summary, since it cannot be trimmed safely.
205
- */
206
- function boundResult(value, maxChars) {
207
- if (maxChars <= 0) return value;
208
- if (typeof value === "string") return truncate(value, maxChars);
209
- let json;
210
- try {
211
- json = JSON.stringify(value);
212
- } catch {
213
- return value;
214
- }
215
- if (json.length <= maxChars) return value;
216
- if (Array.isArray(value)) {
217
- const trimmed = boundArray(value, maxChars);
218
- if (trimmed.length > 0) return trimmed;
219
- }
220
- return {
221
- truncated: true,
222
- note: `Result is too large (${json.length} characters); narrow the request.`,
223
- preview: `${json.slice(0, maxChars)}…`
224
- };
225
- }
226
- /**
227
- * Take as many leading items as fit within `maxChars` (measured against their
228
- * JSON length), returning a trimmed array of the same element type. Silent by
229
- * design: the model gets fewer, valid items rather than a reshaped result.
230
- */
231
- function boundArray(value, maxChars) {
232
- const out = [];
233
- let size = 2;
234
- for (const item of value) {
235
- const itemSize = JSON.stringify(item).length + 1;
236
- if (size + itemSize > maxChars) break;
237
- out.push(item);
238
- size += itemSize;
239
- }
240
- return out;
241
- }
242
- /**
243
- * Create AI SDK tools for Browser Run [Quick Actions](https://developers.cloudflare.com/browser-run/quick-actions/):
244
- * stateless one-shot browsing (read a page as Markdown, extract structured
245
- * data with AI, list links, scrape elements). Unlike `createBrowserTools`,
246
- * these need only the `browser` binding — no Durable Object, loader, or
247
- * sandbox — so they work from any Worker.
248
- *
249
- * @example
250
- * ```ts
251
- * import { createQuickActionTools } from "agents/browser/ai";
252
- *
253
- * const tools = createQuickActionTools({ browser: this.env.BROWSER });
254
- * const result = await generateText({ model, tools, messages });
255
- * ```
256
- */
257
- function createQuickActionTools(options) {
258
- const { browser } = options;
259
- const requestOptions = options.options;
260
- const enabled = new Set(options.actions ?? DEFAULT_QUICK_ACTION_TOOLS);
261
- const maxChars = options.maxChars ?? DEFAULT_QUICK_ACTION_MAX_CHARS;
262
- const tools = {};
263
- if (enabled.has("markdown")) tools.browser_markdown = tool({
264
- description: "Load a web page (or render raw HTML) and return its content as Markdown. Best for reading articles, docs, or any page as text.",
265
- inputSchema: pageInputSchema,
266
- execute: async (input) => boundResult(await browserMarkdown(browser, toPage(input, requestOptions)), maxChars)
267
- });
268
- if (enabled.has("extract")) tools.browser_extract = tool({
269
- description: "Extract structured data from a web page using AI. Describe what you want in 'prompt'. Passing a JSON Schema in 'schema' is strongly recommended — without one the extractor often fails to produce JSON.",
270
- inputSchema: z.object({
271
- url: z.string().url().optional().describe("URL of the page to load"),
272
- html: z.string().optional().describe("Raw HTML to render instead of loading a URL"),
273
- prompt: z.string().optional().describe("What to extract, in natural language"),
274
- schema: z.record(z.string(), z.unknown(), { error: "Schema must be a JSON object" }).optional().describe("Optional JSON Schema describing the desired output")
275
- }).refine((value) => Boolean(value.url) || Boolean(value.html), { message: "Provide either 'url' or 'html'" }).refine((value) => Boolean(value.prompt) || Boolean(value.schema), { message: "Provide either 'prompt' or 'schema'" }),
276
- execute: async (input) => boundResult(await browserExtract(browser, {
277
- ...toPage(input, requestOptions),
278
- prompt: input.prompt,
279
- response_format: input.schema ? {
280
- type: "json_schema",
281
- json_schema: input.schema
282
- } : void 0
283
- }), maxChars)
284
- });
285
- if (enabled.has("links")) tools.browser_links = tool({
286
- description: "Return every link found on a web page (including ones not visible). Useful for discovering pages to follow.",
287
- inputSchema: pageInputSchema,
288
- execute: async (input) => boundResult(await browserLinks(browser, toPage(input, requestOptions)), maxChars)
289
- });
290
- if (enabled.has("scrape")) tools.browser_scrape = tool({
291
- description: "Scrape specific elements from a web page by CSS selector. Returns the matched elements' text, HTML, and attributes.",
292
- inputSchema: z.object({
293
- url: z.string().url().optional().describe("URL of the page to load"),
294
- html: z.string().optional().describe("Raw HTML to render instead of loading a URL"),
295
- selectors: z.array(z.string()).min(1).describe("CSS selectors to extract")
296
- }).refine((value) => Boolean(value.url) || Boolean(value.html), { message: "Provide either 'url' or 'html'" }),
297
- execute: async (input) => boundResult(await browserScrape(browser, {
298
- ...toPage(input, requestOptions),
299
- elements: input.selectors.map((selector) => ({ selector }))
300
- }), maxChars)
301
- });
302
- if (enabled.has("content")) tools.browser_content = tool({
303
- description: "Load a web page and return its fully rendered HTML (after JavaScript runs). Prefer 'browser_markdown' unless you need the raw HTML.",
304
- inputSchema: pageInputSchema,
305
- execute: async (input) => boundResult(await browserContent(browser, toPage(input, requestOptions)), maxChars)
306
- });
307
- return tools;
308
- }
309
- //#endregion
310
- export { browserScreenshotOutput as a, browserExecuteModelOutput as i, createBrowserTools as n, resolveCtx as o, createQuickActionTools as r, transformBrowserResult as s, createBrowserRuntime as t };
311
-
312
- //# sourceMappingURL=ai-fm7xx1BK.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ai-fm7xx1BK.js","names":["agentContext"],"sources":["../src/browser/tool-helpers.ts","../src/browser/ai.ts"],"sourcesContent":["/**\n * Helpers shared by the browser tools: `createBrowserTools` in `ai.ts`, and\n * `browserTool` in `ai-sdk.ts` and `tanstack-ai.ts`. Internal — not an entry\n * point.\n */\nimport type { JSONValue } from \"ai\";\nimport { truncateResult } from \"@cloudflare/codemode\";\nimport { redactBase64Payloads } from \"../core/base64-redaction\";\nimport { __DO_NOT_USE_WILL_BREAK__agentContext as agentContext } from \"../internal_context\";\n\ninterface BrowserScreenshotOutput {\n type: \"browser_screenshot\";\n mediaType: string;\n data: string;\n}\n\nexport function browserScreenshotOutput(\n value: unknown\n): BrowserScreenshotOutput | null {\n const outer =\n typeof value === \"object\" && value !== null\n ? (value as Record<string, unknown>)\n : null;\n const result =\n outer && typeof outer.result === \"object\" && outer.result !== null\n ? (outer.result as Record<string, unknown>)\n : outer;\n if (\n result?.type !== \"browser_screenshot\" ||\n typeof result.mediaType !== \"string\" ||\n typeof result.data !== \"string\"\n ) {\n return null;\n }\n return result as unknown as BrowserScreenshotOutput;\n}\n\nexport function transformBrowserResult(value: unknown): unknown {\n // Keep canonical screenshot output intact for UIMessage persistence and the\n // chat renderer. Other results are redacted before truncation so a nested\n // binary payload cannot become a large serialized preview.\n return browserScreenshotOutput(value)\n ? value\n : truncateResult(redactBase64Payloads(value));\n}\n\n/** What the model sees in place of a browser tool's output. */\nexport type BrowserModelOutput =\n | { type: \"text\"; value: string }\n | { type: \"json\"; value: JSONValue };\n\n/**\n * @param notes Sentences appended when a screenshot collapses the output to\n * text, so notices on the output aren't lost with it.\n */\nexport function browserExecuteModelOutput(\n output: unknown,\n notes: string[] = []\n): BrowserModelOutput {\n const screenshot = browserScreenshotOutput(output);\n if (screenshot) {\n const approximateBytes = Math.floor((screenshot.data.length * 3) / 4);\n return {\n type: \"text\",\n value: [\n `Screenshot captured successfully (${screenshot.mediaType}, approximately ${approximateBytes.toLocaleString()} bytes); the image is kept for the UI and omitted here.`,\n ...notes\n ].join(\" \")\n };\n }\n\n // `calls` is the durable audit log; like the codemode tool's own projection,\n // it stays on the persisted part and never enters the model's context, and\n // the sandbox `logs` are bounded like a result.\n const modelFacing =\n typeof output === \"object\" && output !== null && !Array.isArray(output)\n ? (({ calls: _calls, ...rest }: { calls?: unknown; logs?: unknown }) => ({\n ...rest,\n ...(Array.isArray(rest.logs)\n ? { logs: truncateResult(rest.logs) }\n : {})\n }))(output)\n : output;\n const redacted = redactBase64Payloads(modelFacing);\n try {\n const serialized = JSON.stringify(redacted);\n return {\n type: \"json\",\n value:\n serialized === undefined ? null : (JSON.parse(serialized) as JSONValue)\n };\n } catch {\n return {\n type: \"text\",\n value:\n \"Browser execution completed, but its result could not be serialized for model context.\"\n };\n }\n}\n\n/**\n * The Durable Object state to build the runtime in: the explicit `ctx` if\n * given, otherwise the current Agent's `ctx` (via `getCurrentAgent()`), so\n * `createBrowserRuntime` can be called from an Agent method without threading\n * `this.ctx` through.\n */\nexport function resolveCtx(options: {\n ctx?: DurableObjectState;\n}): DurableObjectState | undefined {\n if (options.ctx) return options.ctx;\n const agent = agentContext.getStore()?.agent as\n | { ctx?: DurableObjectState }\n | undefined;\n return agent?.ctx;\n}\n","import { tool, type ToolSet } from \"ai\";\nimport { z } from \"zod\";\nimport {\n createCodemodeRuntime,\n DynamicWorkerExecutor,\n type CodemodeRuntimeHandle\n} from \"@cloudflare/codemode\";\nimport type { BrowserBinding } from \"./browser-run\";\nimport {\n browserContent,\n browserExtract,\n browserLinks,\n browserMarkdown,\n browserScrape,\n type QuickActionBinding,\n type QuickActionCommonOptions,\n type QuickActionPage\n} from \"./quick-actions\";\nimport {\n BrowserConnector,\n type BrowserConnectorOptions,\n type BrowserConnectorSessionOptions\n} from \"./connector\";\nimport {\n DurableBrowserSessionStore,\n type BrowserSessionStore\n} from \"./session-store\";\nimport {\n browserExecuteModelOutput,\n resolveCtx,\n transformBrowserResult\n} from \"./tool-helpers\";\n\nexport interface CreateBrowserToolsOptions {\n /**\n * Durable Object state. The codemode runtime that backs the browser tool\n * lives in a facet of this DO, and browser session ids are stored in its\n * storage — so the tool must be created from inside a Durable Object\n * (e.g. an Agent).\n *\n * Optional: when omitted, it is resolved from the current Agent via\n * `getCurrentAgent()`, so inside an Agent method you can just pass `browser`\n * and `loader`. Pass it explicitly outside an Agent context.\n *\n * The worker must export the `CodemodeRuntime` class (the\n * `@cloudflare/codemode/vite` plugin does this automatically, or add\n * `export { CodemodeRuntime } from \"@cloudflare/codemode\"` to your entry).\n */\n ctx?: DurableObjectState;\n\n /**\n * WorkerLoader binding for sandboxed code execution.\n *\n * Requires `\"worker_loaders\": [{ \"binding\": \"LOADER\" }]` in wrangler.jsonc.\n */\n loader: WorkerLoader;\n\n /**\n * Browser Rendering binding (Fetcher).\n *\n * This is the primary way to connect — works both locally in\n * `wrangler dev` and when deployed to Cloudflare Workers.\n *\n * Requires `\"browser\": { \"binding\": \"BROWSER\" }` in wrangler.jsonc.\n */\n browser?: BrowserBinding;\n\n /**\n * Optional CDP base URL override (e.g. `http://localhost:9222`).\n *\n * Use when connecting to a manually managed Chrome instance or\n * a remote CDP endpoint behind a tunnel.\n */\n cdpUrl?: string;\n\n /**\n * Headers to send with CDP URL discovery requests.\n * Useful when the CDP endpoint requires authentication\n * (e.g. Cloudflare Access headers).\n */\n cdpHeaders?: Record<string, string>;\n\n /**\n * Browser session lifecycle (binding-backed only). Defaults to one fresh\n * session per codemode execution (`one-shot`).\n */\n session?: BrowserConnectorSessionOptions;\n\n /**\n * Durable store for Browser Run session ids. Defaults to a\n * {@link DurableBrowserSessionStore} over `ctx.storage`.\n */\n store?: BrowserSessionStore;\n\n /**\n * Sandbox execution timeout in milliseconds. Defaults to 30000 (30s).\n * Also used as the per-CDP-command timeout.\n */\n timeout?: number;\n\n /**\n * Codemode runtime name — the durable identity of the tool's executions\n * and snippets. Defaults to `\"browser\"`.\n */\n name?: string;\n\n /**\n * Also expose stateless {@link createQuickActionTools | Quick Action} tools\n * (`browser_markdown`, `browser_extract`, …) alongside the durable\n * `browser_execute` tool.\n *\n * Enabled by default whenever a Browser Run `browser` binding is available\n * (they share it), except when `session.browser` selects Kitesurf because\n * the binding RPC cannot select that engine. Pass `true` or an object to\n * explicitly add Chromium Quick Actions alongside Kitesurf, or `false` to\n * disable them. The Quick Action binding defaults to `browser`; override it via\n * `quickActions.browser`. When only `cdpUrl` is set (no binding), the\n * defaults are skipped silently — pass `quickActions: { browser }` to force\n * them.\n */\n quickActions?:\n | boolean\n | {\n browser?: QuickActionBinding;\n actions?: QuickActionToolName[];\n maxChars?: number;\n options?: QuickActionCommonOptions;\n };\n}\n\n/**\n * The browser tool's moving parts, for hosts that need more than the tools:\n *\n * - `runtime` — the codemode runtime handle (approve/reject paused runs,\n * `expirePaused`, audit via `executions()`, snippets).\n * - `connector` — host-side session helpers: `sessionInfo()`,\n * `closeSession()`, and `sweep()` for a recurring cleanup task.\n * - `tools` — what `createBrowserTools` returns.\n */\nexport interface BrowserRuntime {\n runtime: CodemodeRuntimeHandle;\n connector: BrowserConnector;\n tools: ToolSet;\n}\n\nlet didWarnExperimental = false;\nlet didDebugQuickActionSkip = false;\n\nfunction connectorOptions(\n options: CreateBrowserToolsOptions,\n ctx: DurableObjectState\n): BrowserConnectorOptions {\n if (options.cdpUrl) {\n return {\n cdpUrl: options.cdpUrl,\n cdpHeaders: options.cdpHeaders,\n timeout: options.timeout\n };\n }\n if (!options.browser) {\n throw new Error(\n \"Either 'browser' (Fetcher binding) or 'cdpUrl' must be provided\"\n );\n }\n return {\n browser: options.browser,\n store: options.store ?? new DurableBrowserSessionStore(ctx.storage),\n session: options.session,\n timeout: options.timeout\n };\n}\n\n/**\n * Create the browser codemode runtime: the `browser_execute` tool plus the\n * runtime handle and connector for host-side wiring (approvals, session info,\n * sweeps).\n *\n * @example\n * ```ts\n * export class MyAgent extends Agent<Env> {\n * get browser() {\n * return createBrowserRuntime({\n * ctx: this.ctx,\n * browser: this.env.BROWSER,\n * loader: this.env.LOADER,\n * session: { mode: \"dynamic\" }\n * });\n * }\n *\n * @callable()\n * async closeBrowserSession() {\n * await this.browser.connector.closeSession();\n * }\n * }\n * ```\n */\nexport function createBrowserRuntime(\n options: CreateBrowserToolsOptions\n): BrowserRuntime {\n if (!didWarnExperimental) {\n didWarnExperimental = true;\n console.warn(\n \"[agents/browser] Browser tools are experimental and may change in a future release.\"\n );\n }\n\n const ctx = resolveCtx(options);\n if (!ctx) {\n throw new Error(\n \"createBrowserRuntime requires a Durable Object 'ctx' — pass it explicitly, or call from within an Agent so it can be resolved via getCurrentAgent()\"\n );\n }\n\n const connector = new BrowserConnector(ctx, connectorOptions(options, ctx));\n const runtime = createCodemodeRuntime({\n ctx,\n executor: new DynamicWorkerExecutor({\n loader: options.loader,\n timeout: options.timeout\n }),\n connectors: [connector],\n name: options.name ?? \"browser\",\n transformResult: transformBrowserResult\n });\n\n // `connectorOptions` drops `session` when a custom endpoint is used, so the\n // connector is plain Chromium there whatever the session options say.\n const isKitesurf = !options.cdpUrl && options.session?.browser === \"kitesurf\";\n const browserExecute = runtime.tool({\n connectorHints: {\n cdp: isKitesurf\n ? 'Kitesurf one-shot Browser CDP. Call codemode.describe(\"cdp\") for connector types and Kitesurf execution rules. codemode.search indexes connector methods and snippets, not underlying CDP commands. Complete the task in one execution and do not pause. Return screenshots as { type: \"browser_screenshot\", mediaType: \"image/png\", data: screenshot.data }.'\n : \"Browser CDP. Return screenshots as { type: 'browser_screenshot', mediaType: 'image/png', data: screenshot.data }; the UI keeps the image while the model receives a compact summary.\"\n }\n });\n const tools: ToolSet = {\n browser_execute: {\n ...browserExecute,\n toModelOutput: ({ output }: { output: unknown }) =>\n browserExecuteModelOutput(output)\n }\n };\n\n // Quick Actions ride the same `browser` binding, so they are on by default\n // for Chromium. The binding RPC cannot select Kitesurf, so selecting\n // Kitesurf disables the defaults rather than silently adding Chromium tools.\n // Callers can still request a mixed-engine toolset explicitly.\n const enableQuickActions =\n options.quickActions !== false &&\n (!isKitesurf || options.quickActions !== undefined);\n // `env.BROWSER` satisfies both the CDP `BrowserBinding` (fetch) and the\n // `QuickActionBinding` (quickAction) surfaces; our narrower option type only\n // sees the former, so reuse it here unless an explicit binding wins.\n if (enableQuickActions) {\n const qa =\n typeof options.quickActions === \"object\" ? options.quickActions : {};\n const quickActionBrowser =\n qa.browser ??\n (options.browser as unknown as QuickActionBinding | undefined);\n if (quickActionBrowser) {\n Object.assign(\n tools,\n createQuickActionTools({\n browser: quickActionBrowser,\n actions: qa.actions,\n maxChars: qa.maxChars,\n options: qa.options\n })\n );\n } else if (options.quickActions) {\n // Explicitly requested but no binding to back them.\n throw new Error(\n \"quickActions requires a Browser Run binding — set 'browser' (env.BROWSER) or 'quickActions.browser'\"\n );\n } else if (!didDebugQuickActionSkip) {\n // Defaulted on, but only `cdpUrl` is set: there is no binding to call\n // Quick Actions through, so they are skipped. Surface it once so a user\n // expecting `browser_markdown` et al. isn't left wondering.\n didDebugQuickActionSkip = true;\n console.debug(\n \"[agents/browser] Quick Action tools skipped — no Browser Run binding (only 'cdpUrl' is set). Pass 'browser' or 'quickActions.browser' to enable them.\"\n );\n }\n }\n\n return { runtime, connector, tools };\n}\n\n/**\n * Create AI SDK tools for browser automation via CDP code mode.\n *\n * Returns a `ToolSet` with a single durable `browser_execute` tool backed by\n * a codemode runtime: the model writes TypeScript against the `cdp` connector\n * (`cdp.send`, `cdp.attachToTarget`, `cdp.spec`, …), executions are recorded\n * for abort-and-replay, and browser sessions survive pauses.\n *\n * @example\n * ```ts\n * import { createBrowserTools } from \"agents/browser/ai\";\n * import { generateText } from \"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 result = await generateText({\n * model,\n * tools: { ...browserTools, ...otherTools },\n * messages,\n * });\n * ```\n */\nexport function createBrowserTools(\n options: CreateBrowserToolsOptions\n): ToolSet {\n return createBrowserRuntime(options).tools;\n}\n\n/** A Quick Action exposed as an AI SDK tool. */\nexport type QuickActionToolName =\n | \"markdown\"\n | \"extract\"\n | \"links\"\n | \"scrape\"\n | \"content\";\n\nexport interface CreateQuickActionToolsOptions {\n /**\n * Browser Run binding with Quick Actions support (`env.BROWSER`). Requires a\n * Worker `compatibility_date` of `2026-03-24`+ and `remote: true` for local\n * `wrangler dev`.\n */\n browser: QuickActionBinding;\n\n /**\n * Which tools to expose. Defaults to the text-returning, model-friendly set\n * (`markdown`, `extract`, `links`, `scrape`). `content` (raw HTML) is opt-in\n * since it is large and rarely what a model wants.\n */\n actions?: QuickActionToolName[];\n\n /**\n * Bound every result to roughly this many characters before returning it to\n * the model, to protect the context window, preserving each result's shape:\n * text (markdown/content) is truncated to a string, oversized arrays\n * (links/scrape) are trimmed but stay arrays, and only an opaque oversized\n * object degrades to a truncated-preview summary. Set to `0` to disable.\n * Defaults to 50000.\n */\n maxChars?: number;\n\n /**\n * Common Browser Run options merged into every request — e.g. `cookies`,\n * `authenticate`, or `setExtraHTTPHeaders` for authenticated pages, and\n * `gotoOptions` / `viewport` for JavaScript-heavy pages. The model only ever\n * supplies the page (`url`/`html`) and action-specific fields; these\n * host-supplied options are never exposed to it.\n */\n options?: QuickActionCommonOptions;\n}\n\nconst DEFAULT_QUICK_ACTION_TOOLS: QuickActionToolName[] = [\n \"markdown\",\n \"extract\",\n \"links\",\n \"scrape\"\n];\n\nconst DEFAULT_QUICK_ACTION_MAX_CHARS = 50_000;\n\nconst pageInputSchema = z\n .object({\n url: z.string().url().optional().describe(\"URL of the page to load\"),\n html: z\n .string()\n .optional()\n .describe(\"Raw HTML to render instead of loading a URL\")\n })\n .refine((value) => Boolean(value.url) || Boolean(value.html), {\n message: \"Provide either 'url' or 'html'\"\n });\n\nfunction toPage(\n input: { url?: string; html?: string },\n options?: QuickActionCommonOptions\n): QuickActionPage {\n const page = input.url ? { url: input.url } : { html: input.html as string };\n return { ...options, ...page } as QuickActionPage;\n}\n\nfunction truncate(text: string, maxChars: number): string {\n if (maxChars <= 0 || text.length <= maxChars) return text;\n return `${text.slice(0, maxChars)}\\n\\n[truncated ${text.length - maxChars} characters]`;\n}\n\n/**\n * Keep a tool result within a rough character budget so a single browse cannot\n * blow the model's context window — while preserving the result's shape so the\n * model sees a consistent type across calls:\n *\n * - strings (markdown/content) are truncated to a string;\n * - arrays (links/scrape) are trimmed from the end but stay arrays;\n * - only an opaque oversized object (e.g. a sprawling `extract`) degrades to a\n * `{ truncated, note, preview }` summary, since it cannot be trimmed safely.\n */\nfunction boundResult(value: unknown, maxChars: number): unknown {\n if (maxChars <= 0) return value;\n if (typeof value === \"string\") return truncate(value, maxChars);\n let json: string;\n try {\n json = JSON.stringify(value);\n } catch {\n return value;\n }\n if (json.length <= maxChars) return value;\n if (Array.isArray(value)) {\n const trimmed = boundArray(value, maxChars);\n // If even the first element overflows the budget, `trimmed` is empty —\n // returning `[]` would read as \"no results\" rather than \"too large\", so\n // fall through to the explicit truncated-preview summary instead.\n if (trimmed.length > 0) return trimmed;\n }\n return {\n truncated: true,\n note: `Result is too large (${json.length} characters); narrow the request.`,\n preview: `${json.slice(0, maxChars)}…`\n };\n}\n\n/**\n * Take as many leading items as fit within `maxChars` (measured against their\n * JSON length), returning a trimmed array of the same element type. Silent by\n * design: the model gets fewer, valid items rather than a reshaped result.\n */\nfunction boundArray(value: unknown[], maxChars: number): unknown[] {\n const out: unknown[] = [];\n let size = 2; // the enclosing \"[]\"\n for (const item of value) {\n const itemSize = JSON.stringify(item).length + 1; // + a separating comma\n if (size + itemSize > maxChars) break;\n out.push(item);\n size += itemSize;\n }\n return out;\n}\n\n/**\n * Create AI SDK tools for Browser Run [Quick Actions](https://developers.cloudflare.com/browser-run/quick-actions/):\n * stateless one-shot browsing (read a page as Markdown, extract structured\n * data with AI, list links, scrape elements). Unlike `createBrowserTools`,\n * these need only the `browser` binding — no Durable Object, loader, or\n * sandbox — so they work from any Worker.\n *\n * @example\n * ```ts\n * import { createQuickActionTools } from \"agents/browser/ai\";\n *\n * const tools = createQuickActionTools({ browser: this.env.BROWSER });\n * const result = await generateText({ model, tools, messages });\n * ```\n */\nexport function createQuickActionTools(\n options: CreateQuickActionToolsOptions\n): ToolSet {\n const { browser } = options;\n const requestOptions = options.options;\n const enabled = new Set(options.actions ?? DEFAULT_QUICK_ACTION_TOOLS);\n const maxChars = options.maxChars ?? DEFAULT_QUICK_ACTION_MAX_CHARS;\n const tools: ToolSet = {};\n\n if (enabled.has(\"markdown\")) {\n tools.browser_markdown = tool({\n description:\n \"Load a web page (or render raw HTML) and return its content as Markdown. Best for reading articles, docs, or any page as text.\",\n inputSchema: pageInputSchema,\n execute: async (input) =>\n boundResult(\n await browserMarkdown(browser, toPage(input, requestOptions)),\n maxChars\n )\n });\n }\n\n if (enabled.has(\"extract\")) {\n tools.browser_extract = tool({\n description:\n \"Extract structured data from a web page using AI. Describe what you want in 'prompt'. Passing a JSON Schema in 'schema' is strongly recommended — without one the extractor often fails to produce JSON.\",\n inputSchema: z\n .object({\n url: z.string().url().optional().describe(\"URL of the page to load\"),\n html: z\n .string()\n .optional()\n .describe(\"Raw HTML to render instead of loading a URL\"),\n prompt: z\n .string()\n .optional()\n .describe(\"What to extract, in natural language\"),\n schema: z\n .record(z.string(), z.unknown(), {\n error: \"Schema must be a JSON object\"\n })\n .optional()\n .describe(\"Optional JSON Schema describing the desired output\")\n })\n .refine((value) => Boolean(value.url) || Boolean(value.html), {\n message: \"Provide either 'url' or 'html'\"\n })\n .refine((value) => Boolean(value.prompt) || Boolean(value.schema), {\n message: \"Provide either 'prompt' or 'schema'\"\n }),\n execute: async (input) =>\n boundResult(\n await browserExtract(browser, {\n ...toPage(input, requestOptions),\n prompt: input.prompt,\n response_format: input.schema\n ? { type: \"json_schema\", json_schema: input.schema }\n : undefined\n }),\n maxChars\n )\n });\n }\n\n if (enabled.has(\"links\")) {\n tools.browser_links = tool({\n description:\n \"Return every link found on a web page (including ones not visible). Useful for discovering pages to follow.\",\n inputSchema: pageInputSchema,\n execute: async (input) =>\n boundResult(\n await browserLinks(browser, toPage(input, requestOptions)),\n maxChars\n )\n });\n }\n\n if (enabled.has(\"scrape\")) {\n tools.browser_scrape = tool({\n description:\n \"Scrape specific elements from a web page by CSS selector. Returns the matched elements' text, HTML, and attributes.\",\n inputSchema: z\n .object({\n url: z.string().url().optional().describe(\"URL of the page to load\"),\n html: z\n .string()\n .optional()\n .describe(\"Raw HTML to render instead of loading a URL\"),\n selectors: z\n .array(z.string())\n .min(1)\n .describe(\"CSS selectors to extract\")\n })\n .refine((value) => Boolean(value.url) || Boolean(value.html), {\n message: \"Provide either 'url' or 'html'\"\n }),\n execute: async (input) =>\n boundResult(\n await browserScrape(browser, {\n ...toPage(input, requestOptions),\n elements: input.selectors.map((selector) => ({ selector }))\n }),\n maxChars\n )\n });\n }\n\n if (enabled.has(\"content\")) {\n tools.browser_content = tool({\n description:\n \"Load a web page and return its fully rendered HTML (after JavaScript runs). Prefer 'browser_markdown' unless you need the raw HTML.\",\n inputSchema: pageInputSchema,\n execute: async (input) =>\n boundResult(\n await browserContent(browser, toPage(input, requestOptions)),\n maxChars\n )\n });\n }\n\n return tools;\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ai-sdk.js","names":[],"sources":["../../src/browser/ai-sdk.ts"],"sourcesContent":["/**\n * `agents/browser/ai-sdk` — the AI SDK tool for a persistent `Browser`.\n *\n * The older `createBrowserTools` stays in `agents/browser/ai`.\n */\nimport type { FlexibleSchema } from \"ai\";\nimport {\n browserReportNotes,\n createBrowserToolCore,\n type BrowserToolInput,\n type BrowserToolOptions,\n type BrowserToolOutput\n} from \"./browser-tool\";\nimport {\n browserExecuteModelOutput,\n type BrowserModelOutput\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\n// Stateless Quick Action tools, so one import covers both.\nexport {\n createQuickActionTools,\n type CreateQuickActionToolsOptions,\n type QuickActionToolName\n} from \"./ai\";\n\n/**\n * The AI SDK tool {@link browserTool} returns. Assignable to the AI SDK's\n * `Tool`, and `execute` / `toModelOutput` are always present.\n */\nexport interface BrowserTool {\n description: string;\n inputSchema: FlexibleSchema<BrowserToolInput>;\n execute(\n input: BrowserToolInput,\n options: unknown\n ): Promise<BrowserToolOutput>;\n toModelOutput(options: { output: BrowserToolOutput }): BrowserModelOutput;\n}\n\n/**\n * Create an AI SDK tool that lets the model drive a persistent browser with\n * JavaScript and the Chrome DevTools Protocol.\n *\n * The browser outlives each run: tabs, cookies, and logins carry over. The\n * model never starts, closes, or resets it, and `sessionId: \"active\"`\n * addresses the tab it last worked in. If the browser had to be replaced\n * (it expired or was closed), the code still runs in the new browser and the\n * result says `restarted: true`.\n *\n * Browser Run only (Chromium). Guardrails, `keepAliveMs`, and recording are\n * set on the `Browser`'s provider.\n *\n * @example\n * ```ts\n * import { Browser, browserRun } from \"agents/browser\";\n * import { browserTool } from \"agents/browser/ai-sdk\";\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 tools = {\n * browser: browserTool({ browser: this.browser, loader: this.env.LOADER })\n * };\n * // …pass `tools` to streamText / generateText\n * }\n * }\n * ```\n */\nexport function browserTool(options: BrowserToolOptions): BrowserTool {\n const core = createBrowserToolCore(options, {\n screenshotHint:\n \"To show a screenshot, return { type: 'browser_screenshot', mediaType, data } with data from Page.captureScreenshot and mediaType 'image/png', or 'image/jpeg' if you captured with format: 'jpeg'. The user sees the image; you get a short text summary.\"\n });\n return {\n description: core.description,\n inputSchema: core.inputSchema,\n execute: core.execute,\n toModelOutput: ({ output }) =>\n browserExecuteModelOutput(output, browserReportNotes(output))\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiFA,SAAgB,YAAY,SAA0C;CACpE,MAAM,OAAO,sBAAsB,SAAS,EAC1C,gBACE,4PACJ,CAAC;CACD,OAAO;EACL,aAAa,KAAK;EAClB,aAAa,KAAK;EAClB,SAAS,KAAK;EACd,gBAAgB,EAAE,aAChB,0BAA0B,QAAQ,mBAAmB,MAAM,CAAC;CAChE;AACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/browser/browser.ts"],"sourcesContent":["/**\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 * Browser Run is the only provider today; see {@link browserRun}.\n */\n\nimport { LifecycleCapability } from \"../lifecycle/capability\";\nimport type { CdpConnection } from \"./cdp-connection\";\nimport {\n type BrowserBinding,\n type BrowserSessionGuardrails,\n connectBrowserSession,\n createBrowserSession,\n deleteBrowserSession,\n isMissingBrowserSession,\n listBrowserTargets\n} from \"./browser-run\";\nimport {\n createLiveView,\n type BrowserLiveView,\n type LiveViewMode\n} from \"./live-view\";\nimport {\n DurableBrowserSessionStore,\n type BrowserSessionStore,\n type StoredBrowserSession\n} from \"./session-store\";\nimport { loadCdpSpec, type SearchableCdpSpec } from \"./spec\";\n\n/**\n * Browser Run's server-side `keep_alive` maximum (600 seconds). A `Browser`\n * pins keep-alive here by default so it survives quiet spells between agent\n * turns; the platform reclaims it only after this long idle.\n */\nexport const BROWSER_SESSION_KEEP_ALIVE_MAX_MS = 600_000;\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\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 * 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 {\n /**\n * Platform `keep_alive` in milliseconds: how long an idle browser lives.\n * Defaults to {@link BROWSER_SESSION_KEEP_ALIVE_MAX_MS} (the platform\n * maximum).\n */\n keepAliveMs?: number;\n /** Opt into Browser Run session recording (rrweb capture). */\n recording?: boolean;\n /** Hostname guardrails, fixed at launch for every connection. */\n guardrails?: BrowserSessionGuardrails;\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/** Where a {@link Browser} gets its browser from. */\nexport type BrowserProvider = BrowserRunProvider;\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(\n binding: BrowserBinding,\n options: BrowserRunOptions = {}\n): BrowserRunProvider {\n return { kind: \"browser-run\", binding, options };\n}\n\nexport interface BrowserOptions {\n /** Where the browser runs. Create one with {@link 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.\n */\n store?: BrowserSessionStore;\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 Browser Run 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 * The tab the agent last worked in, when one was recorded for this\n * browser. Absent on a browser this resolution created; a replacement a\n * concurrent caller already created and used may carry one.\n */\n activeTargetId?: string;\n}\n\nexport interface BrowserConnection {\n name: string;\n sessionId: string;\n /**\n * `true` when the browser this caller resolved is gone, even if a\n * concurrent caller already replaced it. See\n * {@link ResolvedBrowser.restarted}.\n */\n restarted: boolean;\n /** See {@link ResolvedBrowser.activeTargetId}. */\n activeTargetId?: string;\n /** Closing this connection does NOT close the browser. */\n cdp: CdpConnection;\n /**\n * Record the tab the agent 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 /**\n * The Chrome DevTools Protocol description this browser serves, read from\n * the browser itself (cached per binding).\n */\n spec(): Promise<SearchableCdpSpec>;\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: Browser Run reclaims idle\n * browsers itself once `keep_alive` elapses, and the next use replaces the\n * lost browser with `restarted: true`.\n *\n * Everything here is host-side. Models never see browser names, Browser Run\n * ids, or Live View URLs through this surface.\n *\n * Store discipline: locks wrap storage operations only — liveness probes and\n * Browser Run create/delete calls always happen outside any lock, with a\n * commit 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 #store?: BrowserSessionStore;\n\n constructor(options: BrowserOptions) {\n const name = options.name ?? DEFAULT_BROWSER_NAME;\n super(`browser:${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 }\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, ...session };\n }\n\n // Live entry on record — probe it outside any lock.\n const alive = await this.#isAlive(existing);\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, ...refreshed };\n }\n // The browser died upstream (expired or reclaimed). Retire the\n // record under this lock, so any resolver that reads it during the\n // replacement window sees the marker and reports restarted: true\n // too.\n await this.#retire(current);\n } finally {\n await lock.release();\n }\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. Commands sent over\n * the connection 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 with `restarted: true` instead of an\n * error.\n */\n async connect(): Promise<BrowserConnection> {\n const resolved = await this.resolve();\n try {\n return await this.#attach(resolved);\n } catch (error) {\n if (!isMissingBrowserSession(error)) throw error;\n await this.#retireIfCurrent(resolved.sessionId);\n }\n // The browser this caller resolved is gone, so report a restart even\n // when a concurrent resolver already replaced it and this resolve\n // reattaches to that replacement.\n const replaced = await this.resolve();\n return this.#attach({ ...replaced, restarted: true });\n }\n\n async #attach(resolved: ResolvedBrowser): Promise<BrowserConnection> {\n let lastTouchAt = Date.now();\n let touchInFlight = false;\n const cdp = await connectBrowserSession(\n this.#provider.binding,\n resolved.sessionId,\n {\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 const { binding } = this.#provider;\n return {\n name: resolved.name,\n sessionId: resolved.sessionId,\n restarted: resolved.restarted,\n activeTargetId: resolved.activeTargetId,\n cdp,\n setActiveTarget: (targetId) =>\n this.#update(resolved.sessionId, (current) => ({\n ...current,\n activeTargetId: targetId,\n updatedAt: Date.now()\n })),\n spec: () =>\n loadCdpSpec({ browser: binding, sessionId: resolved.sessionId })\n };\n }\n\n /**\n * Close the browser: retire the record (so the next resolve reports\n * `restarted: true`) and delete its Browser Run session. Returns `false`\n * when there was nothing to close.\n *\n * The platform delete is best-effort: the retired record is the durable\n * outcome, and a browser whose delete failed is unreachable through this\n * store, so the pinned `keep_alive` (≤600s) reclaims it.\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 deleteBrowserSession(this.#provider.binding, stored.sessionId);\n } catch (error) {\n console.warn(\n `[agents/browser] Failed to delete closed Browser Run 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 try {\n const targets = await listBrowserTargets(\n this.#provider.binding,\n entry.sessionId\n );\n // A human is about to look: minting counts as activity. Target\n // listing yields to the network, so a concurrent close or replacement\n // can retire the browser mid-mint — the touch and the retire\n // serialize on the key lock, and a lost touch means the listed\n // targets are already dead: report the browser gone rather than\n // minting doomed links. Touch *errors* stay best effort — a store\n // 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 createLiveView(entry.sessionId, targets, options?.mode);\n } catch (error) {\n if (isMissingBrowserSession(error)) return undefined;\n throw error;\n }\n }\n\n // ── Internals ────────────────────────────────────────────────────────────\n\n get #provider(): BrowserRunProvider {\n return this.#options.provider;\n }\n\n /** The platform `keep_alive` every create applies. */\n get #keepAliveMs(): number {\n return (\n this.#provider.options.keepAliveMs ?? BROWSER_SESSION_KEEP_ALIVE_MAX_MS\n );\n }\n\n /**\n * Capped at half the keep-alive window: a browser with continuous CDP\n * traffic must never look older than the window the platform uses to\n * reclaim it.\n */\n get #touchIntervalMs(): number {\n return Math.min(\n this.#options.touchIntervalMs ?? SESSION_TOUCH_INTERVAL_MS,\n Math.floor(this.#keepAliveMs / 2)\n );\n }\n\n /** Lazy: `lifecycle.storage` exists only once `Lifecycle.use()` ran. */\n get #sessionStore(): BrowserSessionStore {\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 /** 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 Browser Run session (outside any lock) and commit it, applying\n * the provider's creation options. If a concurrent caller committed an\n * entry first, theirs wins and the redundant session is deleted\n * 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 { binding, options } = this.#provider;\n const info = await createBrowserSession(binding, {\n keepAliveMs: this.#keepAliveMs,\n recording: options.recording,\n guardrails: options.guardrails\n });\n const now = Date.now();\n const stored: StoredBrowserSession = {\n sessionId: info.sessionId,\n createdAt: now,\n updatedAt: now\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 deleteBrowserSession(binding, stored.sessionId);\n } catch (error) {\n console.warn(\n `[agents/browser] Failed to delete redundant Browser Run session ${stored.sessionId}`,\n error\n );\n }\n return { session: winner, restarted };\n }\n return { session: stored, restarted };\n }\n\n async #isAlive(stored: StoredBrowserSession): Promise<boolean> {\n try {\n await listBrowserTargets(this.#provider.binding, stored.sessionId);\n return true;\n } catch (error) {\n if (isMissingBrowserSession(error)) return false;\n throw error;\n }\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"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tanstack-ai.js","names":[],"sources":["../../src/browser/tanstack-ai.ts"],"sourcesContent":["import { 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\";\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 { CreateBrowserToolsOptions } from \"./ai\";\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\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"],"mappings":";;;;;;;;;;AAmCA,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,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"}