agents 0.30.0 → 0.31.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 (130) hide show
  1. package/README.md +4 -2
  2. package/dist/{agent-routing-CDbpVjYq.d.ts → agent-routing-wS1TimZ_.d.ts} +64 -603
  3. package/dist/agent-routing.d.ts +1 -1
  4. package/dist/agent-tool-types.d.ts +1 -1
  5. package/dist/{agent-tools-C8DH8Yii.d.ts → agent-tools-Bamxxb6Y.d.ts} +2 -2
  6. package/dist/agent-tools.d.ts +1 -1
  7. package/dist/{ai-iXw0uOGj.d.ts → ai-CDn3Abax.d.ts} +30 -4
  8. package/dist/{ai-sdk-DRPh2rRa.js → ai-sdk-Kon40i0q.js} +2 -36
  9. package/dist/ai-sdk-Kon40i0q.js.map +1 -0
  10. package/dist/ai-sdk-hW3vntnk.d.ts +72 -0
  11. package/dist/browser/ai.d.ts +1 -1
  12. package/dist/browser/ai.js +16 -3
  13. package/dist/browser/ai.js.map +1 -1
  14. package/dist/browser/index.d.ts +64 -16
  15. package/dist/browser/index.js +4 -4
  16. package/dist/browser/tools/ai-sdk.d.ts +7 -70
  17. package/dist/browser/tools/ai-sdk.js +4 -4
  18. package/dist/browser/tools/ai-sdk.js.map +1 -1
  19. package/dist/browser/tools/pi.d.ts +9 -15
  20. package/dist/browser/tools/pi.js +6 -14
  21. package/dist/browser/tools/pi.js.map +1 -1
  22. package/dist/browser/tools/tanstack-ai.d.ts +46 -23
  23. package/dist/browser/tools/tanstack-ai.js +8 -4
  24. package/dist/browser/tools/tanstack-ai.js.map +1 -1
  25. package/dist/{browser-DccPw_wW.js → browser-1eUbPvC2.js} +413 -14
  26. package/dist/browser-1eUbPvC2.js.map +1 -0
  27. package/dist/{browser-DlXhSAdf.d.ts → browser-D9RNpJuT.d.ts} +254 -20
  28. package/dist/browser-tool-BhjCcNVE.d.ts +71 -0
  29. package/dist/browser-tool-lGnvEBLJ.js +497 -0
  30. package/dist/browser-tool-lGnvEBLJ.js.map +1 -0
  31. package/dist/capability-id-B3J8Nok_.js +13 -0
  32. package/dist/capability-id-B3J8Nok_.js.map +1 -0
  33. package/dist/chat/index.d.ts +2 -2
  34. package/dist/chat-sdk/index.d.ts +3 -3
  35. package/dist/chat-sdk/index.js +1 -1
  36. package/dist/client-4OlMIJx-.js.map +1 -1
  37. package/dist/{client-BnYbNT3z.js → client-DMWujQJ1.js} +68 -244
  38. package/dist/client-DMWujQJ1.js.map +1 -0
  39. package/dist/{client-C27CtRZr.d.ts → client-wn4-yqGz.d.ts} +9 -7
  40. package/dist/client.d.ts +1 -1
  41. package/dist/{connector-DAXBOIIi.js → connector-CM-uuxKM.js} +96 -8
  42. package/dist/connector-CM-uuxKM.js.map +1 -0
  43. package/dist/{connector-20DJ6p4k.d.ts → connector-DZ71EjE-.d.ts} +109 -9
  44. package/dist/experimental/webmcp.js +1 -1
  45. package/dist/{handler-stateless-Bly_nSH3.js → handler-stateless-B5hU3dxK.js} +35 -2
  46. package/dist/handler-stateless-B5hU3dxK.js.map +1 -0
  47. package/dist/{handler-stateless-DxYpJ_XF.d.ts → handler-stateless-C90kD5_R.d.ts} +3 -47
  48. package/dist/harness/container/index.d.ts +15 -15
  49. package/dist/harness/opencode/index.js.map +1 -1
  50. package/dist/harness/pi/index.d.ts +7 -3
  51. package/dist/harness/pi/index.js +46 -15
  52. package/dist/harness/pi/index.js.map +1 -1
  53. package/dist/harness/think/index.d.ts +2 -1
  54. package/dist/{images-xaDv2Fqe.js → images-CfuRCjwq.js} +1 -5
  55. package/dist/images-CfuRCjwq.js.map +1 -0
  56. package/dist/{index-DCxk3OSJ.d.ts → index-8k4qsX1T.d.ts} +2 -28
  57. package/dist/index.d.ts +14 -14
  58. package/dist/index.js +2 -2
  59. package/dist/internal-hosts-Kfp5A392.d.ts +230 -0
  60. package/dist/internal-kmw70xwR.js.map +1 -1
  61. package/dist/invoker-BC1SZ3q1.js +26 -0
  62. package/dist/invoker-BC1SZ3q1.js.map +1 -0
  63. package/dist/invoker-DoLFPJZV.d.ts +21 -0
  64. package/dist/mcp/client/index.d.ts +15 -1
  65. package/dist/mcp/client/index.js +2 -2
  66. package/dist/mcp/client/x402.d.ts +7 -5
  67. package/dist/mcp/client/x402.js +34 -1
  68. package/dist/mcp/client/x402.js.map +1 -1
  69. package/dist/mcp/index.d.ts +561 -27
  70. package/dist/mcp/index.js +180 -3
  71. package/dist/mcp/index.js.map +1 -1
  72. package/dist/mcp/server/index.d.ts +1 -1
  73. package/dist/mcp/server/index.js +1 -1
  74. package/dist/models/ai-sdk/index.d.ts +2 -2
  75. package/dist/models/ai-sdk/index.js +1 -1
  76. package/dist/models/opencode/index.d.ts +2 -2
  77. package/dist/models/opencode/index.js +1 -1
  78. package/dist/models/pi-ai/index.d.ts +3 -28
  79. package/dist/models/pi-ai/index.js +31 -16
  80. package/dist/models/pi-ai/index.js.map +1 -1
  81. package/dist/protocol-BEgrJYC5.d.ts +29 -29
  82. package/dist/react.d.ts +2 -2
  83. package/dist/routing/index.d.ts +1 -1
  84. package/dist/serializable.d.ts +1 -1
  85. package/dist/{session-store-yvwq1f9x.js → session-7CDgVE0z.js} +455 -33
  86. package/dist/session-7CDgVE0z.js.map +1 -0
  87. package/dist/{session-store-_IK6dV-w.d.ts → session-store-B7Ca-u1t.d.ts} +8 -1
  88. package/dist/settings-Dk2il2Hf.js.map +1 -1
  89. package/dist/{src-DfLzdwlK.js → src-BGolXG_H.js} +18 -2
  90. package/dist/src-BGolXG_H.js.map +1 -0
  91. package/dist/sub-routing.d.ts +12 -12
  92. package/dist/tool-CTp72Jb_.d.ts +10 -10
  93. package/dist/tool-DoVberpN.d.ts +22 -22
  94. package/dist/tool-helpers-BodWi4gd.js +256 -0
  95. package/dist/tool-helpers-BodWi4gd.js.map +1 -0
  96. package/dist/{transport-CPh7YGiw.d.ts → transport-5moD2pW5.d.ts} +1 -43
  97. package/dist/types-D2naO3rA.d.ts +49 -0
  98. package/dist/voice/index.d.ts +1 -1
  99. package/dist/workflow-types.d.ts +1 -1
  100. package/dist/workflows.d.ts +1 -1
  101. package/dist/workflows.js +1 -1
  102. package/docs/browse-the-web.md +247 -413
  103. package/docs/get-current-agent.md +4 -7
  104. package/docs/harnesses/pi.md +28 -28
  105. package/docs/human-in-the-loop.md +3 -7
  106. package/docs/index.md +6 -6
  107. package/docs/mcp-client.md +4 -28
  108. package/docs/mcp-servers.md +137 -411
  109. package/docs/mcp-transports.md +30 -289
  110. package/docs/models-pi-ai.md +9 -11
  111. package/docs/models.md +1 -8
  112. package/docs/routing.md +0 -2
  113. package/docs/securing-mcp-servers.md +120 -304
  114. package/package.json +8 -3
  115. package/dist/ai-sdk-DRPh2rRa.js.map +0 -1
  116. package/dist/browser-DccPw_wW.js.map +0 -1
  117. package/dist/browser-tool-B1mhifin.js +0 -616
  118. package/dist/browser-tool-B1mhifin.js.map +0 -1
  119. package/dist/browser-tool-Cxjk5sgG.d.ts +0 -92
  120. package/dist/client-BnYbNT3z.js.map +0 -1
  121. package/dist/connector-DAXBOIIi.js.map +0 -1
  122. package/dist/handler-stateless-Bly_nSH3.js.map +0 -1
  123. package/dist/images-xaDv2Fqe.js.map +0 -1
  124. package/dist/invoker-CG0_p_Wq.d.ts +0 -20
  125. package/dist/invoker-CHMnoxIA.js +0 -57
  126. package/dist/invoker-CHMnoxIA.js.map +0 -1
  127. package/dist/session-store-yvwq1f9x.js.map +0 -1
  128. package/dist/src-DfLzdwlK.js.map +0 -1
  129. package/dist/tool-helpers-O5BDVEAY.js +0 -58
  130. package/dist/tool-helpers-O5BDVEAY.js.map +0 -1
