agents 0.26.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 (301) hide show
  1. package/README.md +8 -9
  2. package/dist/abortable-C1SpfVoi.js +21 -0
  3. package/dist/abortable-C1SpfVoi.js.map +1 -0
  4. package/dist/{agent-routing-0DR0Tk2v.d.ts → agent-routing-QDcchcQP.d.ts} +16 -16
  5. package/dist/agent-routing.d.ts +1 -1
  6. package/dist/agent-tool-types.d.ts +1 -1
  7. package/dist/{agent-tools-YrATZJu2.d.ts → agent-tools-DHL2XI7u.d.ts} +2 -2
  8. package/dist/agent-tools.d.ts +1 -1
  9. package/dist/ai-ahruGw7e.d.ts +239 -0
  10. package/dist/ai-sdk-Bdsf6mSA.js +2444 -0
  11. package/dist/ai-sdk-Bdsf6mSA.js.map +1 -0
  12. package/dist/apply-chunks-1FheRb9W.js +99 -0
  13. package/dist/apply-chunks-1FheRb9W.js.map +1 -0
  14. package/dist/broadcast-state-CEyI4uAK.js +69 -0
  15. package/dist/broadcast-state-CEyI4uAK.js.map +1 -0
  16. package/dist/browser/ai.d.ts +8 -227
  17. package/dist/browser/ai.js +259 -1
  18. package/dist/browser/ai.js.map +1 -0
  19. package/dist/browser/index.d.ts +56 -46
  20. package/dist/browser/index.js +4 -361
  21. package/dist/browser/{ai-sdk.d.ts → tools/ai-sdk.d.ts} +7 -7
  22. package/dist/browser/{ai-sdk.js → tools/ai-sdk.js} +6 -5
  23. package/dist/browser/tools/ai-sdk.js.map +1 -0
  24. package/dist/browser/tools/pi.d.ts +111 -0
  25. package/dist/browser/tools/pi.js +119 -0
  26. package/dist/browser/tools/pi.js.map +1 -0
  27. package/dist/browser/{tanstack-ai.d.ts → tools/tanstack-ai.d.ts} +39 -37
  28. package/dist/browser/{tanstack-ai.js → tools/tanstack-ai.js} +73 -59
  29. package/dist/browser/tools/tanstack-ai.js.map +1 -0
  30. package/dist/browser-CEdNlsaD.js +481 -0
  31. package/dist/browser-CEdNlsaD.js.map +1 -0
  32. package/dist/{browser-DE3d-RLk.d.ts → browser-D30VlpcA.d.ts} +63 -37
  33. package/dist/{browser-tool-CQ10e6Kj.js → browser-tool-4iomavs_.js} +198 -62
  34. package/dist/browser-tool-4iomavs_.js.map +1 -0
  35. package/dist/{browser-tool-DuNdlwZ9.d.ts → browser-tool-vtrPOV8e.d.ts} +25 -6
  36. package/dist/{capability-B4WbF81e.js → capability-DBAKc6UZ.js} +1 -3
  37. package/dist/capability-DBAKc6UZ.js.map +1 -0
  38. package/dist/{capability-runner-DBAnGr6L.d.ts → capability-runner-BiVUtjqC.d.ts} +9 -7
  39. package/dist/channel-Bak_6zDP.d.ts +333 -0
  40. package/dist/chat/index.d.ts +11 -9
  41. package/dist/chat/index.js +5 -194
  42. package/dist/chat/index.js.map +1 -1
  43. package/dist/chat/react.d.ts +2 -4
  44. package/dist/chat/react.js +3 -1
  45. package/dist/chat/react.js.map +1 -1
  46. package/dist/chat-sdk/index.d.ts +1 -1
  47. package/dist/chat-sdk/index.js +1 -1
  48. package/dist/cli.js +144 -0
  49. package/dist/{client-je3koD9Z.d.ts → client-BGIQKJhX.d.ts} +2 -2
  50. package/dist/{client-fyQrLGkD.js → client-BnYbNT3z.js} +29 -5
  51. package/dist/client-BnYbNT3z.js.map +1 -0
  52. package/dist/client-tools-aIBO0Fk7.d.ts +53 -0
  53. package/dist/client.d.ts +1 -1
  54. package/dist/{connector-58XDGvc3.js → connector-CtZylXdk.js} +3 -603
  55. package/dist/connector-CtZylXdk.js.map +1 -0
  56. package/dist/{connector-Cl-VqPmA.d.ts → connector-DEXzVoaY.d.ts} +9 -374
  57. package/dist/conversations-FjHDRZyD.d.ts +192 -0
  58. package/dist/{current-agent-vAk_5JMt.d.ts → current-agent-CKgT5d1O.d.ts} +3 -5
  59. package/dist/current-agent-DhoDkSnH.js.map +1 -1
  60. package/dist/daemon-core-Buubj-y1.js +481 -0
  61. package/dist/daemon-core-Buubj-y1.js.map +1 -0
  62. package/dist/daemon-core-CbohVPAu.d.ts +132 -0
  63. package/dist/{do-oauth-client-provider-Tmf1vgKz.d.ts → do-oauth-client-provider-DfsUcpc3.d.ts} +7 -1
  64. package/dist/{durable-object-lifecycle-HhgOMWq9.d.ts → durable-object-lifecycle-DOI_bzCH.d.ts} +2 -2
  65. package/dist/{channels/email.d.ts → experimental/channels/email/index.d.ts} +25 -12
  66. package/dist/{channels/email.js → experimental/channels/email/index.js} +13 -35
  67. package/dist/experimental/channels/email/index.js.map +1 -0
  68. package/dist/experimental/channels/index.d.ts +270 -0
  69. package/dist/experimental/channels/index.js +1186 -0
  70. package/dist/experimental/channels/index.js.map +1 -0
  71. package/dist/{channels/slack.d.ts → experimental/channels/slack/index.d.ts} +19 -9
  72. package/dist/experimental/channels/slack/index.js +267 -0
  73. package/dist/experimental/channels/slack/index.js.map +1 -0
  74. package/dist/{channels/telegram.d.ts → experimental/channels/telegram/index.d.ts} +19 -9
  75. package/dist/experimental/channels/telegram/index.js +198 -0
  76. package/dist/experimental/channels/telegram/index.js.map +1 -0
  77. package/dist/experimental/channels/web/ai-sdk.d.ts +55 -0
  78. package/dist/experimental/channels/web/ai-sdk.js +266 -0
  79. package/dist/experimental/channels/web/ai-sdk.js.map +1 -0
  80. package/dist/experimental/channels/web/client.d.ts +97 -0
  81. package/dist/experimental/channels/web/client.js +349 -0
  82. package/dist/experimental/channels/web/client.js.map +1 -0
  83. package/dist/experimental/channels/web/index.d.ts +189 -0
  84. package/dist/experimental/channels/web/index.js +332 -0
  85. package/dist/experimental/channels/web/index.js.map +1 -0
  86. package/dist/gateway-BFO6vRDn.d.ts +100 -0
  87. package/dist/handle-zAzh0UW2.d.ts +460 -0
  88. package/dist/harness/ai-sdk/index.d.ts +175 -0
  89. package/dist/harness/ai-sdk/index.js +605 -0
  90. package/dist/harness/ai-sdk/index.js.map +1 -0
  91. package/dist/harness/container/daemon/index.d.ts +76 -0
  92. package/dist/harness/container/daemon/index.js +3 -0
  93. package/dist/harness/container/index.d.ts +740 -0
  94. package/dist/harness/container/index.js +2152 -0
  95. package/dist/harness/container/index.js.map +1 -0
  96. package/dist/harness/container/runtime/index.d.ts +144 -0
  97. package/dist/harness/container/runtime/index.js +1082 -0
  98. package/dist/harness/container/runtime/index.js.map +1 -0
  99. package/dist/harness/opencode/index.d.ts +369 -0
  100. package/dist/harness/opencode/index.js +1072 -0
  101. package/dist/harness/opencode/index.js.map +1 -0
  102. package/dist/harness/pi/index.d.ts +3 -2
  103. package/dist/harness/pi/index.js +3 -3
  104. package/dist/harness/store/index.d.ts +24 -0
  105. package/dist/harness/store/index.js +2 -0
  106. package/dist/harness/think/index.d.ts +821 -0
  107. package/dist/harness/think/index.js +2721 -0
  108. package/dist/harness/think/index.js.map +1 -0
  109. package/dist/harness-B6B9S3rb.d.ts +203 -0
  110. package/dist/{images-iiTDSyRu.js → images-Ce9SCu3X.js} +2 -248
  111. package/dist/images-Ce9SCu3X.js.map +1 -0
  112. package/dist/{index-BreL0YrQ.d.ts → index-BEL3ZV-s.d.ts} +2 -6
  113. package/dist/index-CBt8oRJt.d.ts +198 -0
  114. package/dist/index.d.ts +5 -5
  115. package/dist/index.js +2 -2
  116. package/dist/ingress-EFbrS547.js +9 -0
  117. package/dist/ingress-EFbrS547.js.map +1 -0
  118. package/dist/input-schema-BQykTEp_.js +20 -0
  119. package/dist/input-schema-BQykTEp_.js.map +1 -0
  120. package/dist/input-schema-D2QAtHZH.js +23 -0
  121. package/dist/input-schema-D2QAtHZH.js.map +1 -0
  122. package/dist/internal-kmw70xwR.js +46 -0
  123. package/dist/internal-kmw70xwR.js.map +1 -0
  124. package/dist/internal_context.d.ts +1 -1
  125. package/dist/lifecycle/index.d.ts +3 -3
  126. package/dist/lifecycle/index.js +2 -2
  127. package/dist/{lifecycle-CFu6OV03.js → lifecycle-DGorpAmA.js} +2 -4
  128. package/dist/lifecycle-DGorpAmA.js.map +1 -0
  129. package/dist/mcp/client/do-oauth-client-provider.d.ts +1 -1
  130. package/dist/mcp/client/do-oauth-client-provider.js +9 -0
  131. package/dist/mcp/client/do-oauth-client-provider.js.map +1 -1
  132. package/dist/mcp/client/index.d.ts +1 -1
  133. package/dist/mcp/client/index.js +1 -1
  134. package/dist/mcp/client/x402.d.ts +35 -4
  135. package/dist/mcp/client/x402.js +24 -3
  136. package/dist/mcp/client/x402.js.map +1 -1
  137. package/dist/mcp/index.d.ts +1 -1
  138. package/dist/mcp/index.js +2 -2
  139. package/dist/models/ai-sdk/index.d.ts +16 -14
  140. package/dist/models/ai-sdk/index.js +1 -2442
  141. package/dist/models/opencode/index.d.ts +65 -0
  142. package/dist/models/opencode/index.js +285 -0
  143. package/dist/models/opencode/index.js.map +1 -0
  144. package/dist/models/pi-ai/index.d.ts +9 -7
  145. package/dist/models/pi-ai/index.js +2 -1
  146. package/dist/models/pi-ai/index.js.map +1 -1
  147. package/dist/options-CiIsJHXR.d.ts +130 -0
  148. package/dist/protocol-BEgrJYC5.d.ts +331 -0
  149. package/dist/protocol-CukuI73M.d.ts +386 -0
  150. package/dist/protocol-cOklVBBD.js +227 -0
  151. package/dist/protocol-cOklVBBD.js.map +1 -0
  152. package/dist/{protocol-kHsr-f0X.js → protocol-eXeJxhgu.js} +2 -67
  153. package/dist/protocol-eXeJxhgu.js.map +1 -0
  154. package/dist/protocol-y2sSY584.js +148 -0
  155. package/dist/protocol-y2sSY584.js.map +1 -0
  156. package/dist/queue/index.d.ts +1 -1
  157. package/dist/queue/index.js +1 -1
  158. package/dist/{queue-BB-zfIYF.js → queue-CcIjVYTE.js} +2 -2
  159. package/dist/{queue-BB-zfIYF.js.map → queue-CcIjVYTE.js.map} +1 -1
  160. package/dist/react.d.ts +2 -2
  161. package/dist/routing/index.d.ts +2 -2
  162. package/dist/routing/index.js +1 -1
  163. package/dist/{scheduler-RdACNXDP.d.ts → scheduler-BCLESHSC.d.ts} +2 -12
  164. package/dist/{scheduler-DZ3xQfeK.js → scheduler-FIHv2-kJ.js} +2 -4
  165. package/dist/scheduler-FIHv2-kJ.js.map +1 -0
  166. package/dist/schedules/index.d.ts +1 -1
  167. package/dist/schedules/index.js +1 -1
  168. package/dist/serializable.d.ts +1 -1
  169. package/dist/session-store-Do4xkDcf.js +606 -0
  170. package/dist/session-store-Do4xkDcf.js.map +1 -0
  171. package/dist/session-store-DoPY12xM.d.ts +383 -0
  172. package/dist/sessions/index.d.ts +11 -434
  173. package/dist/sessions/index.js +1 -2271
  174. package/dist/sessions-_vN5BEZX.js +2272 -0
  175. package/dist/sessions-_vN5BEZX.js.map +1 -0
  176. package/dist/settings-C45hDliX.js +253 -0
  177. package/dist/settings-C45hDliX.js.map +1 -0
  178. package/dist/{transport-PfFnt-Ld.d.ts → settings-NgGwF7Qt.d.ts} +14 -208
  179. package/dist/{src-BZeu-SCD.js → src-CQxJFv4l.js} +8 -8
  180. package/dist/src-CQxJFv4l.js.map +1 -0
  181. package/dist/stall-watchdog-BpRF2uwG.js +195 -0
  182. package/dist/stall-watchdog-BpRF2uwG.js.map +1 -0
  183. package/dist/state/index.d.ts +1 -1
  184. package/dist/state/index.js +1 -3
  185. package/dist/state/index.js.map +1 -1
  186. package/dist/store-BKM4Xm9M.d.ts +332 -0
  187. package/dist/store-CslVzfN7.js +458 -0
  188. package/dist/store-CslVzfN7.js.map +1 -0
  189. package/dist/streams/index.d.ts +1 -1
  190. package/dist/streams/index.js +1 -1
  191. package/dist/{streams-DW-UCOUc.js → streams-lsHMJhRW.js} +17 -4
  192. package/dist/streams-lsHMJhRW.js.map +1 -0
  193. package/dist/{streams-Cmkj9QfW.d.ts → streams-vrCtbUIn.d.ts} +2 -2
  194. package/dist/sub-routing.d.ts +1 -1
  195. package/dist/tasks/index.d.ts +1 -1
  196. package/dist/tasks/index.js +1 -1
  197. package/dist/{tasks-68DESxf0.js → tasks-BN_5AWqI.js} +3 -2
  198. package/dist/tasks-BN_5AWqI.js.map +1 -0
  199. package/dist/{tasks-9YxYtByh.d.ts → tasks-E89KdwkS.d.ts} +2 -2
  200. package/dist/tool-CTp72Jb_.d.ts +278 -0
  201. package/dist/tool-CxKsvxjm.js +1126 -0
  202. package/dist/tool-CxKsvxjm.js.map +1 -0
  203. package/dist/tool-DoVberpN.d.ts +301 -0
  204. package/dist/tool-cQRkwSZZ.js +410 -0
  205. package/dist/tool-cQRkwSZZ.js.map +1 -0
  206. package/dist/tool-helpers-O5BDVEAY.js +58 -0
  207. package/dist/tool-helpers-O5BDVEAY.js.map +1 -0
  208. package/dist/transport-k2mFrtL8.d.ts +200 -0
  209. package/dist/turns-DCV3ycDl.js +317 -0
  210. package/dist/turns-DCV3ycDl.js.map +1 -0
  211. package/dist/voice/client.js +1 -1
  212. package/dist/voice/index.d.ts +2 -2
  213. package/dist/voice/index.js +1 -1
  214. package/dist/web/fetch/index.d.ts +89 -0
  215. package/dist/web/fetch/index.js +20 -0
  216. package/dist/web/fetch/index.js.map +1 -0
  217. package/dist/web/fetch/tools/ai-sdk.d.ts +80 -0
  218. package/dist/web/fetch/tools/ai-sdk.js +47 -0
  219. package/dist/web/fetch/tools/ai-sdk.js.map +1 -0
  220. package/dist/web/fetch/tools/pi.d.ts +84 -0
  221. package/dist/web/fetch/tools/pi.js +88 -0
  222. package/dist/web/fetch/tools/pi.js.map +1 -0
  223. package/dist/web/fetch/tools/tanstack-ai.d.ts +78 -0
  224. package/dist/web/fetch/tools/tanstack-ai.js +45 -0
  225. package/dist/web/fetch/tools/tanstack-ai.js.map +1 -0
  226. package/dist/web/search/index.d.ts +58 -0
  227. package/dist/web/search/index.js +2 -0
  228. package/dist/web/search/tools/ai-sdk.d.ts +77 -0
  229. package/dist/web/search/tools/ai-sdk.js +46 -0
  230. package/dist/web/search/tools/ai-sdk.js.map +1 -0
  231. package/dist/web/search/tools/pi.d.ts +82 -0
  232. package/dist/web/search/tools/pi.js +89 -0
  233. package/dist/web/search/tools/pi.js.map +1 -0
  234. package/dist/web/search/tools/tanstack-ai.d.ts +71 -0
  235. package/dist/web/search/tools/tanstack-ai.js +46 -0
  236. package/dist/web/search/tools/tanstack-ai.js.map +1 -0
  237. package/dist/websockets/index.d.ts +12 -296
  238. package/dist/websockets/index.js +1 -1
  239. package/dist/{websockets-Ci-LZFHH.js → websockets-C3zy8YR0.js} +20 -8
  240. package/dist/websockets-C3zy8YR0.js.map +1 -0
  241. package/dist/{wire-types-CrjKvDyi.d.ts → wire-types-Cv0iwjk5.d.ts} +3 -51
  242. package/dist/workflow-types.d.ts +1 -1
  243. package/dist/workflows.d.ts +1 -1
  244. package/dist/workflows.js +1 -1
  245. package/docs/browse-the-web.md +29 -1
  246. package/docs/fetch-the-web.md +276 -0
  247. package/docs/harnesses/pi.md +25 -0
  248. package/docs/harnesses/think.md +215 -0
  249. package/docs/index.md +99 -75
  250. package/docs/lifecycle.md +42 -3
  251. package/docs/queue.md +2 -3
  252. package/docs/scheduling.md +2 -6
  253. package/docs/search-the-web.md +135 -0
  254. package/package.json +148 -44
  255. package/dist/ai-fm7xx1BK.js +0 -312
  256. package/dist/ai-fm7xx1BK.js.map +0 -1
  257. package/dist/browser/ai-sdk.js.map +0 -1
  258. package/dist/browser/index.js.map +0 -1
  259. package/dist/browser/tanstack-ai.js.map +0 -1
  260. package/dist/browser-tool-CQ10e6Kj.js.map +0 -1
  261. package/dist/capability-B4WbF81e.js.map +0 -1
  262. package/dist/channel-Bnm4S7T2.d.ts +0 -491
  263. package/dist/channels/ai-sdk.d.ts +0 -46
  264. package/dist/channels/ai-sdk.js +0 -120
  265. package/dist/channels/ai-sdk.js.map +0 -1
  266. package/dist/channels/email.js.map +0 -1
  267. package/dist/channels/index.d.ts +0 -233
  268. package/dist/channels/index.js +0 -608
  269. package/dist/channels/index.js.map +0 -1
  270. package/dist/channels/slack.js +0 -614
  271. package/dist/channels/slack.js.map +0 -1
  272. package/dist/channels/tanstack-ai.d.ts +0 -39
  273. package/dist/channels/tanstack-ai.js +0 -17
  274. package/dist/channels/tanstack-ai.js.map +0 -1
  275. package/dist/channels/telegram.js +0 -427
  276. package/dist/channels/telegram.js.map +0 -1
  277. package/dist/channels/voice.d.ts +0 -45
  278. package/dist/channels/voice.js +0 -122
  279. package/dist/channels/voice.js.map +0 -1
  280. package/dist/client-fyQrLGkD.js.map +0 -1
  281. package/dist/connector-58XDGvc3.js.map +0 -1
  282. package/dist/images-iiTDSyRu.js.map +0 -1
  283. package/dist/index-BB0kqhIz.d.ts +0 -101
  284. package/dist/ingress-BfetZbMO.js +0 -83
  285. package/dist/ingress-BfetZbMO.js.map +0 -1
  286. package/dist/internal-CYlgHl1l.js +0 -59
  287. package/dist/internal-CYlgHl1l.js.map +0 -1
  288. package/dist/lifecycle-CFu6OV03.js.map +0 -1
  289. package/dist/models/ai-sdk/index.js.map +0 -1
  290. package/dist/protocol-kHsr-f0X.js.map +0 -1
  291. package/dist/scheduler-DZ3xQfeK.js.map +0 -1
  292. package/dist/sessions/index.js.map +0 -1
  293. package/dist/src-BZeu-SCD.js.map +0 -1
  294. package/dist/streams-DW-UCOUc.js.map +0 -1
  295. package/dist/surface-bZZJqBka.js +0 -17
  296. package/dist/surface-bZZJqBka.js.map +0 -1
  297. package/dist/tasks-68DESxf0.js.map +0 -1
  298. package/dist/tool-schema-CBjGPrsQ.js +0 -31
  299. package/dist/tool-schema-CBjGPrsQ.js.map +0 -1
  300. package/dist/websockets-Ci-LZFHH.js.map +0 -1
  301. package/docs/channels.md +0 -323
