@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.
Files changed (2) hide show
  1. package/dist/index.js +174 -83
  2. 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.97",
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 = 9;
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(frame.agentId, frame.active, frame.viewerCount);
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(frame.agentId, frame.events);
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(frame.agentId, frame.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: one profile allows one live browser. */
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(agentId);
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 teammate. An existing session is adopted
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(agentId);
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(agentId);
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(agentId, session);
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(agentId, () => this.closeLocked(agentId, reason));
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(agentId, reason) {
28779
- const session = this.sessions.get(agentId);
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(agentId);
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
- await this.closeLocked(agentId, "stopped");
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.keys()].map((agentId) => this.close(agentId, reason))
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.sessions.get(agentId);
28827
- if (!session || session.closed) return;
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(agentId);
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.sessions.get(agentId);
28845
- if (!session || session.closed) return Promise.resolve();
28846
- this.markActivity(agentId);
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.sessions.get(agentId);
28852
- if (!session || session.closed) return Promise.resolve();
28853
- this.markActivity(agentId);
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 page, one queue. Everything that touches the live page goes through
28869
- * here in arrival order, including tool calls, so an agent action and a
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(agentId);
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(agentId);
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
- markActivity(agentId) {
28954
- const session = this.sessions.get(agentId);
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(session.agentId, "idle_timeout").catch(() => {
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 playwright = await load();
29000
- const context = await playwright.chromium.launchPersistentContext(profileDir, {
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
- for (const page2 of context.pages()) await adopt(page2);
29199
- if (tabs.length === 0) await adopt(await context.newPage());
29200
- activeTabId = tabs[0].tabId;
29201
- context.on("page", (popup) => {
29202
- void adopt(popup).then(async (created) => {
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) await detachSession(tab);
29506
- await context.close();
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zixt/host",
3
- "version": "0.0.97",
3
+ "version": "0.0.98",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./src/client.ts",