@zixt/host 0.0.96 → 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 +179 -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
|
|
@@ -18885,6 +18893,11 @@ var ConversationEventProjection = external_exports.discriminatedUnion("kind", [
|
|
|
18885
18893
|
eventKind: external_exports.enum(["response", "error", "status", "question", "message"]),
|
|
18886
18894
|
summary: external_exports.string(),
|
|
18887
18895
|
taskStatus: TaskStatus.nullable(),
|
|
18896
|
+
/** Authorship of a role-neutral `message`; null for legacy/unknown rows. */
|
|
18897
|
+
messageAuthor: external_exports.object({
|
|
18898
|
+
role: external_exports.enum(["member", "teammate"]),
|
|
18899
|
+
displayName: external_exports.string().nullable()
|
|
18900
|
+
}).strict().nullable().optional(),
|
|
18888
18901
|
/**
|
|
18889
18902
|
* Opaque presentation key shared by the useful report and its synthetic
|
|
18890
18903
|
* terminal wake-up. It is never shown to people; clients use it only to
|
|
@@ -22975,9 +22988,10 @@ var HostClient = class _HostClient {
|
|
|
22975
22988
|
...openRequestId !== void 0 ? { openRequestId } : {}
|
|
22976
22989
|
}),
|
|
22977
22990
|
frame: (payload) => this.sendBrowserFrame({ type: "browser.frame", frame: payload }),
|
|
22978
|
-
ended: (browserSessionId, agentId, reason) => this.sendBrowserFrame({
|
|
22991
|
+
ended: (browserSessionId, taskId, agentId, reason) => this.sendBrowserFrame({
|
|
22979
22992
|
type: "browser.session.ended",
|
|
22980
22993
|
browserSessionId,
|
|
22994
|
+
taskId,
|
|
22981
22995
|
agentId,
|
|
22982
22996
|
reason
|
|
22983
22997
|
})
|
|
@@ -23078,6 +23092,7 @@ var HostClient = class _HostClient {
|
|
|
23078
23092
|
const bridge = this.opts.browser;
|
|
23079
23093
|
if (!bridge) return;
|
|
23080
23094
|
void bridge.open(
|
|
23095
|
+
frame.taskId,
|
|
23081
23096
|
frame.agentId,
|
|
23082
23097
|
frame.browserSessionId,
|
|
23083
23098
|
frame.viewport,
|
|
@@ -23087,6 +23102,7 @@ var HostClient = class _HostClient {
|
|
|
23087
23102
|
this.sendBrowserFrame({
|
|
23088
23103
|
type: "browser.session.ended",
|
|
23089
23104
|
browserSessionId: frame.browserSessionId,
|
|
23105
|
+
taskId: frame.taskId,
|
|
23090
23106
|
agentId: frame.agentId,
|
|
23091
23107
|
reason: "error"
|
|
23092
23108
|
});
|
|
@@ -23094,17 +23110,33 @@ var HostClient = class _HostClient {
|
|
|
23094
23110
|
return;
|
|
23095
23111
|
}
|
|
23096
23112
|
case "browser.close":
|
|
23097
|
-
void this.opts.browser?.close(frame.agentId, "stopped").catch(() => {
|
|
23113
|
+
void this.opts.browser?.close(frame.taskId, frame.agentId, frame.browserSessionId, "stopped").catch(() => {
|
|
23098
23114
|
});
|
|
23099
23115
|
return;
|
|
23100
23116
|
case "browser.view":
|
|
23101
|
-
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
|
+
);
|
|
23102
23124
|
return;
|
|
23103
23125
|
case "browser.input":
|
|
23104
|
-
void this.opts.browser?.applyInput(
|
|
23126
|
+
void this.opts.browser?.applyInput(
|
|
23127
|
+
frame.taskId,
|
|
23128
|
+
frame.agentId,
|
|
23129
|
+
frame.browserSessionId,
|
|
23130
|
+
frame.events
|
|
23131
|
+
);
|
|
23105
23132
|
return;
|
|
23106
23133
|
case "browser.command":
|
|
23107
|
-
void this.opts.browser?.command(
|
|
23134
|
+
void this.opts.browser?.command(
|
|
23135
|
+
frame.taskId,
|
|
23136
|
+
frame.agentId,
|
|
23137
|
+
frame.browserSessionId,
|
|
23138
|
+
frame.command
|
|
23139
|
+
);
|
|
23108
23140
|
return;
|
|
23109
23141
|
case "browser.credential.result": {
|
|
23110
23142
|
const waiter = this.browserCredentialWaiters.get(frame.requestId);
|
|
@@ -28507,7 +28539,7 @@ var BrowserManager = class {
|
|
|
28507
28539
|
* open until a later trusted revision is successfully authorized.
|
|
28508
28540
|
*/
|
|
28509
28541
|
deniedAuthorizations = /* @__PURE__ */ new Map();
|
|
28510
|
-
/** Serializes open/close per teammate
|
|
28542
|
+
/** Serializes profile lifecycle and session open/close per teammate. */
|
|
28511
28543
|
locks = /* @__PURE__ */ new Map();
|
|
28512
28544
|
events = null;
|
|
28513
28545
|
profileRoot;
|
|
@@ -28557,9 +28589,9 @@ var BrowserManager = class {
|
|
|
28557
28589
|
const first = await this.profileInventoryPages().next();
|
|
28558
28590
|
return first.value ?? { agentIds: [], complete: true };
|
|
28559
28591
|
}
|
|
28560
|
-
sessionState(agentId) {
|
|
28561
|
-
const session = this.sessions.get(
|
|
28562
|
-
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;
|
|
28563
28595
|
}
|
|
28564
28596
|
/** Serialized per agent so concurrent tool calls/frames can't double-launch. */
|
|
28565
28597
|
withLock(agentId, work) {
|
|
@@ -28577,6 +28609,11 @@ var BrowserManager = class {
|
|
|
28577
28609
|
throw new Error("invalid agent id for a browser profile path");
|
|
28578
28610
|
}
|
|
28579
28611
|
}
|
|
28612
|
+
validateTaskId(taskId) {
|
|
28613
|
+
if (!TaskId.safeParse(taskId).success) {
|
|
28614
|
+
throw new Error("invalid task id for a browser session");
|
|
28615
|
+
}
|
|
28616
|
+
}
|
|
28580
28617
|
exactChild(root, child) {
|
|
28581
28618
|
const canonicalRoot = resolve7(root);
|
|
28582
28619
|
const target = resolve7(canonicalRoot, child);
|
|
@@ -28689,13 +28726,14 @@ var BrowserManager = class {
|
|
|
28689
28726
|
}
|
|
28690
28727
|
}
|
|
28691
28728
|
/**
|
|
28692
|
-
* Ensure a live session for this
|
|
28729
|
+
* Ensure a live session for this Task. An existing session is adopted
|
|
28693
28730
|
* as-is (the cloud-minted id loses to the live one — the cloud reconciles
|
|
28694
28731
|
* from the announced state). Missing Chromium surfaces as a thrown error
|
|
28695
28732
|
* with the install remedy.
|
|
28696
28733
|
*/
|
|
28697
|
-
ensure(agentId, browserSessionId) {
|
|
28734
|
+
ensure(taskId, agentId, browserSessionId) {
|
|
28698
28735
|
try {
|
|
28736
|
+
this.validateTaskId(taskId);
|
|
28699
28737
|
this.validateAgentId(agentId);
|
|
28700
28738
|
} catch (error52) {
|
|
28701
28739
|
return Promise.reject(error52);
|
|
@@ -28710,12 +28748,15 @@ var BrowserManager = class {
|
|
|
28710
28748
|
if (lifecycle?.state === "purged") {
|
|
28711
28749
|
throw new Error("this browser profile was retired and cannot be recreated locally");
|
|
28712
28750
|
}
|
|
28713
|
-
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
|
+
}
|
|
28714
28755
|
if (existing?.closeFailed) {
|
|
28715
28756
|
throw new Error("the previous browser termination is not yet confirmed");
|
|
28716
28757
|
}
|
|
28717
28758
|
if (existing && !existing.closed) {
|
|
28718
|
-
this.markActivity(
|
|
28759
|
+
this.markActivity(taskId);
|
|
28719
28760
|
return this.stateOf(existing);
|
|
28720
28761
|
}
|
|
28721
28762
|
await this.ensureOwnedDirectory(this.profileRoot);
|
|
@@ -28730,12 +28771,14 @@ var BrowserManager = class {
|
|
|
28730
28771
|
}
|
|
28731
28772
|
await mkdir6(profileDir, { recursive: true, mode: 448 });
|
|
28732
28773
|
const adapter = await this.opts.factory.open({
|
|
28774
|
+
taskId,
|
|
28733
28775
|
agentId,
|
|
28734
28776
|
profileDir,
|
|
28735
28777
|
viewport: this.viewport
|
|
28736
28778
|
});
|
|
28737
28779
|
const session = {
|
|
28738
28780
|
browserSessionId: browserSessionId ?? newId("brs"),
|
|
28781
|
+
taskId,
|
|
28739
28782
|
agentId,
|
|
28740
28783
|
viewport: this.viewport,
|
|
28741
28784
|
adapter,
|
|
@@ -28751,7 +28794,7 @@ var BrowserManager = class {
|
|
|
28751
28794
|
closed: false,
|
|
28752
28795
|
closeFailed: false
|
|
28753
28796
|
};
|
|
28754
|
-
this.sessions.set(
|
|
28797
|
+
this.sessions.set(taskId, session);
|
|
28755
28798
|
adapter.onStateChanged(() => {
|
|
28756
28799
|
if (!session.closed) this.events?.state(this.stateOf(session));
|
|
28757
28800
|
});
|
|
@@ -28761,17 +28804,23 @@ var BrowserManager = class {
|
|
|
28761
28804
|
});
|
|
28762
28805
|
}
|
|
28763
28806
|
/** browser.open handler: ensure, then announce with the request correlation. */
|
|
28764
|
-
async open(agentId, browserSessionId, _viewport, openRequestId, profileRevision) {
|
|
28807
|
+
async open(taskId, agentId, browserSessionId, _viewport, openRequestId, profileRevision) {
|
|
28765
28808
|
if (profileRevision !== void 0) await this.authorizeProfile(agentId, profileRevision);
|
|
28766
|
-
const state = await this.ensure(agentId, browserSessionId);
|
|
28809
|
+
const state = await this.ensure(taskId, agentId, browserSessionId);
|
|
28767
28810
|
this.events?.state(state, openRequestId);
|
|
28768
28811
|
return state;
|
|
28769
28812
|
}
|
|
28770
|
-
close(agentId, reason) {
|
|
28771
|
-
return this.withLock(
|
|
28813
|
+
close(taskId, agentId, browserSessionId, reason) {
|
|
28814
|
+
return this.withLock(
|
|
28815
|
+
agentId,
|
|
28816
|
+
() => this.closeLocked(taskId, reason, { agentId, browserSessionId })
|
|
28817
|
+
);
|
|
28772
28818
|
}
|
|
28773
|
-
async closeLocked(
|
|
28774
|
-
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
|
+
}
|
|
28775
28824
|
if (!session || session.closed && !session.closeFailed) return;
|
|
28776
28825
|
session.closed = true;
|
|
28777
28826
|
session.closeFailed = false;
|
|
@@ -28785,8 +28834,8 @@ var BrowserManager = class {
|
|
|
28785
28834
|
session.closeFailed = true;
|
|
28786
28835
|
throw error52;
|
|
28787
28836
|
}
|
|
28788
|
-
this.sessions.delete(
|
|
28789
|
-
this.events?.ended(session.browserSessionId, agentId, reason);
|
|
28837
|
+
this.sessions.delete(taskId);
|
|
28838
|
+
this.events?.ended(session.browserSessionId, taskId, session.agentId, reason);
|
|
28790
28839
|
}
|
|
28791
28840
|
/** Persist retirement, close a live Chromium, then delete only this exact profile. */
|
|
28792
28841
|
purge(agentId, purgeId, profileRevision) {
|
|
@@ -28806,22 +28855,25 @@ var BrowserManager = class {
|
|
|
28806
28855
|
state: "purged",
|
|
28807
28856
|
purgeId
|
|
28808
28857
|
});
|
|
28809
|
-
|
|
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");
|
|
28810
28860
|
await rm6(this.profilePath(agentId), { recursive: true, force: true, maxRetries: 3 });
|
|
28811
28861
|
await this.syncDirectory(this.profileRoot);
|
|
28812
28862
|
});
|
|
28813
28863
|
}
|
|
28814
28864
|
async closeAll(reason) {
|
|
28815
28865
|
await Promise.allSettled(
|
|
28816
|
-
[...this.sessions.
|
|
28866
|
+
[...this.sessions.values()].map(
|
|
28867
|
+
(session) => this.close(session.taskId, session.agentId, session.browserSessionId, reason)
|
|
28868
|
+
)
|
|
28817
28869
|
);
|
|
28818
28870
|
}
|
|
28819
28871
|
/** Streaming is viewer-driven: no watcher, no capture cost. */
|
|
28820
|
-
async setViewers(agentId, active, viewerCount) {
|
|
28821
|
-
const session = this.
|
|
28822
|
-
if (!session
|
|
28872
|
+
async setViewers(taskId, agentId, browserSessionId, active, viewerCount) {
|
|
28873
|
+
const session = this.exactSession(taskId, agentId, browserSessionId);
|
|
28874
|
+
if (!session) return;
|
|
28823
28875
|
session.viewerCount = active ? Math.max(1, viewerCount) : 0;
|
|
28824
|
-
this.markActivity(
|
|
28876
|
+
this.markActivity(taskId);
|
|
28825
28877
|
if (session.viewerCount > 0) {
|
|
28826
28878
|
await session.adapter.setFrameSink((frame) => this.enqueueFrame(session, frame));
|
|
28827
28879
|
try {
|
|
@@ -28835,17 +28887,17 @@ var BrowserManager = class {
|
|
|
28835
28887
|
session.pendingFrame = null;
|
|
28836
28888
|
}
|
|
28837
28889
|
}
|
|
28838
|
-
applyInput(agentId, events) {
|
|
28839
|
-
const session = this.
|
|
28840
|
-
if (!session
|
|
28841
|
-
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);
|
|
28842
28894
|
return this.enqueue(session, () => session.adapter.applyInput(events));
|
|
28843
28895
|
}
|
|
28844
28896
|
/** Chrome-level action from a human: tabs, reload, history, address bar. */
|
|
28845
|
-
command(agentId, command) {
|
|
28846
|
-
const session = this.
|
|
28847
|
-
if (!session
|
|
28848
|
-
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);
|
|
28849
28901
|
const resizeGeneration = command.action === "resize" ? ++session.resizeGeneration : null;
|
|
28850
28902
|
return this.enqueue(session, async () => {
|
|
28851
28903
|
if (resizeGeneration !== null && resizeGeneration !== session.resizeGeneration) return;
|
|
@@ -28860,9 +28912,9 @@ var BrowserManager = class {
|
|
|
28860
28912
|
});
|
|
28861
28913
|
}
|
|
28862
28914
|
/**
|
|
28863
|
-
* One
|
|
28864
|
-
* here in arrival order, including tool calls, so an agent action
|
|
28865
|
-
* 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.
|
|
28866
28918
|
*/
|
|
28867
28919
|
enqueue(session, work) {
|
|
28868
28920
|
const next = session.inputQueue.then(work, work);
|
|
@@ -28871,11 +28923,11 @@ var BrowserManager = class {
|
|
|
28871
28923
|
return next;
|
|
28872
28924
|
}
|
|
28873
28925
|
/** Tool surface: every call ensures the session and counts as activity. */
|
|
28874
|
-
async tool(agentId) {
|
|
28875
|
-
await this.ensure(agentId);
|
|
28876
|
-
const session = this.sessions.get(
|
|
28926
|
+
async tool(taskId, agentId) {
|
|
28927
|
+
await this.ensure(taskId, agentId);
|
|
28928
|
+
const session = this.sessions.get(taskId);
|
|
28877
28929
|
if (!session || session.closed) throw new Error("browser session is not available");
|
|
28878
|
-
const touch = () => this.markActivity(
|
|
28930
|
+
const touch = () => this.markActivity(taskId);
|
|
28879
28931
|
const queued = (work) => {
|
|
28880
28932
|
touch();
|
|
28881
28933
|
let result;
|
|
@@ -28912,6 +28964,7 @@ var BrowserManager = class {
|
|
|
28912
28964
|
const page2 = session.adapter.state();
|
|
28913
28965
|
return {
|
|
28914
28966
|
browserSessionId: session.browserSessionId,
|
|
28967
|
+
taskId: session.taskId,
|
|
28915
28968
|
agentId: session.agentId,
|
|
28916
28969
|
url: page2.url.slice(0, 2e3),
|
|
28917
28970
|
title: page2.title.slice(0, 300),
|
|
@@ -28936,6 +28989,7 @@ var BrowserManager = class {
|
|
|
28936
28989
|
session.lastFrameSentAt = Date.now();
|
|
28937
28990
|
this.events?.frame({
|
|
28938
28991
|
browserSessionId: session.browserSessionId,
|
|
28992
|
+
taskId: session.taskId,
|
|
28939
28993
|
agentId: session.agentId,
|
|
28940
28994
|
frameSeq: session.frameSeq++,
|
|
28941
28995
|
jpegBase64: pending.jpegBase64,
|
|
@@ -28945,8 +28999,12 @@ var BrowserManager = class {
|
|
|
28945
28999
|
}, delay4);
|
|
28946
29000
|
session.frameTimer.unref?.();
|
|
28947
29001
|
}
|
|
28948
|
-
|
|
28949
|
-
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);
|
|
28950
29008
|
if (!session || session.closed) return;
|
|
28951
29009
|
this.armIdleTimer(session);
|
|
28952
29010
|
}
|
|
@@ -28957,7 +29015,12 @@ var BrowserManager = class {
|
|
|
28957
29015
|
this.armIdleTimer(session);
|
|
28958
29016
|
return;
|
|
28959
29017
|
}
|
|
28960
|
-
void this.close(
|
|
29018
|
+
void this.close(
|
|
29019
|
+
session.taskId,
|
|
29020
|
+
session.agentId,
|
|
29021
|
+
session.browserSessionId,
|
|
29022
|
+
"idle_timeout"
|
|
29023
|
+
).catch(() => {
|
|
28961
29024
|
});
|
|
28962
29025
|
}, this.idleTimeoutMs);
|
|
28963
29026
|
session.idleTimer.unref?.();
|
|
@@ -28977,6 +29040,49 @@ async function loadPlaywright() {
|
|
|
28977
29040
|
var KEY_ALIASES = { " ": "Space" };
|
|
28978
29041
|
function createPlaywrightBrowserAdapterFactory(dependencies = {}) {
|
|
28979
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
|
+
});
|
|
28980
29086
|
return {
|
|
28981
29087
|
kind: "playwright",
|
|
28982
29088
|
async capability() {
|
|
@@ -28991,28 +29097,8 @@ function createPlaywrightBrowserAdapterFactory(dependencies = {}) {
|
|
|
28991
29097
|
}
|
|
28992
29098
|
},
|
|
28993
29099
|
async open({ profileDir, viewport }) {
|
|
28994
|
-
const
|
|
28995
|
-
const context =
|
|
28996
|
-
headless: true,
|
|
28997
|
-
// Without a channel, Playwright resolves `headless: true` to its
|
|
28998
|
-
// `chromium_headless_shell` build — a stripped binary missing pieces
|
|
28999
|
-
// a browser is expected to have (no `window.chrome`, no plugins, a
|
|
29000
|
-
// single-entry `navigator.languages`). Google Accounts reads that
|
|
29001
|
-
// shape as "this browser or app may not be secure" and refuses the
|
|
29002
|
-
// sign-in outright, which made every site behind a Google login a
|
|
29003
|
-
// dead end for the person watching the panel. `chromium` selects the
|
|
29004
|
-
// full browser this adapter's capability probe already checks for
|
|
29005
|
-
// (`executablePath()` resolves to it), so the two now agree.
|
|
29006
|
-
channel: "chromium",
|
|
29007
|
-
viewport,
|
|
29008
|
-
// Headless Chromium announces itself in two ways that bot protection
|
|
29009
|
-
// reads as "not a person": `HeadlessChrome` in the user agent, and
|
|
29010
|
-
// `navigator.webdriver`. This is a real Chrome rendering a real page
|
|
29011
|
-
// for a real person watching it, so neither claim is informative —
|
|
29012
|
-
// sites that act on them simply refuse to load. The user agent is
|
|
29013
|
-
// corrected below, once the browser can tell us its own version.
|
|
29014
|
-
args: ["--disable-blink-features=AutomationControlled"]
|
|
29015
|
-
});
|
|
29100
|
+
const runtime = await acquireRuntime(profileDir, viewport);
|
|
29101
|
+
const context = runtime.context;
|
|
29016
29102
|
let liveViewport = { ...viewport };
|
|
29017
29103
|
const tabs = [];
|
|
29018
29104
|
let activeTabId = null;
|
|
@@ -29170,6 +29256,13 @@ function createPlaywrightBrowserAdapterFactory(dependencies = {}) {
|
|
|
29170
29256
|
});
|
|
29171
29257
|
page2.on("domcontentloaded", () => settle4(tab.navigation));
|
|
29172
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
|
+
});
|
|
29173
29266
|
page2.on("close", () => {
|
|
29174
29267
|
if (tab.loadingTimer) clearTimeout(tab.loadingTimer);
|
|
29175
29268
|
tab.loadingTimer = null;
|
|
@@ -29190,16 +29283,15 @@ function createPlaywrightBrowserAdapterFactory(dependencies = {}) {
|
|
|
29190
29283
|
});
|
|
29191
29284
|
return tab;
|
|
29192
29285
|
};
|
|
29193
|
-
|
|
29194
|
-
|
|
29195
|
-
|
|
29196
|
-
|
|
29197
|
-
|
|
29198
|
-
activeTabId = created.tabId;
|
|
29199
|
-
await syncScreencast();
|
|
29200
|
-
emit();
|
|
29286
|
+
try {
|
|
29287
|
+
await adopt(await context.newPage());
|
|
29288
|
+
} catch (error52) {
|
|
29289
|
+
closed = true;
|
|
29290
|
+
await releaseRuntime(profileDir, runtime).catch(() => {
|
|
29201
29291
|
});
|
|
29202
|
-
|
|
29292
|
+
throw error52;
|
|
29293
|
+
}
|
|
29294
|
+
activeTabId = tabs[0].tabId;
|
|
29203
29295
|
for (const tab of tabs) {
|
|
29204
29296
|
tab.loading = false;
|
|
29205
29297
|
await refreshTitle(tab);
|
|
@@ -29497,8 +29589,11 @@ function createPlaywrightBrowserAdapterFactory(dependencies = {}) {
|
|
|
29497
29589
|
},
|
|
29498
29590
|
async close() {
|
|
29499
29591
|
closed = true;
|
|
29500
|
-
for (const tab of tabs)
|
|
29501
|
-
|
|
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);
|
|
29502
29597
|
}
|
|
29503
29598
|
};
|
|
29504
29599
|
}
|
|
@@ -29675,7 +29770,7 @@ function externalPage(page2, data, truncated = false) {
|
|
|
29675
29770
|
var asString = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
|
|
29676
29771
|
var asNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
29677
29772
|
function createBrowserToolPack(deps) {
|
|
29678
|
-
const { manager, agentId } = deps;
|
|
29773
|
+
const { manager, taskId, agentId } = deps;
|
|
29679
29774
|
let detached = false;
|
|
29680
29775
|
const guard = () => {
|
|
29681
29776
|
if (detached) return "this run has ended; the browser tool is detached";
|
|
@@ -29686,7 +29781,7 @@ function createBrowserToolPack(deps) {
|
|
|
29686
29781
|
const refused = guard();
|
|
29687
29782
|
if (refused) return { ok: false, error: refused };
|
|
29688
29783
|
try {
|
|
29689
|
-
const tool = await manager.tool(agentId);
|
|
29784
|
+
const tool = await manager.tool(taskId, agentId);
|
|
29690
29785
|
switch (name) {
|
|
29691
29786
|
case "browser_navigate": {
|
|
29692
29787
|
const url3 = asString(args["url"]);
|
|
@@ -37938,6 +38033,7 @@ function createCliRunner(adapter, opts = {}) {
|
|
|
37938
38033
|
toolPacks.push(
|
|
37939
38034
|
createBrowserToolPack({
|
|
37940
38035
|
manager: opts.browserManager,
|
|
38036
|
+
taskId: task.taskId,
|
|
37941
38037
|
agentId: task.agentId,
|
|
37942
38038
|
authoritySignal: task.authoritySignal,
|
|
37943
38039
|
cancelledNow: task.cancelledNow,
|