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,12 +1,12 @@
1
- import { s as LifecycleCapability } from "./capability-runner-DBAnGr6L.js";
1
+ import { s as LifecycleCapability } from "./capability-runner-BiVUtjqC.js";
2
2
  import {
3
- $ as CdpConnection,
4
- F as LiveViewMode,
5
- I as BrowserBinding,
6
- M as BrowserLiveView,
7
- O as BrowserSessionStore,
8
- z as BrowserSessionGuardrails
9
- } from "./connector-Cl-VqPmA.js";
3
+ O as CdpConnection,
4
+ a as ListableBrowserSessionStore,
5
+ d as BrowserBinding,
6
+ m as BrowserSessionGuardrails,
7
+ s as BrowserLiveView,
8
+ u as LiveViewMode
9
+ } from "./session-store-DoPY12xM.js";
10
10
 
11
11
  //#region src/browser/spec.d.ts
12
12
  /**
@@ -87,6 +87,16 @@ interface CdpSpecSource {
87
87
  declare function loadCdpSpec(source: CdpSpecSource): Promise<SearchableCdpSpec>;
88
88
  //#endregion
89
89
  //#region src/browser/browser.d.ts
90
+ /**
91
+ * The scope used when a caller doesn't pick one: every caller that leaves
92
+ * `scope` out shares this one active tab. See {@link BrowserConnectOptions}.
93
+ */
94
+ declare const DEFAULT_BROWSER_SCOPE = "shared";
95
+ /**
96
+ * Scope records kept per browser. Writing a new one past the cap deletes the
97
+ * least recently used; a scope that comes back afterwards starts fresh.
98
+ */
99
+ declare const MAX_BROWSER_SCOPES = 100;
90
100
  /**
91
101
  * Browser Run settings applied every time a browser is created — including
92
102
  * when a lost browser is replaced — so options like guardrails survive a
@@ -136,9 +146,9 @@ interface BrowserOptions {
136
146
  * Where the browser's record lives. Defaults to a
137
147
  * {@link DurableBrowserSessionStore} over the host object's storage, which
138
148
  * requires installing the `Browser` with `Lifecycle.use()`. Passing a store
139
- * lets the `Browser` work without Lifecycle.
149
+ * lets the `Browser` work without Lifecycle. It must implement `list`.
140
150
  */
141
- store?: BrowserSessionStore;
151
+ store?: ListableBrowserSessionStore;
142
152
  /** Default CDP command timeout for {@link Browser.connect}. */
143
153
  timeoutMs?: number;
144
154
  /**
@@ -163,32 +173,45 @@ interface ResolvedBrowser {
163
173
  restarted: boolean;
164
174
  createdAt: number;
165
175
  updatedAt: number;
176
+ }
177
+ interface BrowserConnectOptions {
166
178
  /**
167
- * The tab the agent last worked in, when one was recorded for this
168
- * browser. Absent on a browser this resolution created; a replacement a
169
- * concurrent caller already created and used may carry one.
179
+ * Who is driving. Connections with the same scope share an active tab, and
180
+ * each scope keeps its own, so two conversations on one browser don't
181
+ * drive each other's page. Cookies, logins, and the tabs themselves are
182
+ * shared by every scope: a scope splits "which tab is mine", not the
183
+ * browser. Defaults to {@link DEFAULT_BROWSER_SCOPE}.
170
184
  */
171
- activeTargetId?: string;
185
+ scope?: string;
172
186
  }
