@zixt/host 0.0.97 → 0.0.98
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/index.js +174 -83
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28,7 +28,7 @@ import { homedir as homedir3 } from "node:os";
|
|
|
28
28
|
// package.json
|
|
29
29
|
var package_default = {
|
|
30
30
|
name: "@zixt/host",
|
|
31
|
-
version: "0.0.
|
|
31
|
+
version: "0.0.98",
|
|
32
32
|
type: "module",
|
|
33
33
|
exports: {
|
|
34
34
|
".": "./src/client.ts",
|
|
@@ -14764,6 +14764,7 @@ var BrowserTab = external_exports.object({
|
|
|
14764
14764
|
var BROWSER_MAX_TABS = 12;
|
|
14765
14765
|
var BrowserSessionState = external_exports.object({
|
|
14766
14766
|
browserSessionId: BrowserSessionId,
|
|
14767
|
+
taskId: TaskId,
|
|
14767
14768
|
agentId: AgentId,
|
|
14768
14769
|
/** Mirrors the active tab so a viewer can render before tabs arrive. */
|
|
14769
14770
|
url: external_exports.string().max(2e3),
|
|
@@ -14796,6 +14797,7 @@ var BrowserCommand = external_exports.discriminatedUnion("action", [
|
|
|
14796
14797
|
]);
|
|
14797
14798
|
var BrowserFramePayload = external_exports.object({
|
|
14798
14799
|
browserSessionId: BrowserSessionId,
|
|
14800
|
+
taskId: TaskId,
|
|
14799
14801
|
agentId: AgentId,
|
|
14800
14802
|
/** Per-session monotonic counter so late frames can be dropped. */
|
|
14801
14803
|
frameSeq: external_exports.number().int().min(0),
|
|
@@ -17315,7 +17317,7 @@ var ListTasksResponse = external_exports.object({
|
|
|
17315
17317
|
}).strict();
|
|
17316
17318
|
|
|
17317
17319
|
// ../../packages/contracts/src/protocol.ts
|
|
17318
|
-
var PROTOCOL_VERSION =
|
|
17320
|
+
var PROTOCOL_VERSION = 10;
|
|
17319
17321
|
var BROWSER_PROFILE_INVENTORY_PAGE_SIZE = 200;
|
|
17320
17322
|
var HELLO_UNWOUND_ASSIGNMENT_LIMIT = 1e3;
|
|
17321
17323
|
var TASK_CANCEL_ACK_EVENT = "zixt.task.cancel.acknowledged";
|
|
@@ -18305,6 +18307,7 @@ var BrowserScreencastFrame = external_exports.object({
|
|
|
18305
18307
|
var BrowserSessionEndedFrame = external_exports.object({
|
|
18306
18308
|
type: external_exports.literal("browser.session.ended"),
|
|
18307
18309
|
browserSessionId: external_exports.string().min(1).max(200),
|
|
18310
|
+
taskId: TaskId,
|
|
18308
18311
|
agentId: AgentId,
|
|
18309
18312
|
reason: BrowserSessionEndReason
|
|
18310
18313
|
});
|
|
@@ -18340,6 +18343,7 @@ var BrowserCredentialResultFrame = external_exports.discriminatedUnion("ok", [
|
|
|
18340
18343
|
var BrowserOpenFrame = external_exports.object({
|
|
18341
18344
|
type: external_exports.literal("browser.open"),
|
|
18342
18345
|
requestId: external_exports.string().min(1).max(200),
|
|
18346
|
+
taskId: TaskId,
|
|
18343
18347
|
agentId: AgentId,
|
|
18344
18348
|
/**
|
|
18345
18349
|
* Cloud-owned browser-profile lifecycle revision. A Host persists the last
|
|
@@ -18353,11 +18357,13 @@ var BrowserOpenFrame = external_exports.object({
|
|
|
18353
18357
|
});
|
|
18354
18358
|
var BrowserCloseFrame = external_exports.object({
|
|
18355
18359
|
type: external_exports.literal("browser.close"),
|
|
18360
|
+
taskId: TaskId,
|
|
18356
18361
|
agentId: AgentId,
|
|
18357
18362
|
browserSessionId: external_exports.string().min(1).max(200)
|
|
18358
18363
|
});
|
|
18359
18364
|
var BrowserViewFrame = external_exports.object({
|
|
18360
18365
|
type: external_exports.literal("browser.view"),
|
|
18366
|
+
taskId: TaskId,
|
|
18361
18367
|
agentId: AgentId,
|
|
18362
18368
|
browserSessionId: external_exports.string().min(1).max(200),
|
|
18363
18369
|
active: external_exports.boolean(),
|
|
@@ -18365,12 +18371,14 @@ var BrowserViewFrame = external_exports.object({
|
|
|
18365
18371
|
});
|
|
18366
18372
|
var BrowserInputFrame = external_exports.object({
|
|
18367
18373
|
type: external_exports.literal("browser.input"),
|
|
18374
|
+
taskId: TaskId,
|
|
18368
18375
|
agentId: AgentId,
|
|
18369
18376
|
browserSessionId: external_exports.string().min(1).max(200),
|
|
18370
18377
|
events: external_exports.array(BrowserInputEvent).min(1).max(60)
|
|
18371
18378
|
});
|
|
18372
18379
|
var BrowserCommandFrame = external_exports.object({
|
|
18373
18380
|
type: external_exports.literal("browser.command"),
|
|
18381
|
+
taskId: TaskId,
|
|
18374
18382
|
agentId: AgentId,
|
|
18375
18383
|
browserSessionId: external_exports.string().min(1).max(200),
|
|
18376
18384
|
command: BrowserCommand
|
|
@@ -22980,9 +22988,10 @@ var HostClient = class _HostClient {
|
|
|
22980
22988
|
...openRequestId !== void 0 ? { openRequestId } : {}
|
|
22981
22989
|
}),
|
|
22982
22990
|
frame: (payload) => this.sendBrowserFrame({ type: "browser.frame", frame: payload }),
|
|
22983
|
-
ended: (browserSessionId, agentId, reason) => this.sendBrowserFrame({
|
|
22991
|
+
ended: (browserSessionId, taskId, agentId, reason) => this.sendBrowserFrame({
|
|
22984
22992
|
type: "browser.session.ended",
|
|
22985
22993
|
browserSessionId,
|
|
22994
|
+
taskId,
|
|
22986
22995
|
agentId,
|
|
22987
22996
|
reason
|
|
22988
22997
|
})
|
|
@@ -23083,6 +23092,7 @@ var HostClient = class _HostClient {
|
|
|
23083
23092
|
const bridge = this.opts.browser;
|
|
23084
23093
|
if (!bridge) return;
|
|
23085
23094
|
void bridge.open(
|
|
23095
|
+
frame.taskId,
|
|
23086
23096
|
frame.agentId,
|
|
23087
23097
|
frame.browserSessionId,
|
|
23088
23098
|
frame.viewport,
|
|
@@ -23092,6 +23102,7 @@ var HostClient = class _HostClient {
|
|
|
23092
23102
|
this.sendBrowserFrame({
|
|
23093
23103
|
type: "browser.session.ended",
|
|
23094
23104
|
browserSessionId: frame.browserSessionId,
|
|
23105
|
+
taskId: frame.taskId,
|
|
23095
23106
|
agentId: frame.agentId,
|
|
23096
23107
|
reason: "error"
|
|
23097
23108
|
});
|
|
@@ -23099,17 +23110,33 @@ var HostClient = class _HostClient {
|
|
|
23099
23110
|
return;
|
|
23100
23111
|
}
|
|
23101
23112
|
case "browser.close":
|
|
23102
|
-
void this.opts.browser?.close(frame.agentId, "stopped").catch(() => {
|
|
23113
|
+
void this.opts.browser?.close(frame.taskId, frame.agentId, frame.browserSessionId, "stopped").catch(() => {
|
|
23103
23114
|
});
|
|
23104
23115
|
return;
|
|
23105
23116
|
case "browser.view":
|
|
23106
|
-
void this.opts.browser?.setViewers(
|
|
23117
|
+
void this.opts.browser?.setViewers(
|
|
23118
|
+
frame.taskId,
|
|
23119
|
+
frame.agentId,
|
|
23120
|
+
frame.browserSessionId,
|
|
23121
|
+
frame.active,
|
|
23122
|
+
frame.viewerCount
|
|
23123
|
+
);
|
|
23107
23124
|
return;
|
|
23108
23125
|
case "browser.input":
|
|
23109
|
-
void this.opts.browser?.applyInput(
|
|
23126
|
+
void this.opts.browser?.applyInput(
|
|
23127
|
+
frame.taskId,
|
|
23128
|
+
frame.agentId,
|
|
23129
|
+
frame.browserSessionId,
|
|
23130
|
+
frame.events
|
|
23131
|
+
);
|
|
23110
23132
|
return;
|
|
23111
23133
|
case "browser.command":
|
|
23112
|
-
void this.opts.browser?.command(
|
|
23134
|
+
void this.opts.browser?.command(
|
|
23135
|
+
frame.taskId,
|
|
23136
|
+
frame.agentId,
|
|
23137
|
+
frame.browserSessionId,
|
|
23138
|
+
frame.command
|
|
23139
|
+
);
|
|
23113
23140
|
return;
|
|
23114
23141
|
case "browser.credential.result": {
|
|
23115
23142
|
const waiter = this.browserCredentialWaiters.get(frame.requestId);
|
|
@@ -28512,7 +28539,7 @@ var BrowserManager = class {
|
|
|
28512
28539
|
* open until a later trusted revision is successfully authorized.
|
|
28513
28540
|
*/
|
|
28514
28541
|
deniedAuthorizations = /* @__PURE__ */ new Map();
|
|
28515
|
-
/** Serializes open/close per teammate
|
|
28542
|
+
/** Serializes profile lifecycle and session open/close per teammate. */
|
|
28516
28543
|
locks = /* @__PURE__ */ new Map();
|
|
28517
28544
|
events = null;
|
|
28518
28545
|
profileRoot;
|
|
@@ -28562,9 +28589,9 @@ var BrowserManager = class {
|
|
|
28562
28589
|
const first = await this.profileInventoryPages().next();
|
|
28563
28590
|
return first.value ?? { agentIds: [], complete: true };
|
|
28564
28591
|
}
|
|
28565
|
-
sessionState(agentId) {
|
|
28566
|
-
const session = this.sessions.get(
|
|
28567
|
-
return session && !session.closed ? this.stateOf(session) : null;
|
|
28592
|
+
sessionState(taskId, agentId) {
|
|
28593
|
+
const session = this.sessions.get(taskId);
|
|
28594
|
+
return session && !session.closed && (!agentId || session.agentId === agentId) ? this.stateOf(session) : null;
|
|
28568
28595
|
}
|
|
28569
28596
|
/** Serialized per agent so concurrent tool calls/frames can't double-launch. */
|
|
28570
28597
|
withLock(agentId, work) {
|
|
@@ -28582,6 +28609,11 @@ var BrowserManager = class {
|
|
|
28582
28609
|
throw new Error("invalid agent id for a browser profile path");
|
|
28583
28610
|
}
|
|
28584
28611
|
}
|
|
28612
|
+
validateTaskId(taskId) {
|
|
28613
|
+
if (!TaskId.safeParse(taskId).success) {
|
|
28614
|
+
throw new Error("invalid task id for a browser session");
|
|
28615
|
+
}
|
|
28616
|
+
}
|
|
28585
28617
|
exactChild(root, child) {
|
|
28586
28618
|
const canonicalRoot = resolve7(root);
|
|
28587
28619
|
const target = resolve7(canonicalRoot, child);
|
|
@@ -28694,13 +28726,14 @@ var BrowserManager = class {
|
|
|
28694
28726
|
}
|
|
28695
28727
|
}
|
|
28696
28728
|
/**
|
|
28697
|
-
* Ensure a live session for this
|
|
28729
|
+
* Ensure a live session for this Task. An existing session is adopted
|
|
28698
28730
|
* as-is (the cloud-minted id loses to the live one — the cloud reconciles
|
|
28699
28731
|
* from the announced state). Missing Chromium surfaces as a thrown error
|
|
28700
28732
|
* with the install remedy.
|
|
28701
28733
|
*/
|
|
28702
|
-
ensure(agentId, browserSessionId) {
|
|
28734
|
+
ensure(taskId, agentId, browserSessionId) {
|
|
28703
28735
|
try {
|
|
28736
|
+
this.validateTaskId(taskId);
|
|
28704
28737
|
this.validateAgentId(agentId);
|
|
28705
28738
|
} catch (error52) {
|
|
28706
28739
|
return Promise.reject(error52);
|
|
@@ -28715,12 +28748,15 @@ var BrowserManager = class {
|
|
|
28715
28748
|
if (lifecycle?.state === "purged") {
|
|
28716
28749
|
throw new Error("this browser profile was retired and cannot be recreated locally");
|
|
28717
28750
|
}
|
|
28718
|
-
const existing = this.sessions.get(
|
|
28751
|
+
const existing = this.sessions.get(taskId);
|
|
28752
|
+
if (existing && existing.agentId !== agentId) {
|
|
28753
|
+
throw new Error("browser session teammate does not match its Task");
|
|
28754
|
+
}
|
|
28719
28755
|
if (existing?.closeFailed) {
|
|
28720
28756
|
throw new Error("the previous browser termination is not yet confirmed");
|
|
28721
28757
|
}
|
|
28722
28758
|
if (existing && !existing.closed) {
|
|
28723
|
-
this.markActivity(
|
|
28759
|
+
this.markActivity(taskId);
|
|
28724
28760
|
return this.stateOf(existing);
|
|
28725
28761
|
}
|
|
28726
28762
|
await this.ensureOwnedDirectory(this.profileRoot);
|
|
@@ -28735,12 +28771,14 @@ var BrowserManager = class {
|
|
|
28735
28771
|
}
|
|
28736
28772
|
await mkdir6(profileDir, { recursive: true, mode: 448 });
|
|
28737
28773
|
const adapter = await this.opts.factory.open({
|
|
28774
|
+
taskId,
|
|
28738
28775
|
agentId,
|
|
28739
28776
|
profileDir,
|
|
28740
28777
|
viewport: this.viewport
|
|
28741
28778
|
});
|
|
28742
28779
|
const session = {
|
|
28743
28780
|
browserSessionId: browserSessionId ?? newId("brs"),
|
|
28781
|
+
taskId,
|
|
28744
28782
|
agentId,
|
|
28745
28783
|
viewport: this.viewport,
|
|
28746
28784
|
adapter,
|
|
@@ -28756,7 +28794,7 @@ var BrowserManager = class {
|
|
|
28756
28794
|
closed: false,
|
|
28757
28795
|
closeFailed: false
|
|
28758
28796
|
};
|
|
28759
|
-
this.sessions.set(
|
|
28797
|
+
this.sessions.set(taskId, session);
|
|
28760
28798
|
adapter.onStateChanged(() => {
|
|
28761
28799
|
if (!session.closed) this.events?.state(this.stateOf(session));
|
|
28762
28800
|
});
|
|
@@ -28766,17 +28804,23 @@ var BrowserManager = class {
|
|
|
28766
28804
|
});
|
|
28767
28805
|
}
|
|
28768
28806
|
/** browser.open handler: ensure, then announce with the request correlation. */
|
|
28769
|
-
async open(agentId, browserSessionId, _viewport, openRequestId, profileRevision) {
|
|
28807
|
+
async open(taskId, agentId, browserSessionId, _viewport, openRequestId, profileRevision) {
|
|
28770
28808
|
if (profileRevision !== void 0) await this.authorizeProfile(agentId, profileRevision);
|
|
28771
|
-
const state = await this.ensure(agentId, browserSessionId);
|
|
28809
|
+
const state = await this.ensure(taskId, agentId, browserSessionId);
|
|
28772
28810
|
this.events?.state(state, openRequestId);
|
|
28773
28811
|
return state;
|
|
28774
28812
|
}
|
|
28775
|
-
close(agentId, reason) {
|
|
28776
|
-
return this.withLock(
|
|
28813
|
+
close(taskId, agentId, browserSessionId, reason) {
|
|
28814
|
+
return this.withLock(
|
|
28815
|
+
agentId,
|
|
28816
|
+
() => this.closeLocked(taskId, reason, { agentId, browserSessionId })
|
|
28817
|
+
);
|
|
28777
28818
|
}
|
|
28778
|
-
async closeLocked(
|
|
28779
|
-
const session = this.sessions.get(
|
|
28819
|
+
async closeLocked(taskId, reason, expected) {
|
|
28820
|
+
const session = this.sessions.get(taskId);
|
|
28821
|
+
if (expected && session && (session.agentId !== expected.agentId || session.browserSessionId !== expected.browserSessionId)) {
|
|
28822
|
+
return;
|
|
28823
|
+
}
|
|
28780
28824
|
if (!session || session.closed && !session.closeFailed) return;
|
|
28781
28825
|
session.closed = true;
|
|
28782
28826
|
session.closeFailed = false;
|
|
@@ -28790,8 +28834,8 @@ var BrowserManager = class {
|
|
|
28790
28834
|
session.closeFailed = true;
|
|
28791
28835
|
throw error52;
|
|
28792
28836
|
}
|
|
28793
|
-
this.sessions.delete(
|
|
28794
|
-
this.events?.ended(session.browserSessionId, agentId, reason);
|
|
28837
|
+
this.sessions.delete(taskId);
|
|
28838
|
+
this.events?.ended(session.browserSessionId, taskId, session.agentId, reason);
|
|
28795
28839
|
}
|
|
28796
28840
|
/** Persist retirement, close a live Chromium, then delete only this exact profile. */
|
|
28797
28841
|
purge(agentId, purgeId, profileRevision) {
|
|
@@ -28811,22 +28855,25 @@ var BrowserManager = class {
|
|
|
28811
28855
|
state: "purged",
|
|
28812
28856
|
purgeId
|
|
28813
28857
|
});
|
|
28814
|
-
|
|
28858
|
+
const sessionTaskIds = [...this.sessions.values()].filter((session) => session.agentId === agentId).map((session) => session.taskId);
|
|
28859
|
+
for (const taskId of sessionTaskIds) await this.closeLocked(taskId, "stopped");
|
|
28815
28860
|
await rm6(this.profilePath(agentId), { recursive: true, force: true, maxRetries: 3 });
|
|
28816
28861
|
await this.syncDirectory(this.profileRoot);
|
|
28817
28862
|
});
|
|
28818
28863
|
}
|
|
28819
28864
|
async closeAll(reason) {
|
|
28820
28865
|
await Promise.allSettled(
|
|
28821
|
-
[...this.sessions.
|
|
28866
|
+
[...this.sessions.values()].map(
|
|
28867
|
+
(session) => this.close(session.taskId, session.agentId, session.browserSessionId, reason)
|
|
28868
|
+
)
|
|
28822
28869
|
);
|
|
28823
28870
|
}
|
|
28824
28871
|
/** Streaming is viewer-driven: no watcher, no capture cost. */
|
|
28825
|
-
async setViewers(agentId, active, viewerCount) {
|
|
28826
|
-
const session = this.
|
|
28827
|
-
if (!session
|
|
28872
|
+
async setViewers(taskId, agentId, browserSessionId, active, viewerCount) {
|
|
28873
|
+
const session = this.exactSession(taskId, agentId, browserSessionId);
|
|
28874
|
+
if (!session) return;
|
|
28828
28875
|
session.viewerCount = active ? Math.max(1, viewerCount) : 0;
|
|
28829
|
-
this.markActivity(
|
|
28876
|
+
this.markActivity(taskId);
|
|
28830
28877
|
if (session.viewerCount > 0) {
|
|
28831
28878
|
await session.adapter.setFrameSink((frame) => this.enqueueFrame(session, frame));
|
|
28832
28879
|
try {
|
|
@@ -28840,17 +28887,17 @@ var BrowserManager = class {
|
|
|
28840
28887
|
session.pendingFrame = null;
|
|
28841
28888
|
}
|
|
28842
28889
|
}
|
|
28843
|
-
applyInput(agentId, events) {
|
|
28844
|
-
const session = this.
|
|
28845
|
-
if (!session
|
|
28846
|
-
this.markActivity(
|
|
28890
|
+
applyInput(taskId, agentId, browserSessionId, events) {
|
|
28891
|
+
const session = this.exactSession(taskId, agentId, browserSessionId);
|
|
28892
|
+
if (!session) return Promise.resolve();
|
|
28893
|
+
this.markActivity(taskId);
|
|
28847
28894
|
return this.enqueue(session, () => session.adapter.applyInput(events));
|
|
28848
28895
|
}
|
|
28849
28896
|
/** Chrome-level action from a human: tabs, reload, history, address bar. */
|
|
28850
|
-
command(agentId, command) {
|
|
28851
|
-
const session = this.
|
|
28852
|
-
if (!session
|
|
28853
|
-
this.markActivity(
|
|
28897
|
+
command(taskId, agentId, browserSessionId, command) {
|
|
28898
|
+
const session = this.exactSession(taskId, agentId, browserSessionId);
|
|
28899
|
+
if (!session) return Promise.resolve();
|
|
28900
|
+
this.markActivity(taskId);
|
|
28854
28901
|
const resizeGeneration = command.action === "resize" ? ++session.resizeGeneration : null;
|
|
28855
28902
|
return this.enqueue(session, async () => {
|
|
28856
28903
|
if (resizeGeneration !== null && resizeGeneration !== session.resizeGeneration) return;
|
|
@@ -28865,9 +28912,9 @@ var BrowserManager = class {
|
|
|
28865
28912
|
});
|
|
28866
28913
|
}
|
|
28867
28914
|
/**
|
|
28868
|
-
* One
|
|
28869
|
-
* here in arrival order, including tool calls, so an agent action
|
|
28870
|
-
* human's click cannot interleave halfway through each other.
|
|
28915
|
+
* One Task session, one queue. Everything that touches its pages goes
|
|
28916
|
+
* through here in arrival order, including tool calls, so an agent action
|
|
28917
|
+
* and a human's click cannot interleave halfway through each other.
|
|
28871
28918
|
*/
|
|
28872
28919
|
enqueue(session, work) {
|
|
28873
28920
|
const next = session.inputQueue.then(work, work);
|
|
@@ -28876,11 +28923,11 @@ var BrowserManager = class {
|
|
|
28876
28923
|
return next;
|
|
28877
28924
|
}
|
|
28878
28925
|
/** Tool surface: every call ensures the session and counts as activity. */
|
|
28879
|
-
async tool(agentId) {
|
|
28880
|
-
await this.ensure(agentId);
|
|
28881
|
-
const session = this.sessions.get(
|
|
28926
|
+
async tool(taskId, agentId) {
|
|
28927
|
+
await this.ensure(taskId, agentId);
|
|
28928
|
+
const session = this.sessions.get(taskId);
|
|
28882
28929
|
if (!session || session.closed) throw new Error("browser session is not available");
|
|
28883
|
-
const touch = () => this.markActivity(
|
|
28930
|
+
const touch = () => this.markActivity(taskId);
|
|
28884
28931
|
const queued = (work) => {
|
|
28885
28932
|
touch();
|
|
28886
28933
|
let result;
|
|
@@ -28917,6 +28964,7 @@ var BrowserManager = class {
|
|
|
28917
28964
|
const page2 = session.adapter.state();
|
|
28918
28965
|
return {
|
|
28919
28966
|
browserSessionId: session.browserSessionId,
|
|
28967
|
+
taskId: session.taskId,
|
|
28920
28968
|
agentId: session.agentId,
|
|
28921
28969
|
url: page2.url.slice(0, 2e3),
|
|
28922
28970
|
title: page2.title.slice(0, 300),
|
|
@@ -28941,6 +28989,7 @@ var BrowserManager = class {
|
|
|
28941
28989
|
session.lastFrameSentAt = Date.now();
|
|
28942
28990
|
this.events?.frame({
|
|
28943
28991
|
browserSessionId: session.browserSessionId,
|
|
28992
|
+
taskId: session.taskId,
|
|
28944
28993
|
agentId: session.agentId,
|
|
28945
28994
|
frameSeq: session.frameSeq++,
|
|
28946
28995
|
jpegBase64: pending.jpegBase64,
|
|
@@ -28950,8 +28999,12 @@ var BrowserManager = class {
|
|
|
28950
28999
|
}, delay4);
|
|
28951
29000
|
session.frameTimer.unref?.();
|
|
28952
29001
|
}
|
|
28953
|
-
|
|
28954
|
-
const session = this.sessions.get(
|
|
29002
|
+
exactSession(taskId, agentId, browserSessionId) {
|
|
29003
|
+
const session = this.sessions.get(taskId);
|
|
29004
|
+
return session && !session.closed && session.agentId === agentId && session.browserSessionId === browserSessionId ? session : null;
|
|
29005
|
+
}
|
|
29006
|
+
markActivity(taskId) {
|
|
29007
|
+
const session = this.sessions.get(taskId);
|
|
28955
29008
|
if (!session || session.closed) return;
|
|
28956
29009
|
this.armIdleTimer(session);
|
|
28957
29010
|
}
|
|
@@ -28962,7 +29015,12 @@ var BrowserManager = class {
|
|
|
28962
29015
|
this.armIdleTimer(session);
|
|
28963
29016
|
return;
|
|
28964
29017
|
}
|
|
28965
|
-
void this.close(
|
|
29018
|
+
void this.close(
|
|
29019
|
+
session.taskId,
|
|
29020
|
+
session.agentId,
|
|
29021
|
+
session.browserSessionId,
|
|
29022
|
+
"idle_timeout"
|
|
29023
|
+
).catch(() => {
|
|
28966
29024
|
});
|
|
28967
29025
|
}, this.idleTimeoutMs);
|
|
28968
29026
|
session.idleTimer.unref?.();
|
|
@@ -28982,6 +29040,49 @@ async function loadPlaywright() {
|
|
|
28982
29040
|
var KEY_ALIASES = { " ": "Space" };
|
|
28983
29041
|
function createPlaywrightBrowserAdapterFactory(dependencies = {}) {
|
|
28984
29042
|
const load = dependencies.loadPlaywright ?? loadPlaywright;
|
|
29043
|
+
const runtimes = /* @__PURE__ */ new Map();
|
|
29044
|
+
const runtimeLocks = /* @__PURE__ */ new Map();
|
|
29045
|
+
const withRuntimeLock = (profileDir, work) => {
|
|
29046
|
+
const tail = runtimeLocks.get(profileDir) ?? Promise.resolve();
|
|
29047
|
+
const next = tail.then(work, work);
|
|
29048
|
+
runtimeLocks.set(
|
|
29049
|
+
profileDir,
|
|
29050
|
+
next.catch(() => {
|
|
29051
|
+
})
|
|
29052
|
+
);
|
|
29053
|
+
return next;
|
|
29054
|
+
};
|
|
29055
|
+
const acquireRuntime = (profileDir, viewport) => withRuntimeLock(profileDir, async () => {
|
|
29056
|
+
let runtime = runtimes.get(profileDir);
|
|
29057
|
+
if (!runtime) {
|
|
29058
|
+
const playwright = await load();
|
|
29059
|
+
const context = await playwright.chromium.launchPersistentContext(profileDir, {
|
|
29060
|
+
headless: true,
|
|
29061
|
+
// Use the full Chromium build so real sign-in pages see an ordinary
|
|
29062
|
+
// browser shape rather than the headless-shell fingerprint.
|
|
29063
|
+
channel: "chromium",
|
|
29064
|
+
viewport,
|
|
29065
|
+
args: ["--disable-blink-features=AutomationControlled"]
|
|
29066
|
+
});
|
|
29067
|
+
await Promise.all(context.pages().map((page2) => page2.close()));
|
|
29068
|
+
runtime = { context, references: 0 };
|
|
29069
|
+
runtimes.set(profileDir, runtime);
|
|
29070
|
+
}
|
|
29071
|
+
runtime.references += 1;
|
|
29072
|
+
return runtime;
|
|
29073
|
+
});
|
|
29074
|
+
const releaseRuntime = (profileDir, runtime) => withRuntimeLock(profileDir, async () => {
|
|
29075
|
+
if (runtimes.get(profileDir) !== runtime || runtime.references <= 0) return;
|
|
29076
|
+
runtime.references -= 1;
|
|
29077
|
+
if (runtime.references > 0) return;
|
|
29078
|
+
try {
|
|
29079
|
+
await runtime.context.close();
|
|
29080
|
+
runtimes.delete(profileDir);
|
|
29081
|
+
} catch (error52) {
|
|
29082
|
+
runtime.references = 1;
|
|
29083
|
+
throw error52;
|
|
29084
|
+
}
|
|
29085
|
+
});
|
|
28985
29086
|
return {
|
|
28986
29087
|
kind: "playwright",
|
|
28987
29088
|
async capability() {
|
|
@@ -28996,28 +29097,8 @@ function createPlaywrightBrowserAdapterFactory(dependencies = {}) {
|
|
|
28996
29097
|
}
|
|
28997
29098
|
},
|
|
28998
29099
|
async open({ profileDir, viewport }) {
|
|
28999
|
-
const
|
|
29000
|
-
const context =
|
|
29001
|
-
headless: true,
|
|
29002
|
-
// Without a channel, Playwright resolves `headless: true` to its
|
|
29003
|
-
// `chromium_headless_shell` build — a stripped binary missing pieces
|
|
29004
|
-
// a browser is expected to have (no `window.chrome`, no plugins, a
|
|
29005
|
-
// single-entry `navigator.languages`). Google Accounts reads that
|
|
29006
|
-
// shape as "this browser or app may not be secure" and refuses the
|
|
29007
|
-
// sign-in outright, which made every site behind a Google login a
|
|
29008
|
-
// dead end for the person watching the panel. `chromium` selects the
|
|
29009
|
-
// full browser this adapter's capability probe already checks for
|
|
29010
|
-
// (`executablePath()` resolves to it), so the two now agree.
|
|
29011
|
-
channel: "chromium",
|
|
29012
|
-
viewport,
|
|
29013
|
-
// Headless Chromium announces itself in two ways that bot protection
|
|
29014
|
-
// reads as "not a person": `HeadlessChrome` in the user agent, and
|
|
29015
|
-
// `navigator.webdriver`. This is a real Chrome rendering a real page
|
|
29016
|
-
// for a real person watching it, so neither claim is informative —
|
|
29017
|
-
// sites that act on them simply refuse to load. The user agent is
|
|
29018
|
-
// corrected below, once the browser can tell us its own version.
|
|
29019
|
-
args: ["--disable-blink-features=AutomationControlled"]
|
|
29020
|
-
});
|
|
29100
|
+
const runtime = await acquireRuntime(profileDir, viewport);
|
|
29101
|
+
const context = runtime.context;
|
|
29021
29102
|
let liveViewport = { ...viewport };
|
|
29022
29103
|
const tabs = [];
|
|
29023
29104
|
let activeTabId = null;
|
|
@@ -29175,6 +29256,13 @@ function createPlaywrightBrowserAdapterFactory(dependencies = {}) {
|
|
|
29175
29256
|
});
|
|
29176
29257
|
page2.on("domcontentloaded", () => settle4(tab.navigation));
|
|
29177
29258
|
page2.on("load", () => settle4(tab.navigation));
|
|
29259
|
+
page2.on("popup", (popup) => {
|
|
29260
|
+
void adopt(popup).then(async (created) => {
|
|
29261
|
+
activeTabId = created.tabId;
|
|
29262
|
+
await syncScreencast();
|
|
29263
|
+
emit();
|
|
29264
|
+
});
|
|
29265
|
+
});
|
|
29178
29266
|
page2.on("close", () => {
|
|
29179
29267
|
if (tab.loadingTimer) clearTimeout(tab.loadingTimer);
|
|
29180
29268
|
tab.loadingTimer = null;
|
|
@@ -29195,16 +29283,15 @@ function createPlaywrightBrowserAdapterFactory(dependencies = {}) {
|
|
|
29195
29283
|
});
|
|
29196
29284
|
return tab;
|
|
29197
29285
|
};
|
|
29198
|
-
|
|
29199
|
-
|
|
29200
|
-
|
|
29201
|
-
|
|
29202
|
-
|
|
29203
|
-
activeTabId = created.tabId;
|
|
29204
|
-
await syncScreencast();
|
|
29205
|
-
emit();
|
|
29286
|
+
try {
|
|
29287
|
+
await adopt(await context.newPage());
|
|
29288
|
+
} catch (error52) {
|
|
29289
|
+
closed = true;
|
|
29290
|
+
await releaseRuntime(profileDir, runtime).catch(() => {
|
|
29206
29291
|
});
|
|
29207
|
-
|
|
29292
|
+
throw error52;
|
|
29293
|
+
}
|
|
29294
|
+
activeTabId = tabs[0].tabId;
|
|
29208
29295
|
for (const tab of tabs) {
|
|
29209
29296
|
tab.loading = false;
|
|
29210
29297
|
await refreshTitle(tab);
|
|
@@ -29502,8 +29589,11 @@ function createPlaywrightBrowserAdapterFactory(dependencies = {}) {
|
|
|
29502
29589
|
},
|
|
29503
29590
|
async close() {
|
|
29504
29591
|
closed = true;
|
|
29505
|
-
for (const tab of tabs)
|
|
29506
|
-
|
|
29592
|
+
for (const tab of [...tabs]) {
|
|
29593
|
+
await detachSession(tab);
|
|
29594
|
+
if (!tab.page.isClosed()) await tab.page.close();
|
|
29595
|
+
}
|
|
29596
|
+
await releaseRuntime(profileDir, runtime);
|
|
29507
29597
|
}
|
|
29508
29598
|
};
|
|
29509
29599
|
}
|
|
@@ -29680,7 +29770,7 @@ function externalPage(page2, data, truncated = false) {
|
|
|
29680
29770
|
var asString = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
|
|
29681
29771
|
var asNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
29682
29772
|
function createBrowserToolPack(deps) {
|
|
29683
|
-
const { manager, agentId } = deps;
|
|
29773
|
+
const { manager, taskId, agentId } = deps;
|
|
29684
29774
|
let detached = false;
|
|
29685
29775
|
const guard = () => {
|
|
29686
29776
|
if (detached) return "this run has ended; the browser tool is detached";
|
|
@@ -29691,7 +29781,7 @@ function createBrowserToolPack(deps) {
|
|
|
29691
29781
|
const refused = guard();
|
|
29692
29782
|
if (refused) return { ok: false, error: refused };
|
|
29693
29783
|
try {
|
|
29694
|
-
const tool = await manager.tool(agentId);
|
|
29784
|
+
const tool = await manager.tool(taskId, agentId);
|
|
29695
29785
|
switch (name) {
|
|
29696
29786
|
case "browser_navigate": {
|
|
29697
29787
|
const url3 = asString(args["url"]);
|
|
@@ -37943,6 +38033,7 @@ function createCliRunner(adapter, opts = {}) {
|
|
|
37943
38033
|
toolPacks.push(
|
|
37944
38034
|
createBrowserToolPack({
|
|
37945
38035
|
manager: opts.browserManager,
|
|
38036
|
+
taskId: task.taskId,
|
|
37946
38037
|
agentId: task.agentId,
|
|
37947
38038
|
authoritySignal: task.authoritySignal,
|
|
37948
38039
|
cancelledNow: task.cancelledNow,
|