agents 0.27.0 → 0.28.1
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/dist/abortable-C1SpfVoi.js +21 -0
- package/dist/abortable-C1SpfVoi.js.map +1 -0
- package/dist/{agent-routing-Bhdbvf7s.d.ts → agent-routing-BMvWTFEp.d.ts} +21 -15
- package/dist/agent-routing.d.ts +1 -1
- package/dist/agent-tool-types.d.ts +1 -1
- package/dist/{agent-tools-bJZwBMfq.d.ts → agent-tools-ohoewW-B.d.ts} +2 -2
- package/dist/agent-tools.d.ts +1 -1
- package/dist/ai-ahruGw7e.d.ts +239 -0
- package/dist/{ai-sdk-OoGkFy3s.js → ai-sdk-C2iTAwUd.js} +44 -18
- package/dist/ai-sdk-C2iTAwUd.js.map +1 -0
- package/dist/browser/ai.d.ts +8 -227
- package/dist/browser/ai.js +259 -1
- package/dist/browser/ai.js.map +1 -0
- package/dist/browser/index.d.ts +56 -46
- package/dist/browser/index.js +4 -361
- package/dist/browser/{ai-sdk.d.ts → tools/ai-sdk.d.ts} +7 -7
- package/dist/browser/{ai-sdk.js → tools/ai-sdk.js} +5 -4
- package/dist/browser/tools/ai-sdk.js.map +1 -0
- package/dist/browser/tools/pi.d.ts +111 -0
- package/dist/browser/tools/pi.js +119 -0
- package/dist/browser/tools/pi.js.map +1 -0
- package/dist/browser/{tanstack-ai.d.ts → tools/tanstack-ai.d.ts} +39 -37
- package/dist/browser/{tanstack-ai.js → tools/tanstack-ai.js} +73 -59
- package/dist/browser/tools/tanstack-ai.js.map +1 -0
- package/dist/browser-CEdNlsaD.js +481 -0
- package/dist/browser-CEdNlsaD.js.map +1 -0
- package/dist/{browser-tmkcAAI3.d.ts → browser-D30VlpcA.d.ts} +63 -37
- package/dist/{browser-tool-CBqs6hqa.js → browser-tool-4iomavs_.js} +145 -49
- package/dist/browser-tool-4iomavs_.js.map +1 -0
- package/dist/{browser-tool-Bn-nzJQI.d.ts → browser-tool-vtrPOV8e.d.ts} +24 -5
- package/dist/{capability-B4WbF81e.js → capability-DBAKc6UZ.js} +1 -3
- package/dist/capability-DBAKc6UZ.js.map +1 -0
- package/dist/{capability-runner-O5VbQzqg.d.ts → capability-runner-BiVUtjqC.d.ts} +1 -7
- package/dist/chat/index.d.ts +4 -4
- package/dist/chat/index.js +1 -1
- package/dist/chat-sdk/index.d.ts +1 -1
- package/dist/chat-sdk/index.js +1 -1
- package/dist/{client-1tEQ1Oqz.js → client-BnYbNT3z.js} +3 -4
- package/dist/client-BnYbNT3z.js.map +1 -0
- package/dist/{client-DS3Dz9l0.d.ts → client-Bp-JwL3p.d.ts} +2 -2
- package/dist/client.d.ts +1 -1
- package/dist/{connector-58XDGvc3.js → connector-CtZylXdk.js} +3 -603
- package/dist/connector-CtZylXdk.js.map +1 -0
- package/dist/{connector-Cl-VqPmA.d.ts → connector-DEXzVoaY.d.ts} +9 -374
- package/dist/{conversations-DH0ut_8n.d.ts → conversations-FjHDRZyD.d.ts} +4 -4
- package/dist/{current-agent-C07C5o5Y.d.ts → current-agent-CKgT5d1O.d.ts} +3 -5
- package/dist/current-agent-DhoDkSnH.js.map +1 -1
- package/dist/{durable-object-lifecycle-DRqp475_.d.ts → durable-object-lifecycle-DOI_bzCH.d.ts} +2 -2
- package/dist/experimental/channels/index.d.ts +2 -2
- package/dist/experimental/channels/index.js +2 -2
- package/dist/experimental/channels/web/index.d.ts +2 -2
- package/dist/experimental/channels/web/index.js +1 -1
- package/dist/{gateway-BDC07gYO.d.ts → gateway-BFO6vRDn.d.ts} +2 -2
- package/dist/{handler-stateless-VvrWSAVA.js → handler-stateless-Bly_nSH3.js} +12 -3
- package/dist/handler-stateless-Bly_nSH3.js.map +1 -0
- package/dist/harness/ai-sdk/index.d.ts +2 -2
- package/dist/harness/ai-sdk/index.js +1 -1
- package/dist/harness/container/index.d.ts +2 -2
- package/dist/harness/container/index.js +3 -3
- package/dist/harness/opencode/index.d.ts +183 -7
- package/dist/harness/opencode/index.js +355 -24
- package/dist/harness/opencode/index.js.map +1 -1
- package/dist/harness/pi/index.d.ts +2 -2
- package/dist/harness/pi/index.js +3 -3
- package/dist/harness/store/index.d.ts +1 -1
- package/dist/harness/store/index.js +1 -1
- package/dist/harness/think/index.d.ts +2 -2
- package/dist/harness/think/index.js +363 -171
- package/dist/harness/think/index.js.map +1 -1
- package/dist/{images-Ce9SCu3X.js → images-C4hiIexM.js} +46 -4
- package/dist/images-C4hiIexM.js.map +1 -0
- package/dist/{index-D0A5tbWu.d.ts → index-BEL3ZV-s.d.ts} +2 -6
- package/dist/{index-Ib_4dwOo.d.ts → index-CBt8oRJt.d.ts} +3 -5
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/input-schema-BQykTEp_.js +20 -0
- package/dist/input-schema-BQykTEp_.js.map +1 -0
- package/dist/{input-schema-DNrpTu49.js → input-schema-xBofjBKC.js} +3 -3
- package/dist/input-schema-xBofjBKC.js.map +1 -0
- package/dist/internal_context.d.ts +1 -1
- package/dist/lifecycle/index.d.ts +3 -3
- package/dist/lifecycle/index.js +2 -2
- package/dist/{lifecycle-CFu6OV03.js → lifecycle-DGorpAmA.js} +2 -4
- package/dist/lifecycle-DGorpAmA.js.map +1 -0
- package/dist/mcp/client/index.d.ts +1 -1
- package/dist/mcp/client/index.js +1 -1
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +18 -4
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/server/index.js +1 -1
- package/dist/models/ai-sdk/index.d.ts +45 -17
- 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 +8 -3
- package/dist/models/pi-ai/index.js +6 -6
- package/dist/models/pi-ai/index.js.map +1 -1
- package/dist/{options-pwTNq9z6.d.ts → options-CiIsJHXR.d.ts} +2 -6
- package/dist/queue/index.d.ts +1 -1
- package/dist/queue/index.js +1 -1
- package/dist/{queue-BB-zfIYF.js → queue-CcIjVYTE.js} +2 -2
- package/dist/{queue-BB-zfIYF.js.map → queue-CcIjVYTE.js.map} +1 -1
- package/dist/react.d.ts +2 -2
- package/dist/routing/index.d.ts +2 -2
- package/dist/routing/index.js +1 -1
- package/dist/{scheduler-CrFCnv_J.d.ts → scheduler-BCLESHSC.d.ts} +2 -12
- package/dist/{scheduler-DZ3xQfeK.js → scheduler-FIHv2-kJ.js} +2 -4
- package/dist/scheduler-FIHv2-kJ.js.map +1 -0
- package/dist/schedules/index.d.ts +1 -1
- package/dist/schedules/index.js +1 -1
- package/dist/serializable.d.ts +1 -1
- package/dist/session-store-Do4xkDcf.js +606 -0
- package/dist/session-store-Do4xkDcf.js.map +1 -0
- package/dist/session-store-DoPY12xM.d.ts +383 -0
- package/dist/sessions/index.d.ts +1 -1
- package/dist/sessions/index.js +1 -1
- package/dist/{sessions-B9pNuxdT.js → sessions-_vN5BEZX.js} +2 -2
- package/dist/{sessions-B9pNuxdT.js.map → sessions-_vN5BEZX.js.map} +1 -1
- package/dist/{settings-C45hDliX.js → settings-BjAiPd6Z.js} +7 -4
- package/dist/settings-BjAiPd6Z.js.map +1 -0
- package/dist/{settings-NgGwF7Qt.d.ts → settings-DnD__xuE.d.ts} +22 -4
- package/dist/{src-DgCbZtIR.js → src-CQxJFv4l.js} +8 -8
- package/dist/src-CQxJFv4l.js.map +1 -0
- package/dist/state/index.d.ts +1 -1
- package/dist/state/index.js +1 -3
- package/dist/state/index.js.map +1 -1
- package/dist/{store-DLeqKuj3.d.ts → store-BKM4Xm9M.d.ts} +23 -1
- package/dist/{store-vgcPYo8K.js → store-CslVzfN7.js} +28 -2
- package/dist/store-CslVzfN7.js.map +1 -0
- package/dist/streams/index.d.ts +1 -1
- package/dist/streams/index.js +1 -1
- package/dist/{streams-CITClVer.js → streams-lsHMJhRW.js} +2 -2
- package/dist/{streams-CITClVer.js.map → streams-lsHMJhRW.js.map} +1 -1
- package/dist/{streams-fbG61qM8.d.ts → streams-vrCtbUIn.d.ts} +2 -2
- package/dist/sub-routing.d.ts +1 -1
- package/dist/tasks/index.d.ts +1 -1
- package/dist/tasks/index.js +1 -1
- package/dist/{tasks-CkTZNaV1.js → tasks-BN_5AWqI.js} +2 -2
- package/dist/{tasks-CkTZNaV1.js.map → tasks-BN_5AWqI.js.map} +1 -1
- package/dist/{tasks-PtlB4Uyn.d.ts → tasks-E89KdwkS.d.ts} +2 -2
- package/dist/{tool-BPJEbwaX.d.ts → tool-CTp72Jb_.d.ts} +4 -4
- package/dist/tool-CxKsvxjm.js +1126 -0
- package/dist/tool-CxKsvxjm.js.map +1 -0
- package/dist/tool-DoVberpN.d.ts +301 -0
- package/dist/{tool-3PA8xjB_.js → tool-VF0qiO7q.js} +7 -18
- package/dist/tool-VF0qiO7q.js.map +1 -0
- package/dist/tool-helpers-O5BDVEAY.js +58 -0
- package/dist/tool-helpers-O5BDVEAY.js.map +1 -0
- package/dist/{transport-k2mFrtL8.d.ts → transport-DCyarnwm.d.ts} +2 -2
- package/dist/voice/index.d.ts +2 -2
- package/dist/web/fetch/index.d.ts +89 -0
- package/dist/web/fetch/index.js +20 -0
- package/dist/web/fetch/index.js.map +1 -0
- package/dist/web/fetch/tools/ai-sdk.d.ts +80 -0
- package/dist/web/fetch/tools/ai-sdk.js +47 -0
- package/dist/web/fetch/tools/ai-sdk.js.map +1 -0
- package/dist/web/fetch/tools/pi.d.ts +84 -0
- package/dist/web/fetch/tools/pi.js +88 -0
- package/dist/web/fetch/tools/pi.js.map +1 -0
- package/dist/web/fetch/tools/tanstack-ai.d.ts +78 -0
- package/dist/web/fetch/tools/tanstack-ai.js +45 -0
- package/dist/web/fetch/tools/tanstack-ai.js.map +1 -0
- package/dist/{websearch → web/search}/index.d.ts +1 -1
- package/dist/{websearch → web/search}/index.js +1 -1
- package/dist/{websearch → web/search}/tools/ai-sdk.d.ts +2 -2
- package/dist/{websearch → web/search}/tools/ai-sdk.js +3 -3
- package/dist/web/search/tools/ai-sdk.js.map +1 -0
- package/dist/{websearch → web/search}/tools/pi.d.ts +3 -3
- package/dist/{websearch → web/search}/tools/pi.js +2 -2
- package/dist/web/search/tools/pi.js.map +1 -0
- package/dist/{websearch → web/search}/tools/tanstack-ai.d.ts +2 -2
- package/dist/{websearch → web/search}/tools/tanstack-ai.js +3 -3
- package/dist/web/search/tools/tanstack-ai.js.map +1 -0
- package/dist/websockets/index.d.ts +2 -2
- package/dist/websockets/index.js +1 -1
- package/dist/{websockets-CmENGb-Z.js → websockets-C3zy8YR0.js} +3 -5
- package/dist/websockets-C3zy8YR0.js.map +1 -0
- package/dist/workflow-types.d.ts +1 -1
- package/dist/workflows.d.ts +1 -1
- package/dist/workflows.js +1 -1
- package/docs/browse-the-web.md +26 -1
- package/docs/fetch-the-web.md +276 -0
- package/docs/harnesses/pi.md +25 -0
- package/docs/index.md +99 -76
- package/docs/lifecycle.md +2 -3
- package/docs/mcp-client.md +1 -1
- package/docs/mcp-servers.md +1 -1
- package/docs/models-pi-ai.md +11 -0
- package/docs/models.md +45 -3
- package/docs/queue.md +2 -3
- package/docs/scheduling.md +2 -6
- package/docs/search-the-web.md +2 -0
- package/package.json +52 -27
- package/dist/ai-fm7xx1BK.js +0 -312
- package/dist/ai-fm7xx1BK.js.map +0 -1
- package/dist/ai-sdk-OoGkFy3s.js.map +0 -1
- package/dist/browser/ai-sdk.js.map +0 -1
- package/dist/browser/index.js.map +0 -1
- package/dist/browser/tanstack-ai.js.map +0 -1
- package/dist/browser-tool-CBqs6hqa.js.map +0 -1
- package/dist/capability-B4WbF81e.js.map +0 -1
- package/dist/client-1tEQ1Oqz.js.map +0 -1
- package/dist/connector-58XDGvc3.js.map +0 -1
- package/dist/handler-stateless-VvrWSAVA.js.map +0 -1
- package/dist/images-Ce9SCu3X.js.map +0 -1
- package/dist/input-schema-DNrpTu49.js.map +0 -1
- package/dist/lifecycle-CFu6OV03.js.map +0 -1
- package/dist/scheduler-DZ3xQfeK.js.map +0 -1
- package/dist/settings-C45hDliX.js.map +0 -1
- package/dist/src-DgCbZtIR.js.map +0 -1
- package/dist/store-vgcPYo8K.js.map +0 -1
- package/dist/tool-3PA8xjB_.js.map +0 -1
- package/dist/websearch/tools/ai-sdk.js.map +0 -1
- package/dist/websearch/tools/pi.js.map +0 -1
- package/dist/websearch/tools/tanstack-ai.js.map +0 -1
- package/dist/websockets-CmENGb-Z.js.map +0 -1
|
@@ -0,0 +1,481 @@
|
|
|
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 { t as LifecycleCapability } from "./capability-DBAKc6UZ.js";
|
|
4
|
+
import { d as createBrowserSession, f as deleteBrowserSession, h as listBrowserTargets, m as isMissingBrowserSession, n as DurableBrowserSessionStore, o as createLiveView, r as loadCdpSpec, u as connectBrowserSession } from "./session-store-Do4xkDcf.js";
|
|
5
|
+
/**
|
|
6
|
+
* The scope used when a caller doesn't pick one: every caller that leaves
|
|
7
|
+
* `scope` out shares this one active tab. See {@link BrowserConnectOptions}.
|
|
8
|
+
*/
|
|
9
|
+
const DEFAULT_BROWSER_SCOPE = "shared";
|
|
10
|
+
/**
|
|
11
|
+
* Scope records kept per browser. Writing a new one past the cap deletes the
|
|
12
|
+
* least recently used; a scope that comes back afterwards starts fresh.
|
|
13
|
+
*/
|
|
14
|
+
const MAX_BROWSER_SCOPES = 100;
|
|
15
|
+
const NAMED_SESSION_KEY_PREFIX = "browser:session:";
|
|
16
|
+
/** The store key holding the named browser's current record. */
|
|
17
|
+
function namedBrowserSessionKey(name) {
|
|
18
|
+
return `${NAMED_SESSION_KEY_PREFIX}${name}`;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Where a closed or lost browser's record moves: permanent evidence that the
|
|
22
|
+
* name once owned a browser, so a later resolve reports `restarted: true`.
|
|
23
|
+
* Kept outside {@link NAMED_SESSION_KEY_PREFIX} so listing that prefix
|
|
24
|
+
* yields only names that currently own a browser. Grows with the number of
|
|
25
|
+
* distinct names the host has ever used.
|
|
26
|
+
*/
|
|
27
|
+
const RETIRED_SESSION_KEY_PREFIX = "browser:retired:";
|
|
28
|
+
function retiredBrowserSessionKey(name) {
|
|
29
|
+
return `${RETIRED_SESSION_KEY_PREFIX}${name}`;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Where a browser's scope records live. The name's length is part of the
|
|
33
|
+
* prefix, so one browser's prefix never matches another name's (`a` with
|
|
34
|
+
* scope `b:c` vs. `a:b` with scope `c`).
|
|
35
|
+
*/
|
|
36
|
+
function browserScopeKeyPrefix(name) {
|
|
37
|
+
return `browser:scope:${name.length}:${name}:`;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Run the browser on Cloudflare Browser Run.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* new Browser({ provider: browserRun(env.BROWSER, { recording: true }) });
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
function browserRun(binding, options = {}) {
|
|
48
|
+
return {
|
|
49
|
+
kind: "browser-run",
|
|
50
|
+
binding,
|
|
51
|
+
options
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
var _options = /* @__PURE__ */ new WeakMap();
|
|
55
|
+
var _key = /* @__PURE__ */ new WeakMap();
|
|
56
|
+
var _store = /* @__PURE__ */ new WeakMap();
|
|
57
|
+
var _Browser_brand = /* @__PURE__ */ new WeakSet();
|
|
58
|
+
/**
|
|
59
|
+
* One named, persistent browser.
|
|
60
|
+
*
|
|
61
|
+
* Install it with `Lifecycle.use()` — on an Agent subclass or any Durable
|
|
62
|
+
* Object that composes `Lifecycle` — and it stores its record in the
|
|
63
|
+
* object's own storage. It schedules nothing: Browser Run reclaims idle
|
|
64
|
+
* browsers itself once `keep_alive` elapses, and the next use replaces the
|
|
65
|
+
* lost browser with `restarted: true`.
|
|
66
|
+
*
|
|
67
|
+
* Everything here is host-side. Models never see browser names, Browser Run
|
|
68
|
+
* ids, or Live View URLs through this surface.
|
|
69
|
+
*
|
|
70
|
+
* Store discipline: locks wrap storage operations only — liveness probes and
|
|
71
|
+
* Browser Run create/delete calls always happen outside any lock, with a
|
|
72
|
+
* commit re-check to detect concurrent swaps.
|
|
73
|
+
*
|
|
74
|
+
* @experimental The API surface may change before stabilizing.
|
|
75
|
+
*/
|
|
76
|
+
var Browser = class extends LifecycleCapability {
|
|
77
|
+
constructor(options) {
|
|
78
|
+
const name = options.name ?? "default";
|
|
79
|
+
super(`browser:${name}`);
|
|
80
|
+
_classPrivateMethodInitSpec(this, _Browser_brand);
|
|
81
|
+
_classPrivateFieldInitSpec(this, _options, void 0);
|
|
82
|
+
_classPrivateFieldInitSpec(this, _key, void 0);
|
|
83
|
+
_classPrivateFieldInitSpec(this, _store, void 0);
|
|
84
|
+
if (name.trim() === "") throw new Error("Browser names must be non-empty");
|
|
85
|
+
this.name = name;
|
|
86
|
+
_classPrivateFieldSet2(_options, this, options);
|
|
87
|
+
_classPrivateFieldSet2(_key, this, namedBrowserSessionKey(name));
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Reattach to the browser when it is still alive, otherwise create a fresh
|
|
91
|
+
* one. See {@link ResolvedBrowser.restarted} for the mortality signal.
|
|
92
|
+
*/
|
|
93
|
+
async resolve() {
|
|
94
|
+
const { name } = this;
|
|
95
|
+
const key = _classPrivateFieldGet2(_key, this);
|
|
96
|
+
for (let attempt = 0; attempt < 4; attempt++) {
|
|
97
|
+
const existing = await _assertClassBrand(_Browser_brand, this, _readStored).call(this);
|
|
98
|
+
if (existing === void 0) {
|
|
99
|
+
const { session, restarted } = await _assertClassBrand(_Browser_brand, this, _createAndCommit).call(this);
|
|
100
|
+
return {
|
|
101
|
+
name,
|
|
102
|
+
restarted,
|
|
103
|
+
...resolvedFields(session)
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const alive = await _assertClassBrand(_Browser_brand, this, _isAlive).call(this, existing);
|
|
107
|
+
const lock = await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).acquireLock(key);
|
|
108
|
+
try {
|
|
109
|
+
const current = await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).get(key);
|
|
110
|
+
if (current?.sessionId !== existing.sessionId) continue;
|
|
111
|
+
if (alive) {
|
|
112
|
+
const refreshed = {
|
|
113
|
+
...current,
|
|
114
|
+
updatedAt: Date.now()
|
|
115
|
+
};
|
|
116
|
+
await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).set(key, refreshed);
|
|
117
|
+
return {
|
|
118
|
+
name,
|
|
119
|
+
restarted: false,
|
|
120
|
+
...resolvedFields(refreshed)
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
await _assertClassBrand(_Browser_brand, this, _retire).call(this, current);
|
|
124
|
+
} finally {
|
|
125
|
+
await lock.release();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
throw new Error(`Browser "${name}" kept changing concurrently — retry`);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Resolve the browser and open a CDP connection to it for one scope (see
|
|
132
|
+
* {@link BrowserConnectOptions.scope}). Commands sent over the connection
|
|
133
|
+
* refresh the record's `updatedAt` (throttled to
|
|
134
|
+
* {@link SESSION_TOUCH_INTERVAL_MS}), so hosts can tell an actively used
|
|
135
|
+
* browser from one the platform has likely reclaimed.
|
|
136
|
+
*
|
|
137
|
+
* If the browser expires between the liveness probe and the WebSocket
|
|
138
|
+
* upgrade, the record is retired and the browser resolved once more, so
|
|
139
|
+
* the caller gets a fresh browser instead of an error.
|
|
140
|
+
*/
|
|
141
|
+
async connect(options = {}) {
|
|
142
|
+
const scope = options.scope ?? "shared";
|
|
143
|
+
if (scope.trim() === "") throw new Error("Browser scopes must be non-empty");
|
|
144
|
+
let resolved = await this.resolve();
|
|
145
|
+
let cdp;
|
|
146
|
+
try {
|
|
147
|
+
cdp = await _assertClassBrand(_Browser_brand, this, _open).call(this, resolved);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
if (!isMissingBrowserSession(error)) throw error;
|
|
150
|
+
await _assertClassBrand(_Browser_brand, this, _retireIfCurrent).call(this, resolved.sessionId);
|
|
151
|
+
resolved = await this.resolve();
|
|
152
|
+
cdp = await _assertClassBrand(_Browser_brand, this, _open).call(this, resolved);
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
const entered = await _assertClassBrand(_Browser_brand, this, _enterScope).call(this, scope, resolved.sessionId);
|
|
156
|
+
return _assertClassBrand(_Browser_brand, this, _connection).call(this, resolved, scope, cdp, entered);
|
|
157
|
+
} catch (error) {
|
|
158
|
+
cdp.disconnect();
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Close the browser: retire the record (so the next resolve reports
|
|
164
|
+
* `restarted: true`) and delete its Browser Run session. Returns `false`
|
|
165
|
+
* when there was nothing to close.
|
|
166
|
+
*
|
|
167
|
+
* The platform delete is best-effort: the retired record is the durable
|
|
168
|
+
* outcome, and a browser whose delete failed is unreachable through this
|
|
169
|
+
* store, so the pinned `keep_alive` (≤600s) reclaims it.
|
|
170
|
+
*/
|
|
171
|
+
async close() {
|
|
172
|
+
let stored;
|
|
173
|
+
const lock = await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).acquireLock(_classPrivateFieldGet2(_key, this));
|
|
174
|
+
try {
|
|
175
|
+
const current = await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).get(_classPrivateFieldGet2(_key, this));
|
|
176
|
+
if (!current) return false;
|
|
177
|
+
stored = current;
|
|
178
|
+
await _assertClassBrand(_Browser_brand, this, _retire).call(this, current);
|
|
179
|
+
} finally {
|
|
180
|
+
await lock.release();
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
await deleteBrowserSession(_get_provider.call(_assertClassBrand(_Browser_brand, this)).binding, stored.sessionId);
|
|
184
|
+
} catch (error) {
|
|
185
|
+
console.warn(`[agents/browser] Failed to delete closed Browser Run session ${stored.sessionId}`, error);
|
|
186
|
+
}
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Mint Live View URLs for the browser's open tabs, fresh from a target
|
|
191
|
+
* listing. `undefined` when the browser was never created, is closed, or
|
|
192
|
+
* is gone. The URLs are bearer credentials with a short connect window
|
|
193
|
+
* (~5 min) — hand them to a trusted human, never to the model, and never
|
|
194
|
+
* store them: re-mint instead.
|
|
195
|
+
*/
|
|
196
|
+
async liveView(options) {
|
|
197
|
+
const entry = await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).get(_classPrivateFieldGet2(_key, this));
|
|
198
|
+
if (!entry) return void 0;
|
|
199
|
+
try {
|
|
200
|
+
const targets = await listBrowserTargets(_get_provider.call(_assertClassBrand(_Browser_brand, this)).binding, entry.sessionId);
|
|
201
|
+
try {
|
|
202
|
+
if (!await _assertClassBrand(_Browser_brand, this, _touch).call(this, entry.sessionId)) return void 0;
|
|
203
|
+
} catch (error) {
|
|
204
|
+
console.warn(`[agents/browser] Failed to refresh activity for browser "${this.name}"`, error);
|
|
205
|
+
}
|
|
206
|
+
return createLiveView(entry.sessionId, targets, options?.mode);
|
|
207
|
+
} catch (error) {
|
|
208
|
+
if (isMissingBrowserSession(error)) return void 0;
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
function _connection(resolved, scope, cdp, entered) {
|
|
214
|
+
const { sessionId } = resolved;
|
|
215
|
+
const { binding } = _get_provider.call(_assertClassBrand(_Browser_brand, this));
|
|
216
|
+
return {
|
|
217
|
+
name: resolved.name,
|
|
218
|
+
sessionId,
|
|
219
|
+
scope,
|
|
220
|
+
restarted: entered.restarted,
|
|
221
|
+
activeTargetId: entered.activeTargetId,
|
|
222
|
+
cdp,
|
|
223
|
+
setActiveTarget: (targetId) => _assertClassBrand(_Browser_brand, this, _updateScope).call(this, scope, sessionId, targetId),
|
|
224
|
+
targetsInOtherScopes: async () => activeTargets(await _assertClassBrand(_Browser_brand, this, _listScopes).call(this), scope, sessionId),
|
|
225
|
+
spec: () => loadCdpSpec({
|
|
226
|
+
browser: binding,
|
|
227
|
+
sessionId
|
|
228
|
+
})
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
async function _open(resolved) {
|
|
232
|
+
let lastTouchAt = Date.now();
|
|
233
|
+
let touchInFlight = false;
|
|
234
|
+
return connectBrowserSession(_get_provider.call(_assertClassBrand(_Browser_brand, this)).binding, resolved.sessionId, {
|
|
235
|
+
timeoutMs: _classPrivateFieldGet2(_options, this).timeoutMs,
|
|
236
|
+
onActivity: () => {
|
|
237
|
+
const now = Date.now();
|
|
238
|
+
if (touchInFlight || now - lastTouchAt < _get_touchIntervalMs.call(_assertClassBrand(_Browser_brand, this))) return;
|
|
239
|
+
touchInFlight = true;
|
|
240
|
+
lastTouchAt = now;
|
|
241
|
+
_assertClassBrand(_Browser_brand, this, _touch).call(this, resolved.sessionId).catch((error) => {
|
|
242
|
+
console.warn(`[agents/browser] Failed to refresh activity for browser "${this.name}"`, error);
|
|
243
|
+
}).finally(() => {
|
|
244
|
+
touchInFlight = false;
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
function _get_provider() {
|
|
250
|
+
return _classPrivateFieldGet2(_options, this).provider;
|
|
251
|
+
}
|
|
252
|
+
/** The platform `keep_alive` every create applies. */
|
|
253
|
+
function _get_keepAliveMs() {
|
|
254
|
+
return _get_provider.call(_assertClassBrand(_Browser_brand, this)).options.keepAliveMs ?? 6e5;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Capped at half the keep-alive window: a browser with continuous CDP
|
|
258
|
+
* traffic must never look older than the window the platform uses to
|
|
259
|
+
* reclaim it.
|
|
260
|
+
*/
|
|
261
|
+
function _get_touchIntervalMs() {
|
|
262
|
+
return Math.min(_classPrivateFieldGet2(_options, this).touchIntervalMs ?? 6e4, Math.floor(_get_keepAliveMs.call(_assertClassBrand(_Browser_brand, this)) / 2));
|
|
263
|
+
}
|
|
264
|
+
/** Lazy: `lifecycle.storage` exists only once `Lifecycle.use()` ran. */
|
|
265
|
+
function _get_sessionStore() {
|
|
266
|
+
_classPrivateFieldGet2(_store, this) ?? _classPrivateFieldSet2(_store, this, _classPrivateFieldGet2(_options, this).store ?? new DurableBrowserSessionStore(this.lifecycle.storage));
|
|
267
|
+
return _classPrivateFieldGet2(_store, this);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Refresh `updatedAt` while the record still holds `sessionId`. Returns
|
|
271
|
+
* false when a concurrent close or replacement already retired it.
|
|
272
|
+
*/
|
|
273
|
+
function _touch(sessionId) {
|
|
274
|
+
return _assertClassBrand(_Browser_brand, this, _update).call(this, sessionId, (current) => ({
|
|
275
|
+
...current,
|
|
276
|
+
updatedAt: Date.now()
|
|
277
|
+
}));
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Rewrite the record only while it still holds `sessionId`: a replaced or
|
|
281
|
+
* retired record is never resurrected.
|
|
282
|
+
*/
|
|
283
|
+
async function _update(sessionId, change) {
|
|
284
|
+
const lock = await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).acquireLock(_classPrivateFieldGet2(_key, this));
|
|
285
|
+
try {
|
|
286
|
+
const current = await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).get(_classPrivateFieldGet2(_key, this));
|
|
287
|
+
if (current?.sessionId !== sessionId) return false;
|
|
288
|
+
await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).set(_classPrivateFieldGet2(_key, this), change(current));
|
|
289
|
+
return true;
|
|
290
|
+
} finally {
|
|
291
|
+
await lock.release();
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function _scopeKey(scope) {
|
|
295
|
+
return `${browserScopeKeyPrefix(this.name)}${scope}`;
|
|
296
|
+
}
|
|
297
|
+
/** The lock every scope record of this browser is written under. */
|
|
298
|
+
function _get_scopesLockKey() {
|
|
299
|
+
return browserScopeKeyPrefix(this.name);
|
|
300
|
+
}
|
|
301
|
+
/** Every scope record of this browser, by scope. */
|
|
302
|
+
async function _listScopes() {
|
|
303
|
+
const prefix = browserScopeKeyPrefix(this.name);
|
|
304
|
+
const entries = await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).list(prefix);
|
|
305
|
+
const scopes = /* @__PURE__ */ new Map();
|
|
306
|
+
for (const [key, entry] of entries) scopes.set(key.slice(prefix.length), entry);
|
|
307
|
+
return scopes;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Start a connection's scope: a scope whose record names an earlier
|
|
311
|
+
* browser was restarted, and its record moves to this one. A new record
|
|
312
|
+
* past {@link MAX_BROWSER_SCOPES} evicts the least recently used.
|
|
313
|
+
*/
|
|
314
|
+
async function _enterScope(scope, sessionId) {
|
|
315
|
+
const key = _assertClassBrand(_Browser_brand, this, _scopeKey).call(this, scope);
|
|
316
|
+
const lock = await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).acquireLock(_get_scopesLockKey.call(_assertClassBrand(_Browser_brand, this)));
|
|
317
|
+
try {
|
|
318
|
+
const entry = await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).get(key);
|
|
319
|
+
const now = Date.now();
|
|
320
|
+
if (entry?.sessionId === sessionId) {
|
|
321
|
+
await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).set(key, {
|
|
322
|
+
...entry,
|
|
323
|
+
updatedAt: now
|
|
324
|
+
});
|
|
325
|
+
return {
|
|
326
|
+
restarted: false,
|
|
327
|
+
activeTargetId: entry.activeTargetId
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).set(key, {
|
|
331
|
+
sessionId,
|
|
332
|
+
createdAt: now,
|
|
333
|
+
updatedAt: now
|
|
334
|
+
});
|
|
335
|
+
if (!entry) await _assertClassBrand(_Browser_brand, this, _evictScopes).call(this);
|
|
336
|
+
return { restarted: entry !== void 0 };
|
|
337
|
+
} finally {
|
|
338
|
+
await lock.release();
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
/** Delete the least recently used scope records past the cap. */
|
|
342
|
+
async function _evictScopes() {
|
|
343
|
+
const scopes = await _assertClassBrand(_Browser_brand, this, _listScopes).call(this);
|
|
344
|
+
if (scopes.size <= 100) return;
|
|
345
|
+
const oldest = [...scopes].sort(([, a], [, b]) => a.updatedAt - b.updatedAt).slice(0, scopes.size - 100);
|
|
346
|
+
for (const [scope] of oldest) await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).delete(_assertClassBrand(_Browser_brand, this, _scopeKey).call(this, scope));
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Set a scope's tab (or clear it with `undefined`). Never resurrects:
|
|
350
|
+
* does nothing (and returns `false`) once the browser this connection
|
|
351
|
+
* resolved was closed or replaced.
|
|
352
|
+
*/
|
|
353
|
+
async function _updateScope(scope, sessionId, targetId) {
|
|
354
|
+
const key = _assertClassBrand(_Browser_brand, this, _scopeKey).call(this, scope);
|
|
355
|
+
const lock = await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).acquireLock(_get_scopesLockKey.call(_assertClassBrand(_Browser_brand, this)));
|
|
356
|
+
try {
|
|
357
|
+
const current = await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).get(_classPrivateFieldGet2(_key, this));
|
|
358
|
+
const entry = await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).get(key);
|
|
359
|
+
if (current?.sessionId !== sessionId || entry?.sessionId !== sessionId) return false;
|
|
360
|
+
await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).set(key, {
|
|
361
|
+
...entry,
|
|
362
|
+
activeTargetId: targetId,
|
|
363
|
+
updatedAt: Date.now()
|
|
364
|
+
});
|
|
365
|
+
return true;
|
|
366
|
+
} finally {
|
|
367
|
+
await lock.release();
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
/** Retire the record if it still holds `sessionId`. */
|
|
371
|
+
async function _retireIfCurrent(sessionId) {
|
|
372
|
+
const lock = await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).acquireLock(_classPrivateFieldGet2(_key, this));
|
|
373
|
+
try {
|
|
374
|
+
const current = await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).get(_classPrivateFieldGet2(_key, this));
|
|
375
|
+
if (current?.sessionId === sessionId) await _assertClassBrand(_Browser_brand, this, _retire).call(this, current);
|
|
376
|
+
} finally {
|
|
377
|
+
await lock.release();
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Move the record to its retired marker. Callers hold the key lock, so a
|
|
382
|
+
* resolver always sees either the record or the marker.
|
|
383
|
+
*/
|
|
384
|
+
async function _retire(current) {
|
|
385
|
+
await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).set(retiredBrowserSessionKey(this.name), {
|
|
386
|
+
...current,
|
|
387
|
+
closedAt: Date.now()
|
|
388
|
+
});
|
|
389
|
+
await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).delete(_classPrivateFieldGet2(_key, this));
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Create a Browser Run session (outside any lock) and commit it, applying
|
|
393
|
+
* the provider's creation options. If a concurrent caller committed an
|
|
394
|
+
* entry first, theirs wins and the redundant session is deleted
|
|
395
|
+
* best-effort.
|
|
396
|
+
*
|
|
397
|
+
* `restarted` is read under the commit lock: a retired marker — left by
|
|
398
|
+
* close() or a dead-browser recovery — is evidence a prior browser
|
|
399
|
+
* existed, and only first-ever use of the name is not a restart. Markers
|
|
400
|
+
* are permanent, so reading at commit time also catches a browser that
|
|
401
|
+
* was created and closed while this create was in flight.
|
|
402
|
+
*/
|
|
403
|
+
async function _createAndCommit() {
|
|
404
|
+
const { binding, options } = _get_provider.call(_assertClassBrand(_Browser_brand, this));
|
|
405
|
+
const info = await createBrowserSession(binding, {
|
|
406
|
+
keepAliveMs: _get_keepAliveMs.call(_assertClassBrand(_Browser_brand, this)),
|
|
407
|
+
recording: options.recording,
|
|
408
|
+
guardrails: options.guardrails
|
|
409
|
+
});
|
|
410
|
+
const now = Date.now();
|
|
411
|
+
const stored = {
|
|
412
|
+
sessionId: info.sessionId,
|
|
413
|
+
createdAt: now,
|
|
414
|
+
updatedAt: now
|
|
415
|
+
};
|
|
416
|
+
let winner;
|
|
417
|
+
let restarted;
|
|
418
|
+
const lock = await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).acquireLock(_classPrivateFieldGet2(_key, this));
|
|
419
|
+
try {
|
|
420
|
+
restarted = await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).get(retiredBrowserSessionKey(this.name)) !== void 0;
|
|
421
|
+
const current = await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).get(_classPrivateFieldGet2(_key, this));
|
|
422
|
+
if (current === void 0) await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).set(_classPrivateFieldGet2(_key, this), stored);
|
|
423
|
+
else winner = current;
|
|
424
|
+
} finally {
|
|
425
|
+
await lock.release();
|
|
426
|
+
}
|
|
427
|
+
if (winner) {
|
|
428
|
+
try {
|
|
429
|
+
await deleteBrowserSession(binding, stored.sessionId);
|
|
430
|
+
} catch (error) {
|
|
431
|
+
console.warn(`[agents/browser] Failed to delete redundant Browser Run session ${stored.sessionId}`, error);
|
|
432
|
+
}
|
|
433
|
+
return {
|
|
434
|
+
session: winner,
|
|
435
|
+
restarted
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
return {
|
|
439
|
+
session: stored,
|
|
440
|
+
restarted
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
async function _isAlive(stored) {
|
|
444
|
+
try {
|
|
445
|
+
await listBrowserTargets(_get_provider.call(_assertClassBrand(_Browser_brand, this)).binding, stored.sessionId);
|
|
446
|
+
return true;
|
|
447
|
+
} catch (error) {
|
|
448
|
+
if (isMissingBrowserSession(error)) return false;
|
|
449
|
+
throw error;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
async function _readStored() {
|
|
453
|
+
const lock = await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).acquireLock(_classPrivateFieldGet2(_key, this));
|
|
454
|
+
try {
|
|
455
|
+
return await _get_sessionStore.call(_assertClassBrand(_Browser_brand, this)).get(_classPrivateFieldGet2(_key, this));
|
|
456
|
+
} finally {
|
|
457
|
+
await lock.release();
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
/** A store record without fields that belong to scope records. */
|
|
461
|
+
function resolvedFields(session) {
|
|
462
|
+
const { sessionId, createdAt, updatedAt } = session;
|
|
463
|
+
return {
|
|
464
|
+
sessionId,
|
|
465
|
+
createdAt,
|
|
466
|
+
updatedAt
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
/** The tabs scopes other than `scope` are working in, in `sessionId`. */
|
|
470
|
+
function activeTargets(scopes, scope, sessionId) {
|
|
471
|
+
const targets = /* @__PURE__ */ new Set();
|
|
472
|
+
for (const [other, entry] of scopes) {
|
|
473
|
+
if (other === scope || entry.sessionId !== sessionId) continue;
|
|
474
|
+
if (entry.activeTargetId) targets.add(entry.activeTargetId);
|
|
475
|
+
}
|
|
476
|
+
return targets;
|
|
477
|
+
}
|
|
478
|
+
//#endregion
|
|
479
|
+
export { browserRun as i, DEFAULT_BROWSER_SCOPE as n, MAX_BROWSER_SCOPES as r, Browser as t };
|
|
480
|
+
|
|
481
|
+
//# sourceMappingURL=browser-CEdNlsaD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-CEdNlsaD.js","names":[],"sources":["../src/browser/browser.ts"],"sourcesContent":["/**\n * `Browser`: one named, persistent browser for an agent or Durable Object.\n *\n * Its tabs and logins survive between agent runs. When the browser is lost\n * (closed, idle past `keep_alive`, or crashed), the next use starts a fresh\n * one and reports `restarted: true` so the model knows its earlier state is\n * gone.\n *\n * Browser Run is the only provider today; see {@link browserRun}.\n */\n\nimport { LifecycleCapability } from \"../lifecycle/capability\";\nimport type { CdpConnection } from \"./cdp-connection\";\nimport {\n type BrowserBinding,\n type BrowserSessionGuardrails,\n connectBrowserSession,\n createBrowserSession,\n deleteBrowserSession,\n isMissingBrowserSession,\n listBrowserTargets\n} from \"./browser-run\";\nimport {\n createLiveView,\n type BrowserLiveView,\n type LiveViewMode\n} from \"./live-view\";\nimport {\n DurableBrowserSessionStore,\n type ListableBrowserSessionStore,\n type StoredBrowserSession\n} from \"./session-store\";\nimport { loadCdpSpec, type SearchableCdpSpec } from \"./spec\";\n\n/**\n * Browser Run's server-side `keep_alive` maximum (600 seconds). A `Browser`\n * pins keep-alive here by default so it survives quiet spells between agent\n * turns; the platform reclaims it only after this long idle.\n */\nexport const BROWSER_SESSION_KEEP_ALIVE_MAX_MS = 600_000;\n\n/**\n * Minimum interval between store `updatedAt` refreshes driven by CDP traffic\n * on connections returned by {@link Browser.connect}.\n */\nexport const SESSION_TOUCH_INTERVAL_MS = 60_000;\n\n/** The browser name used when a host doesn't pick one. */\nexport const DEFAULT_BROWSER_NAME = \"default\";\n\n/**\n * The scope used when a caller doesn't pick one: every caller that leaves\n * `scope` out shares this one active tab. See {@link BrowserConnectOptions}.\n */\nexport const DEFAULT_BROWSER_SCOPE = \"shared\";\n\n/**\n * Scope records kept per browser. Writing a new one past the cap deletes the\n * least recently used; a scope that comes back afterwards starts fresh.\n */\nexport const MAX_BROWSER_SCOPES = 100;\n\nconst NAMED_SESSION_KEY_PREFIX = \"browser:session:\";\n\n/** The store key holding the named browser's current record. */\nexport function namedBrowserSessionKey(name: string): string {\n return `${NAMED_SESSION_KEY_PREFIX}${name}`;\n}\n\n/**\n * Where a closed or lost browser's record moves: permanent evidence that the\n * name once owned a browser, so a later resolve reports `restarted: true`.\n * Kept outside {@link NAMED_SESSION_KEY_PREFIX} so listing that prefix\n * yields only names that currently own a browser. Grows with the number of\n * distinct names the host has ever used.\n */\nconst RETIRED_SESSION_KEY_PREFIX = \"browser:retired:\";\n\nfunction retiredBrowserSessionKey(name: string): string {\n return `${RETIRED_SESSION_KEY_PREFIX}${name}`;\n}\n\n/**\n * Where a browser's scope records live. The name's length is part of the\n * prefix, so one browser's prefix never matches another name's (`a` with\n * scope `b:c` vs. `a:b` with scope `c`).\n */\nfunction browserScopeKeyPrefix(name: string): string {\n return `browser:scope:${name.length}:${name}:`;\n}\n\n/**\n * Browser Run settings applied every time a browser is created — including\n * when a lost browser is replaced — so options like guardrails survive a\n * restart.\n */\nexport interface BrowserRunOptions {\n /**\n * Platform `keep_alive` in milliseconds: how long an idle browser lives.\n * Defaults to {@link BROWSER_SESSION_KEEP_ALIVE_MAX_MS} (the platform\n * maximum).\n */\n keepAliveMs?: number;\n /** Opt into Browser Run session recording (rrweb capture). */\n recording?: boolean;\n /** Hostname guardrails, fixed at launch for every connection. */\n guardrails?: BrowserSessionGuardrails;\n}\n\n/** A {@link Browser} provider backed by Cloudflare Browser Run. */\nexport interface BrowserRunProvider {\n readonly kind: \"browser-run\";\n readonly binding: BrowserBinding;\n readonly options: BrowserRunOptions;\n}\n\n/** Where a {@link Browser} gets its browser from. */\nexport type BrowserProvider = BrowserRunProvider;\n\n/**\n * Run the browser on Cloudflare Browser Run.\n *\n * @example\n * ```ts\n * new Browser({ provider: browserRun(env.BROWSER, { recording: true }) });\n * ```\n */\nexport function browserRun(\n binding: BrowserBinding,\n options: BrowserRunOptions = {}\n): BrowserRunProvider {\n return { kind: \"browser-run\", binding, options };\n}\n\nexport interface BrowserOptions {\n /** Where the browser runs. Create one with {@link browserRun}. */\n provider: BrowserProvider;\n /**\n * Which browser this is. Two `Browser` objects on the same Durable Object\n * need different names. Defaults to {@link DEFAULT_BROWSER_NAME}.\n */\n name?: string;\n /**\n * Where the browser's record lives. Defaults to a\n * {@link DurableBrowserSessionStore} over the host object's storage, which\n * requires installing the `Browser` with `Lifecycle.use()`. Passing a store\n * lets the `Browser` work without Lifecycle. It must implement `list`.\n */\n store?: ListableBrowserSessionStore;\n /** Default CDP command timeout for {@link Browser.connect}. */\n timeoutMs?: number;\n /**\n * Minimum interval between activity-driven `updatedAt` refreshes.\n * Defaults to {@link SESSION_TOUCH_INTERVAL_MS}, capped at half the\n * keep-alive window.\n *\n * @internal For tests.\n */\n touchIntervalMs?: number;\n}\n\nexport interface ResolvedBrowser {\n name: string;\n /** The Browser Run session id — host-side only, never model-visible. */\n sessionId: string;\n /**\n * `true` when this resolution had to create a fresh browser to replace one\n * that previously existed (was closed, or expired or died upstream). Page\n * state from the prior browser is gone; surface this loudly to the model.\n * `false` only on first-ever use of the name — nothing was lost.\n */\n restarted: boolean;\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface BrowserConnectOptions {\n /**\n * Who is driving. Connections with the same scope share an active tab, and\n * each scope keeps its own, so two conversations on one browser don't\n * drive each other's page. Cookies, logins, and the tabs themselves are\n * shared by every scope: a scope splits \"which tab is mine\", not the\n * browser. Defaults to {@link DEFAULT_BROWSER_SCOPE}.\n */\n scope?: string;\n}\n\nexport interface BrowserConnection {\n name: string;\n sessionId: string;\n /** The scope this connection works in; see {@link BrowserConnectOptions}. */\n scope: string;\n /**\n * `true` when this scope worked in an earlier browser that has since been\n * replaced, whichever scope's connection replaced it. Its tabs and page\n * state are gone; surface this loudly to the model. `false` on a scope's\n * first connection, even one that replaced a lost browser: the scope had\n * nothing to lose. This is per scope, not a browser-wide signal.\n */\n restarted: boolean;\n /**\n * The tab this scope last worked in, when it recorded one in this\n * browser.\n */\n activeTargetId?: string;\n /** Closing this connection does NOT close the browser. */\n cdp: CdpConnection;\n /**\n * Record the tab this scope is working in (or clear it with `undefined`).\n * Never resurrects: returns `false` when the browser was closed or\n * replaced since this connection resolved it.\n */\n setActiveTarget(targetId: string | undefined): Promise<boolean>;\n /** The tabs other scopes are working in, in this browser. */\n targetsInOtherScopes(): Promise<Set<string>>;\n /**\n * The Chrome DevTools Protocol description this browser serves, read from\n * the browser itself (cached per binding).\n */\n spec(): Promise<SearchableCdpSpec>;\n}\n\n/**\n * One named, persistent browser.\n *\n * Install it with `Lifecycle.use()` — on an Agent subclass or any Durable\n * Object that composes `Lifecycle` — and it stores its record in the\n * object's own storage. It schedules nothing: Browser Run reclaims idle\n * browsers itself once `keep_alive` elapses, and the next use replaces the\n * lost browser with `restarted: true`.\n *\n * Everything here is host-side. Models never see browser names, Browser Run\n * ids, or Live View URLs through this surface.\n *\n * Store discipline: locks wrap storage operations only — liveness probes and\n * Browser Run create/delete calls always happen outside any lock, with a\n * commit re-check to detect concurrent swaps.\n *\n * @experimental The API surface may change before stabilizing.\n */\nexport class Browser extends LifecycleCapability {\n /** Which browser this is; see {@link BrowserOptions.name}. */\n readonly name: string;\n readonly #options: BrowserOptions;\n readonly #key: string;\n #store?: ListableBrowserSessionStore;\n\n constructor(options: BrowserOptions) {\n const name = options.name ?? DEFAULT_BROWSER_NAME;\n super(`browser:${name}`);\n if (name.trim() === \"\") {\n throw new Error(\"Browser names must be non-empty\");\n }\n this.name = name;\n this.#options = options;\n this.#key = namedBrowserSessionKey(name);\n }\n\n /**\n * Reattach to the browser when it is still alive, otherwise create a fresh\n * one. See {@link ResolvedBrowser.restarted} for the mortality signal.\n */\n async resolve(): Promise<ResolvedBrowser> {\n const { name } = this;\n const key = this.#key;\n\n // Dead-browser recovery consumes two attempts (retire, then create).\n for (let attempt = 0; attempt < 4; attempt++) {\n const existing = await this.#readStored();\n\n if (existing === undefined) {\n // No browser on record: create one. The commit reports whether the\n // name was ever used before (see #createAndCommit).\n const { session, restarted } = await this.#createAndCommit();\n return { name, restarted, ...resolvedFields(session) };\n }\n\n // Live entry on record — probe it outside any lock.\n const alive = await this.#isAlive(existing);\n const lock = await this.#sessionStore.acquireLock(key);\n try {\n const current = await this.#sessionStore.get(key);\n if (current?.sessionId !== existing.sessionId) {\n continue; // swapped or retired while we probed — revalidate\n }\n if (alive) {\n const refreshed = { ...current, updatedAt: Date.now() };\n await this.#sessionStore.set(key, refreshed);\n return { name, restarted: false, ...resolvedFields(refreshed) };\n }\n // The browser died upstream (expired or reclaimed). Retire the\n // record under this lock, so any resolver that reads it during the\n // replacement window sees the marker and reports restarted: true\n // too.\n await this.#retire(current);\n } finally {\n await lock.release();\n }\n // Re-enter the loop: the next attempt takes the create path.\n }\n\n throw new Error(`Browser \"${name}\" kept changing concurrently — retry`);\n }\n\n /**\n * Resolve the browser and open a CDP connection to it for one scope (see\n * {@link BrowserConnectOptions.scope}). Commands sent over the connection\n * refresh the record's `updatedAt` (throttled to\n * {@link SESSION_TOUCH_INTERVAL_MS}), so hosts can tell an actively used\n * browser from one the platform has likely reclaimed.\n *\n * If the browser expires between the liveness probe and the WebSocket\n * upgrade, the record is retired and the browser resolved once more, so\n * the caller gets a fresh browser instead of an error.\n */\n async connect(\n options: BrowserConnectOptions = {}\n ): Promise<BrowserConnection> {\n const scope = options.scope ?? DEFAULT_BROWSER_SCOPE;\n if (scope.trim() === \"\") {\n throw new Error(\"Browser scopes must be non-empty\");\n }\n let resolved = await this.resolve();\n let cdp: CdpConnection;\n try {\n cdp = await this.#open(resolved);\n } catch (error) {\n if (!isMissingBrowserSession(error)) throw error;\n await this.#retireIfCurrent(resolved.sessionId);\n resolved = await this.resolve();\n cdp = await this.#open(resolved);\n }\n try {\n const entered = await this.#enterScope(scope, resolved.sessionId);\n return this.#connection(resolved, scope, cdp, entered);\n } catch (error) {\n cdp.disconnect();\n throw error;\n }\n }\n\n #connection(\n resolved: ResolvedBrowser,\n scope: string,\n cdp: CdpConnection,\n entered: { restarted: boolean; activeTargetId?: string }\n ): BrowserConnection {\n const { sessionId } = resolved;\n const { binding } = this.#provider;\n return {\n name: resolved.name,\n sessionId,\n scope,\n restarted: entered.restarted,\n activeTargetId: entered.activeTargetId,\n cdp,\n setActiveTarget: (targetId) =>\n this.#updateScope(scope, sessionId, targetId),\n targetsInOtherScopes: async () =>\n activeTargets(await this.#listScopes(), scope, sessionId),\n spec: () => loadCdpSpec({ browser: binding, sessionId })\n };\n }\n\n async #open(resolved: ResolvedBrowser): Promise<CdpConnection> {\n let lastTouchAt = Date.now();\n let touchInFlight = false;\n return connectBrowserSession(this.#provider.binding, resolved.sessionId, {\n timeoutMs: this.#options.timeoutMs,\n onActivity: () => {\n const now = Date.now();\n if (touchInFlight || now - lastTouchAt < this.#touchIntervalMs) {\n return;\n }\n touchInFlight = true;\n lastTouchAt = now;\n void this.#touch(resolved.sessionId)\n .catch((error: unknown) => {\n console.warn(\n `[agents/browser] Failed to refresh activity for browser \"${this.name}\"`,\n error\n );\n })\n .finally(() => {\n touchInFlight = false;\n });\n }\n });\n }\n\n /**\n * Close the browser: retire the record (so the next resolve reports\n * `restarted: true`) and delete its Browser Run session. Returns `false`\n * when there was nothing to close.\n *\n * The platform delete is best-effort: the retired record is the durable\n * outcome, and a browser whose delete failed is unreachable through this\n * store, so the pinned `keep_alive` (≤600s) reclaims it.\n */\n async close(): Promise<boolean> {\n let stored: StoredBrowserSession | undefined;\n const lock = await this.#sessionStore.acquireLock(this.#key);\n try {\n const current = await this.#sessionStore.get(this.#key);\n if (!current) return false;\n stored = current;\n await this.#retire(current);\n } finally {\n await lock.release();\n }\n try {\n await deleteBrowserSession(this.#provider.binding, stored.sessionId);\n } catch (error) {\n console.warn(\n `[agents/browser] Failed to delete closed Browser Run session ${stored.sessionId}`,\n error\n );\n }\n return true;\n }\n\n /**\n * Mint Live View URLs for the browser's open tabs, fresh from a target\n * listing. `undefined` when the browser was never created, is closed, or\n * is gone. The URLs are bearer credentials with a short connect window\n * (~5 min) — hand them to a trusted human, never to the model, and never\n * store them: re-mint instead.\n */\n async liveView(options?: {\n mode?: LiveViewMode;\n }): Promise<BrowserLiveView | undefined> {\n const entry = await this.#sessionStore.get(this.#key);\n if (!entry) return undefined;\n try {\n const targets = await listBrowserTargets(\n this.#provider.binding,\n entry.sessionId\n );\n // A human is about to look: minting counts as activity. Target\n // listing yields to the network, so a concurrent close or replacement\n // can retire the browser mid-mint — the touch and the retire\n // serialize on the key lock, and a lost touch means the listed\n // targets are already dead: report the browser gone rather than\n // minting doomed links. Touch *errors* stay best effort — a store\n // blip must not break minting.\n try {\n if (!(await this.#touch(entry.sessionId))) return undefined;\n } catch (error) {\n console.warn(\n `[agents/browser] Failed to refresh activity for browser \"${this.name}\"`,\n error\n );\n }\n return createLiveView(entry.sessionId, targets, options?.mode);\n } catch (error) {\n if (isMissingBrowserSession(error)) return undefined;\n throw error;\n }\n }\n\n // ── Internals ────────────────────────────────────────────────────────────\n\n get #provider(): BrowserRunProvider {\n return this.#options.provider;\n }\n\n /** The platform `keep_alive` every create applies. */\n get #keepAliveMs(): number {\n return (\n this.#provider.options.keepAliveMs ?? BROWSER_SESSION_KEEP_ALIVE_MAX_MS\n );\n }\n\n /**\n * Capped at half the keep-alive window: a browser with continuous CDP\n * traffic must never look older than the window the platform uses to\n * reclaim it.\n */\n get #touchIntervalMs(): number {\n return Math.min(\n this.#options.touchIntervalMs ?? SESSION_TOUCH_INTERVAL_MS,\n Math.floor(this.#keepAliveMs / 2)\n );\n }\n\n /** Lazy: `lifecycle.storage` exists only once `Lifecycle.use()` ran. */\n get #sessionStore(): ListableBrowserSessionStore {\n this.#store ??=\n this.#options.store ??\n new DurableBrowserSessionStore(this.lifecycle.storage);\n return this.#store;\n }\n\n /**\n * Refresh `updatedAt` while the record still holds `sessionId`. Returns\n * false when a concurrent close or replacement already retired it.\n */\n #touch(sessionId: string): Promise<boolean> {\n return this.#update(sessionId, (current) => ({\n ...current,\n updatedAt: Date.now()\n }));\n }\n\n /**\n * Rewrite the record only while it still holds `sessionId`: a replaced or\n * retired record is never resurrected.\n */\n async #update(\n sessionId: string,\n change: (current: StoredBrowserSession) => StoredBrowserSession\n ): Promise<boolean> {\n const lock = await this.#sessionStore.acquireLock(this.#key);\n try {\n const current = await this.#sessionStore.get(this.#key);\n if (current?.sessionId !== sessionId) {\n return false; // replaced or gone — the caller's view is stale\n }\n await this.#sessionStore.set(this.#key, change(current));\n return true;\n } finally {\n await lock.release();\n }\n }\n\n // ── Scopes ───────────────────────────────────────────────────────────────\n //\n // Each scope has a small record next to the browser's: the browser it last\n // worked in (`sessionId`) and its tab there. All of a browser's scope\n // records share one lock, so claiming a tab can check the others.\n\n #scopeKey(scope: string): string {\n return `${browserScopeKeyPrefix(this.name)}${scope}`;\n }\n\n /** The lock every scope record of this browser is written under. */\n get #scopesLockKey(): string {\n return browserScopeKeyPrefix(this.name);\n }\n\n /** Every scope record of this browser, by scope. */\n async #listScopes(): Promise<Map<string, StoredBrowserSession>> {\n const prefix = browserScopeKeyPrefix(this.name);\n const entries = await this.#sessionStore.list(prefix);\n const scopes = new Map<string, StoredBrowserSession>();\n for (const [key, entry] of entries) {\n scopes.set(key.slice(prefix.length), entry);\n }\n return scopes;\n }\n\n /**\n * Start a connection's scope: a scope whose record names an earlier\n * browser was restarted, and its record moves to this one. A new record\n * past {@link MAX_BROWSER_SCOPES} evicts the least recently used.\n */\n async #enterScope(\n scope: string,\n sessionId: string\n ): Promise<{ restarted: boolean; activeTargetId?: string }> {\n const key = this.#scopeKey(scope);\n const lock = await this.#sessionStore.acquireLock(this.#scopesLockKey);\n try {\n const entry = await this.#sessionStore.get(key);\n const now = Date.now();\n if (entry?.sessionId === sessionId) {\n await this.#sessionStore.set(key, { ...entry, updatedAt: now });\n return { restarted: false, activeTargetId: entry.activeTargetId };\n }\n await this.#sessionStore.set(key, {\n sessionId,\n createdAt: now,\n updatedAt: now\n });\n if (!entry) await this.#evictScopes();\n return { restarted: entry !== undefined };\n } finally {\n await lock.release();\n }\n }\n\n /** Delete the least recently used scope records past the cap. */\n async #evictScopes(): Promise<void> {\n const scopes = await this.#listScopes();\n if (scopes.size <= MAX_BROWSER_SCOPES) return;\n const oldest = [...scopes]\n .sort(([, a], [, b]) => a.updatedAt - b.updatedAt)\n .slice(0, scopes.size - MAX_BROWSER_SCOPES);\n for (const [scope] of oldest) {\n await this.#sessionStore.delete(this.#scopeKey(scope));\n }\n }\n\n /**\n * Set a scope's tab (or clear it with `undefined`). Never resurrects:\n * does nothing (and returns `false`) once the browser this connection\n * resolved was closed or replaced.\n */\n async #updateScope(\n scope: string,\n sessionId: string,\n targetId: string | undefined\n ): Promise<boolean> {\n const key = this.#scopeKey(scope);\n const lock = await this.#sessionStore.acquireLock(this.#scopesLockKey);\n try {\n const current = await this.#sessionStore.get(this.#key);\n const entry = await this.#sessionStore.get(key);\n if (current?.sessionId !== sessionId || entry?.sessionId !== sessionId) {\n return false;\n }\n await this.#sessionStore.set(key, {\n ...entry,\n activeTargetId: targetId,\n updatedAt: Date.now()\n });\n return true;\n } finally {\n await lock.release();\n }\n }\n\n /** Retire the record if it still holds `sessionId`. */\n async #retireIfCurrent(sessionId: string): Promise<void> {\n const lock = await this.#sessionStore.acquireLock(this.#key);\n try {\n const current = await this.#sessionStore.get(this.#key);\n if (current?.sessionId === sessionId) await this.#retire(current);\n } finally {\n await lock.release();\n }\n }\n\n /**\n * Move the record to its retired marker. Callers hold the key lock, so a\n * resolver always sees either the record or the marker.\n */\n async #retire(current: StoredBrowserSession): Promise<void> {\n await this.#sessionStore.set(retiredBrowserSessionKey(this.name), {\n ...current,\n closedAt: Date.now()\n });\n await this.#sessionStore.delete(this.#key);\n }\n\n /**\n * Create a Browser Run session (outside any lock) and commit it, applying\n * the provider's creation options. If a concurrent caller committed an\n * entry first, theirs wins and the redundant session is deleted\n * best-effort.\n *\n * `restarted` is read under the commit lock: a retired marker — left by\n * close() or a dead-browser recovery — is evidence a prior browser\n * existed, and only first-ever use of the name is not a restart. Markers\n * are permanent, so reading at commit time also catches a browser that\n * was created and closed while this create was in flight.\n */\n async #createAndCommit(): Promise<{\n session: StoredBrowserSession;\n restarted: boolean;\n }> {\n const { binding, options } = this.#provider;\n const info = await createBrowserSession(binding, {\n keepAliveMs: this.#keepAliveMs,\n recording: options.recording,\n guardrails: options.guardrails\n });\n const now = Date.now();\n const stored: StoredBrowserSession = {\n sessionId: info.sessionId,\n createdAt: now,\n updatedAt: now\n };\n\n let winner: StoredBrowserSession | undefined;\n let restarted: boolean;\n const lock = await this.#sessionStore.acquireLock(this.#key);\n try {\n restarted =\n (await this.#sessionStore.get(retiredBrowserSessionKey(this.name))) !==\n undefined;\n const current = await this.#sessionStore.get(this.#key);\n if (current === undefined) {\n await this.#sessionStore.set(this.#key, stored);\n } else {\n winner = current;\n }\n } finally {\n await lock.release();\n }\n\n if (winner) {\n try {\n await deleteBrowserSession(binding, stored.sessionId);\n } catch (error) {\n console.warn(\n `[agents/browser] Failed to delete redundant Browser Run session ${stored.sessionId}`,\n error\n );\n }\n return { session: winner, restarted };\n }\n return { session: stored, restarted };\n }\n\n async #isAlive(stored: StoredBrowserSession): Promise<boolean> {\n try {\n await listBrowserTargets(this.#provider.binding, stored.sessionId);\n return true;\n } catch (error) {\n if (isMissingBrowserSession(error)) return false;\n throw error;\n }\n }\n\n async #readStored(): Promise<StoredBrowserSession | undefined> {\n const lock = await this.#sessionStore.acquireLock(this.#key);\n try {\n return await this.#sessionStore.get(this.#key);\n } finally {\n await lock.release();\n }\n }\n}\n\n/** A store record without fields that belong to scope records. */\nfunction resolvedFields(session: StoredBrowserSession) {\n const { sessionId, createdAt, updatedAt } = session;\n return { sessionId, createdAt, updatedAt };\n}\n\n/** The tabs scopes other than `scope` are working in, in `sessionId`. */\nfunction activeTargets(\n scopes: Map<string, StoredBrowserSession>,\n scope: string,\n sessionId: string\n): Set<string> {\n const targets = new Set<string>();\n for (const [other, entry] of scopes) {\n if (other === scope || entry.sessionId !== sessionId) continue;\n if (entry.activeTargetId) targets.add(entry.activeTargetId);\n }\n return targets;\n}\n"],"mappings":"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"}
|