agents 0.27.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/dist/abortable-C1SpfVoi.js +21 -0
  2. package/dist/abortable-C1SpfVoi.js.map +1 -0
  3. package/dist/{agent-routing-Bhdbvf7s.d.ts → agent-routing-QDcchcQP.d.ts} +10 -15
  4. package/dist/agent-routing.d.ts +1 -1
  5. package/dist/agent-tool-types.d.ts +1 -1
  6. package/dist/{agent-tools-bJZwBMfq.d.ts → agent-tools-DHL2XI7u.d.ts} +2 -2
  7. package/dist/agent-tools.d.ts +1 -1
  8. package/dist/ai-ahruGw7e.d.ts +239 -0
  9. package/dist/{ai-sdk-OoGkFy3s.js → ai-sdk-Bdsf6mSA.js} +2 -2
  10. package/dist/{ai-sdk-OoGkFy3s.js.map → ai-sdk-Bdsf6mSA.js.map} +1 -1
  11. package/dist/browser/ai.d.ts +8 -227
  12. package/dist/browser/ai.js +259 -1
  13. package/dist/browser/ai.js.map +1 -0
  14. package/dist/browser/index.d.ts +56 -46
  15. package/dist/browser/index.js +4 -361
  16. package/dist/browser/{ai-sdk.d.ts → tools/ai-sdk.d.ts} +7 -7
  17. package/dist/browser/{ai-sdk.js → tools/ai-sdk.js} +5 -4
  18. package/dist/browser/tools/ai-sdk.js.map +1 -0
  19. package/dist/browser/tools/pi.d.ts +111 -0
  20. package/dist/browser/tools/pi.js +119 -0
  21. package/dist/browser/tools/pi.js.map +1 -0
  22. package/dist/browser/{tanstack-ai.d.ts → tools/tanstack-ai.d.ts} +39 -37
  23. package/dist/browser/{tanstack-ai.js → tools/tanstack-ai.js} +73 -59
  24. package/dist/browser/tools/tanstack-ai.js.map +1 -0
  25. package/dist/browser-CEdNlsaD.js +481 -0
  26. package/dist/browser-CEdNlsaD.js.map +1 -0
  27. package/dist/{browser-tmkcAAI3.d.ts → browser-D30VlpcA.d.ts} +63 -37
  28. package/dist/{browser-tool-CBqs6hqa.js → browser-tool-4iomavs_.js} +145 -49
  29. package/dist/browser-tool-4iomavs_.js.map +1 -0
  30. package/dist/{browser-tool-Bn-nzJQI.d.ts → browser-tool-vtrPOV8e.d.ts} +24 -5
  31. package/dist/{capability-B4WbF81e.js → capability-DBAKc6UZ.js} +1 -3
  32. package/dist/capability-DBAKc6UZ.js.map +1 -0
  33. package/dist/{capability-runner-O5VbQzqg.d.ts → capability-runner-BiVUtjqC.d.ts} +1 -7
  34. package/dist/chat/index.d.ts +4 -4
  35. package/dist/chat/index.js +1 -1
  36. package/dist/chat-sdk/index.d.ts +1 -1
  37. package/dist/chat-sdk/index.js +1 -1
  38. package/dist/{client-DS3Dz9l0.d.ts → client-BGIQKJhX.d.ts} +2 -2
  39. package/dist/{client-1tEQ1Oqz.js → client-BnYbNT3z.js} +3 -4
  40. package/dist/client-BnYbNT3z.js.map +1 -0
  41. package/dist/client.d.ts +1 -1
  42. package/dist/{connector-58XDGvc3.js → connector-CtZylXdk.js} +3 -603
  43. package/dist/connector-CtZylXdk.js.map +1 -0
  44. package/dist/{connector-Cl-VqPmA.d.ts → connector-DEXzVoaY.d.ts} +9 -374
  45. package/dist/{conversations-DH0ut_8n.d.ts → conversations-FjHDRZyD.d.ts} +4 -4
  46. package/dist/{current-agent-C07C5o5Y.d.ts → current-agent-CKgT5d1O.d.ts} +3 -5
  47. package/dist/current-agent-DhoDkSnH.js.map +1 -1
  48. package/dist/{durable-object-lifecycle-DRqp475_.d.ts → durable-object-lifecycle-DOI_bzCH.d.ts} +2 -2
  49. package/dist/experimental/channels/index.d.ts +2 -2
  50. package/dist/experimental/channels/index.js +2 -2
  51. package/dist/experimental/channels/web/index.d.ts +2 -2
  52. package/dist/experimental/channels/web/index.js +1 -1
  53. package/dist/{gateway-BDC07gYO.d.ts → gateway-BFO6vRDn.d.ts} +2 -2
  54. package/dist/harness/ai-sdk/index.d.ts +2 -2
  55. package/dist/harness/ai-sdk/index.js +1 -1
  56. package/dist/harness/container/index.d.ts +2 -2
  57. package/dist/harness/container/index.js +3 -3
  58. package/dist/harness/opencode/index.d.ts +1 -1
  59. package/dist/harness/opencode/index.js +2 -2
  60. package/dist/harness/pi/index.d.ts +2 -2
  61. package/dist/harness/pi/index.js +3 -3
  62. package/dist/harness/store/index.d.ts +1 -1
  63. package/dist/harness/store/index.js +1 -1
  64. package/dist/harness/think/index.d.ts +2 -2
  65. package/dist/harness/think/index.js +363 -171
  66. package/dist/harness/think/index.js.map +1 -1
  67. package/dist/{index-D0A5tbWu.d.ts → index-BEL3ZV-s.d.ts} +2 -6
  68. package/dist/{index-Ib_4dwOo.d.ts → index-CBt8oRJt.d.ts} +3 -5
  69. package/dist/index.d.ts +4 -4
  70. package/dist/index.js +2 -2
  71. package/dist/input-schema-BQykTEp_.js +20 -0
  72. package/dist/input-schema-BQykTEp_.js.map +1 -0
  73. package/dist/{input-schema-DNrpTu49.js → input-schema-D2QAtHZH.js} +3 -3
  74. package/dist/input-schema-D2QAtHZH.js.map +1 -0
  75. package/dist/internal_context.d.ts +1 -1
  76. package/dist/lifecycle/index.d.ts +3 -3
  77. package/dist/lifecycle/index.js +2 -2
  78. package/dist/{lifecycle-CFu6OV03.js → lifecycle-DGorpAmA.js} +2 -4
  79. package/dist/lifecycle-DGorpAmA.js.map +1 -0
  80. package/dist/mcp/client/index.d.ts +1 -1
  81. package/dist/mcp/client/index.js +1 -1
  82. package/dist/mcp/index.d.ts +1 -1
  83. package/dist/mcp/index.js +2 -2
  84. package/dist/models/ai-sdk/index.js +1 -1
  85. package/dist/models/opencode/index.js +1 -1
  86. package/dist/models/pi-ai/index.js +1 -1
  87. package/dist/{options-pwTNq9z6.d.ts → options-CiIsJHXR.d.ts} +2 -6
  88. package/dist/queue/index.d.ts +1 -1
  89. package/dist/queue/index.js +1 -1
  90. package/dist/{queue-BB-zfIYF.js → queue-CcIjVYTE.js} +2 -2
  91. package/dist/{queue-BB-zfIYF.js.map → queue-CcIjVYTE.js.map} +1 -1
  92. package/dist/react.d.ts +2 -2
  93. package/dist/routing/index.d.ts +2 -2
  94. package/dist/routing/index.js +1 -1
  95. package/dist/{scheduler-CrFCnv_J.d.ts → scheduler-BCLESHSC.d.ts} +2 -12
  96. package/dist/{scheduler-DZ3xQfeK.js → scheduler-FIHv2-kJ.js} +2 -4
  97. package/dist/scheduler-FIHv2-kJ.js.map +1 -0
  98. package/dist/schedules/index.d.ts +1 -1
  99. package/dist/schedules/index.js +1 -1
  100. package/dist/serializable.d.ts +1 -1
  101. package/dist/session-store-Do4xkDcf.js +606 -0
  102. package/dist/session-store-Do4xkDcf.js.map +1 -0
  103. package/dist/session-store-DoPY12xM.d.ts +383 -0
  104. package/dist/sessions/index.d.ts +1 -1
  105. package/dist/sessions/index.js +1 -1
  106. package/dist/{sessions-B9pNuxdT.js → sessions-_vN5BEZX.js} +2 -2
  107. package/dist/{sessions-B9pNuxdT.js.map → sessions-_vN5BEZX.js.map} +1 -1
  108. package/dist/{src-DgCbZtIR.js → src-CQxJFv4l.js} +8 -8
  109. package/dist/src-CQxJFv4l.js.map +1 -0
  110. package/dist/state/index.d.ts +1 -1
  111. package/dist/state/index.js +1 -3
  112. package/dist/state/index.js.map +1 -1
  113. package/dist/{store-DLeqKuj3.d.ts → store-BKM4Xm9M.d.ts} +23 -1
  114. package/dist/{store-vgcPYo8K.js → store-CslVzfN7.js} +28 -2
  115. package/dist/store-CslVzfN7.js.map +1 -0
  116. package/dist/streams/index.d.ts +1 -1
  117. package/dist/streams/index.js +1 -1
  118. package/dist/{streams-CITClVer.js → streams-lsHMJhRW.js} +2 -2
  119. package/dist/{streams-CITClVer.js.map → streams-lsHMJhRW.js.map} +1 -1
  120. package/dist/{streams-fbG61qM8.d.ts → streams-vrCtbUIn.d.ts} +2 -2
  121. package/dist/sub-routing.d.ts +1 -1
  122. package/dist/tasks/index.d.ts +1 -1
  123. package/dist/tasks/index.js +1 -1
  124. package/dist/{tasks-CkTZNaV1.js → tasks-BN_5AWqI.js} +2 -2
  125. package/dist/{tasks-CkTZNaV1.js.map → tasks-BN_5AWqI.js.map} +1 -1
  126. package/dist/{tasks-PtlB4Uyn.d.ts → tasks-E89KdwkS.d.ts} +2 -2
  127. package/dist/{tool-BPJEbwaX.d.ts → tool-CTp72Jb_.d.ts} +4 -4
  128. package/dist/tool-CxKsvxjm.js +1126 -0
  129. package/dist/tool-CxKsvxjm.js.map +1 -0
  130. package/dist/tool-DoVberpN.d.ts +301 -0
  131. package/dist/{tool-3PA8xjB_.js → tool-cQRkwSZZ.js} +6 -17
  132. package/dist/tool-cQRkwSZZ.js.map +1 -0
  133. package/dist/tool-helpers-O5BDVEAY.js +58 -0
  134. package/dist/tool-helpers-O5BDVEAY.js.map +1 -0
  135. package/dist/voice/index.d.ts +2 -2
  136. package/dist/web/fetch/index.d.ts +89 -0
  137. package/dist/web/fetch/index.js +20 -0
  138. package/dist/web/fetch/index.js.map +1 -0
  139. package/dist/web/fetch/tools/ai-sdk.d.ts +80 -0
  140. package/dist/web/fetch/tools/ai-sdk.js +47 -0
  141. package/dist/web/fetch/tools/ai-sdk.js.map +1 -0
  142. package/dist/web/fetch/tools/pi.d.ts +84 -0
  143. package/dist/web/fetch/tools/pi.js +88 -0
  144. package/dist/web/fetch/tools/pi.js.map +1 -0
  145. package/dist/web/fetch/tools/tanstack-ai.d.ts +78 -0
  146. package/dist/web/fetch/tools/tanstack-ai.js +45 -0
  147. package/dist/web/fetch/tools/tanstack-ai.js.map +1 -0
  148. package/dist/{websearch → web/search}/index.d.ts +1 -1
  149. package/dist/{websearch → web/search}/index.js +1 -1
  150. package/dist/{websearch → web/search}/tools/ai-sdk.d.ts +2 -2
  151. package/dist/{websearch → web/search}/tools/ai-sdk.js +3 -3
  152. package/dist/web/search/tools/ai-sdk.js.map +1 -0
  153. package/dist/{websearch → web/search}/tools/pi.d.ts +3 -3
  154. package/dist/{websearch → web/search}/tools/pi.js +2 -2
  155. package/dist/web/search/tools/pi.js.map +1 -0
  156. package/dist/{websearch → web/search}/tools/tanstack-ai.d.ts +2 -2
  157. package/dist/{websearch → web/search}/tools/tanstack-ai.js +3 -3
  158. package/dist/web/search/tools/tanstack-ai.js.map +1 -0
  159. package/dist/websockets/index.d.ts +2 -2
  160. package/dist/websockets/index.js +1 -1
  161. package/dist/{websockets-CmENGb-Z.js → websockets-C3zy8YR0.js} +3 -5
  162. package/dist/websockets-C3zy8YR0.js.map +1 -0
  163. package/dist/workflow-types.d.ts +1 -1
  164. package/dist/workflows.d.ts +1 -1
  165. package/dist/workflows.js +1 -1
  166. package/docs/browse-the-web.md +26 -1
  167. package/docs/fetch-the-web.md +276 -0
  168. package/docs/harnesses/pi.md +25 -0
  169. package/docs/index.md +99 -76
  170. package/docs/lifecycle.md +2 -3
  171. package/docs/queue.md +2 -3
  172. package/docs/scheduling.md +2 -6
  173. package/docs/search-the-web.md +2 -0
  174. package/package.json +44 -19
  175. package/dist/ai-fm7xx1BK.js +0 -312
  176. package/dist/ai-fm7xx1BK.js.map +0 -1
  177. package/dist/browser/ai-sdk.js.map +0 -1
  178. package/dist/browser/index.js.map +0 -1
  179. package/dist/browser/tanstack-ai.js.map +0 -1
  180. package/dist/browser-tool-CBqs6hqa.js.map +0 -1
  181. package/dist/capability-B4WbF81e.js.map +0 -1
  182. package/dist/client-1tEQ1Oqz.js.map +0 -1
  183. package/dist/connector-58XDGvc3.js.map +0 -1
  184. package/dist/input-schema-DNrpTu49.js.map +0 -1
  185. package/dist/lifecycle-CFu6OV03.js.map +0 -1
  186. package/dist/scheduler-DZ3xQfeK.js.map +0 -1
  187. package/dist/src-DgCbZtIR.js.map +0 -1
  188. package/dist/store-vgcPYo8K.js.map +0 -1
  189. package/dist/tool-3PA8xjB_.js.map +0 -1
  190. package/dist/websearch/tools/ai-sdk.js.map +0 -1
  191. package/dist/websearch/tools/pi.js.map +0 -1
  192. package/dist/websearch/tools/tanstack-ai.js.map +0 -1
  193. package/dist/websockets-CmENGb-Z.js.map +0 -1