173
187
  interface BrowserConnection {
174
188
  name: string;
175
189
  sessionId: string;
190
+ /** The scope this connection works in; see {@link BrowserConnectOptions}. */
191
+ scope: string;
176
192
  /**
177
- * `true` when the browser this caller resolved is gone, even if a
178
- * concurrent caller already replaced it. See
179
- * {@link ResolvedBrowser.restarted}.
193
+ * `true` when this scope worked in an earlier browser that has since been
194
+ * replaced, whichever scope's connection replaced it. Its tabs and page
195
+ * state are gone; surface this loudly to the model. `false` on a scope's
196
+ * first connection, even one that replaced a lost browser: the scope had
197
+ * nothing to lose. This is per scope, not a browser-wide signal.
180
198
  */
181
199
  restarted: boolean;
182
- /** See {@link ResolvedBrowser.activeTargetId}. */
200
+ /**
201
+ * The tab this scope last worked in, when it recorded one in this
202
+ * browser.
203
+ */
183
204
  activeTargetId?: string;
184
205
  /** Closing this connection does NOT close the browser. */
185
206
  cdp: CdpConnection;
186
207
  /**
187
- * Record the tab the agent is working in (or clear it with `undefined`).
208
+ * Record the tab this scope is working in (or clear it with `undefined`).
188
209
  * Never resurrects: returns `false` when the browser was closed or
189
210
  * replaced since this connection resolved it.
190
211
  */
191
212
  setActiveTarget(targetId: string | undefined): Promise<boolean>;
213
+ /** The tabs other scopes are working in, in this browser. */
214
+ targetsInOtherScopes(): Promise<Set<string>>;
192
215
  /**
193
216
  * The Chrome DevTools Protocol description this browser serves, read from
194
217
  * the browser itself (cached per binding).
@@ -224,17 +247,17 @@ declare class Browser extends LifecycleCapability {
224
247
  */
225
248
  resolve(): Promise<ResolvedBrowser>;
226
249
  /**
227
- * Resolve the browser and open a CDP connection to it. Commands sent over
228
- * the connection refresh the record's `updatedAt` (throttled to
250
+ * Resolve the browser and open a CDP connection to it for one scope (see
251
+ * {@link BrowserConnectOptions.scope}). Commands sent over the connection
252
+ * refresh the record's `updatedAt` (throttled to
229
253
  * {@link SESSION_TOUCH_INTERVAL_MS}), so hosts can tell an actively used
230
254
  * browser from one the platform has likely reclaimed.
231
255
  *
232
256
  * If the browser expires between the liveness probe and the WebSocket
233
257
  * upgrade, the record is retired and the browser resolved once more, so
234
- * the caller gets a fresh browser with `restarted: true` instead of an
235
- * error.
258
+ * the caller gets a fresh browser instead of an error.
236
259
  */
237
- connect(): Promise<BrowserConnection>;
260
+ connect(options?: BrowserConnectOptions): Promise<BrowserConnection>;
238
261
  /**
239
262
  * Close the browser: retire the record (so the next resolve reports
240
263
  * `restarted: true`) and delete its Browser Run session. Returns `false`
@@ -258,18 +281,21 @@ declare class Browser extends LifecycleCapability {
258
281
  }
259
282
  //#endregion
260
283
  export {
261
- BrowserRunOptions as a,
262
- browserRun as c,
263
- CdpSpecSource as d,
264
- SearchableCdpSpec as f,
265
- BrowserProvider as i,
266
- CdpField as l,
267
- BrowserConnection as n,
268
- BrowserRunProvider as o,
269
- loadCdpSpec as p,
270
- BrowserOptions as r,
271
- ResolvedBrowser as s,
284
+ BrowserProvider as a,
285
+ DEFAULT_BROWSER_SCOPE as c,
286
+ browserRun as d,
287
+ CdpField as f,
288
+ loadCdpSpec as g,
289
+ SearchableCdpSpec as h,
290
+ BrowserOptions as i,
291
+ MAX_BROWSER_SCOPES as l,
292
+ CdpSpecSource as m,
293
+ BrowserConnectOptions as n,
294
+ BrowserRunOptions as o,
295
+ CdpItems as p,
296
+ BrowserConnection as r,
297
+ BrowserRunProvider as s,
272
298
  Browser as t,
273
- CdpItems as u
299
+ ResolvedBrowser as u
274
300
  };
275
- //# sourceMappingURL=browser-DE3d-RLk.d.ts.map
301
+ //# sourceMappingURL=browser-D30VlpcA.d.ts.map
@@ -1,8 +1,9 @@
1
1
  import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, r as _assertClassBrand, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-DZBYAB34.js";
2
2
  import { t as _classPrivateMethodInitSpec } from "./classPrivateMethodInitSpec-qMjJ6sHQ.js";
3
- import { E as CdpProtocolError, k as validateConnectorArgs } from "./connector-58XDGvc3.js";
4
- import { o as resolveCtx, s as transformBrowserResult } from "./ai-fm7xx1BK.js";
5
- import { CodemodeConnector, DynamicWorkerExecutor, createCodemodeRuntime } from "@cloudflare/codemode";
3
+ import { s as validateConnectorArgs, v as CdpProtocolError } from "./session-store-Do4xkDcf.js";
4
+ import { i as transformBrowserResult, r as resolveCtx } from "./tool-helpers-O5BDVEAY.js";
5
+ import "./browser-CEdNlsaD.js";
6
+ import { CodemodeConnector, DynamicWorkerExecutor, MAX_DURABLE_VALUE_BYTES, createCodemodeRuntime } from "@cloudflare/codemode";
6
7
  /**
7
8
  * `cdp.attachToTarget` returns `{ sessionId: "target:<targetId>" }` — a
8
9
  * stable handle rather than a connection-scoped CDP session id, so replayed
@@ -11,6 +12,18 @@ import { CodemodeConnector, DynamicWorkerExecutor, createCodemodeRuntime } from
11
12
  const ATTACH_HANDLE_PREFIX = "target:";
12
13
  /** Chrome's JSON-RPC code for a CDP session id it doesn't know. */
13
14
  const CDP_SESSION_NOT_FOUND = -32001;
15
+ /**
16
+ * Codemode records every `send` result for replay and fails the run when one
17
+ * is over {@link MAX_DURABLE_VALUE_BYTES}, telling the model to write to a
18
+ * file, which this sandbox can't. A full-page screenshot is the usual
19
+ * culprit, so say how to take a smaller one instead.
20
+ */
21
+ function assertScreenshotFits(result) {
22
+ const size = JSON.stringify(result ?? null).length;
23
+ if (size <= MAX_DURABLE_VALUE_BYTES) return;
24
+ const megabytes = (size / 1e6).toFixed(1);
25
+ throw new Error(`The screenshot result is ${megabytes} MB, over the 1 MB limit on a cdp.send result. Capture just the viewport (no captureBeyondViewport or clip larger than the window), or pass format: "jpeg" with quality: 60.`);
26
+ }
14
27
  /** {@link BrowserSessionConnector} `#liveParams`: nothing to detach. */
15
28
  const DETACHED = Symbol("detached");
16
29
  /** Reports awaiting their tool call; bounded in case a caller never reads. */
@@ -24,20 +37,25 @@ const BROWSER_LIFETIME_COMMANDS = /* @__PURE__ */ new Set([
24
37
  "Browser.crash",
25
38
  "Browser.crashGpuProcess"
26
39
  ]);
27
- const INSTRUCTIONS = [
28
- "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.",
29
- "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).",
30
- "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\" }).",
31
- "Browser- and Target-scoped commands (Target.getTargets, Target.createTarget, Browser.getVersion) take no sessionId.",
32
- "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.",
33
- "Tabs a page opens on its own (popups, target=_blank links) do not become active; the tool result lists them as newTabs.",
34
- "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 }.",
35
- "Issue CDP calls sequentially — never in parallel (no Promise.all): call order is recorded for durable replay.",
36
- "Page.navigate resolves before the page finishes loading. Poll Runtime.evaluate of document.readyState until it is 'complete' before reading the page.",
37
- "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.",
38
- "Return small results. Write large page dumps to a file or workspace and pass references around."
40
+ /**
41
+ * How to use the `cdp` connector, one rule per line. `codemode.describe("cdp")`
42
+ * returns these, and `browserTool` puts them in its description. Each rule
43
+ * fixes a mistake models make with CDP, or states something about this
44
+ * browser they can't know; leave out what a model knows already.
45
+ */
46
+ const BROWSER_INSTRUCTIONS = [
47
+ "The browser persists between runs: tabs, cookies, and logins are still there next time. You never start or close it.",
48
+ "If the browser was replaced (idle timeout, crash), the result says restarted: true and earlier tabs and logins are gone. Before an action that matters (submitting a form, a purchase), check you're on the page you expect.",
49
+ "Page-scoped commands (Page.*, Runtime.*, DOM.*, Input.*, Network.*, Emulation.*) need sessionId: \"active\", your tab, which is remembered between runs. Browser.* and Target.* commands take no sessionId.",
50
+ "Other open tabs (Target.getTargets) may belong to other conversations using this browser: don't close, navigate, or attach to a tab you didn't open unless the user asks.",
51
+ "Target.createTarget opens a tab and makes it active. To switch to another open tab, call cdp.attachToTarget({ targetId }); the sessionId it returns keeps working in later runs, while one from a raw Target.attachToTarget only works in the current run.",
52
+ "Tabs a page opens itself (popups, target=_blank links) don't become active; the result lists them as newTabs.",
53
+ "Runtime.evaluate: pass returnByValue: true, or objects come back as a remote reference with no value, and awaitPromise: true for async expressions. The value is at result.value. A thrown error doesn't reject: check exceptionDetails.",
54
+ "Page.navigate returns before the page loads and doesn't throw when it fails: check errorText, then poll document.readyState with Runtime.evaluate until it is 'complete'.",
55
+ "Pick out what you need inside Runtime.evaluate and return only that. Results over about 24,000 characters are cut short, and a single cdp.send result over 1 MB (a full-page screenshot, say) fails the run."
39
56
  ].join("\n");
40
57
  var _browser = /* @__PURE__ */ new WeakMap();
58
+ var _scope = /* @__PURE__ */ new WeakMap();
41
59
  var _states = /* @__PURE__ */ new WeakMap();
42
60
  var _connecting = /* @__PURE__ */ new WeakMap();
43
61
  var _reports = /* @__PURE__ */ new WeakMap();
@@ -49,8 +67,10 @@ var _BrowserSessionConnector_brand = /* @__PURE__ */ new WeakSet();
49
67
  * The model never manages sessions: every execution reattaches to the
50
68
  * browser (or gets a replacement, reported as `restarted`), and
51
69
  * `sessionId: "active"` addresses the tab it last worked in. The active tab
52
- * is stored on the browser's record, so it survives between executions for as
53
- * long as the tab does.
70
+ * is stored per scope (see {@link BrowserSessionConnectorOptions.scope}), so
71
+ * it survives between executions for as long as the tab does, and one
72
+ * scope's executions never land in another scope's tab unless the model
73
+ * attaches to it.
54
74
  *
55
75
  * The CDP socket is per pass: opened on the first call, closed when the pass
56
76
  * ends. The browser itself outlives the execution.
@@ -60,16 +80,18 @@ var BrowserSessionConnector = class extends CodemodeConnector {
60
80
  super(ctx, {});
61
81
  _classPrivateMethodInitSpec(this, _BrowserSessionConnector_brand);
62
82
  _classPrivateFieldInitSpec(this, _browser, void 0);
83
+ _classPrivateFieldInitSpec(this, _scope, void 0);
63
84
  _classPrivateFieldInitSpec(this, _states, /* @__PURE__ */ new Map());
64
85
  _classPrivateFieldInitSpec(this, _connecting, /* @__PURE__ */ new Map());
65
86
  _classPrivateFieldInitSpec(this, _reports, /* @__PURE__ */ new Map());
66
87
  _classPrivateFieldSet2(_browser, this, options.browser);
88
+ _classPrivateFieldSet2(_scope, this, options.scope);
67
89
  }
68
90
  name() {
69
91
  return "cdp";
70
92
  }
71
93
  instructions() {
72
- return INSTRUCTIONS;
94
+ return BROWSER_INSTRUCTIONS;
73
95
  }
74
96
  tool(name, tool) {
75
97
  return validateConnectorArgs(this.name(), name, tool);
@@ -117,6 +139,7 @@ var BrowserSessionConnector = class extends CodemodeConnector {
117
139
  throw await _assertClassBrand(_BrowserSessionConnector_brand, this, _teach).call(this, state, error, method, sessionId);
118
140
  }
119
141
  _assertClassBrand(_BrowserSessionConnector_brand, this, _observe).call(this, state, method, liveParams, result);
142
+ if (method === "Page.captureScreenshot") assertScreenshotFits(result);
120
143
  return result;
121
144
  }
122
145
  },
@@ -190,8 +213,9 @@ var BrowserSessionConnector = class extends CodemodeConnector {
190
213
  };
191
214
  }