@@ -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 \"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 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,0KACJ,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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"browser-tool-CQ10e6Kj.js","names":[],"sources":["../src/browser/session-connector.ts","../src/browser/browser-tool.ts"],"sourcesContent":["import {\n CodemodeConnector,\n type ConnectorTool,\n type ConnectorTools,\n type ToolExecuteContext\n} from \"@cloudflare/codemode\";\nimport { validateConnectorArgs } from \"./connector-validation\";\nimport {\n CDP_METHOD_NOT_FOUND,\n CdpProtocolError,\n type CdpConnection\n} from \"./cdp-connection\";\nimport type { BrowserConnection } from \"./browser\";\nimport type { SearchableCdpSpec } from \"./spec\";\n\n/**\n * Where a {@link BrowserSessionConnector} gets its browser. Usually a\n * `Browser` installed on the host's Lifecycle.\n */\nexport interface BrowserSource {\n /** The browser's name, for log messages. */\n readonly name: string;\n /** Reattach to the browser (or replace it) and open a CDP connection. */\n connect(): Promise<BrowserConnection>;\n}\n\nexport interface BrowserSessionConnectorOptions {\n /** The persistent browser this connector drives. */\n browser: BrowserSource;\n}\n\n/** A tab a page opened on its own during an execution. */\nexport interface BrowserNewTab {\n targetId: string;\n url?: string;\n title?: string;\n}\n\n/**\n * What happened to the browser during one execution, beyond the code's own\n * result — read by the tool after the run via\n * {@link BrowserSessionConnector.takeReport}.\n */\nexport interface BrowserExecutionReport {\n /** The browser was replaced before this run; earlier page state is gone. */\n restarted: boolean;\n /** Tabs the page opened itself (popups, `target=_blank`). */\n newTabs: BrowserNewTab[];\n}\n\n/** `sessionId` value that addresses the tab the agent is working in. */\nexport const ACTIVE_PAGE_SESSION = \"active\";\n\n/**\n * `cdp.attachToTarget` returns `{ sessionId: \"target:<targetId>\" }` — a\n * stable handle rather than a connection-scoped CDP session id, so replayed\n * code computes identical arguments. `send` resolves it on the live socket.\n */\nconst ATTACH_HANDLE_PREFIX = \"target:\";\n\n/** Chrome's JSON-RPC code for a CDP session id it doesn't know. */\nconst CDP_SESSION_NOT_FOUND = -32001;\n\n/** {@link BrowserSessionConnector} `#liveParams`: nothing to detach. */\nconst DETACHED = Symbol(\"detached\");\n\n/** Reports awaiting their tool call; bounded in case a caller never reads. */\nconst MAX_PENDING_REPORTS = 100;\n\n/**\n * Commands that would end the browser. The host owns its lifetime, so the\n * model can't close it out from under the agent (and lose its logins).\n */\nconst BROWSER_LIFETIME_COMMANDS = new Set([\n \"Browser.close\",\n \"Browser.crash\",\n \"Browser.crashGpuProcess\"\n]);\n\n/** A CDP target id — for this connector, a tab. */\ntype TargetId = string & { readonly __brand: \"TargetId\" };\n\n/** A CDP session id from `Target.attachToTarget`, valid on one socket only. */\ntype LiveSessionId = string & { readonly __brand: \"LiveSessionId\" };\n\n/** The stable `sessionId` handle `cdp.attachToTarget` gives the model. */\ntype AttachHandle = `${typeof ATTACH_HANDLE_PREFIX}${string}`;\n\ninterface TargetInfo {\n targetId: TargetId;\n type: string;\n url?: string;\n title?: string;\n /** The tab that opened this one (popups, `target=_blank` links). */\n openerId?: TargetId;\n}\n\n/** Per-execution state for one pass. Dropped when the pass ends. */\ninterface ExecutionState {\n connected: BrowserConnection;\n /** Live CDP session per target, valid on this socket only. */\n attached: Map<TargetId, LiveSessionId>;\n /** Page targets open when the pass connected. */\n initialPages: Set<TargetId>;\n /** The tab `\"active\"` resolves to — decided lazily on first use. */\n activeTargetId?: TargetId;\n /** Whether {@link activeTargetId} was decided (vs. never needed). */\n activeResolved: boolean;\n}\n\nconst INSTRUCTIONS = [\n \"This browser persists between executions: tabs, cookies, and logins you leave behind are still there next time. It is managed for you — there is nothing to start, close, or reset.\",\n \"The browser can still be replaced between runs (idle timeout, crash) — even between the passes of a run that paused for approval. The tool result then says restarted: true, and earlier tabs, cookies, and logins are gone. Don't assume a page from an earlier run is still there: check where you are before an action that matters (submitting a form, making a purchase).\",\n 'Page-scoped commands (Page.*, Runtime.*, DOM.*, Input.*, Network.*, Emulation.*) need sessionId: \"active\" — the tab you are working in, which stays the same across executions. Example: await cdp.send({ method: \"Page.navigate\", params: { url }, sessionId: \"active\" }).',\n \"Browser- and Target-scoped commands (Target.getTargets, Target.createTarget, Browser.getVersion) take no sessionId.\",\n 'Opening a tab with Target.createTarget makes it the active tab. To switch to another open tab, call cdp.attachToTarget({ targetId }): it becomes active, and the returned sessionId works like \"active\" for that tab, in this run and later ones. A raw Target.attachToTarget sent through cdp.send returns Chrome\\'s own sessionId, which only lasts for the current run.',\n \"Tabs a page opens on its own (popups, target=_blank links) do not become active; the tool result lists them as newTabs.\",\n \"cdp.send returns the CDP method result directly, not the JSON-RPC envelope: Target.createTarget returns { targetId }, Runtime.evaluate returns { result: { value } }, Page.captureScreenshot returns { data }.\",\n \"Issue CDP calls sequentially — never in parallel (no Promise.all): call order is recorded for durable replay.\",\n \"Page.navigate resolves before the page finishes loading. Poll Runtime.evaluate of document.readyState until it is 'complete' before reading the page.\",\n \"Use cdp.spec() to discover commands, events, and types when unsure. If a command fails or times out, check cdp.getDebugLog() for recent protocol traffic.\",\n \"Return small results. Write large page dumps to a file or workspace and pass references around.\"\n].join(\"\\n\");\n\n/**\n * Codemode connector exposing one host-named, persistent browser over the\n * Chrome DevTools Protocol as the `cdp` global.\n *\n * The model never manages sessions: every execution reattaches to the\n * browser (or gets a replacement, reported as `restarted`), and\n * `sessionId: \"active\"` addresses the tab it last worked in. The active tab\n * is stored on the browser's record, so it survives between executions for as\n * long as the tab does.\n *\n * The CDP socket is per pass: opened on the first call, closed when the pass\n * ends. The browser itself outlives the execution.\n */\nexport class BrowserSessionConnector extends CodemodeConnector {\n readonly #browser: BrowserSource;\n #states = new Map<string, ExecutionState>();\n #connecting = new Map<string, Promise<ExecutionState>>();\n /**\n * In memory, on this instance. An execution resumed after an approval\n * pause may run on a fresh connector, whose report starts over. Nothing\n * pauses today — the browser tool wires no approval tools — so reporting\n * across a resume is left for when approvals are added.\n *\n * The same goes for raw CDP session ids from `Target.attachToTarget` sent\n * through `send`: they belong to one pass's socket, so a replay after a\n * pause would reuse a dead id. The stable handles from `attachToTarget`\n * are the replay-safe path.\n */\n #reports = new Map<string, BrowserExecutionReport>();\n\n constructor(\n ctx: DurableObjectState | ExecutionContext,\n options: BrowserSessionConnectorOptions\n ) {\n super(ctx, {});\n this.#browser = options.browser;\n }\n\n name(): string {\n return \"cdp\";\n }\n\n protected instructions(): string {\n return INSTRUCTIONS;\n }\n\n protected override tool(name: string, tool: ConnectorTool): ConnectorTool {\n return validateConnectorArgs(this.name(), name, tool);\n }\n\n protected tools(): ConnectorTools {\n return {\n send: {\n description:\n 'Send a CDP command and return its method result directly. Pass sessionId: \"active\" for page-scoped commands; omit it for Browser/Target commands.',\n inputSchema: {\n type: \"object\",\n properties: {\n method: {\n type: \"string\",\n description: 'CDP method, e.g. \"Page.navigate\"'\n },\n params: {\n type: \"object\",\n description: \"CDP command parameters\"\n },\n sessionId: {\n type: \"string\",\n description:\n '\"active\" for the current tab, or a handle from attachToTarget. Omit for Browser/Target commands.'\n },\n timeoutMs: {\n type: \"number\",\n description: \"Per-command timeout override in milliseconds\"\n }\n },\n required: [\"method\"]\n },\n execute: async (args, ctx) => {\n const { method, params, sessionId, timeoutMs } = args as {\n method: string;\n params?: Record<string, unknown>;\n sessionId?: string;\n timeoutMs?: number;\n };\n if (BROWSER_LIFETIME_COMMANDS.has(method)) {\n throw new Error(\n `${method} is not available: this browser is managed for you ` +\n `and persists between runs. To close a tab, send ` +\n `Target.closeTarget with its targetId.`\n );\n }\n const state = await this.#state(this.#executionId(ctx));\n const live = await this.#resolveSessionId(state, sessionId);\n const liveParams = this.#liveParams(state, method, params);\n if (liveParams === DETACHED) return {};\n let result: unknown;\n try {\n result = await state.connected.cdp.send(method, liveParams, {\n sessionId: live,\n timeoutMs\n });\n } catch (error) {\n throw await this.#teach(state, error, method, sessionId);\n }\n this.#observe(state, method, liveParams, result);\n return result;\n }\n },\n\n attachToTarget: {\n description:\n \"Switch to an open tab: make it the active tab and return { sessionId } — a handle you can pass to page-scoped send calls for that tab.\",\n inputSchema: {\n type: \"object\",\n properties: {\n targetId: {\n type: \"string\",\n description: \"Target id from Target.getTargets/createTarget\"\n },\n timeoutMs: { type: \"number\" }\n },\n required: [\"targetId\"]\n },\n outputSchema: {\n type: \"object\",\n properties: {\n sessionId: {\n type: \"string\",\n description: \"Session handle for page-scoped send calls\"\n }\n },\n required: [\"sessionId\"]\n },\n execute: async (args, ctx) => {\n const { targetId, timeoutMs } = args as {\n targetId: TargetId;\n timeoutMs?: number;\n };\n const state = await this.#state(this.#executionId(ctx));\n await this.#attach(state, targetId, timeoutMs);\n this.#setActive(state, targetId);\n return { sessionId: attachHandle(targetId) };\n }\n },\n\n spec: {\n description:\n \"Return the searchable Chrome DevTools Protocol spec: domains with their commands, events, and types.\",\n replay: \"reexecute\",\n inputSchema: { type: \"object\", properties: {} },\n execute: async (_args, ctx): Promise<SearchableCdpSpec> => {\n const state = await this.#state(this.#executionId(ctx));\n return state.connected.spec();\n }\n },\n\n getDebugLog: {\n description:\n \"Return recent CDP protocol traffic (sends, receives, warnings) for this execution — useful to diagnose failures and timeouts.\",\n replay: \"reexecute\",\n inputSchema: {\n type: \"object\",\n properties: {\n limit: {\n type: \"number\",\n minimum: 1,\n description:\n \"Max entries to return, newest last (default 50; only the last 400 are kept)\"\n }\n }\n },\n execute: async (args, ctx) => {\n const { limit } = (args ?? {}) as { limit?: number };\n const state = await this.#state(this.#executionId(ctx));\n return state.connected.cdp.getDebugLog(limit);\n }\n },\n\n clearDebugLog: {\n description: \"Clear the CDP debug log for this execution.\",\n inputSchema: { type: \"object\", properties: {} },\n execute: async (_args, ctx) => {\n const state = await this.#state(this.#executionId(ctx));\n state.connected.cdp.clearDebugLog();\n return null;\n }\n }\n };\n }\n\n // ── Codemode execution hooks ─────────────────────────────────────────────\n\n /**\n * The pass is over: note tabs the page opened, save the active tab on the\n * browser's record, and drop the socket. The browser stays alive.\n */\n override async onPassEnd(executionId: string): Promise<void> {\n const state = this.#states.get(executionId);\n if (!state) return;\n this.#states.delete(executionId);\n try {\n const open = await this.#recordNewTabs(executionId, state);\n await this.#saveActiveTarget(state, open);\n } finally {\n state.connected.cdp.disconnect();\n }\n }\n\n /** Nothing per execution outlives the pass; the named browser persists. */\n override async disposeExecution(executionId: string): Promise<void> {\n const state = this.#states.get(executionId);\n if (!state) return;\n this.#states.delete(executionId);\n state.connected.cdp.disconnect();\n }\n\n /**\n * Take (and forget) what happened to the browser during an execution.\n * `undefined` when the execution never touched the browser. Covers the\n * passes this instance ran; see the note on `#reports`.\n */\n takeReport(executionId: string): BrowserExecutionReport | undefined {\n const report = this.#reports.get(executionId);\n this.#reports.delete(executionId);\n return report;\n }\n\n // ── Internals ────────────────────────────────────────────────────────────\n\n #executionId(ctx: ToolExecuteContext | undefined): string {\n if (!ctx?.executionId) {\n throw new Error(\"Browser tools must run inside a codemode execution\");\n }\n return ctx.executionId;\n }\n\n /**\n * Connect once per pass. Concurrent first calls (model code that uses\n * Promise.all despite the instructions) share one connect.\n */\n #state(executionId: string): Promise<ExecutionState> {\n const existing = this.#states.get(executionId);\n if (existing) return Promise.resolve(existing);\n const inFlight = this.#connecting.get(executionId);\n if (inFlight) return inFlight;\n const promise = this.#connect(executionId).finally(() => {\n this.#connecting.delete(executionId);\n });\n this.#connecting.set(executionId, promise);\n return promise;\n }\n\n async #connect(executionId: string): Promise<ExecutionState> {\n const connected = await this.#browser.connect();\n let initialPages: TargetInfo[];\n try {\n initialPages = await this.#pages(connected.cdp);\n } catch (error) {\n connected.cdp.disconnect();\n throw error;\n }\n const state: ExecutionState = {\n connected,\n attached: new Map(),\n initialPages: new Set(initialPages.map((page) => page.targetId)),\n activeResolved: false\n };\n this.#states.set(executionId, state);\n this.#report(executionId, connected.restarted);\n return state;\n }\n\n #report(executionId: string, restarted: boolean): BrowserExecutionReport {\n let report = this.#reports.get(executionId);\n if (!report) {\n report = { restarted, newTabs: [] };\n this.#reports.set(executionId, report);\n if (this.#reports.size > MAX_PENDING_REPORTS) {\n const oldest = this.#reports.keys().next().value;\n if (oldest !== undefined) this.#reports.delete(oldest);\n }\n }\n // A replacement on any pass means earlier page state is gone.\n report.restarted ||= restarted;\n return report;\n }\n\n /**\n * Add tabs the page opened this pass to the execution's report, and drop\n * earlier ones that have since closed: `newTabs` lists page-opened tabs\n * still open when the execution ends, across every pass. Returns the open\n * tabs, or `undefined` when the browser couldn't be asked.\n */\n async #recordNewTabs(\n executionId: string,\n state: ExecutionState\n ): Promise<Set<TargetId> | undefined> {\n let pages: TargetInfo[];\n try {\n pages = await this.#pages(state.connected.cdp);\n } catch (error) {\n this.#warn(\"list the open tabs\", error);\n return undefined;\n }\n const report = this.#report(executionId, state.connected.restarted);\n const opened = new Set<string>(report.newTabs.map((tab) => tab.targetId));\n for (const page of pages) {\n // Chrome sets openerId only on tabs a page opened, never on\n // Target.createTarget tabs — including ones another execution sharing\n // this browser created meanwhile.\n if (!state.initialPages.has(page.targetId) && page.openerId) {\n opened.add(page.targetId);\n }\n }\n report.newTabs = pages\n .filter((page) => opened.has(page.targetId))\n .map(({ targetId, url, title }) => ({ targetId, url, title }));\n return new Set(pages.map((page) => page.targetId));\n }\n\n /**\n * Save the tab this pass settled on, forgetting one that has since closed.\n * Without the list of open tabs, save the choice unchecked: the next\n * execution checks the stored tab is still open before using it.\n */\n async #saveActiveTarget(\n state: ExecutionState,\n open: Set<TargetId> | undefined\n ): Promise<void> {\n const stored = state.connected.activeTargetId as TargetId | undefined;\n const candidate = state.activeResolved ? state.activeTargetId : stored;\n const active =\n candidate && (!open || open.has(candidate)) ? candidate : undefined;\n if (active === stored) return;\n try {\n await state.connected.setActiveTarget(active);\n } catch (error) {\n this.#warn(\"save the active tab\", error);\n }\n }\n\n #warn(action: string, error: unknown): void {\n console.warn(\n `[agents/browser] Failed to ${action} for browser \"${this.#browser.name}\"`,\n error\n );\n }\n\n async #pages(cdp: CdpConnection): Promise<TargetInfo[]> {\n const result = (await cdp.send(\"Target.getTargets\")) as {\n targetInfos?: TargetInfo[];\n };\n return (result?.targetInfos ?? []).filter((info) => info.type === \"page\");\n }\n\n async #resolveSessionId(\n state: ExecutionState,\n sessionId: string | undefined\n ): Promise<LiveSessionId | undefined> {\n if (sessionId === ACTIVE_PAGE_SESSION) {\n return this.#attach(state, await this.#activeTarget(state));\n }\n if (isAttachHandle(sessionId)) {\n return this.#attach(state, targetOfHandle(sessionId));\n }\n // Omitted, or a raw CDP session id the model got from send().\n return sessionId as LiveSessionId | undefined;\n }\n\n /**\n * Decide which tab `\"active\"` means, once per pass: the stored tab if it\n * is still open, else the only open tab, else a new blank tab when none\n * are open, else the first listed tab.\n */\n async #activeTarget(state: ExecutionState): Promise<TargetId> {\n if (state.activeTargetId) return state.activeTargetId;\n const pages = await this.#pages(state.connected.cdp);\n const stored = state.connected.activeTargetId;\n let targetId = pages.find((page) => page.targetId === stored)?.targetId;\n if (!targetId && pages.length === 0) {\n const created = (await state.connected.cdp.send(\"Target.createTarget\", {\n url: \"about:blank\"\n })) as { targetId: TargetId };\n targetId = created.targetId;\n }\n targetId ??= pages[0].targetId;\n this.#setActive(state, targetId);\n return targetId;\n }\n\n #setActive(state: ExecutionState, targetId: TargetId | undefined): void {\n state.activeTargetId = targetId;\n state.activeResolved = true;\n }\n\n async #attach(\n state: ExecutionState,\n targetId: TargetId,\n timeoutMs?: number\n ): Promise<LiveSessionId> {\n const existing = state.attached.get(targetId);\n if (existing) return existing;\n const live = (await state.connected.cdp.attachToTarget(targetId, {\n timeoutMs\n })) as LiveSessionId;\n state.attached.set(targetId, live);\n return live;\n }\n\n /** Track tab changes the model makes through raw Target commands. */\n #observe(\n state: ExecutionState,\n method: string,\n params: Record<string, unknown> | undefined,\n result: unknown\n ): void {\n const targetId =\n typeof params?.targetId === \"string\"\n ? (params.targetId as TargetId)\n : undefined;\n if (method === \"Target.createTarget\") {\n const created = (result as { targetId?: unknown } | undefined)?.targetId;\n if (typeof created === \"string\") {\n this.#setActive(state, created as TargetId);\n }\n } else if (method === \"Target.attachToTarget\" && targetId) {\n this.#setActive(state, targetId);\n } else if (method === \"Target.closeTarget\" && targetId) {\n state.attached.delete(targetId);\n // The next \"active\" use picks a tab afresh.\n if (state.activeTargetId === targetId) state.activeTargetId = undefined;\n } else if (method === \"Target.detachFromTarget\") {\n // Forget a session the model detached, so the next use reattaches.\n for (const [target, live] of state.attached) {\n if (target === targetId || live === params?.sessionId) {\n state.attached.delete(target);\n }\n }\n }\n }\n\n /**\n * Swap a handle in `Target.detachFromTarget`'s `sessionId` param for the\n * live CDP session id. Returns {@link DETACHED} when the handle's tab isn't\n * attached on this socket, so there is nothing to detach.\n */\n #liveParams(\n state: ExecutionState,\n method: string,\n params: Record<string, unknown> | undefined\n ): Record<string, unknown> | undefined | typeof DETACHED {\n const handle = params?.sessionId;\n if (method !== \"Target.detachFromTarget\" || typeof handle !== \"string\") {\n return params;\n }\n if (!isAttachHandle(handle)) return params;\n const live = state.attached.get(targetOfHandle(handle));\n return live ? { ...params, sessionId: live } : DETACHED;\n }\n\n /**\n * Turn the common protocol mistakes into instructions: sending an\n * event as a command, and a page-scoped command without a session.\n */\n async #teach(\n state: ExecutionState,\n error: unknown,\n method: string,\n sessionId: string | undefined\n ): Promise<unknown> {\n if (!(error instanceof CdpProtocolError)) return error;\n if (\n error.code === CDP_SESSION_NOT_FOUND &&\n sessionId &&\n sessionId !== ACTIVE_PAGE_SESSION &&\n !isAttachHandle(sessionId)\n ) {\n return new Error(\n `${error.message}. CDP session ids don't carry over between runs. ` +\n `Use sessionId: \"active\" for the current tab, or the handle from ` +\n `cdp.attachToTarget({ targetId }) for another tab.`\n );\n }\n if (error.code !== CDP_METHOD_NOT_FOUND) return error;\n if (await this.#isEvent(state, method)) {\n return new Error(\n `${error.message}. '${method}' is a CDP *event*, not a command — it ` +\n `cannot be sent. To wait for page state, poll instead (e.g. ` +\n `Runtime.evaluate of document.readyState until \"complete\").`\n );\n }\n if (!sessionId) {\n return new Error(\n `${error.message}. '${method}' is page-scoped: pass sessionId: ` +\n `\"active\" to run it in the current tab — ` +\n `cdp.send({ method: \"${method}\", params, sessionId: \"active\" }).`\n );\n }\n return error;\n }\n\n async #isEvent(state: ExecutionState, method: string): Promise<boolean> {\n try {\n const spec = await state.connected.spec();\n const domain = method.split(\".\")[0];\n return spec.domains.some(\n (d) => d.name === domain && d.events.some((e) => e.event === method)\n );\n } catch {\n return false;\n }\n }\n}\n\nfunction attachHandle(targetId: TargetId): AttachHandle {\n return `${ATTACH_HANDLE_PREFIX}${targetId}`;\n}\n\nfunction isAttachHandle(\n sessionId: string | undefined\n): sessionId is AttachHandle {\n return sessionId?.startsWith(ATTACH_HANDLE_PREFIX) ?? false;\n}\n\nfunction targetOfHandle(handle: AttachHandle): TargetId {\n return handle.slice(ATTACH_HANDLE_PREFIX.length) as TargetId;\n}\n","/**\n * The harness-neutral core of `browserTool`, shared by the AI SDK\n * (`agents/browser/ai-sdk`) and TanStack AI (`agents/browser/tanstack-ai`)\n * adapters. Internal — not an entry point.\n */\nimport {\n createCodemodeRuntime,\n DynamicWorkerExecutor,\n type ProxyToolOutput\n} from \"@cloudflare/codemode\";\nimport {\n BrowserSessionConnector,\n type BrowserExecutionReport,\n type BrowserNewTab,\n type BrowserSource\n} from \"./session-connector\";\nimport { resolveCtx, transformBrowserResult } from \"./tool-helpers\";\n\nexport interface BrowserToolOptions {\n /**\n * The browser the tool drives — usually a `Browser` installed on the\n * host's Lifecycle. Tools built from the same `Browser` share it.\n */\n browser: BrowserSource;\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 /** Sandbox execution timeout in milliseconds. Defaults to 30000 (30s). */\n timeoutMs?: number;\n\n /**\n * Durable Object state for the codemode runtime facet. Optional inside an\n * Agent (resolved via `getCurrentAgent()`); pass it explicitly elsewhere.\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/** The model's input to `browserTool`: JavaScript to run. */\nexport interface BrowserToolInput {\n code: string;\n}\n\n/**\n * The result of one `browserTool` run: the codemode execution result,\n * plus what happened to the browser.\n */\nexport type BrowserToolOutput = ProxyToolOutput & {\n /** The browser was replaced before this run; earlier tabs are gone. */\n restarted?: true;\n /** A sentence telling the model what `restarted` means for it. */\n notice?: string;\n /** Tabs the page opened itself (popups, `target=_blank` links). */\n newTabs?: BrowserNewTab[];\n};\n\nconst RESTARTED_NOTICE =\n \"The browser was restarted before this run (it expired or was closed): earlier tabs, logins, and page state are gone. Your code ran in a fresh browser — navigate again before relying on page state.\";\n\n/**\n * A codemode runtime name per browser. Browser names are host-chosen and\n * unrestricted; runtime names allow only `[a-zA-Z0-9_.-]`, so anything else\n * (including `.`, the escape) is hex-escaped.\n */\nfunction browserRuntimeName(name: string): string {\n const escaped = name.replace(\n /[^a-zA-Z0-9_-]/g,\n (char) => `.${char.codePointAt(0)?.toString(16)}.`\n );\n return `browser-tool_${escaped}`;\n}\n\n/** Add what happened to the browser to the codemode result. */\nfunction withBrowserReport(\n output: ProxyToolOutput,\n report: BrowserExecutionReport | undefined\n): BrowserToolOutput {\n if (!report) return output;\n return {\n ...output,\n ...(report.restarted\n ? { restarted: true as const, notice: RESTARTED_NOTICE }\n : {}),\n ...(report.newTabs.length > 0 ? { newTabs: report.newTabs } : {})\n };\n}\n\n/** Report sentences that must survive a screenshot's text summary. */\nexport function browserReportNotes(output: BrowserToolOutput): string[] {\n const notes: string[] = [];\n if (output.notice) notes.push(output.notice);\n if (output.newTabs?.length) {\n notes.push(\n `New tabs opened by the page: ${JSON.stringify(output.newTabs)}`\n );\n }\n return notes;\n}\n\n/**\n * Build the codemode runtime and connector for one `browserTool`, and wrap\n * `execute` so each result carries the browser report.\n */\nexport function createBrowserToolCore(\n options: BrowserToolOptions,\n adapter: {\n /** What the adapter does with screenshots, told to the model. */\n screenshotHint: string;\n }\n) {\n const ctx = resolveCtx(options);\n if (!ctx) {\n throw new Error(\n \"browserTool 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 BrowserSessionConnector(ctx, {\n browser: options.browser\n });\n const runtime = createCodemodeRuntime({\n ctx,\n executor: new DynamicWorkerExecutor({\n loader: options.loader,\n timeout: options.timeoutMs\n }),\n connectors: [connector],\n name: browserRuntimeName(options.browser.name),\n transformResult: transformBrowserResult\n });\n\n const codemodeTool = runtime.tool({\n connectorHints: {\n cdp: `A persistent browser over CDP — tabs and logins carry over between runs. Use sessionId: \"active\" for page commands. ${adapter.screenshotHint}`\n }\n });\n\n return {\n description: codemodeTool.description,\n inputSchema: codemodeTool.inputSchema,\n execute: async (\n input: BrowserToolInput,\n executeOptions?: unknown\n ): Promise<BrowserToolOutput> => {\n const output = await codemodeTool.execute(input, executeOptions);\n return withBrowserReport(\n output,\n connector.takeReport(output.executionId)\n );\n }\n };\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"capability-B4WbF81e.js","names":[],"sources":["../src/lifecycle/capability.ts"],"sourcesContent":["import type {\n CapabilityStartContext,\n DurableObjectCapability\n} from \"./capability-runner\";\nimport type { Connection } from \"./types\";\nimport type { LifecycleJobs } from \"./job-queue\";\n\n/** Opaque address understood by a Lifecycle routing transport. */\nexport type LifecycleRouteAddress = {\n /** Stable equality and storage key. */\n readonly key: string;\n /** Transport-owned serialized address. */\n readonly data: string;\n};\n\n/** Context supplied with a routed capability message. */\nexport type LifecycleRouteContext = {\n /** Address of the sending Lifecycle, or undefined for an unrouted root. */\n readonly source: LifecycleRouteAddress | undefined;\n /** Capability-owned message payload. */\n readonly payload: unknown;\n};\n\n/** Startup state of a Lifecycle. */\nexport type LifecycleStatus = \"zero\" | \"starting\" | \"started\";\n\n/** Best-effort telemetry available to every Lifecycle capability. */\nexport type LifecycleEvents = {\n /** Publish an event under this capability's stable identity. */\n readonly emit: (type: string, payload: unknown) => void;\n};\n\n/** Routing available to every Lifecycle capability. */\nexport type LifecycleRoutes = {\n /** This Lifecycle's transport address, or undefined at the route root. */\n readonly source: LifecycleRouteAddress | undefined;\n /** Route a capability-owned message to the root Lifecycle. */\n readonly toRoot: (payload: unknown) => Promise<unknown>;\n /** Route a capability-owned message to another Lifecycle. */\n readonly to: (\n target: LifecycleRouteAddress,\n payload: unknown\n ) => Promise<unknown>;\n};\n\n/**\n * Ambient scope a capability supplies when entering host context on\n * behalf of a live connection or request.\n */\nexport type LifecycleHostContextScope = {\n /** The connection the callback runs on behalf of, when there is one. */\n readonly connection?: Connection;\n /** The request the callback runs on behalf of, when there is one. */\n readonly request?: Request;\n};\n\n/**\n * The platform's hibernatable-socket surface, exposed narrowly so a\n * capability that owns connections (e.g. WebSockets) can accept and\n * enumerate them without holding the whole `DurableObjectState`.\n * These are workerd API names, not Lifecycle modeling sockets.\n */\nexport type LifecycleSockets = {\n /** Accept a socket into hibernation under the given tags. */\n readonly accept: (ws: WebSocket, tags: string[]) => void;\n /** Every hibernated socket on the object, optionally by tag. */\n readonly get: (tag?: string) => WebSocket[];\n};\n\n/**\n * Standard services granted to every installed Lifecycle capability.\n *\n * @experimental The API surface may change before stabilizing.\n */\nexport type LifecycleServices = {\n /** The host's Durable Object name; see `Lifecycle.name`. */\n readonly name: string;\n /** The host class name, as exported from the Worker. */\n readonly className: string;\n readonly storage: DurableObjectStorage;\n readonly sockets: LifecycleSockets;\n /**\n * Resolve once Lifecycle startup has finished, starting it if needed, and\n * reject if that startup fails. Calls made from inside startup resolve\n * immediately.\n */\n readonly ready: () => Promise<void>;\n /**\n * Startup state: `\"zero\"` before startup, `\"starting\"` while capability\n * and host startup hooks run, `\"started\"` once `ready()` resolves without\n * waiting.\n */\n readonly status: () => LifecycleStatus;\n /**\n * This capability's scoped access to the Lifecycle-owned work queue.\n * Pushed items are dispatched to `onJob` when due; every queue mutation\n * re-arms the physical alarm automatically.\n */\n readonly jobs: LifecycleJobs;\n /**\n * Keep work this capability hands off at a bounded `onJob` return inside\n * the current alarm's memory-limit breaker domain (#1825). Returns false,\n * tracking nothing, outside an alarm invocation.\n */\n readonly trackAlarmWork: (work: Promise<unknown>) => boolean;\n /**\n * Run a capability-held user callback inside the host invocation context.\n * Capability hooks run outside host context; this is the one boundary for\n * entering it, and a host composition root may substitute its own wrapper\n * (Agent adds tracing span scope). Pass `scope` to make a live\n * connection or request ambient for the callback.\n */\n readonly runInHostContext: (\n fn: () => unknown,\n scope?: LifecycleHostContextScope\n ) => Promise<unknown>;\n readonly events: LifecycleEvents;\n readonly routes: LifecycleRoutes;\n};\n\nconst installedServices = new WeakMap<object, LifecycleServices>();\n\n/**\n * Base class for capabilities that consume standard Lifecycle services.\n *\n * @experimental The API surface may change before stabilizing.\n */\nexport abstract class LifecycleCapability<Props extends object = object> {\n readonly capabilityId: string;\n\n /** How this capability claims traffic; see {@link DurableObjectCapability.claims}. */\n readonly claims: \"selective\" | \"catch-all\" = \"selective\";\n\n protected constructor(capabilityId: string) {\n if (capabilityId.trim() === \"\") {\n throw new Error(\"Lifecycle capability IDs must be non-empty\");\n }\n this.capabilityId = capabilityId;\n }\n\n /** Default startup hook; capabilities override when they own startup work. */\n onStart(_context: CapabilityStartContext<Props>): void {}\n\n /** Standard services when installed, or undefined in isolated unit tests. */\n protected get lifecycleServices(): LifecycleServices | undefined {\n return installedServices.get(this);\n }\n\n /** Standard services supplied when Lifecycle installs this capability. */\n protected get lifecycle(): LifecycleServices {\n const services = this.lifecycleServices;\n if (!services) {\n throw new Error(\n `${this.constructor.name} must be installed with Lifecycle.use() before use`\n );\n }\n return services;\n }\n}\n\n/**\n * @internal Bind the standard service surface to one capability instance.\n * `Lifecycle.use()` calls this during installation. Test a capability by\n * installing it on a minimal Durable Object with a real Lifecycle rather\n * than binding fake services.\n */\nexport function bindLifecycleCapability(\n capability: LifecycleCapability,\n services: LifecycleServices\n): void {\n installedServices.set(capability, services);\n}\n\n/** @internal Read a capability ID without exposing installation internals. */\nexport function lifecycleCapabilityId(\n capability: DurableObjectCapability\n): string | undefined {\n return capability instanceof LifecycleCapability\n ? capability.capabilityId\n : undefined;\n}\n"],"mappings":";AAwHA,MAAM,oCAAoB,IAAI,QAAmC;;;;;;AAOjE,IAAsB,sBAAtB,MAAyE;CAMvE,YAAsB,cAAsB;EAF5C,KAAS,SAAoC;EAG3C,IAAI,aAAa,KAAK,MAAM,IAC1B,MAAM,IAAI,MAAM,4CAA4C;EAE9D,KAAK,eAAe;CACtB;;CAGA,QAAQ,UAA+C,CAAC;;CAGxD,IAAc,oBAAmD;EAC/D,OAAO,kBAAkB,IAAI,IAAI;CACnC;;CAGA,IAAc,YAA+B;EAC3C,MAAM,WAAW,KAAK;EACtB,IAAI,CAAC,UACH,MAAM,IAAI,MACR,GAAG,KAAK,YAAY,KAAK,mDAC3B;EAEF,OAAO;CACT;AACF;;;;;;;AAQA,SAAgB,wBACd,YACA,UACM;CACN,kBAAkB,IAAI,YAAY,QAAQ;AAC5C;;AAGA,SAAgB,sBACd,YACoB;CACpB,OAAO,sBAAsB,sBACzB,WAAW,eACX,KAAA;AACN"}