@@ -1,12 +1,12 @@
1
- import { s as LifecycleCapability } from "./capability-runner-O5VbQzqg.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-tmkcAAI3.d.ts.map
301
+ //# sourceMappingURL=browser-D30VlpcA.d.ts.map
@@ -1,7 +1,8 @@
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";
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";
5
6
  import { CodemodeConnector, DynamicWorkerExecutor, MAX_DURABLE_VALUE_BYTES, createCodemodeRuntime } from "@cloudflare/codemode";
6
7
  /**
7
8
  * `cdp.attachToTarget` returns `{ sessionId: "target:<targetId>" }` — a
@@ -45,7 +46,8 @@ const BROWSER_LIFETIME_COMMANDS = /* @__PURE__ */ new Set([
45
46
  const BROWSER_INSTRUCTIONS = [
46
47
  "The browser persists between runs: tabs, cookies, and logins are still there next time. You never start or close it.",
47
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.",
48
- "Page-scoped commands (Page.*, Runtime.*, DOM.*, Input.*, Network.*, Emulation.*) need sessionId: \"active\", the tab you're working in, which is remembered between runs. Browser.* and Target.* commands take no sessionId.",
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.",
49
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.",
50
52
  "Tabs a page opens itself (popups, target=_blank links) don't become active; the result lists them as newTabs.",
51
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.",
@@ -53,6 +55,7 @@ const BROWSER_INSTRUCTIONS = [
53
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."
54
56
  ].join("\n");
55
57
  var _browser = /* @__PURE__ */ new WeakMap();
58
+ var _scope = /* @__PURE__ */ new WeakMap();
56
59
  var _states = /* @__PURE__ */ new WeakMap();
57
60
  var _connecting = /* @__PURE__ */ new WeakMap();
58
61
  var _reports = /* @__PURE__ */ new WeakMap();
@@ -64,8 +67,10 @@ var _BrowserSessionConnector_brand = /* @__PURE__ */ new WeakSet();
64
67
  * The model never manages sessions: every execution reattaches to the
65
68
  * browser (or gets a replacement, reported as `restarted`), and
66
69
  * `sessionId: "active"` addresses the tab it last worked in. The active tab
67
- * is stored on the browser's record, so it survives between executions for as
68
- * 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.
69
74
  *
70
75
  * The CDP socket is per pass: opened on the first call, closed when the pass
71
76
  * ends. The browser itself outlives the execution.
@@ -75,10 +80,12 @@ var BrowserSessionConnector = class extends CodemodeConnector {
75
80
  super(ctx, {});
76
81
  _classPrivateMethodInitSpec(this, _BrowserSessionConnector_brand);
77
82
  _classPrivateFieldInitSpec(this, _browser, void 0);
83
+ _classPrivateFieldInitSpec(this, _scope, void 0);
78
84
  _classPrivateFieldInitSpec(this, _states, /* @__PURE__ */ new Map());
79
85
  _classPrivateFieldInitSpec(this, _connecting, /* @__PURE__ */ new Map());
80
86
  _classPrivateFieldInitSpec(this, _reports, /* @__PURE__ */ new Map());
81
87
  _classPrivateFieldSet2(_browser, this, options.browser);
88
+ _classPrivateFieldSet2(_scope, this, options.scope);
82
89
  }
83
90
  name() {
84
91
  return "cdp";
@@ -206,8 +213,9 @@ var BrowserSessionConnector = class extends CodemodeConnector {
206
213
  };
207
214
  }
208
215
  /**
209
- * The pass is over: note tabs the page opened, save the active tab on the
210
- * 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.
211
219
  */
212
220
  async onPassEnd(executionId) {
213
221
  const state = _classPrivateFieldGet2(_states, this).get(executionId);
@@ -215,7 +223,8 @@ var BrowserSessionConnector = class extends CodemodeConnector {
215
223
  _classPrivateFieldGet2(_states, this).delete(executionId);
216
224
  try {
217
225
  const open = await _assertClassBrand(_BrowserSessionConnector_brand, this, _recordNewTabs).call(this, executionId, state);
218
- 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);
219
228
  } finally {
220
229
  state.connected.cdp.disconnect();
221
230
  }
@@ -258,7 +267,7 @@ function _state(executionId) {
258
267
  return promise;
259
268
  }
260
269
  async function _connect(executionId) {
261
- 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) });
262
271
  let initialPages;
263
272
  try {
264
273
  initialPages = await _assertClassBrand(_BrowserSessionConnector_brand, this, _pages).call(this, connected.cdp);
@@ -267,10 +276,13 @@ async function _connect(executionId) {
267
276
  throw error;
268
277
  }
269
278
  const state = {
279
+ executionId,
270
280
  connected,
271
281
  attached: /* @__PURE__ */ new Map(),
282
+ workedIn: /* @__PURE__ */ new Set(),
272
283
  initialPages: new Set(initialPages.map((page) => page.targetId)),
273
- activeResolved: false
284
+ activeResolved: false,
285
+ closed: /* @__PURE__ */ new Set()
274
286
  };
275
287
  _classPrivateFieldGet2(_states, this).set(executionId, state);
276
288
  _assertClassBrand(_BrowserSessionConnector_brand, this, _report).call(this, executionId, connected.restarted);
@@ -281,7 +293,9 @@ function _report(executionId, restarted) {
281
293
  if (!report) {
282
294
  report = {
283
295
  restarted,
284
- newTabs: []
296
+ newTabs: [],
297
+ tabClosed: false,
298
+ tabShared: false
285
299
  };
286
300
  _classPrivateFieldGet2(_reports, this).set(executionId, report);
287
301
  if (_classPrivateFieldGet2(_reports, this).size > MAX_PENDING_REPORTS) {
@@ -308,7 +322,18 @@ async function _recordNewTabs(executionId, state) {
308
322
  }
309
323
  const report = _assertClassBrand(_BrowserSessionConnector_brand, this, _report).call(this, executionId, state.connected.restarted);
310
324
  const opened = new Set(report.newTabs.map((tab) => tab.targetId));
311
- 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
+ }
312
337
  report.newTabs = pages.filter((page) => opened.has(page.targetId)).map(({ targetId, url, title }) => ({
313
338
  targetId,
314
339
  url,
@@ -317,20 +342,31 @@ async function _recordNewTabs(executionId, state) {
317
342
  return new Set(pages.map((page) => page.targetId));
318
343
  }
319
344
  /**
320
- * Save the tab this pass settled on, forgetting one that has since closed.
321
- * Without the list of open tabs, save the choice unchecked: the next
322
- * 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.
323
349
  */
324
350
  async function _saveActiveTarget(state, open) {
325
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;
326
353
  const candidate = state.activeResolved ? state.activeTargetId : stored;
327
354
  const active = candidate && (!open || open.has(candidate)) ? candidate : void 0;
328
- if (active === stored) return;
355
+ if (active === stored) return active;
329
356
  try {
330
357
  await state.connected.setActiveTarget(active);
331
358
  } catch (error) {
332
359
  _assertClassBrand(_BrowserSessionConnector_brand, this, _warn).call(this, "save the active tab", error);
333
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
+ }
334
370
  }
335
371
  function _warn(action, error) {
336
372
  console.warn(`[agents/browser] Failed to ${action} for browser "${_classPrivateFieldGet2(_browser, this).name}"`, error);
@@ -344,17 +380,18 @@ async function _resolveSessionId(state, sessionId) {
344
380
  return sessionId;
345
381
  }
346
382
  /**
347
- * Decide which tab `"active"` means, once per pass: the stored tab if it
348
- * is still open, else the only open tab, else a new blank tab when none
349
- * 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.
350
386
  */
351
387
  async function _activeTarget(state) {
352
388
  if (state.activeTargetId) return state.activeTargetId;
353
- const pages = await _assertClassBrand(_BrowserSessionConnector_brand, this, _pages).call(this, state.connected.cdp);
354
- 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;
355
392
  let targetId = pages.find((page) => page.targetId === stored)?.targetId;
356
- if (!targetId && pages.length === 0) targetId = (await state.connected.cdp.send("Target.createTarget", { url: "about:blank" })).targetId;
357
- 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;
358
395
  _assertClassBrand(_BrowserSessionConnector_brand, this, _setActive).call(this, state, targetId);
359
396
  return targetId;
360
397
  }
@@ -367,6 +404,7 @@ async function _attach(state, targetId, timeoutMs) {
367
404
  if (existing) return existing;
368
405
  const live = await state.connected.cdp.attachToTarget(targetId, { timeoutMs });
369
406
  state.attached.set(targetId, live);
407
+ state.workedIn.add(targetId);
370
408
  return live;
371
409
  }
372
410
  /** Track tab changes the model makes through raw Target commands. */
@@ -375,9 +413,12 @@ function _observe(state, method, params, result) {
375
413
  if (method === "Target.createTarget") {
376
414
  const created = result?.targetId;
377
415
  if (typeof created === "string") _assertClassBrand(_BrowserSessionConnector_brand, this, _setActive).call(this, state, created);
378
- } else if (method === "Target.attachToTarget" && targetId) _assertClassBrand(_BrowserSessionConnector_brand, this, _setActive).call(this, state, targetId);
379
- 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) {
380
420
  state.attached.delete(targetId);
421
+ state.closed.add(targetId);
381
422
  if (state.activeTargetId === targetId) state.activeTargetId = void 0;
382
423
  } else if (method === "Target.detachFromTarget") {
383
424
  for (const [target, live] of state.attached) if (target === targetId || live === params?.sessionId) state.attached.delete(target);
@@ -431,12 +472,17 @@ function targetOfHandle(handle) {
431
472
  //#endregion
432
473
  //#region src/browser/browser-tool.ts
433
474
  /**
434
- * The harness-neutral core of `browserTool`, shared by the AI SDK
435
- * (`agents/browser/ai-sdk`) and TanStack AI (`agents/browser/tanstack-ai`)
436
- * 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.
437
479
  */
438
480
  const DEFAULT_TIMEOUT_MS = 6e4;
439
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;
440
486
  /**
441
487
  * A codemode runtime name per browser. Browser names are host-chosen and
442
488
  * unrestricted; runtime names allow only `[a-zA-Z0-9_.-]`, so anything else
@@ -445,15 +491,35 @@ const RESTARTED_NOTICE = "The browser was restarted before this run (it expired
445
491
  function browserRuntimeName(name) {
446
492
  return `browser-tool_${name.replace(/[^a-zA-Z0-9_-]/g, (char) => `.${char.codePointAt(0)?.toString(16)}.`)}`;
447
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
+ }
448
511
  /** Add what happened to the browser to the codemode result. */
449
512
  function withBrowserReport(output, report) {
450
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");
451
519
  return {
452
520
  ...output,
453
- ...report.restarted ? {
454
- restarted: true,
455
- notice: RESTARTED_NOTICE
456
- } : {},
521
+ ...report.restarted ? { restarted: true } : {},
522
+ ...notices.length > 0 ? { notice: notices.join(" ") } : {},
457
523
  ...report.newTabs.length > 0 ? { newTabs: report.newTabs } : {}
458
524
  };
459
525
  }
@@ -467,21 +533,18 @@ function browserReportNotes(output) {
467
533
  /**
468
534
  * Build the codemode runtime and connector for one `browserTool`, and wrap
469
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.
470
540
  */
471
541
  function createBrowserToolCore(options, adapter) {
472
542
  const ctx = resolveCtx(options);
473
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()");
474
544
  const timeout = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
475
- const connector = new BrowserSessionConnector(ctx, { browser: options.browser });
476
- const runtime = createCodemodeRuntime({
477
- ctx,
478
- executor: new DynamicWorkerExecutor({
479
- loader: options.loader,
480
- timeout
481
- }),
482
- connectors: [connector],
483
- name: browserRuntimeName(options.browser.name),
484
- transformResult: transformBrowserResult
545
+ const executor = new DynamicWorkerExecutor({
546
+ loader: options.loader,
547
+ timeout
485
548
  });
486
549
  const description = [
487
550
  "Run JavaScript that drives a persistent Chrome browser over the Chrome DevTools Protocol (CDP).",
@@ -504,17 +567,50 @@ function createBrowserToolCore(options, adapter) {
504
567
  ].join("\n").split("\n").map((rule) => `- ${rule}`).join("\n"),
505
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."
506
569
  ].join("\n");
507
- const codemodeTool = runtime.tool({ description });
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;
508
602
  return {
509
- description: codemodeTool.description,
510
- inputSchema: codemodeTool.inputSchema,
511
- execute: async (input, executeOptions) => {
512
- 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);
513
609
  return withBrowserReport(output, connector.takeReport(output.executionId));
514
- }
610
+ })
515
611
  };
516
612
  }
517
613
  //#endregion
518
614
  export { createBrowserToolCore as n, browserReportNotes as t };
519
615
 
520
- //# sourceMappingURL=browser-tool-CBqs6hqa.js.map
616
+ //# sourceMappingURL=browser-tool-4iomavs_.js.map