@@ -0,0 +1,497 @@
1
+ import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-DZBYAB34.js";
2
+ import { i as validate, n as BrowserSession } from "./session-7CDgVE0z.js";
3
+ import { a as truncateResult, i as transformBrowserResult } from "./tool-helpers-BodWi4gd.js";
4
+ import "./browser-1eUbPvC2.js";
5
+ import { RpcTarget } from "cloudflare:workers";
6
+ //#region src/browser/browser-functions.ts
7
+ const SPEC_SCHEMA = {
8
+ type: "object",
9
+ properties: { domain: {
10
+ type: "string",
11
+ minLength: 1
12
+ } }
13
+ };
14
+ const BROWSER_FUNCTIONS = {
15
+ cdp: {
16
+ doc: "`browser.cdp({ method, params?, sessionId?, timeoutMs? })` sends a CDP command and returns its result without the JSON-RPC envelope. Page-scoped commands take `sessionId: \"active\"`: `await browser.cdp({ method: \"Page.navigate\", params: { url }, sessionId: \"active\" })` returns `{ frameId, errorText? }`. `timeoutMs` limits this one command. A failed command throws, and the error often says how to fix the call.",
17
+ call: (session, input) => session.send(input)
18
+ },
19
+ attachToTarget: {
20
+ doc: "`browser.attachToTarget({ targetId })` switches to an open tab, with `targetId` from `Target.getTargets`, and makes it active: `sessionId: \"active\"` points at it from then on. Returns `{ sessionId }` for that tab.",
21
+ call: (session, input) => session.attachToTarget(input)
22
+ },
23
+ spec: {
24
+ doc: "`browser.spec()` lists the protocol's domains as `{ domains: [{ name, description }] }`; `browser.spec({ domain: \"Page\" })` returns one domain's `{ commands, events, types }`, each command a `{ name, method, description, parameters, returns }` (`name` is `\"navigate\"`, `method` is `\"Page.navigate\"`). Big domains get cut short in your result, so filter in code: `(await browser.spec({ domain: \"Network\" })).commands.find((c) => c.name === \"enable\")`.",
25
+ call: async (session, input) => {
26
+ const { domain } = validate("browser.spec", SPEC_SCHEMA, input);
27
+ return specResult(await session.spec(), domain);
28
+ }
29
+ },
30
+ getDebugLog: {
31
+ doc: "`browser.getDebugLog({ limit? })` returns recent protocol traffic, newest last (50 entries unless `limit` says otherwise), each `{ at, type, ... }`. Use it when a command fails or times out.",
32
+ call: (session, input) => session.getDebugLog(input)
33
+ },
34
+ clearDebugLog: {
35
+ doc: "`browser.clearDebugLog()` forgets the traffic recorded so far, so the next `getDebugLog` shows only what follows.",
36
+ call: (session) => session.clearDebugLog()
37
+ }
38
+ };
39
+ /**
40
+ * Without a domain, every domain's name and first sentence, which is small;
41
+ * with one, that domain's commands, events and types. The whole protocol is
42
+ * about 650 KB, too much for one result.
43
+ */
44
+ function specResult(spec, domain) {
45
+ const { domains } = spec;
46
+ if (domain === void 0) return {
47
+ domains: domains.map(({ name, description }) => ({
48
+ name,
49
+ ...description ? { description: firstSentence(description) } : {}
50
+ })),
51
+ hint: "browser.spec({ domain: \"Page\" }) returns one domain's commands, events and types."
52
+ };
53
+ const found = domains.find((d) => d.name === domain);
54
+ if (found) return found;
55
+ const near = domains.find((d) => d.name.toLowerCase() === domain.toLowerCase());
56
+ throw new Error(`No CDP domain named '${domain}'.` + (near ? ` Did you mean '${near.name}'?` : " browser.spec() lists the domains."));
57
+ }
58
+ /** A CDP description's first sentence, for the list of domains. */
59
+ function firstSentence(text) {
60
+ return text.match(/^[\s\S]*?\.(?=\s|$)/)?.[0] ?? text;
61
+ }
62
+ /** The `host` field of a hosts function's input, or a fix-it error. */
63
+ function hostOf(name, input) {
64
+ const host = typeof input === "object" && input !== null ? input.host : void 0;
65
+ if (typeof host !== "string") throw new Error(`browser.${name} needs { host }: the bare hostname, like "preview.internal".`);
66
+ return host;
67
+ }
68
+ const HOSTS_FUNCTIONS = {
69
+ listHosts: {
70
+ doc: "`browser.listHosts()` returns each internal site and the target it points at.",
71
+ call: (hosts) => hosts.list()
72
+ },
73
+ assignHost: {
74
+ doc: "`browser.assignHost({ host, target })` points an assignable site at a target matching its schema and returns `{ url }`; `host` is the bare hostname, like \"preview.internal\". Navigate to the url next. It stays assigned across runs.",
75
+ assignable: true,
76
+ call: (hosts, input) => hosts.assign(hostOf("assignHost", input), input.target)
77
+ },
78
+ clearHost: {
79
+ doc: "`browser.clearHost({ host })` unassigns a site and returns `{ cleared }`. An unassigned site answers 502.",
80
+ assignable: true,
81
+ call: async (hosts, input) => ({ cleared: await hosts.clear(hostOf("clearHost", input)) })
82
+ }
83
+ };
84
+ /** The hosts functions to offer: assign and clear only if any host can be. */
85
+ function hostsFunctions(infos) {
86
+ const assignable = infos.some((info) => info.assignable);
87
+ return Object.entries(HOSTS_FUNCTIONS).filter(([, definition]) => assignable || !definition.assignable);
88
+ }
89
+ /**
90
+ * The `browser` object's functions, bound to one run's session, plus the
91
+ * hosts functions when the browser has internal hostnames.
92
+ */
93
+ function browserFunctions(session, hosts) {
94
+ const functions = Object.fromEntries(Object.entries(BROWSER_FUNCTIONS).map(([name, definition]) => [name, (input) => definition.call(session, input)]));
95
+ if (hosts) for (const [name, definition] of hostsFunctions(hosts.describe())) functions[name] = (input) => definition.call(hosts, input);
96
+ return functions;
97
+ }
98
+ /** How one internal site reads in the docs. */
99
+ function describeHost(info) {
100
+ const description = info.description ? ` — ${info.description}` : "";
101
+ const target = info.assignable ? `. Assignable; target schema: ${JSON.stringify(info.targetSchema ?? {})}` : "";
102
+ return `- ${info.url}${description}${target}`;
103
+ }
104
+ /**
105
+ * The `browser` object's docs for the model, in Markdown: one line per
106
+ * function, then the internal sites when the browser has any.
107
+ */
108
+ function browserFunctionDocs(hosts) {
109
+ const infos = hosts?.describe() ?? [];
110
+ const lines = [...Object.values(BROWSER_FUNCTIONS), ...hosts ? hostsFunctions(infos).map(([, definition]) => definition) : []].map((definition) => `- ${definition.doc}`);
111
+ if (!hosts) return lines.join("\n");
112
+ return [
113
+ ...lines,
114
+ "",
115
+ "## Internal sites",
116
+ "",
117
+ "The browser can open these sites, which aren't on the public internet. Plain http only: no https:// and no WebSockets, so a dev server's hot reload won't connect; reload the page instead.",
118
+ "",
119
+ ...infos.map(describeHost)
120
+ ].join("\n");
121
+ }
122
+ /**
123
+ * How to use the browser, one rule per line. Each rule fixes a mistake
124
+ * models make with CDP, or states something about this browser they can't
125
+ * know; leave out what a model knows already. Rules about how the code runs
126
+ * (timeouts, screenshots) belong to whatever runs it.
127
+ */
128
+ const BROWSER_RULES = [
129
+ "The browser persists between runs: tabs, cookies, and logins are still there next time. You never start or close it.",
130
+ "If the browser was replaced (idle timeout, crash), a notice says so 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.",
131
+ "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.",
132
+ "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.",
133
+ "Target.createTarget opens a tab and makes it active. To switch to another open tab, call browser.attachToTarget({ targetId }); the sessionId it returns keeps working in later runs, while one from a raw Target.attachToTarget only works in the current run.",
134
+ "Tabs a page opens itself (popups, target=_blank links) don't become active; a notice lists them.",
135
+ "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.",
136
+ "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'.",
137
+ "Pick out what you need inside Runtime.evaluate and return only that. A screenshot over 1 MB fails the run."
138
+ ];
139
+ //#endregion
140
+ //#region src/browser/sandbox.ts
141
+ /**
142
+ * Runs model-written JavaScript in a Dynamic Worker, with a `browser`
143
+ * object whose functions run on the host. Used by `browserTool`.
144
+ * Internal — not an entry point.
145
+ *
146
+ * The Worker has no network access (`globalOutbound: null`) and no bindings:
147
+ * the only way out is `browser`, whose calls go back to the host over RPC.
148
+ */
149
+ /**
150
+ * Code run as a function body returns nothing unless it says `return`. Tell
151
+ * the model, rather than parse the code to return its last expression. A text
152
+ * search for `return` can't tell a real one from one inside a string, so any
153
+ * body run that comes back empty gets the notice.
154
+ */
155
+ const STATEMENTS_NOTICE = "Your code ran as statements and returned nothing. If you wanted a value, `return` it, or write an async arrow function.";
156
+ var _functions = /* @__PURE__ */ new WeakMap();
157
+ /**
158
+ * The host side of `browser`. A failed call resolves with `{ error }`
159
+ * instead of rejecting across RPC; the sandbox rethrows it as a local error
160
+ * the code can catch.
161
+ */
162
+ var HostFunctions = class extends RpcTarget {
163
+ constructor(functions) {
164
+ super();
165
+ _classPrivateFieldInitSpec(this, _functions, void 0);
166
+ _classPrivateFieldSet2(_functions, this, functions);
167
+ }
168
+ async call(name, input) {
169
+ const fn = Object.hasOwn(_classPrivateFieldGet2(_functions, this), name) ? _classPrivateFieldGet2(_functions, this)[name] : void 0;
170
+ if (!fn) return { error: `browser.${name} is not a function.` };
171
+ try {
172
+ return { value: await fn(input) };
173
+ } catch (error) {
174
+ return { error: error instanceof Error ? error.message : String(error) };
175
+ }
176
+ }
177
+ };
178
+ /** Console lines a run keeps, and the characters kept of each. */
179
+ const MAX_LOG_LINES = 100;
180
+ const MAX_LOG_LINE_CHARS = 2e3;
181
+ /**
182
+ * The entry module. `code.js` holds the model's code; keeping it in its own
183
+ * module keeps the harness's names out of its scope.
184
+ */
185
+ function mainModule(timeoutMs, names) {
186
+ return `import { WorkerEntrypoint } from "cloudflare:workers";
187
+ import run from "./code.js";
188
+
189
+ export default class BrowserCode extends WorkerEntrypoint {
190
+ async evaluate(binding) {
191
+ // Logs are kept in the tool's stored output, so bound them here.
192
+ const lines = [];
193
+ let dropped = 0;
194
+ const record = (prefix) => (...args) => {
195
+ if (lines.length >= ${MAX_LOG_LINES}) {
196
+ dropped++;
197
+ return;
198
+ }
199
+ const line = prefix + args.map(String).join(" ");
200
+ lines.push(
201
+ line.length > ${MAX_LOG_LINE_CHARS}
202
+ ? line.slice(0, ${MAX_LOG_LINE_CHARS}) + "…"
203
+ : line
204
+ );
205
+ };
206
+ const logs = () =>
207
+ dropped > 0 ? [...lines, "[" + dropped + " more lines dropped]"] : lines;
208
+ console.log = record("");
209
+ console.info = record("");
210
+ console.warn = record("[warn] ");
211
+ console.error = record("[error] ");
212
+ const call = async (method, input) => {
213
+ const reply = await binding.call(method, input);
214
+ if ("error" in reply) throw new Error(reply.error);
215
+ return reply.value;
216
+ };
217
+ const browser = Object.freeze({
218
+ ${names.map((name) => ` ${JSON.stringify(name)}: (input) => call(${JSON.stringify(name)}, input),`).join("\n")}
219
+ });
220
+ let timer;
221
+ const timeout = new Promise((_, reject) => {
222
+ timer = setTimeout(
223
+ () => reject(new Error("Execution timed out after ${timeoutMs}ms")),
224
+ ${timeoutMs}
225
+ );
226
+ });
227
+ try {
228
+ return {
229
+ result: await Promise.race([run(browser), timeout]),
230
+ logs: logs()
231
+ };
232
+ } catch (error) {
233
+ return {
234
+ error: error instanceof Error ? error.message : String(error),
235
+ logs: logs()
236
+ };
237
+ } finally {
238
+ clearTimeout(timer);
239
+ }
240
+ }
241
+ }
242
+ `;
243
+ }
244
+ /** Strip the markdown fences models often wrap code in. */
245
+ function stripFences(code) {
246
+ return code.trim().match(/^```(?:js|javascript|typescript|ts|tsx|jsx)?\s*\n([\s\S]*?)```\s*$/)?.[1] ?? code.trim();
247
+ }
248
+ /**
249
+ * The model's code as an expression: an async arrow function (what the tool
250
+ * asks for) is called, and any other expression is awaited.
251
+ */
252
+ function expressionModule(code) {
253
+ return `export default async function (browser) {
254
+ const value = (
255
+ ${code.replace(/^export\s+default\s+/, "").replace(/;\s*(\/\/[^\n"'`]*)?\s*$/, "")}
256
+ );
257
+ return typeof value === "function" ? await value() : await value;
258
+ }
259
+ `;
260
+ }
261
+ /** The model's code as a function body, for statements with a `return`. */
262
+ function bodyModule(code) {
263
+ return `export default async function (browser) {
264
+ ${code}
265
+ }
266
+ `;
267
+ }
268
+ /** The Worker failed to start because a module didn't parse. */
269
+ function isSyntaxError(error) {
270
+ return error instanceof Error && /SyntaxError/.test(error.message);
271
+ }
272
+ /**
273
+ * Run the model's code in a fresh Dynamic Worker, with `browser` bound to
274
+ * `functions`.
275
+ *
276
+ * The code is first loaded as an expression (an async arrow function, as
277
+ * the tool asks for). If that doesn't parse, it's loaded once more as a
278
+ * function body, so statements ending in `return` work too; statements
279
+ * with no `return` at all get a `notice`. Errors thrown
280
+ * by the code, including timeouts, come back as `{ error }`; this only
281
+ * rejects when the Worker can't run at all.
282
+ */
283
+ async function runBrowserCode(options) {
284
+ const code = stripFences(options.code);
285
+ if (code === "") return {
286
+ result: void 0,
287
+ logs: []
288
+ };
289
+ const binding = new HostFunctions(options.functions);
290
+ try {
291
+ return await evaluate(options, expressionModule(code), binding);
292
+ } catch (error) {
293
+ if (!isSyntaxError(error)) throw error;
294
+ }
295
+ let ran;
296
+ try {
297
+ ran = await evaluate(options, bodyModule(code), binding);
298
+ } catch (error) {
299
+ if (!isSyntaxError(error)) throw error;
300
+ return {
301
+ error: `Your code didn't parse: ${error.message}. Write an async arrow function, async () => { ... }.`,
302
+ logs: []
303
+ };
304
+ }
305
+ return "result" in ran && ran.result === void 0 ? {
306
+ ...ran,
307
+ notice: STATEMENTS_NOTICE
308
+ } : ran;
309
+ }
310
+ /**
311
+ * How long past `timeoutMs` the host waits before giving up on a run whose
312
+ * sandbox didn't time itself out. Capped at `timeoutMs` for short runs.
313
+ */
314
+ const HOST_TIMEOUT_GRACE_MS = 5e3;
315
+ async function evaluate(options, codeModule, binding) {
316
+ const worker = options.loader.load({
317
+ compatibilityDate: "2025-06-01",
318
+ mainModule: "main.js",
319
+ modules: {
320
+ "main.js": mainModule(options.timeoutMs, Object.keys(options.functions)),
321
+ "code.js": codeModule
322
+ },
323
+ globalOutbound: null
324
+ });
325
+ try {
326
+ const entrypoint = worker.getEntrypoint();
327
+ const running = entrypoint.evaluate(binding);
328
+ running.catch(() => {});
329
+ let timer;
330
+ const deadline = new Promise((resolve) => {
331
+ timer = setTimeout(() => resolve({
332
+ error: `Execution timed out after ${options.timeoutMs}ms`,
333
+ logs: []
334
+ }), options.timeoutMs + Math.min(HOST_TIMEOUT_GRACE_MS, options.timeoutMs));
335
+ });
336
+ try {
337
+ return await Promise.race([running, deadline]);
338
+ } finally {
339
+ clearTimeout(timer);
340
+ disposeQuietly(entrypoint);
341
+ }
342
+ } finally {
343
+ disposeQuietly(worker);
344
+ }
345
+ }
346
+ function disposeQuietly(resource) {
347
+ if (typeof resource !== "object" || resource === null || !(Symbol.dispose in resource)) return;
348
+ const dispose = resource[Symbol.dispose];
349
+ if (typeof dispose !== "function") return;
350
+ try {
351
+ dispose.call(resource);
352
+ } catch {}
353
+ }
354
+ //#endregion
355
+ //#region src/browser/browser-tool.ts
356
+ /**
357
+ * The harness-neutral core of `browserTool`, shared by the adapters
358
+ * in `tools/` (`agents/browser/ai-sdk`, `agents/browser/tanstack-ai`,
359
+ * `agents/browser/pi`). Internal — not an entry point.
360
+ */
361
+ const DEFAULT_TIMEOUT_MS = 6e4;
362
+ 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.";
363
+ 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.";
364
+ 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.";
365
+ /**
366
+ * The model's input schema, as a Standard Schema with a JSON Schema view,
367
+ * so the AI SDK and TanStack AI both accept it without zod.
368
+ */
369
+ const inputJsonSchema = {
370
+ type: "object",
371
+ properties: { code: {
372
+ type: "string",
373
+ description: "An async arrow function that drives the browser with `browser`"
374
+ } },
375
+ required: ["code"],
376
+ additionalProperties: false
377
+ };
378
+ const browserToolInputSchema = { "~standard": {
379
+ version: 1,
380
+ vendor: "agents",
381
+ types: void 0,
382
+ validate: (value) => typeof value === "object" && value !== null && "code" in value && typeof value.code === "string" ? { value: { code: value.code } } : { issues: [{ message: "Expected an object with a string code property" }] },
383
+ jsonSchema: {
384
+ input: (_options) => inputJsonSchema,
385
+ output: (_options) => inputJsonSchema
386
+ }
387
+ } };
388
+ /**
389
+ * The latest run queued per browser and scope, so runs in one scope go one
390
+ * at a time, even from tools built separately (one per turn, say). Per
391
+ * isolate, which is per Durable Object.
392
+ */
393
+ const runQueues = /* @__PURE__ */ new WeakMap();
394
+ function queued(browser, scope, run) {
395
+ let queues = runQueues.get(browser);
396
+ if (!queues) runQueues.set(browser, queues = /* @__PURE__ */ new Map());
397
+ const next = (queues.get(scope) ?? Promise.resolve()).then(run, run);
398
+ const settled = next.catch(() => void 0);
399
+ queues.set(scope, settled);
400
+ settled.then(() => {
401
+ if (queues.get(scope) === settled) queues.delete(scope);
402
+ });
403
+ return next;
404
+ }
405
+ /** The report as a notice on the result, in sentences the model reads. */
406
+ function reportFields(report) {
407
+ if (!report) return {};
408
+ const notices = [
409
+ report.restarted && RESTARTED_NOTICE,
410
+ report.tabClosed && !report.restarted && TAB_CLOSED_NOTICE,
411
+ report.tabShared && TAB_SHARED_NOTICE,
412
+ report.newTabs.length > 0 && `New tabs opened by the page: ${JSON.stringify(report.newTabs)}`
413
+ ].filter((notice) => typeof notice === "string");
414
+ return notices.length > 0 ? { notice: notices.join(" ") } : {};
415
+ }
416
+ /**
417
+ * Build the description and `execute` for one `browserTool`.
418
+ *
419
+ * Each run opens a session on the browser in its scope, runs the model's
420
+ * code in a fresh Dynamic Worker with `browser` bound to that session, closes
421
+ * the session, and adds its report to the result. Runs in one scope wait
422
+ * for each other, since they drive one tab; runs in different scopes go in
423
+ * parallel.
424
+ */
425
+ function createBrowserToolCore(options, adapter) {
426
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
427
+ const rules = [
428
+ ...BROWSER_RULES,
429
+ "Results over about 24,000 characters are cut short.",
430
+ `Each run times out after ${Math.round(timeoutMs / 1e3)}s, so split long jobs (crawling a site, say) across runs.`,
431
+ adapter.screenshotHint,
432
+ "If your code doesn't parse, the line number in the error counts from wrapper code, not yours. Check regex literals and escapes."
433
+ ].map((rule) => `- ${rule}`).join("\n");
434
+ const description = [
435
+ "Run JavaScript that drives a persistent Chrome browser over the Chrome DevTools Protocol (CDP).",
436
+ "",
437
+ "Write an async arrow function, `async () => { ... }`, and return a small JSON value. Use the `browser` global to reach the browser. `fetch` is blocked and there is no file system or Node.js API.",
438
+ "",
439
+ "## browser",
440
+ "",
441
+ browserFunctionDocs(options.browser.internalHosts),
442
+ "",
443
+ "## Rules",
444
+ "",
445
+ rules
446
+ ].join("\n");
447
+ const defaultScope = options.scope ?? "shared";
448
+ async function run(input, scope) {
449
+ const executionId = crypto.randomUUID();
450
+ const session = new BrowserSession(options.browser, { scope });
451
+ let output;
452
+ try {
453
+ const ran = await runBrowserCode({
454
+ loader: options.loader,
455
+ code: input.code,
456
+ functions: browserFunctions(session, options.browser.internalHosts),
457
+ timeoutMs
458
+ });
459
+ const logs = ran.logs.length > 0 ? { logs: ran.logs } : {};
460
+ output = "error" in ran ? {
461
+ status: "error",
462
+ executionId,
463
+ error: truncateResult(ran.error),
464
+ ...logs
465
+ } : {
466
+ status: "completed",
467
+ executionId,
468
+ result: transformBrowserResult(ran.result),
469
+ ...logs,
470
+ ...ran.notice ? { notice: ran.notice } : {}
471
+ };
472
+ } catch (error) {
473
+ output = {
474
+ status: "error",
475
+ executionId,
476
+ error: error instanceof Error ? error.message : String(error)
477
+ };
478
+ }
479
+ const report = reportFields(await session.close());
480
+ const notice = [output.notice, report.notice].filter(Boolean).join(" ");
481
+ return {
482
+ ...output,
483
+ ...report,
484
+ ...notice ? { notice } : {}
485
+ };
486
+ }
487
+ return {
488
+ description,
489
+ inputSchema: browserToolInputSchema,
490
+ /** Run `input` in `scope`, which defaults to the tool's `scope`. */
491
+ execute: (input, scope = defaultScope) => queued(options.browser, scope, () => run(input, scope))
492
+ };
493
+ }
494
+ //#endregion
495
+ export { createBrowserToolCore as t };
496
+
497
+ //# sourceMappingURL=browser-tool-lGnvEBLJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-tool-lGnvEBLJ.js","names":[],"sources":["../src/browser/browser-functions.ts","../src/browser/sandbox.ts","../src/browser/browser-tool.ts"],"sourcesContent":["/**\n * The `browser` object model-written code drives a browser with, defined\n * once. `browserTool`'s sandbox exposes it as the `browser` global, and the same functions, docs and rules are what a\n * Workspace module would expose later. Internal — not an entry point.\n *\n * Every function takes one options object. The CDP functions are backed by\n * a `BrowserSession`, which validates the input; `spec` shapes the session's\n * full protocol into pieces small enough for a result. The hosts functions,\n * there only when the browser has internal hostnames, are backed by its\n * `internalHosts`.\n */\nimport type { BrowserInternalHosts } from \"./browser\";\nimport type { InternalHostInfo } from \"./internal-hosts\";\nimport {\n validate,\n type BrowserAttachInput,\n type BrowserDebugLogInput,\n type BrowserSendInput,\n type BrowserSession\n} from \"./session\";\nimport type { SearchableCdpSpec } from \"./spec\";\n\n/** One function on the `browser` object, as the host runs it. */\nexport type BrowserFunction = (input: unknown) => Promise<unknown>;\n\ninterface BrowserFunctionDefinition {\n /** One line for the model: how to call it and what it returns. */\n doc: string;\n call(session: BrowserSession, input: unknown): Promise<unknown>;\n}\n\n/** `browser.spec`'s input. */\ninterface BrowserSpecInput {\n /** A CDP domain, e.g. `\"Page\"`. Omit for the list of domains. */\n domain?: string;\n}\n\nconst SPEC_SCHEMA = {\n type: \"object\",\n properties: { domain: { type: \"string\", minLength: 1 } }\n} as const;\n\n// SAFETY: Each session method validates its input before using it, so the\n// casts below only tell TypeScript what the session checks at runtime.\nconst BROWSER_FUNCTIONS = {\n cdp: {\n doc: '`browser.cdp({ method, params?, sessionId?, timeoutMs? })` sends a CDP command and returns its result without the JSON-RPC envelope. Page-scoped commands take `sessionId: \"active\"`: `await browser.cdp({ method: \"Page.navigate\", params: { url }, sessionId: \"active\" })` returns `{ frameId, errorText? }`. `timeoutMs` limits this one command. A failed command throws, and the error often says how to fix the call.',\n call: (session, input) => session.send(input as BrowserSendInput)\n },\n attachToTarget: {\n doc: '`browser.attachToTarget({ targetId })` switches to an open tab, with `targetId` from `Target.getTargets`, and makes it active: `sessionId: \"active\"` points at it from then on. Returns `{ sessionId }` for that tab.',\n call: (session, input) =>\n session.attachToTarget(input as BrowserAttachInput)\n },\n spec: {\n doc: '`browser.spec()` lists the protocol\\'s domains as `{ domains: [{ name, description }] }`; `browser.spec({ domain: \"Page\" })` returns one domain\\'s `{ commands, events, types }`, each command a `{ name, method, description, parameters, returns }` (`name` is `\"navigate\"`, `method` is `\"Page.navigate\"`). Big domains get cut short in your result, so filter in code: `(await browser.spec({ domain: \"Network\" })).commands.find((c) => c.name === \"enable\")`.',\n call: async (session, input) => {\n // Check the input before connecting, like the session's own methods.\n const { domain } = validate(\n \"browser.spec\",\n SPEC_SCHEMA,\n input\n ) as BrowserSpecInput;\n return specResult(await session.spec(), domain);\n }\n },\n getDebugLog: {\n doc: \"`browser.getDebugLog({ limit? })` returns recent protocol traffic, newest last (50 entries unless `limit` says otherwise), each `{ at, type, ... }`. Use it when a command fails or times out.\",\n call: (session, input) => session.getDebugLog(input as BrowserDebugLogInput)\n },\n clearDebugLog: {\n doc: \"`browser.clearDebugLog()` forgets the traffic recorded so far, so the next `getDebugLog` shows only what follows.\",\n call: (session) => session.clearDebugLog()\n }\n} satisfies Record<string, BrowserFunctionDefinition>;\n\n/**\n * Without a domain, every domain's name and first sentence, which is small;\n * with one, that domain's commands, events and types. The whole protocol is\n * about 650 KB, too much for one result.\n */\nfunction specResult(spec: SearchableCdpSpec, domain: string | undefined) {\n const { domains } = spec;\n if (domain === undefined) {\n return {\n domains: domains.map(({ name, description }) => ({\n name,\n ...(description ? { description: firstSentence(description) } : {})\n })),\n hint: 'browser.spec({ domain: \"Page\" }) returns one domain\\'s commands, events and types.'\n };\n }\n const found = domains.find((d) => d.name === domain);\n if (found) return found;\n const near = domains.find(\n (d) => d.name.toLowerCase() === domain.toLowerCase()\n );\n throw new Error(\n `No CDP domain named '${domain}'.` +\n (near\n ? ` Did you mean '${near.name}'?`\n : \" browser.spec() lists the domains.\")\n );\n}\n\n/** A CDP description's first sentence, for the list of domains. */\nfunction firstSentence(text: string): string {\n return text.match(/^[\\s\\S]*?\\.(?=\\s|$)/)?.[0] ?? text;\n}\n\ninterface HostsFunctionDefinition {\n doc: string;\n /** Only offered when at least one host is assignable. */\n assignable?: true;\n call(hosts: BrowserInternalHosts, input: unknown): Promise<unknown>;\n}\n\n/** The `host` field of a hosts function's input, or a fix-it error. */\nfunction hostOf(name: string, input: unknown): string {\n const host =\n typeof input === \"object\" && input !== null\n ? (input as { host?: unknown }).host\n : undefined;\n if (typeof host !== \"string\") {\n throw new Error(\n `browser.${name} needs { host }: the bare hostname, like \"preview.internal\".`\n );\n }\n return host;\n}\n\nconst HOSTS_FUNCTIONS = {\n listHosts: {\n doc: \"`browser.listHosts()` returns each internal site and the target it points at.\",\n call: (hosts) => hosts.list()\n },\n assignHost: {\n doc: '`browser.assignHost({ host, target })` points an assignable site at a target matching its schema and returns `{ url }`; `host` is the bare hostname, like \"preview.internal\". Navigate to the url next. It stays assigned across runs.',\n assignable: true,\n call: (hosts, input) =>\n hosts.assign(\n hostOf(\"assignHost\", input),\n (input as { target?: unknown }).target\n )\n },\n clearHost: {\n doc: \"`browser.clearHost({ host })` unassigns a site and returns `{ cleared }`. An unassigned site answers 502.\",\n assignable: true,\n call: async (hosts, input) => ({\n cleared: await hosts.clear(hostOf(\"clearHost\", input))\n })\n }\n} satisfies Record<string, HostsFunctionDefinition>;\n\n/** The hosts functions to offer: assign and clear only if any host can be. */\nfunction hostsFunctions(\n infos: InternalHostInfo[]\n): [string, HostsFunctionDefinition][] {\n const assignable = infos.some((info) => info.assignable);\n return Object.entries(HOSTS_FUNCTIONS).filter(\n ([, definition]: [string, HostsFunctionDefinition]) =>\n assignable || !definition.assignable\n );\n}\n\n/**\n * The `browser` object's functions, bound to one run's session, plus the\n * hosts functions when the browser has internal hostnames.\n */\nexport function browserFunctions(\n session: BrowserSession,\n hosts?: BrowserInternalHosts\n): Record<string, BrowserFunction> {\n const functions: Record<string, BrowserFunction> = Object.fromEntries(\n Object.entries(BROWSER_FUNCTIONS).map(([name, definition]) => [\n name,\n (input: unknown) => definition.call(session, input)\n ])\n );\n if (hosts) {\n for (const [name, definition] of hostsFunctions(hosts.describe())) {\n functions[name] = (input: unknown) => definition.call(hosts, input);\n }\n }\n return functions;\n}\n\n/** How one internal site reads in the docs. */\nfunction describeHost(info: InternalHostInfo): string {\n const description = info.description ? ` — ${info.description}` : \"\";\n const target = info.assignable\n ? `. Assignable; target schema: ${JSON.stringify(info.targetSchema ?? {})}`\n : \"\";\n return `- ${info.url}${description}${target}`;\n}\n\n/**\n * The `browser` object's docs for the model, in Markdown: one line per\n * function, then the internal sites when the browser has any.\n */\nexport function browserFunctionDocs(hosts?: BrowserInternalHosts): string {\n const infos = hosts?.describe() ?? [];\n const functions = [\n ...Object.values(BROWSER_FUNCTIONS),\n ...(hosts ? hostsFunctions(infos).map(([, definition]) => definition) : [])\n ];\n const lines = functions.map((definition) => `- ${definition.doc}`);\n if (!hosts) return lines.join(\"\\n\");\n return [\n ...lines,\n \"\",\n \"## Internal sites\",\n \"\",\n \"The browser can open these sites, which aren't on the public internet. Plain http only: no https:// and no WebSockets, so a dev server's hot reload won't connect; reload the page instead.\",\n \"\",\n ...infos.map(describeHost)\n ].join(\"\\n\");\n}\n\n/**\n * How to use the browser, one rule per line. Each rule fixes a mistake\n * models make with CDP, or states something about this browser they can't\n * know; leave out what a model knows already. Rules about how the code runs\n * (timeouts, screenshots) belong to whatever runs it.\n */\nexport const BROWSER_RULES: readonly string[] = [\n \"The browser persists between runs: tabs, cookies, and logins are still there next time. You never start or close it.\",\n \"If the browser was replaced (idle timeout, crash), a notice says so 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.\",\n '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.',\n \"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.\",\n \"Target.createTarget opens a tab and makes it active. To switch to another open tab, call browser.attachToTarget({ targetId }); the sessionId it returns keeps working in later runs, while one from a raw Target.attachToTarget only works in the current run.\",\n \"Tabs a page opens itself (popups, target=_blank links) don't become active; a notice lists them.\",\n \"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.\",\n \"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'.\",\n \"Pick out what you need inside Runtime.evaluate and return only that. A screenshot over 1 MB fails the run.\"\n];\n","/**\n * Runs model-written JavaScript in a Dynamic Worker, with a `browser`\n * object whose functions run on the host. Used by `browserTool`.\n * Internal — not an entry point.\n *\n * The Worker has no network access (`globalOutbound: null`) and no bindings:\n * the only way out is `browser`, whose calls go back to the host over RPC.\n */\nimport { RpcTarget } from \"cloudflare:workers\";\nimport type { BrowserFunction } from \"./browser-functions\";\n\n/**\n * What a run returns: the code's result or error, its console output, and a\n * `notice` for the model when the code likely didn't do what it meant.\n */\nexport type BrowserCodeRun =\n | { result: unknown; logs: string[]; notice?: string }\n | { error: string; logs: string[] };\n\n/**\n * Code run as a function body returns nothing unless it says `return`. Tell\n * the model, rather than parse the code to return its last expression. A text\n * search for `return` can't tell a real one from one inside a string, so any\n * body run that comes back empty gets the notice.\n */\nconst STATEMENTS_NOTICE =\n \"Your code ran as statements and returned nothing. If you wanted a value, `return` it, or write an async arrow function.\";\n\nexport interface RunBrowserCodeOptions {\n loader: WorkerLoader;\n /** The model's code: an async arrow function, or a function body. */\n code: string;\n /** The functions on the `browser` global, run on the host. */\n functions: Record<string, BrowserFunction>;\n /** How long the code may run before it fails. */\n timeoutMs: number;\n}\n\n/**\n * The host side of `browser`. A failed call resolves with `{ error }`\n * instead of rejecting across RPC; the sandbox rethrows it as a local error\n * the code can catch.\n */\nclass HostFunctions extends RpcTarget {\n readonly #functions: Record<string, BrowserFunction>;\n\n constructor(functions: Record<string, BrowserFunction>) {\n super();\n this.#functions = functions;\n }\n\n async call(\n name: string,\n input: unknown\n ): Promise<{ value: unknown } | { error: string }> {\n const fn = Object.hasOwn(this.#functions, name)\n ? this.#functions[name]\n : undefined;\n if (!fn) return { error: `browser.${name} is not a function.` };\n try {\n return { value: await fn(input) };\n } catch (error) {\n return { error: error instanceof Error ? error.message : String(error) };\n }\n }\n}\n\n/** Console lines a run keeps, and the characters kept of each. */\nconst MAX_LOG_LINES = 100;\nconst MAX_LOG_LINE_CHARS = 2_000;\n\n/**\n * The entry module. `code.js` holds the model's code; keeping it in its own\n * module keeps the harness's names out of its scope.\n */\nfunction mainModule(timeoutMs: number, names: string[]): string {\n return `import { WorkerEntrypoint } from \"cloudflare:workers\";\nimport run from \"./code.js\";\n\nexport default class BrowserCode extends WorkerEntrypoint {\n async evaluate(binding) {\n // Logs are kept in the tool's stored output, so bound them here.\n const lines = [];\n let dropped = 0;\n const record = (prefix) => (...args) => {\n if (lines.length >= ${MAX_LOG_LINES}) {\n dropped++;\n return;\n }\n const line = prefix + args.map(String).join(\" \");\n lines.push(\n line.length > ${MAX_LOG_LINE_CHARS}\n ? line.slice(0, ${MAX_LOG_LINE_CHARS}) + \"…\"\n : line\n );\n };\n const logs = () =>\n dropped > 0 ? [...lines, \"[\" + dropped + \" more lines dropped]\"] : lines;\n console.log = record(\"\");\n console.info = record(\"\");\n console.warn = record(\"[warn] \");\n console.error = record(\"[error] \");\n const call = async (method, input) => {\n const reply = await binding.call(method, input);\n if (\"error\" in reply) throw new Error(reply.error);\n return reply.value;\n };\n const browser = Object.freeze({\n${names.map((name) => ` ${JSON.stringify(name)}: (input) => call(${JSON.stringify(name)}, input),`).join(\"\\n\")}\n });\n let timer;\n const timeout = new Promise((_, reject) => {\n timer = setTimeout(\n () => reject(new Error(\"Execution timed out after ${timeoutMs}ms\")),\n ${timeoutMs}\n );\n });\n try {\n return {\n result: await Promise.race([run(browser), timeout]),\n logs: logs()\n };\n } catch (error) {\n return {\n error: error instanceof Error ? error.message : String(error),\n logs: logs()\n };\n } finally {\n clearTimeout(timer);\n }\n }\n}\n`;\n}\n\n/** Strip the markdown fences models often wrap code in. */\nfunction stripFences(code: string): string {\n const fenced =\n /^```(?:js|javascript|typescript|ts|tsx|jsx)?\\s*\\n([\\s\\S]*?)```\\s*$/;\n return code.trim().match(fenced)?.[1] ?? code.trim();\n}\n\n/**\n * The model's code as an expression: an async arrow function (what the tool\n * asks for) is called, and any other expression is awaited.\n */\nfunction expressionModule(code: string): string {\n const expression = code\n .replace(/^export\\s+default\\s+/, \"\")\n // A last `;`, even with a comment after it, would end the expression.\n .replace(/;\\s*(\\/\\/[^\\n\"'`]*)?\\s*$/, \"\");\n return `export default async function (browser) {\n const value = (\n${expression}\n );\n return typeof value === \"function\" ? await value() : await value;\n}\n`;\n}\n\n/** The model's code as a function body, for statements with a `return`. */\nfunction bodyModule(code: string): string {\n return `export default async function (browser) {\n${code}\n}\n`;\n}\n\n/** The Worker failed to start because a module didn't parse. */\nfunction isSyntaxError(error: unknown): boolean {\n return error instanceof Error && /SyntaxError/.test(error.message);\n}\n\n/**\n * Run the model's code in a fresh Dynamic Worker, with `browser` bound to\n * `functions`.\n *\n * The code is first loaded as an expression (an async arrow function, as\n * the tool asks for). If that doesn't parse, it's loaded once more as a\n * function body, so statements ending in `return` work too; statements\n * with no `return` at all get a `notice`. Errors thrown\n * by the code, including timeouts, come back as `{ error }`; this only\n * rejects when the Worker can't run at all.\n */\nexport async function runBrowserCode(\n options: RunBrowserCodeOptions\n): Promise<BrowserCodeRun> {\n const code = stripFences(options.code);\n if (code === \"\") return { result: undefined, logs: [] };\n const binding = new HostFunctions(options.functions);\n try {\n return await evaluate(options, expressionModule(code), binding);\n } catch (error) {\n if (!isSyntaxError(error)) throw error;\n }\n let ran: BrowserCodeRun;\n try {\n ran = await evaluate(options, bodyModule(code), binding);\n } catch (error) {\n if (!isSyntaxError(error)) throw error;\n return {\n error: `Your code didn't parse: ${(error as Error).message}. Write an async arrow function, async () => { ... }.`,\n logs: []\n };\n }\n return \"result\" in ran && ran.result === undefined\n ? { ...ran, notice: STATEMENTS_NOTICE }\n : ran;\n}\n\n/**\n * How long past `timeoutMs` the host waits before giving up on a run whose\n * sandbox didn't time itself out. Capped at `timeoutMs` for short runs.\n */\nconst HOST_TIMEOUT_GRACE_MS = 5_000;\n\nasync function evaluate(\n options: RunBrowserCodeOptions,\n codeModule: string,\n binding: HostFunctions\n): Promise<BrowserCodeRun> {\n // `load()`, not `get(id, ...)`: every run's code is different, so there\n // is nothing to cache by id.\n const worker = options.loader.load({\n compatibilityDate: \"2025-06-01\",\n mainModule: \"main.js\",\n modules: {\n \"main.js\": mainModule(options.timeoutMs, Object.keys(options.functions)),\n \"code.js\": codeModule\n },\n globalOutbound: null\n });\n // The loaded Worker and its entrypoint stub own native handles. Dispose\n // both once the run settles rather than leaving them to GC, which can\n // finalize them during isolate shutdown and crash workerd.\n try {\n const entrypoint = worker.getEntrypoint() as unknown as {\n evaluate(binding: HostFunctions): Promise<BrowserCodeRun>;\n };\n const running = entrypoint.evaluate(binding);\n // If the deadline wins, the run's own settling is ignored.\n running.catch(() => {});\n // The sandbox times itself out, but its code can cancel that timer, so\n // the host keeps a deadline too. Missing it frees the scope's queue;\n // the run's later browser calls fail once its session closes.\n let timer: ReturnType<typeof setTimeout> | undefined;\n const deadline = new Promise<BrowserCodeRun>((resolve) => {\n timer = setTimeout(\n () =>\n resolve({\n error: `Execution timed out after ${options.timeoutMs}ms`,\n logs: []\n }),\n options.timeoutMs + Math.min(HOST_TIMEOUT_GRACE_MS, options.timeoutMs)\n );\n });\n try {\n return await Promise.race([running, deadline]);\n } finally {\n clearTimeout(timer);\n disposeQuietly(entrypoint);\n }\n } finally {\n disposeQuietly(worker);\n }\n}\n\nfunction disposeQuietly(resource: unknown): void {\n if (\n typeof resource !== \"object\" ||\n resource === null ||\n !(Symbol.dispose in resource)\n ) {\n return;\n }\n const dispose = (resource as { [Symbol.dispose]?: unknown })[Symbol.dispose];\n if (typeof dispose !== \"function\") return;\n try {\n (dispose as () => void).call(resource);\n } catch {\n // Disposal must never mask the run's result.\n }\n}\n","/**\n * The harness-neutral core of `browserTool`, shared by the adapters\n * in `tools/` (`agents/browser/ai-sdk`, `agents/browser/tanstack-ai`,\n * `agents/browser/pi`). Internal — not an entry point.\n */\nimport { DEFAULT_BROWSER_SCOPE } from \"./browser\";\nimport {\n BROWSER_RULES,\n browserFunctionDocs,\n browserFunctions\n} from \"./browser-functions\";\nimport { runBrowserCode } from \"./sandbox\";\nimport {\n BrowserSession,\n type BrowserSessionReport,\n type BrowserSource\n} from \"./session\";\nimport { transformBrowserResult } from \"./tool-helpers\";\nimport { truncateResult } from \"./truncate\";\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 * Worker Loader binding the model's code runs in. Each run gets a fresh\n * Dynamic Worker with no network access.\n *\n * Requires `\"worker_loaders\": [{ \"binding\": \"LOADER\" }]` in wrangler.jsonc.\n */\n loader: WorkerLoader;\n\n /** How long one run may take, in milliseconds. Defaults to 60000 (60s). */\n timeoutMs?: number;\n\n /**\n * Who is driving, when several chats or users share one `Browser`: runs\n * with the same scope share an active tab, and each scope keeps its own.\n * Cookies and logins are shared by every scope. Defaults to `\"shared\"`,\n * one active tab for every tool that leaves it out.\n */\n scope?: string;\n\n /**\n * @deprecated Not needed any more: the tool no longer runs on a codemode\n * facet. Ignored.\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/** What happened to the browser during a run, added to its result. */\ninterface BrowserReportFields {\n /**\n * Sentences telling the model what happened to its browser: it was\n * restarted, its tab was closed by someone else, another scope is working\n * in its tab too, or the page opened new tabs.\n */\n notice?: string;\n}\n\n/**\n * The result of one `browserTool` run: the code's result, or the\n * error it threw, plus what happened to the browser. Errors are returned,\n * not thrown, so the model can read them and try again.\n */\nexport type BrowserToolOutput = (\n | {\n status: \"completed\";\n /** Identifies the run, for logs and UIs. */\n executionId: string;\n result: unknown;\n /** What the code wrote to the console. */\n logs?: string[];\n }\n | {\n status: \"error\";\n executionId: string;\n error: string;\n logs?: string[];\n }\n) &\n BrowserReportFields;\n\nconst DEFAULT_TIMEOUT_MS = 60_000;\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\nconst TAB_CLOSED_NOTICE =\n '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.';\n\nconst TAB_SHARED_NOTICE =\n \"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.\";\n\n/**\n * The model's input schema, as a Standard Schema with a JSON Schema view,\n * so the AI SDK and TanStack AI both accept it without zod.\n */\nconst inputJsonSchema = {\n type: \"object\",\n properties: {\n code: {\n type: \"string\",\n description:\n \"An async arrow function that drives the browser with `browser`\"\n }\n },\n required: [\"code\"],\n additionalProperties: false\n};\n\nconst browserToolInputSchema = {\n \"~standard\": {\n version: 1 as const,\n vendor: \"agents\",\n types: undefined as\n | { readonly input: BrowserToolInput; readonly output: BrowserToolInput }\n | undefined,\n validate: (\n value: unknown\n ):\n | { readonly value: BrowserToolInput }\n | { readonly issues: ReadonlyArray<{ readonly message: string }> } =>\n typeof value === \"object\" &&\n value !== null &&\n \"code\" in value &&\n typeof value.code === \"string\"\n ? { value: { code: value.code } }\n : {\n issues: [\n { message: \"Expected an object with a string code property\" }\n ]\n },\n jsonSchema: {\n input: (_options: { readonly target: string }) => inputJsonSchema,\n output: (_options: { readonly target: string }) => inputJsonSchema\n }\n }\n};\n\n/**\n * The latest run queued per browser and scope, so runs in one scope go one\n * at a time, even from tools built separately (one per turn, say). Per\n * isolate, which is per Durable Object.\n */\nconst runQueues = new WeakMap<BrowserSource, Map<string, Promise<unknown>>>();\n\nfunction queued<T>(\n browser: BrowserSource,\n scope: string,\n run: () => Promise<T>\n): Promise<T> {\n let queues = runQueues.get(browser);\n if (!queues) runQueues.set(browser, (queues = new Map()));\n const previous = queues.get(scope) ?? Promise.resolve();\n const next = previous.then(run, run);\n const settled = next.catch(() => undefined);\n queues.set(scope, settled);\n void settled.then(() => {\n if (queues.get(scope) === settled) queues.delete(scope);\n });\n return next;\n}\n\n/** The report as a notice on the result, in sentences the model reads. */\nfunction reportFields(\n report: BrowserSessionReport | undefined\n): BrowserReportFields {\n if (!report) return {};\n const notices = [\n report.restarted && RESTARTED_NOTICE,\n report.tabClosed && !report.restarted && TAB_CLOSED_NOTICE,\n report.tabShared && TAB_SHARED_NOTICE,\n report.newTabs.length > 0 &&\n `New tabs opened by the page: ${JSON.stringify(report.newTabs)}`\n ].filter((notice) => typeof notice === \"string\");\n return notices.length > 0 ? { notice: notices.join(\" \") } : {};\n}\n\n/**\n * Build the description and `execute` for one `browserTool`.\n *\n * Each run opens a session on the browser in its scope, runs the model's\n * code in a fresh Dynamic Worker with `browser` bound to that session, closes\n * the session, and adds its report to the result. Runs in one scope wait\n * for each other, since they drive one tab; runs in different scopes go in\n * parallel.\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 timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const rules = [\n ...BROWSER_RULES,\n \"Results over about 24,000 characters are cut short.\",\n `Each run times out after ${Math.round(timeoutMs / 1000)}s, so split long jobs (crawling a site, say) across runs.`,\n adapter.screenshotHint,\n \"If your code doesn't parse, the line number in the error counts from wrapper code, not yours. Check regex literals and escapes.\"\n ]\n .map((rule) => `- ${rule}`)\n .join(\"\\n\");\n const description = [\n \"Run JavaScript that drives a persistent Chrome browser over the Chrome DevTools Protocol (CDP).\",\n \"\",\n \"Write an async arrow function, `async () => { ... }`, and return a small JSON value. Use the `browser` global to reach the browser. `fetch` is blocked and there is no file system or Node.js API.\",\n \"\",\n \"## browser\",\n \"\",\n browserFunctionDocs(options.browser.internalHosts),\n \"\",\n \"## Rules\",\n \"\",\n rules\n ].join(\"\\n\");\n\n const defaultScope = options.scope ?? DEFAULT_BROWSER_SCOPE;\n\n async function run(input: BrowserToolInput, scope: string) {\n const executionId = crypto.randomUUID();\n const session = new BrowserSession(options.browser, { scope });\n let output: BrowserToolOutput;\n try {\n const ran = await runBrowserCode({\n loader: options.loader,\n code: input.code,\n functions: browserFunctions(session, options.browser.internalHosts),\n timeoutMs\n });\n const logs = ran.logs.length > 0 ? { logs: ran.logs } : {};\n output =\n \"error\" in ran\n ? {\n status: \"error\",\n executionId,\n // The code can throw anything, so bound it like a result.\n error: truncateResult(ran.error) as string,\n ...logs\n }\n : {\n status: \"completed\",\n executionId,\n result: transformBrowserResult(ran.result),\n ...logs,\n ...(ran.notice ? { notice: ran.notice } : {})\n };\n } catch (error) {\n output = {\n status: \"error\",\n executionId,\n error: error instanceof Error ? error.message : String(error)\n };\n }\n const report = reportFields(await session.close());\n const notice = [output.notice, report.notice].filter(Boolean).join(\" \");\n return { ...output, ...report, ...(notice ? { notice } : {}) };\n }\n\n return {\n description,\n inputSchema: browserToolInputSchema,\n /** Run `input` in `scope`, which defaults to the tool's `scope`. */\n execute: (\n input: BrowserToolInput,\n scope: string = defaultScope\n ): Promise<BrowserToolOutput> =>\n queued(options.browser, scope, () => run(input, scope))\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ //#region src/browser/capability-id.ts
2
+ /**
3
+ * The Lifecycle capability id of the `Browser` named `name`. Its own module
4
+ * so `Agent.fetchInternalHost()` can route to a browser without importing
5
+ * the browser code.
6
+ */
7
+ function browserCapabilityId(name) {
8
+ return `browser:${name}`;
9
+ }
10
+ //#endregion
11
+ export { browserCapabilityId as t };
12
+
13
+ //# sourceMappingURL=capability-id-B3J8Nok_.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability-id-B3J8Nok_.js","names":[],"sources":["../src/browser/capability-id.ts"],"sourcesContent":["/**\n * The Lifecycle capability id of the `Browser` named `name`. Its own module\n * so `Agent.fetchInternalHost()` can route to a browser without importing\n * the browser code.\n */\nexport function browserCapabilityId(name: string): string {\n return `browser:${name}`;\n}\n"],"mappings":";;;;;;AAKA,SAAgB,oBAAoB,MAAsB;CACxD,OAAO,WAAW;AACpB"}
@@ -9,7 +9,7 @@ import {
9
9
  _ as FiberRecoveryContext$1,
10
10
  at as isPositionlessAgentToolChunk,
11
11
  z as AgentToolEventState
12
- } from "../agent-routing-CDbpVjYq.js";
12
+ } from "../agent-routing-wS1TimZ_.js";
13
13
  import { _ as TaskStep, m as TaskRunOptions } from "../tasks-E89KdwkS.js";