192
215
  /**
193
- * The pass is over: note tabs the page opened, save the active tab on the
194
- * browser's record, and drop the socket. The browser stays alive.
216
+ * The pass is over: note tabs the page opened, save the scope's active
217
+ * tab, note whether another scope shares it, and drop the socket. The
218
+ * browser stays alive.
195
219
  */
196
220
  async onPassEnd(executionId) {
197
221
  const state = _classPrivateFieldGet2(_states, this).get(executionId);
@@ -199,7 +223,8 @@ var BrowserSessionConnector = class extends CodemodeConnector {
199
223
  _classPrivateFieldGet2(_states, this).delete(executionId);
200
224
  try {
201
225
  const open = await _assertClassBrand(_BrowserSessionConnector_brand, this, _recordNewTabs).call(this, executionId, state);
202
- await _assertClassBrand(_BrowserSessionConnector_brand, this, _saveActiveTarget).call(this, state, open);
226
+ const active = await _assertClassBrand(_BrowserSessionConnector_brand, this, _saveActiveTarget).call(this, state, open);
227
+ if (active && state.activeResolved) await _assertClassBrand(_BrowserSessionConnector_brand, this, _recordSharedTab).call(this, state, active);
203
228
  } finally {
204
229
  state.connected.cdp.disconnect();
205
230
  }
@@ -242,7 +267,7 @@ function _state(executionId) {
242
267
  return promise;
243
268
  }
244
269
  async function _connect(executionId) {
245
- const connected = await _classPrivateFieldGet2(_browser, this).connect();
270
+ const connected = await _classPrivateFieldGet2(_browser, this).connect(_classPrivateFieldGet2(_scope, this) === void 0 ? void 0 : { scope: _classPrivateFieldGet2(_scope, this) });
246
271
  let initialPages;
247
272
  try {
248
273
  initialPages = await _assertClassBrand(_BrowserSessionConnector_brand, this, _pages).call(this, connected.cdp);
@@ -251,10 +276,13 @@ async function _connect(executionId) {
251
276
  throw error;
252
277
  }
253
278
  const state = {
279
+ executionId,
254
280
  connected,
255
281
  attached: /* @__PURE__ */ new Map(),
282
+ workedIn: /* @__PURE__ */ new Set(),
256
283
  initialPages: new Set(initialPages.map((page) => page.targetId)),
257
- activeResolved: false
284
+ activeResolved: false,
285
+ closed: /* @__PURE__ */ new Set()
258
286
  };
259
287
  _classPrivateFieldGet2(_states, this).set(executionId, state);
260
288
  _assertClassBrand(_BrowserSessionConnector_brand, this, _report).call(this, executionId, connected.restarted);
@@ -265,7 +293,9 @@ function _report(executionId, restarted) {
265
293
  if (!report) {
266
294
  report = {
267
295
  restarted,
268
- newTabs: []
296
+ newTabs: [],
297
+ tabClosed: false,
298
+ tabShared: false
269
299
  };
270
300
  _classPrivateFieldGet2(_reports, this).set(executionId, report);
271
301
  if (_classPrivateFieldGet2(_reports, this).size > MAX_PENDING_REPORTS) {
@@ -292,7 +322,18 @@ async function _recordNewTabs(executionId, state) {
292
322
  }
293
323
  const report = _assertClassBrand(_BrowserSessionConnector_brand, this, _report).call(this, executionId, state.connected.restarted);
294
324
  const opened = new Set(report.newTabs.map((tab) => tab.targetId));
295
- for (const page of pages) if (!state.initialPages.has(page.targetId) && page.openerId) opened.add(page.targetId);
325
+ let grew = true;
326
+ while (grew) {
327
+ grew = false;
328
+ for (const page of pages) {
329
+ const opener = page.openerId;
330
+ if (!opener || opened.has(page.targetId) || state.initialPages.has(page.targetId)) continue;
331
+ if (state.workedIn.has(opener) || opened.has(opener)) {
332
+ opened.add(page.targetId);
333
+ grew = true;
334
+ }
335
+ }
336
+ }
296
337
  report.newTabs = pages.filter((page) => opened.has(page.targetId)).map(({ targetId, url, title }) => ({
297
338
  targetId,
298
339
  url,
@@ -301,20 +342,31 @@ async function _recordNewTabs(executionId, state) {
301
342
  return new Set(pages.map((page) => page.targetId));
302
343
  }
303
344
  /**
304
- * Save the tab this pass settled on, forgetting one that has since closed.
305
- * Without the list of open tabs, save the choice unchecked: the next
306
- * execution checks the stored tab is still open before using it.
345
+ * Save the tab this pass settled on, forgetting one that has since closed,
346
+ * and return it. Without the list of open tabs, save the choice
347
+ * unchecked: the next execution checks the stored tab is still open
348
+ * before using it.
307
349
  */
308
350
  async function _saveActiveTarget(state, open) {
309
351
  const stored = state.connected.activeTargetId;
352
+ if (!state.activeResolved && stored && open && !open.has(stored) && !state.closed.has(stored)) _assertClassBrand(_BrowserSessionConnector_brand, this, _report).call(this, state.executionId, state.connected.restarted).tabClosed = true;
310
353
  const candidate = state.activeResolved ? state.activeTargetId : stored;
311
354
  const active = candidate && (!open || open.has(candidate)) ? candidate : void 0;
312
- if (active === stored) return;
355
+ if (active === stored) return active;
313
356
  try {
314
357
  await state.connected.setActiveTarget(active);
315
358
  } catch (error) {
316
359
  _assertClassBrand(_BrowserSessionConnector_brand, this, _warn).call(this, "save the active tab", error);
317
360
  }
361
+ return active;
362
+ }
363
+ /** Report when another scope is working in this pass's tab too. */
364
+ async function _recordSharedTab(state, active) {
365
+ try {
366
+ if ((await state.connected.targetsInOtherScopes()).has(active)) _assertClassBrand(_BrowserSessionConnector_brand, this, _report).call(this, state.executionId, state.connected.restarted).tabShared = true;
367
+ } catch (error) {
368
+ _assertClassBrand(_BrowserSessionConnector_brand, this, _warn).call(this, "check whether the active tab is shared", error);
369
+ }
318
370
  }
319
371
  function _warn(action, error) {
320
372
  console.warn(`[agents/browser] Failed to ${action} for browser "${_classPrivateFieldGet2(_browser, this).name}"`, error);
@@ -328,17 +380,18 @@ async function _resolveSessionId(state, sessionId) {
328
380
  return sessionId;
329
381
  }
330
382
  /**
331
- * Decide which tab `"active"` means, once per pass: the stored tab if it
332
- * is still open, else the only open tab, else a new blank tab when none
333
- * are open, else the first listed tab.
383
+ * Decide which tab `"active"` means, once per pass: the scope's stored tab
384
+ * if it is still open, else a new blank tab. Never a tab the scope didn't
385
+ * open, even a blank one: it may be another scope's, or a popup.
334
386
  */
335
387
  async function _activeTarget(state) {
336
388
  if (state.activeTargetId) return state.activeTargetId;
337
- const pages = await _assertClassBrand(_BrowserSessionConnector_brand, this, _pages).call(this, state.connected.cdp);
338
- const stored = state.connected.activeTargetId;
389
+ const { connected } = state;
390
+ const pages = await _assertClassBrand(_BrowserSessionConnector_brand, this, _pages).call(this, connected.cdp);
391
+ const stored = connected.activeTargetId;
339
392
  let targetId = pages.find((page) => page.targetId === stored)?.targetId;
340
- if (!targetId && pages.length === 0) targetId = (await state.connected.cdp.send("Target.createTarget", { url: "about:blank" })).targetId;
341
- targetId ??= pages[0].targetId;
393
+ if (!targetId && stored && !state.closed.has(stored)) _assertClassBrand(_BrowserSessionConnector_brand, this, _report).call(this, state.executionId, connected.restarted).tabClosed = true;
394
+ if (!targetId) targetId = (await connected.cdp.send("Target.createTarget", { url: "about:blank" })).targetId;
342
395
  _assertClassBrand(_BrowserSessionConnector_brand, this, _setActive).call(this, state, targetId);
343
396
  return targetId;
344
397
  }
@@ -351,6 +404,7 @@ async function _attach(state, targetId, timeoutMs) {
351
404
  if (existing) return existing;
352
405
  const live = await state.connected.cdp.attachToTarget(targetId, { timeoutMs });
353
406
  state.attached.set(targetId, live);
407
+ state.workedIn.add(targetId);
354
408
  return live;
355
409
  }
356
410
  /** Track tab changes the model makes through raw Target commands. */
@@ -359,9 +413,12 @@ function _observe(state, method, params, result) {
359
413
  if (method === "Target.createTarget") {
360
414
  const created = result?.targetId;
361
415
  if (typeof created === "string") _assertClassBrand(_BrowserSessionConnector_brand, this, _setActive).call(this, state, created);
362
- } else if (method === "Target.attachToTarget" && targetId) _assertClassBrand(_BrowserSessionConnector_brand, this, _setActive).call(this, state, targetId);
363
- else if (method === "Target.closeTarget" && targetId) {
416
+ } else if (method === "Target.attachToTarget" && targetId) {
417
+ _assertClassBrand(_BrowserSessionConnector_brand, this, _setActive).call(this, state, targetId);
418
+ state.workedIn.add(targetId);
419
+ } else if (method === "Target.closeTarget" && targetId) {
364
420
  state.attached.delete(targetId);
421
+ state.closed.add(targetId);
365
422
  if (state.activeTargetId === targetId) state.activeTargetId = void 0;
366
423
  } else if (method === "Target.detachFromTarget") {
367
424
  for (const [target, live] of state.attached) if (target === targetId || live === params?.sessionId) state.attached.delete(target);
@@ -415,11 +472,17 @@ function targetOfHandle(handle) {
415
472
  //#endregion
416
473
  //#region src/browser/browser-tool.ts
417
474
  /**
418
- * The harness-neutral core of `browserTool`, shared by the AI SDK
419
- * (`agents/browser/ai-sdk`) and TanStack AI (`agents/browser/tanstack-ai`)
420
- * adapters. Internal — not an entry point.
475
+ * The harness-neutral core of `browserTool`, shared by the adapters in
476
+ * `tools/` (`agents/browser/ai-sdk`, `agents/browser/tanstack-ai`,
477
+ * `agents/browser/pi`).
478
+ * Internal — not an entry point.
421
479
  */
480
+ const DEFAULT_TIMEOUT_MS = 6e4;
422
481
  const RESTARTED_NOTICE = "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.";
482
+ const TAB_CLOSED_NOTICE = "The tab you were working in was closed (by another conversation using this browser, or a person). `sessionId: \"active\"` now opens a blank tab — navigate again before relying on page state.";
483
+ const TAB_SHARED_NOTICE = "Another conversation using this browser is also working in your tab, so its page may change between your runs. Open a tab of your own with Target.createTarget if you need one.";
484
+ /** Scopes whose runtime and connector stay built; older ones are rebuilt. */
485
+ const MAX_CACHED_SCOPES = 32;
423
486
  /**
424
487
  * A codemode runtime name per browser. Browser names are host-chosen and
425
488
  * unrestricted; runtime names allow only `[a-zA-Z0-9_.-]`, so anything else
@@ -428,15 +491,35 @@ const RESTARTED_NOTICE = "The browser was restarted before this run (it expired
428
491
  function browserRuntimeName(name) {
429
492
  return `browser-tool_${name.replace(/[^a-zA-Z0-9_-]/g, (char) => `.${char.codePointAt(0)?.toString(16)}.`)}`;
430
493
  }
494
+ /**
495
+ * The latest run queued per browser and scope, so runs in one scope go one
496
+ * at a time, even from tools built separately (one per turn, say). Per
497
+ * isolate, which is per Durable Object.
498
+ */
499
+ const runQueues = /* @__PURE__ */ new WeakMap();
500
+ function queued(browser, scope, run) {
501
+ let queues = runQueues.get(browser);
502
+ if (!queues) runQueues.set(browser, queues = /* @__PURE__ */ new Map());
503
+ const next = (queues.get(scope) ?? Promise.resolve()).then(run, run);
504
+ const settled = next.catch(() => void 0);
505
+ queues.set(scope, settled);
506
+ settled.then(() => {
507
+ if (queues.get(scope) === settled) queues.delete(scope);
508
+ });
509
+ return next;
510
+ }
431
511
  /** Add what happened to the browser to the codemode result. */
432
512
  function withBrowserReport(output, report) {
433
513
  if (!report) return output;
514
+ const notices = [
515
+ report.restarted && RESTARTED_NOTICE,
516
+ report.tabClosed && !report.restarted && TAB_CLOSED_NOTICE,
517
+ report.tabShared && TAB_SHARED_NOTICE
518
+ ].filter((notice) => typeof notice === "string");
434
519
  return {
435
520
  ...output,
436
- ...report.restarted ? {
437
- restarted: true,
438
- notice: RESTARTED_NOTICE
439
- } : {},
521
+ ...report.restarted ? { restarted: true } : {},
522
+ ...notices.length > 0 ? { notice: notices.join(" ") } : {},
440
523
  ...report.newTabs.length > 0 ? { newTabs: report.newTabs } : {}
441
524
  };
442
525
  }
@@ -450,31 +533,84 @@ function browserReportNotes(output) {
450
533
  /**
451
534
  * Build the codemode runtime and connector for one `browserTool`, and wrap
452
535
  * `execute` so each result carries the browser report.
536
+ *
537
+ * Each scope gets its own connector (which knows its scope) on the same
538
+ * codemode runtime, built on first use. Runs in one scope wait for each
539
+ * other, since they drive one tab; runs in different scopes go in parallel.
453
540
  */
454
541
  function createBrowserToolCore(options, adapter) {
455
542
  const ctx = resolveCtx(options);
456
543
  if (!ctx) throw new Error("browserTool requires a Durable Object 'ctx' — pass it explicitly, or call from within an Agent so it can be resolved via getCurrentAgent()");
457
- const connector = new BrowserSessionConnector(ctx, { browser: options.browser });
458
- const codemodeTool = createCodemodeRuntime({
459
- ctx,
460
- executor: new DynamicWorkerExecutor({
461
- loader: options.loader,
462
- timeout: options.timeoutMs
463
- }),
464
- connectors: [connector],
465
- name: browserRuntimeName(options.browser.name),
466
- transformResult: transformBrowserResult
467
- }).tool({ connectorHints: { cdp: `A persistent browser over CDP — tabs and logins carry over between runs. Use sessionId: "active" for page commands. ${adapter.screenshotHint}` } });
544
+ const timeout = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
545
+ const executor = new DynamicWorkerExecutor({
546
+ loader: options.loader,
547
+ timeout
548
+ });
549
+ const description = [
550
+ "Run JavaScript that drives a persistent Chrome browser over the Chrome DevTools Protocol (CDP).",
551
+ "",
552
+ "Write an async arrow function, `async () => { ... }`, and return a small JSON value. Use the `cdp` global to reach the browser. `fetch` is blocked and there is no file system or Node.js API.",
553
+ "",
554
+ "## cdp",
555
+ "",
556
+ "- `cdp.send({ method, params?, sessionId?, timeoutMs? })` sends a CDP command and returns its result without the JSON-RPC envelope: `Page.navigate` returns `{ frameId, errorText? }`, `Runtime.evaluate` returns `{ result: { value }, exceptionDetails? }`.",
557
+ "- `cdp.attachToTarget({ targetId })` makes an open tab active and returns `{ sessionId }`.",
558
+ "- `cdp.spec()` returns the whole protocol (about 650 KB) as `{ domains: [{ name, commands, events, types }] }`. Filter it in code, for example `spec.domains.find((d) => d.name === \"Page\").commands.find((c) => c.name === \"navigate\")`.",
559
+ "- `cdp.getDebugLog()` returns recent protocol traffic, for when a command fails or times out.",
560
+ "",
561
+ "## Rules",
562
+ "",
563
+ [
564
+ BROWSER_INSTRUCTIONS,
565
+ `Each run times out after ${Math.round(timeout / 1e3)}s, so split long jobs (crawling a site, say) across runs.`,
566
+ adapter.screenshotHint
567
+ ].join("\n").split("\n").map((rule) => `- ${rule}`).join("\n"),
568
+ "- A `SyntaxError` from \"Failed to start Worker\" means your code didn't parse; its line number points into wrapper code, not yours. Check regex literals and escapes."
569
+ ].join("\n");
570
+ const durableCtx = ctx;
571
+ const scopes = /* @__PURE__ */ new Map();
572
+ function scoped(scope) {
573
+ const cached = scopes.get(scope);
574
+ if (cached) {
575
+ scopes.delete(scope);
576
+ scopes.set(scope, cached);
577
+ return cached;
578
+ }
579
+ const connector = new BrowserSessionConnector(durableCtx, {
580
+ browser: options.browser,
581
+ scope
582
+ });
583
+ const built = {
584
+ connector,
585
+ tool: createCodemodeRuntime({
586
+ ctx: durableCtx,
587
+ executor,
588
+ connectors: [connector],
589
+ name: browserRuntimeName(options.browser.name),
590
+ transformResult: transformBrowserResult
591
+ }).tool({ description })
592
+ };
593
+ scopes.set(scope, built);
594
+ if (scopes.size > MAX_CACHED_SCOPES) {
595
+ const oldest = scopes.keys().next().value;
596
+ if (oldest !== void 0) scopes.delete(oldest);
597
+ }
598
+ return built;
599
+ }
600
+ const defaultScope = options.scope ?? "shared";
601
+ const { inputSchema } = scoped(defaultScope).tool;
468
602
  return {
469
- description: codemodeTool.description,
470
- inputSchema: codemodeTool.inputSchema,
471
- execute: async (input, executeOptions) => {
472
- const output = await codemodeTool.execute(input, executeOptions);
603
+ description,
604
+ inputSchema,
605
+ /** Run `input` in `scope`, which defaults to the tool's `scope`. */
606
+ execute: (input, scope = defaultScope) => queued(options.browser, scope, async () => {
607
+ const { connector, tool } = scoped(scope);
608
+ const output = await tool.execute(input, void 0);
473
609
  return withBrowserReport(output, connector.takeReport(output.executionId));
474
- }
610
+ })
475
611
  };
476
612
  }
477
613
  //#endregion
478
614
  export { createBrowserToolCore as n, browserReportNotes as t };
479
615
 
480
- //# sourceMappingURL=browser-tool-CQ10e6Kj.js.map
616
+ //# sourceMappingURL=browser-tool-4iomavs_.js.map