@zixt/host 0.0.44 → 0.0.45

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 +908 -302
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ import { homedir } from "node:os";
31
31
  // package.json
32
32
  var package_default = {
33
33
  name: "@zixt/host",
34
- version: "0.0.44",
34
+ version: "0.0.45",
35
35
  type: "module",
36
36
  exports: {
37
37
  ".": "./src/client.ts",
@@ -14656,7 +14656,9 @@ var ID_PREFIXES = {
14656
14656
  /** One immutable Conversation timeline entry. */
14657
14657
  conversationEvent: "cve",
14658
14658
  /** One Manager inference call's token-metering row (MG-8). */
14659
- managerUsage: "mgu"
14659
+ managerUsage: "mgu",
14660
+ /** One ordered Manager reply awaiting Slack delivery. */
14661
+ managerSlackOutbox: "mso"
14660
14662
  };
14661
14663
  var idPattern = (prefix) => new RegExp(`^${prefix}_[0-9a-f]{32}$`);
14662
14664
  function newId(prefix) {
@@ -14786,15 +14788,24 @@ var WebLoginValue = external_exports.object({
14786
14788
  username: external_exports.string().min(1).max(500),
14787
14789
  password: external_exports.string().min(1).max(5e3)
14788
14790
  }).strict();
14791
+ var BrowserProfileContext = external_exports.object({
14792
+ taskMachineName: external_exports.string().min(1).max(120),
14793
+ savedProfileMachineName: external_exports.string().min(1).max(120)
14794
+ }).strict();
14789
14795
  var BrowserSessionProjection = external_exports.object({
14790
14796
  status: external_exports.enum(["none", "starting", "live"]),
14791
14797
  session: BrowserSessionState.optional(),
14792
14798
  /** Whether Start would find an online Machine with a working browser. */
14793
14799
  canStart: external_exports.boolean(),
14794
14800
  /** Member-safe reason when canStart is false. */
14795
- reason: external_exports.string().max(300).optional()
14801
+ reason: external_exports.string().max(300).optional(),
14802
+ /** Present only when active work must use a different Machine-local profile. */
14803
+ profileContext: BrowserProfileContext.optional()
14804
+ }).strict();
14805
+ var StartBrowserSessionResponse = external_exports.object({
14806
+ session: BrowserSessionState,
14807
+ profileContext: BrowserProfileContext.optional()
14796
14808
  }).strict();
14797
- var StartBrowserSessionResponse = external_exports.object({ session: BrowserSessionState }).strict();
14798
14809
  var StopBrowserSessionResponse = external_exports.object({ stopped: external_exports.boolean() }).strict();
14799
14810
  var BrowserViewTicketResponse = external_exports.object({ ticket: external_exports.string().min(1).max(200) }).strict();
14800
14811
 
@@ -14818,6 +14829,7 @@ var ACTION_CATEGORIES = [
14818
14829
  "records.modify",
14819
14830
  "records.delete",
14820
14831
  "docker",
14832
+ "browser.use",
14821
14833
  "mcp.call",
14822
14834
  "spend_money"
14823
14835
  ];
@@ -14856,6 +14868,7 @@ var DEFAULT_GUARDRAIL_POLICY = {
14856
14868
  "records.modify": "allow",
14857
14869
  "records.delete": "allow",
14858
14870
  docker: "allow",
14871
+ "browser.use": "allow",
14859
14872
  "mcp.call": "allow",
14860
14873
  spend_money: "allow"
14861
14874
  };
@@ -15317,6 +15330,16 @@ var Agent = external_exports.object({
15317
15330
  orgId: OrgId,
15318
15331
  name: external_exports.string().min(1).max(120),
15319
15332
  status: AgentStatus,
15333
+ /**
15334
+ * Archive has fenced new work but is still waiting for one or more exact
15335
+ * Machines to acknowledge deletion of saved website sessions. `archived`
15336
+ * is exposed only after this becomes null.
15337
+ */
15338
+ profileCleanup: external_exports.object({
15339
+ state: external_exports.literal("pending"),
15340
+ remainingProfiles: external_exports.number().int().min(1),
15341
+ intent: external_exports.enum(["archive", "restore"])
15342
+ }).strict().nullable().default(null),
15320
15343
  roleDescription: external_exports.string().max(2e3).default(""),
15321
15344
  /** Persona instructions — system-prompt-like free text (AG-1). */
15322
15345
  instructions: external_exports.string().max(5e4).default(""),
@@ -15492,6 +15515,11 @@ var Host = external_exports.object({
15492
15515
  status: HostStatus,
15493
15516
  /** Persistent assignment admission; pausing never changes socket presence or live work. */
15494
15517
  acceptingNewWork: external_exports.boolean().default(true),
15518
+ /**
15519
+ * Removal is fenced but the Machine remains paired solely so an offline
15520
+ * Host can reconnect and delete its saved browser profiles truthfully.
15521
+ */
15522
+ profileCleanup: external_exports.object({ state: external_exports.literal("pending"), remainingProfiles: external_exports.number().int().min(1) }).strict().nullable().default(null),
15495
15523
  /** `desktop` today; `cloud` when pods run the same host (PRD §7.6). */
15496
15524
  kind: external_exports.enum(["desktop", "cloud"]),
15497
15525
  lastSeenAt: IsoDate2.nullable(),
@@ -16012,6 +16040,8 @@ var Task = external_exports.object({
16012
16040
  * its first Machine; assignment then records that choice for later runs.
16013
16041
  */
16014
16042
  requestedHostId: HostId.nullable().default(null),
16043
+ /** Browser-dependent work runs only on a Machine with fresh Browser capability. */
16044
+ requiresBrowser: external_exports.boolean().default(false),
16015
16045
  /** TS-16: per-Task runner/model/effort selection; null runs the teammate's configuration. */
16016
16046
  runner: TaskRunnerSelection.nullable().default(null),
16017
16047
  /**
@@ -16169,6 +16199,8 @@ var CreateTaskInput = external_exports.object({
16169
16199
  * another Machine truthfully queues this Task rather than overriding it.
16170
16200
  */
16171
16201
  requestedHostId: HostId.optional(),
16202
+ /** Require a fresh, working AI teammate Browser on the selected Machine. */
16203
+ requiresBrowser: external_exports.boolean().optional(),
16172
16204
  /** Per-Task runner/model/effort selection (TS-16); absent fields fall back to the teammate's configuration. */
16173
16205
  runner: TaskRunnerSelection.optional(),
16174
16206
  /** Files uploaded ahead of this message; each may belong to one Task only (TS-15). */
@@ -16470,12 +16502,15 @@ var TaskSupportBundle = external_exports.object({
16470
16502
  })
16471
16503
  )
16472
16504
  });
16505
+ var TASK_LIST_MAX_LIMIT = 200;
16473
16506
  var ListTasksResponse = external_exports.object({
16474
- tasks: external_exports.array(TaskProjection)
16475
- });
16507
+ tasks: external_exports.array(TaskProjection).max(TASK_LIST_MAX_LIMIT),
16508
+ nextCursor: external_exports.string().nullable()
16509
+ }).strict();
16476
16510
 
16477
16511
  // ../../packages/contracts/src/protocol.ts
16478
- var PROTOCOL_VERSION = 6;
16512
+ var PROTOCOL_VERSION = 7;
16513
+ var BROWSER_PROFILE_INVENTORY_PAGE_SIZE = 200;
16479
16514
  var TASK_CANCEL_ACK_EVENT = "zixt.task.cancel.acknowledged";
16480
16515
  var TASK_CREDENTIAL_ROLLOVER_ACK_EVENT = "zixt.task.credential_rollover.acknowledged";
16481
16516
  var UnwoundAssignmentRef = external_exports.object({
@@ -16753,6 +16788,11 @@ var AgentOp = external_exports.union([
16753
16788
  }),
16754
16789
  external_exports.object({ kind: external_exports.literal("secret.list") }),
16755
16790
  external_exports.object({ kind: external_exports.literal("agents.list") }),
16791
+ /** Speak to the organization Manager without addressing a human channel directly. */
16792
+ external_exports.object({
16793
+ kind: external_exports.literal("manager.message"),
16794
+ message: external_exports.string().min(1).max(5e4)
16795
+ }),
16756
16796
  /** Read one teammate's non-secret standing configuration; omission means self. */
16757
16797
  external_exports.object({ kind: external_exports.literal("agent.get"), agentId: AgentId.optional() }),
16758
16798
  /**
@@ -16953,6 +16993,12 @@ var TaskAssign = external_exports.object({
16953
16993
  type: external_exports.literal("task.assign"),
16954
16994
  taskId: TaskId,
16955
16995
  agentId: AgentId,
16996
+ /**
16997
+ * A current assignment is a trusted authorization to create a fresh
16998
+ * profile after an explicit restore. Missing legacy values never clear a
16999
+ * Host-side purge tombstone.
17000
+ */
17001
+ browserProfileRevision: external_exports.number().int().min(0).optional(),
16956
17002
  epoch: external_exports.number().int().min(1),
16957
17003
  /**
16958
17004
  * Host-enforced task authority deadline, when anything forces one.
@@ -17029,6 +17075,11 @@ var TaskAssign = external_exports.object({
17029
17075
  titleWanted: external_exports.boolean().optional(),
17030
17076
  /** Advisory Ask / Plan / Act snapshot for this attempt (TS-12). */
17031
17077
  interactionMode: InteractionMode.optional(),
17078
+ /**
17079
+ * Browser-dependent placement requirement (BR-7). Additive/optional so
17080
+ * persisted assignments from an older cloud remain parseable.
17081
+ */
17082
+ requiresBrowser: external_exports.boolean().optional(),
17032
17083
  /** Configured workspace selected by the cloud; absence means no repository context. */
17033
17084
  workspace: SafeDisplayPath.optional(),
17034
17085
  /** Verified provider repository selected independently of a local workspace path. */
@@ -17439,6 +17490,12 @@ var BrowserOpenFrame = external_exports.object({
17439
17490
  type: external_exports.literal("browser.open"),
17440
17491
  requestId: external_exports.string().min(1).max(200),
17441
17492
  agentId: AgentId,
17493
+ /**
17494
+ * Cloud-owned browser-profile lifecycle revision. A Host persists the last
17495
+ * purged revision and refuses a stale open, so a delayed frame cannot
17496
+ * recreate an archived teammate's website sessions after cleanup.
17497
+ */
17498
+ profileRevision: external_exports.number().int().min(0).optional(),
17442
17499
  /** Cloud-minted; the host adopts it for the session it opens. */
17443
17500
  browserSessionId: external_exports.string().min(1).max(200),
17444
17501
  viewport: BrowserViewport
@@ -17467,6 +17524,18 @@ var BrowserCommandFrame = external_exports.object({
17467
17524
  browserSessionId: external_exports.string().min(1).max(200),
17468
17525
  command: BrowserCommand
17469
17526
  });
17527
+ var BrowserProfilePurge = external_exports.object({
17528
+ type: external_exports.literal("browser.profile.purge"),
17529
+ purgeId: external_exports.uuid(),
17530
+ agentId: AgentId,
17531
+ profileRevision: external_exports.number().int().min(1)
17532
+ }).strict();
17533
+ var BrowserProfilePurged = external_exports.object({
17534
+ type: external_exports.literal("browser.profile.purged"),
17535
+ purgeId: external_exports.uuid(),
17536
+ agentId: AgentId,
17537
+ profileRevision: external_exports.number().int().min(1)
17538
+ }).strict();
17470
17539
  var TaskInput = external_exports.object({
17471
17540
  type: external_exports.literal("task.input"),
17472
17541
  taskId: TaskId,
@@ -17480,7 +17549,8 @@ var DurableDownMessage = external_exports.discriminatedUnion("type", [
17480
17549
  TaskInput,
17481
17550
  SecretsGrant,
17482
17551
  ApprovalDecision,
17483
- ConnectionsGrant
17552
+ ConnectionsGrant,
17553
+ BrowserProfilePurge
17484
17554
  ]);
17485
17555
  var TaskEvent = external_exports.object({
17486
17556
  type: external_exports.literal("task.event"),
@@ -17633,6 +17703,15 @@ var HelloFrame = external_exports.object({
17633
17703
  protocolVersion: external_exports.number().int(),
17634
17704
  /** Highest outbox seq the host has durably processed; cloud replays after it. */
17635
17705
  cursor: external_exports.number().int().min(0),
17706
+ /**
17707
+ * Bounded, path-validated persistent profile inventory from this Machine.
17708
+ * It migrates profiles created before cloud custody tracking existed.
17709
+ */
17710
+ browserProfileInventory: external_exports.object({
17711
+ agentIds: external_exports.array(AgentId).max(BROWSER_PROFILE_INVENTORY_PAGE_SIZE),
17712
+ /** False keeps conservative legacy migration fencing enabled. */
17713
+ complete: external_exports.boolean()
17714
+ }).strict().optional(),
17636
17715
  /**
17637
17716
  * Exact assignments whose local executors finished unwinding after the
17638
17717
  * prior socket died. Optional for rolling v5 hosts; repeated references are
@@ -17640,6 +17719,11 @@ var HelloFrame = external_exports.object({
17640
17719
  */
17641
17720
  unwoundAssignments: external_exports.array(UnwoundAssignmentRef).max(1e3).optional()
17642
17721
  });
17722
+ var BrowserProfileInventoryFrame = external_exports.object({
17723
+ type: external_exports.literal("browser.profile.inventory"),
17724
+ agentIds: external_exports.array(AgentId).max(BROWSER_PROFILE_INVENTORY_PAGE_SIZE),
17725
+ complete: external_exports.boolean()
17726
+ }).strict();
17643
17727
  var HelloAckFrame = external_exports.object({
17644
17728
  type: external_exports.literal("helloAck"),
17645
17729
  protocolVersion: external_exports.number().int(),
@@ -17722,6 +17806,7 @@ var PingFrame = external_exports.object({ type: external_exports.literal("ping")
17722
17806
  var PongFrame = external_exports.object({ type: external_exports.literal("pong"), at: external_exports.iso.datetime() });
17723
17807
  var HostToCloudFrame = external_exports.discriminatedUnion("type", [
17724
17808
  HelloFrame,
17809
+ BrowserProfileInventoryFrame,
17725
17810
  AckFrame,
17726
17811
  UpFrame,
17727
17812
  ProviderOperationGrantRequestFrame,
@@ -17729,6 +17814,7 @@ var HostToCloudFrame = external_exports.discriminatedUnion("type", [
17729
17814
  BrowserScreencastFrame,
17730
17815
  BrowserSessionEndedFrame,
17731
17816
  BrowserCredentialRequestFrame,
17817
+ BrowserProfilePurged,
17732
17818
  PingFrame,
17733
17819
  PongFrame
17734
17820
  ]);
@@ -18045,7 +18131,9 @@ var HostRemovalOutcome = external_exports.object({
18045
18131
  });
18046
18132
  var RevokeHostResponse = external_exports.object({
18047
18133
  host: AdminHostProjection,
18048
- removal: HostRemovalOutcome
18134
+ removal: HostRemovalOutcome,
18135
+ /** False while the paired Host is retained solely to finish profile deletion. */
18136
+ removed: external_exports.boolean()
18049
18137
  });
18050
18138
  var MemberHostProjection = Host.pick({
18051
18139
  id: true,
@@ -18054,6 +18142,7 @@ var MemberHostProjection = Host.pick({
18054
18142
  operatingSystem: true,
18055
18143
  status: true,
18056
18144
  acceptingNewWork: true,
18145
+ profileCleanup: true,
18057
18146
  kind: true,
18058
18147
  lastSeenAt: true,
18059
18148
  telemetryAt: true,
@@ -18636,18 +18725,28 @@ var ConversationChannel = external_exports.enum(["web", "slack", "whatsapp"]);
18636
18725
  var ConversationStatus = external_exports.enum(["idle", "thinking"]);
18637
18726
  var ConversationProjection = external_exports.object({
18638
18727
  id: ConversationId,
18639
- /** First message prefix until the Manager supplies a concise title. */
18728
+ /** First-message prefix until the one creation-time Manager title call settles. */
18640
18729
  title: external_exports.string().min(1).max(200),
18641
18730
  channel: ConversationChannel,
18642
18731
  status: ConversationStatus,
18643
18732
  /** TS-10 origin trust of the channel; propagated into every delegated Task. */
18644
18733
  trust: external_exports.enum(["internal", "external"]),
18645
- /** Subject of whoever opened the thread (member subject or provider actor). */
18646
- createdBy: external_exports.string(),
18734
+ /** One-use override for the next Task delegated from this thread. */
18735
+ nextDelegationRunner: TaskRunnerSelection.nullable(),
18647
18736
  createdAt: external_exports.string(),
18648
18737
  lastActivityAt: external_exports.string(),
18738
+ /** Archived, but at least one executor has not yet proven it stopped. */
18739
+ archiveState: external_exports.literal("stopping").nullable().optional(),
18649
18740
  archivedAt: external_exports.string().nullable()
18650
- }).strict();
18741
+ }).strict().superRefine((conversation, ctx) => {
18742
+ if (conversation.archiveState === "stopping" && conversation.archivedAt === null) {
18743
+ ctx.addIssue({
18744
+ code: "custom",
18745
+ path: ["archivedAt"],
18746
+ message: "a stopping Manager Task must already be in Archived"
18747
+ });
18748
+ }
18749
+ });
18651
18750
  var conversationEventBase = {
18652
18751
  id: ConversationEventId,
18653
18752
  at: external_exports.string()
@@ -18658,7 +18757,9 @@ var ConversationEventProjection = external_exports.discriminatedUnion("kind", [
18658
18757
  ...conversationEventBase,
18659
18758
  kind: external_exports.literal("user"),
18660
18759
  text: external_exports.string(),
18661
- author: external_exports.object({ subject: external_exports.string(), displayName: external_exports.string().nullable() }).strict()
18760
+ /** Display attribution only; authentication subjects remain internal. */
18761
+ author: external_exports.object({ displayName: external_exports.string().nullable() }).strict(),
18762
+ attachments: external_exports.array(TaskAttachmentRef).max(TASK_MESSAGE_MAX_ATTACHMENTS).optional()
18662
18763
  }).strict(),
18663
18764
  /** The Manager's prose reply. */
18664
18765
  external_exports.object({ ...conversationEventBase, kind: external_exports.literal("manager"), text: external_exports.string() }).strict(),
@@ -18677,21 +18778,70 @@ var ConversationEventProjection = external_exports.discriminatedUnion("kind", [
18677
18778
  kind: external_exports.literal("task"),
18678
18779
  taskId: TaskId,
18679
18780
  agentId: AgentId.nullable(),
18680
- eventKind: external_exports.enum(["response", "error", "status", "question"]),
18781
+ eventKind: external_exports.enum(["response", "error", "status", "question", "message"]),
18681
18782
  summary: external_exports.string(),
18682
- taskStatus: TaskStatus.nullable()
18783
+ taskStatus: TaskStatus.nullable(),
18784
+ /**
18785
+ * Opaque presentation key shared by the useful report and its synthetic
18786
+ * terminal wake-up. It is never shown to people; clients use it only to
18787
+ * render one outcome when recovery commits those records out of order.
18788
+ * Missing on legacy/non-terminal events.
18789
+ */
18790
+ terminalGroup: external_exports.string().min(1).max(200).nullable().optional()
18683
18791
  }).strict(),
18684
18792
  /** A Manager loop failure a person should see (provider outage, refusal). */
18685
18793
  external_exports.object({ ...conversationEventBase, kind: external_exports.literal("error"), message: external_exports.string() }).strict()
18686
18794
  ]);
18795
+ var NonEmptyTaskRunnerSelection = TaskRunnerSelection.refine(
18796
+ (selection) => selection.type !== void 0 || selection.model !== void 0 || selection.effort !== void 0,
18797
+ "choose at least one runtime preference"
18798
+ );
18687
18799
  var CreateConversationRequest = external_exports.object({
18688
18800
  /** Stable client-generated key: retrying the same submission returns one Conversation. */
18689
18801
  requestId: external_exports.uuid(),
18690
- message: external_exports.string().min(1).max(CONVERSATION_MESSAGE_MAX)
18691
- }).strict();
18692
- var PostConversationMessageRequest = external_exports.object({ message: external_exports.string().min(1).max(CONVERSATION_MESSAGE_MAX) }).strict();
18802
+ message: external_exports.string().max(CONVERSATION_MESSAGE_MAX),
18803
+ attachmentIds: external_exports.array(AttachmentId).max(TASK_MESSAGE_MAX_ATTACHMENTS).refine((ids) => new Set(ids).size === ids.length, "duplicate attachment").optional(),
18804
+ /**
18805
+ * Teammate whose persistent Browser is selected beside this web turn.
18806
+ * This is bounded routing context for Browser-directed requests only; it
18807
+ * is never a general delegation preference.
18808
+ */
18809
+ browserAgentId: AgentId.optional(),
18810
+ /**
18811
+ * One-time runtime override for the first Task this thread delegates.
18812
+ * Omission preserves the selected teammate's configured defaults.
18813
+ */
18814
+ initialDelegation: external_exports.object({
18815
+ runner: NonEmptyTaskRunnerSelection
18816
+ }).strict().optional()
18817
+ }).strict().superRefine((value, ctx) => {
18818
+ if (!value.message.trim() && !value.attachmentIds?.length) {
18819
+ ctx.addIssue({
18820
+ code: "custom",
18821
+ path: ["message"],
18822
+ message: "a message needs text or an attachment"
18823
+ });
18824
+ }
18825
+ });
18826
+ var PostConversationMessageRequest = external_exports.object({
18827
+ /** Stable client-generated key: an ambiguous retry appends exactly once. */
18828
+ requestId: external_exports.uuid(),
18829
+ message: external_exports.string().max(CONVERSATION_MESSAGE_MAX),
18830
+ attachmentIds: external_exports.array(AttachmentId).max(TASK_MESSAGE_MAX_ATTACHMENTS).refine((ids) => new Set(ids).size === ids.length, "duplicate attachment").optional(),
18831
+ /** Selected Manager Browser teammate for Browser-directed requests only. */
18832
+ browserAgentId: AgentId.optional()
18833
+ }).strict().superRefine((value, ctx) => {
18834
+ if (!value.message.trim() && !value.attachmentIds?.length) {
18835
+ ctx.addIssue({
18836
+ code: "custom",
18837
+ path: ["message"],
18838
+ message: "a message needs text or an attachment"
18839
+ });
18840
+ }
18841
+ });
18842
+ var UpdateNextDelegationRuntimeRequest = external_exports.object({ runner: NonEmptyTaskRunnerSelection.nullable() }).strict();
18693
18843
  var UpdateConversationRequest = external_exports.object({
18694
- title: external_exports.string().min(1).max(200).optional(),
18844
+ title: external_exports.string().trim().min(1).max(200).optional(),
18695
18845
  archived: external_exports.boolean().optional()
18696
18846
  }).strict().superRefine((value, ctx) => {
18697
18847
  if (value.title === void 0 && value.archived === void 0) {
@@ -18699,7 +18849,47 @@ var UpdateConversationRequest = external_exports.object({
18699
18849
  }
18700
18850
  });
18701
18851
  var ConversationResponse = external_exports.object({ conversation: ConversationProjection }).strict();
18702
- var ListConversationsResponse = external_exports.object({ conversations: external_exports.array(ConversationProjection).max(CONVERSATION_LIST_LIMIT) }).strict();
18852
+ var ListConversationsResponse = external_exports.object({
18853
+ conversations: external_exports.array(ConversationProjection).max(CONVERSATION_LIST_LIMIT),
18854
+ nextCursor: external_exports.string().nullable()
18855
+ }).strict();
18856
+ var ManagerTaskRailSummariesRequest = external_exports.object({
18857
+ conversationIds: external_exports.array(ConversationId).min(1).max(CONVERSATION_LIST_LIMIT),
18858
+ archived: external_exports.boolean(),
18859
+ agentId: AgentId.optional(),
18860
+ statuses: external_exports.array(TaskStatus).min(1).max(TaskStatus.options.length).optional()
18861
+ }).strict().superRefine((value, ctx) => {
18862
+ if (new Set(value.conversationIds).size !== value.conversationIds.length) {
18863
+ ctx.addIssue({
18864
+ code: "custom",
18865
+ path: ["conversationIds"],
18866
+ message: "conversationIds must be unique"
18867
+ });
18868
+ }
18869
+ if (value.statuses && new Set(value.statuses).size !== value.statuses.length) {
18870
+ ctx.addIssue({
18871
+ code: "custom",
18872
+ path: ["statuses"],
18873
+ message: "statuses must be unique"
18874
+ });
18875
+ }
18876
+ });
18877
+ var ManagerTaskRailRepresentative = external_exports.object({
18878
+ id: TaskId,
18879
+ agentId: AgentId,
18880
+ status: TaskStatus,
18881
+ gitSummary: TaskGitSummary.nullable(),
18882
+ createdAt: external_exports.string(),
18883
+ updatedAt: external_exports.string()
18884
+ }).strict();
18885
+ var ManagerTaskRailSummary = external_exports.object({
18886
+ conversationId: ConversationId,
18887
+ hasChildren: external_exports.boolean(),
18888
+ hasAgentMatch: external_exports.boolean(),
18889
+ hasStatusMatch: external_exports.boolean(),
18890
+ representative: ManagerTaskRailRepresentative.nullable()
18891
+ }).strict();
18892
+ var ManagerTaskRailSummariesResponse = external_exports.object({ summaries: external_exports.array(ManagerTaskRailSummary).max(CONVERSATION_LIST_LIMIT) }).strict();
18703
18893
  var ConversationDetailResponse = external_exports.object({
18704
18894
  conversation: ConversationProjection,
18705
18895
  events: external_exports.array(ConversationEventProjection)
@@ -19451,7 +19641,7 @@ async function generateTaskTitle(instructions, runner) {
19451
19641
  instructions.slice(0, INSTRUCTIONS_BUDGET),
19452
19642
  "</task_request>"
19453
19643
  ].join("\n");
19454
- return new Promise((resolve14) => {
19644
+ return new Promise((resolve15) => {
19455
19645
  const child = spawnCli(
19456
19646
  command,
19457
19647
  [
@@ -19474,7 +19664,7 @@ async function generateTaskTitle(instructions, runner) {
19474
19664
  if (settled) return;
19475
19665
  settled = true;
19476
19666
  clearTimeout(timer);
19477
- resolve14(value);
19667
+ resolve15(value);
19478
19668
  };
19479
19669
  const timer = setTimeout(() => {
19480
19670
  child.kill();
@@ -19796,11 +19986,11 @@ function createWorkerWatchdogSendDrain() {
19796
19986
  if (completed) return;
19797
19987
  completed = true;
19798
19988
  pending--;
19799
- if (pending === 0) drained.splice(0).forEach((resolve14) => resolve14());
19989
+ if (pending === 0) drained.splice(0).forEach((resolve15) => resolve15());
19800
19990
  };
19801
19991
  },
19802
19992
  drain: async () => {
19803
- if (pending > 0) await new Promise((resolve14) => drained.push(resolve14));
19993
+ if (pending > 0) await new Promise((resolve15) => drained.push(resolve15));
19804
19994
  }
19805
19995
  };
19806
19996
  }
@@ -20045,7 +20235,7 @@ async function waitForOperationGrantRetry(retryAt, signal) {
20045
20235
  const deadline = Date.parse(retryAt);
20046
20236
  if (!Number.isFinite(deadline) || signal.aborted) return false;
20047
20237
  if (deadline <= Date.now()) return true;
20048
- return await new Promise((resolve14) => {
20238
+ return await new Promise((resolve15) => {
20049
20239
  let settled = false;
20050
20240
  let timer;
20051
20241
  const finish = (ready) => {
@@ -20053,7 +20243,7 @@ async function waitForOperationGrantRetry(retryAt, signal) {
20053
20243
  settled = true;
20054
20244
  if (timer) clearTimeout(timer);
20055
20245
  signal.removeEventListener("abort", onAbort);
20056
- resolve14(ready);
20246
+ resolve15(ready);
20057
20247
  };
20058
20248
  const onAbort = () => finish(false);
20059
20249
  const schedule = () => {
@@ -20321,22 +20511,22 @@ var HostClient = class _HostClient {
20321
20511
  const unwindingAssignments = [...this.activeAssignments.values()];
20322
20512
  for (const cancel of this.cancels.values()) cancel(stopReason);
20323
20513
  for (const entry of this.secretGrants.values()) {
20324
- for (const resolve14 of entry.resolvers) resolve14({});
20514
+ for (const resolve15 of entry.resolvers) resolve15({});
20325
20515
  entry.resolvers = [];
20326
20516
  delete entry.value;
20327
20517
  }
20328
20518
  for (const entry of this.connectionGrants.values()) {
20329
- for (const resolve14 of entry.resolvers) resolve14([]);
20519
+ for (const resolve15 of entry.resolvers) resolve15([]);
20330
20520
  entry.resolvers = [];
20331
20521
  delete entry.value;
20332
20522
  }
20333
20523
  for (const entry of this.providerGrants.values()) {
20334
- for (const resolve14 of entry.resolvers) resolve14([]);
20524
+ for (const resolve15 of entry.resolvers) resolve15([]);
20335
20525
  entry.resolvers = [];
20336
20526
  delete entry.value;
20337
20527
  }
20338
20528
  for (const waiters of this.approvalWaiters.values()) {
20339
- for (const resolve14 of waiters.values()) resolve14({ approved: false, guidance: reason });
20529
+ for (const resolve15 of waiters.values()) resolve15({ approved: false, guidance: reason });
20340
20530
  }
20341
20531
  for (const waiters of this.agentOpWaiters.values()) {
20342
20532
  for (const waiter of waiters.values()) {
@@ -20362,9 +20552,9 @@ var HostClient = class _HostClient {
20362
20552
  let drainTimer;
20363
20553
  const drained = await Promise.race([
20364
20554
  Promise.allSettled(runs).then(() => true),
20365
- new Promise((resolve14) => {
20555
+ new Promise((resolve15) => {
20366
20556
  drainTimer = setTimeout(
20367
- () => resolve14(false),
20557
+ () => resolve15(false),
20368
20558
  this.opts.unwindTimeoutMs ?? _HostClient.DEFAULT_UNWIND_TIMEOUT_MS
20369
20559
  );
20370
20560
  drainTimer.unref?.();
@@ -20395,9 +20585,70 @@ var HostClient = class _HostClient {
20395
20585
  let awaitingHeartbeatReply = false;
20396
20586
  let closeReason;
20397
20587
  let frameTail = Promise.resolve();
20588
+ let inventoryRetryTimer;
20589
+ let inventoryRetryAttempts = 0;
20590
+ const inventoryRetryBaseMs = Math.max(20, Number(this.opts.backoffMs ?? 1e3));
20398
20591
  this.ws = ws;
20399
20592
  this.protocolReady = false;
20593
+ const retryInventory = async () => {
20594
+ if (this.stopped || this.ws !== ws || ws.readyState !== WebSocket.OPEN) return;
20595
+ let sawComplete = false;
20596
+ try {
20597
+ const pages = this.opts.browser?.profileInventoryPages?.();
20598
+ if (pages) {
20599
+ for await (const page2 of pages) {
20600
+ if (!this.send(
20601
+ {
20602
+ type: "browser.profile.inventory",
20603
+ agentIds: page2.agentIds,
20604
+ complete: page2.complete
20605
+ },
20606
+ ws
20607
+ )) {
20608
+ return;
20609
+ }
20610
+ sawComplete = page2.complete;
20611
+ }
20612
+ } else {
20613
+ const page2 = await this.opts.browser?.profileInventory?.();
20614
+ if (!page2) return;
20615
+ if (!this.send(
20616
+ {
20617
+ type: "browser.profile.inventory",
20618
+ agentIds: page2.agentIds,
20619
+ complete: page2.complete
20620
+ },
20621
+ ws
20622
+ )) {
20623
+ return;
20624
+ }
20625
+ sawComplete = page2.complete;
20626
+ }
20627
+ } catch {
20628
+ sawComplete = false;
20629
+ }
20630
+ if (sawComplete) {
20631
+ inventoryRetryAttempts = 0;
20632
+ } else {
20633
+ scheduleInventoryRetry();
20634
+ }
20635
+ };
20636
+ function scheduleInventoryRetry() {
20637
+ if (inventoryRetryTimer || ws.readyState !== WebSocket.OPEN) return;
20638
+ const delayMs = Math.min(
20639
+ 6e4,
20640
+ inventoryRetryBaseMs * 2 ** Math.min(inventoryRetryAttempts++, 6)
20641
+ );
20642
+ inventoryRetryTimer = setTimeout(() => {
20643
+ inventoryRetryTimer = void 0;
20644
+ void retryInventory();
20645
+ }, delayMs);
20646
+ inventoryRetryTimer.unref?.();
20647
+ }
20400
20648
  ws.on("open", () => {
20649
+ const inventoryConfigured = Boolean(
20650
+ this.opts.browser?.profileInventoryPages || this.opts.browser?.profileInventory
20651
+ );
20401
20652
  const unwoundAssignments = [...this.pendingUnwoundAssignments.values()].sort(
20402
20653
  (left, right) => left.taskId === right.taskId ? left.epoch - right.epoch : left.taskId.localeCompare(right.taskId)
20403
20654
  );
@@ -20406,6 +20657,7 @@ var HostClient = class _HostClient {
20406
20657
  type: "hello",
20407
20658
  protocolVersion: PROTOCOL_VERSION,
20408
20659
  cursor: this.cursor,
20660
+ ...inventoryConfigured ? { browserProfileInventory: { agentIds: [], complete: false } } : {},
20409
20661
  ...unwoundAssignments.length > 0 ? { unwoundAssignments } : {}
20410
20662
  },
20411
20663
  ws
@@ -20422,6 +20674,7 @@ var HostClient = class _HostClient {
20422
20674
  this.send({ type: "ping", at: (/* @__PURE__ */ new Date()).toISOString() });
20423
20675
  void this.reportTelemetry();
20424
20676
  }, heartbeatMs);
20677
+ if (inventoryConfigured) void retryInventory();
20425
20678
  });
20426
20679
  ws.on("message", (raw) => {
20427
20680
  if (this.ws !== ws) return;
@@ -20431,6 +20684,7 @@ var HostClient = class _HostClient {
20431
20684
  });
20432
20685
  });
20433
20686
  ws.on("close", (code) => {
20687
+ if (inventoryRetryTimer) clearTimeout(inventoryRetryTimer);
20434
20688
  void this.handleClose(ws, code, closeReason, frameTail);
20435
20689
  });
20436
20690
  ws.on("error", () => {
@@ -20444,9 +20698,9 @@ var HostClient = class _HostClient {
20444
20698
  let frameDrainTimer;
20445
20699
  const framesDrained = await Promise.race([
20446
20700
  frameTail.then(() => true),
20447
- new Promise((resolve14) => {
20701
+ new Promise((resolve15) => {
20448
20702
  frameDrainTimer = setTimeout(
20449
- () => resolve14(false),
20703
+ () => resolve15(false),
20450
20704
  this.opts.unwindTimeoutMs ?? _HostClient.DEFAULT_UNWIND_TIMEOUT_MS
20451
20705
  );
20452
20706
  frameDrainTimer.unref?.();
@@ -20823,7 +21077,13 @@ var HostClient = class _HostClient {
20823
21077
  case "browser.open": {
20824
21078
  const bridge = this.opts.browser;
20825
21079
  if (!bridge) return;
20826
- void bridge.open(frame.agentId, frame.browserSessionId, frame.viewport, frame.requestId).catch(() => {
21080
+ void bridge.open(
21081
+ frame.agentId,
21082
+ frame.browserSessionId,
21083
+ frame.viewport,
21084
+ frame.requestId,
21085
+ frame.profileRevision
21086
+ ).catch(() => {
20827
21087
  this.sendBrowserFrame({
20828
21088
  type: "browser.session.ended",
20829
21089
  browserSessionId: frame.browserSessionId,
@@ -20834,7 +21094,8 @@ var HostClient = class _HostClient {
20834
21094
  return;
20835
21095
  }
20836
21096
  case "browser.close":
20837
- void this.opts.browser?.close(frame.agentId, "stopped");
21097
+ void this.opts.browser?.close(frame.agentId, "stopped").catch(() => {
21098
+ });
20838
21099
  return;
20839
21100
  case "browser.view":
20840
21101
  void this.opts.browser?.setViewers(frame.agentId, frame.active, frame.viewerCount);
@@ -20935,8 +21196,34 @@ var HostClient = class _HostClient {
20935
21196
  async handleMessage(message) {
20936
21197
  switch (message.type) {
20937
21198
  case "task.assign":
21199
+ if (message.browserProfileRevision !== void 0) {
21200
+ try {
21201
+ await this.opts.browser?.authorizeProfile?.(
21202
+ message.agentId,
21203
+ message.browserProfileRevision
21204
+ );
21205
+ } catch {
21206
+ }
21207
+ }
20938
21208
  this.startTask(message);
20939
21209
  return;
21210
+ case "browser.profile.purge": {
21211
+ const browser = this.opts.browser;
21212
+ void (async () => {
21213
+ try {
21214
+ if (!browser?.purge) return;
21215
+ await browser.purge(message.agentId, message.purgeId, message.profileRevision);
21216
+ this.send({
21217
+ type: "browser.profile.purged",
21218
+ purgeId: message.purgeId,
21219
+ agentId: message.agentId,
21220
+ profileRevision: message.profileRevision
21221
+ });
21222
+ } catch {
21223
+ }
21224
+ })();
21225
+ return;
21226
+ }
20940
21227
  case "task.cancel":
20941
21228
  this.cancels.get(`${message.taskId}:${message.epoch}`)?.(
20942
21229
  message.purpose === "credential_rollover" ? "credential_rollover" : "cloud_cancel"
@@ -20957,7 +21244,7 @@ var HostClient = class _HostClient {
20957
21244
  const entry = this.secretGrants.get(key) ?? { resolvers: [] };
20958
21245
  entry.value = message.secrets;
20959
21246
  entry.expiresAt = expiresAt;
20960
- for (const resolve14 of entry.resolvers) resolve14(message.secrets);
21247
+ for (const resolve15 of entry.resolvers) resolve15(message.secrets);
20961
21248
  entry.resolvers = [];
20962
21249
  this.secretGrants.set(key, entry);
20963
21250
  return;
@@ -20988,13 +21275,13 @@ var HostClient = class _HostClient {
20988
21275
  const entry = this.connectionGrants.get(key) ?? { resolvers: [] };
20989
21276
  entry.value = message.connections;
20990
21277
  entry.expiresAt = expiresAt;
20991
- for (const resolve14 of entry.resolvers) resolve14(message.connections);
21278
+ for (const resolve15 of entry.resolvers) resolve15(message.connections);
20992
21279
  entry.resolvers = [];
20993
21280
  this.connectionGrants.set(key, entry);
20994
21281
  const providerEntry = this.providerGrants.get(key) ?? { resolvers: [] };
20995
21282
  providerEntry.value = providers;
20996
21283
  providerEntry.expiresAt = authorityExpiresAt;
20997
- for (const resolve14 of providerEntry.resolvers) resolve14(providers);
21284
+ for (const resolve15 of providerEntry.resolvers) resolve15(providers);
20998
21285
  providerEntry.resolvers = [];
20999
21286
  this.providerGrants.set(key, providerEntry);
21000
21287
  return;
@@ -21128,8 +21415,8 @@ var HostClient = class _HostClient {
21128
21415
  return redactCredentialText(text, sensitiveSnapshot()).slice(0, maxLength);
21129
21416
  };
21130
21417
  let resolveCancelled;
21131
- const cancelledPromise = new Promise((resolve14) => {
21132
- resolveCancelled = resolve14;
21418
+ const cancelledPromise = new Promise((resolve15) => {
21419
+ resolveCancelled = resolve15;
21133
21420
  });
21134
21421
  const endAuthority = (reason = "cloud_cancel") => {
21135
21422
  if (stopReason) return;
@@ -21138,21 +21425,21 @@ var HostClient = class _HostClient {
21138
21425
  authorityController.abort(reason);
21139
21426
  const secretEntry = this.secretGrants.get(cancelKey);
21140
21427
  if (secretEntry) {
21141
- for (const resolve14 of secretEntry.resolvers) resolve14({});
21428
+ for (const resolve15 of secretEntry.resolvers) resolve15({});
21142
21429
  secretEntry.resolvers = [];
21143
21430
  delete secretEntry.value;
21144
21431
  }
21145
21432
  this.secretGrants.delete(cancelKey);
21146
21433
  const connectionEntry = this.connectionGrants.get(cancelKey);
21147
21434
  if (connectionEntry) {
21148
- for (const resolve14 of connectionEntry.resolvers) resolve14([]);
21435
+ for (const resolve15 of connectionEntry.resolvers) resolve15([]);
21149
21436
  connectionEntry.resolvers = [];
21150
21437
  delete connectionEntry.value;
21151
21438
  }
21152
21439
  this.connectionGrants.delete(cancelKey);
21153
21440
  const providerEntry = this.providerGrants.get(cancelKey);
21154
21441
  if (providerEntry) {
21155
- for (const resolve14 of providerEntry.resolvers) resolve14([]);
21442
+ for (const resolve15 of providerEntry.resolvers) resolve15([]);
21156
21443
  providerEntry.resolvers = [];
21157
21444
  delete providerEntry.value;
21158
21445
  }
@@ -21160,8 +21447,8 @@ var HostClient = class _HostClient {
21160
21447
  this.clearAuthorityExpiry(cancelKey);
21161
21448
  const approvalWaiters = this.approvalWaiters.get(cancelKey);
21162
21449
  if (approvalWaiters) {
21163
- for (const resolve14 of approvalWaiters.values()) {
21164
- resolve14({ approved: false, guidance: "task was cancelled" });
21450
+ for (const resolve15 of approvalWaiters.values()) {
21451
+ resolve15({ approved: false, guidance: "task was cancelled" });
21165
21452
  }
21166
21453
  approvalWaiters.clear();
21167
21454
  }
@@ -21287,9 +21574,9 @@ var HostClient = class _HostClient {
21287
21574
  return value;
21288
21575
  };
21289
21576
  if (entry.value) return Promise.resolve(capture(entry.value));
21290
- return new Promise((resolve14) => {
21291
- entry.resolvers.push((value) => resolve14(capture(value)));
21292
- setTimeout(() => resolve14(capture(entry.value ?? {})), _HostClient.SECRETS_WAIT_MS);
21577
+ return new Promise((resolve15) => {
21578
+ entry.resolvers.push((value) => resolve15(capture(value)));
21579
+ setTimeout(() => resolve15(capture(entry.value ?? {})), _HostClient.SECRETS_WAIT_MS);
21293
21580
  });
21294
21581
  };
21295
21582
  const connections = () => {
@@ -21306,9 +21593,9 @@ var HostClient = class _HostClient {
21306
21593
  return value;
21307
21594
  };
21308
21595
  if (entry.value) return Promise.resolve(capture(entry.value));
21309
- return new Promise((resolve14) => {
21310
- entry.resolvers.push((value) => resolve14(capture(value)));
21311
- setTimeout(() => resolve14(capture(entry.value ?? [])), _HostClient.SECRETS_WAIT_MS);
21596
+ return new Promise((resolve15) => {
21597
+ entry.resolvers.push((value) => resolve15(capture(value)));
21598
+ setTimeout(() => resolve15(capture(entry.value ?? [])), _HostClient.SECRETS_WAIT_MS);
21312
21599
  });
21313
21600
  };
21314
21601
  const providers = () => {
@@ -21325,9 +21612,9 @@ var HostClient = class _HostClient {
21325
21612
  return value;
21326
21613
  };
21327
21614
  if (entry.value !== void 0) return Promise.resolve(capture(entry.value));
21328
- return new Promise((resolve14) => {
21329
- entry.resolvers.push((value) => resolve14(capture(value)));
21330
- setTimeout(() => resolve14(capture(entry.value ?? [])), _HostClient.SECRETS_WAIT_MS);
21615
+ return new Promise((resolve15) => {
21616
+ entry.resolvers.push((value) => resolve15(capture(value)));
21617
+ setTimeout(() => resolve15(capture(entry.value ?? [])), _HostClient.SECRETS_WAIT_MS);
21331
21618
  });
21332
21619
  };
21333
21620
  const linear = async () => {
@@ -21353,13 +21640,13 @@ var HostClient = class _HostClient {
21353
21640
  payload: safe(payload, 5e4),
21354
21641
  ...questionChoices ? { questionChoices: [...questionChoices] } : {}
21355
21642
  });
21356
- return new Promise((resolve14) => {
21643
+ return new Promise((resolve15) => {
21357
21644
  const waiters = this.approvalWaiters.get(cancelKey) ?? /* @__PURE__ */ new Map();
21358
21645
  this.approvalWaiters.set(cancelKey, waiters);
21359
- waiters.set(requestId, resolve14);
21646
+ waiters.set(requestId, resolve15);
21360
21647
  void cancelledPromise.then(() => {
21361
21648
  if (waiters.delete(requestId)) {
21362
- resolve14({ approved: false, guidance: "task was cancelled" });
21649
+ resolve15({ approved: false, guidance: "task was cancelled" });
21363
21650
  }
21364
21651
  });
21365
21652
  });
@@ -21397,11 +21684,11 @@ var HostClient = class _HostClient {
21397
21684
  if (existing) message = existing;
21398
21685
  else terminalMessages.set(requestId, message);
21399
21686
  }
21400
- return new Promise((resolve14) => {
21687
+ return new Promise((resolve15) => {
21401
21688
  const waiters = this.agentOpWaiters.get(cancelKey) ?? /* @__PURE__ */ new Map();
21402
21689
  this.agentOpWaiters.set(cancelKey, waiters);
21403
21690
  if (waiters.has(requestId)) {
21404
- resolve14({ ok: false, error: "provider settlement request is already in flight" });
21691
+ resolve15({ ok: false, error: "provider settlement request is already in flight" });
21405
21692
  return;
21406
21693
  }
21407
21694
  const timer = setTimeout(() => {
@@ -21412,7 +21699,7 @@ var HostClient = class _HostClient {
21412
21699
  (pending) => !(pending.type === "agent.op" && pending.requestId === requestId)
21413
21700
  );
21414
21701
  }
21415
- resolve14({
21702
+ resolve15({
21416
21703
  ok: false,
21417
21704
  error: terminal ? "The provider call completed, but Zixt could not record its outcome. Do not retry; wait for reconciliation." : "the platform did not answer in time; verify with a list_* tool before retrying a mutating call"
21418
21705
  });
@@ -21420,7 +21707,7 @@ var HostClient = class _HostClient {
21420
21707
  }, _HostClient.AGENT_OP_TIMEOUT_MS);
21421
21708
  timer.unref?.();
21422
21709
  waiters.set(requestId, {
21423
- resolve: resolve14,
21710
+ resolve: resolve15,
21424
21711
  timer,
21425
21712
  ...terminal ? { terminalMessage: message } : {}
21426
21713
  });
@@ -21466,12 +21753,12 @@ var HostClient = class _HostClient {
21466
21753
  "No GitHub change was attempted; the authority grant request was invalid."
21467
21754
  );
21468
21755
  }
21469
- const outcome = await new Promise((resolve14) => {
21756
+ const outcome = await new Promise((resolve15) => {
21470
21757
  const timer = setTimeout(() => {
21471
21758
  const waiter = this.operationGrantWaiters.get(requestId);
21472
21759
  if (!waiter) return;
21473
21760
  this.operationGrantWaiters.delete(requestId);
21474
- resolve14({ grant: null, retryable: true, reason: "no_reply_from_zixt" });
21761
+ resolve15({ grant: null, retryable: true, reason: "no_reply_from_zixt" });
21475
21762
  }, this.operationGrantTimeoutMs);
21476
21763
  timer.unref?.();
21477
21764
  this.operationGrantWaiters.set(requestId, {
@@ -21484,9 +21771,9 @@ var HostClient = class _HostClient {
21484
21771
  timer,
21485
21772
  accept: (grant) => {
21486
21773
  addSensitiveValues(providerGrantSensitiveValues(grant));
21487
- resolve14({ grant });
21774
+ resolve15({ grant });
21488
21775
  },
21489
- deny: (retryable, reason, detail, retryAt, retryCode) => resolve14({
21776
+ deny: (retryable, reason, detail, retryAt, retryCode) => resolve15({
21490
21777
  grant: null,
21491
21778
  retryable,
21492
21779
  reason,
@@ -21500,7 +21787,7 @@ var HostClient = class _HostClient {
21500
21787
  } catch {
21501
21788
  clearTimeout(timer);
21502
21789
  this.operationGrantWaiters.delete(requestId);
21503
- resolve14({ grant: null, retryable: false, reason: "connection_unavailable" });
21790
+ resolve15({ grant: null, retryable: false, reason: "connection_unavailable" });
21504
21791
  }
21505
21792
  });
21506
21793
  if (outcome.grant) {
@@ -21556,7 +21843,7 @@ var HostClient = class _HostClient {
21556
21843
  )
21557
21844
  );
21558
21845
  }
21559
- return new Promise((resolve14, reject3) => {
21846
+ return new Promise((resolve15, reject3) => {
21560
21847
  const timer = setTimeout(() => {
21561
21848
  if (this.browserCredentialWaiters.delete(requestId)) {
21562
21849
  reject3(
@@ -21575,7 +21862,7 @@ var HostClient = class _HostClient {
21575
21862
  timer,
21576
21863
  accept: (credential) => {
21577
21864
  addSensitiveValues(webLoginSensitiveValues(credential));
21578
- resolve14(credential);
21865
+ resolve15(credential);
21579
21866
  },
21580
21867
  deny: (reason) => reject3(new Error(reason))
21581
21868
  });
@@ -21881,6 +22168,9 @@ var ProcessTreeTerminationError = class extends Error {
21881
22168
  }
21882
22169
  name = "ProcessTreeTerminationError";
21883
22170
  };
22171
+ function posixIdentityReadMeansMissing(error52) {
22172
+ return ["ENOENT", "ESRCH"].includes(error52.code ?? "");
22173
+ }
21884
22174
  function commandContainsNonce(command, nonce) {
21885
22175
  const escaped = nonce.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
21886
22176
  return new RegExp(`(?:^|[\\s"'])${escaped}(?:$|[\\s"'])`).test(command);
@@ -21893,14 +22183,14 @@ async function observeWindowsGuardianNonce(pid, nonce) {
21893
22183
  "Windows runner identity could not be observed"
21894
22184
  );
21895
22185
  }
21896
- return new Promise((resolve14, reject3) => {
22186
+ return new Promise((resolve15, reject3) => {
21897
22187
  let done = false;
21898
22188
  const finish = (result) => {
21899
22189
  if (done) return;
21900
22190
  done = true;
21901
22191
  clearTimeout(timeout);
21902
22192
  if (result instanceof Error) reject3(result);
21903
- else resolve14(result);
22193
+ else resolve15(result);
21904
22194
  };
21905
22195
  const timeout = setTimeout(
21906
22196
  () => finish(
@@ -21938,14 +22228,14 @@ async function observePosixGuardianNonce(pid, nonce) {
21938
22228
  const args = command.toString("utf8").split("\0");
21939
22229
  return args.includes(nonce) ? "match" : "mismatch";
21940
22230
  } catch (error52) {
21941
- if (error52.code === "ENOENT") return "missing";
22231
+ if (posixIdentityReadMeansMissing(error52)) return "missing";
21942
22232
  throw new ProcessTreeTerminationError(
21943
22233
  "state_unknown",
21944
22234
  "POSIX runner identity could not be observed"
21945
22235
  );
21946
22236
  }
21947
22237
  }
21948
- return new Promise((resolve14, reject3) => {
22238
+ return new Promise((resolve15, reject3) => {
21949
22239
  const observer = spawn2("/bin/ps", ["-ww", "-o", "command=", "-p", String(pid)], {
21950
22240
  stdio: ["ignore", "pipe", "ignore"]
21951
22241
  });
@@ -21956,7 +22246,7 @@ async function observePosixGuardianNonce(pid, nonce) {
21956
22246
  done = true;
21957
22247
  clearTimeout(timeout);
21958
22248
  if (result instanceof Error) reject3(result);
21959
- else resolve14(result);
22249
+ else resolve15(result);
21960
22250
  };
21961
22251
  const timeout = setTimeout(() => {
21962
22252
  observer.kill("SIGKILL");
@@ -22003,7 +22293,7 @@ async function observeGuardianIdentity(pid, identity) {
22003
22293
  return process.platform === "win32" ? observeWindowsGuardianNonce(pid, identity.nonce) : observePosixGuardianNonce(pid, identity.nonce);
22004
22294
  }
22005
22295
  function delay(ms) {
22006
- return new Promise((resolve14) => setTimeout(resolve14, ms));
22296
+ return new Promise((resolve15) => setTimeout(resolve15, ms));
22007
22297
  }
22008
22298
  function posixProcessRecordsFromPs(output) {
22009
22299
  const records = [];
@@ -22036,7 +22326,7 @@ function posixProcessRecordsFromPs(output) {
22036
22326
  return records;
22037
22327
  }
22038
22328
  async function snapshotPosixProcesses() {
22039
- return new Promise((resolve14, reject3) => {
22329
+ return new Promise((resolve15, reject3) => {
22040
22330
  const observer = spawn2("/bin/ps", ["-axo", "uid=,pid=,ppid=,pgid=,stat="], {
22041
22331
  stdio: ["ignore", "pipe", "ignore"]
22042
22332
  });
@@ -22049,7 +22339,7 @@ async function snapshotPosixProcesses() {
22049
22339
  if (error52) reject3(error52);
22050
22340
  else {
22051
22341
  try {
22052
- resolve14(posixProcessRecordsFromPs(output));
22342
+ resolve15(posixProcessRecordsFromPs(output));
22053
22343
  } catch (caught) {
22054
22344
  reject3(caught);
22055
22345
  }
@@ -22384,7 +22674,7 @@ async function snapshotWindowsDescendants(rootPid) {
22384
22674
  "Windows process-tree observation could not start"
22385
22675
  );
22386
22676
  }
22387
- return new Promise((resolve14, reject3) => {
22677
+ return new Promise((resolve15, reject3) => {
22388
22678
  let done = false;
22389
22679
  const timeout = setTimeout(() => {
22390
22680
  if (done) return;
@@ -22411,7 +22701,7 @@ async function snapshotWindowsDescendants(rootPid) {
22411
22701
  return;
22412
22702
  }
22413
22703
  try {
22414
- resolve14(completeWindowsDescendantPids(rootPid, processes));
22704
+ resolve15(completeWindowsDescendantPids(rootPid, processes));
22415
22705
  } catch (caught) {
22416
22706
  reject3(caught);
22417
22707
  }
@@ -22458,7 +22748,7 @@ async function waitForProcessesExit(pids, timeoutMs) {
22458
22748
  }
22459
22749
  async function runTaskkill(pid, command, timeoutMs = TASKKILL_TIMEOUT_MS, independentlyTrackedPids = []) {
22460
22750
  const trustedCommand = command ?? defaultTaskkillCommand();
22461
- const result = await new Promise((resolve14, reject3) => {
22751
+ const result = await new Promise((resolve15, reject3) => {
22462
22752
  const killer = spawn2(trustedCommand, ["/PID", String(pid), "/T", "/F"], {
22463
22753
  stdio: ["ignore", "pipe", "pipe"],
22464
22754
  windowsHide: true
@@ -22493,7 +22783,7 @@ async function runTaskkill(pid, command, timeoutMs = TASKKILL_TIMEOUT_MS, indepe
22493
22783
  done = true;
22494
22784
  clearTimeout(timeout);
22495
22785
  if (error52) reject3(error52);
22496
- else resolve14({ code: killer.exitCode, output, outputTruncated });
22786
+ else resolve15({ code: killer.exitCode, output, outputTruncated });
22497
22787
  };
22498
22788
  killer.once(
22499
22789
  "error",
@@ -23170,12 +23460,12 @@ async function createWindowsJobContainment(pid, options) {
23170
23460
  stderr = `${stderr}${String(chunk)}`.slice(-HELPER_OUTPUT_LIMIT);
23171
23461
  });
23172
23462
  const helperEvents = helper;
23173
- const exited = new Promise((resolve14) => {
23463
+ const exited = new Promise((resolve15) => {
23174
23464
  let completed = false;
23175
23465
  const complete = (code, signal) => {
23176
23466
  if (completed) return;
23177
23467
  completed = true;
23178
- resolve14({ code, signal });
23468
+ resolve15({ code, signal });
23179
23469
  };
23180
23470
  helperEvents.once("error", () => {
23181
23471
  failProtocol(new Error("Windows Job Object helper could not start"));
@@ -23188,7 +23478,7 @@ async function createWindowsJobContainment(pid, options) {
23188
23478
  });
23189
23479
  const nextLine = async (expected) => {
23190
23480
  if (protocolFailure) throw protocolFailure;
23191
- const line = lines.shift() ?? await new Promise((resolve14, reject3) => {
23481
+ const line = lines.shift() ?? await new Promise((resolve15, reject3) => {
23192
23482
  const timer = setTimeout(
23193
23483
  () => reject3(timeoutError("Windows Job Object helper did not answer in time")),
23194
23484
  timeoutMs
@@ -23196,7 +23486,7 @@ async function createWindowsJobContainment(pid, options) {
23196
23486
  timer.unref?.();
23197
23487
  lineWaiters.push((value) => {
23198
23488
  clearTimeout(timer);
23199
- resolve14(value);
23489
+ resolve15(value);
23200
23490
  });
23201
23491
  });
23202
23492
  if (protocolFailure) throw protocolFailure;
@@ -23209,8 +23499,8 @@ async function createWindowsJobContainment(pid, options) {
23209
23499
  }
23210
23500
  const stopped = await Promise.race([
23211
23501
  exited.then(() => true),
23212
- new Promise((resolve14) => {
23213
- const timer = setTimeout(() => resolve14(false), timeoutMs);
23502
+ new Promise((resolve15) => {
23503
+ const timer = setTimeout(() => resolve15(false), timeoutMs);
23214
23504
  timer.unref?.();
23215
23505
  })
23216
23506
  ]);
@@ -23269,7 +23559,7 @@ async function awaitWindowsContainmentGate(env = process.env, input = process.st
23269
23559
  if (nonce === void 0) return true;
23270
23560
  if (!SAFE_NONCE2.test(nonce)) return false;
23271
23561
  const expected = windowsContainmentGate(nonce).trimEnd();
23272
- return new Promise((resolve14) => {
23562
+ return new Promise((resolve15) => {
23273
23563
  let pending = Buffer.alloc(0);
23274
23564
  let settled = false;
23275
23565
  const finish = (result) => {
@@ -23280,7 +23570,7 @@ async function awaitWindowsContainmentGate(env = process.env, input = process.st
23280
23570
  input.off("end", onEnd);
23281
23571
  input.off("error", onEnd);
23282
23572
  if (result) input.pause();
23283
- resolve14(result);
23573
+ resolve15(result);
23284
23574
  };
23285
23575
  const onData = (chunk) => {
23286
23576
  pending = Buffer.concat([pending, chunk]);
@@ -23705,7 +23995,7 @@ async function installRelease(version2, options = {}) {
23705
23995
  installerContainmentSetupError = error52;
23706
23996
  return null;
23707
23997
  }) : Promise.resolve(null);
23708
- const installed = await new Promise((resolve14, reject3) => {
23998
+ const installed = await new Promise((resolve15, reject3) => {
23709
23999
  let finished = false;
23710
24000
  let cleanupStarted = false;
23711
24001
  let exitObserved = false;
@@ -23721,7 +24011,7 @@ async function installRelease(version2, options = {}) {
23721
24011
  finished = true;
23722
24012
  clearTimeout(timer);
23723
24013
  options.signal?.removeEventListener("abort", requestCleanup);
23724
- resolve14(result);
24014
+ resolve15(result);
23725
24015
  };
23726
24016
  const requestCleanup = () => {
23727
24017
  if (cleanupStarted || finished) return;
@@ -24034,11 +24324,11 @@ async function runWorkerCompatibilityProxy(env = process.env, argv = process.arg
24034
24324
  child.stdin?.on("error", () => {
24035
24325
  });
24036
24326
  process.stdin.pipe(child.stdin);
24037
- return new Promise((resolve14) => {
24038
- child.once("error", () => resolve14(1));
24327
+ return new Promise((resolve15) => {
24328
+ child.once("error", () => resolve15(1));
24039
24329
  child.once("exit", (code) => {
24040
24330
  process.stdin.unpipe(child.stdin);
24041
- resolve14(code ?? 1);
24331
+ resolve15(code ?? 1);
24042
24332
  });
24043
24333
  });
24044
24334
  }
@@ -24116,11 +24406,11 @@ async function launchHostSupervisor(options = {}) {
24116
24406
  const waitOrStop = async (ms) => {
24117
24407
  if (stopping) return false;
24118
24408
  if (!customDelay) {
24119
- await new Promise((resolve14) => {
24409
+ await new Promise((resolve15) => {
24120
24410
  const finish = () => {
24121
24411
  clearTimeout(timer);
24122
24412
  stopController.signal.removeEventListener("abort", finish);
24123
- resolve14();
24413
+ resolve15();
24124
24414
  };
24125
24415
  const timer = setTimeout(finish, ms);
24126
24416
  stopController.signal.addEventListener("abort", finish, { once: true });
@@ -24128,8 +24418,8 @@ async function launchHostSupervisor(options = {}) {
24128
24418
  return !stopping;
24129
24419
  }
24130
24420
  let finishStop;
24131
- const stopped = new Promise((resolve14) => {
24132
- finishStop = () => resolve14();
24421
+ const stopped = new Promise((resolve15) => {
24422
+ finishStop = () => resolve15();
24133
24423
  stopController.signal.addEventListener("abort", finishStop, { once: true });
24134
24424
  });
24135
24425
  await Promise.race([customDelay(ms), stopped]);
@@ -24252,19 +24542,19 @@ async function launchHostSupervisor(options = {}) {
24252
24542
  child = spawnSupervisor(entry, version2, ownershipDirectory, containmentGateNonce);
24253
24543
  const launchedSupervisor = child;
24254
24544
  let resolveChildExited;
24255
- const childExited = new Promise((resolve14) => {
24256
- resolveChildExited = resolve14;
24545
+ const childExited = new Promise((resolve15) => {
24546
+ resolveChildExited = resolve15;
24257
24547
  });
24258
24548
  const supervisorContainmentAbort = new AbortController();
24259
24549
  void childExited.then(() => supervisorContainmentAbort.abort());
24260
24550
  const outcomePromise = new Promise(
24261
- (resolve14) => {
24551
+ (resolve15) => {
24262
24552
  let observed = false;
24263
24553
  const finish = (code, signal) => {
24264
24554
  if (observed) return;
24265
24555
  observed = true;
24266
24556
  resolveChildExited();
24267
- resolve14({ code, signal });
24557
+ resolve15({ code, signal });
24268
24558
  };
24269
24559
  child.once("error", () => finish(1, null));
24270
24560
  child.once("exit", finish);
@@ -24285,12 +24575,12 @@ async function launchHostSupervisor(options = {}) {
24285
24575
  if (!supervisorContainment || !launchedSupervisor.stdin) {
24286
24576
  throw new Error("supervisor Job Object gate is unavailable");
24287
24577
  }
24288
- await new Promise((resolve14, reject3) => {
24578
+ await new Promise((resolve15, reject3) => {
24289
24579
  launchedSupervisor.stdin.write(
24290
24580
  windowsContainmentGate(containmentGateNonce),
24291
24581
  (error52) => {
24292
24582
  if (error52) reject3(error52);
24293
- else resolve14();
24583
+ else resolve15();
24294
24584
  }
24295
24585
  );
24296
24586
  });
@@ -24432,18 +24722,18 @@ async function superviseHost(options = {}) {
24432
24722
  }
24433
24723
  }
24434
24724
  let announceShutdown;
24435
- const shutdownAnnounced = new Promise((resolve14) => {
24436
- announceShutdown = resolve14;
24725
+ const shutdownAnnounced = new Promise((resolve15) => {
24726
+ announceShutdown = resolve15;
24437
24727
  });
24438
24728
  const attempted = /* @__PURE__ */ new Set();
24439
24729
  const waitOrShutdown = async (ms) => {
24440
24730
  if (shuttingDown2) return false;
24441
24731
  if (!customDelay) {
24442
- await new Promise((resolve14) => {
24732
+ await new Promise((resolve15) => {
24443
24733
  const finish = () => {
24444
24734
  clearTimeout(timer);
24445
24735
  shutdownController.signal.removeEventListener("abort", finish);
24446
- resolve14();
24736
+ resolve15();
24447
24737
  };
24448
24738
  const timer = setTimeout(finish, ms);
24449
24739
  shutdownController.signal.addEventListener("abort", finish, { once: true });
@@ -24586,19 +24876,19 @@ async function superviseHost(options = {}) {
24586
24876
  child = spawnWorker(command, watchdogLaunch, compatibilityOwnership, containmentGateNonce);
24587
24877
  const watchedChild = child;
24588
24878
  let resolveChildExited;
24589
- const childExited = new Promise((resolve14) => {
24590
- resolveChildExited = resolve14;
24879
+ const childExited = new Promise((resolve15) => {
24880
+ resolveChildExited = resolve15;
24591
24881
  });
24592
24882
  const workerContainmentAbort = new AbortController();
24593
24883
  void childExited.then(() => workerContainmentAbort.abort());
24594
24884
  const outcomePromise = new Promise(
24595
- (resolve14) => {
24885
+ (resolve15) => {
24596
24886
  let observed = false;
24597
24887
  const finish = (result) => {
24598
24888
  if (observed) return;
24599
24889
  observed = true;
24600
24890
  resolveChildExited();
24601
- resolve14(result);
24891
+ resolve15(result);
24602
24892
  };
24603
24893
  watchedChild.once("error", () => finish({ code: 1, signal: null }));
24604
24894
  watchedChild.once(
@@ -24620,10 +24910,10 @@ async function superviseHost(options = {}) {
24620
24910
  if (!workerContainment || !watchedChild.stdin) {
24621
24911
  throw new Error("worker Job Object gate is unavailable");
24622
24912
  }
24623
- await new Promise((resolve14, reject3) => {
24913
+ await new Promise((resolve15, reject3) => {
24624
24914
  watchedChild.stdin.write(windowsContainmentGate(containmentGateNonce), (error52) => {
24625
24915
  if (error52) reject3(error52);
24626
- else resolve14();
24916
+ else resolve15();
24627
24917
  });
24628
24918
  });
24629
24919
  }
@@ -24890,6 +25180,20 @@ async function superviseHost(options = {}) {
24890
25180
  import { hostname as hostname3 } from "node:os";
24891
25181
 
24892
25182
  // src/browser/adapter.ts
25183
+ var BROWSER_LOGIN_ORIGIN_CHANGED = "No sign-in was attempted: the browser left the approved sign-in site. Return to that site and try again.";
25184
+ function requireBrowserLoginOrigin(currentUrl, loginUrl) {
25185
+ try {
25186
+ const current = new URL(currentUrl);
25187
+ const expected = new URL(loginUrl);
25188
+ const currentIsWeb = current.protocol === "https:" || current.protocol === "http:";
25189
+ const expectedIsWeb = expected.protocol === "https:" || expected.protocol === "http:";
25190
+ if (currentIsWeb && expectedIsWeb && current.origin === expected.origin) {
25191
+ return expected.origin;
25192
+ }
25193
+ } catch {
25194
+ }
25195
+ throw new Error(BROWSER_LOGIN_ORIGIN_CHANGED);
25196
+ }
24893
25197
  var DEMO_FRAME_JPEG_BASE64 = "/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAIAAgDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3+iiigD//2Q==";
24894
25198
  function createDemoBrowserAdapterFactory() {
24895
25199
  return {
@@ -25039,6 +25343,7 @@ function createDemoBrowserAdapterFactory() {
25039
25343
  return frame();
25040
25344
  },
25041
25345
  async fillLogin(credential) {
25346
+ requireBrowserLoginOrigin(active().url, credential.loginUrl);
25042
25347
  const landedUrl = new URL("/account", credential.loginUrl).toString();
25043
25348
  const tab = active();
25044
25349
  tab.url = landedUrl;
@@ -25061,9 +25366,9 @@ function createDemoBrowserAdapterFactory() {
25061
25366
  }
25062
25367
 
25063
25368
  // src/browser/manager.ts
25064
- import { mkdir as mkdir4 } from "node:fs/promises";
25369
+ import { lstat as lstat4, mkdir as mkdir4, open as open4, opendir, readFile as readFile5, rename as rename3, rm as rm4 } from "node:fs/promises";
25065
25370
  import { homedir as homedir2 } from "node:os";
25066
- import { join as join7 } from "node:path";
25371
+ import { dirname as dirname4, join as join7, resolve as resolve4 } from "node:path";
25067
25372
  var SAFE_SEGMENT = /^[A-Za-z0-9_-]{1,200}$/;
25068
25373
  var FRAME_MIN_INTERVAL_MS = 100;
25069
25374
  var IDLE_TIMEOUT_MS = 15 * 6e4;
@@ -25071,18 +25376,34 @@ var BrowserManager = class {
25071
25376
  constructor(opts) {
25072
25377
  this.opts = opts;
25073
25378
  this.profileRoot = opts.profileRoot ?? join7(homedir2(), ".zixt", "browser-profiles");
25379
+ this.profileStateRoot = join7(this.profileRoot, ".profile-state");
25074
25380
  this.viewport = opts.viewport ?? BROWSER_DEFAULT_VIEWPORT;
25075
25381
  this.idleTimeoutMs = opts.idleTimeoutMs ?? IDLE_TIMEOUT_MS;
25076
25382
  this.frameMinIntervalMs = opts.frameMinIntervalMs ?? FRAME_MIN_INTERVAL_MS;
25383
+ this.profileInventoryLimit = Math.max(
25384
+ 1,
25385
+ Math.min(
25386
+ BROWSER_PROFILE_INVENTORY_PAGE_SIZE,
25387
+ Math.trunc(opts.profileInventoryLimit ?? BROWSER_PROFILE_INVENTORY_PAGE_SIZE)
25388
+ )
25389
+ );
25077
25390
  }
25078
25391
  sessions = /* @__PURE__ */ new Map();
25392
+ /**
25393
+ * Fail closed after lifecycle authorization could not be durably checked or
25394
+ * written. Non-browser runner work may continue, but no local Browser can
25395
+ * open until a later trusted revision is successfully authorized.
25396
+ */
25397
+ deniedAuthorizations = /* @__PURE__ */ new Map();
25079
25398
  /** Serializes open/close per teammate: one profile allows one live browser. */
25080
25399
  locks = /* @__PURE__ */ new Map();
25081
25400
  events = null;
25082
25401
  profileRoot;
25402
+ profileStateRoot;
25083
25403
  viewport;
25084
25404
  idleTimeoutMs;
25085
25405
  frameMinIntervalMs;
25406
+ profileInventoryLimit;
25086
25407
  /**
25087
25408
  * Wired once by the host client; replaces any prior socket generation's
25088
25409
  * sink. Attaching re-announces every live session, because the cloud keeps
@@ -25099,6 +25420,31 @@ var BrowserManager = class {
25099
25420
  capability() {
25100
25421
  return this.opts.factory.capability();
25101
25422
  }
25423
+ /**
25424
+ * Page a path-validated migration inventory without holding an unbounded
25425
+ * directory in memory. A final `complete: true` page is emitted even when
25426
+ * the directory contains an exact multiple of the page size.
25427
+ */
25428
+ async *profileInventoryPages() {
25429
+ await this.ensureOwnedDirectory(this.profileRoot);
25430
+ const agentIds = [];
25431
+ const directory = await opendir(this.profileRoot);
25432
+ for await (const entry of directory) {
25433
+ if (!entry.isDirectory() || entry.isSymbolicLink() || !AgentId.safeParse(entry.name).success) {
25434
+ continue;
25435
+ }
25436
+ if (agentIds.length === this.profileInventoryLimit) {
25437
+ yield { agentIds: agentIds.splice(0).sort(), complete: false };
25438
+ }
25439
+ agentIds.push(entry.name);
25440
+ }
25441
+ yield { agentIds: agentIds.sort(), complete: true };
25442
+ }
25443
+ /** First bounded page retained for embedders that have not adopted continuations. */
25444
+ async profileInventory() {
25445
+ const first = await this.profileInventoryPages().next();
25446
+ return first.value ?? { agentIds: [], complete: true };
25447
+ }
25102
25448
  sessionState(agentId) {
25103
25449
  const session = this.sessions.get(agentId);
25104
25450
  return session && !session.closed ? this.stateOf(session) : null;
@@ -25114,6 +25460,122 @@ var BrowserManager = class {
25114
25460
  );
25115
25461
  return next;
25116
25462
  }
25463
+ validateAgentId(agentId) {
25464
+ if (!SAFE_SEGMENT.test(agentId) || !AgentId.safeParse(agentId).success) {
25465
+ throw new Error("invalid agent id for a browser profile path");
25466
+ }
25467
+ }
25468
+ exactChild(root, child) {
25469
+ const canonicalRoot = resolve4(root);
25470
+ const target = resolve4(canonicalRoot, child);
25471
+ if (dirname4(target) !== canonicalRoot) {
25472
+ throw new Error("browser profile path escaped its owned root");
25473
+ }
25474
+ return target;
25475
+ }
25476
+ async ensureOwnedDirectory(path) {
25477
+ await mkdir4(path, { recursive: true, mode: 448 });
25478
+ const stat3 = await lstat4(path);
25479
+ if (!stat3.isDirectory() || stat3.isSymbolicLink()) {
25480
+ throw new Error("browser profile root must be an owned directory, not a symbolic link");
25481
+ }
25482
+ }
25483
+ /**
25484
+ * POSIX needs directory fsync for a rename/unlink to survive sudden power
25485
+ * loss. Windows does not support opening directories this way, so only its
25486
+ * unsupported-operation errors are tolerated; file fsync still remains
25487
+ * mandatory on every platform.
25488
+ */
25489
+ async syncDirectory(path) {
25490
+ try {
25491
+ const directory = await open4(path, "r");
25492
+ try {
25493
+ await directory.sync();
25494
+ } finally {
25495
+ await directory.close();
25496
+ }
25497
+ } catch (error52) {
25498
+ const code = error52.code;
25499
+ if (process.platform === "win32" && (code === "EISDIR" || code === "EACCES" || code === "EPERM" || code === "EINVAL" || code === "ENOTSUP")) {
25500
+ return;
25501
+ }
25502
+ throw error52;
25503
+ }
25504
+ }
25505
+ profilePath(agentId) {
25506
+ return this.exactChild(this.profileRoot, agentId);
25507
+ }
25508
+ statePath(agentId) {
25509
+ return this.exactChild(this.profileStateRoot, `${agentId}.json`);
25510
+ }
25511
+ async readProfileState(agentId) {
25512
+ try {
25513
+ const raw = JSON.parse(await readFile5(this.statePath(agentId), "utf8"));
25514
+ if (typeof raw !== "object" || raw === null || !Number.isInteger(raw.revision) || Number(raw.revision) < 0 || !["allowed", "purged"].includes(String(raw.state))) {
25515
+ throw new Error("browser profile lifecycle marker is invalid");
25516
+ }
25517
+ return raw;
25518
+ } catch (error52) {
25519
+ if (error52.code === "ENOENT") return null;
25520
+ throw error52;
25521
+ }
25522
+ }
25523
+ async writeProfileState(agentId, state) {
25524
+ await this.ensureOwnedDirectory(this.profileRoot);
25525
+ await this.ensureOwnedDirectory(this.profileStateRoot);
25526
+ const destination = this.statePath(agentId);
25527
+ const temporary = this.exactChild(
25528
+ this.profileStateRoot,
25529
+ `${agentId}.${crypto.randomUUID()}.tmp`
25530
+ );
25531
+ try {
25532
+ const marker = await open4(temporary, "wx+", 384);
25533
+ try {
25534
+ await marker.writeFile(JSON.stringify(state), "utf8");
25535
+ await marker.sync();
25536
+ } finally {
25537
+ await marker.close();
25538
+ }
25539
+ await rename3(temporary, destination);
25540
+ await this.syncDirectory(this.profileStateRoot);
25541
+ await this.syncDirectory(this.profileRoot);
25542
+ } catch (error52) {
25543
+ await rm4(temporary, { force: true }).catch(() => {
25544
+ });
25545
+ throw error52;
25546
+ }
25547
+ }
25548
+ /** Trusted cloud lifecycle authorization after an explicit restore/current assignment. */
25549
+ async authorizeProfile(agentId, revision) {
25550
+ this.validateAgentId(agentId);
25551
+ if (!Number.isInteger(revision) || revision < 0) {
25552
+ throw new Error("invalid browser profile lifecycle revision");
25553
+ }
25554
+ try {
25555
+ await this.withLock(agentId, async () => {
25556
+ const current = await this.readProfileState(agentId);
25557
+ if (current && current.revision > revision) {
25558
+ throw new Error("stale browser profile authorization was refused");
25559
+ }
25560
+ if (current?.state === "purged" && current.revision === revision) {
25561
+ throw new Error("this browser profile remains retired until the AI teammate is restored");
25562
+ }
25563
+ if (!current || current.revision < revision || current.state !== "allowed") {
25564
+ await this.writeProfileState(agentId, { revision, state: "allowed" });
25565
+ }
25566
+ });
25567
+ const deniedRevision = this.deniedAuthorizations.get(agentId);
25568
+ if (deniedRevision !== void 0 && revision >= deniedRevision) {
25569
+ this.deniedAuthorizations.delete(agentId);
25570
+ }
25571
+ } catch (error52) {
25572
+ this.deniedAuthorizations.set(
25573
+ agentId,
25574
+ Math.max(revision, this.deniedAuthorizations.get(agentId) ?? revision)
25575
+ );
25576
+ throw error52;
25577
+ }
25578
+ }
25117
25579
  /**
25118
25580
  * Ensure a live session for this teammate. An existing session is adopted
25119
25581
  * as-is (the cloud-minted id loses to the live one — the cloud reconciles
@@ -25121,16 +25583,39 @@ var BrowserManager = class {
25121
25583
  * with the install remedy.
25122
25584
  */
25123
25585
  ensure(agentId, browserSessionId) {
25124
- if (!SAFE_SEGMENT.test(agentId)) {
25125
- return Promise.reject(new Error("invalid agent id for a browser profile path"));
25586
+ try {
25587
+ this.validateAgentId(agentId);
25588
+ } catch (error52) {
25589
+ return Promise.reject(error52);
25126
25590
  }
25127
25591
  return this.withLock(agentId, async () => {
25592
+ if (this.deniedAuthorizations.has(agentId)) {
25593
+ throw new Error(
25594
+ "this browser profile is unavailable until its lifecycle authorization succeeds"
25595
+ );
25596
+ }
25597
+ const lifecycle = await this.readProfileState(agentId);
25598
+ if (lifecycle?.state === "purged") {
25599
+ throw new Error("this browser profile was retired and cannot be recreated locally");
25600
+ }
25128
25601
  const existing = this.sessions.get(agentId);
25602
+ if (existing?.closeFailed) {
25603
+ throw new Error("the previous browser termination is not yet confirmed");
25604
+ }
25129
25605
  if (existing && !existing.closed) {
25130
25606
  this.markActivity(agentId);
25131
25607
  return this.stateOf(existing);
25132
25608
  }
25133
- const profileDir = join7(this.profileRoot, agentId);
25609
+ await this.ensureOwnedDirectory(this.profileRoot);
25610
+ const profileDir = this.profilePath(agentId);
25611
+ try {
25612
+ const existingProfile = await lstat4(profileDir);
25613
+ if (existingProfile.isSymbolicLink() || !existingProfile.isDirectory()) {
25614
+ throw new Error("browser profile path is not an owned directory");
25615
+ }
25616
+ } catch (error52) {
25617
+ if (error52.code !== "ENOENT") throw error52;
25618
+ }
25134
25619
  await mkdir4(profileDir, { recursive: true, mode: 448 });
25135
25620
  const adapter = await this.opts.factory.open({
25136
25621
  agentId,
@@ -25151,7 +25636,8 @@ var BrowserManager = class {
25151
25636
  framesPaused: false,
25152
25637
  inputQueue: Promise.resolve(),
25153
25638
  resizeGeneration: 0,
25154
- closed: false
25639
+ closed: false,
25640
+ closeFailed: false
25155
25641
  };
25156
25642
  this.sessions.set(agentId, session);
25157
25643
  adapter.onStateChanged(() => {
@@ -25163,7 +25649,8 @@ var BrowserManager = class {
25163
25649
  });
25164
25650
  }
25165
25651
  /** browser.open handler: ensure, then announce with the request correlation. */
25166
- async open(agentId, browserSessionId, _viewport, openRequestId) {
25652
+ async open(agentId, browserSessionId, _viewport, openRequestId, profileRevision) {
25653
+ if (profileRevision !== void 0) await this.authorizeProfile(agentId, profileRevision);
25167
25654
  const state = await this.ensure(agentId, browserSessionId);
25168
25655
  this.events?.state(state, openRequestId);
25169
25656
  return state;
@@ -25173,18 +25660,45 @@ var BrowserManager = class {
25173
25660
  }
25174
25661
  async closeLocked(agentId, reason) {
25175
25662
  const session = this.sessions.get(agentId);
25176
- if (!session || session.closed) return;
25663
+ if (!session || session.closed && !session.closeFailed) return;
25177
25664
  session.closed = true;
25665
+ session.closeFailed = false;
25178
25666
  if (session.frameTimer) clearTimeout(session.frameTimer);
25179
25667
  if (session.idleTimer) clearTimeout(session.idleTimer);
25180
- this.sessions.delete(agentId);
25181
25668
  try {
25182
- await session.adapter.setFrameSink(null);
25669
+ await session.adapter.setFrameSink(null).catch(() => {
25670
+ });
25183
25671
  await session.adapter.close();
25184
- } catch {
25672
+ } catch (error52) {
25673
+ session.closeFailed = true;
25674
+ throw error52;
25185
25675
  }
25676
+ this.sessions.delete(agentId);
25186
25677
  this.events?.ended(session.browserSessionId, agentId, reason);
25187
25678
  }
25679
+ /** Persist retirement, close a live Chromium, then delete only this exact profile. */
25680
+ purge(agentId, purgeId, profileRevision) {
25681
+ try {
25682
+ this.validateAgentId(agentId);
25683
+ } catch (error52) {
25684
+ return Promise.reject(error52);
25685
+ }
25686
+ if (!Number.isInteger(profileRevision) || profileRevision < 1) {
25687
+ return Promise.reject(new Error("invalid browser profile purge revision"));
25688
+ }
25689
+ return this.withLock(agentId, async () => {
25690
+ const current = await this.readProfileState(agentId);
25691
+ if (current && current.revision > profileRevision) return;
25692
+ await this.writeProfileState(agentId, {
25693
+ revision: profileRevision,
25694
+ state: "purged",
25695
+ purgeId
25696
+ });
25697
+ await this.closeLocked(agentId, "stopped");
25698
+ await rm4(this.profilePath(agentId), { recursive: true, force: true, maxRetries: 3 });
25699
+ await this.syncDirectory(this.profileRoot);
25700
+ });
25701
+ }
25188
25702
  async closeAll(reason) {
25189
25703
  await Promise.allSettled(
25190
25704
  [...this.sessions.keys()].map((agentId) => this.close(agentId, reason))
@@ -25224,8 +25738,8 @@ var BrowserManager = class {
25224
25738
  return this.enqueue(session, async () => {
25225
25739
  if (resizeGeneration !== null && resizeGeneration !== session.resizeGeneration) return;
25226
25740
  await session.adapter.command(command);
25227
- const changesCaptureTarget = command.action === "activateTab" || command.action === "newTab" || command.action === "closeTab";
25228
- if (changesCaptureTarget && session.viewerCount > 0) {
25741
+ const needsImmediateFrame = command.action === "resize" || command.action === "activateTab" || command.action === "newTab" || command.action === "closeTab";
25742
+ if (needsImmediateFrame && session.viewerCount > 0) {
25229
25743
  try {
25230
25744
  this.enqueueFrame(session, await session.adapter.screenshot());
25231
25745
  } catch {
@@ -25331,7 +25845,8 @@ var BrowserManager = class {
25331
25845
  this.armIdleTimer(session);
25332
25846
  return;
25333
25847
  }
25334
- void this.close(session.agentId, "idle_timeout");
25848
+ void this.close(session.agentId, "idle_timeout").catch(() => {
25849
+ });
25335
25850
  }, this.idleTimeoutMs);
25336
25851
  session.idleTimer.unref?.();
25337
25852
  }
@@ -25348,12 +25863,13 @@ async function loadPlaywright() {
25348
25863
  return import("playwright-core");
25349
25864
  }
25350
25865
  var KEY_ALIASES = { " ": "Space" };
25351
- function createPlaywrightBrowserAdapterFactory() {
25866
+ function createPlaywrightBrowserAdapterFactory(dependencies = {}) {
25867
+ const load = dependencies.loadPlaywright ?? loadPlaywright;
25352
25868
  return {
25353
25869
  kind: "playwright",
25354
25870
  async capability() {
25355
25871
  try {
25356
- const playwright = await loadPlaywright();
25872
+ const playwright = await load();
25357
25873
  const executable = playwright.chromium.executablePath();
25358
25874
  if (!executable) return { status: "unavailable", error: BROWSER_INSTALL_REMEDY };
25359
25875
  await access3(executable);
@@ -25363,7 +25879,7 @@ function createPlaywrightBrowserAdapterFactory() {
25363
25879
  }
25364
25880
  },
25365
25881
  async open({ profileDir, viewport }) {
25366
- const playwright = await loadPlaywright();
25882
+ const playwright = await load();
25367
25883
  const context = await playwright.chromium.launchPersistentContext(profileDir, {
25368
25884
  headless: true,
25369
25885
  viewport,
@@ -25732,8 +26248,10 @@ function createPlaywrightBrowserAdapterFactory() {
25732
26248
  async fillLogin(credential) {
25733
26249
  const tab = requireActive();
25734
26250
  const { page: page2 } = tab;
26251
+ const expectedOrigin = requireBrowserLoginOrigin(safeUrl(page2), credential.loginUrl);
25735
26252
  const password = page2.locator('input[type="password"]:visible').first();
25736
26253
  await password.waitFor({ state: "visible", timeout: ACTION_TIMEOUT_MS });
26254
+ requireBrowserLoginOrigin(safeUrl(page2), credential.loginUrl);
25737
26255
  const username = page2.locator(
25738
26256
  [
25739
26257
  'input[autocomplete="username"]:visible',
@@ -25743,14 +26261,66 @@ function createPlaywrightBrowserAdapterFactory() {
25743
26261
  'input[type="text"]:visible'
25744
26262
  ].join(", ")
25745
26263
  ).first();
25746
- if (await username.count() > 0) {
25747
- try {
25748
- await username.fill(credential.username, { timeout: ACTION_TIMEOUT_MS });
25749
- } catch {
26264
+ const [passwordInputHandle] = await password.elementHandles();
26265
+ if (!passwordInputHandle) throw new Error("the password field disappeared");
26266
+ const [usernameInputHandle] = await username.elementHandles();
26267
+ const originSentinel = "ZIXT_BROWSER_LOGIN_ORIGIN_CHANGED";
26268
+ try {
26269
+ await passwordInputHandle.evaluate(
26270
+ (node, values) => {
26271
+ const browser = globalThis;
26272
+ const passwordInput = node;
26273
+ if (passwordInput.tagName !== "INPUT" || browser.location.origin !== values.origin) {
26274
+ throw new Error(values.originSentinel);
26275
+ }
26276
+ const visible = (input) => {
26277
+ const style = browser.getComputedStyle(input);
26278
+ const bounds = input.getBoundingClientRect();
26279
+ return input.isConnected && !input.disabled && style.display !== "none" && style.visibility !== "hidden" && bounds.width > 0 && bounds.height > 0;
26280
+ };
26281
+ const write = (input, value) => {
26282
+ input.focus();
26283
+ const setter = Object.getOwnPropertyDescriptor(
26284
+ browser.HTMLInputElement.prototype,
26285
+ "value"
26286
+ )?.set;
26287
+ if (setter) setter.call(input, value);
26288
+ else input.value = value;
26289
+ input.dispatchEvent(
26290
+ new browser.Event("input", { bubbles: true, composed: true })
26291
+ );
26292
+ input.dispatchEvent(new browser.Event("change", { bubbles: true }));
26293
+ };
26294
+ const usernameInput = values.usernameInput;
26295
+ if (usernameInput && usernameInput.ownerDocument === passwordInput.ownerDocument && visible(usernameInput)) {
26296
+ write(usernameInput, values.username);
26297
+ }
26298
+ if (!passwordInput.isConnected || browser.location.origin !== values.origin) {
26299
+ throw new Error(values.originSentinel);
26300
+ }
26301
+ write(passwordInput, values.password);
26302
+ },
26303
+ {
26304
+ usernameInput: usernameInputHandle ?? null,
26305
+ origin: expectedOrigin,
26306
+ originSentinel,
26307
+ username: credential.username,
26308
+ password: credential.password
26309
+ }
26310
+ );
26311
+ requireBrowserLoginOrigin(safeUrl(page2), credential.loginUrl);
26312
+ await passwordInputHandle.press("Enter");
26313
+ } catch (error52) {
26314
+ if (String(error52).includes(originSentinel)) {
26315
+ throw new Error(BROWSER_LOGIN_ORIGIN_CHANGED);
25750
26316
  }
26317
+ throw error52;
26318
+ } finally {
26319
+ await Promise.allSettled([
26320
+ passwordInputHandle.dispose(),
26321
+ ...usernameInputHandle ? [usernameInputHandle.dispose()] : []
26322
+ ]);
25751
26323
  }
25752
- await password.fill(credential.password, { timeout: ACTION_TIMEOUT_MS });
25753
- await password.press("Enter");
25754
26324
  try {
25755
26325
  await page2.waitForLoadState("load", { timeout: 15e3 });
25756
26326
  } catch {
@@ -25796,10 +26366,7 @@ function createPlaywrightBrowserAdapterFactory() {
25796
26366
  async close() {
25797
26367
  closed = true;
25798
26368
  for (const tab of tabs) await detachSession(tab);
25799
- try {
25800
- await context.close();
25801
- } catch {
25802
- }
26369
+ await context.close();
25803
26370
  }
25804
26371
  };
25805
26372
  }
@@ -25809,9 +26376,9 @@ function createPlaywrightBrowserAdapterFactory() {
25809
26376
  // src/runners/cli-runner.ts
25810
26377
  import { spawn as spawn8 } from "node:child_process";
25811
26378
  import { randomUUID as randomUUID10 } from "node:crypto";
25812
- import { lstat as lstat9, mkdir as mkdir10, realpath as realpath7 } from "node:fs/promises";
26379
+ import { lstat as lstat10, mkdir as mkdir10, realpath as realpath7 } from "node:fs/promises";
25813
26380
  import { homedir as homedir4 } from "node:os";
25814
- import { dirname as dirname6, isAbsolute as isAbsolute13, join as join14, resolve as resolve7 } from "node:path";
26381
+ import { dirname as dirname7, isAbsolute as isAbsolute13, join as join14, resolve as resolve8 } from "node:path";
25815
26382
 
25816
26383
  // src/tool-packs/browser/tool-definitions.ts
25817
26384
  function definition(name, description, properties, required2 = []) {
@@ -26033,9 +26600,13 @@ function createBrowserToolPack(deps) {
26033
26600
  });
26034
26601
  const credential = await deps.requestWebLogin(credentialName, origin);
26035
26602
  try {
26603
+ requireBrowserLoginOrigin(tool.state().url, credential.loginUrl);
26036
26604
  const { landedUrl } = await tool.fillLogin(credential);
26037
26605
  return { ok: true, result: { loggedIn: true, landedUrl } };
26038
- } catch {
26606
+ } catch (error52) {
26607
+ if (error52 instanceof Error && error52.message === BROWSER_LOGIN_ORIGIN_CHANGED) {
26608
+ return { ok: false, error: BROWSER_LOGIN_ORIGIN_CHANGED };
26609
+ }
26039
26610
  return {
26040
26611
  ok: false,
26041
26612
  error: "no login form was found or the sign-in did not complete; browser_read the page and try again, or ask a human to take over from the Browser panel"
@@ -28500,13 +29071,13 @@ function createGithubPushOrchestrator(input) {
28500
29071
  // src/tool-packs/github/git-bridge.ts
28501
29072
  import { spawn as spawn5 } from "node:child_process";
28502
29073
  import { randomUUID as randomUUID7 } from "node:crypto";
28503
- import { chmod as chmod3, lstat as lstat5, mkdir as mkdir5, realpath as realpath3, rm as rm4 } from "node:fs/promises";
28504
- import { dirname as dirname4, isAbsolute as isAbsolute8, join as join9, relative as relative4 } from "node:path";
29074
+ import { chmod as chmod3, lstat as lstat6, mkdir as mkdir5, realpath as realpath3, rm as rm5 } from "node:fs/promises";
29075
+ import { dirname as dirname5, isAbsolute as isAbsolute8, join as join9, relative as relative4 } from "node:path";
28505
29076
 
28506
29077
  // src/tool-packs/github/git-credential-broker.ts
28507
29078
  import { createServer } from "node:http";
28508
29079
  import { randomBytes, randomUUID as randomUUID6, timingSafeEqual } from "node:crypto";
28509
- import { chmod as chmod2, lstat as lstat4, realpath as realpath2, writeFile } from "node:fs/promises";
29080
+ import { chmod as chmod2, lstat as lstat5, realpath as realpath2, writeFile } from "node:fs/promises";
28510
29081
  import { isAbsolute as isAbsolute7, join as join8, relative as relative3 } from "node:path";
28511
29082
  var MAX_REQUEST_BYTES = 16 * 1024;
28512
29083
  var FILE_MODE = 384;
@@ -28631,7 +29202,7 @@ async function createGithubGitCredentialBroker(input) {
28631
29202
  throw new Error("GitHub credential authority has expired");
28632
29203
  }
28633
29204
  assertRepositoryFullName(input.repositoryFullName);
28634
- const rootEntry = await lstat4(input.runArtifactsRoot);
29205
+ const rootEntry = await lstat5(input.runArtifactsRoot);
28635
29206
  if (!rootEntry.isDirectory() || rootEntry.isSymbolicLink()) {
28636
29207
  throw new Error("Git credential broker requires a private real run directory");
28637
29208
  }
@@ -28742,7 +29313,7 @@ function assertBelow(parent, child, label) {
28742
29313
  void label;
28743
29314
  }
28744
29315
  async function requireRealDirectory(path, label) {
28745
- const entry = await lstat5(path).catch(() => null);
29316
+ const entry = await lstat6(path).catch(() => null);
28746
29317
  if (!entry || !entry.isDirectory() || entry.isSymbolicLink()) {
28747
29318
  void label;
28748
29319
  throw new GithubGitProcessError("invalid_input");
@@ -28752,9 +29323,9 @@ async function requireRealDirectory(path, label) {
28752
29323
  async function validateTokenlessPaths(command) {
28753
29324
  if (command.kind === "clone-from-bridge") {
28754
29325
  if (!isAbsolute8(command.destination)) throw new GithubGitProcessError("invalid_input");
28755
- const parent = await requireRealDirectory(dirname4(command.destination), "clone parent");
29326
+ const parent = await requireRealDirectory(dirname5(command.destination), "clone parent");
28756
29327
  assertBelow(parent, command.destination, "clone destination");
28757
- const destination = await lstat5(command.destination).catch((error52) => {
29328
+ const destination = await lstat6(command.destination).catch((error52) => {
28758
29329
  if (error52.code === "ENOENT") return null;
28759
29330
  throw error52;
28760
29331
  });
@@ -28862,8 +29433,8 @@ async function runGit(input, args, env) {
28862
29433
  let settled = false;
28863
29434
  let stopping = false;
28864
29435
  let resolveExited;
28865
- const exited = new Promise((resolve14) => {
28866
- resolveExited = resolve14;
29436
+ const exited = new Promise((resolve15) => {
29437
+ resolveExited = resolve15;
28867
29438
  });
28868
29439
  child.once("exit", resolveExited);
28869
29440
  const cleanup = () => {
@@ -29059,7 +29630,7 @@ function createGithubGitBridge(input) {
29059
29630
  const real = await requireRealDirectory(path, "git bridge");
29060
29631
  assertBelow(current.bridges, real, "git bridge");
29061
29632
  const hooks = join9(real, "hooks");
29062
- await rm4(hooks, { recursive: true, force: true });
29633
+ await rm5(hooks, { recursive: true, force: true });
29063
29634
  await mkdir5(hooks, { mode: DIRECTORY_MODE });
29064
29635
  await chmod3(hooks, DIRECTORY_MODE);
29065
29636
  const config2 = join9(real, "config");
@@ -29067,7 +29638,7 @@ function createGithubGitBridge(input) {
29067
29638
  active.add(real);
29068
29639
  return real;
29069
29640
  } catch (error52) {
29070
- await rm4(path, { recursive: true, force: true }).catch(() => {
29641
+ await rm5(path, { recursive: true, force: true }).catch(() => {
29071
29642
  });
29072
29643
  throw error52;
29073
29644
  }
@@ -29161,7 +29732,7 @@ function createGithubGitBridge(input) {
29161
29732
  },
29162
29733
  async destroyPrivateBridge(path) {
29163
29734
  const bridge = await requireBridge(path);
29164
- await rm4(bridge, { recursive: true, force: true });
29735
+ await rm5(bridge, { recursive: true, force: true });
29165
29736
  active.delete(bridge);
29166
29737
  credentialed2.delete(bridge);
29167
29738
  },
@@ -29169,7 +29740,7 @@ function createGithubGitBridge(input) {
29169
29740
  if (closed) return;
29170
29741
  closed = true;
29171
29742
  const paths = [...active];
29172
- await Promise.all(paths.map((path) => rm4(path, { recursive: true, force: true })));
29743
+ await Promise.all(paths.map((path) => rm5(path, { recursive: true, force: true })));
29173
29744
  active.clear();
29174
29745
  credentialed2.clear();
29175
29746
  }
@@ -29510,8 +30081,8 @@ function createRepositoryTools(runtime) {
29510
30081
 
29511
30082
  // src/tool-packs/github/workspace.ts
29512
30083
  import { randomUUID as randomUUID8 } from "node:crypto";
29513
- import { chmod as chmod4, lstat as lstat6, mkdir as mkdir6, readFile as readFile5, realpath as realpath4, rename as rename3, rm as rm5, writeFile as writeFile2 } from "node:fs/promises";
29514
- import { isAbsolute as isAbsolute9, join as join10, relative as relative5, resolve as resolve4 } from "node:path";
30084
+ import { chmod as chmod4, lstat as lstat7, mkdir as mkdir6, readFile as readFile6, realpath as realpath4, rename as rename4, rm as rm6, writeFile as writeFile2 } from "node:fs/promises";
30085
+ import { isAbsolute as isAbsolute9, join as join10, relative as relative5, resolve as resolve5 } from "node:path";
29515
30086
  var SAFE_LOCAL_ID = /^[A-Za-z0-9_-]{1,200}$/;
29516
30087
  var FULL_NAME2 = /^[A-Za-z0-9_.-]{1,100}\/[A-Za-z0-9_.-]{1,100}$/;
29517
30088
  var DIRECTORY_MODE2 = 448;
@@ -29534,10 +30105,10 @@ function assertBelow2(parent, child, label) {
29534
30105
  }
29535
30106
  }
29536
30107
  function samePath(left, right) {
29537
- return process.platform === "win32" ? resolve4(left).toLowerCase() === resolve4(right).toLowerCase() : resolve4(left) === resolve4(right);
30108
+ return process.platform === "win32" ? resolve5(left).toLowerCase() === resolve5(right).toLowerCase() : resolve5(left) === resolve5(right);
29538
30109
  }
29539
30110
  async function requireRealDirectory2(path, label) {
29540
- const entry = await lstat6(path).catch(() => null);
30111
+ const entry = await lstat7(path).catch(() => null);
29541
30112
  if (!entry || !entry.isDirectory() || entry.isSymbolicLink()) {
29542
30113
  throw new Error(`${label} must be a real directory, not a symbolic link or junction`);
29543
30114
  }
@@ -29574,7 +30145,7 @@ function parseMetadata(text) {
29574
30145
  }
29575
30146
  async function pathExists(path) {
29576
30147
  try {
29577
- await lstat6(path);
30148
+ await lstat7(path);
29578
30149
  return true;
29579
30150
  } catch (error52) {
29580
30151
  if (error52.code === "ENOENT") return false;
@@ -29636,11 +30207,11 @@ async function createGithubWorkspaceService(input) {
29636
30207
  if (!await pathExists(destination) || !await pathExists(metadataPath)) {
29637
30208
  throw new Error("GitHub repository workspace has not been prepared");
29638
30209
  }
29639
- const metadataEntry = await lstat6(metadataPath);
30210
+ const metadataEntry = await lstat7(metadataPath);
29640
30211
  if (!metadataEntry.isFile() || metadataEntry.isSymbolicLink()) {
29641
30212
  throw new Error("GitHub workspace metadata is invalid");
29642
30213
  }
29643
- const metadata = parseMetadata(await readFile5(metadataPath, "utf8"));
30214
+ const metadata = parseMetadata(await readFile6(metadataPath, "utf8"));
29644
30215
  if (metadata.repositoryId !== parsed.data || metadata.fullName !== repository.fullName || !samePath(metadata.path, destination)) {
29645
30216
  throw new Error("GitHub workspace metadata does not match this repository");
29646
30217
  }
@@ -29690,15 +30261,15 @@ async function createGithubWorkspaceService(input) {
29690
30261
  });
29691
30262
  await chmod4(metadataTemporary, FILE_MODE2);
29692
30263
  try {
29693
- await rename3(temporaryReal, destination);
30264
+ await rename4(temporaryReal, destination);
29694
30265
  try {
29695
- await rename3(metadataTemporary, metadataPath);
30266
+ await rename4(metadataTemporary, metadataPath);
29696
30267
  } catch (error52) {
29697
- await rm5(destination, { recursive: true, force: true });
30268
+ await rm6(destination, { recursive: true, force: true });
29698
30269
  throw error52;
29699
30270
  }
29700
30271
  } finally {
29701
- await rm5(metadataTemporary, { force: true }).catch(() => {
30272
+ await rm6(metadataTemporary, { force: true }).catch(() => {
29702
30273
  });
29703
30274
  }
29704
30275
  const path = await requireRealDirectory2(destination, "GitHub repository");
@@ -29710,7 +30281,7 @@ async function createGithubWorkspaceService(input) {
29710
30281
  headSha
29711
30282
  };
29712
30283
  } finally {
29713
- await rm5(temporary, { recursive: true, force: true }).catch(() => {
30284
+ await rm6(temporary, { recursive: true, force: true }).catch(() => {
29714
30285
  });
29715
30286
  }
29716
30287
  };
@@ -29738,11 +30309,11 @@ async function createGithubWorkspaceService(input) {
29738
30309
  expiresAt: authority.expiresAt
29739
30310
  });
29740
30311
  }
29741
- const metadataEntry = await lstat6(metadataPath);
30312
+ const metadataEntry = await lstat7(metadataPath);
29742
30313
  if (!metadataEntry.isFile() || metadataEntry.isSymbolicLink()) {
29743
30314
  throw new Error("GitHub workspace metadata is invalid");
29744
30315
  }
29745
- const metadata = parseMetadata(await readFile5(metadataPath, "utf8"));
30316
+ const metadata = parseMetadata(await readFile6(metadataPath, "utf8"));
29746
30317
  if (metadata.repositoryId !== repository.repositoryId || metadata.fullName !== repository.fullName || !samePath(metadata.path, destination)) {
29747
30318
  throw new Error("GitHub workspace metadata does not match this repository");
29748
30319
  }
@@ -31351,8 +31922,8 @@ var linearToolPackFactory = {
31351
31922
  async create(grant, context) {
31352
31923
  let resolveCancelled;
31353
31924
  let closed = false;
31354
- const cancelled = new Promise((resolve14) => {
31355
- resolveCancelled = resolve14;
31925
+ const cancelled = new Promise((resolve15) => {
31926
+ resolveCancelled = resolve15;
31356
31927
  });
31357
31928
  const cancel = () => {
31358
31929
  if (closed) return;
@@ -31721,6 +32292,21 @@ var TOOLS = [
31721
32292
  required: ["agent_id", "message"]
31722
32293
  }
31723
32294
  },
32295
+ {
32296
+ name: "message_manager",
32297
+ description: "Message the organization Manager. Use this when a human should be informed or asked through the organization\u2019s conversation front door, or when the Manager should decide how to route what you need. This does not address Slack or WhatsApp directly.",
32298
+ inputSchema: {
32299
+ type: "object",
32300
+ properties: {
32301
+ message: {
32302
+ type: "string",
32303
+ description: "A self-contained report, request, or question for the Manager."
32304
+ }
32305
+ },
32306
+ required: ["message"],
32307
+ additionalProperties: false
32308
+ }
32309
+ },
31724
32310
  {
31725
32311
  name: "get_org_config",
31726
32312
  description: "The organization inventory: connected integrations with their ids and endpoints, MCP connections, and the names of credentials that reach your session as environment variables. Read this before deciding how to reach a system nothing wrapped in a tool.",
@@ -31963,6 +32549,8 @@ function opFor(name, args) {
31963
32549
  message: str("message"),
31964
32550
  ...typeof args["task_id"] === "string" && args["task_id"] ? { taskId: args["task_id"] } : {}
31965
32551
  };
32552
+ case "message_manager":
32553
+ return { kind: "manager.message", message: str("message") };
31966
32554
  case "get_org_config":
31967
32555
  return { kind: "org.config" };
31968
32556
  case "read_credential": {
@@ -32021,7 +32609,7 @@ function createAskUserServer() {
32021
32609
  let server;
32022
32610
  let listening;
32023
32611
  function ensureListening() {
32024
- listening ??= new Promise((resolve14, reject3) => {
32612
+ listening ??= new Promise((resolve15, reject3) => {
32025
32613
  server = createServer2((req, res) => {
32026
32614
  res.on("error", () => {
32027
32615
  });
@@ -32037,7 +32625,7 @@ function createAskUserServer() {
32037
32625
  server.on("error", reject3);
32038
32626
  server.listen(0, "127.0.0.1", () => {
32039
32627
  const address = server.address();
32040
- if (address && typeof address === "object") resolve14(address.port);
32628
+ if (address && typeof address === "object") resolve15(address.port);
32041
32629
  else reject3(new Error("ask_user server failed to bind"));
32042
32630
  });
32043
32631
  server.unref();
@@ -32369,7 +32957,7 @@ function buildRunnerEnv(input) {
32369
32957
  // src/runners/github-shell-auth.ts
32370
32958
  import { execFile } from "node:child_process";
32371
32959
  import { randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
32372
- import { chmod as chmod5, lstat as lstat7, mkdir as mkdir8, realpath as realpath5, writeFile as writeFile4 } from "node:fs/promises";
32960
+ import { chmod as chmod5, lstat as lstat8, mkdir as mkdir8, realpath as realpath5, writeFile as writeFile4 } from "node:fs/promises";
32373
32961
  import { createServer as createServer3 } from "node:http";
32374
32962
  import { isAbsolute as isAbsolute11, join as join12, relative as relative6 } from "node:path";
32375
32963
  var MAX_REQUEST_BYTES2 = 16 * 1024;
@@ -32754,7 +33342,7 @@ async function writePrivate(path, content, executable = false) {
32754
33342
  await chmod5(path, executable ? EXECUTABLE_FILE_MODE : PRIVATE_FILE_MODE);
32755
33343
  }
32756
33344
  async function prepareHelpers(input) {
32757
- const rootEntry = await lstat7(input.runRoot);
33345
+ const rootEntry = await lstat8(input.runRoot);
32758
33346
  if (!rootEntry.isDirectory() || rootEntry.isSymbolicLink()) {
32759
33347
  throw new Error("GitHub shell authentication requires a private real run directory");
32760
33348
  }
@@ -33001,7 +33589,7 @@ password=${credential.accessToken}
33001
33589
 
33002
33590
  // src/runners/working-context.ts
33003
33591
  import { spawn as spawn6 } from "node:child_process";
33004
- import { resolve as resolve5 } from "node:path";
33592
+ import { resolve as resolve6 } from "node:path";
33005
33593
  var COMMAND_TIMEOUT_MS = 5e3;
33006
33594
  var OUTPUT_LIMIT_BYTES = 128 * 1024;
33007
33595
  var COMMAND_STOP_TIMEOUT_MS = 2e4;
@@ -33359,8 +33947,8 @@ async function repositoryState(directory, git, env, signal) {
33359
33947
  const pathLines = paths.trim().split(/\r?\n/);
33360
33948
  if (pathLines.length < 3 || !pathLines[0] || !pathLines[1] || !pathLines[2]) return null;
33361
33949
  const root = pathLines[0];
33362
- const gitDirectory = resolve5(directory, pathLines[1]);
33363
- const commonDirectory = resolve5(directory, pathLines[2]);
33950
+ const gitDirectory = resolve6(directory, pathLines[1]);
33951
+ const commonDirectory = resolve6(directory, pathLines[2]);
33364
33952
  const records = status.split(/\0|\r?\n/).filter(Boolean);
33365
33953
  const rawBranch = statusField(records, "branch.head");
33366
33954
  if (!rawBranch || rawBranch.length > 512 || !isSafeSingleLineDisplayText(rawBranch)) return null;
@@ -33507,18 +34095,18 @@ var WorkingContextPullRequestCache = class {
33507
34095
  import { spawn as spawn7 } from "node:child_process";
33508
34096
  import {
33509
34097
  chmod as chmod6,
33510
- lstat as lstat8,
34098
+ lstat as lstat9,
33511
34099
  mkdir as mkdir9,
33512
- open as open4,
34100
+ open as open5,
33513
34101
  readdir as readdir3,
33514
- readFile as readFile6,
34102
+ readFile as readFile7,
33515
34103
  realpath as realpath6,
33516
- rename as rename4,
33517
- rm as rm6,
34104
+ rename as rename5,
34105
+ rm as rm7,
33518
34106
  writeFile as writeFile5
33519
34107
  } from "node:fs/promises";
33520
34108
  import { homedir as homedir3 } from "node:os";
33521
- import { dirname as dirname5, isAbsolute as isAbsolute12, join as join13, relative as relative7, resolve as resolve6, sep as sep3, win32 as win322 } from "node:path";
34109
+ import { dirname as dirname6, isAbsolute as isAbsolute12, join as join13, relative as relative7, resolve as resolve7, sep as sep3, win32 as win322 } from "node:path";
33522
34110
  var SAFE_SEGMENT2 = /^[A-Za-z0-9_-]{1,200}$/;
33523
34111
  var DIRECTORY_MODE4 = 448;
33524
34112
  var FILE_MODE3 = 384;
@@ -33751,7 +34339,7 @@ function assertBelow3(parent, child) {
33751
34339
  if (escapes) throw new Error("run artifact path escapes its private root");
33752
34340
  }
33753
34341
  async function requireRealDirectory3(path, label) {
33754
- const entry = await lstat8(path);
34342
+ const entry = await lstat9(path);
33755
34343
  if (entry.isSymbolicLink()) throw new Error(`${label} must not be a symbolic link`);
33756
34344
  if (!entry.isDirectory()) throw new Error(`${label} must be a directory`);
33757
34345
  return realpath6(path);
@@ -33768,7 +34356,7 @@ async function rejectWindowsSymlinkAncestors(profile, target) {
33768
34356
  for (const segment of path.split("\\").filter(Boolean)) {
33769
34357
  current = win322.join(current, segment);
33770
34358
  try {
33771
- const entry = await lstat8(current);
34359
+ const entry = await lstat9(current);
33772
34360
  if (entry.isSymbolicLink()) {
33773
34361
  throw new Error("run artifact path must not contain symbolic links or junctions");
33774
34362
  }
@@ -33779,16 +34367,16 @@ async function rejectWindowsSymlinkAncestors(profile, target) {
33779
34367
  }
33780
34368
  }
33781
34369
  async function prepareRoot(root) {
33782
- const absolute = resolve6(root);
34370
+ const absolute = resolve7(root);
33783
34371
  let realProfile;
33784
34372
  if (process.platform === "win32") {
33785
- const profile = resolve6(homedir3());
34373
+ const profile = resolve7(homedir3());
33786
34374
  assertWindowsProfileBoundary(profile, absolute);
33787
34375
  await rejectWindowsSymlinkAncestors(profile, absolute);
33788
34376
  realProfile = await realpath6(profile);
33789
34377
  }
33790
34378
  try {
33791
- await lstat8(absolute);
34379
+ await lstat9(absolute);
33792
34380
  } catch (error52) {
33793
34381
  if (!isMissing(error52)) throw error52;
33794
34382
  await mkdir9(absolute, { recursive: true, mode: DIRECTORY_MODE4 });
@@ -33802,7 +34390,7 @@ async function prepareAgentRoot(root, agentId) {
33802
34390
  const path = join13(root, agentId);
33803
34391
  assertBelow3(root, path);
33804
34392
  try {
33805
- await lstat8(path);
34393
+ await lstat9(path);
33806
34394
  } catch (error52) {
33807
34395
  if (!isMissing(error52)) throw error52;
33808
34396
  try {
@@ -33893,7 +34481,7 @@ async function createRunArtifacts(input) {
33893
34481
  requireSafeSegment(input.agentId, "agentId");
33894
34482
  requireSafeSegment(input.runToken, "runToken");
33895
34483
  const root = await prepareRoot(input.root);
33896
- const removeTree = input.removeTree ?? ((path) => rm6(path, { recursive: true, force: true }));
34484
+ const removeTree = input.removeTree ?? ((path) => rm7(path, { recursive: true, force: true }));
33897
34485
  const cleanupRetryDelayMs = input.cleanupRetryDelayMs ?? CLEANUP_RETRY_DELAY_MS;
33898
34486
  const agentRoot = await prepareAgentRoot(root, input.agentId);
33899
34487
  await lockDownWindowsDirectories([root, agentRoot]);
@@ -33952,10 +34540,10 @@ async function removePrivateTreeWithRetries(path, removeTree, retryDelayMs) {
33952
34540
  }
33953
34541
  }
33954
34542
  async function sweepOrphanedRunArtifacts(root) {
33955
- const absolute = resolve6(root);
34543
+ const absolute = resolve7(root);
33956
34544
  let realProfile;
33957
34545
  if (process.platform === "win32") {
33958
- const profile = resolve6(homedir3());
34546
+ const profile = resolve7(homedir3());
33959
34547
  assertWindowsProfileBoundary(profile, absolute);
33960
34548
  await rejectWindowsSymlinkAncestors(profile, absolute);
33961
34549
  realProfile = await realpath6(profile);
@@ -33980,7 +34568,7 @@ async function sweepOrphanedRunArtifacts(root) {
33980
34568
  if (!SAFE_SEGMENT2.test(run3.name) || !run3.isDirectory() || run3.isSymbolicLink()) continue;
33981
34569
  const runPath = join13(agentPath, run3.name);
33982
34570
  assertBelow3(agentPath, runPath);
33983
- await rm6(runPath, { recursive: true, force: true });
34571
+ await rm7(runPath, { recursive: true, force: true });
33984
34572
  removed++;
33985
34573
  }
33986
34574
  }
@@ -33990,7 +34578,7 @@ function defaultRunRegistryRoot() {
33990
34578
  return join13(homedir3(), ".zixt", "run-registry");
33991
34579
  }
33992
34580
  async function syncRunRegistryDirectory(path) {
33993
- const handle = await open4(path, "r");
34581
+ const handle = await open5(path, "r");
33994
34582
  try {
33995
34583
  await handle.sync();
33996
34584
  } finally {
@@ -34000,9 +34588,9 @@ async function syncRunRegistryDirectory(path) {
34000
34588
  async function ensureDurableRunRegistryRoot(registryRoot, syncDirectory7) {
34001
34589
  const firstCreated = await mkdir9(registryRoot, { recursive: true, mode: DIRECTORY_MODE4 });
34002
34590
  if (firstCreated && process.platform !== "win32") {
34003
- const first = resolve6(firstCreated);
34004
- const target = resolve6(registryRoot);
34005
- await syncDirectory7(dirname5(first));
34591
+ const first = resolve7(firstCreated);
34592
+ const target = resolve7(registryRoot);
34593
+ await syncDirectory7(dirname6(first));
34006
34594
  let current = first;
34007
34595
  for (const part of relative7(first, target).split(sep3).filter(Boolean)) {
34008
34596
  await syncDirectory7(current);
@@ -34019,12 +34607,12 @@ async function recordRunAssignment(runToken, record2, registryRoot = defaultRunR
34019
34607
  try {
34020
34608
  const syncDirectory7 = options.syncDirectory ?? syncRunRegistryDirectory;
34021
34609
  await ensureDurableRunRegistryRoot(registryRoot, syncDirectory7);
34022
- handle = await open4(temporary, "wx", FILE_MODE3);
34610
+ handle = await open5(temporary, "wx", FILE_MODE3);
34023
34611
  await handle.writeFile(JSON.stringify(record2), "utf8");
34024
34612
  await handle.sync();
34025
34613
  await handle.close();
34026
34614
  handle = void 0;
34027
- await rename4(temporary, destination);
34615
+ await rename5(temporary, destination);
34028
34616
  if (process.platform !== "win32") {
34029
34617
  await syncDirectory7(registryRoot);
34030
34618
  }
@@ -34034,7 +34622,7 @@ async function recordRunAssignment(runToken, record2, registryRoot = defaultRunR
34034
34622
  } finally {
34035
34623
  await handle?.close().catch(() => {
34036
34624
  });
34037
- await rm6(temporary, { force: true }).catch(() => {
34625
+ await rm7(temporary, { force: true }).catch(() => {
34038
34626
  });
34039
34627
  }
34040
34628
  }
@@ -34046,7 +34634,7 @@ async function forgetRunAssignment(runToken, registryRoot = defaultRunRegistryRo
34046
34634
  if (retainingAssignments) return;
34047
34635
  if (!SAFE_SEGMENT2.test(runToken)) return;
34048
34636
  try {
34049
- await rm6(join13(registryRoot, `${runToken}.json`), { force: true });
34637
+ await rm7(join13(registryRoot, `${runToken}.json`), { force: true });
34050
34638
  } catch {
34051
34639
  }
34052
34640
  }
@@ -34091,7 +34679,7 @@ async function readRecordedRunAssignmentEntries(registryRoot = defaultRunRegistr
34091
34679
  if (!SAFE_SEGMENT2.test(runToken)) continue;
34092
34680
  let text;
34093
34681
  try {
34094
- text = await readFile6(join13(registryRoot, entry.name), "utf8");
34682
+ text = await readFile7(join13(registryRoot, entry.name), "utf8");
34095
34683
  } catch {
34096
34684
  continue;
34097
34685
  }
@@ -34103,7 +34691,7 @@ async function readRecordedRunAssignmentEntries(registryRoot = defaultRunRegistr
34103
34691
  async function readRecordedRunAssignmentEntriesStrict(registryRoot = defaultRunRegistryRoot()) {
34104
34692
  let rootStat;
34105
34693
  try {
34106
- rootStat = await lstat8(registryRoot);
34694
+ rootStat = await lstat9(registryRoot);
34107
34695
  } catch (error52) {
34108
34696
  if (isMissing(error52)) return [];
34109
34697
  throw new Error("run registry state could not be observed");
@@ -34127,7 +34715,7 @@ async function readRecordedRunAssignmentEntriesStrict(registryRoot = defaultRunR
34127
34715
  }
34128
34716
  let text;
34129
34717
  try {
34130
- text = await readFile6(join13(registryRoot, entry.name), "utf8");
34718
+ text = await readFile7(join13(registryRoot, entry.name), "utf8");
34131
34719
  } catch {
34132
34720
  throw new Error("committed run registry witness could not be read");
34133
34721
  }
@@ -34144,13 +34732,13 @@ async function forgetAcknowledgedRunAssignments(assignments, registryRoot = defa
34144
34732
  );
34145
34733
  const entries = await readRecordedRunAssignmentEntries(registryRoot);
34146
34734
  await Promise.all(
34147
- entries.filter(({ record: record2 }) => acknowledged.has(`${record2.taskId}:${record2.epoch}`)).map(({ runToken }) => rm6(join13(registryRoot, `${runToken}.json`), { force: true }))
34735
+ entries.filter(({ record: record2 }) => acknowledged.has(`${record2.taskId}:${record2.epoch}`)).map(({ runToken }) => rm7(join13(registryRoot, `${runToken}.json`), { force: true }))
34148
34736
  );
34149
34737
  }
34150
34738
  async function forgetSupersededRunAssignments(taskId, epoch, registryRoot = defaultRunRegistryRoot()) {
34151
34739
  const entries = await readRecordedRunAssignmentEntries(registryRoot);
34152
34740
  await Promise.all(
34153
- entries.filter(({ record: record2 }) => record2.taskId === taskId && record2.epoch < epoch).map(({ runToken }) => rm6(join13(registryRoot, `${runToken}.json`), { force: true }))
34741
+ entries.filter(({ record: record2 }) => record2.taskId === taskId && record2.epoch < epoch).map(({ runToken }) => rm7(join13(registryRoot, `${runToken}.json`), { force: true }))
34154
34742
  );
34155
34743
  }
34156
34744
 
@@ -34221,7 +34809,7 @@ function truncateThought(text) {
34221
34809
  }
34222
34810
  var MAX_APPROVAL_PAYLOAD = 5e4;
34223
34811
  async function requireRealDirectory4(path, label) {
34224
- const entry = await lstat9(path).catch(() => null);
34812
+ const entry = await lstat10(path).catch(() => null);
34225
34813
  if (!entry) throw new Error(`${label} does not exist`);
34226
34814
  if (entry.isSymbolicLink()) throw new Error(`${label} must not be a symbolic link`);
34227
34815
  if (!entry.isDirectory()) throw new Error(`${label} must be a directory`);
@@ -34237,7 +34825,7 @@ function createCliRunner(adapter, opts = {}) {
34237
34825
  const prefixArgs = opts.commandPrefixArgs ?? [];
34238
34826
  const maxWallTimeMs = opts.maxWallTimeMs;
34239
34827
  const workspaceRoot = opts.workspaceRoot ?? defaultRunnerWorkspaceRoot();
34240
- const artifactRoot = opts.artifactRoot ?? (opts.workspaceRoot === void 0 ? defaultRunnerArtifactRoot() : join14(dirname6(workspaceRoot), "run-artifacts"));
34828
+ const artifactRoot = opts.artifactRoot ?? (opts.workspaceRoot === void 0 ? defaultRunnerArtifactRoot() : join14(dirname7(workspaceRoot), "run-artifacts"));
34241
34829
  const runRegistryRoot2 = opts.runRegistryRoot ?? defaultRunRegistryRoot();
34242
34830
  const createArtifacts = opts.createArtifacts ?? createRunArtifacts;
34243
34831
  const toolPackRegistry2 = opts.toolPackRegistry ?? createDefaultToolPackRegistry();
@@ -34281,7 +34869,12 @@ function createCliRunner(adapter, opts = {}) {
34281
34869
  usage: { inputTokens: 0, outputTokens: 0 }
34282
34870
  });
34283
34871
  if (task.cancelledNow()) return cancelledBeforeRun();
34284
- const runner = task.spec.runner ?? { type: "claude-code", auth: "machine" };
34872
+ const configuredRunner = task.spec.runner ?? {
34873
+ type: "claude-code",
34874
+ auth: "machine"
34875
+ };
34876
+ const runner = { ...configuredRunner };
34877
+ if (runner.model === "default") delete runner.model;
34285
34878
  if (runner.type !== adapter.type) {
34286
34879
  return {
34287
34880
  outcome: "failed",
@@ -34393,7 +34986,13 @@ function createCliRunner(adapter, opts = {}) {
34393
34986
  )
34394
34987
  );
34395
34988
  }
34396
- if (opts.browserManager) {
34989
+ const browserAvailable = opts.browserAvailable ? await opts.browserAvailable() : true;
34990
+ if (task.spec.requiresBrowser && (!opts.browserManager || !browserAvailable)) {
34991
+ throw new Error(
34992
+ "This Task needs a web browser, but the Browser is no longer available on this Machine. Install the pinned Chromium build or continue the Task on a Browser-ready Machine."
34993
+ );
34994
+ }
34995
+ if (task.spec.requiresBrowser && opts.browserManager && browserAvailable) {
34397
34996
  toolPacks.push(
34398
34997
  createBrowserToolPack({
34399
34998
  manager: opts.browserManager,
@@ -34564,8 +35163,8 @@ ${attachmentSection}` : prompt;
34564
35163
  let changed = false;
34565
35164
  for (const path of paths) {
34566
35165
  if (!path || path.length > 4096) continue;
34567
- const absolutePath = isAbsolute13(path) ? path : resolve7(cwd, path);
34568
- const directory = dirname6(absolutePath);
35166
+ const absolutePath = isAbsolute13(path) ? path : resolve8(cwd, path);
35167
+ const directory = dirname7(absolutePath);
34569
35168
  observedWorkingDirectories.delete(directory);
34570
35169
  observedWorkingDirectories.add(directory);
34571
35170
  while (observedWorkingDirectories.size > 19) {
@@ -34987,7 +35586,7 @@ function runCliProcess(options) {
34987
35586
  usage: { inputTokens: 0, outputTokens: 0 }
34988
35587
  });
34989
35588
  }
34990
- return new Promise((resolve14) => {
35589
+ return new Promise((resolve15) => {
34991
35590
  const platform = options.platform ?? process.platform;
34992
35591
  const containmentGateNonce = options.guardian && platform === "win32" ? randomUUID10() : void 0;
34993
35592
  const child = options.guardian ? spawn8(
@@ -35001,7 +35600,7 @@ function runCliProcess(options) {
35001
35600
  // The idle pre-assignment guardian must never load from or depend
35002
35601
  // on an untrusted Task checkout. Only the post-gate target enters
35003
35602
  // the requested working directory from its private release frame.
35004
- cwd: dirname6(options.guardian.scriptPath),
35603
+ cwd: dirname7(options.guardian.scriptPath),
35005
35604
  env: runnerGuardianEnv(process.env, containmentGateNonce),
35006
35605
  stdio: ["pipe", "pipe", "pipe"],
35007
35606
  windowsHide: true,
@@ -35048,7 +35647,7 @@ function runCliProcess(options) {
35048
35647
  clearInterval(timer);
35049
35648
  unregisterFollowUps?.();
35050
35649
  parser.stop?.();
35051
- resolve14(result);
35650
+ resolve15(result);
35052
35651
  };
35053
35652
  const terminate = (result) => {
35054
35653
  if (settled || forcedResult) return;
@@ -35279,7 +35878,7 @@ function runCliProcess(options) {
35279
35878
  import { randomUUID as randomUUID11 } from "node:crypto";
35280
35879
 
35281
35880
  // src/runners/runtime-observation.ts
35282
- import { open as open5, readdir as readdir4, realpath as realpath8 } from "node:fs/promises";
35881
+ import { open as open6, readdir as readdir4, realpath as realpath8 } from "node:fs/promises";
35283
35882
  import { homedir as homedir5 } from "node:os";
35284
35883
  import { join as join15 } from "node:path";
35285
35884
  var READ_WINDOW_BYTES = 1024 * 1024;
@@ -35291,7 +35890,7 @@ function homeFrom(env) {
35291
35890
  async function readHead(path) {
35292
35891
  let handle;
35293
35892
  try {
35294
- handle = await open5(path, "r");
35893
+ handle = await open6(path, "r");
35295
35894
  const buffer = Buffer.alloc(READ_WINDOW_BYTES);
35296
35895
  const { bytesRead } = await handle.read(buffer, 0, READ_WINDOW_BYTES, 0);
35297
35896
  return buffer.subarray(0, bytesRead).toString("utf8");
@@ -35305,7 +35904,7 @@ async function readHead(path) {
35305
35904
  async function readTail(path) {
35306
35905
  let handle;
35307
35906
  try {
35308
- handle = await open5(path, "r");
35907
+ handle = await open6(path, "r");
35309
35908
  const { size } = await handle.stat();
35310
35909
  const start = Math.max(0, size - READ_WINDOW_BYTES);
35311
35910
  const length = Math.min(size, READ_WINDOW_BYTES);
@@ -35393,7 +35992,7 @@ async function readCodexSessionRuntime(input) {
35393
35992
  }
35394
35993
  var codexCatalogCache = /* @__PURE__ */ new Map();
35395
35994
  async function loadCodexModelCatalog(command, prefixArgs, env) {
35396
- const output = await new Promise((resolve14) => {
35995
+ const output = await new Promise((resolve15) => {
35397
35996
  const child = spawnCli(command, [...prefixArgs, "debug", "models"], {
35398
35997
  stdio: ["ignore", "pipe", "ignore"],
35399
35998
  windowsHide: true,
@@ -35408,7 +36007,7 @@ async function loadCodexModelCatalog(command, prefixArgs, env) {
35408
36007
  if (settled) return;
35409
36008
  settled = true;
35410
36009
  clearTimeout(timer);
35411
- resolve14(value);
36010
+ resolve15(value);
35412
36011
  };
35413
36012
  const timer = setTimeout(() => {
35414
36013
  child.kill();
@@ -35513,8 +36112,8 @@ function createRuntimeReporter(input, sessionId) {
35513
36112
  var EFFORT_READ_ATTEMPTS = 5;
35514
36113
  var EFFORT_READ_INTERVAL_MS = 3e3;
35515
36114
  function delay2(ms) {
35516
- return new Promise((resolve14) => {
35517
- const timer = setTimeout(resolve14, ms);
36115
+ return new Promise((resolve15) => {
36116
+ const timer = setTimeout(resolve15, ms);
35518
36117
  timer.unref?.();
35519
36118
  });
35520
36119
  }
@@ -35591,10 +36190,10 @@ function createClaudeLiveParser(onStream, onSessionModel) {
35591
36190
  },
35592
36191
  async steer(followUp) {
35593
36192
  if (!write) return false;
35594
- return await new Promise((resolve14) => {
35595
- acknowledgements.set(followUp.inputId, resolve14);
36193
+ return await new Promise((resolve15) => {
36194
+ acknowledgements.set(followUp.inputId, resolve15);
35596
36195
  void write(input(followUp.inputId, followUp.text)).catch(() => {
35597
- if (acknowledgements.delete(followUp.inputId)) resolve14(false);
36196
+ if (acknowledgements.delete(followUp.inputId)) resolve15(false);
35598
36197
  });
35599
36198
  });
35600
36199
  },
@@ -35748,13 +36347,13 @@ function improveErrorMessage(error52) {
35748
36347
  return "Anthropic authentication failed. Sign in with `claude` on this host, or set a valid ANTHROPIC_API_KEY secret and switch the agent to API-key auth.";
35749
36348
  }
35750
36349
  if (lower.includes("issue with the selected model") || lower.includes("model_not_found")) {
35751
- return "The selected model is not available. Pick a different model in the agent profile.";
36350
+ return "The selected model is not available. Open the failed Task, choose another model or Default in its runtime controls, then Retry.";
35752
36351
  }
35753
36352
  return error52;
35754
36353
  }
35755
36354
 
35756
36355
  // src/runners/codex.ts
35757
- import { mkdir as mkdir11, readFile as readFile7, writeFile as writeFile6 } from "node:fs/promises";
36356
+ import { mkdir as mkdir11, readFile as readFile8, writeFile as writeFile6 } from "node:fs/promises";
35758
36357
  import { randomUUID as randomUUID12 } from "node:crypto";
35759
36358
  import { homedir as homedir6 } from "node:os";
35760
36359
  import { join as join16 } from "node:path";
@@ -35769,7 +36368,7 @@ function threadIndexPath(root, agentId, sessionKey) {
35769
36368
  }
35770
36369
  async function readThreadId(path) {
35771
36370
  try {
35772
- const parsed = JSON.parse(await readFile7(path, "utf8"));
36371
+ const parsed = JSON.parse(await readFile8(path, "utf8"));
35773
36372
  return typeof parsed.threadId === "string" && /^[A-Za-z0-9-]{1,120}$/.test(parsed.threadId) ? parsed.threadId : null;
35774
36373
  } catch {
35775
36374
  return null;
@@ -35901,8 +36500,8 @@ ${value}` : value;
35901
36500
  var RUNTIME_READ_ATTEMPTS = 5;
35902
36501
  var RUNTIME_READ_INTERVAL_MS = 2e3;
35903
36502
  function delay3(ms) {
35904
- return new Promise((resolve14) => {
35905
- const timer = setTimeout(resolve14, ms);
36503
+ return new Promise((resolve15) => {
36504
+ const timer = setTimeout(resolve15, ms);
35906
36505
  timer.unref?.();
35907
36506
  });
35908
36507
  }
@@ -35941,7 +36540,7 @@ function createCodexAppServerParser(onStream, options) {
35941
36540
  const turnReadyWaiters = /* @__PURE__ */ new Set();
35942
36541
  const usage = () => ({ inputTokens, outputTokens });
35943
36542
  const settleTurnReadiness = (ready) => {
35944
- for (const resolve14 of turnReadyWaiters) resolve14(ready);
36543
+ for (const resolve15 of turnReadyWaiters) resolve15(ready);
35945
36544
  turnReadyWaiters.clear();
35946
36545
  };
35947
36546
  const send = async (message) => {
@@ -36122,12 +36721,12 @@ function createCodexAppServerParser(onStream, options) {
36122
36721
  async steer(input) {
36123
36722
  if (stopped) return false;
36124
36723
  if (!activeTurnId) {
36125
- const ready = await new Promise((resolve14) => turnReadyWaiters.add(resolve14));
36724
+ const ready = await new Promise((resolve15) => turnReadyWaiters.add(resolve15));
36126
36725
  if (!ready || stopped) return false;
36127
36726
  }
36128
36727
  if (!threadId || !activeTurnId) return false;
36129
- return await new Promise((resolve14) => {
36130
- steerWaiters.set(input.inputId, resolve14);
36728
+ return await new Promise((resolve15) => {
36729
+ steerWaiters.set(input.inputId, resolve15);
36131
36730
  void send({
36132
36731
  id: `steer:${input.inputId}`,
36133
36732
  method: "turn/steer",
@@ -36138,7 +36737,7 @@ function createCodexAppServerParser(onStream, options) {
36138
36737
  clientUserMessageId: input.inputId
36139
36738
  }
36140
36739
  }).catch(() => {
36141
- if (steerWaiters.delete(input.inputId)) resolve14(false);
36740
+ if (steerWaiters.delete(input.inputId)) resolve15(false);
36142
36741
  });
36143
36742
  });
36144
36743
  },
@@ -36146,7 +36745,7 @@ function createCodexAppServerParser(onStream, options) {
36146
36745
  stopped = true;
36147
36746
  write = null;
36148
36747
  settleTurnReadiness(false);
36149
- for (const resolve14 of steerWaiters.values()) resolve14(false);
36748
+ for (const resolve15 of steerWaiters.values()) resolve15(false);
36150
36749
  steerWaiters.clear();
36151
36750
  },
36152
36751
  push(chunk) {
@@ -36317,7 +36916,7 @@ function improveCodexErrorMessage(error52) {
36317
36916
  return "OpenAI authentication failed. Sign in with `codex login` on this host, or set a valid OPENAI_API_KEY secret and switch the agent to API-key auth.";
36318
36917
  }
36319
36918
  if (lower.includes("model_not_found") || lower.includes("model") && (lower.includes("not found") || lower.includes("unsupported") || lower.includes("invalid"))) {
36320
- return "The selected model is not available. Pick a different model in the agent profile.";
36919
+ return "The selected model is not available. Open the failed Task, choose another model or Default in its runtime controls, then Retry.";
36321
36920
  }
36322
36921
  return error52;
36323
36922
  }
@@ -36325,7 +36924,7 @@ function improveCodexErrorMessage(error52) {
36325
36924
  // src/runners/git-preflight.ts
36326
36925
  import { spawn as spawn9 } from "node:child_process";
36327
36926
  import { realpath as realpath9 } from "node:fs/promises";
36328
- import { isAbsolute as isAbsolute14, resolve as resolve8 } from "node:path";
36927
+ import { isAbsolute as isAbsolute14, resolve as resolve9 } from "node:path";
36329
36928
  var OUTPUT_LIMIT = 8192;
36330
36929
  var DEFAULT_TIMEOUT_MS4 = 1e4;
36331
36930
  var VERSION_PATTERN = /^git version [^\r\n]{1,108}$/;
@@ -36344,7 +36943,7 @@ async function preflightGit(options = {}) {
36344
36943
  if (configured !== void 0 && !isAbsolute14(configured)) {
36345
36944
  return unavailable("configured git command must be an absolute file", checkedAt);
36346
36945
  }
36347
- const trustedCwd = await realpath9(resolve8(options.trustedCwd ?? process.cwd())).catch(() => null);
36946
+ const trustedCwd = await realpath9(resolve9(options.trustedCwd ?? process.cwd())).catch(() => null);
36348
36947
  if (!trustedCwd)
36349
36948
  return unavailable("Host-owned git preflight directory is unavailable", checkedAt);
36350
36949
  const executablePath = await resolveTrustedCliCommand(configured ?? "git", {
@@ -36566,7 +37165,7 @@ function parseAuth(result) {
36566
37165
  return "unknown";
36567
37166
  }
36568
37167
  function run2(command, args) {
36569
- return new Promise((resolve14) => {
37168
+ return new Promise((resolve15) => {
36570
37169
  const child = spawnCli(command, args, {
36571
37170
  stdio: ["ignore", "pipe", "pipe"],
36572
37171
  windowsHide: true
@@ -36582,7 +37181,7 @@ function run2(command, args) {
36582
37181
  if (settled) return;
36583
37182
  settled = true;
36584
37183
  clearTimeout(timeout);
36585
- resolve14(result);
37184
+ resolve15(result);
36586
37185
  };
36587
37186
  const timeout = setTimeout(() => {
36588
37187
  child.kill();
@@ -36596,9 +37195,9 @@ function run2(command, args) {
36596
37195
  // src/linux-service.ts
36597
37196
  import { spawn as spawn10 } from "node:child_process";
36598
37197
  import { constants as constants2 } from "node:fs";
36599
- import { access as access4, chmod as chmod7, mkdir as mkdir12, open as open6, rename as rename5, rm as rm7 } from "node:fs/promises";
37198
+ import { access as access4, chmod as chmod7, mkdir as mkdir12, open as open7, rename as rename6, rm as rm8 } from "node:fs/promises";
36600
37199
  import { homedir as homedir7, userInfo } from "node:os";
36601
- import { basename as basename3, dirname as dirname7, join as join17, relative as relative8, resolve as resolve9, sep as sep4 } from "node:path";
37200
+ import { basename as basename3, dirname as dirname8, join as join17, relative as relative8, resolve as resolve10, sep as sep4 } from "node:path";
36602
37201
  var SERVICE_NAME = "zixt-host.service";
36603
37202
  var SYSTEM_SERVICE_COMMAND_TIMEOUT_MS = 7e4;
36604
37203
  var SERVICE_STABILITY_DELAY_MS = 2e3;
@@ -36626,7 +37225,7 @@ function boundedAppend(current, chunk) {
36626
37225
  }
36627
37226
  async function defaultRunCommand(command, args) {
36628
37227
  const commandEnvironment3 = systemServiceCommandEnvironment();
36629
- return new Promise((resolve14) => {
37228
+ return new Promise((resolve15) => {
36630
37229
  const child = spawn10(command, [...args], {
36631
37230
  stdio: ["ignore", "pipe", "pipe"],
36632
37231
  env: commandEnvironment3,
@@ -36640,7 +37239,7 @@ async function defaultRunCommand(command, args) {
36640
37239
  if (settled) return;
36641
37240
  settled = true;
36642
37241
  if (timer) clearTimeout(timer);
36643
- resolve14(result);
37242
+ resolve15(result);
36644
37243
  };
36645
37244
  child.stdout?.on("data", (chunk) => {
36646
37245
  stdout = boundedAppend(stdout, chunk);
@@ -36682,11 +37281,16 @@ function systemdQuotedValue(value, escapeDollar) {
36682
37281
  function systemdUnitValue(value) {
36683
37282
  return systemdQuotedValue(value, true);
36684
37283
  }
36685
- function systemdDirectiveValue(value) {
36686
- return systemdQuotedValue(value, false);
37284
+ function systemdDirectivePath(value) {
37285
+ const path = oneLine(value, "service path");
37286
+ if (!path.startsWith("/")) throw new Error("service path must be absolute");
37287
+ return path.replace(/[%\s"'\\$]/gu, (character) => {
37288
+ if (character === "%") return "%%";
37289
+ return [...Buffer.from(character, "utf8")].map((byte) => `\\x${byte.toString(16).padStart(2, "0")}`).join("");
37290
+ });
36687
37291
  }
36688
37292
  async function defaultSyncDirectory(path) {
36689
- const directory = await open6(path, "r");
37293
+ const directory = await open7(path, "r");
36690
37294
  try {
36691
37295
  await directory.sync();
36692
37296
  } finally {
@@ -36696,9 +37300,9 @@ async function defaultSyncDirectory(path) {
36696
37300
  async function ensureDirectory(path, mode, syncDirectory7) {
36697
37301
  const firstCreated = await mkdir12(path, { recursive: true, mode });
36698
37302
  if (!firstCreated) return;
36699
- const first = resolve9(firstCreated);
36700
- const target = resolve9(path);
36701
- await syncDirectory7(dirname7(first));
37303
+ const first = resolve10(firstCreated);
37304
+ const target = resolve10(path);
37305
+ await syncDirectory7(dirname8(first));
36702
37306
  let current = first;
36703
37307
  const descendants = relative8(first, target);
36704
37308
  for (const part of descendants ? descendants.split(sep4) : []) {
@@ -36707,20 +37311,20 @@ async function ensureDirectory(path, mode, syncDirectory7) {
36707
37311
  }
36708
37312
  }
36709
37313
  async function replacePrivateFile(path, contents, mode, syncDirectory7) {
36710
- const parent = dirname7(path);
37314
+ const parent = dirname8(path);
36711
37315
  await ensureDirectory(parent, 448, syncDirectory7);
36712
37316
  const temporary = join17(parent, `.${basename3(path)}.${process.pid}.${crypto.randomUUID()}.tmp`);
36713
- const handle = await open6(temporary, "wx", mode);
37317
+ const handle = await open7(temporary, "wx", mode);
36714
37318
  try {
36715
37319
  await handle.writeFile(contents, "utf8");
36716
37320
  await handle.sync();
36717
37321
  await handle.close();
36718
- await rename5(temporary, path);
37322
+ await rename6(temporary, path);
36719
37323
  await chmod7(path, mode);
36720
37324
  await syncDirectory7(parent);
36721
37325
  } catch (error52) {
36722
37326
  await handle.close().catch(() => void 0);
36723
- await rm7(temporary, { force: true }).catch(() => void 0);
37327
+ await rm8(temporary, { force: true }).catch(() => void 0);
36724
37328
  throw error52;
36725
37329
  }
36726
37330
  }
@@ -36771,7 +37375,7 @@ async function installLinuxService(options) {
36771
37375
  const resolveCommand = options.resolveCommand ?? defaultResolveCommand;
36772
37376
  const run3 = options.runCommand ?? defaultRunCommand;
36773
37377
  const syncDirectory7 = options.syncDirectory ?? defaultSyncDirectory;
36774
- const stabilityDelay = options.delay ?? ((ms) => new Promise((resolve14) => setTimeout(resolve14, ms)));
37378
+ const stabilityDelay = options.delay ?? ((ms) => new Promise((resolve15) => setTimeout(resolve15, ms)));
36775
37379
  const [systemctl, loginctl] = await Promise.all([
36776
37380
  resolveCommand("systemctl"),
36777
37381
  resolveCommand("loginctl")
@@ -36818,7 +37422,7 @@ async function installLinuxService(options) {
36818
37422
  // Type=exec does not report startup success until the kernel has executed
36819
37423
  // Node, so a missing/corrupt release cannot masquerade as an active Host.
36820
37424
  "Type=exec",
36821
- `EnvironmentFile=${systemdDirectiveValue(environmentPath)}`,
37425
+ `EnvironmentFile=${systemdDirectivePath(environmentPath)}`,
36822
37426
  `ExecStart=${systemdUnitValue(process.execPath)} ${systemdUnitValue(currentEntry)}`,
36823
37427
  "Restart=on-failure",
36824
37428
  "RestartPreventExitStatus=64",
@@ -36884,9 +37488,9 @@ async function installLinuxService(options) {
36884
37488
  // src/macos-service.ts
36885
37489
  import { spawn as spawn11 } from "node:child_process";
36886
37490
  import { constants as constants3 } from "node:fs";
36887
- import { access as access5, chmod as chmod8, mkdir as mkdir13, open as open7, rename as rename6, rm as rm8 } from "node:fs/promises";
37491
+ import { access as access5, chmod as chmod8, mkdir as mkdir13, open as open8, rename as rename7, rm as rm9 } from "node:fs/promises";
36888
37492
  import { homedir as homedir8, userInfo as userInfo2 } from "node:os";
36889
- import { basename as basename4, dirname as dirname8, join as join18, relative as relative9, resolve as resolve10, sep as sep5 } from "node:path";
37493
+ import { basename as basename4, dirname as dirname9, join as join18, relative as relative9, resolve as resolve11, sep as sep5 } from "node:path";
36890
37494
  var LAUNCH_AGENT_LABEL = "ai.zixt.host";
36891
37495
  var SERVICE_STABILITY_DELAY_MS2 = 2e3;
36892
37496
  var COMMAND_TIMEOUT_MS2 = 7e4;
@@ -36902,7 +37506,7 @@ function shellValue(value) {
36902
37506
  return `'${oneLine2(value, "service setting").replaceAll("'", `'"'"'`)}'`;
36903
37507
  }
36904
37508
  async function syncDirectory4(path) {
36905
- const directory = await open7(path, "r");
37509
+ const directory = await open8(path, "r");
36906
37510
  try {
36907
37511
  await directory.sync();
36908
37512
  } finally {
@@ -36912,9 +37516,9 @@ async function syncDirectory4(path) {
36912
37516
  async function ensureDirectory2(path, sync) {
36913
37517
  const firstCreated = await mkdir13(path, { recursive: true, mode: 448 });
36914
37518
  if (!firstCreated) return;
36915
- const first = resolve10(firstCreated);
36916
- const target = resolve10(path);
36917
- await sync(dirname8(first));
37519
+ const first = resolve11(firstCreated);
37520
+ const target = resolve11(path);
37521
+ await sync(dirname9(first));
36918
37522
  let current = first;
36919
37523
  for (const part of relative9(first, target).split(sep5).filter(Boolean)) {
36920
37524
  await sync(current);
@@ -36922,20 +37526,20 @@ async function ensureDirectory2(path, sync) {
36922
37526
  }
36923
37527
  }
36924
37528
  async function replacePrivateFile2(path, contents, mode, sync) {
36925
- const parent = dirname8(path);
37529
+ const parent = dirname9(path);
36926
37530
  await ensureDirectory2(parent, sync);
36927
37531
  const temporary = join18(parent, `.${basename4(path)}.${process.pid}.${crypto.randomUUID()}.tmp`);
36928
- const handle = await open7(temporary, "wx", mode);
37532
+ const handle = await open8(temporary, "wx", mode);
36929
37533
  try {
36930
37534
  await handle.writeFile(contents, "utf8");
36931
37535
  await handle.sync();
36932
37536
  await handle.close();
36933
- await rename6(temporary, path);
37537
+ await rename7(temporary, path);
36934
37538
  await chmod8(path, mode);
36935
37539
  await sync(parent);
36936
37540
  } catch (error52) {
36937
37541
  await handle.close().catch(() => void 0);
36938
- await rm8(temporary, { force: true }).catch(() => void 0);
37542
+ await rm9(temporary, { force: true }).catch(() => void 0);
36939
37543
  throw error52;
36940
37544
  }
36941
37545
  }
@@ -37115,9 +37719,9 @@ async function installMacosService(options) {
37115
37719
  // src/windows-service.ts
37116
37720
  import { spawn as spawn12 } from "node:child_process";
37117
37721
  import { constants as constants4 } from "node:fs";
37118
- import { access as access6, mkdir as mkdir14, open as open8, readFile as readFile8, rename as rename7, rm as rm9 } from "node:fs/promises";
37722
+ import { access as access6, mkdir as mkdir14, open as open9, readFile as readFile9, rename as rename8, rm as rm10 } from "node:fs/promises";
37119
37723
  import { homedir as homedir9 } from "node:os";
37120
- import { basename as basename5, dirname as dirname9, isAbsolute as isAbsolute15, join as join19, relative as relative10, resolve as resolve11, sep as sep6 } from "node:path";
37724
+ import { basename as basename5, dirname as dirname10, isAbsolute as isAbsolute15, join as join19, relative as relative10, resolve as resolve12, sep as sep6 } from "node:path";
37121
37725
  var TASK_NAME = "Zixt Host";
37122
37726
  var COMMAND_TIMEOUT_MS3 = 7e4;
37123
37727
  var SERVICE_STABILITY_DELAY_MS3 = 2e3;
@@ -37135,7 +37739,7 @@ function psLiteral(value) {
37135
37739
  }
37136
37740
  async function syncDirectory5(path) {
37137
37741
  if (process.platform === "win32") return;
37138
- const directory = await open8(path, "r");
37742
+ const directory = await open9(path, "r");
37139
37743
  try {
37140
37744
  await directory.sync();
37141
37745
  } finally {
@@ -37145,9 +37749,9 @@ async function syncDirectory5(path) {
37145
37749
  async function ensureDirectory3(path, sync) {
37146
37750
  const firstCreated = await mkdir14(path, { recursive: true, mode: 448 });
37147
37751
  if (!firstCreated) return;
37148
- const first = resolve11(firstCreated);
37149
- const target = resolve11(path);
37150
- await sync(dirname9(first));
37752
+ const first = resolve12(firstCreated);
37753
+ const target = resolve12(path);
37754
+ await sync(dirname10(first));
37151
37755
  let current = first;
37152
37756
  for (const part of relative10(first, target).split(sep6).filter(Boolean)) {
37153
37757
  await sync(current);
@@ -37155,19 +37759,19 @@ async function ensureDirectory3(path, sync) {
37155
37759
  }
37156
37760
  }
37157
37761
  async function replacePrivateFile3(path, contents, sync) {
37158
- const parent = dirname9(path);
37762
+ const parent = dirname10(path);
37159
37763
  await ensureDirectory3(parent, sync);
37160
37764
  const temporary = join19(parent, `.${basename5(path)}.${process.pid}.${crypto.randomUUID()}.tmp`);
37161
- const handle = await open8(temporary, "wx", 384);
37765
+ const handle = await open9(temporary, "wx", 384);
37162
37766
  try {
37163
37767
  await handle.writeFile(contents, "utf8");
37164
37768
  await handle.sync();
37165
37769
  await handle.close();
37166
- await rename7(temporary, path);
37770
+ await rename8(temporary, path);
37167
37771
  await sync(parent);
37168
37772
  } catch (error52) {
37169
37773
  await handle.close().catch(() => void 0);
37170
- await rm9(temporary, { force: true }).catch(() => void 0);
37774
+ await rm10(temporary, { force: true }).catch(() => void 0);
37171
37775
  throw error52;
37172
37776
  }
37173
37777
  }
@@ -37300,7 +37904,7 @@ exit $code
37300
37904
  }
37301
37905
  async function defaultObserveStatus(path, generation) {
37302
37906
  try {
37303
- const text = (await readFile8(path, "utf8")).replace(/^\uFEFF/, "");
37907
+ const text = (await readFile9(path, "utf8")).replace(/^\uFEFF/, "");
37304
37908
  const value = JSON.parse(text);
37305
37909
  if (value.schema !== 1 || value.generation !== generation || typeof value.pid !== "number" || !Number.isSafeInteger(value.pid) || value.pid <= 0) {
37306
37910
  return null;
@@ -37402,7 +38006,7 @@ async function installWindowsService(options) {
37402
38006
  );
37403
38007
  await replacePrivateFile3(launcherPath, launcherSource2(configPath, statusPath), sync);
37404
38008
  await replacePrivateFile3(taskXmlPath, taskXml({ sid, powershell, launcherPath, home }), sync);
37405
- await rm9(statusPath, { force: true });
38009
+ await rm10(statusPath, { force: true });
37406
38010
  const acl = await run3(icacls, [
37407
38011
  configRoot,
37408
38012
  "/inheritance:r",
@@ -37454,9 +38058,9 @@ async function installSystemService(options) {
37454
38058
  }
37455
38059
 
37456
38060
  // src/terminal-outcomes.ts
37457
- import { chmod as chmod9, lstat as lstat10, mkdir as mkdir15, open as open9, readdir as readdir5, readFile as readFile9, rename as rename8, rm as rm10 } from "node:fs/promises";
38061
+ import { chmod as chmod9, lstat as lstat11, mkdir as mkdir15, open as open10, readdir as readdir5, readFile as readFile10, rename as rename9, rm as rm11 } from "node:fs/promises";
37458
38062
  import { homedir as homedir10 } from "node:os";
37459
- import { dirname as dirname10, join as join20, relative as relative11, resolve as resolve12, sep as sep7 } from "node:path";
38063
+ import { dirname as dirname11, join as join20, relative as relative11, resolve as resolve13, sep as sep7 } from "node:path";
37460
38064
  var DIRECTORY_MODE5 = 448;
37461
38065
  var FILE_MODE4 = 384;
37462
38066
  var MAX_OUTCOME_BYTES = 4 * 1024 * 1024;
@@ -37474,7 +38078,7 @@ function outcomePath(root, hostId, taskId, epoch) {
37474
38078
  }
37475
38079
  async function syncDirectory6(root) {
37476
38080
  if (process.platform === "win32") return;
37477
- const handle = await open9(root, "r");
38081
+ const handle = await open10(root, "r");
37478
38082
  try {
37479
38083
  await handle.sync();
37480
38084
  } finally {
@@ -37484,16 +38088,16 @@ async function syncDirectory6(root) {
37484
38088
  async function requirePrivateRoot(root, sync = syncDirectory6) {
37485
38089
  const firstCreated = await mkdir15(root, { recursive: true, mode: DIRECTORY_MODE5 });
37486
38090
  if (firstCreated) {
37487
- const first = resolve12(firstCreated);
37488
- const target = resolve12(root);
37489
- await sync(dirname10(first));
38091
+ const first = resolve13(firstCreated);
38092
+ const target = resolve13(root);
38093
+ await sync(dirname11(first));
37490
38094
  let current = first;
37491
38095
  for (const part of relative11(first, target).split(sep7).filter(Boolean)) {
37492
38096
  await sync(current);
37493
38097
  current = join20(current, part);
37494
38098
  }
37495
38099
  }
37496
- const stat3 = await lstat10(root);
38100
+ const stat3 = await lstat11(root);
37497
38101
  if (stat3.isSymbolicLink() || !stat3.isDirectory()) {
37498
38102
  throw new Error("terminal outcome journal root is not a trusted directory");
37499
38103
  }
@@ -37521,7 +38125,7 @@ async function recordTerminalOutcome(hostId, input, root = defaultTerminalOutcom
37521
38125
  const destination = outcomePath(root, hostId, outcome.taskId, outcome.epoch);
37522
38126
  try {
37523
38127
  const existing = parseCommittedOutcome(
37524
- await readFile9(destination, { encoding: "utf8", flag: "r" }),
38128
+ await readFile10(destination, { encoding: "utf8", flag: "r" }),
37525
38129
  outcome.taskId,
37526
38130
  outcome.epoch
37527
38131
  );
@@ -37536,25 +38140,25 @@ async function recordTerminalOutcome(hostId, input, root = defaultTerminalOutcom
37536
38140
  );
37537
38141
  let handle;
37538
38142
  try {
37539
- handle = await open9(temporary, "wx", FILE_MODE4);
38143
+ handle = await open10(temporary, "wx", FILE_MODE4);
37540
38144
  await handle.writeFile(JSON.stringify(outcome), "utf8");
37541
38145
  await handle.sync();
37542
38146
  await handle.close();
37543
38147
  handle = void 0;
37544
- await rename8(temporary, destination);
38148
+ await rename9(temporary, destination);
37545
38149
  await sync(scopedRoot);
37546
38150
  await sync(root);
37547
38151
  } finally {
37548
38152
  await handle?.close().catch(() => {
37549
38153
  });
37550
- await rm10(temporary, { force: true }).catch(() => {
38154
+ await rm11(temporary, { force: true }).catch(() => {
37551
38155
  });
37552
38156
  }
37553
38157
  }
37554
38158
  async function readTerminalOutcomesStrict(root = defaultTerminalOutcomeRoot()) {
37555
38159
  let rootStat;
37556
38160
  try {
37557
- rootStat = await lstat10(root);
38161
+ rootStat = await lstat11(root);
37558
38162
  } catch (error52) {
37559
38163
  if (error52.code === "ENOENT") return [];
37560
38164
  throw new Error("terminal outcome journal could not be observed");
@@ -37571,7 +38175,7 @@ async function readTerminalOutcomesStrict(root = defaultTerminalOutcomeRoot()) {
37571
38175
  throw new Error("terminal outcome Host scope is not a trusted directory");
37572
38176
  }
37573
38177
  const scopedRoot = hostOutcomeRoot(root, hostEntry.name);
37574
- const scopedStat = await lstat10(scopedRoot);
38178
+ const scopedStat = await lstat11(scopedRoot);
37575
38179
  if (scopedStat.isSymbolicLink() || !scopedStat.isDirectory()) {
37576
38180
  throw new Error("terminal outcome Host scope is not a trusted directory");
37577
38181
  }
@@ -37584,12 +38188,12 @@ async function readTerminalOutcomesStrict(root = defaultTerminalOutcomeRoot()) {
37584
38188
  throw new Error("committed terminal outcome is not a trusted regular file");
37585
38189
  }
37586
38190
  const path = join20(scopedRoot, entry.name);
37587
- const stat3 = await lstat10(path);
38191
+ const stat3 = await lstat11(path);
37588
38192
  if (!stat3.isFile() || stat3.isSymbolicLink() || stat3.size > MAX_OUTCOME_BYTES) {
37589
38193
  throw new Error("committed terminal outcome is not a trusted regular file");
37590
38194
  }
37591
38195
  const outcome = parseCommittedOutcome(
37592
- await readFile9(path, "utf8"),
38196
+ await readFile10(path, "utf8"),
37593
38197
  match[1],
37594
38198
  Number(match[2])
37595
38199
  );
@@ -37616,7 +38220,7 @@ async function forgetAcknowledgedTerminalOutcomes(refs, root = defaultTerminalOu
37616
38220
  if (acknowledged.get(`${hostId}:${outcome.taskId}:${outcome.epoch}`) !== outcome.resultId) {
37617
38221
  continue;
37618
38222
  }
37619
- await rm10(outcomePath(root, hostId, outcome.taskId, outcome.epoch), { force: true });
38223
+ await rm11(outcomePath(root, hostId, outcome.taskId, outcome.epoch), { force: true });
37620
38224
  changedHostRoots.add(hostOutcomeRoot(root, hostId));
37621
38225
  }
37622
38226
  for (const scopedRoot of changedHostRoots) await syncDirectory6(scopedRoot);
@@ -37628,7 +38232,7 @@ async function forgetSupersededTerminalOutcomes(hostId, taskId, epoch, root = de
37628
38232
  if (scoped.hostId !== hostId) continue;
37629
38233
  const { outcome } = scoped;
37630
38234
  if (outcome.taskId !== taskId || outcome.epoch >= epoch) continue;
37631
- await rm10(outcomePath(root, hostId, outcome.taskId, outcome.epoch), { force: true });
38235
+ await rm11(outcomePath(root, hostId, outcome.taskId, outcome.epoch), { force: true });
37632
38236
  removed = true;
37633
38237
  }
37634
38238
  if (removed) await syncDirectory6(hostOutcomeRoot(root, hostId));
@@ -37736,12 +38340,12 @@ function createHostLogger(options = {}) {
37736
38340
  }
37737
38341
 
37738
38342
  // src/demo-state.ts
37739
- import { isAbsolute as isAbsolute16, join as join21, parse as parse3, resolve as resolve13 } from "node:path";
38343
+ import { isAbsolute as isAbsolute16, join as join21, parse as parse3, resolve as resolve14 } from "node:path";
37740
38344
  var DEMO_STATE_ROOT_ENV = "ZIXT_DEMO_STATE_ROOT";
37741
38345
  function resolveDemoHostStatePaths(env = process.env) {
37742
38346
  const configured = env.ZIXT_RUNNER === "demo" ? env[DEMO_STATE_ROOT_ENV] : void 0;
37743
38347
  if (!configured) return null;
37744
- const root = resolve13(configured);
38348
+ const root = resolve14(configured);
37745
38349
  if (!isAbsolute16(configured) || root === parse3(root).root) {
37746
38350
  throw new Error(`${DEMO_STATE_ROOT_ENV} must be a dedicated absolute directory`);
37747
38351
  }
@@ -38063,6 +38667,7 @@ var claudeCode = createClaudeCodeRunner({
38063
38667
  ...runRegistryRoot ? { runRegistryRoot } : {},
38064
38668
  toolPackRegistry,
38065
38669
  browserManager,
38670
+ browserAvailable: async () => (await currentBrowserCapability()).status === "ok",
38066
38671
  gitPreflight: currentGitPreflight,
38067
38672
  onUnsafeRunnerCleanup: restartAfterUnsafeRunnerCleanup
38068
38673
  });
@@ -38073,6 +38678,7 @@ var codex = createCodexRunner({
38073
38678
  ...codexThreadIndexRoot ? { threadIndexRoot: codexThreadIndexRoot } : {},
38074
38679
  toolPackRegistry,
38075
38680
  browserManager,
38681
+ browserAvailable: async () => (await currentBrowserCapability()).status === "ok",
38076
38682
  gitPreflight: currentGitPreflight,
38077
38683
  onUnsafeRunnerCleanup: restartAfterUnsafeRunnerCleanup
38078
38684
  });