14
14
  import {
15
15
  n as ClientToolSchema,
@@ -34,7 +34,7 @@ import {
34
34
  r as AgentToolProgressEmitResult,
35
35
  s as interceptAgentToolBroadcast,
36
36
  t as AgentToolBroadcastHooks
37
- } from "../agent-tools-C8DH8Yii.js";
37
+ } from "../agent-tools-Bamxxb6Y.js";
38
38
  import { l as SessionMessage } from "../types-B7LojTe4.js";
39
39
  import { ModelMessage, UIMessage } from "ai";
40
40
  import { Connection, FiberRecoveryContext } from "agents";
@@ -1,8 +1,8 @@
1
1
  import {
2
- bn as DynamicAgentClass,
3
2
  c as Agent,
4
- xn as DynamicAgentStub
5
- } from "../agent-routing-CDbpVjYq.js";
3
+ nn as DynamicAgentStub,
4
+ tn as DynamicAgentClass
5
+ } from "../agent-routing-wS1TimZ_.js";
6
6
  import { Lock, QueueEntry, StateAdapter } from "chat";
7
7
 
8
8
  //#region src/chat-sdk/agent.d.ts
@@ -1,4 +1,4 @@
1
- import { i as getCurrentAgent, t as Agent } from "../src-DfLzdwlK.js";
1
+ import { i as getCurrentAgent, t as Agent } from "../src-BGolXG_H.js";
2
2
  //#region src/chat-sdk/agent.ts
3
3
  const NEXT_CLEANUP_AT_KEY = "next_cleanup_at";
4
4
  const CLEANUP_SCHEDULE_ID_KEY = "cleanup_schedule_id";