@zixt/host 0.0.44 → 0.0.46

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 +939 -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.46",
35
35
  type: "module",
36
36
  exports: {
37
37
  ".": "./src/client.ts",
@@ -14651,12 +14651,16 @@ var ID_PREFIXES = {
14651
14651
  memory: "mry",
14652
14652
  /** Org-singleton Manager configuration (PRD §8.13). */
14653
14653
  manager: "mgr",
14654
+ /** One organization fact the Manager keeps across threads (MG-11). */
14655
+ managerMemory: "mgm",
14654
14656
  /** One Manager conversation thread (MG-2). */
14655
14657
  conversation: "cnv",
14656
14658
  /** One immutable Conversation timeline entry. */
14657
14659
  conversationEvent: "cve",
14658
14660
  /** One Manager inference call's token-metering row (MG-8). */
14659
- managerUsage: "mgu"
14661
+ managerUsage: "mgu",
14662
+ /** One ordered Manager reply awaiting Slack delivery. */
14663
+ managerSlackOutbox: "mso"
14660
14664
  };
14661
14665
  var idPattern = (prefix) => new RegExp(`^${prefix}_[0-9a-f]{32}$`);
14662
14666
  function newId(prefix) {
@@ -14680,6 +14684,7 @@ var BrowserSessionId = idSchema(ID_PREFIXES.browserSession, "browser session id"
14680
14684
  var AttachmentId = idSchema(ID_PREFIXES.attachment, "attachment id");
14681
14685
  var MemoryId = idSchema(ID_PREFIXES.memory, "memory id");
14682
14686
  var ManagerId = idSchema(ID_PREFIXES.manager, "manager id");
14687
+ var ManagerMemoryId = idSchema(ID_PREFIXES.managerMemory, "manager memory id");
14683
14688
  var ConversationId = idSchema(ID_PREFIXES.conversation, "conversation id");
14684
14689
  var ConversationEventId = idSchema(ID_PREFIXES.conversationEvent, "conversation event id");
14685
14690
 
@@ -14786,15 +14791,24 @@ var WebLoginValue = external_exports.object({
14786
14791
  username: external_exports.string().min(1).max(500),
14787
14792
  password: external_exports.string().min(1).max(5e3)
14788
14793
  }).strict();
14794
+ var BrowserProfileContext = external_exports.object({
14795
+ taskMachineName: external_exports.string().min(1).max(120),
14796
+ savedProfileMachineName: external_exports.string().min(1).max(120)
14797
+ }).strict();
14789
14798
  var BrowserSessionProjection = external_exports.object({
14790
14799
  status: external_exports.enum(["none", "starting", "live"]),
14791
14800
  session: BrowserSessionState.optional(),
14792
14801
  /** Whether Start would find an online Machine with a working browser. */
14793
14802
  canStart: external_exports.boolean(),
14794
14803
  /** Member-safe reason when canStart is false. */
14795
- reason: external_exports.string().max(300).optional()
14804
+ reason: external_exports.string().max(300).optional(),
14805
+ /** Present only when active work must use a different Machine-local profile. */
14806
+ profileContext: BrowserProfileContext.optional()
14807
+ }).strict();
14808
+ var StartBrowserSessionResponse = external_exports.object({
14809
+ session: BrowserSessionState,
14810
+ profileContext: BrowserProfileContext.optional()
14796
14811
  }).strict();
14797
- var StartBrowserSessionResponse = external_exports.object({ session: BrowserSessionState }).strict();
14798
14812
  var StopBrowserSessionResponse = external_exports.object({ stopped: external_exports.boolean() }).strict();
14799
14813
  var BrowserViewTicketResponse = external_exports.object({ ticket: external_exports.string().min(1).max(200) }).strict();
14800
14814
 
@@ -14818,6 +14832,7 @@ var ACTION_CATEGORIES = [
14818
14832
  "records.modify",
14819
14833
  "records.delete",
14820
14834
  "docker",
14835
+ "browser.use",
14821
14836
  "mcp.call",
14822
14837
  "spend_money"
14823
14838
  ];
@@ -14856,6 +14871,7 @@ var DEFAULT_GUARDRAIL_POLICY = {
14856
14871
  "records.modify": "allow",
14857
14872
  "records.delete": "allow",
14858
14873
  docker: "allow",
14874
+ "browser.use": "allow",
14859
14875
  "mcp.call": "allow",
14860
14876
  spend_money: "allow"
14861
14877
  };
@@ -15317,6 +15333,16 @@ var Agent = external_exports.object({
15317
15333
  orgId: OrgId,
15318
15334
  name: external_exports.string().min(1).max(120),
15319
15335
  status: AgentStatus,
15336
+ /**
15337
+ * Archive has fenced new work but is still waiting for one or more exact
15338
+ * Machines to acknowledge deletion of saved website sessions. `archived`
15339
+ * is exposed only after this becomes null.
15340
+ */
15341
+ profileCleanup: external_exports.object({
15342
+ state: external_exports.literal("pending"),
15343
+ remainingProfiles: external_exports.number().int().min(1),
15344
+ intent: external_exports.enum(["archive", "restore"])
15345
+ }).strict().nullable().default(null),
15320
15346
  roleDescription: external_exports.string().max(2e3).default(""),
15321
15347
  /** Persona instructions — system-prompt-like free text (AG-1). */
15322
15348
  instructions: external_exports.string().max(5e4).default(""),
@@ -15492,6 +15518,11 @@ var Host = external_exports.object({
15492
15518
  status: HostStatus,
15493
15519
  /** Persistent assignment admission; pausing never changes socket presence or live work. */
15494
15520
  acceptingNewWork: external_exports.boolean().default(true),
15521
+ /**
15522
+ * Removal is fenced but the Machine remains paired solely so an offline
15523
+ * Host can reconnect and delete its saved browser profiles truthfully.
15524
+ */
15525
+ profileCleanup: external_exports.object({ state: external_exports.literal("pending"), remainingProfiles: external_exports.number().int().min(1) }).strict().nullable().default(null),
15495
15526
  /** `desktop` today; `cloud` when pods run the same host (PRD §7.6). */
15496
15527
  kind: external_exports.enum(["desktop", "cloud"]),
15497
15528
  lastSeenAt: IsoDate2.nullable(),
@@ -16012,6 +16043,8 @@ var Task = external_exports.object({
16012
16043
  * its first Machine; assignment then records that choice for later runs.
16013
16044
  */
16014
16045
  requestedHostId: HostId.nullable().default(null),
16046
+ /** Browser-dependent work runs only on a Machine with fresh Browser capability. */
16047
+ requiresBrowser: external_exports.boolean().default(false),
16015
16048
  /** TS-16: per-Task runner/model/effort selection; null runs the teammate's configuration. */
16016
16049
  runner: TaskRunnerSelection.nullable().default(null),
16017
16050
  /**
@@ -16169,6 +16202,8 @@ var CreateTaskInput = external_exports.object({
16169
16202
  * another Machine truthfully queues this Task rather than overriding it.
16170
16203
  */
16171
16204
  requestedHostId: HostId.optional(),
16205
+ /** Require a fresh, working AI teammate Browser on the selected Machine. */
16206
+ requiresBrowser: external_exports.boolean().optional(),
16172
16207
  /** Per-Task runner/model/effort selection (TS-16); absent fields fall back to the teammate's configuration. */
16173
16208
  runner: TaskRunnerSelection.optional(),
16174
16209
  /** Files uploaded ahead of this message; each may belong to one Task only (TS-15). */
@@ -16470,12 +16505,15 @@ var TaskSupportBundle = external_exports.object({
16470
16505
  })
16471
16506
  )
16472
16507
  });
16508
+ var TASK_LIST_MAX_LIMIT = 200;
16473
16509
  var ListTasksResponse = external_exports.object({
16474
- tasks: external_exports.array(TaskProjection)
16475
- });
16510
+ tasks: external_exports.array(TaskProjection).max(TASK_LIST_MAX_LIMIT),
16511
+ nextCursor: external_exports.string().nullable()
16512
+ }).strict();
16476
16513
 
16477
16514
  // ../../packages/contracts/src/protocol.ts
16478
- var PROTOCOL_VERSION = 6;
16515
+ var PROTOCOL_VERSION = 7;
16516
+ var BROWSER_PROFILE_INVENTORY_PAGE_SIZE = 200;
16479
16517
  var TASK_CANCEL_ACK_EVENT = "zixt.task.cancel.acknowledged";
16480
16518
  var TASK_CREDENTIAL_ROLLOVER_ACK_EVENT = "zixt.task.credential_rollover.acknowledged";
16481
16519
  var UnwoundAssignmentRef = external_exports.object({
@@ -16753,6 +16791,11 @@ var AgentOp = external_exports.union([
16753
16791
  }),
16754
16792
  external_exports.object({ kind: external_exports.literal("secret.list") }),
16755
16793
  external_exports.object({ kind: external_exports.literal("agents.list") }),
16794
+ /** Speak to the organization Manager without addressing a human channel directly. */
16795
+ external_exports.object({
16796
+ kind: external_exports.literal("manager.message"),
16797
+ message: external_exports.string().min(1).max(5e4)
16798
+ }),
16756
16799
  /** Read one teammate's non-secret standing configuration; omission means self. */
16757
16800
  external_exports.object({ kind: external_exports.literal("agent.get"), agentId: AgentId.optional() }),
16758
16801
  /**
@@ -16953,6 +16996,12 @@ var TaskAssign = external_exports.object({
16953
16996
  type: external_exports.literal("task.assign"),
16954
16997
  taskId: TaskId,
16955
16998
  agentId: AgentId,
16999
+ /**
17000
+ * A current assignment is a trusted authorization to create a fresh
17001
+ * profile after an explicit restore. Missing legacy values never clear a
17002
+ * Host-side purge tombstone.
17003
+ */
17004
+ browserProfileRevision: external_exports.number().int().min(0).optional(),
16956
17005
  epoch: external_exports.number().int().min(1),
16957
17006
  /**
16958
17007
  * Host-enforced task authority deadline, when anything forces one.
@@ -17029,6 +17078,11 @@ var TaskAssign = external_exports.object({
17029
17078
  titleWanted: external_exports.boolean().optional(),
17030
17079
  /** Advisory Ask / Plan / Act snapshot for this attempt (TS-12). */
17031
17080
  interactionMode: InteractionMode.optional(),
17081
+ /**
17082
+ * Browser-dependent placement requirement (BR-7). Additive/optional so
17083
+ * persisted assignments from an older cloud remain parseable.
17084
+ */
17085
+ requiresBrowser: external_exports.boolean().optional(),
17032
17086
  /** Configured workspace selected by the cloud; absence means no repository context. */
17033
17087
  workspace: SafeDisplayPath.optional(),
17034
17088
  /** Verified provider repository selected independently of a local workspace path. */
@@ -17439,6 +17493,12 @@ var BrowserOpenFrame = external_exports.object({
17439
17493
  type: external_exports.literal("browser.open"),
17440
17494
  requestId: external_exports.string().min(1).max(200),
17441
17495
  agentId: AgentId,
17496
+ /**
17497
+ * Cloud-owned browser-profile lifecycle revision. A Host persists the last
17498
+ * purged revision and refuses a stale open, so a delayed frame cannot
17499
+ * recreate an archived teammate's website sessions after cleanup.
17500
+ */
17501
+ profileRevision: external_exports.number().int().min(0).optional(),
17442
17502
  /** Cloud-minted; the host adopts it for the session it opens. */
17443
17503
  browserSessionId: external_exports.string().min(1).max(200),
17444
17504
  viewport: BrowserViewport
@@ -17467,6 +17527,18 @@ var BrowserCommandFrame = external_exports.object({
17467
17527
  browserSessionId: external_exports.string().min(1).max(200),
17468
17528
  command: BrowserCommand
17469
17529
  });
17530
+ var BrowserProfilePurge = external_exports.object({
17531
+ type: external_exports.literal("browser.profile.purge"),
17532
+ purgeId: external_exports.uuid(),
17533
+ agentId: AgentId,
17534
+ profileRevision: external_exports.number().int().min(1)
17535
+ }).strict();
17536
+ var BrowserProfilePurged = external_exports.object({
17537
+ type: external_exports.literal("browser.profile.purged"),
17538
+ purgeId: external_exports.uuid(),
17539
+ agentId: AgentId,
17540
+ profileRevision: external_exports.number().int().min(1)
17541
+ }).strict();
17470
17542
  var TaskInput = external_exports.object({
17471
17543
  type: external_exports.literal("task.input"),
17472
17544
  taskId: TaskId,
@@ -17480,7 +17552,8 @@ var DurableDownMessage = external_exports.discriminatedUnion("type", [
17480
17552
  TaskInput,
17481
17553
  SecretsGrant,
17482
17554
  ApprovalDecision,
17483
- ConnectionsGrant
17555
+ ConnectionsGrant,
17556
+ BrowserProfilePurge
17484
17557
  ]);
17485
17558
  var TaskEvent = external_exports.object({
17486
17559
  type: external_exports.literal("task.event"),
@@ -17633,6 +17706,15 @@ var HelloFrame = external_exports.object({
17633
17706
  protocolVersion: external_exports.number().int(),
17634
17707
  /** Highest outbox seq the host has durably processed; cloud replays after it. */
17635
17708
  cursor: external_exports.number().int().min(0),
17709
+ /**
17710
+ * Bounded, path-validated persistent profile inventory from this Machine.
17711
+ * It migrates profiles created before cloud custody tracking existed.
17712
+ */
17713
+ browserProfileInventory: external_exports.object({
17714
+ agentIds: external_exports.array(AgentId).max(BROWSER_PROFILE_INVENTORY_PAGE_SIZE),
17715
+ /** False keeps conservative legacy migration fencing enabled. */
17716
+ complete: external_exports.boolean()
17717
+ }).strict().optional(),
17636
17718
  /**
17637
17719
  * Exact assignments whose local executors finished unwinding after the
17638
17720
  * prior socket died. Optional for rolling v5 hosts; repeated references are
@@ -17640,6 +17722,11 @@ var HelloFrame = external_exports.object({
17640
17722
  */
17641
17723
  unwoundAssignments: external_exports.array(UnwoundAssignmentRef).max(1e3).optional()
17642
17724
  });
17725
+ var BrowserProfileInventoryFrame = external_exports.object({
17726
+ type: external_exports.literal("browser.profile.inventory"),
17727
+ agentIds: external_exports.array(AgentId).max(BROWSER_PROFILE_INVENTORY_PAGE_SIZE),
17728
+ complete: external_exports.boolean()
17729
+ }).strict();
17643
17730
  var HelloAckFrame = external_exports.object({
17644
17731
  type: external_exports.literal("helloAck"),
17645
17732
  protocolVersion: external_exports.number().int(),
@@ -17722,6 +17809,7 @@ var PingFrame = external_exports.object({ type: external_exports.literal("ping")
17722
17809
  var PongFrame = external_exports.object({ type: external_exports.literal("pong"), at: external_exports.iso.datetime() });
17723
17810
  var HostToCloudFrame = external_exports.discriminatedUnion("type", [
17724
17811
  HelloFrame,
17812
+ BrowserProfileInventoryFrame,
17725
17813
  AckFrame,
17726
17814
  UpFrame,
17727
17815
  ProviderOperationGrantRequestFrame,
@@ -17729,6 +17817,7 @@ var HostToCloudFrame = external_exports.discriminatedUnion("type", [
17729
17817
  BrowserScreencastFrame,
17730
17818
  BrowserSessionEndedFrame,
17731
17819
  BrowserCredentialRequestFrame,
17820
+ BrowserProfilePurged,
17732
17821
  PingFrame,
17733
17822
  PongFrame
17734
17823
  ]);
@@ -18045,7 +18134,9 @@ var HostRemovalOutcome = external_exports.object({
18045
18134
  });
18046
18135
  var RevokeHostResponse = external_exports.object({
18047
18136
  host: AdminHostProjection,
18048
- removal: HostRemovalOutcome
18137
+ removal: HostRemovalOutcome,
18138
+ /** False while the paired Host is retained solely to finish profile deletion. */
18139
+ removed: external_exports.boolean()
18049
18140
  });
18050
18141
  var MemberHostProjection = Host.pick({
18051
18142
  id: true,
@@ -18054,6 +18145,7 @@ var MemberHostProjection = Host.pick({
18054
18145
  operatingSystem: true,
18055
18146
  status: true,
18056
18147
  acceptingNewWork: true,
18148
+ profileCleanup: true,
18057
18149
  kind: true,
18058
18150
  lastSeenAt: true,
18059
18151
  telemetryAt: true,
@@ -18632,22 +18724,60 @@ var UpdateManagerRequest = external_exports.object({
18632
18724
  ctx.addIssue({ code: "custom", message: "nothing to update" });
18633
18725
  }
18634
18726
  });
18727
+ var ManagerMemoryKind = external_exports.enum([
18728
+ /** How recurring organization work is actually done ("releases ship Thursday"). */
18729
+ "process",
18730
+ /** A settled organization decision and, ideally, why. */
18731
+ "decision",
18732
+ /** A standing preference for how the organization wants to be served. */
18733
+ "preference",
18734
+ /** A durable organization fact that is not derivable from configuration. */
18735
+ "fact"
18736
+ ]);
18737
+ var MANAGER_MEMORY_SUBJECT_MAX = 60;
18738
+ var MANAGER_MEMORY_TEXT_MAX = 300;
18739
+ var MANAGER_MEMORY_MAX_ITEMS = 60;
18740
+ var ManagerMemoryProjection = external_exports.object({
18741
+ id: ManagerMemoryId,
18742
+ kind: ManagerMemoryKind,
18743
+ /** Short normalized topic; (kind, subject) is the deduplication key. */
18744
+ subject: external_exports.string().min(1).max(MANAGER_MEMORY_SUBJECT_MAX),
18745
+ text: external_exports.string().min(1).max(MANAGER_MEMORY_TEXT_MAX),
18746
+ /** Thread the Manager learned it in, for provenance. */
18747
+ sourceConversationId: ConversationId.nullable(),
18748
+ createdAt: external_exports.string(),
18749
+ updatedAt: external_exports.string()
18750
+ }).strict();
18751
+ var ListManagerMemoriesResponse = external_exports.object({
18752
+ memories: external_exports.array(ManagerMemoryProjection).max(MANAGER_MEMORY_MAX_ITEMS),
18753
+ limit: external_exports.number().int().min(1)
18754
+ }).strict();
18635
18755
  var ConversationChannel = external_exports.enum(["web", "slack", "whatsapp"]);
18636
18756
  var ConversationStatus = external_exports.enum(["idle", "thinking"]);
18637
18757
  var ConversationProjection = external_exports.object({
18638
18758
  id: ConversationId,
18639
- /** First message prefix until the Manager supplies a concise title. */
18759
+ /** First-message prefix until the one creation-time Manager title call settles. */
18640
18760
  title: external_exports.string().min(1).max(200),
18641
18761
  channel: ConversationChannel,
18642
18762
  status: ConversationStatus,
18643
18763
  /** TS-10 origin trust of the channel; propagated into every delegated Task. */
18644
18764
  trust: external_exports.enum(["internal", "external"]),
18645
- /** Subject of whoever opened the thread (member subject or provider actor). */
18646
- createdBy: external_exports.string(),
18765
+ /** One-use override for the next Task delegated from this thread. */
18766
+ nextDelegationRunner: TaskRunnerSelection.nullable(),
18647
18767
  createdAt: external_exports.string(),
18648
18768
  lastActivityAt: external_exports.string(),
18769
+ /** Archived, but at least one executor has not yet proven it stopped. */
18770
+ archiveState: external_exports.literal("stopping").nullable().optional(),
18649
18771
  archivedAt: external_exports.string().nullable()
18650
- }).strict();
18772
+ }).strict().superRefine((conversation, ctx) => {
18773
+ if (conversation.archiveState === "stopping" && conversation.archivedAt === null) {
18774
+ ctx.addIssue({
18775
+ code: "custom",
18776
+ path: ["archivedAt"],
18777
+ message: "a stopping Manager Task must already be in Archived"
18778
+ });
18779
+ }
18780
+ });
18651
18781
  var conversationEventBase = {
18652
18782
  id: ConversationEventId,
18653
18783
  at: external_exports.string()
@@ -18658,7 +18788,9 @@ var ConversationEventProjection = external_exports.discriminatedUnion("kind", [
18658
18788
  ...conversationEventBase,
18659
18789
  kind: external_exports.literal("user"),
18660
18790
  text: external_exports.string(),
18661
- author: external_exports.object({ subject: external_exports.string(), displayName: external_exports.string().nullable() }).strict()
18791
+ /** Display attribution only; authentication subjects remain internal. */
18792
+ author: external_exports.object({ displayName: external_exports.string().nullable() }).strict(),
18793
+ attachments: external_exports.array(TaskAttachmentRef).max(TASK_MESSAGE_MAX_ATTACHMENTS).optional()
18662
18794
  }).strict(),
18663
18795
  /** The Manager's prose reply. */
18664
18796
  external_exports.object({ ...conversationEventBase, kind: external_exports.literal("manager"), text: external_exports.string() }).strict(),
@@ -18677,21 +18809,70 @@ var ConversationEventProjection = external_exports.discriminatedUnion("kind", [
18677
18809
  kind: external_exports.literal("task"),
18678
18810
  taskId: TaskId,
18679
18811
  agentId: AgentId.nullable(),
18680
- eventKind: external_exports.enum(["response", "error", "status", "question"]),
18812
+ eventKind: external_exports.enum(["response", "error", "status", "question", "message"]),
18681
18813
  summary: external_exports.string(),
18682
- taskStatus: TaskStatus.nullable()
18814
+ taskStatus: TaskStatus.nullable(),
18815
+ /**
18816
+ * Opaque presentation key shared by the useful report and its synthetic
18817
+ * terminal wake-up. It is never shown to people; clients use it only to
18818
+ * render one outcome when recovery commits those records out of order.
18819
+ * Missing on legacy/non-terminal events.
18820
+ */
18821
+ terminalGroup: external_exports.string().min(1).max(200).nullable().optional()
18683
18822
  }).strict(),
18684
18823
  /** A Manager loop failure a person should see (provider outage, refusal). */
18685
18824
  external_exports.object({ ...conversationEventBase, kind: external_exports.literal("error"), message: external_exports.string() }).strict()
18686
18825
  ]);
18826
+ var NonEmptyTaskRunnerSelection = TaskRunnerSelection.refine(
18827
+ (selection) => selection.type !== void 0 || selection.model !== void 0 || selection.effort !== void 0,
18828
+ "choose at least one runtime preference"
18829
+ );
18687
18830
  var CreateConversationRequest = external_exports.object({
18688
18831
  /** Stable client-generated key: retrying the same submission returns one Conversation. */
18689
18832
  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();
18833
+ message: external_exports.string().max(CONVERSATION_MESSAGE_MAX),
18834
+ attachmentIds: external_exports.array(AttachmentId).max(TASK_MESSAGE_MAX_ATTACHMENTS).refine((ids) => new Set(ids).size === ids.length, "duplicate attachment").optional(),
18835
+ /**
18836
+ * Teammate whose persistent Browser is selected beside this web turn.
18837
+ * This is bounded routing context for Browser-directed requests only; it
18838
+ * is never a general delegation preference.
18839
+ */
18840
+ browserAgentId: AgentId.optional(),
18841
+ /**
18842
+ * One-time runtime override for the first Task this thread delegates.
18843
+ * Omission preserves the selected teammate's configured defaults.
18844
+ */
18845
+ initialDelegation: external_exports.object({
18846
+ runner: NonEmptyTaskRunnerSelection
18847
+ }).strict().optional()
18848
+ }).strict().superRefine((value, ctx) => {
18849
+ if (!value.message.trim() && !value.attachmentIds?.length) {
18850
+ ctx.addIssue({
18851
+ code: "custom",
18852
+ path: ["message"],
18853
+ message: "a message needs text or an attachment"
18854
+ });
18855
+ }
18856
+ });
18857
+ var PostConversationMessageRequest = external_exports.object({
18858
+ /** Stable client-generated key: an ambiguous retry appends exactly once. */
18859
+ requestId: external_exports.uuid(),
18860
+ message: external_exports.string().max(CONVERSATION_MESSAGE_MAX),
18861
+ attachmentIds: external_exports.array(AttachmentId).max(TASK_MESSAGE_MAX_ATTACHMENTS).refine((ids) => new Set(ids).size === ids.length, "duplicate attachment").optional(),
18862
+ /** Selected Manager Browser teammate for Browser-directed requests only. */
18863
+ browserAgentId: AgentId.optional()
18864
+ }).strict().superRefine((value, ctx) => {
18865
+ if (!value.message.trim() && !value.attachmentIds?.length) {
18866
+ ctx.addIssue({
18867
+ code: "custom",
18868
+ path: ["message"],
18869
+ message: "a message needs text or an attachment"
18870
+ });
18871
+ }
18872
+ });
18873
+ var UpdateNextDelegationRuntimeRequest = external_exports.object({ runner: NonEmptyTaskRunnerSelection.nullable() }).strict();
18693
18874
  var UpdateConversationRequest = external_exports.object({
18694
- title: external_exports.string().min(1).max(200).optional(),
18875
+ title: external_exports.string().trim().min(1).max(200).optional(),
18695
18876
  archived: external_exports.boolean().optional()
18696
18877
  }).strict().superRefine((value, ctx) => {
18697
18878
  if (value.title === void 0 && value.archived === void 0) {
@@ -18699,7 +18880,47 @@ var UpdateConversationRequest = external_exports.object({
18699
18880
  }
18700
18881
  });
18701
18882
  var ConversationResponse = external_exports.object({ conversation: ConversationProjection }).strict();
18702
- var ListConversationsResponse = external_exports.object({ conversations: external_exports.array(ConversationProjection).max(CONVERSATION_LIST_LIMIT) }).strict();
18883
+ var ListConversationsResponse = external_exports.object({
18884
+ conversations: external_exports.array(ConversationProjection).max(CONVERSATION_LIST_LIMIT),
18885
+ nextCursor: external_exports.string().nullable()
18886
+ }).strict();
18887
+ var ManagerTaskRailSummariesRequest = external_exports.object({
18888
+ conversationIds: external_exports.array(ConversationId).min(1).max(CONVERSATION_LIST_LIMIT),
18889
+ archived: external_exports.boolean(),
18890
+ agentId: AgentId.optional(),
18891
+ statuses: external_exports.array(TaskStatus).min(1).max(TaskStatus.options.length).optional()
18892
+ }).strict().superRefine((value, ctx) => {
18893
+ if (new Set(value.conversationIds).size !== value.conversationIds.length) {
18894
+ ctx.addIssue({
18895
+ code: "custom",
18896
+ path: ["conversationIds"],
18897
+ message: "conversationIds must be unique"
18898
+ });
18899
+ }
18900
+ if (value.statuses && new Set(value.statuses).size !== value.statuses.length) {
18901
+ ctx.addIssue({
18902
+ code: "custom",
18903
+ path: ["statuses"],
18904
+ message: "statuses must be unique"
18905
+ });
18906
+ }
18907
+ });
18908
+ var ManagerTaskRailRepresentative = external_exports.object({
18909
+ id: TaskId,
18910
+ agentId: AgentId,
18911
+ status: TaskStatus,
18912
+ gitSummary: TaskGitSummary.nullable(),
18913
+ createdAt: external_exports.string(),
18914
+ updatedAt: external_exports.string()
18915
+ }).strict();
18916
+ var ManagerTaskRailSummary = external_exports.object({
18917
+ conversationId: ConversationId,
18918
+ hasChildren: external_exports.boolean(),
18919
+ hasAgentMatch: external_exports.boolean(),
18920
+ hasStatusMatch: external_exports.boolean(),
18921
+ representative: ManagerTaskRailRepresentative.nullable()
18922
+ }).strict();
18923
+ var ManagerTaskRailSummariesResponse = external_exports.object({ summaries: external_exports.array(ManagerTaskRailSummary).max(CONVERSATION_LIST_LIMIT) }).strict();
18703
18924
  var ConversationDetailResponse = external_exports.object({
18704
18925
  conversation: ConversationProjection,
18705
18926
  events: external_exports.array(ConversationEventProjection)
@@ -19451,7 +19672,7 @@ async function generateTaskTitle(instructions, runner) {
19451
19672
  instructions.slice(0, INSTRUCTIONS_BUDGET),
19452
19673
  "</task_request>"
19453
19674
  ].join("\n");
19454
- return new Promise((resolve14) => {
19675
+ return new Promise((resolve15) => {
19455
19676
  const child = spawnCli(
19456
19677
  command,
19457
19678
  [
@@ -19474,7 +19695,7 @@ async function generateTaskTitle(instructions, runner) {
19474
19695
  if (settled) return;
19475
19696
  settled = true;
19476
19697
  clearTimeout(timer);
19477
- resolve14(value);
19698
+ resolve15(value);
19478
19699
  };
19479
19700
  const timer = setTimeout(() => {
19480
19701
  child.kill();
@@ -19796,11 +20017,11 @@ function createWorkerWatchdogSendDrain() {
19796
20017
  if (completed) return;
19797
20018
  completed = true;
19798
20019
  pending--;
19799
- if (pending === 0) drained.splice(0).forEach((resolve14) => resolve14());
20020
+ if (pending === 0) drained.splice(0).forEach((resolve15) => resolve15());
19800
20021
  };
19801
20022
  },
19802
20023
  drain: async () => {
19803
- if (pending > 0) await new Promise((resolve14) => drained.push(resolve14));
20024
+ if (pending > 0) await new Promise((resolve15) => drained.push(resolve15));
19804
20025
  }
19805
20026
  };
19806
20027
  }
@@ -20045,7 +20266,7 @@ async function waitForOperationGrantRetry(retryAt, signal) {
20045
20266
  const deadline = Date.parse(retryAt);
20046
20267
  if (!Number.isFinite(deadline) || signal.aborted) return false;
20047
20268
  if (deadline <= Date.now()) return true;
20048
- return await new Promise((resolve14) => {
20269
+ return await new Promise((resolve15) => {
20049
20270
  let settled = false;
20050
20271
  let timer;
20051
20272
  const finish = (ready) => {
@@ -20053,7 +20274,7 @@ async function waitForOperationGrantRetry(retryAt, signal) {
20053
20274
  settled = true;
20054
20275
  if (timer) clearTimeout(timer);
20055
20276
  signal.removeEventListener("abort", onAbort);
20056
- resolve14(ready);
20277
+ resolve15(ready);
20057
20278
  };
20058
20279
  const onAbort = () => finish(false);
20059
20280
  const schedule = () => {
@@ -20321,22 +20542,22 @@ var HostClient = class _HostClient {
20321
20542
  const unwindingAssignments = [...this.activeAssignments.values()];
20322
20543
  for (const cancel of this.cancels.values()) cancel(stopReason);
20323
20544
  for (const entry of this.secretGrants.values()) {
20324
- for (const resolve14 of entry.resolvers) resolve14({});
20545
+ for (const resolve15 of entry.resolvers) resolve15({});
20325
20546
  entry.resolvers = [];
20326
20547
  delete entry.value;
20327
20548
  }
20328
20549
  for (const entry of this.connectionGrants.values()) {
20329
- for (const resolve14 of entry.resolvers) resolve14([]);
20550
+ for (const resolve15 of entry.resolvers) resolve15([]);
20330
20551
  entry.resolvers = [];
20331
20552
  delete entry.value;
20332
20553
  }
20333
20554
  for (const entry of this.providerGrants.values()) {
20334
- for (const resolve14 of entry.resolvers) resolve14([]);
20555
+ for (const resolve15 of entry.resolvers) resolve15([]);
20335
20556
  entry.resolvers = [];
20336
20557
  delete entry.value;
20337
20558
  }
20338
20559
  for (const waiters of this.approvalWaiters.values()) {
20339
- for (const resolve14 of waiters.values()) resolve14({ approved: false, guidance: reason });
20560
+ for (const resolve15 of waiters.values()) resolve15({ approved: false, guidance: reason });
20340
20561
  }
20341
20562
  for (const waiters of this.agentOpWaiters.values()) {
20342
20563
  for (const waiter of waiters.values()) {
@@ -20362,9 +20583,9 @@ var HostClient = class _HostClient {
20362
20583
  let drainTimer;
20363
20584
  const drained = await Promise.race([
20364
20585
  Promise.allSettled(runs).then(() => true),
20365
- new Promise((resolve14) => {
20586
+ new Promise((resolve15) => {
20366
20587
  drainTimer = setTimeout(
20367
- () => resolve14(false),
20588
+ () => resolve15(false),
20368
20589
  this.opts.unwindTimeoutMs ?? _HostClient.DEFAULT_UNWIND_TIMEOUT_MS
20369
20590
  );
20370
20591
  drainTimer.unref?.();
@@ -20395,9 +20616,70 @@ var HostClient = class _HostClient {
20395
20616
  let awaitingHeartbeatReply = false;
20396
20617
  let closeReason;
20397
20618
  let frameTail = Promise.resolve();
20619
+ let inventoryRetryTimer;
20620
+ let inventoryRetryAttempts = 0;
20621
+ const inventoryRetryBaseMs = Math.max(20, Number(this.opts.backoffMs ?? 1e3));
20398
20622
  this.ws = ws;
20399
20623
  this.protocolReady = false;
20624
+ const retryInventory = async () => {
20625
+ if (this.stopped || this.ws !== ws || ws.readyState !== WebSocket.OPEN) return;
20626
+ let sawComplete = false;
20627
+ try {
20628
+ const pages = this.opts.browser?.profileInventoryPages?.();
20629
+ if (pages) {
20630
+ for await (const page2 of pages) {
20631
+ if (!this.send(
20632
+ {
20633
+ type: "browser.profile.inventory",
20634
+ agentIds: page2.agentIds,
20635
+ complete: page2.complete
20636
+ },
20637
+ ws
20638
+ )) {
20639
+ return;
20640
+ }
20641
+ sawComplete = page2.complete;
20642
+ }
20643
+ } else {
20644
+ const page2 = await this.opts.browser?.profileInventory?.();
20645
+ if (!page2) return;
20646
+ if (!this.send(
20647
+ {
20648
+ type: "browser.profile.inventory",
20649
+ agentIds: page2.agentIds,
20650
+ complete: page2.complete
20651
+ },
20652
+ ws
20653
+ )) {
20654
+ return;
20655
+ }
20656
+ sawComplete = page2.complete;
20657
+ }
20658
+ } catch {
20659
+ sawComplete = false;
20660
+ }
20661
+ if (sawComplete) {
20662
+ inventoryRetryAttempts = 0;
20663
+ } else {
20664
+ scheduleInventoryRetry();
20665
+ }
20666
+ };
20667
+ function scheduleInventoryRetry() {
20668
+ if (inventoryRetryTimer || ws.readyState !== WebSocket.OPEN) return;
20669
+ const delayMs = Math.min(
20670
+ 6e4,
20671
+ inventoryRetryBaseMs * 2 ** Math.min(inventoryRetryAttempts++, 6)
20672
+ );
20673
+ inventoryRetryTimer = setTimeout(() => {
20674
+ inventoryRetryTimer = void 0;
20675
+ void retryInventory();
20676
+ }, delayMs);
20677
+ inventoryRetryTimer.unref?.();
20678
+ }
20400
20679
  ws.on("open", () => {
20680
+ const inventoryConfigured = Boolean(
20681
+ this.opts.browser?.profileInventoryPages || this.opts.browser?.profileInventory
20682
+ );
20401
20683
  const unwoundAssignments = [...this.pendingUnwoundAssignments.values()].sort(
20402
20684
  (left, right) => left.taskId === right.taskId ? left.epoch - right.epoch : left.taskId.localeCompare(right.taskId)
20403
20685
  );
@@ -20406,6 +20688,7 @@ var HostClient = class _HostClient {
20406
20688
  type: "hello",
20407
20689
  protocolVersion: PROTOCOL_VERSION,
20408
20690
  cursor: this.cursor,
20691
+ ...inventoryConfigured ? { browserProfileInventory: { agentIds: [], complete: false } } : {},
20409
20692
  ...unwoundAssignments.length > 0 ? { unwoundAssignments } : {}
20410
20693
  },
20411
20694
  ws
@@ -20422,6 +20705,7 @@ var HostClient = class _HostClient {
20422
20705
  this.send({ type: "ping", at: (/* @__PURE__ */ new Date()).toISOString() });
20423
20706
  void this.reportTelemetry();
20424
20707
  }, heartbeatMs);
20708
+ if (inventoryConfigured) void retryInventory();
20425
20709
  });
20426
20710
  ws.on("message", (raw) => {
20427
20711
  if (this.ws !== ws) return;
@@ -20431,6 +20715,7 @@ var HostClient = class _HostClient {
20431
20715
  });
20432
20716
  });
20433
20717
  ws.on("close", (code) => {
20718
+ if (inventoryRetryTimer) clearTimeout(inventoryRetryTimer);
20434
20719
  void this.handleClose(ws, code, closeReason, frameTail);
20435
20720
  });
20436
20721
  ws.on("error", () => {
@@ -20444,9 +20729,9 @@ var HostClient = class _HostClient {
20444
20729
  let frameDrainTimer;
20445
20730
  const framesDrained = await Promise.race([
20446
20731
  frameTail.then(() => true),
20447
- new Promise((resolve14) => {
20732
+ new Promise((resolve15) => {
20448
20733
  frameDrainTimer = setTimeout(
20449
- () => resolve14(false),
20734
+ () => resolve15(false),
20450
20735
  this.opts.unwindTimeoutMs ?? _HostClient.DEFAULT_UNWIND_TIMEOUT_MS
20451
20736
  );
20452
20737
  frameDrainTimer.unref?.();
@@ -20823,7 +21108,13 @@ var HostClient = class _HostClient {
20823
21108
  case "browser.open": {
20824
21109
  const bridge = this.opts.browser;
20825
21110
  if (!bridge) return;
20826
- void bridge.open(frame.agentId, frame.browserSessionId, frame.viewport, frame.requestId).catch(() => {
21111
+ void bridge.open(
21112
+ frame.agentId,
21113
+ frame.browserSessionId,
21114
+ frame.viewport,
21115
+ frame.requestId,
21116
+ frame.profileRevision
21117
+ ).catch(() => {
20827
21118
  this.sendBrowserFrame({
20828
21119
  type: "browser.session.ended",
20829
21120
  browserSessionId: frame.browserSessionId,
@@ -20834,7 +21125,8 @@ var HostClient = class _HostClient {
20834
21125
  return;
20835
21126
  }
20836
21127
  case "browser.close":
20837
- void this.opts.browser?.close(frame.agentId, "stopped");
21128
+ void this.opts.browser?.close(frame.agentId, "stopped").catch(() => {
21129
+ });
20838
21130
  return;
20839
21131
  case "browser.view":
20840
21132
  void this.opts.browser?.setViewers(frame.agentId, frame.active, frame.viewerCount);
@@ -20935,8 +21227,34 @@ var HostClient = class _HostClient {
20935
21227
  async handleMessage(message) {
20936
21228
  switch (message.type) {
20937
21229
  case "task.assign":
21230
+ if (message.browserProfileRevision !== void 0) {
21231
+ try {
21232
+ await this.opts.browser?.authorizeProfile?.(
21233
+ message.agentId,
21234
+ message.browserProfileRevision
21235
+ );
21236
+ } catch {
21237
+ }
21238
+ }
20938
21239
  this.startTask(message);
20939
21240
  return;
21241
+ case "browser.profile.purge": {
21242
+ const browser = this.opts.browser;
21243
+ void (async () => {
21244
+ try {
21245
+ if (!browser?.purge) return;
21246
+ await browser.purge(message.agentId, message.purgeId, message.profileRevision);
21247
+ this.send({
21248
+ type: "browser.profile.purged",
21249
+ purgeId: message.purgeId,
21250
+ agentId: message.agentId,
21251
+ profileRevision: message.profileRevision
21252
+ });
21253
+ } catch {
21254
+ }
21255
+ })();
21256
+ return;
21257
+ }
20940
21258
  case "task.cancel":
20941
21259
  this.cancels.get(`${message.taskId}:${message.epoch}`)?.(
20942
21260
  message.purpose === "credential_rollover" ? "credential_rollover" : "cloud_cancel"
@@ -20957,7 +21275,7 @@ var HostClient = class _HostClient {
20957
21275
  const entry = this.secretGrants.get(key) ?? { resolvers: [] };
20958
21276
  entry.value = message.secrets;
20959
21277
  entry.expiresAt = expiresAt;
20960
- for (const resolve14 of entry.resolvers) resolve14(message.secrets);
21278
+ for (const resolve15 of entry.resolvers) resolve15(message.secrets);
20961
21279
  entry.resolvers = [];
20962
21280
  this.secretGrants.set(key, entry);
20963
21281
  return;
@@ -20988,13 +21306,13 @@ var HostClient = class _HostClient {
20988
21306
  const entry = this.connectionGrants.get(key) ?? { resolvers: [] };
20989
21307
  entry.value = message.connections;
20990
21308
  entry.expiresAt = expiresAt;
20991
- for (const resolve14 of entry.resolvers) resolve14(message.connections);
21309
+ for (const resolve15 of entry.resolvers) resolve15(message.connections);
20992
21310
  entry.resolvers = [];
20993
21311
  this.connectionGrants.set(key, entry);
20994
21312
  const providerEntry = this.providerGrants.get(key) ?? { resolvers: [] };
20995
21313
  providerEntry.value = providers;
20996
21314
  providerEntry.expiresAt = authorityExpiresAt;
20997
- for (const resolve14 of providerEntry.resolvers) resolve14(providers);
21315
+ for (const resolve15 of providerEntry.resolvers) resolve15(providers);
20998
21316
  providerEntry.resolvers = [];
20999
21317
  this.providerGrants.set(key, providerEntry);
21000
21318
  return;
@@ -21128,8 +21446,8 @@ var HostClient = class _HostClient {
21128
21446
  return redactCredentialText(text, sensitiveSnapshot()).slice(0, maxLength);
21129
21447
  };
21130
21448
  let resolveCancelled;
21131
- const cancelledPromise = new Promise((resolve14) => {
21132
- resolveCancelled = resolve14;
21449
+ const cancelledPromise = new Promise((resolve15) => {
21450
+ resolveCancelled = resolve15;
21133
21451
  });
21134
21452
  const endAuthority = (reason = "cloud_cancel") => {
21135
21453
  if (stopReason) return;
@@ -21138,21 +21456,21 @@ var HostClient = class _HostClient {
21138
21456
  authorityController.abort(reason);
21139
21457
  const secretEntry = this.secretGrants.get(cancelKey);
21140
21458
  if (secretEntry) {
21141
- for (const resolve14 of secretEntry.resolvers) resolve14({});
21459
+ for (const resolve15 of secretEntry.resolvers) resolve15({});
21142
21460
  secretEntry.resolvers = [];
21143
21461
  delete secretEntry.value;
21144
21462
  }
21145
21463
  this.secretGrants.delete(cancelKey);
21146
21464
  const connectionEntry = this.connectionGrants.get(cancelKey);
21147
21465
  if (connectionEntry) {
21148
- for (const resolve14 of connectionEntry.resolvers) resolve14([]);
21466
+ for (const resolve15 of connectionEntry.resolvers) resolve15([]);
21149
21467
  connectionEntry.resolvers = [];
21150
21468
  delete connectionEntry.value;
21151
21469
  }
21152
21470
  this.connectionGrants.delete(cancelKey);
21153
21471
  const providerEntry = this.providerGrants.get(cancelKey);
21154
21472
  if (providerEntry) {
21155
- for (const resolve14 of providerEntry.resolvers) resolve14([]);
21473
+ for (const resolve15 of providerEntry.resolvers) resolve15([]);
21156
21474
  providerEntry.resolvers = [];
21157
21475
  delete providerEntry.value;
21158
21476
  }
@@ -21160,8 +21478,8 @@ var HostClient = class _HostClient {
21160
21478
  this.clearAuthorityExpiry(cancelKey);
21161
21479
  const approvalWaiters = this.approvalWaiters.get(cancelKey);
21162
21480
  if (approvalWaiters) {
21163
- for (const resolve14 of approvalWaiters.values()) {
21164
- resolve14({ approved: false, guidance: "task was cancelled" });
21481
+ for (const resolve15 of approvalWaiters.values()) {
21482
+ resolve15({ approved: false, guidance: "task was cancelled" });
21165
21483
  }
21166
21484
  approvalWaiters.clear();
21167
21485
  }
@@ -21287,9 +21605,9 @@ var HostClient = class _HostClient {
21287
21605
  return value;
21288
21606
  };
21289
21607
  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);
21608
+ return new Promise((resolve15) => {
21609
+ entry.resolvers.push((value) => resolve15(capture(value)));
21610
+ setTimeout(() => resolve15(capture(entry.value ?? {})), _HostClient.SECRETS_WAIT_MS);
21293
21611
  });
21294
21612
  };
21295
21613
  const connections = () => {
@@ -21306,9 +21624,9 @@ var HostClient = class _HostClient {
21306
21624
  return value;
21307
21625
  };
21308
21626
  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);
21627
+ return new Promise((resolve15) => {
21628
+ entry.resolvers.push((value) => resolve15(capture(value)));
21629
+ setTimeout(() => resolve15(capture(entry.value ?? [])), _HostClient.SECRETS_WAIT_MS);
21312
21630
  });
21313
21631
  };
21314
21632
  const providers = () => {
@@ -21325,9 +21643,9 @@ var HostClient = class _HostClient {
21325
21643
  return value;
21326
21644
  };
21327
21645
  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);
21646
+ return new Promise((resolve15) => {
21647
+ entry.resolvers.push((value) => resolve15(capture(value)));
21648
+ setTimeout(() => resolve15(capture(entry.value ?? [])), _HostClient.SECRETS_WAIT_MS);
21331
21649
  });
21332
21650
  };
21333
21651
  const linear = async () => {
@@ -21353,13 +21671,13 @@ var HostClient = class _HostClient {
21353
21671
  payload: safe(payload, 5e4),
21354
21672
  ...questionChoices ? { questionChoices: [...questionChoices] } : {}
21355
21673
  });
21356
- return new Promise((resolve14) => {
21674
+ return new Promise((resolve15) => {
21357
21675
  const waiters = this.approvalWaiters.get(cancelKey) ?? /* @__PURE__ */ new Map();
21358
21676
  this.approvalWaiters.set(cancelKey, waiters);
21359
- waiters.set(requestId, resolve14);
21677
+ waiters.set(requestId, resolve15);
21360
21678
  void cancelledPromise.then(() => {
21361
21679
  if (waiters.delete(requestId)) {
21362
- resolve14({ approved: false, guidance: "task was cancelled" });
21680
+ resolve15({ approved: false, guidance: "task was cancelled" });
21363
21681
  }
21364
21682
  });
21365
21683
  });
@@ -21397,11 +21715,11 @@ var HostClient = class _HostClient {
21397
21715
  if (existing) message = existing;
21398
21716
  else terminalMessages.set(requestId, message);
21399
21717
  }
21400
- return new Promise((resolve14) => {
21718
+ return new Promise((resolve15) => {
21401
21719
  const waiters = this.agentOpWaiters.get(cancelKey) ?? /* @__PURE__ */ new Map();
21402
21720
  this.agentOpWaiters.set(cancelKey, waiters);
21403
21721
  if (waiters.has(requestId)) {
21404
- resolve14({ ok: false, error: "provider settlement request is already in flight" });
21722
+ resolve15({ ok: false, error: "provider settlement request is already in flight" });
21405
21723
  return;
21406
21724
  }
21407
21725
  const timer = setTimeout(() => {
@@ -21412,7 +21730,7 @@ var HostClient = class _HostClient {
21412
21730
  (pending) => !(pending.type === "agent.op" && pending.requestId === requestId)
21413
21731
  );
21414
21732
  }
21415
- resolve14({
21733
+ resolve15({
21416
21734
  ok: false,
21417
21735
  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
21736
  });
@@ -21420,7 +21738,7 @@ var HostClient = class _HostClient {
21420
21738
  }, _HostClient.AGENT_OP_TIMEOUT_MS);
21421
21739
  timer.unref?.();
21422
21740
  waiters.set(requestId, {
21423
- resolve: resolve14,
21741
+ resolve: resolve15,
21424
21742
  timer,
21425
21743
  ...terminal ? { terminalMessage: message } : {}
21426
21744
  });
@@ -21466,12 +21784,12 @@ var HostClient = class _HostClient {
21466
21784
  "No GitHub change was attempted; the authority grant request was invalid."
21467
21785
  );
21468
21786
  }
21469
- const outcome = await new Promise((resolve14) => {
21787
+ const outcome = await new Promise((resolve15) => {
21470
21788
  const timer = setTimeout(() => {
21471
21789
  const waiter = this.operationGrantWaiters.get(requestId);
21472
21790
  if (!waiter) return;
21473
21791
  this.operationGrantWaiters.delete(requestId);
21474
- resolve14({ grant: null, retryable: true, reason: "no_reply_from_zixt" });
21792
+ resolve15({ grant: null, retryable: true, reason: "no_reply_from_zixt" });
21475
21793
  }, this.operationGrantTimeoutMs);
21476
21794
  timer.unref?.();
21477
21795
  this.operationGrantWaiters.set(requestId, {
@@ -21484,9 +21802,9 @@ var HostClient = class _HostClient {
21484
21802
  timer,
21485
21803
  accept: (grant) => {
21486
21804
  addSensitiveValues(providerGrantSensitiveValues(grant));
21487
- resolve14({ grant });
21805
+ resolve15({ grant });
21488
21806
  },
21489
- deny: (retryable, reason, detail, retryAt, retryCode) => resolve14({
21807
+ deny: (retryable, reason, detail, retryAt, retryCode) => resolve15({
21490
21808
  grant: null,
21491
21809
  retryable,
21492
21810
  reason,
@@ -21500,7 +21818,7 @@ var HostClient = class _HostClient {
21500
21818
  } catch {
21501
21819
  clearTimeout(timer);
21502
21820
  this.operationGrantWaiters.delete(requestId);
21503
- resolve14({ grant: null, retryable: false, reason: "connection_unavailable" });
21821
+ resolve15({ grant: null, retryable: false, reason: "connection_unavailable" });
21504
21822
  }
21505
21823
  });
21506
21824
  if (outcome.grant) {
@@ -21556,7 +21874,7 @@ var HostClient = class _HostClient {
21556
21874
  )
21557
21875
  );
21558
21876
  }
21559
- return new Promise((resolve14, reject3) => {
21877
+ return new Promise((resolve15, reject3) => {
21560
21878
  const timer = setTimeout(() => {
21561
21879
  if (this.browserCredentialWaiters.delete(requestId)) {
21562
21880
  reject3(
@@ -21575,7 +21893,7 @@ var HostClient = class _HostClient {
21575
21893
  timer,
21576
21894
  accept: (credential) => {
21577
21895
  addSensitiveValues(webLoginSensitiveValues(credential));
21578
- resolve14(credential);
21896
+ resolve15(credential);
21579
21897
  },
21580
21898
  deny: (reason) => reject3(new Error(reason))
21581
21899
  });
@@ -21881,6 +22199,9 @@ var ProcessTreeTerminationError = class extends Error {
21881
22199
  }
21882
22200
  name = "ProcessTreeTerminationError";
21883
22201
  };
22202
+ function posixIdentityReadMeansMissing(error52) {
22203
+ return ["ENOENT", "ESRCH"].includes(error52.code ?? "");
22204
+ }
21884
22205
  function commandContainsNonce(command, nonce) {
21885
22206
  const escaped = nonce.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
21886
22207
  return new RegExp(`(?:^|[\\s"'])${escaped}(?:$|[\\s"'])`).test(command);
@@ -21893,14 +22214,14 @@ async function observeWindowsGuardianNonce(pid, nonce) {
21893
22214
  "Windows runner identity could not be observed"
21894
22215
  );
21895
22216
  }
21896
- return new Promise((resolve14, reject3) => {
22217
+ return new Promise((resolve15, reject3) => {
21897
22218
  let done = false;
21898
22219
  const finish = (result) => {
21899
22220
  if (done) return;
21900
22221
  done = true;
21901
22222
  clearTimeout(timeout);
21902
22223
  if (result instanceof Error) reject3(result);
21903
- else resolve14(result);
22224
+ else resolve15(result);
21904
22225
  };
21905
22226
  const timeout = setTimeout(
21906
22227
  () => finish(
@@ -21938,14 +22259,14 @@ async function observePosixGuardianNonce(pid, nonce) {
21938
22259
  const args = command.toString("utf8").split("\0");
21939
22260
  return args.includes(nonce) ? "match" : "mismatch";
21940
22261
  } catch (error52) {
21941
- if (error52.code === "ENOENT") return "missing";
22262
+ if (posixIdentityReadMeansMissing(error52)) return "missing";
21942
22263
  throw new ProcessTreeTerminationError(
21943
22264
  "state_unknown",
21944
22265
  "POSIX runner identity could not be observed"
21945
22266
  );
21946
22267
  }
21947
22268
  }
21948
- return new Promise((resolve14, reject3) => {
22269
+ return new Promise((resolve15, reject3) => {
21949
22270
  const observer = spawn2("/bin/ps", ["-ww", "-o", "command=", "-p", String(pid)], {
21950
22271
  stdio: ["ignore", "pipe", "ignore"]
21951
22272
  });
@@ -21956,7 +22277,7 @@ async function observePosixGuardianNonce(pid, nonce) {
21956
22277
  done = true;
21957
22278
  clearTimeout(timeout);
21958
22279
  if (result instanceof Error) reject3(result);
21959
- else resolve14(result);
22280
+ else resolve15(result);
21960
22281
  };
21961
22282
  const timeout = setTimeout(() => {
21962
22283
  observer.kill("SIGKILL");
@@ -22003,7 +22324,7 @@ async function observeGuardianIdentity(pid, identity) {
22003
22324
  return process.platform === "win32" ? observeWindowsGuardianNonce(pid, identity.nonce) : observePosixGuardianNonce(pid, identity.nonce);
22004
22325
  }
22005
22326
  function delay(ms) {
22006
- return new Promise((resolve14) => setTimeout(resolve14, ms));
22327
+ return new Promise((resolve15) => setTimeout(resolve15, ms));
22007
22328
  }
22008
22329
  function posixProcessRecordsFromPs(output) {
22009
22330
  const records = [];
@@ -22036,7 +22357,7 @@ function posixProcessRecordsFromPs(output) {
22036
22357
  return records;
22037
22358
  }
22038
22359
  async function snapshotPosixProcesses() {
22039
- return new Promise((resolve14, reject3) => {
22360
+ return new Promise((resolve15, reject3) => {
22040
22361
  const observer = spawn2("/bin/ps", ["-axo", "uid=,pid=,ppid=,pgid=,stat="], {
22041
22362
  stdio: ["ignore", "pipe", "ignore"]
22042
22363
  });
@@ -22049,7 +22370,7 @@ async function snapshotPosixProcesses() {
22049
22370
  if (error52) reject3(error52);
22050
22371
  else {
22051
22372
  try {
22052
- resolve14(posixProcessRecordsFromPs(output));
22373
+ resolve15(posixProcessRecordsFromPs(output));
22053
22374
  } catch (caught) {
22054
22375
  reject3(caught);
22055
22376
  }
@@ -22384,7 +22705,7 @@ async function snapshotWindowsDescendants(rootPid) {
22384
22705
  "Windows process-tree observation could not start"
22385
22706
  );
22386
22707
  }
22387
- return new Promise((resolve14, reject3) => {
22708
+ return new Promise((resolve15, reject3) => {
22388
22709
  let done = false;
22389
22710
  const timeout = setTimeout(() => {
22390
22711
  if (done) return;
@@ -22411,7 +22732,7 @@ async function snapshotWindowsDescendants(rootPid) {
22411
22732
  return;
22412
22733
  }
22413
22734
  try {
22414
- resolve14(completeWindowsDescendantPids(rootPid, processes));
22735
+ resolve15(completeWindowsDescendantPids(rootPid, processes));
22415
22736
  } catch (caught) {
22416
22737
  reject3(caught);
22417
22738
  }
@@ -22458,7 +22779,7 @@ async function waitForProcessesExit(pids, timeoutMs) {
22458
22779
  }
22459
22780
  async function runTaskkill(pid, command, timeoutMs = TASKKILL_TIMEOUT_MS, independentlyTrackedPids = []) {
22460
22781
  const trustedCommand = command ?? defaultTaskkillCommand();
22461
- const result = await new Promise((resolve14, reject3) => {
22782
+ const result = await new Promise((resolve15, reject3) => {
22462
22783
  const killer = spawn2(trustedCommand, ["/PID", String(pid), "/T", "/F"], {
22463
22784
  stdio: ["ignore", "pipe", "pipe"],
22464
22785
  windowsHide: true
@@ -22493,7 +22814,7 @@ async function runTaskkill(pid, command, timeoutMs = TASKKILL_TIMEOUT_MS, indepe
22493
22814
  done = true;
22494
22815
  clearTimeout(timeout);
22495
22816
  if (error52) reject3(error52);
22496
- else resolve14({ code: killer.exitCode, output, outputTruncated });
22817
+ else resolve15({ code: killer.exitCode, output, outputTruncated });
22497
22818
  };
22498
22819
  killer.once(
22499
22820
  "error",
@@ -23170,12 +23491,12 @@ async function createWindowsJobContainment(pid, options) {
23170
23491
  stderr = `${stderr}${String(chunk)}`.slice(-HELPER_OUTPUT_LIMIT);
23171
23492
  });
23172
23493
  const helperEvents = helper;
23173
- const exited = new Promise((resolve14) => {
23494
+ const exited = new Promise((resolve15) => {
23174
23495
  let completed = false;
23175
23496
  const complete = (code, signal) => {
23176
23497
  if (completed) return;
23177
23498
  completed = true;
23178
- resolve14({ code, signal });
23499
+ resolve15({ code, signal });
23179
23500
  };
23180
23501
  helperEvents.once("error", () => {
23181
23502
  failProtocol(new Error("Windows Job Object helper could not start"));
@@ -23188,7 +23509,7 @@ async function createWindowsJobContainment(pid, options) {
23188
23509
  });
23189
23510
  const nextLine = async (expected) => {
23190
23511
  if (protocolFailure) throw protocolFailure;
23191
- const line = lines.shift() ?? await new Promise((resolve14, reject3) => {
23512
+ const line = lines.shift() ?? await new Promise((resolve15, reject3) => {
23192
23513
  const timer = setTimeout(
23193
23514
  () => reject3(timeoutError("Windows Job Object helper did not answer in time")),
23194
23515
  timeoutMs
@@ -23196,7 +23517,7 @@ async function createWindowsJobContainment(pid, options) {
23196
23517
  timer.unref?.();
23197
23518
  lineWaiters.push((value) => {
23198
23519
  clearTimeout(timer);
23199
- resolve14(value);
23520
+ resolve15(value);
23200
23521
  });
23201
23522
  });
23202
23523
  if (protocolFailure) throw protocolFailure;
@@ -23209,8 +23530,8 @@ async function createWindowsJobContainment(pid, options) {
23209
23530
  }
23210
23531
  const stopped = await Promise.race([
23211
23532
  exited.then(() => true),
23212
- new Promise((resolve14) => {
23213
- const timer = setTimeout(() => resolve14(false), timeoutMs);
23533
+ new Promise((resolve15) => {
23534
+ const timer = setTimeout(() => resolve15(false), timeoutMs);
23214
23535
  timer.unref?.();
23215
23536
  })
23216
23537
  ]);
@@ -23269,7 +23590,7 @@ async function awaitWindowsContainmentGate(env = process.env, input = process.st
23269
23590
  if (nonce === void 0) return true;
23270
23591
  if (!SAFE_NONCE2.test(nonce)) return false;
23271
23592
  const expected = windowsContainmentGate(nonce).trimEnd();
23272
- return new Promise((resolve14) => {
23593
+ return new Promise((resolve15) => {
23273
23594
  let pending = Buffer.alloc(0);
23274
23595
  let settled = false;
23275
23596
  const finish = (result) => {
@@ -23280,7 +23601,7 @@ async function awaitWindowsContainmentGate(env = process.env, input = process.st
23280
23601
  input.off("end", onEnd);
23281
23602
  input.off("error", onEnd);
23282
23603
  if (result) input.pause();
23283
- resolve14(result);
23604
+ resolve15(result);
23284
23605
  };
23285
23606
  const onData = (chunk) => {
23286
23607
  pending = Buffer.concat([pending, chunk]);
@@ -23705,7 +24026,7 @@ async function installRelease(version2, options = {}) {
23705
24026
  installerContainmentSetupError = error52;
23706
24027
  return null;
23707
24028
  }) : Promise.resolve(null);
23708
- const installed = await new Promise((resolve14, reject3) => {
24029
+ const installed = await new Promise((resolve15, reject3) => {
23709
24030
  let finished = false;
23710
24031
  let cleanupStarted = false;
23711
24032
  let exitObserved = false;
@@ -23721,7 +24042,7 @@ async function installRelease(version2, options = {}) {
23721
24042
  finished = true;
23722
24043
  clearTimeout(timer);
23723
24044
  options.signal?.removeEventListener("abort", requestCleanup);
23724
- resolve14(result);
24045
+ resolve15(result);
23725
24046
  };
23726
24047
  const requestCleanup = () => {
23727
24048
  if (cleanupStarted || finished) return;
@@ -24034,11 +24355,11 @@ async function runWorkerCompatibilityProxy(env = process.env, argv = process.arg
24034
24355
  child.stdin?.on("error", () => {
24035
24356
  });
24036
24357
  process.stdin.pipe(child.stdin);
24037
- return new Promise((resolve14) => {
24038
- child.once("error", () => resolve14(1));
24358
+ return new Promise((resolve15) => {
24359
+ child.once("error", () => resolve15(1));
24039
24360
  child.once("exit", (code) => {
24040
24361
  process.stdin.unpipe(child.stdin);
24041
- resolve14(code ?? 1);
24362
+ resolve15(code ?? 1);
24042
24363
  });
24043
24364
  });
24044
24365
  }
@@ -24116,11 +24437,11 @@ async function launchHostSupervisor(options = {}) {
24116
24437
  const waitOrStop = async (ms) => {
24117
24438
  if (stopping) return false;
24118
24439
  if (!customDelay) {
24119
- await new Promise((resolve14) => {
24440
+ await new Promise((resolve15) => {
24120
24441
  const finish = () => {
24121
24442
  clearTimeout(timer);
24122
24443
  stopController.signal.removeEventListener("abort", finish);
24123
- resolve14();
24444
+ resolve15();
24124
24445
  };
24125
24446
  const timer = setTimeout(finish, ms);
24126
24447
  stopController.signal.addEventListener("abort", finish, { once: true });
@@ -24128,8 +24449,8 @@ async function launchHostSupervisor(options = {}) {
24128
24449
  return !stopping;
24129
24450
  }
24130
24451
  let finishStop;
24131
- const stopped = new Promise((resolve14) => {
24132
- finishStop = () => resolve14();
24452
+ const stopped = new Promise((resolve15) => {
24453
+ finishStop = () => resolve15();
24133
24454
  stopController.signal.addEventListener("abort", finishStop, { once: true });
24134
24455
  });
24135
24456
  await Promise.race([customDelay(ms), stopped]);
@@ -24252,19 +24573,19 @@ async function launchHostSupervisor(options = {}) {
24252
24573
  child = spawnSupervisor(entry, version2, ownershipDirectory, containmentGateNonce);
24253
24574
  const launchedSupervisor = child;
24254
24575
  let resolveChildExited;
24255
- const childExited = new Promise((resolve14) => {
24256
- resolveChildExited = resolve14;
24576
+ const childExited = new Promise((resolve15) => {
24577
+ resolveChildExited = resolve15;
24257
24578
  });
24258
24579
  const supervisorContainmentAbort = new AbortController();
24259
24580
  void childExited.then(() => supervisorContainmentAbort.abort());
24260
24581
  const outcomePromise = new Promise(
24261
- (resolve14) => {
24582
+ (resolve15) => {
24262
24583
  let observed = false;
24263
24584
  const finish = (code, signal) => {
24264
24585
  if (observed) return;
24265
24586
  observed = true;
24266
24587
  resolveChildExited();
24267
- resolve14({ code, signal });
24588
+ resolve15({ code, signal });
24268
24589
  };
24269
24590
  child.once("error", () => finish(1, null));
24270
24591
  child.once("exit", finish);
@@ -24285,12 +24606,12 @@ async function launchHostSupervisor(options = {}) {
24285
24606
  if (!supervisorContainment || !launchedSupervisor.stdin) {
24286
24607
  throw new Error("supervisor Job Object gate is unavailable");
24287
24608
  }
24288
- await new Promise((resolve14, reject3) => {
24609
+ await new Promise((resolve15, reject3) => {
24289
24610
  launchedSupervisor.stdin.write(
24290
24611
  windowsContainmentGate(containmentGateNonce),
24291
24612
  (error52) => {
24292
24613
  if (error52) reject3(error52);
24293
- else resolve14();
24614
+ else resolve15();
24294
24615
  }
24295
24616
  );
24296
24617
  });
@@ -24432,18 +24753,18 @@ async function superviseHost(options = {}) {
24432
24753
  }
24433
24754
  }
24434
24755
  let announceShutdown;
24435
- const shutdownAnnounced = new Promise((resolve14) => {
24436
- announceShutdown = resolve14;
24756
+ const shutdownAnnounced = new Promise((resolve15) => {
24757
+ announceShutdown = resolve15;
24437
24758
  });
24438
24759
  const attempted = /* @__PURE__ */ new Set();
24439
24760
  const waitOrShutdown = async (ms) => {
24440
24761
  if (shuttingDown2) return false;
24441
24762
  if (!customDelay) {
24442
- await new Promise((resolve14) => {
24763
+ await new Promise((resolve15) => {
24443
24764
  const finish = () => {
24444
24765
  clearTimeout(timer);
24445
24766
  shutdownController.signal.removeEventListener("abort", finish);
24446
- resolve14();
24767
+ resolve15();
24447
24768
  };
24448
24769
  const timer = setTimeout(finish, ms);
24449
24770
  shutdownController.signal.addEventListener("abort", finish, { once: true });
@@ -24586,19 +24907,19 @@ async function superviseHost(options = {}) {
24586
24907
  child = spawnWorker(command, watchdogLaunch, compatibilityOwnership, containmentGateNonce);
24587
24908
  const watchedChild = child;
24588
24909
  let resolveChildExited;
24589
- const childExited = new Promise((resolve14) => {
24590
- resolveChildExited = resolve14;
24910
+ const childExited = new Promise((resolve15) => {
24911
+ resolveChildExited = resolve15;
24591
24912
  });
24592
24913
  const workerContainmentAbort = new AbortController();
24593
24914
  void childExited.then(() => workerContainmentAbort.abort());
24594
24915
  const outcomePromise = new Promise(
24595
- (resolve14) => {
24916
+ (resolve15) => {
24596
24917
  let observed = false;
24597
24918
  const finish = (result) => {
24598
24919
  if (observed) return;
24599
24920
  observed = true;
24600
24921
  resolveChildExited();
24601
- resolve14(result);
24922
+ resolve15(result);
24602
24923
  };
24603
24924
  watchedChild.once("error", () => finish({ code: 1, signal: null }));
24604
24925
  watchedChild.once(
@@ -24620,10 +24941,10 @@ async function superviseHost(options = {}) {
24620
24941
  if (!workerContainment || !watchedChild.stdin) {
24621
24942
  throw new Error("worker Job Object gate is unavailable");
24622
24943
  }
24623
- await new Promise((resolve14, reject3) => {
24944
+ await new Promise((resolve15, reject3) => {
24624
24945
  watchedChild.stdin.write(windowsContainmentGate(containmentGateNonce), (error52) => {
24625
24946
  if (error52) reject3(error52);
24626
- else resolve14();
24947
+ else resolve15();
24627
24948
  });
24628
24949
  });
24629
24950
  }
@@ -24890,6 +25211,20 @@ async function superviseHost(options = {}) {
24890
25211
  import { hostname as hostname3 } from "node:os";
24891
25212
 
24892
25213
  // src/browser/adapter.ts
25214
+ 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.";
25215
+ function requireBrowserLoginOrigin(currentUrl, loginUrl) {
25216
+ try {
25217
+ const current = new URL(currentUrl);
25218
+ const expected = new URL(loginUrl);
25219
+ const currentIsWeb = current.protocol === "https:" || current.protocol === "http:";
25220
+ const expectedIsWeb = expected.protocol === "https:" || expected.protocol === "http:";
25221
+ if (currentIsWeb && expectedIsWeb && current.origin === expected.origin) {
25222
+ return expected.origin;
25223
+ }
25224
+ } catch {
25225
+ }
25226
+ throw new Error(BROWSER_LOGIN_ORIGIN_CHANGED);
25227
+ }
24893
25228
  var DEMO_FRAME_JPEG_BASE64 = "/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAIAAgDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3+iiigD//2Q==";
24894
25229
  function createDemoBrowserAdapterFactory() {
24895
25230
  return {
@@ -25039,6 +25374,7 @@ function createDemoBrowserAdapterFactory() {
25039
25374
  return frame();
25040
25375
  },
25041
25376
  async fillLogin(credential) {
25377
+ requireBrowserLoginOrigin(active().url, credential.loginUrl);
25042
25378
  const landedUrl = new URL("/account", credential.loginUrl).toString();
25043
25379
  const tab = active();
25044
25380
  tab.url = landedUrl;
@@ -25061,9 +25397,9 @@ function createDemoBrowserAdapterFactory() {
25061
25397
  }
25062
25398
 
25063
25399
  // src/browser/manager.ts
25064
- import { mkdir as mkdir4 } from "node:fs/promises";
25400
+ 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
25401
  import { homedir as homedir2 } from "node:os";
25066
- import { join as join7 } from "node:path";
25402
+ import { dirname as dirname4, join as join7, resolve as resolve4 } from "node:path";
25067
25403
  var SAFE_SEGMENT = /^[A-Za-z0-9_-]{1,200}$/;
25068
25404
  var FRAME_MIN_INTERVAL_MS = 100;
25069
25405
  var IDLE_TIMEOUT_MS = 15 * 6e4;
@@ -25071,18 +25407,34 @@ var BrowserManager = class {
25071
25407
  constructor(opts) {
25072
25408
  this.opts = opts;
25073
25409
  this.profileRoot = opts.profileRoot ?? join7(homedir2(), ".zixt", "browser-profiles");
25410
+ this.profileStateRoot = join7(this.profileRoot, ".profile-state");
25074
25411
  this.viewport = opts.viewport ?? BROWSER_DEFAULT_VIEWPORT;
25075
25412
  this.idleTimeoutMs = opts.idleTimeoutMs ?? IDLE_TIMEOUT_MS;
25076
25413
  this.frameMinIntervalMs = opts.frameMinIntervalMs ?? FRAME_MIN_INTERVAL_MS;
25414
+ this.profileInventoryLimit = Math.max(
25415
+ 1,
25416
+ Math.min(
25417
+ BROWSER_PROFILE_INVENTORY_PAGE_SIZE,
25418
+ Math.trunc(opts.profileInventoryLimit ?? BROWSER_PROFILE_INVENTORY_PAGE_SIZE)
25419
+ )
25420
+ );
25077
25421
  }
25078
25422
  sessions = /* @__PURE__ */ new Map();
25423
+ /**
25424
+ * Fail closed after lifecycle authorization could not be durably checked or
25425
+ * written. Non-browser runner work may continue, but no local Browser can
25426
+ * open until a later trusted revision is successfully authorized.
25427
+ */
25428
+ deniedAuthorizations = /* @__PURE__ */ new Map();
25079
25429
  /** Serializes open/close per teammate: one profile allows one live browser. */
25080
25430
  locks = /* @__PURE__ */ new Map();
25081
25431
  events = null;
25082
25432
  profileRoot;
25433
+ profileStateRoot;
25083
25434
  viewport;
25084
25435
  idleTimeoutMs;
25085
25436
  frameMinIntervalMs;
25437
+ profileInventoryLimit;
25086
25438
  /**
25087
25439
  * Wired once by the host client; replaces any prior socket generation's
25088
25440
  * sink. Attaching re-announces every live session, because the cloud keeps
@@ -25099,6 +25451,31 @@ var BrowserManager = class {
25099
25451
  capability() {
25100
25452
  return this.opts.factory.capability();
25101
25453
  }
25454
+ /**
25455
+ * Page a path-validated migration inventory without holding an unbounded
25456
+ * directory in memory. A final `complete: true` page is emitted even when
25457
+ * the directory contains an exact multiple of the page size.
25458
+ */
25459
+ async *profileInventoryPages() {
25460
+ await this.ensureOwnedDirectory(this.profileRoot);
25461
+ const agentIds = [];
25462
+ const directory = await opendir(this.profileRoot);
25463
+ for await (const entry of directory) {
25464
+ if (!entry.isDirectory() || entry.isSymbolicLink() || !AgentId.safeParse(entry.name).success) {
25465
+ continue;
25466
+ }
25467
+ if (agentIds.length === this.profileInventoryLimit) {
25468
+ yield { agentIds: agentIds.splice(0).sort(), complete: false };
25469
+ }
25470
+ agentIds.push(entry.name);
25471
+ }
25472
+ yield { agentIds: agentIds.sort(), complete: true };
25473
+ }
25474
+ /** First bounded page retained for embedders that have not adopted continuations. */
25475
+ async profileInventory() {
25476
+ const first = await this.profileInventoryPages().next();
25477
+ return first.value ?? { agentIds: [], complete: true };
25478
+ }
25102
25479
  sessionState(agentId) {
25103
25480
  const session = this.sessions.get(agentId);
25104
25481
  return session && !session.closed ? this.stateOf(session) : null;
@@ -25114,6 +25491,122 @@ var BrowserManager = class {
25114
25491
  );
25115
25492
  return next;
25116
25493
  }
25494
+ validateAgentId(agentId) {
25495
+ if (!SAFE_SEGMENT.test(agentId) || !AgentId.safeParse(agentId).success) {
25496
+ throw new Error("invalid agent id for a browser profile path");
25497
+ }
25498
+ }
25499
+ exactChild(root, child) {
25500
+ const canonicalRoot = resolve4(root);
25501
+ const target = resolve4(canonicalRoot, child);
25502
+ if (dirname4(target) !== canonicalRoot) {
25503
+ throw new Error("browser profile path escaped its owned root");
25504
+ }
25505
+ return target;
25506
+ }
25507
+ async ensureOwnedDirectory(path) {
25508
+ await mkdir4(path, { recursive: true, mode: 448 });
25509
+ const stat3 = await lstat4(path);
25510
+ if (!stat3.isDirectory() || stat3.isSymbolicLink()) {
25511
+ throw new Error("browser profile root must be an owned directory, not a symbolic link");
25512
+ }
25513
+ }
25514
+ /**
25515
+ * POSIX needs directory fsync for a rename/unlink to survive sudden power
25516
+ * loss. Windows does not support opening directories this way, so only its
25517
+ * unsupported-operation errors are tolerated; file fsync still remains
25518
+ * mandatory on every platform.
25519
+ */
25520
+ async syncDirectory(path) {
25521
+ try {
25522
+ const directory = await open4(path, "r");
25523
+ try {
25524
+ await directory.sync();
25525
+ } finally {
25526
+ await directory.close();
25527
+ }
25528
+ } catch (error52) {
25529
+ const code = error52.code;
25530
+ if (process.platform === "win32" && (code === "EISDIR" || code === "EACCES" || code === "EPERM" || code === "EINVAL" || code === "ENOTSUP")) {
25531
+ return;
25532
+ }
25533
+ throw error52;
25534
+ }
25535
+ }
25536
+ profilePath(agentId) {
25537
+ return this.exactChild(this.profileRoot, agentId);
25538
+ }
25539
+ statePath(agentId) {
25540
+ return this.exactChild(this.profileStateRoot, `${agentId}.json`);
25541
+ }
25542
+ async readProfileState(agentId) {
25543
+ try {
25544
+ const raw = JSON.parse(await readFile5(this.statePath(agentId), "utf8"));
25545
+ if (typeof raw !== "object" || raw === null || !Number.isInteger(raw.revision) || Number(raw.revision) < 0 || !["allowed", "purged"].includes(String(raw.state))) {
25546
+ throw new Error("browser profile lifecycle marker is invalid");
25547
+ }
25548
+ return raw;
25549
+ } catch (error52) {
25550
+ if (error52.code === "ENOENT") return null;
25551
+ throw error52;
25552
+ }
25553
+ }
25554
+ async writeProfileState(agentId, state) {
25555
+ await this.ensureOwnedDirectory(this.profileRoot);
25556
+ await this.ensureOwnedDirectory(this.profileStateRoot);
25557
+ const destination = this.statePath(agentId);
25558
+ const temporary = this.exactChild(
25559
+ this.profileStateRoot,
25560
+ `${agentId}.${crypto.randomUUID()}.tmp`
25561
+ );
25562
+ try {
25563
+ const marker = await open4(temporary, "wx+", 384);
25564
+ try {
25565
+ await marker.writeFile(JSON.stringify(state), "utf8");
25566
+ await marker.sync();
25567
+ } finally {
25568
+ await marker.close();
25569
+ }
25570
+ await rename3(temporary, destination);
25571
+ await this.syncDirectory(this.profileStateRoot);
25572
+ await this.syncDirectory(this.profileRoot);
25573
+ } catch (error52) {
25574
+ await rm4(temporary, { force: true }).catch(() => {
25575
+ });
25576
+ throw error52;
25577
+ }
25578
+ }
25579
+ /** Trusted cloud lifecycle authorization after an explicit restore/current assignment. */
25580
+ async authorizeProfile(agentId, revision) {
25581
+ this.validateAgentId(agentId);
25582
+ if (!Number.isInteger(revision) || revision < 0) {
25583
+ throw new Error("invalid browser profile lifecycle revision");
25584
+ }
25585
+ try {
25586
+ await this.withLock(agentId, async () => {
25587
+ const current = await this.readProfileState(agentId);
25588
+ if (current && current.revision > revision) {
25589
+ throw new Error("stale browser profile authorization was refused");
25590
+ }
25591
+ if (current?.state === "purged" && current.revision === revision) {
25592
+ throw new Error("this browser profile remains retired until the AI teammate is restored");
25593
+ }
25594
+ if (!current || current.revision < revision || current.state !== "allowed") {
25595
+ await this.writeProfileState(agentId, { revision, state: "allowed" });
25596
+ }
25597
+ });
25598
+ const deniedRevision = this.deniedAuthorizations.get(agentId);
25599
+ if (deniedRevision !== void 0 && revision >= deniedRevision) {
25600
+ this.deniedAuthorizations.delete(agentId);
25601
+ }
25602
+ } catch (error52) {
25603
+ this.deniedAuthorizations.set(
25604
+ agentId,
25605
+ Math.max(revision, this.deniedAuthorizations.get(agentId) ?? revision)
25606
+ );
25607
+ throw error52;
25608
+ }
25609
+ }
25117
25610
  /**
25118
25611
  * Ensure a live session for this teammate. An existing session is adopted
25119
25612
  * as-is (the cloud-minted id loses to the live one — the cloud reconciles
@@ -25121,16 +25614,39 @@ var BrowserManager = class {
25121
25614
  * with the install remedy.
25122
25615
  */
25123
25616
  ensure(agentId, browserSessionId) {
25124
- if (!SAFE_SEGMENT.test(agentId)) {
25125
- return Promise.reject(new Error("invalid agent id for a browser profile path"));
25617
+ try {
25618
+ this.validateAgentId(agentId);
25619
+ } catch (error52) {
25620
+ return Promise.reject(error52);
25126
25621
  }
25127
25622
  return this.withLock(agentId, async () => {
25623
+ if (this.deniedAuthorizations.has(agentId)) {
25624
+ throw new Error(
25625
+ "this browser profile is unavailable until its lifecycle authorization succeeds"
25626
+ );
25627
+ }
25628
+ const lifecycle = await this.readProfileState(agentId);
25629
+ if (lifecycle?.state === "purged") {
25630
+ throw new Error("this browser profile was retired and cannot be recreated locally");
25631
+ }
25128
25632
  const existing = this.sessions.get(agentId);
25633
+ if (existing?.closeFailed) {
25634
+ throw new Error("the previous browser termination is not yet confirmed");
25635
+ }
25129
25636
  if (existing && !existing.closed) {
25130
25637
  this.markActivity(agentId);
25131
25638
  return this.stateOf(existing);
25132
25639
  }
25133
- const profileDir = join7(this.profileRoot, agentId);
25640
+ await this.ensureOwnedDirectory(this.profileRoot);
25641
+ const profileDir = this.profilePath(agentId);
25642
+ try {
25643
+ const existingProfile = await lstat4(profileDir);
25644
+ if (existingProfile.isSymbolicLink() || !existingProfile.isDirectory()) {
25645
+ throw new Error("browser profile path is not an owned directory");
25646
+ }
25647
+ } catch (error52) {
25648
+ if (error52.code !== "ENOENT") throw error52;
25649
+ }
25134
25650
  await mkdir4(profileDir, { recursive: true, mode: 448 });
25135
25651
  const adapter = await this.opts.factory.open({
25136
25652
  agentId,
@@ -25151,7 +25667,8 @@ var BrowserManager = class {
25151
25667
  framesPaused: false,
25152
25668
  inputQueue: Promise.resolve(),
25153
25669
  resizeGeneration: 0,
25154
- closed: false
25670
+ closed: false,
25671
+ closeFailed: false
25155
25672
  };
25156
25673
  this.sessions.set(agentId, session);
25157
25674
  adapter.onStateChanged(() => {
@@ -25163,7 +25680,8 @@ var BrowserManager = class {
25163
25680
  });
25164
25681
  }
25165
25682
  /** browser.open handler: ensure, then announce with the request correlation. */
25166
- async open(agentId, browserSessionId, _viewport, openRequestId) {
25683
+ async open(agentId, browserSessionId, _viewport, openRequestId, profileRevision) {
25684
+ if (profileRevision !== void 0) await this.authorizeProfile(agentId, profileRevision);
25167
25685
  const state = await this.ensure(agentId, browserSessionId);
25168
25686
  this.events?.state(state, openRequestId);
25169
25687
  return state;
@@ -25173,18 +25691,45 @@ var BrowserManager = class {
25173
25691
  }
25174
25692
  async closeLocked(agentId, reason) {
25175
25693
  const session = this.sessions.get(agentId);
25176
- if (!session || session.closed) return;
25694
+ if (!session || session.closed && !session.closeFailed) return;
25177
25695
  session.closed = true;
25696
+ session.closeFailed = false;
25178
25697
  if (session.frameTimer) clearTimeout(session.frameTimer);
25179
25698
  if (session.idleTimer) clearTimeout(session.idleTimer);
25180
- this.sessions.delete(agentId);
25181
25699
  try {
25182
- await session.adapter.setFrameSink(null);
25700
+ await session.adapter.setFrameSink(null).catch(() => {
25701
+ });
25183
25702
  await session.adapter.close();
25184
- } catch {
25703
+ } catch (error52) {
25704
+ session.closeFailed = true;
25705
+ throw error52;
25185
25706
  }
25707
+ this.sessions.delete(agentId);
25186
25708
  this.events?.ended(session.browserSessionId, agentId, reason);
25187
25709
  }
25710
+ /** Persist retirement, close a live Chromium, then delete only this exact profile. */
25711
+ purge(agentId, purgeId, profileRevision) {
25712
+ try {
25713
+ this.validateAgentId(agentId);
25714
+ } catch (error52) {
25715
+ return Promise.reject(error52);
25716
+ }
25717
+ if (!Number.isInteger(profileRevision) || profileRevision < 1) {
25718
+ return Promise.reject(new Error("invalid browser profile purge revision"));
25719
+ }
25720
+ return this.withLock(agentId, async () => {
25721
+ const current = await this.readProfileState(agentId);
25722
+ if (current && current.revision > profileRevision) return;
25723
+ await this.writeProfileState(agentId, {
25724
+ revision: profileRevision,
25725
+ state: "purged",
25726
+ purgeId
25727
+ });
25728
+ await this.closeLocked(agentId, "stopped");
25729
+ await rm4(this.profilePath(agentId), { recursive: true, force: true, maxRetries: 3 });
25730
+ await this.syncDirectory(this.profileRoot);
25731
+ });
25732
+ }
25188
25733
  async closeAll(reason) {
25189
25734
  await Promise.allSettled(
25190
25735
  [...this.sessions.keys()].map((agentId) => this.close(agentId, reason))
@@ -25224,8 +25769,8 @@ var BrowserManager = class {
25224
25769
  return this.enqueue(session, async () => {
25225
25770
  if (resizeGeneration !== null && resizeGeneration !== session.resizeGeneration) return;
25226
25771
  await session.adapter.command(command);
25227
- const changesCaptureTarget = command.action === "activateTab" || command.action === "newTab" || command.action === "closeTab";
25228
- if (changesCaptureTarget && session.viewerCount > 0) {
25772
+ const needsImmediateFrame = command.action === "resize" || command.action === "activateTab" || command.action === "newTab" || command.action === "closeTab";
25773
+ if (needsImmediateFrame && session.viewerCount > 0) {
25229
25774
  try {
25230
25775
  this.enqueueFrame(session, await session.adapter.screenshot());
25231
25776
  } catch {
@@ -25331,7 +25876,8 @@ var BrowserManager = class {
25331
25876
  this.armIdleTimer(session);
25332
25877
  return;
25333
25878
  }
25334
- void this.close(session.agentId, "idle_timeout");
25879
+ void this.close(session.agentId, "idle_timeout").catch(() => {
25880
+ });
25335
25881
  }, this.idleTimeoutMs);
25336
25882
  session.idleTimer.unref?.();
25337
25883
  }
@@ -25348,12 +25894,13 @@ async function loadPlaywright() {
25348
25894
  return import("playwright-core");
25349
25895
  }
25350
25896
  var KEY_ALIASES = { " ": "Space" };
25351
- function createPlaywrightBrowserAdapterFactory() {
25897
+ function createPlaywrightBrowserAdapterFactory(dependencies = {}) {
25898
+ const load = dependencies.loadPlaywright ?? loadPlaywright;
25352
25899
  return {
25353
25900
  kind: "playwright",
25354
25901
  async capability() {
25355
25902
  try {
25356
- const playwright = await loadPlaywright();
25903
+ const playwright = await load();
25357
25904
  const executable = playwright.chromium.executablePath();
25358
25905
  if (!executable) return { status: "unavailable", error: BROWSER_INSTALL_REMEDY };
25359
25906
  await access3(executable);
@@ -25363,7 +25910,7 @@ function createPlaywrightBrowserAdapterFactory() {
25363
25910
  }
25364
25911
  },
25365
25912
  async open({ profileDir, viewport }) {
25366
- const playwright = await loadPlaywright();
25913
+ const playwright = await load();
25367
25914
  const context = await playwright.chromium.launchPersistentContext(profileDir, {
25368
25915
  headless: true,
25369
25916
  viewport,
@@ -25732,8 +26279,10 @@ function createPlaywrightBrowserAdapterFactory() {
25732
26279
  async fillLogin(credential) {
25733
26280
  const tab = requireActive();
25734
26281
  const { page: page2 } = tab;
26282
+ const expectedOrigin = requireBrowserLoginOrigin(safeUrl(page2), credential.loginUrl);
25735
26283
  const password = page2.locator('input[type="password"]:visible').first();
25736
26284
  await password.waitFor({ state: "visible", timeout: ACTION_TIMEOUT_MS });
26285
+ requireBrowserLoginOrigin(safeUrl(page2), credential.loginUrl);
25737
26286
  const username = page2.locator(
25738
26287
  [
25739
26288
  'input[autocomplete="username"]:visible',
@@ -25743,14 +26292,66 @@ function createPlaywrightBrowserAdapterFactory() {
25743
26292
  'input[type="text"]:visible'
25744
26293
  ].join(", ")
25745
26294
  ).first();
25746
- if (await username.count() > 0) {
25747
- try {
25748
- await username.fill(credential.username, { timeout: ACTION_TIMEOUT_MS });
25749
- } catch {
26295
+ const [passwordInputHandle] = await password.elementHandles();
26296
+ if (!passwordInputHandle) throw new Error("the password field disappeared");
26297
+ const [usernameInputHandle] = await username.elementHandles();
26298
+ const originSentinel = "ZIXT_BROWSER_LOGIN_ORIGIN_CHANGED";
26299
+ try {
26300
+ await passwordInputHandle.evaluate(
26301
+ (node, values) => {
26302
+ const browser = globalThis;
26303
+ const passwordInput = node;
26304
+ if (passwordInput.tagName !== "INPUT" || browser.location.origin !== values.origin) {
26305
+ throw new Error(values.originSentinel);
26306
+ }
26307
+ const visible = (input) => {
26308
+ const style = browser.getComputedStyle(input);
26309
+ const bounds = input.getBoundingClientRect();
26310
+ return input.isConnected && !input.disabled && style.display !== "none" && style.visibility !== "hidden" && bounds.width > 0 && bounds.height > 0;
26311
+ };
26312
+ const write = (input, value) => {
26313
+ input.focus();
26314
+ const setter = Object.getOwnPropertyDescriptor(
26315
+ browser.HTMLInputElement.prototype,
26316
+ "value"
26317
+ )?.set;
26318
+ if (setter) setter.call(input, value);
26319
+ else input.value = value;
26320
+ input.dispatchEvent(
26321
+ new browser.Event("input", { bubbles: true, composed: true })
26322
+ );
26323
+ input.dispatchEvent(new browser.Event("change", { bubbles: true }));
26324
+ };
26325
+ const usernameInput = values.usernameInput;
26326
+ if (usernameInput && usernameInput.ownerDocument === passwordInput.ownerDocument && visible(usernameInput)) {
26327
+ write(usernameInput, values.username);
26328
+ }
26329
+ if (!passwordInput.isConnected || browser.location.origin !== values.origin) {
26330
+ throw new Error(values.originSentinel);
26331
+ }
26332
+ write(passwordInput, values.password);
26333
+ },
26334
+ {
26335
+ usernameInput: usernameInputHandle ?? null,
26336
+ origin: expectedOrigin,
26337
+ originSentinel,
26338
+ username: credential.username,
26339
+ password: credential.password
26340
+ }
26341
+ );
26342
+ requireBrowserLoginOrigin(safeUrl(page2), credential.loginUrl);
26343
+ await passwordInputHandle.press("Enter");
26344
+ } catch (error52) {
26345
+ if (String(error52).includes(originSentinel)) {
26346
+ throw new Error(BROWSER_LOGIN_ORIGIN_CHANGED);
25750
26347
  }
26348
+ throw error52;
26349
+ } finally {
26350
+ await Promise.allSettled([
26351
+ passwordInputHandle.dispose(),
26352
+ ...usernameInputHandle ? [usernameInputHandle.dispose()] : []
26353
+ ]);
25751
26354
  }
25752
- await password.fill(credential.password, { timeout: ACTION_TIMEOUT_MS });
25753
- await password.press("Enter");
25754
26355
  try {
25755
26356
  await page2.waitForLoadState("load", { timeout: 15e3 });
25756
26357
  } catch {
@@ -25796,10 +26397,7 @@ function createPlaywrightBrowserAdapterFactory() {
25796
26397
  async close() {
25797
26398
  closed = true;
25798
26399
  for (const tab of tabs) await detachSession(tab);
25799
- try {
25800
- await context.close();
25801
- } catch {
25802
- }
26400
+ await context.close();
25803
26401
  }
25804
26402
  };
25805
26403
  }
@@ -25809,9 +26407,9 @@ function createPlaywrightBrowserAdapterFactory() {
25809
26407
  // src/runners/cli-runner.ts
25810
26408
  import { spawn as spawn8 } from "node:child_process";
25811
26409
  import { randomUUID as randomUUID10 } from "node:crypto";
25812
- import { lstat as lstat9, mkdir as mkdir10, realpath as realpath7 } from "node:fs/promises";
26410
+ import { lstat as lstat10, mkdir as mkdir10, realpath as realpath7 } from "node:fs/promises";
25813
26411
  import { homedir as homedir4 } from "node:os";
25814
- import { dirname as dirname6, isAbsolute as isAbsolute13, join as join14, resolve as resolve7 } from "node:path";
26412
+ import { dirname as dirname7, isAbsolute as isAbsolute13, join as join14, resolve as resolve8 } from "node:path";
25815
26413
 
25816
26414
  // src/tool-packs/browser/tool-definitions.ts
25817
26415
  function definition(name, description, properties, required2 = []) {
@@ -26033,9 +26631,13 @@ function createBrowserToolPack(deps) {
26033
26631
  });
26034
26632
  const credential = await deps.requestWebLogin(credentialName, origin);
26035
26633
  try {
26634
+ requireBrowserLoginOrigin(tool.state().url, credential.loginUrl);
26036
26635
  const { landedUrl } = await tool.fillLogin(credential);
26037
26636
  return { ok: true, result: { loggedIn: true, landedUrl } };
26038
- } catch {
26637
+ } catch (error52) {
26638
+ if (error52 instanceof Error && error52.message === BROWSER_LOGIN_ORIGIN_CHANGED) {
26639
+ return { ok: false, error: BROWSER_LOGIN_ORIGIN_CHANGED };
26640
+ }
26039
26641
  return {
26040
26642
  ok: false,
26041
26643
  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 +29102,13 @@ function createGithubPushOrchestrator(input) {
28500
29102
  // src/tool-packs/github/git-bridge.ts
28501
29103
  import { spawn as spawn5 } from "node:child_process";
28502
29104
  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";
29105
+ import { chmod as chmod3, lstat as lstat6, mkdir as mkdir5, realpath as realpath3, rm as rm5 } from "node:fs/promises";
29106
+ import { dirname as dirname5, isAbsolute as isAbsolute8, join as join9, relative as relative4 } from "node:path";
28505
29107
 
28506
29108
  // src/tool-packs/github/git-credential-broker.ts
28507
29109
  import { createServer } from "node:http";
28508
29110
  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";
29111
+ import { chmod as chmod2, lstat as lstat5, realpath as realpath2, writeFile } from "node:fs/promises";
28510
29112
  import { isAbsolute as isAbsolute7, join as join8, relative as relative3 } from "node:path";
28511
29113
  var MAX_REQUEST_BYTES = 16 * 1024;
28512
29114
  var FILE_MODE = 384;
@@ -28631,7 +29233,7 @@ async function createGithubGitCredentialBroker(input) {
28631
29233
  throw new Error("GitHub credential authority has expired");
28632
29234
  }
28633
29235
  assertRepositoryFullName(input.repositoryFullName);
28634
- const rootEntry = await lstat4(input.runArtifactsRoot);
29236
+ const rootEntry = await lstat5(input.runArtifactsRoot);
28635
29237
  if (!rootEntry.isDirectory() || rootEntry.isSymbolicLink()) {
28636
29238
  throw new Error("Git credential broker requires a private real run directory");
28637
29239
  }
@@ -28742,7 +29344,7 @@ function assertBelow(parent, child, label) {
28742
29344
  void label;
28743
29345
  }
28744
29346
  async function requireRealDirectory(path, label) {
28745
- const entry = await lstat5(path).catch(() => null);
29347
+ const entry = await lstat6(path).catch(() => null);
28746
29348
  if (!entry || !entry.isDirectory() || entry.isSymbolicLink()) {
28747
29349
  void label;
28748
29350
  throw new GithubGitProcessError("invalid_input");
@@ -28752,9 +29354,9 @@ async function requireRealDirectory(path, label) {
28752
29354
  async function validateTokenlessPaths(command) {
28753
29355
  if (command.kind === "clone-from-bridge") {
28754
29356
  if (!isAbsolute8(command.destination)) throw new GithubGitProcessError("invalid_input");
28755
- const parent = await requireRealDirectory(dirname4(command.destination), "clone parent");
29357
+ const parent = await requireRealDirectory(dirname5(command.destination), "clone parent");
28756
29358
  assertBelow(parent, command.destination, "clone destination");
28757
- const destination = await lstat5(command.destination).catch((error52) => {
29359
+ const destination = await lstat6(command.destination).catch((error52) => {
28758
29360
  if (error52.code === "ENOENT") return null;
28759
29361
  throw error52;
28760
29362
  });
@@ -28862,8 +29464,8 @@ async function runGit(input, args, env) {
28862
29464
  let settled = false;
28863
29465
  let stopping = false;
28864
29466
  let resolveExited;
28865
- const exited = new Promise((resolve14) => {
28866
- resolveExited = resolve14;
29467
+ const exited = new Promise((resolve15) => {
29468
+ resolveExited = resolve15;
28867
29469
  });
28868
29470
  child.once("exit", resolveExited);
28869
29471
  const cleanup = () => {
@@ -29059,7 +29661,7 @@ function createGithubGitBridge(input) {
29059
29661
  const real = await requireRealDirectory(path, "git bridge");
29060
29662
  assertBelow(current.bridges, real, "git bridge");
29061
29663
  const hooks = join9(real, "hooks");
29062
- await rm4(hooks, { recursive: true, force: true });
29664
+ await rm5(hooks, { recursive: true, force: true });
29063
29665
  await mkdir5(hooks, { mode: DIRECTORY_MODE });
29064
29666
  await chmod3(hooks, DIRECTORY_MODE);
29065
29667
  const config2 = join9(real, "config");
@@ -29067,7 +29669,7 @@ function createGithubGitBridge(input) {
29067
29669
  active.add(real);
29068
29670
  return real;
29069
29671
  } catch (error52) {
29070
- await rm4(path, { recursive: true, force: true }).catch(() => {
29672
+ await rm5(path, { recursive: true, force: true }).catch(() => {
29071
29673
  });
29072
29674
  throw error52;
29073
29675
  }
@@ -29161,7 +29763,7 @@ function createGithubGitBridge(input) {
29161
29763
  },
29162
29764
  async destroyPrivateBridge(path) {
29163
29765
  const bridge = await requireBridge(path);
29164
- await rm4(bridge, { recursive: true, force: true });
29766
+ await rm5(bridge, { recursive: true, force: true });
29165
29767
  active.delete(bridge);
29166
29768
  credentialed2.delete(bridge);
29167
29769
  },
@@ -29169,7 +29771,7 @@ function createGithubGitBridge(input) {
29169
29771
  if (closed) return;
29170
29772
  closed = true;
29171
29773
  const paths = [...active];
29172
- await Promise.all(paths.map((path) => rm4(path, { recursive: true, force: true })));
29774
+ await Promise.all(paths.map((path) => rm5(path, { recursive: true, force: true })));
29173
29775
  active.clear();
29174
29776
  credentialed2.clear();
29175
29777
  }
@@ -29510,8 +30112,8 @@ function createRepositoryTools(runtime) {
29510
30112
 
29511
30113
  // src/tool-packs/github/workspace.ts
29512
30114
  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";
30115
+ 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";
30116
+ import { isAbsolute as isAbsolute9, join as join10, relative as relative5, resolve as resolve5 } from "node:path";
29515
30117
  var SAFE_LOCAL_ID = /^[A-Za-z0-9_-]{1,200}$/;
29516
30118
  var FULL_NAME2 = /^[A-Za-z0-9_.-]{1,100}\/[A-Za-z0-9_.-]{1,100}$/;
29517
30119
  var DIRECTORY_MODE2 = 448;
@@ -29534,10 +30136,10 @@ function assertBelow2(parent, child, label) {
29534
30136
  }
29535
30137
  }
29536
30138
  function samePath(left, right) {
29537
- return process.platform === "win32" ? resolve4(left).toLowerCase() === resolve4(right).toLowerCase() : resolve4(left) === resolve4(right);
30139
+ return process.platform === "win32" ? resolve5(left).toLowerCase() === resolve5(right).toLowerCase() : resolve5(left) === resolve5(right);
29538
30140
  }
29539
30141
  async function requireRealDirectory2(path, label) {
29540
- const entry = await lstat6(path).catch(() => null);
30142
+ const entry = await lstat7(path).catch(() => null);
29541
30143
  if (!entry || !entry.isDirectory() || entry.isSymbolicLink()) {
29542
30144
  throw new Error(`${label} must be a real directory, not a symbolic link or junction`);
29543
30145
  }
@@ -29574,7 +30176,7 @@ function parseMetadata(text) {
29574
30176
  }
29575
30177
  async function pathExists(path) {
29576
30178
  try {
29577
- await lstat6(path);
30179
+ await lstat7(path);
29578
30180
  return true;
29579
30181
  } catch (error52) {
29580
30182
  if (error52.code === "ENOENT") return false;
@@ -29636,11 +30238,11 @@ async function createGithubWorkspaceService(input) {
29636
30238
  if (!await pathExists(destination) || !await pathExists(metadataPath)) {
29637
30239
  throw new Error("GitHub repository workspace has not been prepared");
29638
30240
  }
29639
- const metadataEntry = await lstat6(metadataPath);
30241
+ const metadataEntry = await lstat7(metadataPath);
29640
30242
  if (!metadataEntry.isFile() || metadataEntry.isSymbolicLink()) {
29641
30243
  throw new Error("GitHub workspace metadata is invalid");
29642
30244
  }
29643
- const metadata = parseMetadata(await readFile5(metadataPath, "utf8"));
30245
+ const metadata = parseMetadata(await readFile6(metadataPath, "utf8"));
29644
30246
  if (metadata.repositoryId !== parsed.data || metadata.fullName !== repository.fullName || !samePath(metadata.path, destination)) {
29645
30247
  throw new Error("GitHub workspace metadata does not match this repository");
29646
30248
  }
@@ -29690,15 +30292,15 @@ async function createGithubWorkspaceService(input) {
29690
30292
  });
29691
30293
  await chmod4(metadataTemporary, FILE_MODE2);
29692
30294
  try {
29693
- await rename3(temporaryReal, destination);
30295
+ await rename4(temporaryReal, destination);
29694
30296
  try {
29695
- await rename3(metadataTemporary, metadataPath);
30297
+ await rename4(metadataTemporary, metadataPath);
29696
30298
  } catch (error52) {
29697
- await rm5(destination, { recursive: true, force: true });
30299
+ await rm6(destination, { recursive: true, force: true });
29698
30300
  throw error52;
29699
30301
  }
29700
30302
  } finally {
29701
- await rm5(metadataTemporary, { force: true }).catch(() => {
30303
+ await rm6(metadataTemporary, { force: true }).catch(() => {
29702
30304
  });
29703
30305
  }
29704
30306
  const path = await requireRealDirectory2(destination, "GitHub repository");
@@ -29710,7 +30312,7 @@ async function createGithubWorkspaceService(input) {
29710
30312
  headSha
29711
30313
  };
29712
30314
  } finally {
29713
- await rm5(temporary, { recursive: true, force: true }).catch(() => {
30315
+ await rm6(temporary, { recursive: true, force: true }).catch(() => {
29714
30316
  });
29715
30317
  }
29716
30318
  };
@@ -29738,11 +30340,11 @@ async function createGithubWorkspaceService(input) {
29738
30340
  expiresAt: authority.expiresAt
29739
30341
  });
29740
30342
  }
29741
- const metadataEntry = await lstat6(metadataPath);
30343
+ const metadataEntry = await lstat7(metadataPath);
29742
30344
  if (!metadataEntry.isFile() || metadataEntry.isSymbolicLink()) {
29743
30345
  throw new Error("GitHub workspace metadata is invalid");
29744
30346
  }
29745
- const metadata = parseMetadata(await readFile5(metadataPath, "utf8"));
30347
+ const metadata = parseMetadata(await readFile6(metadataPath, "utf8"));
29746
30348
  if (metadata.repositoryId !== repository.repositoryId || metadata.fullName !== repository.fullName || !samePath(metadata.path, destination)) {
29747
30349
  throw new Error("GitHub workspace metadata does not match this repository");
29748
30350
  }
@@ -31351,8 +31953,8 @@ var linearToolPackFactory = {
31351
31953
  async create(grant, context) {
31352
31954
  let resolveCancelled;
31353
31955
  let closed = false;
31354
- const cancelled = new Promise((resolve14) => {
31355
- resolveCancelled = resolve14;
31956
+ const cancelled = new Promise((resolve15) => {
31957
+ resolveCancelled = resolve15;
31356
31958
  });
31357
31959
  const cancel = () => {
31358
31960
  if (closed) return;
@@ -31721,6 +32323,21 @@ var TOOLS = [
31721
32323
  required: ["agent_id", "message"]
31722
32324
  }
31723
32325
  },
32326
+ {
32327
+ name: "message_manager",
32328
+ 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.",
32329
+ inputSchema: {
32330
+ type: "object",
32331
+ properties: {
32332
+ message: {
32333
+ type: "string",
32334
+ description: "A self-contained report, request, or question for the Manager."
32335
+ }
32336
+ },
32337
+ required: ["message"],
32338
+ additionalProperties: false
32339
+ }
32340
+ },
31724
32341
  {
31725
32342
  name: "get_org_config",
31726
32343
  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 +32580,8 @@ function opFor(name, args) {
31963
32580
  message: str("message"),
31964
32581
  ...typeof args["task_id"] === "string" && args["task_id"] ? { taskId: args["task_id"] } : {}
31965
32582
  };
32583
+ case "message_manager":
32584
+ return { kind: "manager.message", message: str("message") };
31966
32585
  case "get_org_config":
31967
32586
  return { kind: "org.config" };
31968
32587
  case "read_credential": {
@@ -32021,7 +32640,7 @@ function createAskUserServer() {
32021
32640
  let server;
32022
32641
  let listening;
32023
32642
  function ensureListening() {
32024
- listening ??= new Promise((resolve14, reject3) => {
32643
+ listening ??= new Promise((resolve15, reject3) => {
32025
32644
  server = createServer2((req, res) => {
32026
32645
  res.on("error", () => {
32027
32646
  });
@@ -32037,7 +32656,7 @@ function createAskUserServer() {
32037
32656
  server.on("error", reject3);
32038
32657
  server.listen(0, "127.0.0.1", () => {
32039
32658
  const address = server.address();
32040
- if (address && typeof address === "object") resolve14(address.port);
32659
+ if (address && typeof address === "object") resolve15(address.port);
32041
32660
  else reject3(new Error("ask_user server failed to bind"));
32042
32661
  });
32043
32662
  server.unref();
@@ -32369,7 +32988,7 @@ function buildRunnerEnv(input) {
32369
32988
  // src/runners/github-shell-auth.ts
32370
32989
  import { execFile } from "node:child_process";
32371
32990
  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";
32991
+ import { chmod as chmod5, lstat as lstat8, mkdir as mkdir8, realpath as realpath5, writeFile as writeFile4 } from "node:fs/promises";
32373
32992
  import { createServer as createServer3 } from "node:http";
32374
32993
  import { isAbsolute as isAbsolute11, join as join12, relative as relative6 } from "node:path";
32375
32994
  var MAX_REQUEST_BYTES2 = 16 * 1024;
@@ -32754,7 +33373,7 @@ async function writePrivate(path, content, executable = false) {
32754
33373
  await chmod5(path, executable ? EXECUTABLE_FILE_MODE : PRIVATE_FILE_MODE);
32755
33374
  }
32756
33375
  async function prepareHelpers(input) {
32757
- const rootEntry = await lstat7(input.runRoot);
33376
+ const rootEntry = await lstat8(input.runRoot);
32758
33377
  if (!rootEntry.isDirectory() || rootEntry.isSymbolicLink()) {
32759
33378
  throw new Error("GitHub shell authentication requires a private real run directory");
32760
33379
  }
@@ -33001,7 +33620,7 @@ password=${credential.accessToken}
33001
33620
 
33002
33621
  // src/runners/working-context.ts
33003
33622
  import { spawn as spawn6 } from "node:child_process";
33004
- import { resolve as resolve5 } from "node:path";
33623
+ import { resolve as resolve6 } from "node:path";
33005
33624
  var COMMAND_TIMEOUT_MS = 5e3;
33006
33625
  var OUTPUT_LIMIT_BYTES = 128 * 1024;
33007
33626
  var COMMAND_STOP_TIMEOUT_MS = 2e4;
@@ -33359,8 +33978,8 @@ async function repositoryState(directory, git, env, signal) {
33359
33978
  const pathLines = paths.trim().split(/\r?\n/);
33360
33979
  if (pathLines.length < 3 || !pathLines[0] || !pathLines[1] || !pathLines[2]) return null;
33361
33980
  const root = pathLines[0];
33362
- const gitDirectory = resolve5(directory, pathLines[1]);
33363
- const commonDirectory = resolve5(directory, pathLines[2]);
33981
+ const gitDirectory = resolve6(directory, pathLines[1]);
33982
+ const commonDirectory = resolve6(directory, pathLines[2]);
33364
33983
  const records = status.split(/\0|\r?\n/).filter(Boolean);
33365
33984
  const rawBranch = statusField(records, "branch.head");
33366
33985
  if (!rawBranch || rawBranch.length > 512 || !isSafeSingleLineDisplayText(rawBranch)) return null;
@@ -33507,18 +34126,18 @@ var WorkingContextPullRequestCache = class {
33507
34126
  import { spawn as spawn7 } from "node:child_process";
33508
34127
  import {
33509
34128
  chmod as chmod6,
33510
- lstat as lstat8,
34129
+ lstat as lstat9,
33511
34130
  mkdir as mkdir9,
33512
- open as open4,
34131
+ open as open5,
33513
34132
  readdir as readdir3,
33514
- readFile as readFile6,
34133
+ readFile as readFile7,
33515
34134
  realpath as realpath6,
33516
- rename as rename4,
33517
- rm as rm6,
34135
+ rename as rename5,
34136
+ rm as rm7,
33518
34137
  writeFile as writeFile5
33519
34138
  } from "node:fs/promises";
33520
34139
  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";
34140
+ 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
34141
  var SAFE_SEGMENT2 = /^[A-Za-z0-9_-]{1,200}$/;
33523
34142
  var DIRECTORY_MODE4 = 448;
33524
34143
  var FILE_MODE3 = 384;
@@ -33751,7 +34370,7 @@ function assertBelow3(parent, child) {
33751
34370
  if (escapes) throw new Error("run artifact path escapes its private root");
33752
34371
  }
33753
34372
  async function requireRealDirectory3(path, label) {
33754
- const entry = await lstat8(path);
34373
+ const entry = await lstat9(path);
33755
34374
  if (entry.isSymbolicLink()) throw new Error(`${label} must not be a symbolic link`);
33756
34375
  if (!entry.isDirectory()) throw new Error(`${label} must be a directory`);
33757
34376
  return realpath6(path);
@@ -33768,7 +34387,7 @@ async function rejectWindowsSymlinkAncestors(profile, target) {
33768
34387
  for (const segment of path.split("\\").filter(Boolean)) {
33769
34388
  current = win322.join(current, segment);
33770
34389
  try {
33771
- const entry = await lstat8(current);
34390
+ const entry = await lstat9(current);
33772
34391
  if (entry.isSymbolicLink()) {
33773
34392
  throw new Error("run artifact path must not contain symbolic links or junctions");
33774
34393
  }
@@ -33779,16 +34398,16 @@ async function rejectWindowsSymlinkAncestors(profile, target) {
33779
34398
  }
33780
34399
  }
33781
34400
  async function prepareRoot(root) {
33782
- const absolute = resolve6(root);
34401
+ const absolute = resolve7(root);
33783
34402
  let realProfile;
33784
34403
  if (process.platform === "win32") {
33785
- const profile = resolve6(homedir3());
34404
+ const profile = resolve7(homedir3());
33786
34405
  assertWindowsProfileBoundary(profile, absolute);
33787
34406
  await rejectWindowsSymlinkAncestors(profile, absolute);
33788
34407
  realProfile = await realpath6(profile);
33789
34408
  }
33790
34409
  try {
33791
- await lstat8(absolute);
34410
+ await lstat9(absolute);
33792
34411
  } catch (error52) {
33793
34412
  if (!isMissing(error52)) throw error52;
33794
34413
  await mkdir9(absolute, { recursive: true, mode: DIRECTORY_MODE4 });
@@ -33802,7 +34421,7 @@ async function prepareAgentRoot(root, agentId) {
33802
34421
  const path = join13(root, agentId);
33803
34422
  assertBelow3(root, path);
33804
34423
  try {
33805
- await lstat8(path);
34424
+ await lstat9(path);
33806
34425
  } catch (error52) {
33807
34426
  if (!isMissing(error52)) throw error52;
33808
34427
  try {
@@ -33893,7 +34512,7 @@ async function createRunArtifacts(input) {
33893
34512
  requireSafeSegment(input.agentId, "agentId");
33894
34513
  requireSafeSegment(input.runToken, "runToken");
33895
34514
  const root = await prepareRoot(input.root);
33896
- const removeTree = input.removeTree ?? ((path) => rm6(path, { recursive: true, force: true }));
34515
+ const removeTree = input.removeTree ?? ((path) => rm7(path, { recursive: true, force: true }));
33897
34516
  const cleanupRetryDelayMs = input.cleanupRetryDelayMs ?? CLEANUP_RETRY_DELAY_MS;
33898
34517
  const agentRoot = await prepareAgentRoot(root, input.agentId);
33899
34518
  await lockDownWindowsDirectories([root, agentRoot]);
@@ -33952,10 +34571,10 @@ async function removePrivateTreeWithRetries(path, removeTree, retryDelayMs) {
33952
34571
  }
33953
34572
  }
33954
34573
  async function sweepOrphanedRunArtifacts(root) {
33955
- const absolute = resolve6(root);
34574
+ const absolute = resolve7(root);
33956
34575
  let realProfile;
33957
34576
  if (process.platform === "win32") {
33958
- const profile = resolve6(homedir3());
34577
+ const profile = resolve7(homedir3());
33959
34578
  assertWindowsProfileBoundary(profile, absolute);
33960
34579
  await rejectWindowsSymlinkAncestors(profile, absolute);
33961
34580
  realProfile = await realpath6(profile);
@@ -33980,7 +34599,7 @@ async function sweepOrphanedRunArtifacts(root) {
33980
34599
  if (!SAFE_SEGMENT2.test(run3.name) || !run3.isDirectory() || run3.isSymbolicLink()) continue;
33981
34600
  const runPath = join13(agentPath, run3.name);
33982
34601
  assertBelow3(agentPath, runPath);
33983
- await rm6(runPath, { recursive: true, force: true });
34602
+ await rm7(runPath, { recursive: true, force: true });
33984
34603
  removed++;
33985
34604
  }
33986
34605
  }
@@ -33990,7 +34609,7 @@ function defaultRunRegistryRoot() {
33990
34609
  return join13(homedir3(), ".zixt", "run-registry");
33991
34610
  }
33992
34611
  async function syncRunRegistryDirectory(path) {
33993
- const handle = await open4(path, "r");
34612
+ const handle = await open5(path, "r");
33994
34613
  try {
33995
34614
  await handle.sync();
33996
34615
  } finally {
@@ -34000,9 +34619,9 @@ async function syncRunRegistryDirectory(path) {
34000
34619
  async function ensureDurableRunRegistryRoot(registryRoot, syncDirectory7) {
34001
34620
  const firstCreated = await mkdir9(registryRoot, { recursive: true, mode: DIRECTORY_MODE4 });
34002
34621
  if (firstCreated && process.platform !== "win32") {
34003
- const first = resolve6(firstCreated);
34004
- const target = resolve6(registryRoot);
34005
- await syncDirectory7(dirname5(first));
34622
+ const first = resolve7(firstCreated);
34623
+ const target = resolve7(registryRoot);
34624
+ await syncDirectory7(dirname6(first));
34006
34625
  let current = first;
34007
34626
  for (const part of relative7(first, target).split(sep3).filter(Boolean)) {
34008
34627
  await syncDirectory7(current);
@@ -34019,12 +34638,12 @@ async function recordRunAssignment(runToken, record2, registryRoot = defaultRunR
34019
34638
  try {
34020
34639
  const syncDirectory7 = options.syncDirectory ?? syncRunRegistryDirectory;
34021
34640
  await ensureDurableRunRegistryRoot(registryRoot, syncDirectory7);
34022
- handle = await open4(temporary, "wx", FILE_MODE3);
34641
+ handle = await open5(temporary, "wx", FILE_MODE3);
34023
34642
  await handle.writeFile(JSON.stringify(record2), "utf8");
34024
34643
  await handle.sync();
34025
34644
  await handle.close();
34026
34645
  handle = void 0;
34027
- await rename4(temporary, destination);
34646
+ await rename5(temporary, destination);
34028
34647
  if (process.platform !== "win32") {
34029
34648
  await syncDirectory7(registryRoot);
34030
34649
  }
@@ -34034,7 +34653,7 @@ async function recordRunAssignment(runToken, record2, registryRoot = defaultRunR
34034
34653
  } finally {
34035
34654
  await handle?.close().catch(() => {
34036
34655
  });
34037
- await rm6(temporary, { force: true }).catch(() => {
34656
+ await rm7(temporary, { force: true }).catch(() => {
34038
34657
  });
34039
34658
  }
34040
34659
  }
@@ -34046,7 +34665,7 @@ async function forgetRunAssignment(runToken, registryRoot = defaultRunRegistryRo
34046
34665
  if (retainingAssignments) return;
34047
34666
  if (!SAFE_SEGMENT2.test(runToken)) return;
34048
34667
  try {
34049
- await rm6(join13(registryRoot, `${runToken}.json`), { force: true });
34668
+ await rm7(join13(registryRoot, `${runToken}.json`), { force: true });
34050
34669
  } catch {
34051
34670
  }
34052
34671
  }
@@ -34091,7 +34710,7 @@ async function readRecordedRunAssignmentEntries(registryRoot = defaultRunRegistr
34091
34710
  if (!SAFE_SEGMENT2.test(runToken)) continue;
34092
34711
  let text;
34093
34712
  try {
34094
- text = await readFile6(join13(registryRoot, entry.name), "utf8");
34713
+ text = await readFile7(join13(registryRoot, entry.name), "utf8");
34095
34714
  } catch {
34096
34715
  continue;
34097
34716
  }
@@ -34103,7 +34722,7 @@ async function readRecordedRunAssignmentEntries(registryRoot = defaultRunRegistr
34103
34722
  async function readRecordedRunAssignmentEntriesStrict(registryRoot = defaultRunRegistryRoot()) {
34104
34723
  let rootStat;
34105
34724
  try {
34106
- rootStat = await lstat8(registryRoot);
34725
+ rootStat = await lstat9(registryRoot);
34107
34726
  } catch (error52) {
34108
34727
  if (isMissing(error52)) return [];
34109
34728
  throw new Error("run registry state could not be observed");
@@ -34127,7 +34746,7 @@ async function readRecordedRunAssignmentEntriesStrict(registryRoot = defaultRunR
34127
34746
  }
34128
34747
  let text;
34129
34748
  try {
34130
- text = await readFile6(join13(registryRoot, entry.name), "utf8");
34749
+ text = await readFile7(join13(registryRoot, entry.name), "utf8");
34131
34750
  } catch {
34132
34751
  throw new Error("committed run registry witness could not be read");
34133
34752
  }
@@ -34144,13 +34763,13 @@ async function forgetAcknowledgedRunAssignments(assignments, registryRoot = defa
34144
34763
  );
34145
34764
  const entries = await readRecordedRunAssignmentEntries(registryRoot);
34146
34765
  await Promise.all(
34147
- entries.filter(({ record: record2 }) => acknowledged.has(`${record2.taskId}:${record2.epoch}`)).map(({ runToken }) => rm6(join13(registryRoot, `${runToken}.json`), { force: true }))
34766
+ entries.filter(({ record: record2 }) => acknowledged.has(`${record2.taskId}:${record2.epoch}`)).map(({ runToken }) => rm7(join13(registryRoot, `${runToken}.json`), { force: true }))
34148
34767
  );
34149
34768
  }
34150
34769
  async function forgetSupersededRunAssignments(taskId, epoch, registryRoot = defaultRunRegistryRoot()) {
34151
34770
  const entries = await readRecordedRunAssignmentEntries(registryRoot);
34152
34771
  await Promise.all(
34153
- entries.filter(({ record: record2 }) => record2.taskId === taskId && record2.epoch < epoch).map(({ runToken }) => rm6(join13(registryRoot, `${runToken}.json`), { force: true }))
34772
+ entries.filter(({ record: record2 }) => record2.taskId === taskId && record2.epoch < epoch).map(({ runToken }) => rm7(join13(registryRoot, `${runToken}.json`), { force: true }))
34154
34773
  );
34155
34774
  }
34156
34775
 
@@ -34221,7 +34840,7 @@ function truncateThought(text) {
34221
34840
  }
34222
34841
  var MAX_APPROVAL_PAYLOAD = 5e4;
34223
34842
  async function requireRealDirectory4(path, label) {
34224
- const entry = await lstat9(path).catch(() => null);
34843
+ const entry = await lstat10(path).catch(() => null);
34225
34844
  if (!entry) throw new Error(`${label} does not exist`);
34226
34845
  if (entry.isSymbolicLink()) throw new Error(`${label} must not be a symbolic link`);
34227
34846
  if (!entry.isDirectory()) throw new Error(`${label} must be a directory`);
@@ -34237,7 +34856,7 @@ function createCliRunner(adapter, opts = {}) {
34237
34856
  const prefixArgs = opts.commandPrefixArgs ?? [];
34238
34857
  const maxWallTimeMs = opts.maxWallTimeMs;
34239
34858
  const workspaceRoot = opts.workspaceRoot ?? defaultRunnerWorkspaceRoot();
34240
- const artifactRoot = opts.artifactRoot ?? (opts.workspaceRoot === void 0 ? defaultRunnerArtifactRoot() : join14(dirname6(workspaceRoot), "run-artifacts"));
34859
+ const artifactRoot = opts.artifactRoot ?? (opts.workspaceRoot === void 0 ? defaultRunnerArtifactRoot() : join14(dirname7(workspaceRoot), "run-artifacts"));
34241
34860
  const runRegistryRoot2 = opts.runRegistryRoot ?? defaultRunRegistryRoot();
34242
34861
  const createArtifacts = opts.createArtifacts ?? createRunArtifacts;
34243
34862
  const toolPackRegistry2 = opts.toolPackRegistry ?? createDefaultToolPackRegistry();
@@ -34281,7 +34900,12 @@ function createCliRunner(adapter, opts = {}) {
34281
34900
  usage: { inputTokens: 0, outputTokens: 0 }
34282
34901
  });
34283
34902
  if (task.cancelledNow()) return cancelledBeforeRun();
34284
- const runner = task.spec.runner ?? { type: "claude-code", auth: "machine" };
34903
+ const configuredRunner = task.spec.runner ?? {
34904
+ type: "claude-code",
34905
+ auth: "machine"
34906
+ };
34907
+ const runner = { ...configuredRunner };
34908
+ if (runner.model === "default") delete runner.model;
34285
34909
  if (runner.type !== adapter.type) {
34286
34910
  return {
34287
34911
  outcome: "failed",
@@ -34393,7 +35017,13 @@ function createCliRunner(adapter, opts = {}) {
34393
35017
  )
34394
35018
  );
34395
35019
  }
34396
- if (opts.browserManager) {
35020
+ const browserAvailable = opts.browserAvailable ? await opts.browserAvailable() : true;
35021
+ if (task.spec.requiresBrowser && (!opts.browserManager || !browserAvailable)) {
35022
+ throw new Error(
35023
+ "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."
35024
+ );
35025
+ }
35026
+ if (task.spec.requiresBrowser && opts.browserManager && browserAvailable) {
34397
35027
  toolPacks.push(
34398
35028
  createBrowserToolPack({
34399
35029
  manager: opts.browserManager,
@@ -34564,8 +35194,8 @@ ${attachmentSection}` : prompt;
34564
35194
  let changed = false;
34565
35195
  for (const path of paths) {
34566
35196
  if (!path || path.length > 4096) continue;
34567
- const absolutePath = isAbsolute13(path) ? path : resolve7(cwd, path);
34568
- const directory = dirname6(absolutePath);
35197
+ const absolutePath = isAbsolute13(path) ? path : resolve8(cwd, path);
35198
+ const directory = dirname7(absolutePath);
34569
35199
  observedWorkingDirectories.delete(directory);
34570
35200
  observedWorkingDirectories.add(directory);
34571
35201
  while (observedWorkingDirectories.size > 19) {
@@ -34987,7 +35617,7 @@ function runCliProcess(options) {
34987
35617
  usage: { inputTokens: 0, outputTokens: 0 }
34988
35618
  });
34989
35619
  }
34990
- return new Promise((resolve14) => {
35620
+ return new Promise((resolve15) => {
34991
35621
  const platform = options.platform ?? process.platform;
34992
35622
  const containmentGateNonce = options.guardian && platform === "win32" ? randomUUID10() : void 0;
34993
35623
  const child = options.guardian ? spawn8(
@@ -35001,7 +35631,7 @@ function runCliProcess(options) {
35001
35631
  // The idle pre-assignment guardian must never load from or depend
35002
35632
  // on an untrusted Task checkout. Only the post-gate target enters
35003
35633
  // the requested working directory from its private release frame.
35004
- cwd: dirname6(options.guardian.scriptPath),
35634
+ cwd: dirname7(options.guardian.scriptPath),
35005
35635
  env: runnerGuardianEnv(process.env, containmentGateNonce),
35006
35636
  stdio: ["pipe", "pipe", "pipe"],
35007
35637
  windowsHide: true,
@@ -35048,7 +35678,7 @@ function runCliProcess(options) {
35048
35678
  clearInterval(timer);
35049
35679
  unregisterFollowUps?.();
35050
35680
  parser.stop?.();
35051
- resolve14(result);
35681
+ resolve15(result);
35052
35682
  };
35053
35683
  const terminate = (result) => {
35054
35684
  if (settled || forcedResult) return;
@@ -35279,7 +35909,7 @@ function runCliProcess(options) {
35279
35909
  import { randomUUID as randomUUID11 } from "node:crypto";
35280
35910
 
35281
35911
  // src/runners/runtime-observation.ts
35282
- import { open as open5, readdir as readdir4, realpath as realpath8 } from "node:fs/promises";
35912
+ import { open as open6, readdir as readdir4, realpath as realpath8 } from "node:fs/promises";
35283
35913
  import { homedir as homedir5 } from "node:os";
35284
35914
  import { join as join15 } from "node:path";
35285
35915
  var READ_WINDOW_BYTES = 1024 * 1024;
@@ -35291,7 +35921,7 @@ function homeFrom(env) {
35291
35921
  async function readHead(path) {
35292
35922
  let handle;
35293
35923
  try {
35294
- handle = await open5(path, "r");
35924
+ handle = await open6(path, "r");
35295
35925
  const buffer = Buffer.alloc(READ_WINDOW_BYTES);
35296
35926
  const { bytesRead } = await handle.read(buffer, 0, READ_WINDOW_BYTES, 0);
35297
35927
  return buffer.subarray(0, bytesRead).toString("utf8");
@@ -35305,7 +35935,7 @@ async function readHead(path) {
35305
35935
  async function readTail(path) {
35306
35936
  let handle;
35307
35937
  try {
35308
- handle = await open5(path, "r");
35938
+ handle = await open6(path, "r");
35309
35939
  const { size } = await handle.stat();
35310
35940
  const start = Math.max(0, size - READ_WINDOW_BYTES);
35311
35941
  const length = Math.min(size, READ_WINDOW_BYTES);
@@ -35393,7 +36023,7 @@ async function readCodexSessionRuntime(input) {
35393
36023
  }
35394
36024
  var codexCatalogCache = /* @__PURE__ */ new Map();
35395
36025
  async function loadCodexModelCatalog(command, prefixArgs, env) {
35396
- const output = await new Promise((resolve14) => {
36026
+ const output = await new Promise((resolve15) => {
35397
36027
  const child = spawnCli(command, [...prefixArgs, "debug", "models"], {
35398
36028
  stdio: ["ignore", "pipe", "ignore"],
35399
36029
  windowsHide: true,
@@ -35408,7 +36038,7 @@ async function loadCodexModelCatalog(command, prefixArgs, env) {
35408
36038
  if (settled) return;
35409
36039
  settled = true;
35410
36040
  clearTimeout(timer);
35411
- resolve14(value);
36041
+ resolve15(value);
35412
36042
  };
35413
36043
  const timer = setTimeout(() => {
35414
36044
  child.kill();
@@ -35513,8 +36143,8 @@ function createRuntimeReporter(input, sessionId) {
35513
36143
  var EFFORT_READ_ATTEMPTS = 5;
35514
36144
  var EFFORT_READ_INTERVAL_MS = 3e3;
35515
36145
  function delay2(ms) {
35516
- return new Promise((resolve14) => {
35517
- const timer = setTimeout(resolve14, ms);
36146
+ return new Promise((resolve15) => {
36147
+ const timer = setTimeout(resolve15, ms);
35518
36148
  timer.unref?.();
35519
36149
  });
35520
36150
  }
@@ -35591,10 +36221,10 @@ function createClaudeLiveParser(onStream, onSessionModel) {
35591
36221
  },
35592
36222
  async steer(followUp) {
35593
36223
  if (!write) return false;
35594
- return await new Promise((resolve14) => {
35595
- acknowledgements.set(followUp.inputId, resolve14);
36224
+ return await new Promise((resolve15) => {
36225
+ acknowledgements.set(followUp.inputId, resolve15);
35596
36226
  void write(input(followUp.inputId, followUp.text)).catch(() => {
35597
- if (acknowledgements.delete(followUp.inputId)) resolve14(false);
36227
+ if (acknowledgements.delete(followUp.inputId)) resolve15(false);
35598
36228
  });
35599
36229
  });
35600
36230
  },
@@ -35748,13 +36378,13 @@ function improveErrorMessage(error52) {
35748
36378
  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
36379
  }
35750
36380
  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.";
36381
+ return "The selected model is not available. Open the failed Task, choose another model or Default in its runtime controls, then Retry.";
35752
36382
  }
35753
36383
  return error52;
35754
36384
  }
35755
36385
 
35756
36386
  // src/runners/codex.ts
35757
- import { mkdir as mkdir11, readFile as readFile7, writeFile as writeFile6 } from "node:fs/promises";
36387
+ import { mkdir as mkdir11, readFile as readFile8, writeFile as writeFile6 } from "node:fs/promises";
35758
36388
  import { randomUUID as randomUUID12 } from "node:crypto";
35759
36389
  import { homedir as homedir6 } from "node:os";
35760
36390
  import { join as join16 } from "node:path";
@@ -35769,7 +36399,7 @@ function threadIndexPath(root, agentId, sessionKey) {
35769
36399
  }
35770
36400
  async function readThreadId(path) {
35771
36401
  try {
35772
- const parsed = JSON.parse(await readFile7(path, "utf8"));
36402
+ const parsed = JSON.parse(await readFile8(path, "utf8"));
35773
36403
  return typeof parsed.threadId === "string" && /^[A-Za-z0-9-]{1,120}$/.test(parsed.threadId) ? parsed.threadId : null;
35774
36404
  } catch {
35775
36405
  return null;
@@ -35901,8 +36531,8 @@ ${value}` : value;
35901
36531
  var RUNTIME_READ_ATTEMPTS = 5;
35902
36532
  var RUNTIME_READ_INTERVAL_MS = 2e3;
35903
36533
  function delay3(ms) {
35904
- return new Promise((resolve14) => {
35905
- const timer = setTimeout(resolve14, ms);
36534
+ return new Promise((resolve15) => {
36535
+ const timer = setTimeout(resolve15, ms);
35906
36536
  timer.unref?.();
35907
36537
  });
35908
36538
  }
@@ -35941,7 +36571,7 @@ function createCodexAppServerParser(onStream, options) {
35941
36571
  const turnReadyWaiters = /* @__PURE__ */ new Set();
35942
36572
  const usage = () => ({ inputTokens, outputTokens });
35943
36573
  const settleTurnReadiness = (ready) => {
35944
- for (const resolve14 of turnReadyWaiters) resolve14(ready);
36574
+ for (const resolve15 of turnReadyWaiters) resolve15(ready);
35945
36575
  turnReadyWaiters.clear();
35946
36576
  };
35947
36577
  const send = async (message) => {
@@ -36122,12 +36752,12 @@ function createCodexAppServerParser(onStream, options) {
36122
36752
  async steer(input) {
36123
36753
  if (stopped) return false;
36124
36754
  if (!activeTurnId) {
36125
- const ready = await new Promise((resolve14) => turnReadyWaiters.add(resolve14));
36755
+ const ready = await new Promise((resolve15) => turnReadyWaiters.add(resolve15));
36126
36756
  if (!ready || stopped) return false;
36127
36757
  }
36128
36758
  if (!threadId || !activeTurnId) return false;
36129
- return await new Promise((resolve14) => {
36130
- steerWaiters.set(input.inputId, resolve14);
36759
+ return await new Promise((resolve15) => {
36760
+ steerWaiters.set(input.inputId, resolve15);
36131
36761
  void send({
36132
36762
  id: `steer:${input.inputId}`,
36133
36763
  method: "turn/steer",
@@ -36138,7 +36768,7 @@ function createCodexAppServerParser(onStream, options) {
36138
36768
  clientUserMessageId: input.inputId
36139
36769
  }
36140
36770
  }).catch(() => {
36141
- if (steerWaiters.delete(input.inputId)) resolve14(false);
36771
+ if (steerWaiters.delete(input.inputId)) resolve15(false);
36142
36772
  });
36143
36773
  });
36144
36774
  },
@@ -36146,7 +36776,7 @@ function createCodexAppServerParser(onStream, options) {
36146
36776
  stopped = true;
36147
36777
  write = null;
36148
36778
  settleTurnReadiness(false);
36149
- for (const resolve14 of steerWaiters.values()) resolve14(false);
36779
+ for (const resolve15 of steerWaiters.values()) resolve15(false);
36150
36780
  steerWaiters.clear();
36151
36781
  },
36152
36782
  push(chunk) {
@@ -36317,7 +36947,7 @@ function improveCodexErrorMessage(error52) {
36317
36947
  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
36948
  }
36319
36949
  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.";
36950
+ return "The selected model is not available. Open the failed Task, choose another model or Default in its runtime controls, then Retry.";
36321
36951
  }
36322
36952
  return error52;
36323
36953
  }
@@ -36325,7 +36955,7 @@ function improveCodexErrorMessage(error52) {
36325
36955
  // src/runners/git-preflight.ts
36326
36956
  import { spawn as spawn9 } from "node:child_process";
36327
36957
  import { realpath as realpath9 } from "node:fs/promises";
36328
- import { isAbsolute as isAbsolute14, resolve as resolve8 } from "node:path";
36958
+ import { isAbsolute as isAbsolute14, resolve as resolve9 } from "node:path";
36329
36959
  var OUTPUT_LIMIT = 8192;
36330
36960
  var DEFAULT_TIMEOUT_MS4 = 1e4;
36331
36961
  var VERSION_PATTERN = /^git version [^\r\n]{1,108}$/;
@@ -36344,7 +36974,7 @@ async function preflightGit(options = {}) {
36344
36974
  if (configured !== void 0 && !isAbsolute14(configured)) {
36345
36975
  return unavailable("configured git command must be an absolute file", checkedAt);
36346
36976
  }
36347
- const trustedCwd = await realpath9(resolve8(options.trustedCwd ?? process.cwd())).catch(() => null);
36977
+ const trustedCwd = await realpath9(resolve9(options.trustedCwd ?? process.cwd())).catch(() => null);
36348
36978
  if (!trustedCwd)
36349
36979
  return unavailable("Host-owned git preflight directory is unavailable", checkedAt);
36350
36980
  const executablePath = await resolveTrustedCliCommand(configured ?? "git", {
@@ -36566,7 +37196,7 @@ function parseAuth(result) {
36566
37196
  return "unknown";
36567
37197
  }
36568
37198
  function run2(command, args) {
36569
- return new Promise((resolve14) => {
37199
+ return new Promise((resolve15) => {
36570
37200
  const child = spawnCli(command, args, {
36571
37201
  stdio: ["ignore", "pipe", "pipe"],
36572
37202
  windowsHide: true
@@ -36582,7 +37212,7 @@ function run2(command, args) {
36582
37212
  if (settled) return;
36583
37213
  settled = true;
36584
37214
  clearTimeout(timeout);
36585
- resolve14(result);
37215
+ resolve15(result);
36586
37216
  };
36587
37217
  const timeout = setTimeout(() => {
36588
37218
  child.kill();
@@ -36596,9 +37226,9 @@ function run2(command, args) {
36596
37226
  // src/linux-service.ts
36597
37227
  import { spawn as spawn10 } from "node:child_process";
36598
37228
  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";
37229
+ import { access as access4, chmod as chmod7, mkdir as mkdir12, open as open7, rename as rename6, rm as rm8 } from "node:fs/promises";
36600
37230
  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";
37231
+ import { basename as basename3, dirname as dirname8, join as join17, relative as relative8, resolve as resolve10, sep as sep4 } from "node:path";
36602
37232
  var SERVICE_NAME = "zixt-host.service";
36603
37233
  var SYSTEM_SERVICE_COMMAND_TIMEOUT_MS = 7e4;
36604
37234
  var SERVICE_STABILITY_DELAY_MS = 2e3;
@@ -36626,7 +37256,7 @@ function boundedAppend(current, chunk) {
36626
37256
  }
36627
37257
  async function defaultRunCommand(command, args) {
36628
37258
  const commandEnvironment3 = systemServiceCommandEnvironment();
36629
- return new Promise((resolve14) => {
37259
+ return new Promise((resolve15) => {
36630
37260
  const child = spawn10(command, [...args], {
36631
37261
  stdio: ["ignore", "pipe", "pipe"],
36632
37262
  env: commandEnvironment3,
@@ -36640,7 +37270,7 @@ async function defaultRunCommand(command, args) {
36640
37270
  if (settled) return;
36641
37271
  settled = true;
36642
37272
  if (timer) clearTimeout(timer);
36643
- resolve14(result);
37273
+ resolve15(result);
36644
37274
  };
36645
37275
  child.stdout?.on("data", (chunk) => {
36646
37276
  stdout = boundedAppend(stdout, chunk);
@@ -36682,11 +37312,16 @@ function systemdQuotedValue(value, escapeDollar) {
36682
37312
  function systemdUnitValue(value) {
36683
37313
  return systemdQuotedValue(value, true);
36684
37314
  }
36685
- function systemdDirectiveValue(value) {
36686
- return systemdQuotedValue(value, false);
37315
+ function systemdDirectivePath(value) {
37316
+ const path = oneLine(value, "service path");
37317
+ if (!path.startsWith("/")) throw new Error("service path must be absolute");
37318
+ return path.replace(/[%\s"'\\$]/gu, (character) => {
37319
+ if (character === "%") return "%%";
37320
+ return [...Buffer.from(character, "utf8")].map((byte) => `\\x${byte.toString(16).padStart(2, "0")}`).join("");
37321
+ });
36687
37322
  }
36688
37323
  async function defaultSyncDirectory(path) {
36689
- const directory = await open6(path, "r");
37324
+ const directory = await open7(path, "r");
36690
37325
  try {
36691
37326
  await directory.sync();
36692
37327
  } finally {
@@ -36696,9 +37331,9 @@ async function defaultSyncDirectory(path) {
36696
37331
  async function ensureDirectory(path, mode, syncDirectory7) {
36697
37332
  const firstCreated = await mkdir12(path, { recursive: true, mode });
36698
37333
  if (!firstCreated) return;
36699
- const first = resolve9(firstCreated);
36700
- const target = resolve9(path);
36701
- await syncDirectory7(dirname7(first));
37334
+ const first = resolve10(firstCreated);
37335
+ const target = resolve10(path);
37336
+ await syncDirectory7(dirname8(first));
36702
37337
  let current = first;
36703
37338
  const descendants = relative8(first, target);
36704
37339
  for (const part of descendants ? descendants.split(sep4) : []) {
@@ -36707,20 +37342,20 @@ async function ensureDirectory(path, mode, syncDirectory7) {
36707
37342
  }
36708
37343
  }
36709
37344
  async function replacePrivateFile(path, contents, mode, syncDirectory7) {
36710
- const parent = dirname7(path);
37345
+ const parent = dirname8(path);
36711
37346
  await ensureDirectory(parent, 448, syncDirectory7);
36712
37347
  const temporary = join17(parent, `.${basename3(path)}.${process.pid}.${crypto.randomUUID()}.tmp`);
36713
- const handle = await open6(temporary, "wx", mode);
37348
+ const handle = await open7(temporary, "wx", mode);
36714
37349
  try {
36715
37350
  await handle.writeFile(contents, "utf8");
36716
37351
  await handle.sync();
36717
37352
  await handle.close();
36718
- await rename5(temporary, path);
37353
+ await rename6(temporary, path);
36719
37354
  await chmod7(path, mode);
36720
37355
  await syncDirectory7(parent);
36721
37356
  } catch (error52) {
36722
37357
  await handle.close().catch(() => void 0);
36723
- await rm7(temporary, { force: true }).catch(() => void 0);
37358
+ await rm8(temporary, { force: true }).catch(() => void 0);
36724
37359
  throw error52;
36725
37360
  }
36726
37361
  }
@@ -36771,7 +37406,7 @@ async function installLinuxService(options) {
36771
37406
  const resolveCommand = options.resolveCommand ?? defaultResolveCommand;
36772
37407
  const run3 = options.runCommand ?? defaultRunCommand;
36773
37408
  const syncDirectory7 = options.syncDirectory ?? defaultSyncDirectory;
36774
- const stabilityDelay = options.delay ?? ((ms) => new Promise((resolve14) => setTimeout(resolve14, ms)));
37409
+ const stabilityDelay = options.delay ?? ((ms) => new Promise((resolve15) => setTimeout(resolve15, ms)));
36775
37410
  const [systemctl, loginctl] = await Promise.all([
36776
37411
  resolveCommand("systemctl"),
36777
37412
  resolveCommand("loginctl")
@@ -36818,7 +37453,7 @@ async function installLinuxService(options) {
36818
37453
  // Type=exec does not report startup success until the kernel has executed
36819
37454
  // Node, so a missing/corrupt release cannot masquerade as an active Host.
36820
37455
  "Type=exec",
36821
- `EnvironmentFile=${systemdDirectiveValue(environmentPath)}`,
37456
+ `EnvironmentFile=${systemdDirectivePath(environmentPath)}`,
36822
37457
  `ExecStart=${systemdUnitValue(process.execPath)} ${systemdUnitValue(currentEntry)}`,
36823
37458
  "Restart=on-failure",
36824
37459
  "RestartPreventExitStatus=64",
@@ -36884,9 +37519,9 @@ async function installLinuxService(options) {
36884
37519
  // src/macos-service.ts
36885
37520
  import { spawn as spawn11 } from "node:child_process";
36886
37521
  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";
37522
+ import { access as access5, chmod as chmod8, mkdir as mkdir13, open as open8, rename as rename7, rm as rm9 } from "node:fs/promises";
36888
37523
  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";
37524
+ import { basename as basename4, dirname as dirname9, join as join18, relative as relative9, resolve as resolve11, sep as sep5 } from "node:path";
36890
37525
  var LAUNCH_AGENT_LABEL = "ai.zixt.host";
36891
37526
  var SERVICE_STABILITY_DELAY_MS2 = 2e3;
36892
37527
  var COMMAND_TIMEOUT_MS2 = 7e4;
@@ -36902,7 +37537,7 @@ function shellValue(value) {
36902
37537
  return `'${oneLine2(value, "service setting").replaceAll("'", `'"'"'`)}'`;
36903
37538
  }
36904
37539
  async function syncDirectory4(path) {
36905
- const directory = await open7(path, "r");
37540
+ const directory = await open8(path, "r");
36906
37541
  try {
36907
37542
  await directory.sync();
36908
37543
  } finally {
@@ -36912,9 +37547,9 @@ async function syncDirectory4(path) {
36912
37547
  async function ensureDirectory2(path, sync) {
36913
37548
  const firstCreated = await mkdir13(path, { recursive: true, mode: 448 });
36914
37549
  if (!firstCreated) return;
36915
- const first = resolve10(firstCreated);
36916
- const target = resolve10(path);
36917
- await sync(dirname8(first));
37550
+ const first = resolve11(firstCreated);
37551
+ const target = resolve11(path);
37552
+ await sync(dirname9(first));
36918
37553
  let current = first;
36919
37554
  for (const part of relative9(first, target).split(sep5).filter(Boolean)) {
36920
37555
  await sync(current);
@@ -36922,20 +37557,20 @@ async function ensureDirectory2(path, sync) {
36922
37557
  }
36923
37558
  }
36924
37559
  async function replacePrivateFile2(path, contents, mode, sync) {
36925
- const parent = dirname8(path);
37560
+ const parent = dirname9(path);
36926
37561
  await ensureDirectory2(parent, sync);
36927
37562
  const temporary = join18(parent, `.${basename4(path)}.${process.pid}.${crypto.randomUUID()}.tmp`);
36928
- const handle = await open7(temporary, "wx", mode);
37563
+ const handle = await open8(temporary, "wx", mode);
36929
37564
  try {
36930
37565
  await handle.writeFile(contents, "utf8");
36931
37566
  await handle.sync();
36932
37567
  await handle.close();
36933
- await rename6(temporary, path);
37568
+ await rename7(temporary, path);
36934
37569
  await chmod8(path, mode);
36935
37570
  await sync(parent);
36936
37571
  } catch (error52) {
36937
37572
  await handle.close().catch(() => void 0);
36938
- await rm8(temporary, { force: true }).catch(() => void 0);
37573
+ await rm9(temporary, { force: true }).catch(() => void 0);
36939
37574
  throw error52;
36940
37575
  }
36941
37576
  }
@@ -37115,9 +37750,9 @@ async function installMacosService(options) {
37115
37750
  // src/windows-service.ts
37116
37751
  import { spawn as spawn12 } from "node:child_process";
37117
37752
  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";
37753
+ import { access as access6, mkdir as mkdir14, open as open9, readFile as readFile9, rename as rename8, rm as rm10 } from "node:fs/promises";
37119
37754
  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";
37755
+ 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
37756
  var TASK_NAME = "Zixt Host";
37122
37757
  var COMMAND_TIMEOUT_MS3 = 7e4;
37123
37758
  var SERVICE_STABILITY_DELAY_MS3 = 2e3;
@@ -37135,7 +37770,7 @@ function psLiteral(value) {
37135
37770
  }
37136
37771
  async function syncDirectory5(path) {
37137
37772
  if (process.platform === "win32") return;
37138
- const directory = await open8(path, "r");
37773
+ const directory = await open9(path, "r");
37139
37774
  try {
37140
37775
  await directory.sync();
37141
37776
  } finally {
@@ -37145,9 +37780,9 @@ async function syncDirectory5(path) {
37145
37780
  async function ensureDirectory3(path, sync) {
37146
37781
  const firstCreated = await mkdir14(path, { recursive: true, mode: 448 });
37147
37782
  if (!firstCreated) return;
37148
- const first = resolve11(firstCreated);
37149
- const target = resolve11(path);
37150
- await sync(dirname9(first));
37783
+ const first = resolve12(firstCreated);
37784
+ const target = resolve12(path);
37785
+ await sync(dirname10(first));
37151
37786
  let current = first;
37152
37787
  for (const part of relative10(first, target).split(sep6).filter(Boolean)) {
37153
37788
  await sync(current);
@@ -37155,19 +37790,19 @@ async function ensureDirectory3(path, sync) {
37155
37790
  }
37156
37791
  }
37157
37792
  async function replacePrivateFile3(path, contents, sync) {
37158
- const parent = dirname9(path);
37793
+ const parent = dirname10(path);
37159
37794
  await ensureDirectory3(parent, sync);
37160
37795
  const temporary = join19(parent, `.${basename5(path)}.${process.pid}.${crypto.randomUUID()}.tmp`);
37161
- const handle = await open8(temporary, "wx", 384);
37796
+ const handle = await open9(temporary, "wx", 384);
37162
37797
  try {
37163
37798
  await handle.writeFile(contents, "utf8");
37164
37799
  await handle.sync();
37165
37800
  await handle.close();
37166
- await rename7(temporary, path);
37801
+ await rename8(temporary, path);
37167
37802
  await sync(parent);
37168
37803
  } catch (error52) {
37169
37804
  await handle.close().catch(() => void 0);
37170
- await rm9(temporary, { force: true }).catch(() => void 0);
37805
+ await rm10(temporary, { force: true }).catch(() => void 0);
37171
37806
  throw error52;
37172
37807
  }
37173
37808
  }
@@ -37300,7 +37935,7 @@ exit $code
37300
37935
  }
37301
37936
  async function defaultObserveStatus(path, generation) {
37302
37937
  try {
37303
- const text = (await readFile8(path, "utf8")).replace(/^\uFEFF/, "");
37938
+ const text = (await readFile9(path, "utf8")).replace(/^\uFEFF/, "");
37304
37939
  const value = JSON.parse(text);
37305
37940
  if (value.schema !== 1 || value.generation !== generation || typeof value.pid !== "number" || !Number.isSafeInteger(value.pid) || value.pid <= 0) {
37306
37941
  return null;
@@ -37402,7 +38037,7 @@ async function installWindowsService(options) {
37402
38037
  );
37403
38038
  await replacePrivateFile3(launcherPath, launcherSource2(configPath, statusPath), sync);
37404
38039
  await replacePrivateFile3(taskXmlPath, taskXml({ sid, powershell, launcherPath, home }), sync);
37405
- await rm9(statusPath, { force: true });
38040
+ await rm10(statusPath, { force: true });
37406
38041
  const acl = await run3(icacls, [
37407
38042
  configRoot,
37408
38043
  "/inheritance:r",
@@ -37454,9 +38089,9 @@ async function installSystemService(options) {
37454
38089
  }
37455
38090
 
37456
38091
  // 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";
38092
+ 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
38093
  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";
38094
+ import { dirname as dirname11, join as join20, relative as relative11, resolve as resolve13, sep as sep7 } from "node:path";
37460
38095
  var DIRECTORY_MODE5 = 448;
37461
38096
  var FILE_MODE4 = 384;
37462
38097
  var MAX_OUTCOME_BYTES = 4 * 1024 * 1024;
@@ -37474,7 +38109,7 @@ function outcomePath(root, hostId, taskId, epoch) {
37474
38109
  }
37475
38110
  async function syncDirectory6(root) {
37476
38111
  if (process.platform === "win32") return;
37477
- const handle = await open9(root, "r");
38112
+ const handle = await open10(root, "r");
37478
38113
  try {
37479
38114
  await handle.sync();
37480
38115
  } finally {
@@ -37484,16 +38119,16 @@ async function syncDirectory6(root) {
37484
38119
  async function requirePrivateRoot(root, sync = syncDirectory6) {
37485
38120
  const firstCreated = await mkdir15(root, { recursive: true, mode: DIRECTORY_MODE5 });
37486
38121
  if (firstCreated) {
37487
- const first = resolve12(firstCreated);
37488
- const target = resolve12(root);
37489
- await sync(dirname10(first));
38122
+ const first = resolve13(firstCreated);
38123
+ const target = resolve13(root);
38124
+ await sync(dirname11(first));
37490
38125
  let current = first;
37491
38126
  for (const part of relative11(first, target).split(sep7).filter(Boolean)) {
37492
38127
  await sync(current);
37493
38128
  current = join20(current, part);
37494
38129
  }
37495
38130
  }
37496
- const stat3 = await lstat10(root);
38131
+ const stat3 = await lstat11(root);
37497
38132
  if (stat3.isSymbolicLink() || !stat3.isDirectory()) {
37498
38133
  throw new Error("terminal outcome journal root is not a trusted directory");
37499
38134
  }
@@ -37521,7 +38156,7 @@ async function recordTerminalOutcome(hostId, input, root = defaultTerminalOutcom
37521
38156
  const destination = outcomePath(root, hostId, outcome.taskId, outcome.epoch);
37522
38157
  try {
37523
38158
  const existing = parseCommittedOutcome(
37524
- await readFile9(destination, { encoding: "utf8", flag: "r" }),
38159
+ await readFile10(destination, { encoding: "utf8", flag: "r" }),
37525
38160
  outcome.taskId,
37526
38161
  outcome.epoch
37527
38162
  );
@@ -37536,25 +38171,25 @@ async function recordTerminalOutcome(hostId, input, root = defaultTerminalOutcom
37536
38171
  );
37537
38172
  let handle;
37538
38173
  try {
37539
- handle = await open9(temporary, "wx", FILE_MODE4);
38174
+ handle = await open10(temporary, "wx", FILE_MODE4);
37540
38175
  await handle.writeFile(JSON.stringify(outcome), "utf8");
37541
38176
  await handle.sync();
37542
38177
  await handle.close();
37543
38178
  handle = void 0;
37544
- await rename8(temporary, destination);
38179
+ await rename9(temporary, destination);
37545
38180
  await sync(scopedRoot);
37546
38181
  await sync(root);
37547
38182
  } finally {
37548
38183
  await handle?.close().catch(() => {
37549
38184
  });
37550
- await rm10(temporary, { force: true }).catch(() => {
38185
+ await rm11(temporary, { force: true }).catch(() => {
37551
38186
  });
37552
38187
  }
37553
38188
  }
37554
38189
  async function readTerminalOutcomesStrict(root = defaultTerminalOutcomeRoot()) {
37555
38190
  let rootStat;
37556
38191
  try {
37557
- rootStat = await lstat10(root);
38192
+ rootStat = await lstat11(root);
37558
38193
  } catch (error52) {
37559
38194
  if (error52.code === "ENOENT") return [];
37560
38195
  throw new Error("terminal outcome journal could not be observed");
@@ -37571,7 +38206,7 @@ async function readTerminalOutcomesStrict(root = defaultTerminalOutcomeRoot()) {
37571
38206
  throw new Error("terminal outcome Host scope is not a trusted directory");
37572
38207
  }
37573
38208
  const scopedRoot = hostOutcomeRoot(root, hostEntry.name);
37574
- const scopedStat = await lstat10(scopedRoot);
38209
+ const scopedStat = await lstat11(scopedRoot);
37575
38210
  if (scopedStat.isSymbolicLink() || !scopedStat.isDirectory()) {
37576
38211
  throw new Error("terminal outcome Host scope is not a trusted directory");
37577
38212
  }
@@ -37584,12 +38219,12 @@ async function readTerminalOutcomesStrict(root = defaultTerminalOutcomeRoot()) {
37584
38219
  throw new Error("committed terminal outcome is not a trusted regular file");
37585
38220
  }
37586
38221
  const path = join20(scopedRoot, entry.name);
37587
- const stat3 = await lstat10(path);
38222
+ const stat3 = await lstat11(path);
37588
38223
  if (!stat3.isFile() || stat3.isSymbolicLink() || stat3.size > MAX_OUTCOME_BYTES) {
37589
38224
  throw new Error("committed terminal outcome is not a trusted regular file");
37590
38225
  }
37591
38226
  const outcome = parseCommittedOutcome(
37592
- await readFile9(path, "utf8"),
38227
+ await readFile10(path, "utf8"),
37593
38228
  match[1],
37594
38229
  Number(match[2])
37595
38230
  );
@@ -37616,7 +38251,7 @@ async function forgetAcknowledgedTerminalOutcomes(refs, root = defaultTerminalOu
37616
38251
  if (acknowledged.get(`${hostId}:${outcome.taskId}:${outcome.epoch}`) !== outcome.resultId) {
37617
38252
  continue;
37618
38253
  }
37619
- await rm10(outcomePath(root, hostId, outcome.taskId, outcome.epoch), { force: true });
38254
+ await rm11(outcomePath(root, hostId, outcome.taskId, outcome.epoch), { force: true });
37620
38255
  changedHostRoots.add(hostOutcomeRoot(root, hostId));
37621
38256
  }
37622
38257
  for (const scopedRoot of changedHostRoots) await syncDirectory6(scopedRoot);
@@ -37628,7 +38263,7 @@ async function forgetSupersededTerminalOutcomes(hostId, taskId, epoch, root = de
37628
38263
  if (scoped.hostId !== hostId) continue;
37629
38264
  const { outcome } = scoped;
37630
38265
  if (outcome.taskId !== taskId || outcome.epoch >= epoch) continue;
37631
- await rm10(outcomePath(root, hostId, outcome.taskId, outcome.epoch), { force: true });
38266
+ await rm11(outcomePath(root, hostId, outcome.taskId, outcome.epoch), { force: true });
37632
38267
  removed = true;
37633
38268
  }
37634
38269
  if (removed) await syncDirectory6(hostOutcomeRoot(root, hostId));
@@ -37736,12 +38371,12 @@ function createHostLogger(options = {}) {
37736
38371
  }
37737
38372
 
37738
38373
  // src/demo-state.ts
37739
- import { isAbsolute as isAbsolute16, join as join21, parse as parse3, resolve as resolve13 } from "node:path";
38374
+ import { isAbsolute as isAbsolute16, join as join21, parse as parse3, resolve as resolve14 } from "node:path";
37740
38375
  var DEMO_STATE_ROOT_ENV = "ZIXT_DEMO_STATE_ROOT";
37741
38376
  function resolveDemoHostStatePaths(env = process.env) {
37742
38377
  const configured = env.ZIXT_RUNNER === "demo" ? env[DEMO_STATE_ROOT_ENV] : void 0;
37743
38378
  if (!configured) return null;
37744
- const root = resolve13(configured);
38379
+ const root = resolve14(configured);
37745
38380
  if (!isAbsolute16(configured) || root === parse3(root).root) {
37746
38381
  throw new Error(`${DEMO_STATE_ROOT_ENV} must be a dedicated absolute directory`);
37747
38382
  }
@@ -38063,6 +38698,7 @@ var claudeCode = createClaudeCodeRunner({
38063
38698
  ...runRegistryRoot ? { runRegistryRoot } : {},
38064
38699
  toolPackRegistry,
38065
38700
  browserManager,
38701
+ browserAvailable: async () => (await currentBrowserCapability()).status === "ok",
38066
38702
  gitPreflight: currentGitPreflight,
38067
38703
  onUnsafeRunnerCleanup: restartAfterUnsafeRunnerCleanup
38068
38704
  });
@@ -38073,6 +38709,7 @@ var codex = createCodexRunner({
38073
38709
  ...codexThreadIndexRoot ? { threadIndexRoot: codexThreadIndexRoot } : {},
38074
38710
  toolPackRegistry,
38075
38711
  browserManager,
38712
+ browserAvailable: async () => (await currentBrowserCapability()).status === "ok",
38076
38713
  gitPreflight: currentGitPreflight,
38077
38714
  onUnsafeRunnerCleanup: restartAfterUnsafeRunnerCleanup
38078
38715
  });