agents 0.30.1 → 0.32.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.
- package/README.md +2 -1
- package/dist/{agent-routing-CDbpVjYq.d.ts → agent-routing-vVMA7m8Y.d.ts} +69 -608
- package/dist/agent-routing.d.ts +1 -1
- package/dist/agent-tool-types.d.ts +1 -1
- package/dist/{agent-tools-C8DH8Yii.d.ts → agent-tools-gGXXkf1z.d.ts} +2 -2
- package/dist/agent-tools.d.ts +1 -1
- package/dist/agent-tools.js +0 -1
- package/dist/agent-tools.js.map +1 -1
- package/dist/{ai-iXw0uOGj.d.ts → ai-CDn3Abax.d.ts} +30 -4
- package/dist/{ai-sdk-DRPh2rRa.js → ai-sdk-Kon40i0q.js} +2 -36
- package/dist/ai-sdk-Kon40i0q.js.map +1 -0
- package/dist/browser/ai.d.ts +1 -1
- package/dist/browser/ai.js +16 -3
- package/dist/browser/ai.js.map +1 -1
- package/dist/browser/index.d.ts +64 -16
- package/dist/browser/index.js +4 -4
- package/dist/browser/tools/ai-sdk.d.ts +7 -5
- package/dist/browser/tools/ai-sdk.js +4 -4
- package/dist/browser/tools/ai-sdk.js.map +1 -1
- package/dist/browser/tools/pi.d.ts +9 -15
- package/dist/browser/tools/pi.js +6 -14
- package/dist/browser/tools/pi.js.map +1 -1
- package/dist/browser/tools/tanstack-ai.d.ts +48 -25
- package/dist/browser/tools/tanstack-ai.js +8 -4
- package/dist/browser/tools/tanstack-ai.js.map +1 -1
- package/dist/{browser-DccPw_wW.js → browser-1eUbPvC2.js} +413 -14
- package/dist/browser-1eUbPvC2.js.map +1 -0
- package/dist/{browser-DlXhSAdf.d.ts → browser-D9RNpJuT.d.ts} +254 -20
- package/dist/browser-tool-BhjCcNVE.d.ts +71 -0
- package/dist/browser-tool-DRan7Lk6.js +497 -0
- package/dist/browser-tool-DRan7Lk6.js.map +1 -0
- package/dist/capability-id-B3J8Nok_.js +13 -0
- package/dist/capability-id-B3J8Nok_.js.map +1 -0
- package/dist/catalog-BpOejOsE.js +239 -0
- package/dist/catalog-BpOejOsE.js.map +1 -0
- package/dist/catalog-BpiVV1OF.d.ts +173 -0
- package/dist/chat/index.d.ts +2 -2
- package/dist/chat-sdk/index.d.ts +3 -3
- package/dist/chat-sdk/index.js +1 -1
- package/dist/client-4OlMIJx-.js.map +1 -1
- package/dist/{client-BnYbNT3z.js → client-DMWujQJ1.js} +68 -244
- package/dist/client-DMWujQJ1.js.map +1 -0
- package/dist/{client-C27CtRZr.d.ts → client-eSfgYvd3.d.ts} +9 -7
- package/dist/client.d.ts +1 -1
- package/dist/{cloudflare-Dzvc7V2N.js → cloudflare-CUUm_9q9.js} +26 -4
- package/dist/cloudflare-CUUm_9q9.js.map +1 -0
- package/dist/{connector-DAXBOIIi.js → connector-CM-uuxKM.js} +96 -8
- package/dist/connector-CM-uuxKM.js.map +1 -0
- package/dist/{connector-20DJ6p4k.d.ts → connector-DZ71EjE-.d.ts} +109 -9
- package/dist/{do-oauth-client-provider-DfsUcpc3.d.ts → do-oauth-client-provider-DtNqv7oj.d.ts} +26 -2
- package/dist/{index-YSKgfgg9.d.ts → emitter-BJZtCVjQ.d.ts} +6 -140
- package/dist/emitter-DPOVcCln.js +15 -0
- package/dist/emitter-DPOVcCln.js.map +1 -0
- package/dist/experimental/channels/web/index.js +1 -1
- package/dist/experimental/webmcp.js +1 -1
- package/dist/{handler-stateless-Bly_nSH3.js → handler-stateless-B5hU3dxK.js} +35 -2
- package/dist/handler-stateless-B5hU3dxK.js.map +1 -0
- package/dist/{handler-stateless-DxYpJ_XF.d.ts → handler-stateless-C90kD5_R.d.ts} +3 -47
- package/dist/harness/container/index.d.ts +15 -15
- package/dist/harness/container/index.js +0 -1
- package/dist/harness/container/index.js.map +1 -1
- package/dist/harness/container/runtime/index.js +2 -2
- package/dist/harness/opencode/index.js +0 -1
- package/dist/harness/opencode/index.js.map +1 -1
- package/dist/harness/pi/index.d.ts +15 -7
- package/dist/harness/pi/index.js +74 -151
- package/dist/harness/pi/index.js.map +1 -1
- package/dist/harness/think/index.d.ts +2 -1
- package/dist/{images-xaDv2Fqe.js → images-CfuRCjwq.js} +1 -5
- package/dist/images-CfuRCjwq.js.map +1 -0
- package/dist/{index-DCxk3OSJ.d.ts → index-8k4qsX1T.d.ts} +2 -28
- package/dist/index.d.ts +15 -16
- package/dist/index.js +3 -3
- package/dist/internal-hosts-Kfp5A392.d.ts +230 -0
- package/dist/internal-kmw70xwR.js.map +1 -1
- package/dist/invoker-BC1SZ3q1.js +26 -0
- package/dist/invoker-BC1SZ3q1.js.map +1 -0
- package/dist/invoker-DoLFPJZV.d.ts +21 -0
- package/dist/markdown-BZ-VLtA1.js +86 -0
- package/dist/markdown-BZ-VLtA1.js.map +1 -0
- package/dist/mcp/client/do-oauth-client-provider.d.ts +4 -2
- package/dist/mcp/client/do-oauth-client-provider.js +15 -1
- package/dist/mcp/client/do-oauth-client-provider.js.map +1 -1
- package/dist/mcp/client/index.d.ts +15 -1
- package/dist/mcp/client/index.js +2 -2
- package/dist/mcp/client/x402.d.ts +7 -5
- package/dist/mcp/client/x402.js +34 -1
- package/dist/mcp/client/x402.js.map +1 -1
- package/dist/mcp/index.d.ts +561 -27
- package/dist/mcp/index.js +180 -3
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/server/index.d.ts +1 -1
- package/dist/mcp/server/index.js +1 -1
- package/dist/models/ai-sdk/index.d.ts +2 -2
- package/dist/models/ai-sdk/index.js +1 -1
- package/dist/models/opencode/index.d.ts +2 -2
- package/dist/models/opencode/index.js +1 -1
- package/dist/models/pi-ai/index.d.ts +3 -28
- package/dist/models/pi-ai/index.js +160 -18
- package/dist/models/pi-ai/index.js.map +1 -1
- package/dist/observability/ai/index.js +1 -2
- package/dist/observability/ai/index.js.map +1 -1
- package/dist/observability/index.d.ts +170 -9
- package/dist/observability/index.js +386 -11
- package/dist/observability/index.js.map +1 -1
- package/dist/protocol-BEgrJYC5.d.ts +29 -29
- package/dist/react.d.ts +2 -2
- package/dist/resource-B59OV2a2.js +325 -0
- package/dist/resource-B59OV2a2.js.map +1 -0
- package/dist/routing/index.d.ts +1 -1
- package/dist/script-CFDLTk5b.d.ts +91 -0
- package/dist/script-workspace-BpdAYq1R.d.ts +41 -0
- package/dist/serializable.d.ts +1 -1
- package/dist/{session-store-yvwq1f9x.js → session-7CDgVE0z.js} +455 -33
- package/dist/session-7CDgVE0z.js.map +1 -0
- package/dist/{session-store-_IK6dV-w.d.ts → session-store-B7Ca-u1t.d.ts} +8 -1
- package/dist/settings-Dk2il2Hf.js.map +1 -1
- package/dist/skill-DRBcIBjC.d.ts +221 -0
- package/dist/skills/index.d.ts +275 -36
- package/dist/skills/index.js +265 -1534
- package/dist/skills/index.js.map +1 -1
- package/dist/skills/runner.d.ts +37 -0
- package/dist/skills/runner.js +912 -0
- package/dist/skills/runner.js.map +1 -0
- package/dist/skills/tools/ai-sdk.d.ts +36 -0
- package/dist/skills/tools/ai-sdk.js +94 -0
- package/dist/skills/tools/ai-sdk.js.map +1 -0
- package/dist/source-9RQzlEhM.d.ts +112 -0
- package/dist/{src-DfLzdwlK.js → src-DoGNJ4Ud.js} +21 -6
- package/dist/src-DoGNJ4Ud.js.map +1 -0
- package/dist/sub-routing.d.ts +12 -12
- package/dist/tool-CTp72Jb_.d.ts +10 -10
- package/dist/tool-CxKsvxjm.js.map +1 -1
- package/dist/tool-DkT_DJBm.js.map +1 -1
- package/dist/tool-DoVberpN.d.ts +22 -22
- package/dist/tool-helpers-BI7ifO2S.js +255 -0
- package/dist/tool-helpers-BI7ifO2S.js.map +1 -0
- package/dist/{transport-CPh7YGiw.d.ts → transport-5moD2pW5.d.ts} +1 -43
- package/dist/types-D2naO3rA.d.ts +49 -0
- package/dist/vite.d.ts +2 -2
- package/dist/vite.js +10 -258
- package/dist/vite.js.map +1 -1
- package/dist/voice/index.d.ts +1 -1
- package/dist/websockets/index.js +1 -1
- package/dist/{websockets-CyoeosdO.js → websockets-C6SJtWCJ.js} +1 -2
- package/dist/{websockets-CyoeosdO.js.map → websockets-C6SJtWCJ.js.map} +1 -1
- package/dist/workflow-types.d.ts +1 -1
- package/dist/workflows.d.ts +1 -1
- package/dist/workflows.js +0 -1
- package/dist/workflows.js.map +1 -1
- package/docs/browse-the-web.md +247 -413
- package/docs/harnesses/pi.md +36 -30
- package/docs/index.md +4 -4
- package/docs/mcp-client.md +25 -1
- package/docs/mcp-servers.md +26 -7
- package/docs/models-pi-ai.md +9 -11
- package/docs/models.md +1 -8
- package/docs/observability.md +5 -3
- package/docs/securing-mcp-servers.md +113 -302
- package/package.json +31 -17
- package/dist/ai-sdk-DRPh2rRa.js.map +0 -1
- package/dist/browser-DccPw_wW.js.map +0 -1
- package/dist/browser-tool-B1mhifin.js +0 -616
- package/dist/browser-tool-B1mhifin.js.map +0 -1
- package/dist/browser-tool-Cxjk5sgG.d.ts +0 -92
- package/dist/client-BnYbNT3z.js.map +0 -1
- package/dist/cloudflare-Dzvc7V2N.js.map +0 -1
- package/dist/connector-DAXBOIIi.js.map +0 -1
- package/dist/handler-stateless-Bly_nSH3.js.map +0 -1
- package/dist/images-xaDv2Fqe.js.map +0 -1
- package/dist/index-iv9FTXX9.d.ts +0 -311
- package/dist/invoker-CG0_p_Wq.d.ts +0 -20
- package/dist/invoker-CHMnoxIA.js +0 -57
- package/dist/invoker-CHMnoxIA.js.map +0 -1
- package/dist/session-store-yvwq1f9x.js.map +0 -1
- package/dist/skills/compile.d.ts +0 -40
- package/dist/skills/compile.js +0 -65
- package/dist/skills/compile.js.map +0 -1
- package/dist/src-DfLzdwlK.js.map +0 -1
- package/dist/tool-helpers-O5BDVEAY.js +0 -58
- package/dist/tool-helpers-O5BDVEAY.js.map +0 -1
- package/skills-module.d.ts +0 -22
|
@@ -1,616 +0,0 @@
|
|
|
1
|
-
import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, r as _assertClassBrand, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-DZBYAB34.js";
|
|
2
|
-
import { t as _classPrivateMethodInitSpec } from "./classPrivateMethodInitSpec-qMjJ6sHQ.js";
|
|
3
|
-
import { b as CdpProtocolError, s as validateConnectorArgs } from "./session-store-yvwq1f9x.js";
|
|
4
|
-
import { i as transformBrowserResult, r as resolveCtx } from "./tool-helpers-O5BDVEAY.js";
|
|
5
|
-
import "./browser-DccPw_wW.js";
|
|
6
|
-
import { CodemodeConnector, DynamicWorkerExecutor, MAX_DURABLE_VALUE_BYTES, createCodemodeRuntime } from "@cloudflare/codemode";
|
|
7
|
-
/**
|
|
8
|
-
* `cdp.attachToTarget` returns `{ sessionId: "target:<targetId>" }` — a
|
|
9
|
-
* stable handle rather than a connection-scoped CDP session id, so replayed
|
|
10
|
-
* code computes identical arguments. `send` resolves it on the live socket.
|
|
11
|
-
*/
|
|
12
|
-
const ATTACH_HANDLE_PREFIX = "target:";
|
|
13
|
-
/** Chrome's JSON-RPC code for a CDP session id it doesn't know. */
|
|
14
|
-
const CDP_SESSION_NOT_FOUND = -32001;
|
|
15
|
-
/**
|
|
16
|
-
* Codemode records every `send` result for replay and fails the run when one
|
|
17
|
-
* is over {@link MAX_DURABLE_VALUE_BYTES}, telling the model to write to a
|
|
18
|
-
* file, which this sandbox can't. A full-page screenshot is the usual
|
|
19
|
-
* culprit, so say how to take a smaller one instead.
|
|
20
|
-
*/
|
|
21
|
-
function assertScreenshotFits(result) {
|
|
22
|
-
const size = JSON.stringify(result ?? null).length;
|
|
23
|
-
if (size <= MAX_DURABLE_VALUE_BYTES) return;
|
|
24
|
-
const megabytes = (size / 1e6).toFixed(1);
|
|
25
|
-
throw new Error(`The screenshot result is ${megabytes} MB, over the 1 MB limit on a cdp.send result. Capture just the viewport (no captureBeyondViewport or clip larger than the window), or pass format: "jpeg" with quality: 60.`);
|
|
26
|
-
}
|
|
27
|
-
/** {@link BrowserSessionConnector} `#liveParams`: nothing to detach. */
|
|
28
|
-
const DETACHED = Symbol("detached");
|
|
29
|
-
/** Reports awaiting their tool call; bounded in case a caller never reads. */
|
|
30
|
-
const MAX_PENDING_REPORTS = 100;
|
|
31
|
-
/**
|
|
32
|
-
* Commands that would end the browser. The host owns its lifetime, so the
|
|
33
|
-
* model can't close it out from under the agent (and lose its logins).
|
|
34
|
-
*/
|
|
35
|
-
const BROWSER_LIFETIME_COMMANDS = /* @__PURE__ */ new Set([
|
|
36
|
-
"Browser.close",
|
|
37
|
-
"Browser.crash",
|
|
38
|
-
"Browser.crashGpuProcess"
|
|
39
|
-
]);
|
|
40
|
-
/**
|
|
41
|
-
* How to use the `cdp` connector, one rule per line. `codemode.describe("cdp")`
|
|
42
|
-
* returns these, and `browserTool` puts them in its description. Each rule
|
|
43
|
-
* fixes a mistake models make with CDP, or states something about this
|
|
44
|
-
* browser they can't know; leave out what a model knows already.
|
|
45
|
-
*/
|
|
46
|
-
const BROWSER_INSTRUCTIONS = [
|
|
47
|
-
"The browser persists between runs: tabs, cookies, and logins are still there next time. You never start or close it.",
|
|
48
|
-
"If the browser was replaced (idle timeout, crash), the result says restarted: true and earlier tabs and logins are gone. Before an action that matters (submitting a form, a purchase), check you're on the page you expect.",
|
|
49
|
-
"Page-scoped commands (Page.*, Runtime.*, DOM.*, Input.*, Network.*, Emulation.*) need sessionId: \"active\", your tab, which is remembered between runs. Browser.* and Target.* commands take no sessionId.",
|
|
50
|
-
"Other open tabs (Target.getTargets) may belong to other conversations using this browser: don't close, navigate, or attach to a tab you didn't open unless the user asks.",
|
|
51
|
-
"Target.createTarget opens a tab and makes it active. To switch to another open tab, call cdp.attachToTarget({ targetId }); the sessionId it returns keeps working in later runs, while one from a raw Target.attachToTarget only works in the current run.",
|
|
52
|
-
"Tabs a page opens itself (popups, target=_blank links) don't become active; the result lists them as newTabs.",
|
|
53
|
-
"Runtime.evaluate: pass returnByValue: true, or objects come back as a remote reference with no value, and awaitPromise: true for async expressions. The value is at result.value. A thrown error doesn't reject: check exceptionDetails.",
|
|
54
|
-
"Page.navigate returns before the page loads and doesn't throw when it fails: check errorText, then poll document.readyState with Runtime.evaluate until it is 'complete'.",
|
|
55
|
-
"Pick out what you need inside Runtime.evaluate and return only that. Results over about 24,000 characters are cut short, and a single cdp.send result over 1 MB (a full-page screenshot, say) fails the run."
|
|
56
|
-
].join("\n");
|
|
57
|
-
var _browser = /* @__PURE__ */ new WeakMap();
|
|
58
|
-
var _scope = /* @__PURE__ */ new WeakMap();
|
|
59
|
-
var _states = /* @__PURE__ */ new WeakMap();
|
|
60
|
-
var _connecting = /* @__PURE__ */ new WeakMap();
|
|
61
|
-
var _reports = /* @__PURE__ */ new WeakMap();
|
|
62
|
-
var _BrowserSessionConnector_brand = /* @__PURE__ */ new WeakSet();
|
|
63
|
-
/**
|
|
64
|
-
* Codemode connector exposing one host-named, persistent browser over the
|
|
65
|
-
* Chrome DevTools Protocol as the `cdp` global.
|
|
66
|
-
*
|
|
67
|
-
* The model never manages sessions: every execution reattaches to the
|
|
68
|
-
* browser (or gets a replacement, reported as `restarted`), and
|
|
69
|
-
* `sessionId: "active"` addresses the tab it last worked in. The active tab
|
|
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.
|
|
74
|
-
*
|
|
75
|
-
* The CDP socket is per pass: opened on the first call, closed when the pass
|
|
76
|
-
* ends. The browser itself outlives the execution.
|
|
77
|
-
*/
|
|
78
|
-
var BrowserSessionConnector = class extends CodemodeConnector {
|
|
79
|
-
constructor(ctx, options) {
|
|
80
|
-
super(ctx, {});
|
|
81
|
-
_classPrivateMethodInitSpec(this, _BrowserSessionConnector_brand);
|
|
82
|
-
_classPrivateFieldInitSpec(this, _browser, void 0);
|
|
83
|
-
_classPrivateFieldInitSpec(this, _scope, void 0);
|
|
84
|
-
_classPrivateFieldInitSpec(this, _states, /* @__PURE__ */ new Map());
|
|
85
|
-
_classPrivateFieldInitSpec(this, _connecting, /* @__PURE__ */ new Map());
|
|
86
|
-
_classPrivateFieldInitSpec(this, _reports, /* @__PURE__ */ new Map());
|
|
87
|
-
_classPrivateFieldSet2(_browser, this, options.browser);
|
|
88
|
-
_classPrivateFieldSet2(_scope, this, options.scope);
|
|
89
|
-
}
|
|
90
|
-
name() {
|
|
91
|
-
return "cdp";
|
|
92
|
-
}
|
|
93
|
-
instructions() {
|
|
94
|
-
return BROWSER_INSTRUCTIONS;
|
|
95
|
-
}
|
|
96
|
-
tool(name, tool) {
|
|
97
|
-
return validateConnectorArgs(this.name(), name, tool);
|
|
98
|
-
}
|
|
99
|
-
tools() {
|
|
100
|
-
return {
|
|
101
|
-
send: {
|
|
102
|
-
description: "Send a CDP command and return its method result directly. Pass sessionId: \"active\" for page-scoped commands; omit it for Browser/Target commands.",
|
|
103
|
-
inputSchema: {
|
|
104
|
-
type: "object",
|
|
105
|
-
properties: {
|
|
106
|
-
method: {
|
|
107
|
-
type: "string",
|
|
108
|
-
description: "CDP method, e.g. \"Page.navigate\""
|
|
109
|
-
},
|
|
110
|
-
params: {
|
|
111
|
-
type: "object",
|
|
112
|
-
description: "CDP command parameters"
|
|
113
|
-
},
|
|
114
|
-
sessionId: {
|
|
115
|
-
type: "string",
|
|
116
|
-
description: "\"active\" for the current tab, or a handle from attachToTarget. Omit for Browser/Target commands."
|
|
117
|
-
},
|
|
118
|
-
timeoutMs: {
|
|
119
|
-
type: "number",
|
|
120
|
-
description: "Per-command timeout override in milliseconds"
|
|
121
|
-
}
|
|
122
|
-
},
|
|
123
|
-
required: ["method"]
|
|
124
|
-
},
|
|
125
|
-
execute: async (args, ctx) => {
|
|
126
|
-
const { method, params, sessionId, timeoutMs } = args;
|
|
127
|
-
if (BROWSER_LIFETIME_COMMANDS.has(method)) throw new Error(`${method} is not available: this browser is managed for you and persists between runs. To close a tab, send Target.closeTarget with its targetId.`);
|
|
128
|
-
const state = await _assertClassBrand(_BrowserSessionConnector_brand, this, _state).call(this, _assertClassBrand(_BrowserSessionConnector_brand, this, _executionId).call(this, ctx));
|
|
129
|
-
const live = await _assertClassBrand(_BrowserSessionConnector_brand, this, _resolveSessionId).call(this, state, sessionId);
|
|
130
|
-
const liveParams = _assertClassBrand(_BrowserSessionConnector_brand, this, _liveParams).call(this, state, method, params);
|
|
131
|
-
if (liveParams === DETACHED) return {};
|
|
132
|
-
let result;
|
|
133
|
-
try {
|
|
134
|
-
result = await state.connected.cdp.send(method, liveParams, {
|
|
135
|
-
sessionId: live,
|
|
136
|
-
timeoutMs
|
|
137
|
-
});
|
|
138
|
-
} catch (error) {
|
|
139
|
-
throw await _assertClassBrand(_BrowserSessionConnector_brand, this, _teach).call(this, state, error, method, sessionId);
|
|
140
|
-
}
|
|
141
|
-
_assertClassBrand(_BrowserSessionConnector_brand, this, _observe).call(this, state, method, liveParams, result);
|
|
142
|
-
if (method === "Page.captureScreenshot") assertScreenshotFits(result);
|
|
143
|
-
return result;
|
|
144
|
-
}
|
|
145
|
-
},
|
|
146
|
-
attachToTarget: {
|
|
147
|
-
description: "Switch to an open tab: make it the active tab and return { sessionId } — a handle you can pass to page-scoped send calls for that tab.",
|
|
148
|
-
inputSchema: {
|
|
149
|
-
type: "object",
|
|
150
|
-
properties: {
|
|
151
|
-
targetId: {
|
|
152
|
-
type: "string",
|
|
153
|
-
description: "Target id from Target.getTargets/createTarget"
|
|
154
|
-
},
|
|
155
|
-
timeoutMs: { type: "number" }
|
|
156
|
-
},
|
|
157
|
-
required: ["targetId"]
|
|
158
|
-
},
|
|
159
|
-
outputSchema: {
|
|
160
|
-
type: "object",
|
|
161
|
-
properties: { sessionId: {
|
|
162
|
-
type: "string",
|
|
163
|
-
description: "Session handle for page-scoped send calls"
|
|
164
|
-
} },
|
|
165
|
-
required: ["sessionId"]
|
|
166
|
-
},
|
|
167
|
-
execute: async (args, ctx) => {
|
|
168
|
-
const { targetId, timeoutMs } = args;
|
|
169
|
-
const state = await _assertClassBrand(_BrowserSessionConnector_brand, this, _state).call(this, _assertClassBrand(_BrowserSessionConnector_brand, this, _executionId).call(this, ctx));
|
|
170
|
-
await _assertClassBrand(_BrowserSessionConnector_brand, this, _attach).call(this, state, targetId, timeoutMs);
|
|
171
|
-
_assertClassBrand(_BrowserSessionConnector_brand, this, _setActive).call(this, state, targetId);
|
|
172
|
-
return { sessionId: attachHandle(targetId) };
|
|
173
|
-
}
|
|
174
|
-
},
|
|
175
|
-
spec: {
|
|
176
|
-
description: "Return the searchable Chrome DevTools Protocol spec: domains with their commands, events, and types.",
|
|
177
|
-
replay: "reexecute",
|
|
178
|
-
inputSchema: {
|
|
179
|
-
type: "object",
|
|
180
|
-
properties: {}
|
|
181
|
-
},
|
|
182
|
-
execute: async (_args, ctx) => {
|
|
183
|
-
return (await _assertClassBrand(_BrowserSessionConnector_brand, this, _state).call(this, _assertClassBrand(_BrowserSessionConnector_brand, this, _executionId).call(this, ctx))).connected.spec();
|
|
184
|
-
}
|
|
185
|
-
},
|
|
186
|
-
getDebugLog: {
|
|
187
|
-
description: "Return recent CDP protocol traffic (sends, receives, warnings) for this execution — useful to diagnose failures and timeouts.",
|
|
188
|
-
replay: "reexecute",
|
|
189
|
-
inputSchema: {
|
|
190
|
-
type: "object",
|
|
191
|
-
properties: { limit: {
|
|
192
|
-
type: "number",
|
|
193
|
-
minimum: 1,
|
|
194
|
-
description: "Max entries to return, newest last (default 50; only the last 400 are kept)"
|
|
195
|
-
} }
|
|
196
|
-
},
|
|
197
|
-
execute: async (args, ctx) => {
|
|
198
|
-
const { limit } = args ?? {};
|
|
199
|
-
return (await _assertClassBrand(_BrowserSessionConnector_brand, this, _state).call(this, _assertClassBrand(_BrowserSessionConnector_brand, this, _executionId).call(this, ctx))).connected.cdp.getDebugLog(limit);
|
|
200
|
-
}
|
|
201
|
-
},
|
|
202
|
-
clearDebugLog: {
|
|
203
|
-
description: "Clear the CDP debug log for this execution.",
|
|
204
|
-
inputSchema: {
|
|
205
|
-
type: "object",
|
|
206
|
-
properties: {}
|
|
207
|
-
},
|
|
208
|
-
execute: async (_args, ctx) => {
|
|
209
|
-
(await _assertClassBrand(_BrowserSessionConnector_brand, this, _state).call(this, _assertClassBrand(_BrowserSessionConnector_brand, this, _executionId).call(this, ctx))).connected.cdp.clearDebugLog();
|
|
210
|
-
return null;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
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.
|
|
219
|
-
*/
|
|
220
|
-
async onPassEnd(executionId) {
|
|
221
|
-
const state = _classPrivateFieldGet2(_states, this).get(executionId);
|
|
222
|
-
if (!state) return;
|
|
223
|
-
_classPrivateFieldGet2(_states, this).delete(executionId);
|
|
224
|
-
try {
|
|
225
|
-
const open = await _assertClassBrand(_BrowserSessionConnector_brand, this, _recordNewTabs).call(this, executionId, state);
|
|
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);
|
|
228
|
-
} finally {
|
|
229
|
-
state.connected.cdp.disconnect();
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
/** Nothing per execution outlives the pass; the named browser persists. */
|
|
233
|
-
async disposeExecution(executionId) {
|
|
234
|
-
const state = _classPrivateFieldGet2(_states, this).get(executionId);
|
|
235
|
-
if (!state) return;
|
|
236
|
-
_classPrivateFieldGet2(_states, this).delete(executionId);
|
|
237
|
-
state.connected.cdp.disconnect();
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Take (and forget) what happened to the browser during an execution.
|
|
241
|
-
* `undefined` when the execution never touched the browser. Covers the
|
|
242
|
-
* passes this instance ran; see the note on `#reports`.
|
|
243
|
-
*/
|
|
244
|
-
takeReport(executionId) {
|
|
245
|
-
const report = _classPrivateFieldGet2(_reports, this).get(executionId);
|
|
246
|
-
_classPrivateFieldGet2(_reports, this).delete(executionId);
|
|
247
|
-
return report;
|
|
248
|
-
}
|
|
249
|
-
};
|
|
250
|
-
function _executionId(ctx) {
|
|
251
|
-
if (!ctx?.executionId) throw new Error("Browser tools must run inside a codemode execution");
|
|
252
|
-
return ctx.executionId;
|
|
253
|
-
}
|
|
254
|
-
/**
|
|
255
|
-
* Connect once per pass. Concurrent first calls (model code that uses
|
|
256
|
-
* Promise.all despite the instructions) share one connect.
|
|
257
|
-
*/
|
|
258
|
-
function _state(executionId) {
|
|
259
|
-
const existing = _classPrivateFieldGet2(_states, this).get(executionId);
|
|
260
|
-
if (existing) return Promise.resolve(existing);
|
|
261
|
-
const inFlight = _classPrivateFieldGet2(_connecting, this).get(executionId);
|
|
262
|
-
if (inFlight) return inFlight;
|
|
263
|
-
const promise = _assertClassBrand(_BrowserSessionConnector_brand, this, _connect).call(this, executionId).finally(() => {
|
|
264
|
-
_classPrivateFieldGet2(_connecting, this).delete(executionId);
|
|
265
|
-
});
|
|
266
|
-
_classPrivateFieldGet2(_connecting, this).set(executionId, promise);
|
|
267
|
-
return promise;
|
|
268
|
-
}
|
|
269
|
-
async function _connect(executionId) {
|
|
270
|
-
const connected = await _classPrivateFieldGet2(_browser, this).connect(_classPrivateFieldGet2(_scope, this) === void 0 ? void 0 : { scope: _classPrivateFieldGet2(_scope, this) });
|
|
271
|
-
let initialPages;
|
|
272
|
-
try {
|
|
273
|
-
initialPages = await _assertClassBrand(_BrowserSessionConnector_brand, this, _pages).call(this, connected.cdp);
|
|
274
|
-
} catch (error) {
|
|
275
|
-
connected.cdp.disconnect();
|
|
276
|
-
throw error;
|
|
277
|
-
}
|
|
278
|
-
const state = {
|
|
279
|
-
executionId,
|
|
280
|
-
connected,
|
|
281
|
-
attached: /* @__PURE__ */ new Map(),
|
|
282
|
-
workedIn: /* @__PURE__ */ new Set(),
|
|
283
|
-
initialPages: new Set(initialPages.map((page) => page.targetId)),
|
|
284
|
-
activeResolved: false,
|
|
285
|
-
closed: /* @__PURE__ */ new Set()
|
|
286
|
-
};
|
|
287
|
-
_classPrivateFieldGet2(_states, this).set(executionId, state);
|
|
288
|
-
_assertClassBrand(_BrowserSessionConnector_brand, this, _report).call(this, executionId, connected.restarted);
|
|
289
|
-
return state;
|
|
290
|
-
}
|
|
291
|
-
function _report(executionId, restarted) {
|
|
292
|
-
let report = _classPrivateFieldGet2(_reports, this).get(executionId);
|
|
293
|
-
if (!report) {
|
|
294
|
-
report = {
|
|
295
|
-
restarted,
|
|
296
|
-
newTabs: [],
|
|
297
|
-
tabClosed: false,
|
|
298
|
-
tabShared: false
|
|
299
|
-
};
|
|
300
|
-
_classPrivateFieldGet2(_reports, this).set(executionId, report);
|
|
301
|
-
if (_classPrivateFieldGet2(_reports, this).size > MAX_PENDING_REPORTS) {
|
|
302
|
-
const oldest = _classPrivateFieldGet2(_reports, this).keys().next().value;
|
|
303
|
-
if (oldest !== void 0) _classPrivateFieldGet2(_reports, this).delete(oldest);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
report.restarted ||= restarted;
|
|
307
|
-
return report;
|
|
308
|
-
}
|
|
309
|
-
/**
|
|
310
|
-
* Add tabs the page opened this pass to the execution's report, and drop
|
|
311
|
-
* earlier ones that have since closed: `newTabs` lists page-opened tabs
|
|
312
|
-
* still open when the execution ends, across every pass. Returns the open
|
|
313
|
-
* tabs, or `undefined` when the browser couldn't be asked.
|
|
314
|
-
*/
|
|
315
|
-
async function _recordNewTabs(executionId, state) {
|
|
316
|
-
let pages;
|
|
317
|
-
try {
|
|
318
|
-
pages = await _assertClassBrand(_BrowserSessionConnector_brand, this, _pages).call(this, state.connected.cdp);
|
|
319
|
-
} catch (error) {
|
|
320
|
-
_assertClassBrand(_BrowserSessionConnector_brand, this, _warn).call(this, "list the open tabs", error);
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
const report = _assertClassBrand(_BrowserSessionConnector_brand, this, _report).call(this, executionId, state.connected.restarted);
|
|
324
|
-
const opened = new Set(report.newTabs.map((tab) => tab.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
|
-
}
|
|
337
|
-
report.newTabs = pages.filter((page) => opened.has(page.targetId)).map(({ targetId, url, title }) => ({
|
|
338
|
-
targetId,
|
|
339
|
-
url,
|
|
340
|
-
title
|
|
341
|
-
}));
|
|
342
|
-
return new Set(pages.map((page) => page.targetId));
|
|
343
|
-
}
|
|
344
|
-
/**
|
|
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.
|
|
349
|
-
*/
|
|
350
|
-
async function _saveActiveTarget(state, open) {
|
|
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;
|
|
353
|
-
const candidate = state.activeResolved ? state.activeTargetId : stored;
|
|
354
|
-
const active = candidate && (!open || open.has(candidate)) ? candidate : void 0;
|
|
355
|
-
if (active === stored) return active;
|
|
356
|
-
try {
|
|
357
|
-
await state.connected.setActiveTarget(active);
|
|
358
|
-
} catch (error) {
|
|
359
|
-
_assertClassBrand(_BrowserSessionConnector_brand, this, _warn).call(this, "save the active tab", error);
|
|
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
|
-
}
|
|
370
|
-
}
|
|
371
|
-
function _warn(action, error) {
|
|
372
|
-
console.warn(`[agents/browser] Failed to ${action} for browser "${_classPrivateFieldGet2(_browser, this).name}"`, error);
|
|
373
|
-
}
|
|
374
|
-
async function _pages(cdp) {
|
|
375
|
-
return ((await cdp.send("Target.getTargets"))?.targetInfos ?? []).filter((info) => info.type === "page");
|
|
376
|
-
}
|
|
377
|
-
async function _resolveSessionId(state, sessionId) {
|
|
378
|
-
if (sessionId === "active") return _assertClassBrand(_BrowserSessionConnector_brand, this, _attach).call(this, state, await _assertClassBrand(_BrowserSessionConnector_brand, this, _activeTarget).call(this, state));
|
|
379
|
-
if (isAttachHandle(sessionId)) return _assertClassBrand(_BrowserSessionConnector_brand, this, _attach).call(this, state, targetOfHandle(sessionId));
|
|
380
|
-
return sessionId;
|
|
381
|
-
}
|
|
382
|
-
/**
|
|
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.
|
|
386
|
-
*/
|
|
387
|
-
async function _activeTarget(state) {
|
|
388
|
-
if (state.activeTargetId) return state.activeTargetId;
|
|
389
|
-
const { connected } = state;
|
|
390
|
-
const pages = await _assertClassBrand(_BrowserSessionConnector_brand, this, _pages).call(this, connected.cdp);
|
|
391
|
-
const stored = connected.activeTargetId;
|
|
392
|
-
let targetId = pages.find((page) => page.targetId === stored)?.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;
|
|
395
|
-
_assertClassBrand(_BrowserSessionConnector_brand, this, _setActive).call(this, state, targetId);
|
|
396
|
-
return targetId;
|
|
397
|
-
}
|
|
398
|
-
function _setActive(state, targetId) {
|
|
399
|
-
state.activeTargetId = targetId;
|
|
400
|
-
state.activeResolved = true;
|
|
401
|
-
}
|
|
402
|
-
async function _attach(state, targetId, timeoutMs) {
|
|
403
|
-
const existing = state.attached.get(targetId);
|
|
404
|
-
if (existing) return existing;
|
|
405
|
-
const live = await state.connected.cdp.attachToTarget(targetId, { timeoutMs });
|
|
406
|
-
state.attached.set(targetId, live);
|
|
407
|
-
state.workedIn.add(targetId);
|
|
408
|
-
return live;
|
|
409
|
-
}
|
|
410
|
-
/** Track tab changes the model makes through raw Target commands. */
|
|
411
|
-
function _observe(state, method, params, result) {
|
|
412
|
-
const targetId = typeof params?.targetId === "string" ? params.targetId : void 0;
|
|
413
|
-
if (method === "Target.createTarget") {
|
|
414
|
-
const created = result?.targetId;
|
|
415
|
-
if (typeof created === "string") _assertClassBrand(_BrowserSessionConnector_brand, this, _setActive).call(this, state, created);
|
|
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) {
|
|
420
|
-
state.attached.delete(targetId);
|
|
421
|
-
state.closed.add(targetId);
|
|
422
|
-
if (state.activeTargetId === targetId) state.activeTargetId = void 0;
|
|
423
|
-
} else if (method === "Target.detachFromTarget") {
|
|
424
|
-
for (const [target, live] of state.attached) if (target === targetId || live === params?.sessionId) state.attached.delete(target);
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
/**
|
|
428
|
-
* Swap a handle in `Target.detachFromTarget`'s `sessionId` param for the
|
|
429
|
-
* live CDP session id. Returns {@link DETACHED} when the handle's tab isn't
|
|
430
|
-
* attached on this socket, so there is nothing to detach.
|
|
431
|
-
*/
|
|
432
|
-
function _liveParams(state, method, params) {
|
|
433
|
-
const handle = params?.sessionId;
|
|
434
|
-
if (method !== "Target.detachFromTarget" || typeof handle !== "string") return params;
|
|
435
|
-
if (!isAttachHandle(handle)) return params;
|
|
436
|
-
const live = state.attached.get(targetOfHandle(handle));
|
|
437
|
-
return live ? {
|
|
438
|
-
...params,
|
|
439
|
-
sessionId: live
|
|
440
|
-
} : DETACHED;
|
|
441
|
-
}
|
|
442
|
-
/**
|
|
443
|
-
* Turn the common protocol mistakes into instructions: sending an
|
|
444
|
-
* event as a command, and a page-scoped command without a session.
|
|
445
|
-
*/
|
|
446
|
-
async function _teach(state, error, method, sessionId) {
|
|
447
|
-
if (!(error instanceof CdpProtocolError)) return error;
|
|
448
|
-
if (error.code === CDP_SESSION_NOT_FOUND && sessionId && sessionId !== "active" && !isAttachHandle(sessionId)) return /* @__PURE__ */ new Error(`${error.message}. CDP session ids don't carry over between runs. Use sessionId: "active" for the current tab, or the handle from cdp.attachToTarget({ targetId }) for another tab.`);
|
|
449
|
-
if (error.code !== -32601) return error;
|
|
450
|
-
if (await _assertClassBrand(_BrowserSessionConnector_brand, this, _isEvent).call(this, state, method)) return /* @__PURE__ */ new Error(`${error.message}. '${method}' is a CDP *event*, not a command — it cannot be sent. To wait for page state, poll instead (e.g. Runtime.evaluate of document.readyState until "complete").`);
|
|
451
|
-
if (!sessionId) return /* @__PURE__ */ new Error(`${error.message}. '${method}' is page-scoped: pass sessionId: "active" to run it in the current tab — cdp.send({ method: "${method}", params, sessionId: "active" }).`);
|
|
452
|
-
return error;
|
|
453
|
-
}
|
|
454
|
-
async function _isEvent(state, method) {
|
|
455
|
-
try {
|
|
456
|
-
const spec = await state.connected.spec();
|
|
457
|
-
const domain = method.split(".")[0];
|
|
458
|
-
return spec.domains.some((d) => d.name === domain && d.events.some((e) => e.event === method));
|
|
459
|
-
} catch {
|
|
460
|
-
return false;
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
function attachHandle(targetId) {
|
|
464
|
-
return `${ATTACH_HANDLE_PREFIX}${targetId}`;
|
|
465
|
-
}
|
|
466
|
-
function isAttachHandle(sessionId) {
|
|
467
|
-
return sessionId?.startsWith(ATTACH_HANDLE_PREFIX) ?? false;
|
|
468
|
-
}
|
|
469
|
-
function targetOfHandle(handle) {
|
|
470
|
-
return handle.slice(7);
|
|
471
|
-
}
|
|
472
|
-
//#endregion
|
|
473
|
-
//#region src/browser/browser-tool.ts
|
|
474
|
-
/**
|
|
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.
|
|
479
|
-
*/
|
|
480
|
-
const DEFAULT_TIMEOUT_MS = 6e4;
|
|
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;
|
|
486
|
-
/**
|
|
487
|
-
* A codemode runtime name per browser. Browser names are host-chosen and
|
|
488
|
-
* unrestricted; runtime names allow only `[a-zA-Z0-9_.-]`, so anything else
|
|
489
|
-
* (including `.`, the escape) is hex-escaped.
|
|
490
|
-
*/
|
|
491
|
-
function browserRuntimeName(name) {
|
|
492
|
-
return `browser-tool_${name.replace(/[^a-zA-Z0-9_-]/g, (char) => `.${char.codePointAt(0)?.toString(16)}.`)}`;
|
|
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
|
-
}
|
|
511
|
-
/** Add what happened to the browser to the codemode result. */
|
|
512
|
-
function withBrowserReport(output, report) {
|
|
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");
|
|
519
|
-
return {
|
|
520
|
-
...output,
|
|
521
|
-
...report.restarted ? { restarted: true } : {},
|
|
522
|
-
...notices.length > 0 ? { notice: notices.join(" ") } : {},
|
|
523
|
-
...report.newTabs.length > 0 ? { newTabs: report.newTabs } : {}
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
/** Report sentences that must survive a screenshot's text summary. */
|
|
527
|
-
function browserReportNotes(output) {
|
|
528
|
-
const notes = [];
|
|
529
|
-
if (output.notice) notes.push(output.notice);
|
|
530
|
-
if (output.newTabs?.length) notes.push(`New tabs opened by the page: ${JSON.stringify(output.newTabs)}`);
|
|
531
|
-
return notes;
|
|
532
|
-
}
|
|
533
|
-
/**
|
|
534
|
-
* Build the codemode runtime and connector for one `browserTool`, and wrap
|
|
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.
|
|
540
|
-
*/
|
|
541
|
-
function createBrowserToolCore(options, adapter) {
|
|
542
|
-
const ctx = resolveCtx(options);
|
|
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()");
|
|
544
|
-
const timeout = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
545
|
-
const executor = new DynamicWorkerExecutor({
|
|
546
|
-
loader: options.loader,
|
|
547
|
-
timeout
|
|
548
|
-
});
|
|
549
|
-
const description = [
|
|
550
|
-
"Run JavaScript that drives a persistent Chrome browser over the Chrome DevTools Protocol (CDP).",
|
|
551
|
-
"",
|
|
552
|
-
"Write an async arrow function, `async () => { ... }`, and return a small JSON value. Use the `cdp` global to reach the browser. `fetch` is blocked and there is no file system or Node.js API.",
|
|
553
|
-
"",
|
|
554
|
-
"## cdp",
|
|
555
|
-
"",
|
|
556
|
-
"- `cdp.send({ method, params?, sessionId?, timeoutMs? })` sends a CDP command and returns its result without the JSON-RPC envelope: `Page.navigate` returns `{ frameId, errorText? }`, `Runtime.evaluate` returns `{ result: { value }, exceptionDetails? }`.",
|
|
557
|
-
"- `cdp.attachToTarget({ targetId })` makes an open tab active and returns `{ sessionId }`.",
|
|
558
|
-
"- `cdp.spec()` returns the whole protocol (about 650 KB) as `{ domains: [{ name, commands, events, types }] }`. Filter it in code, for example `spec.domains.find((d) => d.name === \"Page\").commands.find((c) => c.name === \"navigate\")`.",
|
|
559
|
-
"- `cdp.getDebugLog()` returns recent protocol traffic, for when a command fails or times out.",
|
|
560
|
-
"",
|
|
561
|
-
"## Rules",
|
|
562
|
-
"",
|
|
563
|
-
[
|
|
564
|
-
BROWSER_INSTRUCTIONS,
|
|
565
|
-
`Each run times out after ${Math.round(timeout / 1e3)}s, so split long jobs (crawling a site, say) across runs.`,
|
|
566
|
-
adapter.screenshotHint
|
|
567
|
-
].join("\n").split("\n").map((rule) => `- ${rule}`).join("\n"),
|
|
568
|
-
"- A `SyntaxError` from \"Failed to start Worker\" means your code didn't parse; its line number points into wrapper code, not yours. Check regex literals and escapes."
|
|
569
|
-
].join("\n");
|
|
570
|
-
const durableCtx = ctx;
|
|
571
|
-
const scopes = /* @__PURE__ */ new Map();
|
|
572
|
-
function scoped(scope) {
|
|
573
|
-
const cached = scopes.get(scope);
|
|
574
|
-
if (cached) {
|
|
575
|
-
scopes.delete(scope);
|
|
576
|
-
scopes.set(scope, cached);
|
|
577
|
-
return cached;
|
|
578
|
-
}
|
|
579
|
-
const connector = new BrowserSessionConnector(durableCtx, {
|
|
580
|
-
browser: options.browser,
|
|
581
|
-
scope
|
|
582
|
-
});
|
|
583
|
-
const built = {
|
|
584
|
-
connector,
|
|
585
|
-
tool: createCodemodeRuntime({
|
|
586
|
-
ctx: durableCtx,
|
|
587
|
-
executor,
|
|
588
|
-
connectors: [connector],
|
|
589
|
-
name: browserRuntimeName(options.browser.name),
|
|
590
|
-
transformResult: transformBrowserResult
|
|
591
|
-
}).tool({ description })
|
|
592
|
-
};
|
|
593
|
-
scopes.set(scope, built);
|
|
594
|
-
if (scopes.size > MAX_CACHED_SCOPES) {
|
|
595
|
-
const oldest = scopes.keys().next().value;
|
|
596
|
-
if (oldest !== void 0) scopes.delete(oldest);
|
|
597
|
-
}
|
|
598
|
-
return built;
|
|
599
|
-
}
|
|
600
|
-
const defaultScope = options.scope ?? "shared";
|
|
601
|
-
const { inputSchema } = scoped(defaultScope).tool;
|
|
602
|
-
return {
|
|
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);
|
|
609
|
-
return withBrowserReport(output, connector.takeReport(output.executionId));
|
|
610
|
-
})
|
|
611
|
-
};
|
|
612
|
-
}
|
|
613
|
-
//#endregion
|
|
614
|
-
export { createBrowserToolCore as n, browserReportNotes as t };
|
|
615
|
-
|
|
616
|
-
//# sourceMappingURL=browser-tool-B1mhifin.js.map
|