@zixt/host 0.0.135 → 0.0.137

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 +843 -182
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -28,7 +28,7 @@ import { homedir as homedir4 } from "node:os";
28
28
  // package.json
29
29
  var package_default = {
30
30
  name: "@zixt/host",
31
- version: "0.0.135",
31
+ version: "0.0.137",
32
32
  type: "module",
33
33
  exports: {
34
34
  ".": "./src/client.ts",
@@ -14700,6 +14700,10 @@ var ID_PREFIXES = {
14700
14700
  voiceTurn: "vtr",
14701
14701
  /** One signed voice-provider webhook receipt. */
14702
14702
  voiceWebhookEvent: "vwe",
14703
+ /** One live browser microphone session with the Zixt Guide (UI-16). */
14704
+ guideVoiceSession: "gvs",
14705
+ /** One Guide reply authorized to be spoken on that session. */
14706
+ guideVoiceSpeech: "gvt",
14703
14707
  /** Org-singleton Company setup record (PRD OB-5). */
14704
14708
  companySetup: "cst",
14705
14709
  /** One durable set of teammate-proposed Automations awaiting an Admin (OB-12). */
@@ -14780,6 +14784,11 @@ var VoiceLineId = idSchema(ID_PREFIXES.voiceLine, "voice line id");
14780
14784
  var VoiceCallerId = idSchema(ID_PREFIXES.voiceCaller, "voice caller id");
14781
14785
  var VoiceCallId = idSchema(ID_PREFIXES.voiceCall, "voice call id");
14782
14786
  var VoiceTurnId = idSchema(ID_PREFIXES.voiceTurn, "voice turn id");
14787
+ var GuideVoiceSessionId = idSchema(
14788
+ ID_PREFIXES.guideVoiceSession,
14789
+ "Guide voice session id"
14790
+ );
14791
+ var GuideVoiceSpeechId = idSchema(ID_PREFIXES.guideVoiceSpeech, "Guide speech id");
14783
14792
  var VoiceWebhookEventId = idSchema(
14784
14793
  ID_PREFIXES.voiceWebhookEvent,
14785
14794
  "voice webhook event id"
@@ -19659,6 +19668,19 @@ var TestWebhookDeliveryRequest = external_exports.object({
19659
19668
  // ../../packages/contracts/src/manager.ts
19660
19669
  var MANAGER_DEFAULT_MODEL = "gpt-5.6-luna";
19661
19670
  var MANAGER_DEFAULT_PORTRAIT = 23;
19671
+ var ManagerVoiceName = external_exports.enum([
19672
+ "marin",
19673
+ "cedar",
19674
+ "alloy",
19675
+ "ash",
19676
+ "ballad",
19677
+ "coral",
19678
+ "echo",
19679
+ "sage",
19680
+ "shimmer",
19681
+ "verse"
19682
+ ]);
19683
+ var MANAGER_DEFAULT_VOICE = "marin";
19662
19684
  var MANAGER_DISPLAY_NAME_MAX = 60;
19663
19685
  var MANAGER_INSTRUCTIONS_MAX = 2e4;
19664
19686
  var CONVERSATION_MESSAGE_MAX = 1e5;
@@ -19694,6 +19716,11 @@ var ManagerConfigProjection = external_exports.object({
19694
19716
  * never rejected by a server that has not shipped the newer artwork yet.
19695
19717
  */
19696
19718
  portrait: external_exports.number().int().min(0).max(63).default(MANAGER_DEFAULT_PORTRAIT),
19719
+ /**
19720
+ * The voice every spoken Zixt surface renders in for this organization.
19721
+ * Older rows carry none and read as MANAGER_DEFAULT_VOICE.
19722
+ */
19723
+ voice: ManagerVoiceName.default(MANAGER_DEFAULT_VOICE),
19697
19724
  revision: external_exports.number().int().min(0),
19698
19725
  updatedAt: external_exports.string().nullable()
19699
19726
  }).strict();
@@ -19709,10 +19736,11 @@ var UpdateManagerRequest = external_exports.object({
19709
19736
  answerDirectMessages: external_exports.boolean().optional()
19710
19737
  }).strict().optional(),
19711
19738
  portrait: external_exports.number().int().min(0).max(63).optional(),
19739
+ voice: ManagerVoiceName.optional(),
19712
19740
  /** Optimistic concurrency: refuse when another admin saved first. */
19713
19741
  expectedRevision: external_exports.number().int().min(0).optional()
19714
19742
  }).strict().superRefine((value, ctx) => {
19715
- if (value.displayName === void 0 && value.instructions === void 0 && value.model === void 0 && value.reasoningEffort === void 0 && value.slack === void 0 && value.portrait === void 0) {
19743
+ if (value.displayName === void 0 && value.instructions === void 0 && value.model === void 0 && value.reasoningEffort === void 0 && value.slack === void 0 && value.portrait === void 0 && value.voice === void 0) {
19716
19744
  ctx.addIssue({ code: "custom", message: "nothing to update" });
19717
19745
  }
19718
19746
  });
@@ -22184,7 +22212,13 @@ var SetupGuideReplyRequest = external_exports.object({
22184
22212
  onboarding: SetupGuideOnboardingState,
22185
22213
  actions: external_exports.array(SetupGuideAvailableAction).max(128),
22186
22214
  toolResults: external_exports.array(SetupGuideToolResult).max(12).default([]),
22187
- mission: SetupGuideMission.nullable().optional()
22215
+ mission: SetupGuideMission.nullable().optional(),
22216
+ /**
22217
+ * The live Guide voice session this turn was spoken into. It only authorizes
22218
+ * the resulting reply to be spoken on that same session; it never changes
22219
+ * what the Guide may read, propose, or execute.
22220
+ */
22221
+ voiceSessionId: GuideVoiceSessionId.optional()
22188
22222
  });
22189
22223
  var SetupGuideProposedAction = external_exports.object({
22190
22224
  actionId: external_exports.string().regex(/^[a-z][a-z0-9_.-]{0,79}$/),
@@ -22195,11 +22229,21 @@ var SetupGuideProposedAction = external_exports.object({
22195
22229
  */
22196
22230
  parameters: external_exports.array(SetupGuideActionParameter).max(12)
22197
22231
  });
22198
- var SetupGuideReplyResponse = external_exports.object({
22232
+ var SetupGuideAnswer = external_exports.object({
22199
22233
  message: GuideText,
22200
22234
  /** One tool at a time keeps UI execution and its result causally ordered. */
22201
22235
  proposedActions: external_exports.array(SetupGuideProposedAction).max(1)
22202
22236
  });
22237
+ var SetupGuideReplyResponse = SetupGuideAnswer.extend({
22238
+ /**
22239
+ * Identity this exact committed reply may be spoken under, present only when
22240
+ * the request named a live voice session. The browser runs a bounded action
22241
+ * loop, so only it knows which reply in that loop is the person's answer;
22242
+ * this id is how it says "speak that one" without ever sending prose back for
22243
+ * the cloud to read aloud.
22244
+ */
22245
+ speech: external_exports.object({ id: GuideVoiceSpeechId }).strict().nullable().default(null)
22246
+ });
22203
22247
  var SetupGuideTranscriptionRequest = external_exports.object({
22204
22248
  mediaType: external_exports.enum(["audio/webm", "audio/mp4", "audio/ogg"]),
22205
22249
  /** Bounded short-lived audio. The route decodes it in memory and never stores it. */
@@ -22208,6 +22252,52 @@ var SetupGuideTranscriptionRequest = external_exports.object({
22208
22252
  var SetupGuideTranscriptionResponse = external_exports.object({
22209
22253
  text: GuideText
22210
22254
  });
22255
+ var StartGuideVoiceSessionRequest = external_exports.object({
22256
+ /** Stable client fence; an ambiguous provider create is never resubmitted under it. */
22257
+ requestId: external_exports.uuid(),
22258
+ offerSdp: BrowserVoiceSessionDescription
22259
+ }).strict();
22260
+ var GuideVoiceSessionStatus = external_exports.enum([
22261
+ "connecting",
22262
+ "connected",
22263
+ "ending",
22264
+ "ended",
22265
+ "failed"
22266
+ ]);
22267
+ var GuideVoiceSessionProjection = external_exports.object({
22268
+ id: GuideVoiceSessionId,
22269
+ status: GuideVoiceSessionStatus,
22270
+ startedAt: IsoDate2,
22271
+ expiresAt: IsoDate2
22272
+ }).strict();
22273
+ var StartGuideVoiceSessionResponse = external_exports.object({
22274
+ session: GuideVoiceSessionProjection,
22275
+ /** Applied directly to the browser peer connection and never persisted by Zixt. */
22276
+ answerSdp: BrowserVoiceSessionDescription
22277
+ }).strict();
22278
+ var AckGuideVoiceSessionResponse = external_exports.object({ session: GuideVoiceSessionProjection }).strict();
22279
+ var EndGuideVoiceSessionResponse = external_exports.object({ ended: external_exports.literal(true) }).strict();
22280
+ var GuideVoiceCaption = external_exports.string().max(4e3);
22281
+ var GuideVoicePresentationFrame = external_exports.discriminatedUnion("type", [
22282
+ external_exports.object({ type: external_exports.literal("listening_started") }).strict(),
22283
+ external_exports.object({ type: external_exports.literal("listening_stopped") }).strict(),
22284
+ external_exports.object({ type: external_exports.literal("user_caption"), text: GuideVoiceCaption, final: external_exports.boolean() }).strict(),
22285
+ external_exports.object({
22286
+ type: external_exports.literal("guide_speech_started"),
22287
+ speechId: GuideVoiceSpeechId,
22288
+ text: GuideVoiceCaption
22289
+ }).strict(),
22290
+ external_exports.object({ type: external_exports.literal("guide_speech_stopped"), speechId: GuideVoiceSpeechId }).strict(),
22291
+ external_exports.object({ type: external_exports.literal("guide_speech_failed"), speechId: GuideVoiceSpeechId }).strict(),
22292
+ external_exports.object({ type: external_exports.literal("guide_speech_idle") }).strict(),
22293
+ external_exports.object({ type: external_exports.literal("transcription_failed") }).strict(),
22294
+ external_exports.object({ type: external_exports.literal("session_ended") }).strict(),
22295
+ external_exports.object({ type: external_exports.literal("session_failed") }).strict()
22296
+ ]);
22297
+ var SpeakGuideVoiceReplyRequest = external_exports.object({ speechId: GuideVoiceSpeechId }).strict();
22298
+ var SpeakGuideVoiceReplyResponse = external_exports.object({ speaking: external_exports.boolean() }).strict();
22299
+ var InterruptGuideVoiceSessionRequest = external_exports.object({ speechId: GuideVoiceSpeechId }).strict();
22300
+ var InterruptGuideVoiceSessionResponse = external_exports.object({ interrupted: external_exports.boolean() }).strict();
22211
22301
 
22212
22302
  // ../../packages/contracts/src/roles.ts
22213
22303
  var CompanyToolKind = external_exports.enum([
@@ -22472,6 +22562,12 @@ var CompanySetupProgress = external_exports.object({
22472
22562
  /** Every recommended step is done or skipped. */
22473
22563
  recommendedSettled: external_exports.boolean()
22474
22564
  });
22565
+ var COMPANY_SETUP_PLANNING_NOTE_MAX = 200;
22566
+ var COMPANY_SETUP_PLANNING_NOTES_MAX = 20;
22567
+ var CompanySetupPlanningNote = external_exports.object({
22568
+ at: IsoDate,
22569
+ text: external_exports.string().trim().min(1).max(COMPANY_SETUP_PLANNING_NOTE_MAX)
22570
+ });
22475
22571
  var CompanySetupApproval = external_exports.object({
22476
22572
  at: IsoDate,
22477
22573
  by: MemberId.nullable(),
@@ -22524,7 +22620,12 @@ var CompanySetupView = external_exports.object({
22524
22620
  * The plan is being proposed in the background (a planner call can take a
22525
22621
  * minute); the page polls until the plan lands or an error is recorded.
22526
22622
  */
22527
- planning: external_exports.object({ startedAt: IsoDate, error: external_exports.string().max(500).nullable() }).nullable(),
22623
+ planning: external_exports.object({
22624
+ startedAt: IsoDate,
22625
+ error: external_exports.string().max(500).nullable(),
22626
+ /** Oldest first; the planner's visible decisions so far. */
22627
+ notes: external_exports.array(CompanySetupPlanningNote).max(COMPANY_SETUP_PLANNING_NOTES_MAX).default([])
22628
+ }).nullable(),
22528
22629
  approval: CompanySetupApproval.nullable(),
22529
22630
  steps: external_exports.array(CompanySetupStep),
22530
22631
  progress: CompanySetupProgress,
@@ -31615,11 +31716,11 @@ function createPlaywrightBrowserAdapterFactory(dependencies = {}) {
31615
31716
  }
31616
31717
 
31617
31718
  // src/runners/cli-runner.ts
31618
- import { spawn as spawn10 } from "node:child_process";
31619
- import { randomUUID as randomUUID11 } from "node:crypto";
31620
- import { lstat as lstat11, mkdir as mkdir12, realpath as realpath8 } from "node:fs/promises";
31719
+ import { spawn as spawn11 } from "node:child_process";
31720
+ import { randomUUID as randomUUID12 } from "node:crypto";
31721
+ import { lstat as lstat11, mkdir as mkdir13, realpath as realpath8 } from "node:fs/promises";
31621
31722
  import { homedir as homedir7 } from "node:os";
31622
- import { dirname as dirname10, isAbsolute as isAbsolute16, join as join18, resolve as resolve10 } from "node:path";
31723
+ import { dirname as dirname10, isAbsolute as isAbsolute16, join as join19, resolve as resolve10 } from "node:path";
31623
31724
 
31624
31725
  // src/tool-packs/browser/authentication-wall.ts
31625
31726
  var AUTH_PATH_SEGMENT = /(?:^|\/)(?:log[-_]?in|sign[-_]?in|sso|saml|auth|authorize|authenticate|oauth2?|session\/new|checkpoint)(?:\/|$)/i;
@@ -38006,6 +38107,415 @@ function renderAttachmentSection(files) {
38006
38107
 
38007
38108
  // src/runners/ask-user-server.ts
38008
38109
  import { createServer as createServer2 } from "node:http";
38110
+
38111
+ // src/runners/software-install.ts
38112
+ import { spawn as spawn9 } from "node:child_process";
38113
+ import { createHash as createHash6, randomUUID as randomUUID11 } from "node:crypto";
38114
+ import { chmod as chmod6, mkdir as mkdir11, readFile as readFile10, readdir as readdir5, rename as rename6, rm as rm10, symlink as symlink2, writeFile as writeFile7 } from "node:fs/promises";
38115
+ import { join as join17, relative as relative8, resolve as resolvePath, sep as sep5 } from "node:path";
38116
+ var SOFTWARE_INSTALL_METHODS = ["npm", "download"];
38117
+ var SOFTWARE_INSTALL_REFUSAL = "Zixt installs software only into its own folder on this Machine, using npm or a direct download. It never runs a system package manager, never asks for elevation, and never builds from source. Tell the person plainly what is missing, why this work needs it, and the exact command they would run themselves.";
38118
+ var MAX_DOWNLOAD_BYTES = 512 * 1024 * 1024;
38119
+ var PROCESS_TIMEOUT_MS = 10 * 6e4;
38120
+ var DOWNLOAD_TIMEOUT_MS = 10 * 6e4;
38121
+ var FAILURE_COOLDOWN_MS2 = 10 * 6e4;
38122
+ var MAX_ARCHIVE_SEARCH_DEPTH = 5;
38123
+ var MANIFEST_NAME = "installed.json";
38124
+ var COMMAND_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,63}$/;
38125
+ var NPM_SPEC_PATTERN = /^(?:@[a-z0-9~][a-z0-9._~-]*\/)?[a-z0-9~][a-z0-9._~-]*(?:@\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)?$/;
38126
+ function defaultSoftwareToolsRoot() {
38127
+ return defaultRunnerToolsRoot();
38128
+ }
38129
+ function softwareSearchPathEntries(toolsRoot = defaultSoftwareToolsRoot()) {
38130
+ return [toolsRoot, join17(toolsRoot, "bin")];
38131
+ }
38132
+ function refuseSoftwareRequest(request) {
38133
+ if (!COMMAND_PATTERN.test(request.command)) {
38134
+ return "The command name must be a plain executable name with no path, spaces, or shell characters.";
38135
+ }
38136
+ if (!SOFTWARE_INSTALL_METHODS.includes(request.method)) {
38137
+ return SOFTWARE_INSTALL_REFUSAL;
38138
+ }
38139
+ if (request.method === "npm") {
38140
+ if (!NPM_SPEC_PATTERN.test(request.source)) {
38141
+ return "The npm source must be a package name, optionally with one exact version as name@1.2.3. Ranges, tags, git specs, URLs, and local paths are not installed.";
38142
+ }
38143
+ if (request.sha256 !== void 0) {
38144
+ return "An expected sha256 applies only to downloads; the npm registry names versions, not file digests.";
38145
+ }
38146
+ return null;
38147
+ }
38148
+ let url3;
38149
+ try {
38150
+ url3 = new URL(request.source);
38151
+ } catch {
38152
+ return "The download source must be an absolute HTTPS URL.";
38153
+ }
38154
+ if (url3.protocol !== "https:") return "The download source must use HTTPS.";
38155
+ if (url3.username || url3.password) {
38156
+ return "The download source must not carry credentials in the URL.";
38157
+ }
38158
+ if (request.sha256 !== void 0 && !/^[a-f0-9]{64}$/i.test(request.sha256)) {
38159
+ return "The expected sha256 must be 64 hexadecimal characters.";
38160
+ }
38161
+ return null;
38162
+ }
38163
+ async function runProcess(plan, label) {
38164
+ const env = sanitizedInstallerEnv(process.env);
38165
+ await new Promise((resolveRun, rejectRun) => {
38166
+ const child = plan.shellLine !== void 0 ? spawn9(plan.shellLine, {
38167
+ ...plan.cwd ? { cwd: plan.cwd } : {},
38168
+ env,
38169
+ // stdin stays closed; output reaches the ordinary worker console, so
38170
+ // progress and failure land on the Host console like a runner install.
38171
+ stdio: ["ignore", "inherit", "inherit"],
38172
+ windowsHide: true,
38173
+ shell: true
38174
+ }) : spawn9(plan.command, plan.args, {
38175
+ ...plan.cwd ? { cwd: plan.cwd } : {},
38176
+ env,
38177
+ stdio: ["ignore", "inherit", "inherit"],
38178
+ windowsHide: true
38179
+ });
38180
+ let settled = false;
38181
+ const finish = (error52) => {
38182
+ if (settled) return;
38183
+ settled = true;
38184
+ clearTimeout(timer);
38185
+ if (error52) rejectRun(error52);
38186
+ else resolveRun();
38187
+ };
38188
+ const timer = setTimeout(() => {
38189
+ child.kill();
38190
+ finish(new Error(`${label} did not finish within 10 minutes`));
38191
+ }, PROCESS_TIMEOUT_MS);
38192
+ timer.unref?.();
38193
+ child.once("error", (error52) => finish(error52));
38194
+ child.once("exit", (code, signal) => {
38195
+ if (code === 0) finish();
38196
+ else if (signal) finish(new Error(`${label} stopped with ${signal}`));
38197
+ else finish(new Error(`${label} exited with code ${code ?? "unknown"}`));
38198
+ });
38199
+ });
38200
+ }
38201
+ async function resolveTarCommand(platform) {
38202
+ if (platform !== "win32") return "tar";
38203
+ const root = process.env["SYSTEMROOT"] ?? process.env["WINDIR"];
38204
+ if (!root) return "tar";
38205
+ return await resolveTrustedCliCommand(join17(root, "System32", "tar.exe"), { platform }) ?? "tar";
38206
+ }
38207
+ function archiveKindFor(url3) {
38208
+ let path;
38209
+ try {
38210
+ path = new URL(url3).pathname.toLowerCase();
38211
+ } catch {
38212
+ return "binary";
38213
+ }
38214
+ if (/\.(?:tar\.(?:gz|xz|bz2)|tgz|tbz2|txz|tar)$/.test(path)) return "tar";
38215
+ if (path.endsWith(".zip")) return "zip";
38216
+ return "binary";
38217
+ }
38218
+ async function findExtractedExecutable(root, command, platform = process.platform) {
38219
+ const names = platform === "win32" ? [`${command}.exe`, `${command}.cmd`, `${command}.bat`, command] : [command];
38220
+ let frontier = [root];
38221
+ for (let depth = 0; depth <= MAX_ARCHIVE_SEARCH_DEPTH && frontier.length > 0; depth += 1) {
38222
+ const next = [];
38223
+ for (const directory of frontier) {
38224
+ let entries;
38225
+ try {
38226
+ entries = await readdir5(directory, { withFileTypes: true });
38227
+ } catch {
38228
+ continue;
38229
+ }
38230
+ for (const name of names) {
38231
+ if (entries.some((entry) => entry.name === name && entry.isFile())) {
38232
+ return join17(directory, name);
38233
+ }
38234
+ }
38235
+ for (const entry of entries) {
38236
+ if (entry.isDirectory()) next.push(join17(directory, entry.name));
38237
+ }
38238
+ }
38239
+ frontier = next;
38240
+ }
38241
+ return null;
38242
+ }
38243
+ function withinRoot(root, candidate) {
38244
+ const base = resolvePath(root);
38245
+ const target = resolvePath(candidate);
38246
+ return target === base || target.startsWith(base.endsWith(sep5) ? base : `${base}${sep5}`);
38247
+ }
38248
+ function downloadedBinaryName(command, source, platform = process.platform) {
38249
+ if (platform !== "win32") return command;
38250
+ const extension = /(\.[A-Za-z0-9]{1,8})$/.exec(new URL(source).pathname)?.[1]?.toLowerCase();
38251
+ if (extension !== ".exe" && extension !== ".com") {
38252
+ throw new Error(
38253
+ "a direct Windows download must be a .exe or .com file; use the tool\u2019s .zip release instead"
38254
+ );
38255
+ }
38256
+ return `${command}${extension}`;
38257
+ }
38258
+ async function placeDownloadedPayload(body, request, options) {
38259
+ const platform = options.platform ?? process.platform;
38260
+ const { toolsRoot } = options;
38261
+ const staging = join17(toolsRoot, ".staging", randomUUID11());
38262
+ const stagedPackage = join17(staging, "pkg");
38263
+ const packageRoot = join17(toolsRoot, "pkgs", request.command);
38264
+ const binDirectory = platform === "win32" ? toolsRoot : join17(toolsRoot, "bin");
38265
+ const kind = archiveKindFor(request.source);
38266
+ await mkdir11(stagedPackage, { recursive: true, mode: 448 });
38267
+ try {
38268
+ if (kind === "binary") {
38269
+ await writeFile7(
38270
+ join17(stagedPackage, downloadedBinaryName(request.command, request.source, platform)),
38271
+ body,
38272
+ {
38273
+ mode: 448
38274
+ }
38275
+ );
38276
+ } else {
38277
+ await writeFile7(join17(staging, "archive"), body, { mode: 448 });
38278
+ if (kind === "tar" || platform !== "linux") {
38279
+ await runProcess(
38280
+ {
38281
+ command: await resolveTarCommand(platform),
38282
+ args: ["-xf", "archive", "-C", "pkg"],
38283
+ cwd: staging
38284
+ },
38285
+ "the archive extraction"
38286
+ );
38287
+ } else {
38288
+ await runProcess(
38289
+ { command: "unzip", args: ["-q", "-o", "archive", "-d", "pkg"], cwd: staging },
38290
+ "the archive extraction"
38291
+ );
38292
+ }
38293
+ await rm10(join17(staging, "archive"), { force: true }).catch(() => void 0);
38294
+ }
38295
+ const stagedExecutable = await findExtractedExecutable(
38296
+ stagedPackage,
38297
+ request.command,
38298
+ platform
38299
+ );
38300
+ if (!stagedExecutable) {
38301
+ throw new Error(`the download did not contain an executable named ${request.command}`);
38302
+ }
38303
+ if (!withinRoot(stagedPackage, stagedExecutable)) {
38304
+ throw new Error("the archive tried to place its executable outside the Zixt folder");
38305
+ }
38306
+ await mkdir11(join17(toolsRoot, "pkgs"), { recursive: true, mode: 448 });
38307
+ await rm10(packageRoot, { recursive: true, force: true });
38308
+ await rename6(stagedPackage, packageRoot);
38309
+ const executable = join17(packageRoot, relative8(stagedPackage, stagedExecutable));
38310
+ await mkdir11(binDirectory, { recursive: true, mode: 448 });
38311
+ if (platform === "win32") {
38312
+ const shim = join17(binDirectory, `${request.command}.cmd`);
38313
+ await writeFile7(shim, `@echo off\r
38314
+ ${quoteForCmd(executable)} %*\r
38315
+ `, {
38316
+ encoding: "utf8",
38317
+ mode: 448
38318
+ });
38319
+ return shim;
38320
+ }
38321
+ await chmod6(executable, 493);
38322
+ const link = join17(binDirectory, request.command);
38323
+ await rm10(link, { force: true });
38324
+ await symlink2(executable, link);
38325
+ return link;
38326
+ } finally {
38327
+ await rm10(staging, { recursive: true, force: true }).catch(() => void 0);
38328
+ }
38329
+ }
38330
+ function createSoftwareInstaller(options = {}) {
38331
+ const platform = options.platform ?? process.platform;
38332
+ const toolsRoot = options.toolsRoot ?? defaultSoftwareToolsRoot();
38333
+ const now = options.now ?? Date.now;
38334
+ const manifestPath = join17(toolsRoot, MANIFEST_NAME);
38335
+ const inFlight = /* @__PURE__ */ new Map();
38336
+ const failures = /* @__PURE__ */ new Map();
38337
+ const searchPath = () => {
38338
+ const inherited = options.resolution?.searchPath ?? process.env.PATH ?? "";
38339
+ const separator = platform === "win32" ? ";" : ":";
38340
+ return [inherited, ...softwareSearchPathEntries(toolsRoot)].filter(Boolean).join(separator);
38341
+ };
38342
+ const resolveCommand = async (command) => resolveTrustedCliCommand(command, {
38343
+ ...options.resolution ?? {},
38344
+ platform,
38345
+ searchPath: searchPath()
38346
+ });
38347
+ const readManifest = async () => {
38348
+ try {
38349
+ const parsed = JSON.parse(await readFile10(manifestPath, "utf8"));
38350
+ return Array.isArray(parsed) ? parsed : [];
38351
+ } catch {
38352
+ return [];
38353
+ }
38354
+ };
38355
+ let manifestTurn = Promise.resolve();
38356
+ const recordInstall = (record2) => {
38357
+ const write = manifestTurn.then(async () => {
38358
+ const existing = (await readManifest()).filter((entry) => entry.command !== record2.command);
38359
+ const staged = `${manifestPath}.${randomUUID11()}`;
38360
+ await writeFile7(staged, `${JSON.stringify([...existing, record2], null, 2)}
38361
+ `, {
38362
+ encoding: "utf8",
38363
+ mode: 384
38364
+ });
38365
+ await rename6(staged, manifestPath);
38366
+ });
38367
+ manifestTurn = write.catch(() => void 0);
38368
+ return write;
38369
+ };
38370
+ const installFromNpm = async (request) => {
38371
+ await mkdir11(toolsRoot, { recursive: true, mode: 448 });
38372
+ const npm = await resolveInstallerCommand({ platform });
38373
+ const args = [
38374
+ "install",
38375
+ "-g",
38376
+ "--prefix",
38377
+ toolsRoot,
38378
+ "--no-audit",
38379
+ "--no-fund",
38380
+ "--ignore-scripts",
38381
+ // `--` ends npm's flag parsing, so even a spec that slipped the pattern
38382
+ // could not become an npm option.
38383
+ "--",
38384
+ request.source
38385
+ ];
38386
+ if (platform === "win32") {
38387
+ await runProcess(
38388
+ { shellLine: windowsInstallerCommandLine(npm, args), cwd: toolsRoot },
38389
+ "the npm install"
38390
+ );
38391
+ } else {
38392
+ await runProcess({ command: npm, args, cwd: toolsRoot }, "the npm install");
38393
+ }
38394
+ };
38395
+ const installFromDownload = async (request) => {
38396
+ const response = await fetch(request.source, {
38397
+ redirect: "follow",
38398
+ signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS)
38399
+ });
38400
+ if (!response.ok) throw new Error(`the download answered HTTP ${response.status}`);
38401
+ if (response.url && !response.url.startsWith("https:")) {
38402
+ throw new Error("the download redirected off HTTPS and was not installed");
38403
+ }
38404
+ const declared = Number(response.headers.get("content-length") ?? "0");
38405
+ if (Number.isFinite(declared) && declared > MAX_DOWNLOAD_BYTES) {
38406
+ throw new Error("the download is larger than Zixt installs unattended");
38407
+ }
38408
+ if (!response.body) throw new Error("the download had no body");
38409
+ const reader = response.body.getReader();
38410
+ const chunks = [];
38411
+ let total = 0;
38412
+ for (; ; ) {
38413
+ const { done, value } = await reader.read();
38414
+ if (done) break;
38415
+ total += value.byteLength;
38416
+ if (total > MAX_DOWNLOAD_BYTES) {
38417
+ await reader.cancel().catch(() => void 0);
38418
+ throw new Error("the download is larger than Zixt installs unattended");
38419
+ }
38420
+ chunks.push(Buffer.from(value));
38421
+ }
38422
+ const body = Buffer.concat(chunks);
38423
+ if (body.length === 0) throw new Error("the download was empty");
38424
+ const digest = createHash6("sha256").update(body).digest("hex");
38425
+ if (request.sha256 && digest !== request.sha256.toLowerCase()) {
38426
+ throw new Error("the download did not match the expected sha256");
38427
+ }
38428
+ await placeDownloadedPayload(body, request, { toolsRoot, platform });
38429
+ };
38430
+ const perform = options.perform ?? (async (request) => {
38431
+ if (request.method === "npm") await installFromNpm(request);
38432
+ else await installFromDownload(request);
38433
+ const resolved = await resolveCommand(request.command);
38434
+ if (!resolved) {
38435
+ throw new Error(
38436
+ `the install finished but ${request.command} is still not runnable on this Machine`
38437
+ );
38438
+ }
38439
+ return resolved;
38440
+ });
38441
+ const attempt = async (request) => {
38442
+ options.onEvent?.({ command: request.command, state: "started" });
38443
+ try {
38444
+ const path = await perform(request, toolsRoot);
38445
+ if (!withinRoot(toolsRoot, path)) {
38446
+ throw new Error(
38447
+ `${request.command} resolves outside the Zixt folder and was not installed by Zixt`
38448
+ );
38449
+ }
38450
+ const record2 = {
38451
+ command: request.command,
38452
+ method: request.method,
38453
+ source: request.source,
38454
+ path,
38455
+ sha256: request.sha256 ? request.sha256.toLowerCase() : null,
38456
+ installedAt: new Date(now()).toISOString()
38457
+ };
38458
+ await recordInstall(record2).catch(() => void 0);
38459
+ options.onEvent?.({ command: request.command, state: "completed" });
38460
+ return { ok: true, record: record2 };
38461
+ } catch (error52) {
38462
+ const message = error52 instanceof Error ? error52.message : "unknown software install error";
38463
+ options.onEvent?.({ command: request.command, state: "failed", error: message });
38464
+ return { ok: false, error: message };
38465
+ }
38466
+ };
38467
+ return {
38468
+ toolsRoot,
38469
+ async probe(command) {
38470
+ if (!COMMAND_PATTERN.test(command)) {
38471
+ return { command, present: false, path: null, managed: false };
38472
+ }
38473
+ const path = await resolveCommand(command);
38474
+ return {
38475
+ command,
38476
+ present: path !== null,
38477
+ path,
38478
+ managed: path !== null && withinRoot(toolsRoot, path)
38479
+ };
38480
+ },
38481
+ async install(request) {
38482
+ const refusal = refuseSoftwareRequest(request);
38483
+ if (refusal) return { ok: false, error: refusal };
38484
+ const key = [
38485
+ request.command,
38486
+ request.method,
38487
+ request.source,
38488
+ request.sha256?.toLowerCase() ?? ""
38489
+ ].join("\n");
38490
+ const running = inFlight.get(key);
38491
+ if (running) return running;
38492
+ const failure2 = failures.get(key);
38493
+ if (failure2 && now() - failure2.at < FAILURE_COOLDOWN_MS2) {
38494
+ return {
38495
+ ok: false,
38496
+ error: `This exact install already failed on this Machine: ${failure2.error}. Do not repeat it. Finish the work another way, or tell the person what is missing and the command they would run.`
38497
+ };
38498
+ }
38499
+ const started = (async () => {
38500
+ const result = await attempt(request);
38501
+ if (result.ok) failures.delete(key);
38502
+ else failures.set(key, { at: now(), error: result.error });
38503
+ return result;
38504
+ })();
38505
+ inFlight.set(key, started);
38506
+ try {
38507
+ return await started;
38508
+ } finally {
38509
+ inFlight.delete(key);
38510
+ }
38511
+ },
38512
+ async installed() {
38513
+ return readManifest();
38514
+ }
38515
+ };
38516
+ }
38517
+
38518
+ // src/runners/ask-user-server.ts
38009
38519
  var CADENCE_PROPS = {
38010
38520
  cadence_kind: {
38011
38521
  type: "string",
@@ -38121,6 +38631,55 @@ var TOOLS = [
38121
38631
  additionalProperties: false
38122
38632
  }
38123
38633
  },
38634
+ {
38635
+ name: "check_software",
38636
+ description: "Check whether a command is already available on this Machine before assuming it is missing. Returns whether it is present, where it resolved, and whether Zixt is the one that installed it.",
38637
+ inputSchema: {
38638
+ type: "object",
38639
+ properties: {
38640
+ command: {
38641
+ type: "string",
38642
+ description: "The plain command name, for example ffmpeg or gh. No path, no arguments."
38643
+ }
38644
+ },
38645
+ required: ["command"],
38646
+ additionalProperties: false
38647
+ }
38648
+ },
38649
+ {
38650
+ name: "install_software",
38651
+ description: "Install a command this Machine is missing, into the folder Zixt owns, after the person approves it. Prefer not to call this. Installing software on someone else\u2019s computer is friction, so first try to finish the work with what the Machine already has: a different tool, a project dependency, or another approach. Call it only when the missing command genuinely blocks the work or is clearly the right way to do it, and never speculatively. Zixt can install an npm package, or download and unpack a release from an HTTPS URL. It cannot use a system package manager such as apt, brew, winget, or choco, cannot ask for elevation or sudo, and cannot build from source. When the tool needs any of those, or the install looks complicated, do not call this: tell the person what is missing, why this work needs it, and the exact command they would run on their operating system. The person sees your reason and the exact source before anything runs, and may say no.",
38652
+ inputSchema: {
38653
+ type: "object",
38654
+ properties: {
38655
+ command: {
38656
+ type: "string",
38657
+ description: "The plain command name the work needs on PATH, for example ffmpeg."
38658
+ },
38659
+ reason: {
38660
+ type: "string",
38661
+ minLength: 1,
38662
+ maxLength: 500,
38663
+ description: "Why this work needs it, in one plain sentence. The person reads this before deciding."
38664
+ },
38665
+ method: {
38666
+ type: "string",
38667
+ enum: [...SOFTWARE_INSTALL_METHODS],
38668
+ description: "npm for an npm package; download for a release archive or binary."
38669
+ },
38670
+ source: {
38671
+ type: "string",
38672
+ description: "npm: the package name, optionally name@version with an exact version. download: the HTTPS URL of the release archive or binary."
38673
+ },
38674
+ sha256: {
38675
+ type: "string",
38676
+ description: "download only: the published SHA-256 of that file, when the project publishes one. Include it whenever you can."
38677
+ }
38678
+ },
38679
+ required: ["command", "reason", "method", "source"],
38680
+ additionalProperties: false
38681
+ }
38682
+ },
38124
38683
  {
38125
38684
  name: "publish_file",
38126
38685
  description: "Snapshot a file you created in this Task workspace into Zixt so the user can download it. This only creates the Zixt file; it does not send it anywhere externally. The result returns an artifact_id.",
@@ -38906,6 +39465,57 @@ function createAskUserServer() {
38906
39465
  }
38907
39466
  return;
38908
39467
  }
39468
+ if (surface.platform && (name === "check_software" || name === "install_software")) {
39469
+ if (!handlers.software) {
39470
+ toolText("Software checks are unavailable for this runner.", true);
39471
+ return;
39472
+ }
39473
+ const command = typeof args["command"] === "string" ? args["command"].trim() : "";
39474
+ if (!command) {
39475
+ toolText("missing required argument `command`", true);
39476
+ return;
39477
+ }
39478
+ try {
39479
+ if (name === "check_software") {
39480
+ toolText(JSON.stringify(await handlers.software.check(command), null, 2));
39481
+ return;
39482
+ }
39483
+ const reason = typeof args["reason"] === "string" ? args["reason"].trim() : "";
39484
+ const method = typeof args["method"] === "string" ? args["method"] : "";
39485
+ const source = typeof args["source"] === "string" ? args["source"].trim() : "";
39486
+ const sha256 = typeof args["sha256"] === "string" && args["sha256"] ? args["sha256"] : void 0;
39487
+ if (!reason) {
39488
+ toolText(
39489
+ "missing required argument `reason`: the person decides by reading why this work needs it",
39490
+ true
39491
+ );
39492
+ return;
39493
+ }
39494
+ if (!SOFTWARE_INSTALL_METHODS.includes(method)) {
39495
+ toolText(`\`method\` must be one of ${SOFTWARE_INSTALL_METHODS.join(", ")}.`, true);
39496
+ return;
39497
+ }
39498
+ const request = {
39499
+ command,
39500
+ method,
39501
+ source,
39502
+ sha256
39503
+ };
39504
+ const refusal = refuseSoftwareRequest(request);
39505
+ if (refusal) {
39506
+ toolText(refusal, true);
39507
+ return;
39508
+ }
39509
+ const outcome = await handlers.software.install({ ...request, reason });
39510
+ toolText(outcome.detail, !outcome.ok);
39511
+ } catch (err) {
39512
+ toolText(
39513
+ `The software request could not be completed: ${String(err instanceof Error ? err.message : err)}`,
39514
+ true
39515
+ );
39516
+ }
39517
+ return;
39518
+ }
38909
39519
  if (surface.platform && name === "publish_file") {
38910
39520
  if (!handlers.publishFile) {
38911
39521
  toolText("file publishing is unavailable for this runner", true);
@@ -39021,6 +39631,7 @@ function createAskUserServer() {
39021
39631
  agentOp: input.agentOp,
39022
39632
  ..."requestApproval" in input && input.requestApproval ? { requestApproval: input.requestApproval } : {},
39023
39633
  ..."publishFile" in input && input.publishFile ? { publishFile: input.publishFile } : {},
39634
+ ..."software" in input && input.software ? { software: input.software } : {},
39024
39635
  ...toolPacks.length > 0 ? { toolPacks } : {}
39025
39636
  };
39026
39637
  const toolOwners = /* @__PURE__ */ new Map();
@@ -39173,7 +39784,10 @@ function buildRunnerEnv(input) {
39173
39784
  env[name] = value;
39174
39785
  }
39175
39786
  }
39176
- const searchPath = sanitizeInheritedSearchPath(inheritedValue(input.inherited, "PATH"));
39787
+ const searchPath = [
39788
+ sanitizeInheritedSearchPath(inheritedValue(input.inherited, "PATH")),
39789
+ ...(input.softwareToolsPath ?? []).filter((entry) => isAbsolute14(entry))
39790
+ ].filter((entry) => entry !== "").join(delimiter2);
39177
39791
  const gitConfig = input.githubShell ? [
39178
39792
  ["credential.helper", ""],
39179
39793
  ["credential.helper", input.githubShell.gitCredentialHelper],
@@ -39240,9 +39854,9 @@ function buildRunnerEnv(input) {
39240
39854
  // src/runners/github-shell-auth.ts
39241
39855
  import { execFile } from "node:child_process";
39242
39856
  import { randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
39243
- import { chmod as chmod6, lstat as lstat10, mkdir as mkdir11, realpath as realpath7, writeFile as writeFile7 } from "node:fs/promises";
39857
+ import { chmod as chmod7, lstat as lstat10, mkdir as mkdir12, realpath as realpath7, writeFile as writeFile8 } from "node:fs/promises";
39244
39858
  import { createServer as createServer3 } from "node:http";
39245
- import { isAbsolute as isAbsolute15, join as join17, relative as relative8 } from "node:path";
39859
+ import { isAbsolute as isAbsolute15, join as join18, relative as relative9 } from "node:path";
39246
39860
  var MAX_REQUEST_BYTES2 = 16 * 1024;
39247
39861
  var DIRECTORY_MODE4 = 448;
39248
39862
  var PRIVATE_FILE_MODE = 384;
@@ -39606,7 +40220,7 @@ function activationCredential(grant, now = Date.now()) {
39606
40220
  return expiresAt.getTime() <= now ? null : { accessToken: grant.accessToken, expiresAt };
39607
40221
  }
39608
40222
  function assertChildPath2(parent, child) {
39609
- const path = relative8(parent, child);
40223
+ const path = relative9(parent, child);
39610
40224
  if (!path || path === ".." || path.startsWith("../") || path.startsWith("..\\") || isAbsolute15(path)) {
39611
40225
  throw new Error("GitHub shell helper path escaped its private run directory");
39612
40226
  }
@@ -39618,11 +40232,11 @@ function quoteForPosixShell(value) {
39618
40232
  return quoteForGitShell2(value);
39619
40233
  }
39620
40234
  async function writePrivate(path, content, executable = false) {
39621
- await writeFile7(path, content, {
40235
+ await writeFile8(path, content, {
39622
40236
  flag: "wx",
39623
40237
  mode: executable ? EXECUTABLE_FILE_MODE : PRIVATE_FILE_MODE
39624
40238
  });
39625
- await chmod6(path, executable ? EXECUTABLE_FILE_MODE : PRIVATE_FILE_MODE);
40239
+ await chmod7(path, executable ? EXECUTABLE_FILE_MODE : PRIVATE_FILE_MODE);
39626
40240
  }
39627
40241
  async function prepareHelpers(input) {
39628
40242
  const rootEntry = await lstat10(input.runRoot);
@@ -39630,7 +40244,7 @@ async function prepareHelpers(input) {
39630
40244
  throw new Error("GitHub shell authentication requires a private real run directory");
39631
40245
  }
39632
40246
  const runRoot = await realpath7(input.runRoot);
39633
- const helperPath = join17(runRoot, "github-shell-git-credential.cjs");
40247
+ const helperPath = join18(runRoot, "github-shell-git-credential.cjs");
39634
40248
  assertChildPath2(runRoot, helperPath);
39635
40249
  await writePrivate(helperPath, GIT_HELPER_SOURCE);
39636
40250
  if (!input.ghExecutablePath) {
@@ -39641,14 +40255,14 @@ async function prepareHelpers(input) {
39641
40255
  wrapperSourcePath: null
39642
40256
  };
39643
40257
  }
39644
- const shellToolsDirectory = join17(runRoot, "shell-tools");
40258
+ const shellToolsDirectory = join18(runRoot, "shell-tools");
39645
40259
  assertChildPath2(runRoot, shellToolsDirectory);
39646
- await mkdir11(shellToolsDirectory, { mode: DIRECTORY_MODE4 });
39647
- await chmod6(shellToolsDirectory, DIRECTORY_MODE4);
39648
- const wrapperSourcePath = join17(runRoot, "github-shell-gh-wrapper.cjs");
40260
+ await mkdir12(shellToolsDirectory, { mode: DIRECTORY_MODE4 });
40261
+ await chmod7(shellToolsDirectory, DIRECTORY_MODE4);
40262
+ const wrapperSourcePath = join18(runRoot, "github-shell-gh-wrapper.cjs");
39649
40263
  assertChildPath2(runRoot, wrapperSourcePath);
39650
40264
  await writePrivate(wrapperSourcePath, GH_WRAPPER_SOURCE);
39651
- const wrapperPath = join17(shellToolsDirectory, process.platform === "win32" ? "gh.cmd" : "gh");
40265
+ const wrapperPath = join18(shellToolsDirectory, process.platform === "win32" ? "gh.cmd" : "gh");
39652
40266
  assertChildPath2(runRoot, wrapperPath);
39653
40267
  const launcher = process.platform === "win32" ? `@"${process.execPath.replaceAll('"', '""')}" "${wrapperSourcePath.replaceAll('"', '""')}" %*\r
39654
40268
  ` : `#!/bin/sh
@@ -39871,7 +40485,7 @@ password=${credential.accessToken}
39871
40485
  }
39872
40486
 
39873
40487
  // src/runners/working-context.ts
39874
- import { spawn as spawn9 } from "node:child_process";
40488
+ import { spawn as spawn10 } from "node:child_process";
39875
40489
  import { resolve as resolve9 } from "node:path";
39876
40490
  var COMMAND_TIMEOUT_MS = 5e3;
39877
40491
  var OUTPUT_LIMIT_BYTES = 128 * 1024;
@@ -40002,7 +40616,7 @@ async function stopWorkingContextCommand(child, childExited, options = {}) {
40002
40616
  function run(command, args, cwd, env, signal) {
40003
40617
  if (signal?.aborted) return Promise.resolve(null);
40004
40618
  return new Promise((resolvePromise) => {
40005
- const child = spawn9(command, [...args], {
40619
+ const child = spawn10(command, [...args], {
40006
40620
  cwd,
40007
40621
  env: { ...env, GIT_OPTIONAL_LOCKS: "0" },
40008
40622
  detached: process.platform !== "win32",
@@ -40420,6 +41034,7 @@ var WorkingContextPullRequestCache = class {
40420
41034
  };
40421
41035
 
40422
41036
  // src/runners/cli-runner.ts
41037
+ var softwareInstaller = createSoftwareInstaller();
40423
41038
  var PRIVATE_CLEANUP_FAILURE = "Private runner cleanup could not be completed. Restart the Zixt Host before accepting more work.";
40424
41039
  var PROCESS_CLEANUP_FAILURE = "Runner process cleanup could not be confirmed. Restart the Zixt Host before accepting more work.";
40425
41040
  var PROVIDER_CLEANUP_FAILURE = "Provider tool cleanup could not be completed. Restart the Zixt Host before accepting more work.";
@@ -40442,7 +41057,7 @@ async function settlesWithin(promise2, timeoutMs) {
40442
41057
  }
40443
41058
  }
40444
41059
  function defaultRunnerWorkspaceRoot() {
40445
- return join18(homedir7(), ".zixt", "workspaces");
41060
+ return join19(homedir7(), ".zixt", "workspaces");
40446
41061
  }
40447
41062
  function defaultRunnerArtifactRoot() {
40448
41063
  return defaultRunArtifactRoot();
@@ -40491,7 +41106,7 @@ function createCliRunner(adapter, opts = {}) {
40491
41106
  const prefixArgs = opts.commandPrefixArgs ?? [];
40492
41107
  const maxWallTimeMs = opts.maxWallTimeMs;
40493
41108
  const workspaceRoot = opts.workspaceRoot ?? defaultRunnerWorkspaceRoot();
40494
- const artifactRoot = opts.artifactRoot ?? (opts.workspaceRoot === void 0 ? defaultRunnerArtifactRoot() : join18(dirname10(workspaceRoot), "run-artifacts"));
41109
+ const artifactRoot = opts.artifactRoot ?? (opts.workspaceRoot === void 0 ? defaultRunnerArtifactRoot() : join19(dirname10(workspaceRoot), "run-artifacts"));
40495
41110
  const runRegistryRoot2 = opts.runRegistryRoot ?? defaultRunRegistryRoot();
40496
41111
  const createArtifacts = opts.createArtifacts ?? createRunArtifacts;
40497
41112
  const toolPackRegistry2 = opts.toolPackRegistry ?? createDefaultToolPackRegistry();
@@ -40509,7 +41124,7 @@ function createCliRunner(adapter, opts = {}) {
40509
41124
  };
40510
41125
  const askUserServer = createAskUserServer();
40511
41126
  const windowsRoot = process.env.SystemRoot ?? process.env.WINDIR;
40512
- const windowsComspecCandidate = process.platform === "win32" && windowsRoot ? join18(windowsRoot, "System32", "cmd.exe") : void 0;
41127
+ const windowsComspecCandidate = process.platform === "win32" && windowsRoot ? join19(windowsRoot, "System32", "cmd.exe") : void 0;
40513
41128
  let safetyFailure;
40514
41129
  return async (task) => {
40515
41130
  if (safetyFailure) {
@@ -40549,8 +41164,8 @@ function createCliRunner(adapter, opts = {}) {
40549
41164
  usage: { inputTokens: 0, outputTokens: 0 }
40550
41165
  };
40551
41166
  }
40552
- const taskRoot = join18(workspaceRoot, task.agentId);
40553
- await mkdir12(taskRoot, { recursive: true });
41167
+ const taskRoot = join19(workspaceRoot, task.agentId);
41168
+ await mkdir13(taskRoot, { recursive: true });
40554
41169
  if (task.cancelledNow()) return cancelledBeforeRun();
40555
41170
  const configuredWorkspace = task.spec.workspace;
40556
41171
  let cwd = taskRoot;
@@ -40597,7 +41212,7 @@ function createCliRunner(adapter, opts = {}) {
40597
41212
  summaryEvidence: "host_observed"
40598
41213
  };
40599
41214
  }
40600
- const runToken = randomUUID11();
41215
+ const runToken = randomUUID12();
40601
41216
  const artifacts = await createArtifacts({
40602
41217
  root: artifactRoot,
40603
41218
  agentId: task.agentId,
@@ -40653,7 +41268,8 @@ function createCliRunner(adapter, opts = {}) {
40653
41268
  runner: { type: adapter.type, auth: runner.auth },
40654
41269
  gitIdentity: githubCommitIdentity(providerGrants),
40655
41270
  isolation: artifacts,
40656
- githubShell
41271
+ githubShell,
41272
+ softwareToolsPath: softwareSearchPathEntries(softwareInstaller.toolsRoot)
40657
41273
  });
40658
41274
  toolPacks = await toolPackRegistry2.createInstances(providerGrants, {
40659
41275
  taskId: task.taskId,
@@ -40746,6 +41362,51 @@ function createCliRunner(adapter, opts = {}) {
40746
41362
  }
40747
41363
  },
40748
41364
  publishFile,
41365
+ // AG-4a. The person approves in the Task thread through the ordinary
41366
+ // approvals pipeline, so the wall clock pauses while they decide, and
41367
+ // a refusal is an answer the session can act on rather than a failure.
41368
+ software: {
41369
+ check: (command2) => softwareInstaller.probe(command2),
41370
+ install: async ({ reason, ...request }) => {
41371
+ pendingAsks++;
41372
+ let decision;
41373
+ try {
41374
+ decision = await task.requestApproval(
41375
+ "agent.question",
41376
+ `Install ${request.command} on this Machine: ${reason}`.slice(0, 500),
41377
+ JSON.stringify({
41378
+ command: request.command,
41379
+ reason,
41380
+ method: request.method,
41381
+ source: request.source,
41382
+ ...request.sha256 ? { sha256: request.sha256 } : {},
41383
+ folder: softwareInstaller.toolsRoot
41384
+ }).slice(0, MAX_APPROVAL_PAYLOAD)
41385
+ );
41386
+ } finally {
41387
+ pendingAsks--;
41388
+ }
41389
+ if (!decision.approved) {
41390
+ return {
41391
+ ok: false,
41392
+ detail: decision.guidance ?? `The person did not approve installing ${request.command}. Do not ask again for this task. Finish the work another way, or explain what is blocked.`
41393
+ };
41394
+ }
41395
+ task.event("status", `Installing ${request.command} on this Machine`, {
41396
+ tool: "install_software"
41397
+ });
41398
+ const result2 = await softwareInstaller.install(request);
41399
+ task.event(
41400
+ "status",
41401
+ result2.ok ? `Installed ${request.command} on this Machine` : `Could not install ${request.command} on this Machine`,
41402
+ { tool: "install_software" }
41403
+ );
41404
+ return result2.ok ? {
41405
+ ok: true,
41406
+ detail: `${request.command} is installed at ${result2.record.path} and is on your PATH. Continue the work.`
41407
+ } : { ok: false, detail: result2.error };
41408
+ }
41409
+ },
40749
41410
  agentOp: (op) => task.agentOp(op),
40750
41411
  // GitHub repository work belongs in the installed `git` and `gh`
40751
41412
  // commands backed by GithubShellAuth. Do not advertise the bundled
@@ -40970,8 +41631,8 @@ ${attachmentSection}` : prompt;
40970
41631
  }
40971
41632
  comspec = resolvedWindowsComspec;
40972
41633
  }
40973
- const exitMarker = `__ZIXT_RUNNER_EXIT_${randomUUID11()}__`;
40974
- const guardianNonce = randomUUID11();
41634
+ const exitMarker = `__ZIXT_RUNNER_EXIT_${randomUUID12()}__`;
41635
+ const guardianNonce = randomUUID12();
40975
41636
  return runCliProcess({
40976
41637
  command: resolvedCommand,
40977
41638
  args,
@@ -41328,8 +41989,8 @@ function runCliProcess(options) {
41328
41989
  }
41329
41990
  return new Promise((resolve19) => {
41330
41991
  const platform = options.platform ?? process.platform;
41331
- const containmentGateNonce = options.guardian && platform === "win32" ? randomUUID11() : void 0;
41332
- const child = options.guardian ? spawn10(
41992
+ const containmentGateNonce = options.guardian && platform === "win32" ? randomUUID12() : void 0;
41993
+ const child = options.guardian ? spawn11(
41333
41994
  options.guardian.nodeCommand,
41334
41995
  [
41335
41996
  options.guardian.scriptPath,
@@ -41617,12 +42278,12 @@ function runCliProcess(options) {
41617
42278
  }
41618
42279
 
41619
42280
  // src/runners/claude-code.ts
41620
- import { randomUUID as randomUUID12 } from "node:crypto";
42281
+ import { randomUUID as randomUUID13 } from "node:crypto";
41621
42282
 
41622
42283
  // src/runners/runtime-observation.ts
41623
- import { open as open6, readdir as readdir5, realpath as realpath9 } from "node:fs/promises";
42284
+ import { open as open6, readdir as readdir6, realpath as realpath9 } from "node:fs/promises";
41624
42285
  import { homedir as homedir8 } from "node:os";
41625
- import { join as join19 } from "node:path";
42286
+ import { join as join20 } from "node:path";
41626
42287
  var READ_WINDOW_BYTES = 1024 * 1024;
41627
42288
  var CATALOG_TIMEOUT_MS = 15e3;
41628
42289
  var CATALOG_OUTPUT_LIMIT_BYTES = 4 * 1024 * 1024;
@@ -41682,9 +42343,9 @@ function displayValue(value, maxLength) {
41682
42343
  return trimmed;
41683
42344
  }
41684
42345
  function claudeTranscriptPath(input) {
41685
- const configDir = input.env["CLAUDE_CONFIG_DIR"] || join19(homeFrom(input.env), ".claude");
42346
+ const configDir = input.env["CLAUDE_CONFIG_DIR"] || join20(homeFrom(input.env), ".claude");
41686
42347
  const slug = input.resolvedCwd.replace(/[^a-zA-Z0-9]/g, "-");
41687
- return join19(configDir, "projects", slug, `${input.sessionId}.jsonl`);
42348
+ return join20(configDir, "projects", slug, `${input.sessionId}.jsonl`);
41688
42349
  }
41689
42350
  async function readClaudeSessionEffort(input) {
41690
42351
  const resolvedCwd = await realpath9(input.cwd).catch(() => input.cwd);
@@ -41699,19 +42360,19 @@ async function readClaudeSessionEffort(input) {
41699
42360
  return null;
41700
42361
  }
41701
42362
  async function newestDirectories(root, limit) {
41702
- const entries = await readdir5(root, { withFileTypes: true }).catch(() => []);
41703
- return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort((left, right) => right.localeCompare(left)).slice(0, limit).map((name) => join19(root, name));
42363
+ const entries = await readdir6(root, { withFileTypes: true }).catch(() => []);
42364
+ return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort((left, right) => right.localeCompare(left)).slice(0, limit).map((name) => join20(root, name));
41704
42365
  }
41705
42366
  async function findCodexRolloutPath(input) {
41706
- const codexHome = input.env["CODEX_HOME"] || join19(homeFrom(input.env), ".codex");
41707
- const sessions = join19(codexHome, "sessions");
42367
+ const codexHome = input.env["CODEX_HOME"] || join20(homeFrom(input.env), ".codex");
42368
+ const sessions = join20(codexHome, "sessions");
41708
42369
  const suffix = `-${input.threadId}.jsonl`;
41709
42370
  for (const year of await newestDirectories(sessions, 2)) {
41710
42371
  for (const month of await newestDirectories(year, 2)) {
41711
42372
  for (const day of await newestDirectories(month, 3)) {
41712
- const files = await readdir5(day).catch(() => []);
42373
+ const files = await readdir6(day).catch(() => []);
41713
42374
  const match = files.find((name) => name.endsWith(suffix));
41714
- if (match) return join19(day, match);
42375
+ if (match) return join20(day, match);
41715
42376
  }
41716
42377
  }
41717
42378
  }
@@ -41809,7 +42470,7 @@ var claudeCodeAdapter = {
41809
42470
  input.gitDetected,
41810
42471
  liveInput
41811
42472
  );
41812
- const sessionId = input.task.spec.sessionKey ?? randomUUID12();
42473
+ const sessionId = input.task.spec.sessionKey ?? randomUUID13();
41813
42474
  const observeRuntime = createRuntimeReporter(input, sessionId);
41814
42475
  return {
41815
42476
  argsFor: (mode) => [
@@ -41938,7 +42599,7 @@ function createClaudeLiveParser(onStream, onSessionModel) {
41938
42599
  ...parser,
41939
42600
  async start(writer, prompt) {
41940
42601
  write = writer;
41941
- await writer(input(randomUUID12(), prompt));
42602
+ await writer(input(randomUUID13(), prompt));
41942
42603
  },
41943
42604
  async steer(followUp) {
41944
42605
  if (!write) return false;
@@ -42105,22 +42766,22 @@ function improveErrorMessage(error52) {
42105
42766
  }
42106
42767
 
42107
42768
  // src/runners/codex.ts
42108
- import { mkdir as mkdir13, readFile as readFile10, writeFile as writeFile8 } from "node:fs/promises";
42109
- import { randomUUID as randomUUID13 } from "node:crypto";
42769
+ import { mkdir as mkdir14, readFile as readFile11, writeFile as writeFile9 } from "node:fs/promises";
42770
+ import { randomUUID as randomUUID14 } from "node:crypto";
42110
42771
  import { homedir as homedir9 } from "node:os";
42111
- import { join as join20 } from "node:path";
42772
+ import { join as join21 } from "node:path";
42112
42773
  var CODEX_NOT_FOUND_MESSAGE = "The `codex` CLI was not found on this Machine. Install it (npm install -g @openai/codex) and sign in with `codex login`, or switch the agent to API-key auth.";
42113
42774
  function defaultCodexThreadIndexRoot() {
42114
- return join20(homedir9(), ".zixt", "codex-threads");
42775
+ return join21(homedir9(), ".zixt", "codex-threads");
42115
42776
  }
42116
42777
  var SAFE_SEGMENT3 = /^[A-Za-z0-9_-]{1,200}$/;
42117
42778
  function threadIndexPath(root, agentId, sessionKey) {
42118
42779
  if (!SAFE_SEGMENT3.test(agentId) || !SAFE_SEGMENT3.test(sessionKey)) return null;
42119
- return join20(root, agentId, `${sessionKey}.json`);
42780
+ return join21(root, agentId, `${sessionKey}.json`);
42120
42781
  }
42121
42782
  async function readThreadId(path) {
42122
42783
  try {
42123
- const parsed = JSON.parse(await readFile10(path, "utf8"));
42784
+ const parsed = JSON.parse(await readFile11(path, "utf8"));
42124
42785
  return typeof parsed.threadId === "string" && /^[A-Za-z0-9-]{1,120}$/.test(parsed.threadId) ? parsed.threadId : null;
42125
42786
  } catch {
42126
42787
  return null;
@@ -42220,7 +42881,7 @@ ${value}` : value;
42220
42881
  const recordedThreadId = indexPath ? await readThreadId(indexPath) : null;
42221
42882
  const rememberThread = (threadId) => {
42222
42883
  if (!indexPath) return;
42223
- void mkdir13(join20(threadIndexRoot, task.agentId), { recursive: true }).then(() => writeFile8(indexPath, JSON.stringify({ threadId }), "utf8")).catch(() => {
42884
+ void mkdir14(join21(threadIndexRoot, task.agentId), { recursive: true }).then(() => writeFile9(indexPath, JSON.stringify({ threadId }), "utf8")).catch(() => {
42224
42885
  });
42225
42886
  };
42226
42887
  const observeRuntime = (threadId) => {
@@ -42324,7 +42985,7 @@ function createCodexAppServerParser(onStream, options) {
42324
42985
  params: {
42325
42986
  threadId,
42326
42987
  input: [{ type: "text", text: prompt }],
42327
- clientUserMessageId: randomUUID13(),
42988
+ clientUserMessageId: randomUUID14(),
42328
42989
  ...options.model ? { model: options.model } : {},
42329
42990
  ...options.effort ? { effort: options.effort } : {}
42330
42991
  }
@@ -42683,7 +43344,7 @@ function improveCodexErrorMessage(error52) {
42683
43344
  }
42684
43345
 
42685
43346
  // src/runners/git-preflight.ts
42686
- import { spawn as spawn11 } from "node:child_process";
43347
+ import { spawn as spawn12 } from "node:child_process";
42687
43348
  import { realpath as realpath10 } from "node:fs/promises";
42688
43349
  import { isAbsolute as isAbsolute17, resolve as resolve11 } from "node:path";
42689
43350
  var OUTPUT_LIMIT = 8192;
@@ -42732,7 +43393,7 @@ async function preflightGit(options = {}) {
42732
43393
  }
42733
43394
  async function runVersionProbe(input) {
42734
43395
  return new Promise((resolvePromise) => {
42735
- const child = spawn11(input.executablePath, input.args, {
43396
+ const child = spawn12(input.executablePath, input.args, {
42736
43397
  cwd: input.cwd,
42737
43398
  env: {
42738
43399
  ...process.env.SystemRoot ? { SystemRoot: process.env.SystemRoot } : {},
@@ -42954,16 +43615,16 @@ function run2(command, args) {
42954
43615
  }
42955
43616
 
42956
43617
  // src/linux-service.ts
42957
- import { spawn as spawn12 } from "node:child_process";
43618
+ import { spawn as spawn13 } from "node:child_process";
42958
43619
  import { constants as constants2 } from "node:fs";
42959
- import { access as access5, chmod as chmod8, mkdir as mkdir15, open as open7, rename as rename7, rm as rm11 } from "node:fs/promises";
43620
+ import { access as access5, chmod as chmod9, mkdir as mkdir16, open as open7, rename as rename8, rm as rm12 } from "node:fs/promises";
42960
43621
  import { homedir as homedir11, userInfo } from "node:os";
42961
- import { basename as basename4, dirname as dirname11, join as join22, relative as relative9, resolve as resolve13, sep as sep6 } from "node:path";
43622
+ import { basename as basename4, dirname as dirname11, join as join23, relative as relative10, resolve as resolve13, sep as sep7 } from "node:path";
42962
43623
 
42963
43624
  // src/service-runtime.ts
42964
- import { access as access4, chmod as chmod7, copyFile, mkdir as mkdir14, rename as rename6, rm as rm10 } from "node:fs/promises";
43625
+ import { access as access4, chmod as chmod8, copyFile, mkdir as mkdir15, rename as rename7, rm as rm11 } from "node:fs/promises";
42965
43626
  import { homedir as homedir10 } from "node:os";
42966
- import { join as join21, resolve as resolve12, sep as sep5 } from "node:path";
43627
+ import { join as join22, resolve as resolve12, sep as sep6 } from "node:path";
42967
43628
  async function ensureDurableServiceNode(options = {}) {
42968
43629
  const execPath = resolve12(options.execPath ?? process.execPath);
42969
43630
  const home = options.home ?? homedir10();
@@ -42973,22 +43634,22 @@ async function ensureDurableServiceNode(options = {}) {
42973
43634
  throw new Error("the Node runtime version is not a safe directory name");
42974
43635
  }
42975
43636
  const zixtRoot = resolve12(home, ".zixt");
42976
- if (execPath === zixtRoot || execPath.startsWith(zixtRoot + sep5)) return execPath;
42977
- const directory = join21(zixtRoot, "runtime", `node-${version2}`);
42978
- const destination = join21(directory, platform === "win32" ? "node.exe" : "node");
43637
+ if (execPath === zixtRoot || execPath.startsWith(zixtRoot + sep6)) return execPath;
43638
+ const directory = join22(zixtRoot, "runtime", `node-${version2}`);
43639
+ const destination = join22(directory, platform === "win32" ? "node.exe" : "node");
42979
43640
  const alreadyCopied = await access4(destination).then(
42980
43641
  () => true,
42981
43642
  () => false
42982
43643
  );
42983
43644
  if (alreadyCopied) return destination;
42984
- await mkdir14(directory, { recursive: true, mode: 448 });
43645
+ await mkdir15(directory, { recursive: true, mode: 448 });
42985
43646
  const temporary = `${destination}.${process.pid}.${crypto.randomUUID()}.tmp`;
42986
43647
  try {
42987
43648
  await copyFile(execPath, temporary);
42988
- await chmod7(temporary, 493);
42989
- await rename6(temporary, destination);
43649
+ await chmod8(temporary, 493);
43650
+ await rename7(temporary, destination);
42990
43651
  } catch (error52) {
42991
- await rm10(temporary, { force: true }).catch(() => void 0);
43652
+ await rm11(temporary, { force: true }).catch(() => void 0);
42992
43653
  throw error52;
42993
43654
  }
42994
43655
  await options.syncDirectory?.(directory);
@@ -43024,7 +43685,7 @@ function boundedAppend(current, chunk) {
43024
43685
  async function defaultRunCommand(command, args) {
43025
43686
  const commandEnvironment3 = systemServiceCommandEnvironment();
43026
43687
  return new Promise((resolve19) => {
43027
- const child = spawn12(command, [...args], {
43688
+ const child = spawn13(command, [...args], {
43028
43689
  stdio: ["ignore", "pipe", "pipe"],
43029
43690
  env: commandEnvironment3,
43030
43691
  windowsHide: true
@@ -43096,33 +43757,33 @@ async function defaultSyncDirectory(path) {
43096
43757
  }
43097
43758
  }
43098
43759
  async function ensureDirectory(path, mode, syncDirectory8) {
43099
- const firstCreated = await mkdir15(path, { recursive: true, mode });
43760
+ const firstCreated = await mkdir16(path, { recursive: true, mode });
43100
43761
  if (!firstCreated) return;
43101
43762
  const first = resolve13(firstCreated);
43102
43763
  const target = resolve13(path);
43103
43764
  await syncDirectory8(dirname11(first));
43104
43765
  let current = first;
43105
- const descendants = relative9(first, target);
43106
- for (const part of descendants ? descendants.split(sep6) : []) {
43766
+ const descendants = relative10(first, target);
43767
+ for (const part of descendants ? descendants.split(sep7) : []) {
43107
43768
  await syncDirectory8(current);
43108
- current = join22(current, part);
43769
+ current = join23(current, part);
43109
43770
  }
43110
43771
  }
43111
43772
  async function replacePrivateFile(path, contents, mode, syncDirectory8) {
43112
43773
  const parent = dirname11(path);
43113
43774
  await ensureDirectory(parent, 448, syncDirectory8);
43114
- const temporary = join22(parent, `.${basename4(path)}.${process.pid}.${crypto.randomUUID()}.tmp`);
43775
+ const temporary = join23(parent, `.${basename4(path)}.${process.pid}.${crypto.randomUUID()}.tmp`);
43115
43776
  const handle = await open7(temporary, "wx", mode);
43116
43777
  try {
43117
43778
  await handle.writeFile(contents, "utf8");
43118
43779
  await handle.sync();
43119
43780
  await handle.close();
43120
- await rename7(temporary, path);
43121
- await chmod8(path, mode);
43781
+ await rename8(temporary, path);
43782
+ await chmod9(path, mode);
43122
43783
  await syncDirectory8(parent);
43123
43784
  } catch (error52) {
43124
43785
  await handle.close().catch(() => void 0);
43125
- await rm11(temporary, { force: true }).catch(() => void 0);
43786
+ await rm12(temporary, { force: true }).catch(() => void 0);
43126
43787
  throw error52;
43127
43788
  }
43128
43789
  }
@@ -43163,11 +43824,11 @@ async function installLinuxService(options) {
43163
43824
  "command search path"
43164
43825
  );
43165
43826
  const cloudUrl = options.cloudUrl ? oneLine(options.cloudUrl, "Zixt Cloud address") : void 0;
43166
- const xdgConfigHome = env.XDG_CONFIG_HOME ? oneLine(env.XDG_CONFIG_HOME, "Linux configuration path") : join22(home, ".config");
43167
- const configRoot = options.serviceConfigRoot ?? join22(xdgConfigHome, "zixt");
43168
- const unitRoot = options.userUnitRoot ?? join22(xdgConfigHome, "systemd", "user");
43169
- const environmentPath = join22(configRoot, "host.env");
43170
- const unitPath = join22(unitRoot, SERVICE_NAME);
43827
+ const xdgConfigHome = env.XDG_CONFIG_HOME ? oneLine(env.XDG_CONFIG_HOME, "Linux configuration path") : join23(home, ".config");
43828
+ const configRoot = options.serviceConfigRoot ?? join23(xdgConfigHome, "zixt");
43829
+ const unitRoot = options.userUnitRoot ?? join23(xdgConfigHome, "systemd", "user");
43830
+ const environmentPath = join23(configRoot, "host.env");
43831
+ const unitPath = join23(unitRoot, SERVICE_NAME);
43171
43832
  const installVersion = options.installVersion ?? ((version2, onFailure) => installRelease(version2, onFailure ? { onFailure } : {}));
43172
43833
  const activateVersion = options.activateVersion ?? (options.installVersion ? async (entry) => entry : activateInstalledRelease);
43173
43834
  const resolveCommand = options.resolveCommand ?? defaultResolveCommand;
@@ -43207,7 +43868,7 @@ async function installLinuxService(options) {
43207
43868
  }
43208
43869
  }
43209
43870
  await ensureDirectory(configRoot, 448, syncDirectory8);
43210
- await chmod8(configRoot, 448);
43871
+ await chmod9(configRoot, 448);
43211
43872
  const serviceEnvironment = [
43212
43873
  `ZIXT_HOST_TOKEN=${systemdEnvironmentValue(token2)}`,
43213
43874
  ...cloudUrl ? [`ZIXT_CLOUD_URL=${systemdEnvironmentValue(cloudUrl)}`] : [],
@@ -43295,11 +43956,11 @@ async function installLinuxService(options) {
43295
43956
  }
43296
43957
 
43297
43958
  // src/macos-service.ts
43298
- import { spawn as spawn13 } from "node:child_process";
43959
+ import { spawn as spawn14 } from "node:child_process";
43299
43960
  import { constants as constants3 } from "node:fs";
43300
- import { access as access6, chmod as chmod9, mkdir as mkdir16, open as open8, rename as rename8, rm as rm12 } from "node:fs/promises";
43961
+ import { access as access6, chmod as chmod10, mkdir as mkdir17, open as open8, rename as rename9, rm as rm13 } from "node:fs/promises";
43301
43962
  import { homedir as homedir12, userInfo as userInfo2 } from "node:os";
43302
- import { basename as basename5, dirname as dirname12, join as join23, relative as relative10, resolve as resolve14, sep as sep7 } from "node:path";
43963
+ import { basename as basename5, dirname as dirname12, join as join24, relative as relative11, resolve as resolve14, sep as sep8 } from "node:path";
43303
43964
  var LAUNCH_AGENT_LABEL = "ai.zixt.host";
43304
43965
  var SERVICE_STABILITY_DELAY_MS2 = 2e3;
43305
43966
  var STATUS_WAIT_MS = 2e4;
@@ -43324,32 +43985,32 @@ async function syncDirectory4(path) {
43324
43985
  }
43325
43986
  }
43326
43987
  async function ensureDirectory2(path, sync) {
43327
- const firstCreated = await mkdir16(path, { recursive: true, mode: 448 });
43988
+ const firstCreated = await mkdir17(path, { recursive: true, mode: 448 });
43328
43989
  if (!firstCreated) return;
43329
43990
  const first = resolve14(firstCreated);
43330
43991
  const target = resolve14(path);
43331
43992
  await sync(dirname12(first));
43332
43993
  let current = first;
43333
- for (const part of relative10(first, target).split(sep7).filter(Boolean)) {
43994
+ for (const part of relative11(first, target).split(sep8).filter(Boolean)) {
43334
43995
  await sync(current);
43335
- current = join23(current, part);
43996
+ current = join24(current, part);
43336
43997
  }
43337
43998
  }
43338
43999
  async function replacePrivateFile2(path, contents, mode, sync) {
43339
44000
  const parent = dirname12(path);
43340
44001
  await ensureDirectory2(parent, sync);
43341
- const temporary = join23(parent, `.${basename5(path)}.${process.pid}.${crypto.randomUUID()}.tmp`);
44002
+ const temporary = join24(parent, `.${basename5(path)}.${process.pid}.${crypto.randomUUID()}.tmp`);
43342
44003
  const handle = await open8(temporary, "wx", mode);
43343
44004
  try {
43344
44005
  await handle.writeFile(contents, "utf8");
43345
44006
  await handle.sync();
43346
44007
  await handle.close();
43347
- await rename8(temporary, path);
43348
- await chmod9(path, mode);
44008
+ await rename9(temporary, path);
44009
+ await chmod10(path, mode);
43349
44010
  await sync(parent);
43350
44011
  } catch (error52) {
43351
44012
  await handle.close().catch(() => void 0);
43352
- await rm12(temporary, { force: true }).catch(() => void 0);
44013
+ await rm13(temporary, { force: true }).catch(() => void 0);
43353
44014
  throw error52;
43354
44015
  }
43355
44016
  }
@@ -43363,7 +44024,7 @@ function commandEnvironment(env) {
43363
44024
  }
43364
44025
  async function defaultRunCommand2(command, args, env) {
43365
44026
  return new Promise((resolveResult) => {
43366
- const child = spawn13(command, [...args], {
44027
+ const child = spawn14(command, [...args], {
43367
44028
  stdio: ["ignore", "pipe", "pipe"],
43368
44029
  env: commandEnvironment(env)
43369
44030
  });
@@ -43435,14 +44096,14 @@ async function installMacosService(options) {
43435
44096
  options.path ?? env.PATH ?? "/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin",
43436
44097
  "command search path"
43437
44098
  );
43438
- const configRoot = options.configRoot ?? join23(home, "Library", "Application Support", "Zixt");
43439
- const launchAgentsRoot = options.launchAgentsRoot ?? join23(home, "Library", "LaunchAgents");
43440
- const logRoot = options.logRoot ?? join23(home, "Library", "Logs", "Zixt");
43441
- const configPath = join23(configRoot, "host.env");
43442
- const launcherPath = join23(configRoot, "host-launcher.sh");
43443
- const plistPath = join23(launchAgentsRoot, `${LAUNCH_AGENT_LABEL}.plist`);
43444
- const stdoutPath = join23(logRoot, "host.log");
43445
- const stderrPath = join23(logRoot, "host-error.log");
44099
+ const configRoot = options.configRoot ?? join24(home, "Library", "Application Support", "Zixt");
44100
+ const launchAgentsRoot = options.launchAgentsRoot ?? join24(home, "Library", "LaunchAgents");
44101
+ const logRoot = options.logRoot ?? join24(home, "Library", "Logs", "Zixt");
44102
+ const configPath = join24(configRoot, "host.env");
44103
+ const launcherPath = join24(configRoot, "host-launcher.sh");
44104
+ const plistPath = join24(launchAgentsRoot, `${LAUNCH_AGENT_LABEL}.plist`);
44105
+ const stdoutPath = join24(logRoot, "host.log");
44106
+ const stderrPath = join24(logRoot, "host-error.log");
43446
44107
  const installVersion = options.installVersion ?? ((version2, onFailure) => installRelease(version2, onFailure ? { onFailure } : {}));
43447
44108
  const activateVersion = options.activateVersion ?? (options.installVersion ? async (entry) => entry : (entry) => activateInstalledRelease(entry));
43448
44109
  const resolveCommand = options.resolveCommand ?? (async () => defaultResolveCommand2());
@@ -43465,7 +44126,7 @@ async function installMacosService(options) {
43465
44126
  await ensureDirectory2(configRoot, sync);
43466
44127
  await ensureDirectory2(launchAgentsRoot, sync);
43467
44128
  await ensureDirectory2(logRoot, sync);
43468
- await chmod9(configRoot, 448);
44129
+ await chmod10(configRoot, 448);
43469
44130
  const serviceEnvironment = [
43470
44131
  `ZIXT_HOST_TOKEN=${shellValue(token2)}`,
43471
44132
  ...cloudUrl ? [`ZIXT_CLOUD_URL=${shellValue(cloudUrl)}`] : [],
@@ -43543,11 +44204,11 @@ async function installMacosService(options) {
43543
44204
  }
43544
44205
 
43545
44206
  // src/windows-service.ts
43546
- import { spawn as spawn14 } from "node:child_process";
44207
+ import { spawn as spawn15 } from "node:child_process";
43547
44208
  import { constants as constants4 } from "node:fs";
43548
- import { access as access7, mkdir as mkdir17, open as open9, readFile as readFile11, rename as rename9, rm as rm13 } from "node:fs/promises";
44209
+ import { access as access7, mkdir as mkdir18, open as open9, readFile as readFile12, rename as rename10, rm as rm14 } from "node:fs/promises";
43549
44210
  import { homedir as homedir13 } from "node:os";
43550
- import { basename as basename6, dirname as dirname13, isAbsolute as isAbsolute18, join as join24, relative as relative11, resolve as resolve15, sep as sep8 } from "node:path";
44211
+ import { basename as basename6, dirname as dirname13, isAbsolute as isAbsolute18, join as join25, relative as relative12, resolve as resolve15, sep as sep9 } from "node:path";
43551
44212
  var TASK_NAME = "Zixt Host";
43552
44213
  var COMMAND_TIMEOUT_MS3 = 7e4;
43553
44214
  var SERVICE_STABILITY_DELAY_MS3 = 2e3;
@@ -43573,31 +44234,31 @@ async function syncDirectory5(path) {
43573
44234
  }
43574
44235
  }
43575
44236
  async function ensureDirectory3(path, sync) {
43576
- const firstCreated = await mkdir17(path, { recursive: true, mode: 448 });
44237
+ const firstCreated = await mkdir18(path, { recursive: true, mode: 448 });
43577
44238
  if (!firstCreated) return;
43578
44239
  const first = resolve15(firstCreated);
43579
44240
  const target = resolve15(path);
43580
44241
  await sync(dirname13(first));
43581
44242
  let current = first;
43582
- for (const part of relative11(first, target).split(sep8).filter(Boolean)) {
44243
+ for (const part of relative12(first, target).split(sep9).filter(Boolean)) {
43583
44244
  await sync(current);
43584
- current = join24(current, part);
44245
+ current = join25(current, part);
43585
44246
  }
43586
44247
  }
43587
44248
  async function replacePrivateFile3(path, contents, sync, encoding = "utf8") {
43588
44249
  const parent = dirname13(path);
43589
44250
  await ensureDirectory3(parent, sync);
43590
- const temporary = join24(parent, `.${basename6(path)}.${process.pid}.${crypto.randomUUID()}.tmp`);
44251
+ const temporary = join25(parent, `.${basename6(path)}.${process.pid}.${crypto.randomUUID()}.tmp`);
43591
44252
  const handle = await open9(temporary, "wx", 384);
43592
44253
  try {
43593
44254
  await handle.writeFile(encoding === "utf16le" ? `\uFEFF${contents}` : contents, encoding);
43594
44255
  await handle.sync();
43595
44256
  await handle.close();
43596
- await rename9(temporary, path);
44257
+ await rename10(temporary, path);
43597
44258
  await sync(parent);
43598
44259
  } catch (error52) {
43599
44260
  await handle.close().catch(() => void 0);
43600
- await rm13(temporary, { force: true }).catch(() => void 0);
44261
+ await rm14(temporary, { force: true }).catch(() => void 0);
43601
44262
  throw error52;
43602
44263
  }
43603
44264
  }
@@ -43608,7 +44269,7 @@ function commandEnvironment2(env) {
43608
44269
  }
43609
44270
  async function runChild(command, args, env, input) {
43610
44271
  return new Promise((resolveResult) => {
43611
- const child = spawn14(command, [...args], {
44272
+ const child = spawn15(command, [...args], {
43612
44273
  stdio: [input === void 0 ? "ignore" : "pipe", "pipe", "pipe"],
43613
44274
  env: commandEnvironment2(env),
43614
44275
  windowsHide: true
@@ -43642,7 +44303,7 @@ async function runChild(command, args, env, input) {
43642
44303
  async function defaultResolveCommand3(name, env) {
43643
44304
  const root = env.SYSTEMROOT ?? env.WINDIR;
43644
44305
  if (!root || !isAbsolute18(root)) return null;
43645
- const candidate = name === "powershell" ? join24(root, "System32", "WindowsPowerShell", "v1.0", "powershell.exe") : join24(root, "System32", `${name}.exe`);
44306
+ const candidate = name === "powershell" ? join25(root, "System32", "WindowsPowerShell", "v1.0", "powershell.exe") : join25(root, "System32", `${name}.exe`);
43646
44307
  return access7(candidate, constants4.X_OK).then(
43647
44308
  () => candidate,
43648
44309
  () => null
@@ -43732,7 +44393,7 @@ exit $code
43732
44393
  }
43733
44394
  async function defaultObserveStatus(path, generation) {
43734
44395
  try {
43735
- const text = (await readFile11(path, "utf8")).replace(/^\uFEFF/, "");
44396
+ const text = (await readFile12(path, "utf8")).replace(/^\uFEFF/, "");
43736
44397
  const value = JSON.parse(text);
43737
44398
  if (value.schema !== 1 || value.generation !== generation || typeof value.pid !== "number" || !Number.isSafeInteger(value.pid) || value.pid <= 0) {
43738
44399
  return null;
@@ -43793,12 +44454,12 @@ async function installWindowsService(options) {
43793
44454
  const token2 = oneLine3(options.token, "pairing code");
43794
44455
  const cloudUrl = options.cloudUrl ? oneLine3(options.cloudUrl, "Zixt Cloud address") : void 0;
43795
44456
  const path = oneLine3(options.path ?? env.PATH ?? "", "command search path");
43796
- const configRoot = options.configRoot ?? join24(localAppData, "Zixt", "Host");
43797
- const configPath = join24(configRoot, "host.json");
43798
- const launcherPath = join24(configRoot, "host-launcher.ps1");
43799
- const launchShimPath = join24(configRoot, "host-launch.vbs");
43800
- const taskXmlPath = join24(configRoot, "host-task.xml");
43801
- const statusPath = join24(configRoot, "host-status.json");
44457
+ const configRoot = options.configRoot ?? join25(localAppData, "Zixt", "Host");
44458
+ const configPath = join25(configRoot, "host.json");
44459
+ const launcherPath = join25(configRoot, "host-launcher.ps1");
44460
+ const launchShimPath = join25(configRoot, "host-launch.vbs");
44461
+ const taskXmlPath = join25(configRoot, "host-task.xml");
44462
+ const statusPath = join25(configRoot, "host-status.json");
43802
44463
  const installVersion = options.installVersion ?? ((version2, onFailure) => installRelease(version2, onFailure ? { onFailure } : {}));
43803
44464
  const activateVersion = options.activateVersion ?? (options.installVersion ? async (entry) => entry : (entry) => activateInstalledRelease(entry));
43804
44465
  const resolveCommand = options.resolveCommand ?? ((name) => defaultResolveCommand3(name, env));
@@ -43865,7 +44526,7 @@ async function installWindowsService(options) {
43865
44526
  sync,
43866
44527
  "utf16le"
43867
44528
  );
43868
- await rm13(statusPath, { force: true });
44529
+ await rm14(statusPath, { force: true });
43869
44530
  const acl = await run3(icacls, [
43870
44531
  configRoot,
43871
44532
  "/inheritance:r",
@@ -43925,23 +44586,23 @@ async function installSystemService(options) {
43925
44586
  }
43926
44587
 
43927
44588
  // src/terminal-outcomes.ts
43928
- import { chmod as chmod10, lstat as lstat12, mkdir as mkdir18, open as open10, readdir as readdir6, readFile as readFile12, rename as rename10, rm as rm14 } from "node:fs/promises";
44589
+ import { chmod as chmod11, lstat as lstat12, mkdir as mkdir19, open as open10, readdir as readdir7, readFile as readFile13, rename as rename11, rm as rm15 } from "node:fs/promises";
43929
44590
  import { homedir as homedir14 } from "node:os";
43930
- import { dirname as dirname14, join as join25, relative as relative12, resolve as resolve16, sep as sep9 } from "node:path";
44591
+ import { dirname as dirname14, join as join26, relative as relative13, resolve as resolve16, sep as sep10 } from "node:path";
43931
44592
  var DIRECTORY_MODE5 = 448;
43932
44593
  var FILE_MODE4 = 384;
43933
44594
  var MAX_OUTCOME_BYTES = 4 * 1024 * 1024;
43934
44595
  var HOST_DIRECTORY = /^hst_[0-9a-f]{32}$/;
43935
44596
  var OUTCOME_FILE = /^(tsk_[0-9a-f]{32})\.([1-9][0-9]*)\.json$/;
43936
44597
  function defaultTerminalOutcomeRoot() {
43937
- return join25(homedir14(), ".zixt", "terminal-outcomes");
44598
+ return join26(homedir14(), ".zixt", "terminal-outcomes");
43938
44599
  }
43939
44600
  function hostOutcomeRoot(root, hostId) {
43940
44601
  if (!HOST_DIRECTORY.test(hostId)) throw new Error("terminal outcome Host identity is malformed");
43941
- return join25(root, hostId);
44602
+ return join26(root, hostId);
43942
44603
  }
43943
44604
  function outcomePath(root, hostId, taskId, epoch) {
43944
- return join25(hostOutcomeRoot(root, hostId), `${taskId}.${epoch}.json`);
44605
+ return join26(hostOutcomeRoot(root, hostId), `${taskId}.${epoch}.json`);
43945
44606
  }
43946
44607
  async function syncDirectory6(root) {
43947
44608
  if (process.platform === "win32") return;
@@ -43953,22 +44614,22 @@ async function syncDirectory6(root) {
43953
44614
  }
43954
44615
  }
43955
44616
  async function requirePrivateRoot(root, sync = syncDirectory6) {
43956
- const firstCreated = await mkdir18(root, { recursive: true, mode: DIRECTORY_MODE5 });
44617
+ const firstCreated = await mkdir19(root, { recursive: true, mode: DIRECTORY_MODE5 });
43957
44618
  if (firstCreated) {
43958
44619
  const first = resolve16(firstCreated);
43959
44620
  const target = resolve16(root);
43960
44621
  await sync(dirname14(first));
43961
44622
  let current = first;
43962
- for (const part of relative12(first, target).split(sep9).filter(Boolean)) {
44623
+ for (const part of relative13(first, target).split(sep10).filter(Boolean)) {
43963
44624
  await sync(current);
43964
- current = join25(current, part);
44625
+ current = join26(current, part);
43965
44626
  }
43966
44627
  }
43967
44628
  const stat4 = await lstat12(root);
43968
44629
  if (stat4.isSymbolicLink() || !stat4.isDirectory()) {
43969
44630
  throw new Error("terminal outcome journal root is not a trusted directory");
43970
44631
  }
43971
- await chmod10(root, DIRECTORY_MODE5);
44632
+ await chmod11(root, DIRECTORY_MODE5);
43972
44633
  }
43973
44634
  function parseCommittedOutcome(text, taskId, epoch) {
43974
44635
  let json2;
@@ -43992,7 +44653,7 @@ async function recordTerminalOutcome(hostId, input, root = defaultTerminalOutcom
43992
44653
  const destination = outcomePath(root, hostId, outcome.taskId, outcome.epoch);
43993
44654
  try {
43994
44655
  const existing = parseCommittedOutcome(
43995
- await readFile12(destination, { encoding: "utf8", flag: "r" }),
44656
+ await readFile13(destination, { encoding: "utf8", flag: "r" }),
43996
44657
  outcome.taskId,
43997
44658
  outcome.epoch
43998
44659
  );
@@ -44001,7 +44662,7 @@ async function recordTerminalOutcome(hostId, input, root = defaultTerminalOutcom
44001
44662
  } catch (error52) {
44002
44663
  if (error52.code !== "ENOENT") throw error52;
44003
44664
  }
44004
- const temporary = join25(
44665
+ const temporary = join26(
44005
44666
  scopedRoot,
44006
44667
  `.${outcome.taskId}.${outcome.epoch}.${process.pid}.${Date.now()}.${outcome.resultId}.tmp`
44007
44668
  );
@@ -44012,13 +44673,13 @@ async function recordTerminalOutcome(hostId, input, root = defaultTerminalOutcom
44012
44673
  await handle.sync();
44013
44674
  await handle.close();
44014
44675
  handle = void 0;
44015
- await rename10(temporary, destination);
44676
+ await rename11(temporary, destination);
44016
44677
  await sync(scopedRoot);
44017
44678
  await sync(root);
44018
44679
  } finally {
44019
44680
  await handle?.close().catch(() => {
44020
44681
  });
44021
- await rm14(temporary, { force: true }).catch(() => {
44682
+ await rm15(temporary, { force: true }).catch(() => {
44022
44683
  });
44023
44684
  }
44024
44685
  }
@@ -44033,8 +44694,8 @@ async function readTerminalOutcomesStrict(root = defaultTerminalOutcomeRoot()) {
44033
44694
  if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
44034
44695
  throw new Error("terminal outcome journal root is not a trusted directory");
44035
44696
  }
44036
- await chmod10(root, DIRECTORY_MODE5);
44037
- const hostEntries = await readdir6(root, { withFileTypes: true });
44697
+ await chmod11(root, DIRECTORY_MODE5);
44698
+ const hostEntries = await readdir7(root, { withFileTypes: true });
44038
44699
  const outcomes = [];
44039
44700
  const resultIds = /* @__PURE__ */ new Set();
44040
44701
  for (const hostEntry of hostEntries) {
@@ -44046,21 +44707,21 @@ async function readTerminalOutcomesStrict(root = defaultTerminalOutcomeRoot()) {
44046
44707
  if (scopedStat.isSymbolicLink() || !scopedStat.isDirectory()) {
44047
44708
  throw new Error("terminal outcome Host scope is not a trusted directory");
44048
44709
  }
44049
- await chmod10(scopedRoot, DIRECTORY_MODE5);
44050
- const entries = await readdir6(scopedRoot, { withFileTypes: true });
44710
+ await chmod11(scopedRoot, DIRECTORY_MODE5);
44711
+ const entries = await readdir7(scopedRoot, { withFileTypes: true });
44051
44712
  for (const entry of entries) {
44052
44713
  if (!entry.name.endsWith(".json")) continue;
44053
44714
  const match = OUTCOME_FILE.exec(entry.name);
44054
44715
  if (!match || !entry.isFile() || entry.isSymbolicLink()) {
44055
44716
  throw new Error("committed terminal outcome is not a trusted regular file");
44056
44717
  }
44057
- const path = join25(scopedRoot, entry.name);
44718
+ const path = join26(scopedRoot, entry.name);
44058
44719
  const stat4 = await lstat12(path);
44059
44720
  if (!stat4.isFile() || stat4.isSymbolicLink() || stat4.size > MAX_OUTCOME_BYTES) {
44060
44721
  throw new Error("committed terminal outcome is not a trusted regular file");
44061
44722
  }
44062
44723
  const outcome = parseCommittedOutcome(
44063
- await readFile12(path, "utf8"),
44724
+ await readFile13(path, "utf8"),
44064
44725
  match[1],
44065
44726
  Number(match[2])
44066
44727
  );
@@ -44087,7 +44748,7 @@ async function forgetAcknowledgedTerminalOutcomes(refs, root = defaultTerminalOu
44087
44748
  if (acknowledged.get(`${hostId}:${outcome.taskId}:${outcome.epoch}`) !== outcome.resultId) {
44088
44749
  continue;
44089
44750
  }
44090
- await rm14(outcomePath(root, hostId, outcome.taskId, outcome.epoch), { force: true });
44751
+ await rm15(outcomePath(root, hostId, outcome.taskId, outcome.epoch), { force: true });
44091
44752
  changedHostRoots.add(hostOutcomeRoot(root, hostId));
44092
44753
  }
44093
44754
  for (const scopedRoot of changedHostRoots) await syncDirectory6(scopedRoot);
@@ -44099,22 +44760,22 @@ async function forgetSupersededTerminalOutcomes(hostId, taskId, epoch, root = de
44099
44760
  if (scoped.hostId !== hostId) continue;
44100
44761
  const { outcome } = scoped;
44101
44762
  if (outcome.taskId !== taskId || outcome.epoch >= epoch) continue;
44102
- await rm14(outcomePath(root, hostId, outcome.taskId, outcome.epoch), { force: true });
44763
+ await rm15(outcomePath(root, hostId, outcome.taskId, outcome.epoch), { force: true });
44103
44764
  removed = true;
44104
44765
  }
44105
44766
  if (removed) await syncDirectory6(hostOutcomeRoot(root, hostId));
44106
44767
  }
44107
44768
 
44108
44769
  // src/accepted-assignments.ts
44109
- import { chmod as chmod11, lstat as lstat13, mkdir as mkdir19, open as open11, readdir as readdir7, rename as rename11, rm as rm15 } from "node:fs/promises";
44770
+ import { chmod as chmod12, lstat as lstat13, mkdir as mkdir20, open as open11, readdir as readdir8, rename as rename12, rm as rm16 } from "node:fs/promises";
44110
44771
  import { homedir as homedir15 } from "node:os";
44111
- import { dirname as dirname15, join as join26, relative as relative13, resolve as resolve17, sep as sep10 } from "node:path";
44772
+ import { dirname as dirname15, join as join27, relative as relative14, resolve as resolve17, sep as sep11 } from "node:path";
44112
44773
  var DIRECTORY_MODE6 = 448;
44113
44774
  var FILE_MODE5 = 384;
44114
44775
  var CLAIM_FILE = /^(tsk_[0-9a-f]{32})\.([1-9][0-9]*)\.json$/;
44115
44776
  var TASK_ID = /^tsk_[0-9a-f]{32}$/;
44116
44777
  function defaultAcceptedAssignmentRoot() {
44117
- return join26(homedir15(), ".zixt", "accepted-assignments");
44778
+ return join27(homedir15(), ".zixt", "accepted-assignments");
44118
44779
  }
44119
44780
  async function syncDirectory7(root) {
44120
44781
  if (process.platform === "win32") return;
@@ -44126,29 +44787,29 @@ async function syncDirectory7(root) {
44126
44787
  }
44127
44788
  }
44128
44789
  async function requirePrivateRoot2(root, sync = syncDirectory7) {
44129
- const firstCreated = await mkdir19(root, { recursive: true, mode: DIRECTORY_MODE6 });
44790
+ const firstCreated = await mkdir20(root, { recursive: true, mode: DIRECTORY_MODE6 });
44130
44791
  if (firstCreated) {
44131
44792
  const first = resolve17(firstCreated);
44132
44793
  const target = resolve17(root);
44133
44794
  await sync(dirname15(first));
44134
44795
  let current = first;
44135
- for (const part of relative13(first, target).split(sep10).filter(Boolean)) {
44796
+ for (const part of relative14(first, target).split(sep11).filter(Boolean)) {
44136
44797
  await sync(current);
44137
- current = join26(current, part);
44798
+ current = join27(current, part);
44138
44799
  }
44139
44800
  }
44140
44801
  const stat4 = await lstat13(root);
44141
44802
  if (stat4.isSymbolicLink() || !stat4.isDirectory()) {
44142
44803
  throw new Error("accepted assignment journal root is not a trusted directory");
44143
44804
  }
44144
- await chmod11(root, DIRECTORY_MODE6);
44805
+ await chmod12(root, DIRECTORY_MODE6);
44145
44806
  }
44146
44807
  function claimPath(root, taskId, epoch) {
44147
44808
  if (!TASK_ID.test(taskId)) throw new Error("accepted assignment Task identity is malformed");
44148
44809
  if (!Number.isSafeInteger(epoch) || epoch < 1) {
44149
44810
  throw new Error("accepted assignment epoch is malformed");
44150
44811
  }
44151
- return join26(root, `${taskId}.${epoch}.json`);
44812
+ return join27(root, `${taskId}.${epoch}.json`);
44152
44813
  }
44153
44814
  async function recordAcceptedAssignment(assignment, root = defaultAcceptedAssignmentRoot(), options = {}) {
44154
44815
  const sync = options.syncDirectory ?? syncDirectory7;
@@ -44158,7 +44819,7 @@ async function recordAcceptedAssignment(assignment, root = defaultAcceptedAssign
44158
44819
  } catch {
44159
44820
  return false;
44160
44821
  }
44161
- const temporary = join26(
44822
+ const temporary = join27(
44162
44823
  root,
44163
44824
  `.${assignment.taskId}.${assignment.epoch}.${process.pid}.${Date.now()}.tmp`
44164
44825
  );
@@ -44170,7 +44831,7 @@ async function recordAcceptedAssignment(assignment, root = defaultAcceptedAssign
44170
44831
  await handle.sync();
44171
44832
  await handle.close();
44172
44833
  handle = void 0;
44173
- await rename11(temporary, destination);
44834
+ await rename12(temporary, destination);
44174
44835
  if (process.platform !== "win32") await sync(root);
44175
44836
  return true;
44176
44837
  } catch {
@@ -44178,7 +44839,7 @@ async function recordAcceptedAssignment(assignment, root = defaultAcceptedAssign
44178
44839
  } finally {
44179
44840
  await handle?.close().catch(() => {
44180
44841
  });
44181
- await rm15(temporary, { force: true }).catch(() => {
44842
+ await rm16(temporary, { force: true }).catch(() => {
44182
44843
  });
44183
44844
  }
44184
44845
  }
@@ -44193,9 +44854,9 @@ async function recoverAcceptedAssignments(root = defaultAcceptedAssignmentRoot()
44193
44854
  if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
44194
44855
  throw new Error("accepted assignment journal root is not a trusted directory");
44195
44856
  }
44196
- await chmod11(root, DIRECTORY_MODE6);
44857
+ await chmod12(root, DIRECTORY_MODE6);
44197
44858
  const claims = [];
44198
- for (const entry of await readdir7(root, { withFileTypes: true })) {
44859
+ for (const entry of await readdir8(root, { withFileTypes: true })) {
44199
44860
  if (!entry.isFile()) continue;
44200
44861
  const match = CLAIM_FILE.exec(entry.name);
44201
44862
  if (!match) continue;
@@ -44212,7 +44873,7 @@ async function forgetAcceptedAssignment(assignment, root = defaultAcceptedAssign
44212
44873
  } catch {
44213
44874
  return;
44214
44875
  }
44215
- await rm15(path, { force: true }).catch(() => {
44876
+ await rm16(path, { force: true }).catch(() => {
44216
44877
  });
44217
44878
  }
44218
44879
  async function forgetAcknowledgedAcceptedAssignments(assignments, root = defaultAcceptedAssignmentRoot()) {
@@ -44220,9 +44881,9 @@ async function forgetAcknowledgedAcceptedAssignments(assignments, root = default
44220
44881
  }
44221
44882
 
44222
44883
  // src/local-observability.ts
44223
- import { appendFile, mkdir as mkdir20, open as open12, readdir as readdir8, rename as rename12, rm as rm16, stat as stat3 } from "node:fs/promises";
44884
+ import { appendFile, mkdir as mkdir21, open as open12, readdir as readdir9, rename as rename13, rm as rm17, stat as stat3 } from "node:fs/promises";
44224
44885
  import { homedir as homedir16 } from "node:os";
44225
- import { basename as basename7, dirname as dirname16, join as join27 } from "node:path";
44886
+ import { basename as basename7, dirname as dirname16, join as join28 } from "node:path";
44226
44887
 
44227
44888
  // src/logger.ts
44228
44889
  var ANSI = {
@@ -44334,11 +44995,11 @@ var LOCAL_STATUS_FILE = "status.json";
44334
44995
  var LOCAL_REQUESTS_DIR = "requests";
44335
44996
  var DEFAULT_CONSOLE_ROTATE_BYTES = 2 * 1024 * 1024;
44336
44997
  function defaultLocalObservabilityRoot() {
44337
- return join27(homedir16(), ".zixt", "observability");
44998
+ return join28(homedir16(), ".zixt", "observability");
44338
44999
  }
44339
45000
  function createLocalConsoleSink(options = {}) {
44340
45001
  const root = options.root ?? defaultLocalObservabilityRoot();
44341
- const consolePath = join27(root, LOCAL_CONSOLE_FILE);
45002
+ const consolePath = join28(root, LOCAL_CONSOLE_FILE);
44342
45003
  const rotateBytes = options.rotateBytes ?? DEFAULT_CONSOLE_ROTATE_BYTES;
44343
45004
  let disabled = false;
44344
45005
  let prepared = false;
@@ -44348,7 +45009,7 @@ function createLocalConsoleSink(options = {}) {
44348
45009
  if (disabled) return;
44349
45010
  try {
44350
45011
  if (!prepared) {
44351
- await mkdir20(root, { recursive: true, mode: 448 });
45012
+ await mkdir21(root, { recursive: true, mode: 448 });
44352
45013
  approximateBytes = await stat3(consolePath).then(
44353
45014
  (existing) => existing.size,
44354
45015
  () => 0
@@ -44356,8 +45017,8 @@ function createLocalConsoleSink(options = {}) {
44356
45017
  prepared = true;
44357
45018
  }
44358
45019
  if (approximateBytes >= rotateBytes) {
44359
- await rm16(join27(root, LOCAL_CONSOLE_PREVIOUS_FILE), { force: true });
44360
- await rename12(consolePath, join27(root, LOCAL_CONSOLE_PREVIOUS_FILE)).catch(
45020
+ await rm17(join28(root, LOCAL_CONSOLE_PREVIOUS_FILE), { force: true });
45021
+ await rename13(consolePath, join28(root, LOCAL_CONSOLE_PREVIOUS_FILE)).catch(
44361
45022
  (error52) => {
44362
45023
  if (error52.code !== "ENOENT") throw error52;
44363
45024
  }
@@ -44388,11 +45049,11 @@ function createLocalConsoleSink(options = {}) {
44388
45049
  };
44389
45050
  }
44390
45051
  async function consumeRunnerInstallRequests(root = defaultLocalObservabilityRoot()) {
44391
- const directory = join27(root, LOCAL_REQUESTS_DIR);
45052
+ const directory = join28(root, LOCAL_REQUESTS_DIR);
44392
45053
  const requested = /* @__PURE__ */ new Set();
44393
45054
  let names;
44394
45055
  try {
44395
- names = await readdir8(directory);
45056
+ names = await readdir9(directory);
44396
45057
  } catch {
44397
45058
  return requested;
44398
45059
  }
@@ -44400,7 +45061,7 @@ async function consumeRunnerInstallRequests(root = defaultLocalObservabilityRoot
44400
45061
  const name = `install-runner-${type}.json`;
44401
45062
  if (!names.includes(name)) continue;
44402
45063
  try {
44403
- await rm16(join27(directory, name), { force: true });
45064
+ await rm17(join28(directory, name), { force: true });
44404
45065
  requested.add(type);
44405
45066
  } catch {
44406
45067
  }
@@ -44408,13 +45069,13 @@ async function consumeRunnerInstallRequests(root = defaultLocalObservabilityRoot
44408
45069
  return requested;
44409
45070
  }
44410
45071
  async function writeLocalStatus(status, root = defaultLocalObservabilityRoot()) {
44411
- const destination = join27(root, LOCAL_STATUS_FILE);
44412
- const temporary = join27(
45072
+ const destination = join28(root, LOCAL_STATUS_FILE);
45073
+ const temporary = join28(
44413
45074
  dirname16(destination),
44414
45075
  `.${basename7(destination)}.${process.pid}.${crypto.randomUUID()}.tmp`
44415
45076
  );
44416
45077
  try {
44417
- await mkdir20(root, { recursive: true, mode: 448 });
45078
+ await mkdir21(root, { recursive: true, mode: 448 });
44418
45079
  const handle = await open12(temporary, "wx", 384);
44419
45080
  try {
44420
45081
  await handle.writeFile(`${JSON.stringify(status)}
@@ -44422,14 +45083,14 @@ async function writeLocalStatus(status, root = defaultLocalObservabilityRoot())
44422
45083
  } finally {
44423
45084
  await handle.close();
44424
45085
  }
44425
- await rename12(temporary, destination);
45086
+ await rename13(temporary, destination);
44426
45087
  } catch {
44427
- await rm16(temporary, { force: true }).catch(() => void 0);
45088
+ await rm17(temporary, { force: true }).catch(() => void 0);
44428
45089
  }
44429
45090
  }
44430
45091
 
44431
45092
  // src/demo-state.ts
44432
- import { isAbsolute as isAbsolute19, join as join28, parse as parse3, resolve as resolve18 } from "node:path";
45093
+ import { isAbsolute as isAbsolute19, join as join29, parse as parse3, resolve as resolve18 } from "node:path";
44433
45094
  var DEMO_STATE_ROOT_ENV = "ZIXT_DEMO_STATE_ROOT";
44434
45095
  function resolveDemoHostStatePaths(env = process.env) {
44435
45096
  const configured = env.ZIXT_RUNNER === "demo" ? env[DEMO_STATE_ROOT_ENV] : void 0;
@@ -44439,14 +45100,14 @@ function resolveDemoHostStatePaths(env = process.env) {
44439
45100
  throw new Error(`${DEMO_STATE_ROOT_ENV} must be a dedicated absolute directory`);
44440
45101
  }
44441
45102
  return {
44442
- runRegistryRoot: join28(root, "run-registry"),
44443
- terminalOutcomeRoot: join28(root, "terminal-outcomes"),
44444
- acceptedAssignmentRoot: join28(root, "accepted-assignments"),
44445
- runArtifactRoot: join28(root, "run-artifacts"),
44446
- browserProfileRoot: join28(root, "browser-profiles"),
44447
- runnerWorkspaceRoot: join28(root, "workspaces"),
44448
- codexThreadIndexRoot: join28(root, "codex-threads"),
44449
- localObservabilityRoot: join28(root, "local-observability")
45103
+ runRegistryRoot: join29(root, "run-registry"),
45104
+ terminalOutcomeRoot: join29(root, "terminal-outcomes"),
45105
+ acceptedAssignmentRoot: join29(root, "accepted-assignments"),
45106
+ runArtifactRoot: join29(root, "run-artifacts"),
45107
+ browserProfileRoot: join29(root, "browser-profiles"),
45108
+ runnerWorkspaceRoot: join29(root, "workspaces"),
45109
+ codexThreadIndexRoot: join29(root, "codex-threads"),
45110
+ localObservabilityRoot: join29(root, "local-observability")
44450
45111
  };
44451
45112
  }
44452
45113