@vtxmacro/cli 2026.8.36 → 2026.8.37

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/bin/vtx.js +595 -120
  2. package/package.json +1 -1
package/bin/vtx.js CHANGED
@@ -38,7 +38,7 @@ var init_agent_cli_release = __esm({
38
38
  "agent-cli-release.json"() {
39
39
  agent_cli_release_default = {
40
40
  package_name: "@vtxmacro/cli",
41
- package_version: "2026.8.36",
41
+ package_version: "2026.8.37",
42
42
  codex_package_name: "@openai/codex",
43
43
  codex_version: "0.147.0",
44
44
  platforms: {
@@ -29047,7 +29047,7 @@ function createDefaultInferenceHostRunnerDependencies(options) {
29047
29047
  envelopePublicKey: options.envelopePublicKey
29048
29048
  };
29049
29049
  }
29050
- var import_ajv, RUNTIME_RECEIPT_SCHEMA_VERSION, ATTEMPT_RECEIPT_SCHEMA_VERSION, DEFAULT_ADVERTISEMENT_TTL_MS, DEFAULT_ADVERTISEMENT_REFRESH_LEAD_MS, DEFAULT_HOST_HEARTBEAT_MS, DEFAULT_PROVIDER_RATE_LIMIT_REFRESH_MS, DEFAULT_ATTEMPT_HEARTBEAT_MS, DEFAULT_DRAIN_TIMEOUT_MS, DEFAULT_REMOTE_RETRY_LIMIT, MIN_SLEEP_MS, DEFAULT_CODEX_ACCOUNT_COOLDOWN_MS, MIN_CLAIM_START_WINDOW_MS, UNBOUNDED_AVAILABLE_SLOTS, buildCodexInferenceAdvertisedModels, FileInferenceHostRuntimeReceiptStore, InferenceHostRecoveryRequiredError, InferenceHostRunnerError, defaultSleep, abortError, defaultRegisterSignalHandlers, safeInteger, exactObjectKeys, validIso, validateAttemptReceipt, validateRuntimeReceipt, sha256, stableOperationId, buildAttemptStartRequest, isoAt, providerWeeklyQuotaFromRateLimits, finitePositiveOption, validateRunnerOptions, assertLocalCredentialIdentity, retryableRemoteError, remoteRetryAfterMs, controlPlaneFatal, defaultValidateOutput, objectRecord, positiveUtf8ByteLimit, assertCompilableOutputSchema, parseOutputContract, membershipFailureDisposition, safeFailureCode, exactJson2, runnerIdentityMismatch, assertAdvertisementResult, assertHostHeartbeatResult, assertClaimResult, assertStartResult, assertJobHeartbeatResult, assertTerminalResult, classifyFailure, ambiguousHeartbeatTransport, reportedTokenUsage, reportedUsage, InferenceHostRunner;
29050
+ var import_ajv, RUNTIME_RECEIPT_SCHEMA_VERSION, ATTEMPT_RECEIPT_SCHEMA_VERSION, DEFAULT_ADVERTISEMENT_TTL_MS, DEFAULT_ADVERTISEMENT_REFRESH_LEAD_MS, DEFAULT_HOST_HEARTBEAT_MS, DEFAULT_PROVIDER_RATE_LIMIT_REFRESH_MS, DEFAULT_ATTEMPT_HEARTBEAT_MS, DEFAULT_DRAIN_TIMEOUT_MS, DEFAULT_REMOTE_RETRY_LIMIT, MIN_SLEEP_MS, DEFAULT_CODEX_ACCOUNT_COOLDOWN_MS, MIN_CLAIM_START_WINDOW_MS, UNBOUNDED_AVAILABLE_SLOTS, buildCodexInferenceAdvertisedModels, summarizeInferenceHostRuntimeRecovery, FileInferenceHostRuntimeReceiptStore, InferenceHostRecoveryRequiredError, InferenceHostRunnerError, defaultSleep, abortError, defaultRegisterSignalHandlers, safeInteger, exactObjectKeys, validIso, validateAttemptReceipt, validateRuntimeReceipt, sha256, stableOperationId, buildAttemptStartRequest, isoAt, providerWeeklyQuotaFromRateLimits, finitePositiveOption, validateRunnerOptions, assertLocalCredentialIdentity, retryableRemoteError, remoteRetryAfterMs, controlPlaneFatal, defaultValidateOutput, objectRecord, positiveUtf8ByteLimit, assertCompilableOutputSchema, parseOutputContract, membershipFailureDisposition, safeFailureCode, exactJson2, runnerIdentityMismatch, assertAdvertisementResult, assertHostHeartbeatResult, assertClaimResult, assertStartResult, assertJobHeartbeatResult, assertTerminalResult, classifyFailure, ambiguousHeartbeatTransport, reportedTokenUsage, reportedUsage, InferenceHostRunner;
29051
29051
  var init_runner = __esm({
29052
29052
  "lib/inference-host/runner.ts"() {
29053
29053
  "use strict";
@@ -29104,6 +29104,35 @@ var init_runner = __esm({
29104
29104
  }
29105
29105
  return models;
29106
29106
  };
29107
+ summarizeInferenceHostRuntimeRecovery = (receipt) => {
29108
+ const phaseCounts = {
29109
+ claimed: 0,
29110
+ started: 0,
29111
+ dispatched: 0,
29112
+ terminal_pending: 0
29113
+ };
29114
+ const terminalOperations = [];
29115
+ for (const attempt of Object.values(receipt.attempts)) {
29116
+ phaseCounts[attempt.phase] += 1;
29117
+ const terminal = attempt.terminal_request;
29118
+ if (!terminal) continue;
29119
+ const failed = terminal.schema_version === "external_inference_job_fail_v1";
29120
+ terminalOperations.push({
29121
+ job_id: attempt.job_id,
29122
+ attempt_id: attempt.attempt_id,
29123
+ operation_kind: failed ? "fail" : "complete",
29124
+ dispatch_outcome: terminal.outcome.dispatch_outcome,
29125
+ failure_category: failed ? terminal.failure_category : null,
29126
+ failure_code: failed ? terminal.failure_code : null,
29127
+ retryable: failed ? terminal.retryable : null
29128
+ });
29129
+ }
29130
+ terminalOperations.sort((left, right) => left.job_id.localeCompare(right.job_id) || left.attempt_id.localeCompare(right.attempt_id));
29131
+ return {
29132
+ phase_counts: phaseCounts,
29133
+ terminal_operations: terminalOperations
29134
+ };
29135
+ };
29107
29136
  FileInferenceHostRuntimeReceiptStore = class {
29108
29137
  constructor(path, readPrivateFile = readInferencePrivateFile) {
29109
29138
  this.path = path;
@@ -29744,6 +29773,22 @@ var init_runner = __esm({
29744
29773
  } catch {
29745
29774
  }
29746
29775
  };
29776
+ const emitTerminalRecoveryFailure = (recovery, error48) => {
29777
+ const cause = error48 instanceof InferenceHostRunnerError && error48.code === "terminal_outcome_unconfirmed" && error48.cause ? error48.cause : error48;
29778
+ const rawCode = cause instanceof ExternalInferenceMcpError || cause instanceof InferenceHostRunnerError || cause instanceof CodexAppServerError ? cause.code : "terminal_recovery_failed";
29779
+ const terminal = recovery.terminal_request;
29780
+ emitDiagnostic("terminal_recovery_failed", {
29781
+ job_id: recovery.job_id,
29782
+ attempt_id: recovery.attempt_id,
29783
+ attempt_phase: recovery.phase,
29784
+ terminal_operation: terminal?.schema_version === "external_inference_job_fail_v1" ? "fail" : "complete",
29785
+ error_code: safeFailureCode(rawCode, "terminal_recovery_failed"),
29786
+ retryable: retryableRemoteError(cause),
29787
+ definitively_not_applied: Boolean(
29788
+ cause && typeof cause === "object" && "definitivelyNotApplied" in cause && cause.definitivelyNotApplied === true
29789
+ )
29790
+ });
29791
+ };
29747
29792
  const accountKey = inferenceCredentialAccountKey({
29748
29793
  issuer: localState.issuer,
29749
29794
  clientId: localState.client_id,
@@ -30009,17 +30054,6 @@ var init_runner = __esm({
30009
30054
  let nextClaimAt = Math.max(now(), providerRetryAtMs ?? 0);
30010
30055
  let pendingClaimPromotions = 0;
30011
30056
  let onceClaimed = false;
30012
- emitDiagnostic("runtime_started", {
30013
- max_concurrency: settings.maxConcurrency,
30014
- active_attempts: active.size,
30015
- provider_cooldown_reason: providerCooldownReason,
30016
- provider_cooldown_until: providerRetryAtMs === null ? null : isoAt(providerRetryAtMs),
30017
- provider_weekly_quota: providerWeeklyQuotaFromRateLimits(
30018
- providerRateLimits,
30019
- providerRateLimitsObservedAtMs,
30020
- now()
30021
- )
30022
- });
30023
30057
  const recoveryQueue = [];
30024
30058
  const launchClaim = (claim, recovery, claimRequest) => {
30025
30059
  if (!recovery) {
@@ -30114,6 +30148,10 @@ var init_runner = __esm({
30114
30148
  }
30115
30149
  }).catch((error48) => {
30116
30150
  failed += 1;
30151
+ const recovery2 = receipt.attempts[attemptId];
30152
+ if (recovery2?.phase === "terminal_pending") {
30153
+ emitTerminalRecoveryFailure(recovery2, error48);
30154
+ }
30117
30155
  requestDrain(controlPlaneFatal(error48) ? "authority_lost" : "attempt_terminal_unconfirmed");
30118
30156
  }).finally(() => {
30119
30157
  settleClaimPromotion();
@@ -30169,6 +30207,7 @@ var init_runner = __esm({
30169
30207
  await this.dependencies.codexAdapter.acknowledgeAttempt?.(recovery.attempt_id);
30170
30208
  await removeRecoveredAttempt(recovery.attempt_id);
30171
30209
  } catch (error48) {
30210
+ emitTerminalRecoveryFailure(recovery, error48);
30172
30211
  requestDrain(controlPlaneFatal(error48) ? "authority_lost" : "attempt_terminal_unconfirmed");
30173
30212
  break;
30174
30213
  }
@@ -30176,6 +30215,19 @@ var init_runner = __esm({
30176
30215
  }
30177
30216
  recoveryQueue.push(recovery);
30178
30217
  }
30218
+ if (!drainRequested) {
30219
+ emitDiagnostic("runtime_started", {
30220
+ max_concurrency: settings.maxConcurrency,
30221
+ active_attempts: active.size,
30222
+ provider_cooldown_reason: providerCooldownReason,
30223
+ provider_cooldown_until: providerRetryAtMs === null ? null : isoAt(providerRetryAtMs),
30224
+ provider_weekly_quota: providerWeeklyQuotaFromRateLimits(
30225
+ providerRateLimits,
30226
+ providerRateLimitsObservedAtMs,
30227
+ now()
30228
+ )
30229
+ });
30230
+ }
30179
30231
  while (!drainRequested && recoveryQueue.length > 0 && (settings.maxConcurrency === null || active.size < settings.maxConcurrency)) {
30180
30232
  const recovery = recoveryQueue.shift();
30181
30233
  launchClaim(recovery.claim, recovery);
@@ -30908,11 +30960,12 @@ var init_runner = __esm({
30908
30960
 
30909
30961
  // lib/inference-host/service.ts
30910
30962
  import { spawn as spawn5 } from "node:child_process";
30963
+ import { randomUUID } from "node:crypto";
30911
30964
  import { createWriteStream, readFileSync } from "node:fs";
30912
30965
  import { access as access3, chmod as chmod3, mkdir as mkdir3, readFile as readFile5, rm as rm4, writeFile as writeFile2 } from "node:fs/promises";
30913
30966
  import { homedir as homedir2 } from "node:os";
30914
30967
  import { dirname as dirname4, join as join5, resolve as resolve4 } from "node:path";
30915
- var SERVICE_NAME, SYSTEMD_UNIT, LAUNCHD_LABEL, SERVICE_COOPERATIVE_STOP_SECONDS, inferenceHostServiceChildEnvironment, isWindowsSubsystemForLinux, inferenceHostServiceManifestPath, inferenceHostServiceDesiredPath, inferenceHostServiceLogPath, xmlEscape, plistEscape, systemdQuote, defaultRunCommand, managerName, assertRuntimeEnvironment, withoutConcurrencyLimit, assertServicePath, assertWorker, assertManifest, assertDesiredState, readInferenceHostServiceManifest, readInferenceHostServiceDesired, readDesiredAcrossAtomicReplacement, writeDesired, runtimeEnvironment, serviceArguments, windowsOwnedCommandLine, vbScriptString, windowsServiceLauncher, windowsTaskXml, systemdUnit, launchAgentPlist, InferenceHostServiceManager, appendServiceLog, spawnServiceChild, runInferenceHostServiceSupervisor;
30968
+ var SERVICE_NAME, SYSTEMD_UNIT, LAUNCHD_LABEL, SERVICE_COOPERATIVE_STOP_SECONDS, INFERENCE_HOST_SERVICE_DRAIN_COMMAND, inferenceHostServiceChildEnvironment, isWindowsSubsystemForLinux, inferenceHostServiceManifestPath, inferenceHostServiceDesiredPath, inferenceHostServiceLogPath, inferenceHostServiceRuntimePath, xmlEscape, plistEscape, systemdQuote, defaultRunCommand, managerName, assertRuntimeEnvironment, withoutConcurrencyLimit, assertServicePath, manifestGeneration, assertWorker, assertManifest, assertServiceRuntimeState, assertDesiredState, readInferenceHostServiceManifest, readManifestAcrossAtomicReplacement, readInferenceHostServiceRuntime, readRuntimeAcrossAtomicReplacement, readInferenceHostServiceDesired, readDesiredAcrossAtomicReplacement, writeDesired, runtimeEnvironment, serviceArguments, windowsOwnedCommandLine, vbScriptString, windowsServiceLauncher, windowsTaskXml, systemdUnit, launchAgentPlist, sameWorker, sameServiceDefinition, sameWorkerSet, InferenceHostServiceManager, appendServiceLog, spawnInferenceHostServiceChild, runInferenceHostServiceSupervisor;
30916
30969
  var init_service = __esm({
30917
30970
  "lib/inference-host/service.ts"() {
30918
30971
  "use strict";
@@ -30921,6 +30974,7 @@ var init_service = __esm({
30921
30974
  SYSTEMD_UNIT = "vtx-inference-host.service";
30922
30975
  LAUNCHD_LABEL = "com.vtxmacro.inference-host";
30923
30976
  SERVICE_COOPERATIVE_STOP_SECONDS = 75;
30977
+ INFERENCE_HOST_SERVICE_DRAIN_COMMAND = "vtx-inference-host-service-drain-v1";
30924
30978
  inferenceHostServiceChildEnvironment = (runtimeEnvironment2, inheritedEnvironment = process.env) => {
30925
30979
  const environment = { ...inheritedEnvironment };
30926
30980
  for (const key of Object.keys(environment)) {
@@ -30928,7 +30982,11 @@ var init_service = __esm({
30928
30982
  delete environment[key];
30929
30983
  }
30930
30984
  }
30931
- return { ...environment, ...runtimeEnvironment2 };
30985
+ return {
30986
+ ...environment,
30987
+ ...runtimeEnvironment2,
30988
+ VTX_INFERENCE_HOST_SERVICE_CHILD: "1"
30989
+ };
30932
30990
  };
30933
30991
  isWindowsSubsystemForLinux = (env = process.env, kernelRelease) => Boolean(
30934
30992
  String(env.WSL_INTEROP || "").trim() || String(env.WSL_DISTRO_NAME || "").trim() || /microsoft/iu.test(kernelRelease ?? (() => {
@@ -30942,6 +31000,7 @@ var init_service = __esm({
30942
31000
  inferenceHostServiceManifestPath = (config2) => `${config2.supervisorStatePath}.service.json`;
30943
31001
  inferenceHostServiceDesiredPath = (config2) => `${config2.supervisorStatePath}.service-desired.json`;
30944
31002
  inferenceHostServiceLogPath = (config2) => `${config2.supervisorStatePath}.service.log`;
31003
+ inferenceHostServiceRuntimePath = (config2) => `${config2.supervisorStatePath}.service-runtime.json`;
30945
31004
  xmlEscape = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
30946
31005
  plistEscape = xmlEscape;
30947
31006
  systemdQuote = (value) => `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("%", "%%")}"`;
@@ -30992,6 +31051,7 @@ var init_service = __esm({
30992
31051
  }
30993
31052
  return value;
30994
31053
  };
31054
+ manifestGeneration = (value, installedAt) => typeof value === "string" && /^[A-Za-z0-9._:-]{1,160}$/u.test(value) ? value : `legacy:${installedAt}`;
30995
31055
  assertWorker = (value) => {
30996
31056
  if (!value || typeof value !== "object" || Array.isArray(value)) {
30997
31057
  throw new Error("Inference-host service worker is invalid.");
@@ -31032,6 +31092,7 @@ var init_service = __esm({
31032
31092
  });
31033
31093
  return {
31034
31094
  schema_version: "vtx_inference_service_v3",
31095
+ generation: manifestGeneration(void 0, legacy.installed_at),
31035
31096
  installed_at: legacy.installed_at,
31036
31097
  executable: assertServicePath(legacy.executable),
31037
31098
  script: assertServicePath(legacy.script),
@@ -31065,6 +31126,7 @@ var init_service = __esm({
31065
31126
  }
31066
31127
  return {
31067
31128
  schema_version: "vtx_inference_service_v3",
31129
+ generation: manifestGeneration(void 0, legacy.installed_at),
31068
31130
  installed_at: legacy.installed_at,
31069
31131
  executable: assertServicePath(legacy.executable),
31070
31132
  script: assertServicePath(legacy.script),
@@ -31082,6 +31144,34 @@ var init_service = __esm({
31082
31144
  if (new Set(workers.map((worker) => worker.instance_name)).size !== workers.length) {
31083
31145
  throw new Error("Inference-host service worker names must be unique.");
31084
31146
  }
31147
+ return {
31148
+ ...record2,
31149
+ generation: manifestGeneration(record2.generation, String(record2.installed_at)),
31150
+ workers
31151
+ };
31152
+ };
31153
+ assertServiceRuntimeState = (value) => {
31154
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
31155
+ throw new Error("Inference-host service runtime state is invalid.");
31156
+ }
31157
+ const record2 = value;
31158
+ if (record2.schema_version !== "vtx_inference_service_runtime_v1" || typeof record2.manifest_generation !== "string" || record2.rejected_manifest_generation !== void 0 && record2.rejected_manifest_generation !== null && typeof record2.rejected_manifest_generation !== "string" || typeof record2.updated_at !== "string" || !Number.isFinite(Date.parse(record2.updated_at)) || !Array.isArray(record2.workers)) {
31159
+ throw new Error("Inference-host service runtime state is invalid.");
31160
+ }
31161
+ const workers = record2.workers.map((worker) => {
31162
+ if (!worker || typeof worker !== "object" || Array.isArray(worker)) {
31163
+ throw new Error("Inference-host service worker runtime state is invalid.");
31164
+ }
31165
+ const item = worker;
31166
+ if (typeof item.instance_name !== "string" || !["starting", "running", "failed", "draining"].includes(String(item.state)) || item.error !== null && typeof item.error !== "string") {
31167
+ throw new Error("Inference-host service worker runtime state is invalid.");
31168
+ }
31169
+ return {
31170
+ instance_name: item.instance_name,
31171
+ state: item.state,
31172
+ error: item.error
31173
+ };
31174
+ });
31085
31175
  return { ...record2, workers };
31086
31176
  };
31087
31177
  assertDesiredState = (value) => {
@@ -31104,6 +31194,42 @@ var init_service = __esm({
31104
31194
  throw error48;
31105
31195
  }
31106
31196
  };
31197
+ readManifestAcrossAtomicReplacement = async (path) => {
31198
+ for (let attempt = 0; attempt < 3; attempt += 1) {
31199
+ try {
31200
+ return await readInferenceHostServiceManifest(path);
31201
+ } catch (error48) {
31202
+ if (!(error48 instanceof Error) || !error48.message.includes("changed while it was opened")) {
31203
+ throw error48;
31204
+ }
31205
+ }
31206
+ }
31207
+ return await readInferenceHostServiceManifest(path);
31208
+ };
31209
+ readInferenceHostServiceRuntime = async (path) => {
31210
+ const raw = await readInferencePrivateFile(path, "Inference-host service runtime state");
31211
+ if (raw === null) return null;
31212
+ try {
31213
+ return assertServiceRuntimeState(JSON.parse(raw));
31214
+ } catch (error48) {
31215
+ if (error48 instanceof SyntaxError) {
31216
+ throw new Error("Inference-host service runtime state is not valid JSON.");
31217
+ }
31218
+ throw error48;
31219
+ }
31220
+ };
31221
+ readRuntimeAcrossAtomicReplacement = async (path) => {
31222
+ for (let attempt = 0; attempt < 3; attempt += 1) {
31223
+ try {
31224
+ return await readInferenceHostServiceRuntime(path);
31225
+ } catch (error48) {
31226
+ if (!(error48 instanceof Error) || !error48.message.includes("changed while it was opened")) {
31227
+ throw error48;
31228
+ }
31229
+ }
31230
+ }
31231
+ return await readInferenceHostServiceRuntime(path);
31232
+ };
31107
31233
  readInferenceHostServiceDesired = async (path) => {
31108
31234
  const raw = await readInferencePrivateFile(path, "Inference-host service desired state");
31109
31235
  if (raw === null) return false;
@@ -31226,6 +31352,9 @@ WantedBy=default.target
31226
31352
  <key>StandardErrorPath</key><string>${plistEscape(logPath)}</string>
31227
31353
  </dict></plist>
31228
31354
  `;
31355
+ sameWorker = (left, right) => JSON.stringify(left) === JSON.stringify(right);
31356
+ sameServiceDefinition = (left, right) => left.executable === right.executable && left.script === right.script && left.log_path === right.log_path;
31357
+ sameWorkerSet = (left, right) => left.workers.length === right.workers.length && left.workers.every((worker, index) => sameWorker(worker, right.workers[index]));
31229
31358
  InferenceHostServiceManager = class {
31230
31359
  constructor(config2, dependencies = {}) {
31231
31360
  this.config = config2;
@@ -31242,6 +31371,8 @@ WantedBy=default.target
31242
31371
  });
31243
31372
  this.stopWaitAttempts = dependencies.stopWaitAttempts ?? SERVICE_COOPERATIVE_STOP_SECONDS * 4;
31244
31373
  this.startWaitAttempts = dependencies.startWaitAttempts ?? 40;
31374
+ this.reconcileWaitAttempts = dependencies.reconcileWaitAttempts ?? SERVICE_COOPERATIVE_STOP_SECONDS * 4;
31375
+ this.confirmInitialReadiness = dependencies.confirmInitialReadiness ?? true;
31245
31376
  this.acquireProcessLock = dependencies.acquireProcessLock ?? acquireInferenceHostProcessLock;
31246
31377
  managerName(this.platform);
31247
31378
  if (dependencies.platform === void 0 && this.platform === "linux" && isWindowsSubsystemForLinux()) {
@@ -31259,6 +31390,9 @@ WantedBy=default.target
31259
31390
  logPath() {
31260
31391
  return inferenceHostServiceLogPath(this.config);
31261
31392
  }
31393
+ runtimePath() {
31394
+ return inferenceHostServiceRuntimePath(this.config);
31395
+ }
31262
31396
  controlLockPath() {
31263
31397
  return `${this.config.supervisorProcessLockPath}.service-control`;
31264
31398
  }
@@ -31344,7 +31478,77 @@ WantedBy=default.target
31344
31478
  }
31345
31479
  throw new Error("Background service did not reach an active state within 10 seconds.");
31346
31480
  }
31347
- async registerManifestUnlocked(manifest, desiredRunning) {
31481
+ async restoreRunningSupervisor(manifest) {
31482
+ const restoredManifest = assertManifest({
31483
+ ...manifest,
31484
+ generation: randomUUID(),
31485
+ installed_at: this.now().toISOString()
31486
+ });
31487
+ await writeAtomicInferencePrivateFile(
31488
+ this.manifestPath(),
31489
+ `${JSON.stringify(restoredManifest, null, 2)}
31490
+ `
31491
+ );
31492
+ await writeDesired(this.desiredPath(), true, this.now());
31493
+ const result2 = await this.managerCommand("start");
31494
+ if (result2.exitCode !== 0 && !/already running|in progress|already loaded|service is already loaded/iu.test(`${result2.stdout}
31495
+ ${result2.stderr}`)) {
31496
+ throw new Error(`Background service restoration failed: ${result2.stderr.trim()}`);
31497
+ }
31498
+ if (this.platform === "darwin") {
31499
+ const domain2 = `gui/${typeof process.getuid === "function" ? process.getuid() : 0}`;
31500
+ const kicked = await this.runCommand("launchctl", [
31501
+ "kickstart",
31502
+ `${domain2}/${LAUNCHD_LABEL}`
31503
+ ]);
31504
+ if (kicked.exitCode !== 0) {
31505
+ throw new Error(`Background service restoration kickstart failed: ${kicked.stderr.trim()}`);
31506
+ }
31507
+ }
31508
+ await this.waitForManagerActive();
31509
+ if (!this.confirmInitialReadiness) return await this.status();
31510
+ const runtime = await this.waitForManifestApplied(restoredManifest);
31511
+ return await this.status(runtime);
31512
+ }
31513
+ async waitForManifestApplied(manifest, targetInstanceName) {
31514
+ let consecutiveReadyObservations = 0;
31515
+ for (let attempt = 0; attempt < this.reconcileWaitAttempts; attempt += 1) {
31516
+ let runtime = null;
31517
+ try {
31518
+ runtime = await readRuntimeAcrossAtomicReplacement(this.runtimePath());
31519
+ } catch (error48) {
31520
+ if (!(error48 instanceof Error) || !error48.message.includes("changed while it was opened")) {
31521
+ throw error48;
31522
+ }
31523
+ }
31524
+ if (runtime?.manifest_generation === manifest.generation) {
31525
+ const configuredNames = manifest.workers.map((worker) => worker.instance_name).sort();
31526
+ const runtimeNames = runtime.workers.map((worker) => worker.instance_name).sort();
31527
+ const exactWorkerSet = JSON.stringify(configuredNames) === JSON.stringify(runtimeNames);
31528
+ const target = targetInstanceName ? runtime.workers.find((worker) => worker.instance_name === targetInstanceName) : null;
31529
+ const allReady = runtime.workers.every((worker) => worker.state === "running");
31530
+ if (exactWorkerSet && (targetInstanceName ? target?.state === "running" : allReady)) {
31531
+ consecutiveReadyObservations += 1;
31532
+ if (consecutiveReadyObservations >= 2) return runtime;
31533
+ } else {
31534
+ consecutiveReadyObservations = 0;
31535
+ }
31536
+ if (exactWorkerSet && target?.state === "failed") {
31537
+ throw new Error(`Inference-host worker ${targetInstanceName} failed before readiness.`);
31538
+ }
31539
+ } else {
31540
+ consecutiveReadyObservations = 0;
31541
+ }
31542
+ if (runtime?.rejected_manifest_generation === manifest.generation) {
31543
+ throw new Error("Inference-host supervisor rejected the updated worker set.");
31544
+ }
31545
+ await this.sleep(250);
31546
+ }
31547
+ throw new Error(
31548
+ targetInstanceName ? `Inference-host supervisor did not confirm worker ${targetInstanceName} ready before timeout.` : "Inference-host supervisor did not confirm the updated worker set before timeout."
31549
+ );
31550
+ }
31551
+ async registerManifestUnlocked(manifest, desiredRunning, targetInstanceName) {
31348
31552
  const args = serviceArguments(manifest.script, this.manifestPath());
31349
31553
  const definition = this.platform === "win32" ? windowsTaskXml(this.windowsLauncherPath(), this.windowsDirectory, this.username) : this.platform === "darwin" ? launchAgentPlist(manifest.executable, args, manifest.log_path) : systemdUnit(manifest.executable, args);
31350
31554
  let managerInstallAttempted = false;
@@ -31380,6 +31584,9 @@ ${started.stderr}`)) {
31380
31584
  throw new Error(`Background service start failed: ${started.stderr.trim()}`);
31381
31585
  }
31382
31586
  await this.waitForManagerActive();
31587
+ if (this.confirmInitialReadiness) {
31588
+ await this.waitForManifestApplied(manifest, targetInstanceName);
31589
+ }
31383
31590
  }
31384
31591
  return await this.status();
31385
31592
  } catch (error48) {
@@ -31404,22 +31611,80 @@ ${cleanup.stderr}`)) {
31404
31611
  }
31405
31612
  await rm4(this.manifestPath(), { force: true }).catch(() => void 0);
31406
31613
  await rm4(this.desiredPath(), { force: true }).catch(() => void 0);
31614
+ await rm4(this.runtimePath(), { force: true }).catch(() => void 0);
31407
31615
  if (this.platform === "linux") {
31408
31616
  await this.runCommand("systemctl", ["--user", "daemon-reload"]).catch(() => void 0);
31409
31617
  }
31410
31618
  throw error48;
31411
31619
  }
31412
31620
  }
31413
- async replaceManifestUnlocked(next, desiredRunning) {
31621
+ async replaceManifestUnlocked(next, desiredRunning, targetInstanceName) {
31414
31622
  const previous = await readInferenceHostServiceManifest(this.manifestPath());
31415
31623
  const previousDesired = previous ? await readInferenceHostServiceDesired(this.desiredPath()) : false;
31416
- if (previous) await this.uninstallUnlocked();
31624
+ if (previous && sameServiceDefinition(previous, next) && sameWorkerSet(previous, next) && previousDesired === desiredRunning) {
31625
+ return await this.status();
31626
+ }
31627
+ if (previous && previousDesired && desiredRunning && sameServiceDefinition(previous, next)) {
31628
+ const current = await this.status();
31629
+ const runtime = await readRuntimeAcrossAtomicReplacement(this.runtimePath()).catch(() => null);
31630
+ if (current.manager_active && runtime?.manifest_generation === previous.generation) {
31631
+ await writeAtomicInferencePrivateFile(
31632
+ this.manifestPath(),
31633
+ `${JSON.stringify(next, null, 2)}
31634
+ `
31635
+ );
31636
+ if (!this.confirmInitialReadiness) return await this.status();
31637
+ try {
31638
+ const confirmedRuntime = await this.waitForManifestApplied(next, targetInstanceName);
31639
+ return await this.status(confirmedRuntime);
31640
+ } catch (error48) {
31641
+ await writeAtomicInferencePrivateFile(
31642
+ this.manifestPath(),
31643
+ `${JSON.stringify(previous, null, 2)}
31644
+ `
31645
+ );
31646
+ try {
31647
+ await this.waitForManifestApplied(
31648
+ previous,
31649
+ targetInstanceName && previous.workers.some(
31650
+ (worker) => worker.instance_name === targetInstanceName
31651
+ ) ? targetInstanceName : void 0
31652
+ );
31653
+ } catch (rollbackError) {
31654
+ throw new Error(
31655
+ "Inference-host live reconfiguration failed and rollback was not confirmed.",
31656
+ { cause: new AggregateError([error48, rollbackError]) }
31657
+ );
31658
+ }
31659
+ throw new Error(
31660
+ "Inference-host live reconfiguration failed; the previous worker set was restored.",
31661
+ { cause: error48 }
31662
+ );
31663
+ }
31664
+ }
31665
+ }
31666
+ if (previous) {
31667
+ try {
31668
+ await this.uninstallUnlocked();
31669
+ } catch (error48) {
31670
+ throw new Error(
31671
+ "Inference-host service reconfiguration could not stop the previous supervisor; its desired state was restored.",
31672
+ { cause: error48 }
31673
+ );
31674
+ }
31675
+ }
31417
31676
  try {
31418
- return await this.registerManifestUnlocked(next, desiredRunning);
31677
+ return await this.registerManifestUnlocked(next, desiredRunning, targetInstanceName);
31419
31678
  } catch (error48) {
31420
31679
  if (!previous) throw error48;
31421
31680
  try {
31422
- await this.registerManifestUnlocked(previous, previousDesired);
31681
+ await this.registerManifestUnlocked(
31682
+ previous,
31683
+ previousDesired,
31684
+ targetInstanceName && previous.workers.some(
31685
+ (worker) => worker.instance_name === targetInstanceName
31686
+ ) ? targetInstanceName : void 0
31687
+ );
31423
31688
  } catch (rollbackError) {
31424
31689
  throw new Error(
31425
31690
  "Inference-host service reconfiguration failed and the previous supervisor could not be restored.",
@@ -31476,6 +31741,7 @@ ${cleanup.stderr}`)) {
31476
31741
  ].sort((left, right) => left.instance_name.localeCompare(right.instance_name));
31477
31742
  const manifest = assertManifest({
31478
31743
  schema_version: "vtx_inference_service_v3",
31744
+ generation: randomUUID(),
31479
31745
  installed_at: this.now().toISOString(),
31480
31746
  executable: this.executable,
31481
31747
  script: this.script,
@@ -31484,7 +31750,8 @@ ${cleanup.stderr}`)) {
31484
31750
  });
31485
31751
  return await this.replaceManifestUnlocked(
31486
31752
  manifest,
31487
- options.startImmediately !== false
31753
+ options.startImmediately !== false,
31754
+ this.config.instanceName
31488
31755
  );
31489
31756
  }
31490
31757
  async start() {
@@ -31516,41 +31783,56 @@ ${result2.stderr}`)) {
31516
31783
  if (!manifest) {
31517
31784
  throw new Error("Inference-host service is not installed.");
31518
31785
  }
31786
+ const restoreOnFailure = await readInferenceHostServiceDesired(this.desiredPath());
31519
31787
  await writeDesired(this.desiredPath(), false, this.now());
31520
- const serviceLockPath = `${this.config.supervisorProcessLockPath}.service`;
31521
- let serviceReleased = false;
31522
- for (let attempt = 0; attempt < this.stopWaitAttempts; attempt += 1) {
31523
- try {
31524
- const probe = await this.acquireProcessLock(serviceLockPath);
31525
- await probe.release();
31526
- serviceReleased = true;
31527
- break;
31528
- } catch (error48) {
31529
- if (error48 instanceof Error && error48.message.includes("Another inference host process already owns")) {
31530
- await this.sleep(250);
31531
- continue;
31788
+ try {
31789
+ const serviceLockPath = `${this.config.supervisorProcessLockPath}.service`;
31790
+ let serviceReleased = false;
31791
+ for (let attempt = 0; attempt < this.stopWaitAttempts; attempt += 1) {
31792
+ try {
31793
+ const probe = await this.acquireProcessLock(serviceLockPath);
31794
+ await probe.release();
31795
+ serviceReleased = true;
31796
+ break;
31797
+ } catch (error48) {
31798
+ if (error48 instanceof Error && error48.message.includes("Another inference host process already owns")) {
31799
+ await this.sleep(250);
31800
+ continue;
31801
+ }
31802
+ throw error48;
31532
31803
  }
31533
- throw error48;
31534
31804
  }
31535
- }
31536
- if (!serviceReleased) {
31537
- throw new Error(
31538
- `Inference-host workers did not stop cooperatively within ${SERVICE_COOPERATIVE_STOP_SECONDS} seconds; refusing forced termination while cleanup may be pending.`
31539
- );
31540
- }
31541
- const result2 = await this.managerCommand("stop");
31542
- if (result2.exitCode !== 0 && !/not running|not found|does not exist|not loaded|cannot find|no such process/iu.test(`${result2.stdout}
31805
+ if (!serviceReleased) {
31806
+ throw new Error(
31807
+ `Inference-host workers did not stop cooperatively within ${SERVICE_COOPERATIVE_STOP_SECONDS} seconds; refusing forced termination while cleanup may be pending.`
31808
+ );
31809
+ }
31810
+ const result2 = await this.managerCommand("stop");
31811
+ if (result2.exitCode !== 0 && !/not running|not found|does not exist|not loaded|cannot find|no such process/iu.test(`${result2.stdout}
31543
31812
  ${result2.stderr}`)) {
31544
- throw new Error(`Background service stop failed: ${result2.stderr.trim()}`);
31545
- }
31546
- const status = await this.status();
31547
- if (status.manager_active) {
31548
- throw new Error("Background service manager still reports the service active after stop.");
31813
+ throw new Error(`Background service stop failed: ${result2.stderr.trim()}`);
31814
+ }
31815
+ const status = await this.status();
31816
+ if (status.manager_active) {
31817
+ throw new Error("Background service manager still reports the service active after stop.");
31818
+ }
31819
+ return status;
31820
+ } catch (error48) {
31821
+ if (!restoreOnFailure) throw error48;
31822
+ try {
31823
+ await this.restoreRunningSupervisor(manifest);
31824
+ } catch (restoreError) {
31825
+ throw new Error(
31826
+ "Inference-host stop failed and the previous supervisor could not be restored.",
31827
+ { cause: new AggregateError([error48, restoreError]) }
31828
+ );
31829
+ }
31830
+ throw error48;
31549
31831
  }
31550
- return status;
31551
31832
  }
31552
- async status() {
31833
+ async status(confirmedRuntime) {
31553
31834
  const manifest = await readInferenceHostServiceManifest(this.manifestPath());
31835
+ const runtime = confirmedRuntime?.manifest_generation === manifest?.generation ? confirmedRuntime : await readRuntimeAcrossAtomicReplacement(this.runtimePath()).catch(() => null);
31554
31836
  const desired = await readInferenceHostServiceDesired(this.desiredPath());
31555
31837
  const result2 = await this.managerCommand("status");
31556
31838
  const output3 = result2.stdout.trim();
@@ -31568,7 +31850,8 @@ ${result2.stderr}`)) {
31568
31850
  display_name: worker.display_name,
31569
31851
  max_concurrency: worker.max_concurrency,
31570
31852
  authenticated_account_email: worker.authenticated_account_email,
31571
- authenticated_account_plan: worker.authenticated_account_plan
31853
+ authenticated_account_plan: worker.authenticated_account_plan,
31854
+ runtime_state: runtime?.manifest_generation === manifest.generation && managerActive ? runtime.workers.find((item) => item.instance_name === worker.instance_name)?.state ?? "unknown" : "unknown"
31572
31855
  })) ?? []
31573
31856
  };
31574
31857
  }
@@ -31606,6 +31889,7 @@ ${result2.stderr}`)) {
31606
31889
  if (remaining.length === 0) return await this.uninstallUnlocked();
31607
31890
  return await this.replaceManifestUnlocked({
31608
31891
  ...manifest,
31892
+ generation: randomUUID(),
31609
31893
  installed_at: this.now().toISOString(),
31610
31894
  workers: remaining
31611
31895
  }, await readInferenceHostServiceDesired(this.desiredPath()));
@@ -31633,6 +31917,7 @@ ${result2.stderr}`)) {
31633
31917
  if (this.platform === "linux") await this.runCommand("systemctl", ["--user", "daemon-reload"]);
31634
31918
  await rm4(this.manifestPath(), { force: true });
31635
31919
  await rm4(this.desiredPath(), { force: true });
31920
+ await rm4(this.runtimePath(), { force: true });
31636
31921
  return {
31637
31922
  installed: false,
31638
31923
  desired_running: false,
@@ -31654,7 +31939,8 @@ ${result2.stderr}`)) {
31654
31939
  `, resolvePromise);
31655
31940
  });
31656
31941
  };
31657
- spawnServiceChild = async (manifest, worker, signal) => {
31942
+ spawnInferenceHostServiceChild = async (manifest, worker, signal, onReady = () => {
31943
+ }) => {
31658
31944
  const args = [
31659
31945
  manifest.script,
31660
31946
  "inference-host",
@@ -31667,6 +31953,8 @@ ${result2.stderr}`)) {
31667
31953
  const log = createWriteStream(manifest.log_path, { flags: "a", mode: 384 });
31668
31954
  return await new Promise((resolvePromise, reject) => {
31669
31955
  let stdout = "";
31956
+ let cooperativeStopTimedOut = false;
31957
+ let cooperativeStopTimer = null;
31670
31958
  const pending = { stdout: "", stderr: "" };
31671
31959
  const writeTaggedOutput = (stream, text, flush = false) => {
31672
31960
  const lines = `${pending[stream]}${text}`.split(/\r?\n/u);
@@ -31679,6 +31967,7 @@ ${result2.stderr}`)) {
31679
31967
  try {
31680
31968
  const parsed = JSON.parse(line);
31681
31969
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
31970
+ if (parsed.event === "runtime_started") onReady();
31682
31971
  log.write(`${JSON.stringify({
31683
31972
  ...parsed,
31684
31973
  instance_name: worker.instance_name
@@ -31701,7 +31990,7 @@ ${result2.stderr}`)) {
31701
31990
  const child = spawn5(manifest.executable, args, {
31702
31991
  env: inferenceHostServiceChildEnvironment(worker.runtime_environment),
31703
31992
  windowsHide: true,
31704
- stdio: ["ignore", "pipe", "pipe"]
31993
+ stdio: ["pipe", "pipe", "pipe"]
31705
31994
  });
31706
31995
  child.stdout.on("data", (chunk) => {
31707
31996
  const text = chunk.toString("utf8");
@@ -31709,10 +31998,22 @@ ${result2.stderr}`)) {
31709
31998
  stdout = `${stdout}${text}`.slice(-65536);
31710
31999
  });
31711
32000
  child.stderr.on("data", (chunk) => writeTaggedOutput("stderr", chunk.toString("utf8")));
31712
- const onAbort = () => child.kill("SIGTERM");
31713
- signal.addEventListener("abort", onAbort, { once: true });
32001
+ child.stdin.on("error", () => {
32002
+ });
32003
+ const onAbort = () => {
32004
+ child.stdin.end(`${INFERENCE_HOST_SERVICE_DRAIN_COMMAND}
32005
+ `);
32006
+ cooperativeStopTimer = setTimeout(() => {
32007
+ cooperativeStopTimedOut = true;
32008
+ child.kill("SIGKILL");
32009
+ }, SERVICE_COOPERATIVE_STOP_SECONDS * 1e3);
32010
+ cooperativeStopTimer.unref();
32011
+ };
32012
+ if (signal.aborted) onAbort();
32013
+ else signal.addEventListener("abort", onAbort, { once: true });
31714
32014
  child.once("error", (error48) => {
31715
32015
  signal.removeEventListener("abort", onAbort);
32016
+ if (cooperativeStopTimer) clearTimeout(cooperativeStopTimer);
31716
32017
  writeTaggedOutput("stdout", "", true);
31717
32018
  writeTaggedOutput("stderr", "", true);
31718
32019
  log.end();
@@ -31720,6 +32021,7 @@ ${result2.stderr}`)) {
31720
32021
  });
31721
32022
  child.once("exit", (code) => {
31722
32023
  signal.removeEventListener("abort", onAbort);
32024
+ if (cooperativeStopTimer) clearTimeout(cooperativeStopTimer);
31723
32025
  writeTaggedOutput("stdout", "", true);
31724
32026
  writeTaggedOutput("stderr", "", true);
31725
32027
  log.end();
@@ -31739,81 +32041,231 @@ ${result2.stderr}`)) {
31739
32041
  resolvePromise({
31740
32042
  exitCode: code ?? 1,
31741
32043
  uptimeMs: Date.now() - startedAt,
31742
- drainReason
32044
+ drainReason,
32045
+ cooperativeStopTimedOut
31743
32046
  });
31744
32047
  });
31745
32048
  });
31746
32049
  };
31747
32050
  runInferenceHostServiceSupervisor = async (manifestPath, options = {}) => {
31748
- const manifest = await readInferenceHostServiceManifest(manifestPath);
31749
- if (!manifest) throw new Error("Inference-host service manifest is missing.");
32051
+ const initialManifest = await readInferenceHostServiceManifest(manifestPath);
32052
+ if (!initialManifest) throw new Error("Inference-host service manifest is missing.");
31750
32053
  const desiredPath = manifestPath.replace(/\.service\.json$/u, ".service-desired.json");
32054
+ const runtimePath = manifestPath.replace(/\.service\.json$/u, ".service-runtime.json");
31751
32055
  const signal = options.signal ?? new AbortController().signal;
31752
32056
  const sleep4 = options.sleep ?? (async (milliseconds) => {
31753
32057
  await new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds));
31754
32058
  });
31755
- const launch = options.runWorker ?? options.spawnChild ?? spawnServiceChild;
31756
- await appendServiceLog(manifest.log_path, "service_supervisor_started", {
32059
+ const launch = options.runWorker ?? options.spawnChild ?? spawnInferenceHostServiceChild;
32060
+ await appendServiceLog(initialManifest.log_path, "service_supervisor_started", {
31757
32061
  adapter: "codex",
31758
- instances: manifest.workers.map((worker) => worker.instance_name)
32062
+ instances: initialManifest.workers.map((worker) => worker.instance_name)
31759
32063
  });
31760
- const superviseWorker = async (worker) => {
32064
+ const workers = /* @__PURE__ */ new Map();
32065
+ let appliedManifest = initialManifest;
32066
+ let rejectedManifestGeneration = null;
32067
+ let runtimeWriteChain = Promise.resolve();
32068
+ const persistRuntime = async () => {
32069
+ const state = {
32070
+ schema_version: "vtx_inference_service_runtime_v1",
32071
+ manifest_generation: appliedManifest.generation,
32072
+ rejected_manifest_generation: rejectedManifestGeneration,
32073
+ updated_at: (/* @__PURE__ */ new Date()).toISOString(),
32074
+ workers: [...workers.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([instanceName, record2]) => ({
32075
+ instance_name: instanceName,
32076
+ state: record2.state,
32077
+ error: record2.error
32078
+ }))
32079
+ };
32080
+ runtimeWriteChain = runtimeWriteChain.then(async () => {
32081
+ await writeAtomicInferencePrivateFile(runtimePath, `${JSON.stringify(state, null, 2)}
32082
+ `);
32083
+ });
32084
+ await runtimeWriteChain;
32085
+ };
32086
+ const updateRecord = async (record2, state, error48 = null) => {
32087
+ if (workers.get(record2.worker.instance_name) !== record2) return;
32088
+ record2.state = state;
32089
+ record2.error = error48;
32090
+ await persistRuntime();
32091
+ };
32092
+ const waitRetry = async (milliseconds, controller) => {
32093
+ if (controller.signal.aborted || signal.aborted) return;
32094
+ let finishAbort;
32095
+ const aborted2 = new Promise((resolvePromise) => {
32096
+ finishAbort = resolvePromise;
32097
+ });
32098
+ controller.signal.addEventListener("abort", finishAbort, { once: true });
32099
+ signal.addEventListener("abort", finishAbort, { once: true });
32100
+ try {
32101
+ await Promise.race([sleep4(milliseconds), aborted2]);
32102
+ } finally {
32103
+ controller.signal.removeEventListener("abort", finishAbort);
32104
+ signal.removeEventListener("abort", finishAbort);
32105
+ }
32106
+ };
32107
+ const superviseWorker = async (serviceManifest, record2) => {
31761
32108
  let failures = 0;
31762
- while (!signal.aborted && await readDesiredAcrossAtomicReplacement(desiredPath)) {
32109
+ while (!signal.aborted && !record2.controller.signal.aborted && await readDesiredAcrossAtomicReplacement(desiredPath)) {
31763
32110
  try {
31764
- const workerController = new AbortController();
31765
- const forwardAbort = () => workerController.abort();
32111
+ await updateRecord(record2, "starting");
32112
+ const attemptController = new AbortController();
32113
+ const forwardAbort = () => attemptController.abort();
31766
32114
  signal.addEventListener("abort", forwardAbort, { once: true });
31767
- let workerComplete = false;
31768
- let monitorError = null;
31769
- const desiredMonitor = (async () => {
31770
- while (!workerComplete && !workerController.signal.aborted) {
31771
- await sleep4(500);
31772
- if (!await readDesiredAcrossAtomicReplacement(desiredPath)) {
31773
- workerController.abort();
31774
- break;
31775
- }
31776
- }
31777
- })().catch((error48) => {
31778
- monitorError = error48;
31779
- workerController.abort();
31780
- });
32115
+ record2.controller.signal.addEventListener("abort", forwardAbort, { once: true });
31781
32116
  let result2;
32117
+ let launchSettled = false;
32118
+ let readinessTask = Promise.resolve();
32119
+ let readinessSignalled = false;
31782
32120
  try {
31783
- result2 = await launch(manifest, worker, workerController.signal);
32121
+ result2 = await launch(
32122
+ serviceManifest,
32123
+ record2.worker,
32124
+ attemptController.signal,
32125
+ () => {
32126
+ if (readinessSignalled) return;
32127
+ readinessSignalled = true;
32128
+ readinessTask = (async () => {
32129
+ await sleep4(500);
32130
+ if (!launchSettled && !attemptController.signal.aborted && !record2.controller.signal.aborted && !signal.aborted) {
32131
+ await updateRecord(record2, "running");
32132
+ }
32133
+ })();
32134
+ }
32135
+ );
31784
32136
  } finally {
31785
- workerComplete = true;
31786
- workerController.abort();
32137
+ launchSettled = true;
32138
+ attemptController.abort();
31787
32139
  signal.removeEventListener("abort", forwardAbort);
31788
- await desiredMonitor;
32140
+ record2.controller.signal.removeEventListener("abort", forwardAbort);
32141
+ await readinessTask;
31789
32142
  }
31790
- if (monitorError) throw monitorError;
31791
- if (signal.aborted || !await readDesiredAcrossAtomicReplacement(desiredPath)) break;
32143
+ if (result2.cooperativeStopTimedOut) {
32144
+ await updateRecord(record2, "failed", "worker_cooperative_stop_timeout");
32145
+ await appendServiceLog(serviceManifest.log_path, "worker_cooperative_stop_timeout", {
32146
+ instance_name: record2.worker.instance_name
32147
+ });
32148
+ throw new Error("worker_cooperative_stop_timeout");
32149
+ }
32150
+ if (signal.aborted || record2.controller.signal.aborted || !await readDesiredAcrossAtomicReplacement(desiredPath)) break;
31792
32151
  failures = result2.uptimeMs >= 6e4 ? 0 : failures + 1;
31793
32152
  const retryAfterMs = Math.min(1e3 * 2 ** Math.min(failures, 5), 3e4);
31794
- await appendServiceLog(manifest.log_path, "worker_exited", {
31795
- instance_name: worker.instance_name,
32153
+ await updateRecord(record2, "failed", "worker_exited");
32154
+ await appendServiceLog(serviceManifest.log_path, "worker_exited", {
32155
+ instance_name: record2.worker.instance_name,
31796
32156
  exit_code: result2.exitCode,
31797
32157
  uptime_ms: result2.uptimeMs,
31798
32158
  drain_reason: result2.drainReason ?? null,
31799
32159
  retry_after_ms: retryAfterMs
31800
32160
  });
31801
- await sleep4(retryAfterMs);
32161
+ await waitRetry(retryAfterMs, record2.controller);
31802
32162
  } catch (error48) {
31803
- if (signal.aborted) break;
32163
+ if (error48 instanceof Error && error48.message === "worker_cooperative_stop_timeout") throw error48;
32164
+ if (signal.aborted || record2.controller.signal.aborted) break;
31804
32165
  failures += 1;
31805
32166
  const retryAfterMs = Math.min(1e3 * 2 ** Math.min(failures, 5), 3e4);
31806
- await appendServiceLog(manifest.log_path, "worker_launch_failed", {
31807
- instance_name: worker.instance_name,
32167
+ await updateRecord(record2, "failed", "worker_launch_failed");
32168
+ await appendServiceLog(serviceManifest.log_path, "worker_launch_failed", {
32169
+ instance_name: record2.worker.instance_name,
31808
32170
  error: error48 instanceof Error ? error48.message : "unknown",
31809
32171
  retry_after_ms: retryAfterMs
31810
32172
  });
31811
- await sleep4(retryAfterMs);
32173
+ await waitRetry(retryAfterMs, record2.controller);
32174
+ }
32175
+ }
32176
+ };
32177
+ const startWorker = (serviceManifest, worker) => {
32178
+ const record2 = {
32179
+ worker,
32180
+ controller: new AbortController(),
32181
+ promise: Promise.resolve(),
32182
+ state: "starting",
32183
+ error: null
32184
+ };
32185
+ workers.set(worker.instance_name, record2);
32186
+ record2.promise = superviseWorker(serviceManifest, record2);
32187
+ };
32188
+ const reconcile = async (next) => {
32189
+ const nextByName = new Map(next.workers.map((worker) => [worker.instance_name, worker]));
32190
+ const retiring = [...workers.values()].filter((record2) => {
32191
+ const nextWorker = nextByName.get(record2.worker.instance_name);
32192
+ return !nextWorker || !sameWorker(record2.worker, nextWorker);
32193
+ });
32194
+ for (const record2 of retiring) {
32195
+ await updateRecord(record2, "draining");
32196
+ record2.controller.abort();
32197
+ }
32198
+ const settled = await Promise.allSettled(retiring.map((record2) => record2.promise));
32199
+ const failedDrain = settled.find(
32200
+ (result2) => result2.status === "rejected"
32201
+ );
32202
+ if (failedDrain) {
32203
+ for (const record2 of retiring) workers.delete(record2.worker.instance_name);
32204
+ for (const worker of appliedManifest.workers) {
32205
+ if (!workers.has(worker.instance_name)) startWorker(appliedManifest, worker);
32206
+ }
32207
+ rejectedManifestGeneration = next.generation;
32208
+ await persistRuntime();
32209
+ await appendServiceLog(appliedManifest.log_path, "service_manifest_rejected", {
32210
+ manifest_generation: next.generation,
32211
+ reason: failedDrain.reason instanceof Error ? failedDrain.reason.message : "worker_cooperative_stop_failed"
32212
+ });
32213
+ return;
32214
+ }
32215
+ for (const record2 of retiring) workers.delete(record2.worker.instance_name);
32216
+ for (const worker of next.workers) {
32217
+ if (!workers.has(worker.instance_name)) startWorker(next, worker);
32218
+ }
32219
+ appliedManifest = next;
32220
+ rejectedManifestGeneration = null;
32221
+ await persistRuntime();
32222
+ await appendServiceLog(next.log_path, "service_manifest_applied", {
32223
+ manifest_generation: next.generation,
32224
+ instances: next.workers.map((worker) => worker.instance_name)
32225
+ });
32226
+ };
32227
+ const drainWorkers = async () => {
32228
+ const draining = [...workers.values()];
32229
+ for (const record2 of draining) {
32230
+ await updateRecord(record2, "draining");
32231
+ record2.controller.abort();
32232
+ }
32233
+ const settled = await Promise.allSettled(draining.map((record2) => record2.promise));
32234
+ const failedDrain = settled.find(
32235
+ (result2) => result2.status === "rejected"
32236
+ );
32237
+ if (failedDrain) throw failedDrain.reason;
32238
+ for (const record2 of draining) {
32239
+ if (workers.get(record2.worker.instance_name) === record2) {
32240
+ workers.delete(record2.worker.instance_name);
31812
32241
  }
31813
32242
  }
32243
+ await persistRuntime();
31814
32244
  };
31815
- await Promise.all(manifest.workers.map(superviseWorker));
31816
- await appendServiceLog(manifest.log_path, "service_supervisor_stopped");
32245
+ await reconcile(initialManifest);
32246
+ try {
32247
+ while (!signal.aborted) {
32248
+ while (!signal.aborted && await readDesiredAcrossAtomicReplacement(desiredPath)) {
32249
+ const next = await readManifestAcrossAtomicReplacement(manifestPath);
32250
+ if (!next) throw new Error("Inference-host service manifest is missing.");
32251
+ if (next.generation !== appliedManifest.generation && next.generation !== rejectedManifestGeneration) await reconcile(next);
32252
+ await sleep4(250);
32253
+ }
32254
+ await drainWorkers();
32255
+ if (signal.aborted || !await readDesiredAcrossAtomicReplacement(desiredPath)) break;
32256
+ const restored = await readManifestAcrossAtomicReplacement(manifestPath);
32257
+ if (!restored) throw new Error("Inference-host service manifest is missing.");
32258
+ await reconcile(restored);
32259
+ await appendServiceLog(restored.log_path, "service_desired_state_restored", {
32260
+ manifest_generation: restored.generation,
32261
+ instances: restored.workers.map((worker) => worker.instance_name)
32262
+ });
32263
+ }
32264
+ } finally {
32265
+ await drainWorkers();
32266
+ await runtimeWriteChain;
32267
+ await appendServiceLog(appliedManifest.log_path, "service_supervisor_stopped");
32268
+ }
31817
32269
  };
31818
32270
  }
31819
32271
  });
@@ -31822,9 +32274,10 @@ ${result2.stderr}`)) {
31822
32274
  var cli_exports = {};
31823
32275
  __export(cli_exports, {
31824
32276
  INFERENCE_HOST_CLI_VERSION: () => INFERENCE_HOST_CLI_VERSION,
32277
+ registerInferenceHostServiceControlInput: () => registerInferenceHostServiceControlInput,
31825
32278
  runInferenceHostCli: () => runInferenceHostCli
31826
32279
  });
31827
- import { randomUUID } from "node:crypto";
32280
+ import { randomUUID as randomUUID2 } from "node:crypto";
31828
32281
  import { spawn as spawn6 } from "node:child_process";
31829
32282
  import { lstat as lstat4, realpath as realpath4, rm as rm5 } from "node:fs/promises";
31830
32283
  import { join as join6, resolve as resolve5 } from "node:path";
@@ -31902,7 +32355,7 @@ async function runInferenceHostCli(argv2, env = process.env, dependencies = {})
31902
32355
  };
31903
32356
  }
31904
32357
  }
31905
- var INFERENCE_HOST_CLI_VERSION, runtimeReceiptPath, codexRecoveryPath, codexGuardianReceiptRoot, revocationCheckpointPath, foregroundHostLockPath, AGENT_HEARTBEAT_INTERVAL_MS, AGENT_HEARTBEAT_MAX_RETRY_DELAY_MS, retryableAgentHeartbeatError, assertRevocationCheckpoint, readRevocationCheckpoint, writeRevocationCheckpoint, clearRevocationCheckpoint, render, INFERENCE_HOST_HELP, parseHostConcurrency, parseInferenceHostArgs, defaultOpenBrowser, defaultRegisterLifecycleSignalHandlers, lifecycleCancellation, configuredCredentialStore, accountKeyForState, assertCredentialMatchesState2, revocationCheckpointForCredential, assertRevocationCheckpointMatchesLocalIdentity, fileExistsPrivately, inspectCodexAuthentication, clearLocalRuntimeArtifacts, assertDurableServiceUninstalled, assertLocalRuntimeArtifactsMayBeDiscarded, defaultRunForeground, login, codexLogin, codexLogout, localStatus, doctor, cleanupLogin, runHost, defaultReadStdin, parseAgentStdin, agentOperationId, agentSession, agentConnect, agentRun, agentNext, agentComplete, agentFail, withAgentCommandLock, serviceCommand, hasExplicitCredentialStoreConfiguration, commandUsesInstalledServiceCredentials, resolveInferenceHostCommandConfig;
32358
+ var INFERENCE_HOST_CLI_VERSION, runtimeReceiptPath, codexRecoveryPath, codexGuardianReceiptRoot, revocationCheckpointPath, foregroundHostLockPath, AGENT_HEARTBEAT_INTERVAL_MS, AGENT_HEARTBEAT_MAX_RETRY_DELAY_MS, retryableAgentHeartbeatError, assertRevocationCheckpoint, readRevocationCheckpoint, writeRevocationCheckpoint, clearRevocationCheckpoint, render, INFERENCE_HOST_HELP, parseHostConcurrency, parseInferenceHostArgs, defaultOpenBrowser, registerInferenceHostServiceControlInput, defaultRegisterLifecycleSignalHandlers, lifecycleCancellation, configuredCredentialStore, accountKeyForState, assertCredentialMatchesState2, revocationCheckpointForCredential, assertRevocationCheckpointMatchesLocalIdentity, fileExistsPrivately, inspectCodexAuthentication, clearLocalRuntimeArtifacts, assertDurableServiceUninstalled, assertLocalRuntimeArtifactsMayBeDiscarded, defaultRunForeground, login, codexLogin, codexLogout, localStatus, doctor, cleanupLogin, runHost, defaultReadStdin, parseAgentStdin, agentOperationId, agentSession, agentConnect, agentRun, agentNext, agentComplete, agentFail, withAgentCommandLock, serviceCommand, hasExplicitCredentialStoreConfiguration, commandUsesInstalledServiceCredentials, resolveInferenceHostCommandConfig;
31906
32359
  var init_cli = __esm({
31907
32360
  "lib/inference-host/cli.ts"() {
31908
32361
  "use strict";
@@ -32147,14 +32600,35 @@ Durable service:
32147
32600
  });
32148
32601
  child.unref();
32149
32602
  };
32603
+ registerInferenceHostServiceControlInput = (input, abort) => {
32604
+ let pendingControlInput = "";
32605
+ const onControlInput = (chunk) => {
32606
+ pendingControlInput = `${pendingControlInput}${chunk.toString()}`.slice(-4096);
32607
+ const lines = pendingControlInput.split(/\r?\n/u);
32608
+ pendingControlInput = lines.pop() ?? "";
32609
+ if (lines.some((line) => line === INFERENCE_HOST_SERVICE_DRAIN_COMMAND)) {
32610
+ abort();
32611
+ }
32612
+ };
32613
+ input.on("data", onControlInput);
32614
+ input.resume();
32615
+ return () => {
32616
+ input.off("data", onControlInput);
32617
+ input.pause();
32618
+ };
32619
+ };
32150
32620
  defaultRegisterLifecycleSignalHandlers = (abort) => {
32151
32621
  const onSigint = () => abort("SIGINT");
32152
32622
  const onSigterm = () => abort("SIGTERM");
32153
32623
  process.on("SIGINT", onSigint);
32154
32624
  process.on("SIGTERM", onSigterm);
32625
+ const serviceChild = process.env.VTX_INFERENCE_HOST_SERVICE_CHILD === "1";
32626
+ const unregisterControlInput = serviceChild ? registerInferenceHostServiceControlInput(process.stdin, onSigterm) : () => {
32627
+ };
32155
32628
  return () => {
32156
32629
  process.off("SIGINT", onSigint);
32157
32630
  process.off("SIGTERM", onSigterm);
32631
+ unregisterControlInput();
32158
32632
  };
32159
32633
  };
32160
32634
  lifecycleCancellation = (dependencies) => {
@@ -32321,7 +32795,7 @@ Durable service:
32321
32795
  throw new Error("Inference host credential recovery is required before login.");
32322
32796
  }
32323
32797
  const keyPair = generateExternalInferenceEnvelopeKeyPair();
32324
- const hostId = randomUUID();
32798
+ const hostId = randomUUID2();
32325
32799
  const beginLogin = dependencies.beginLogin ?? beginInferenceOAuthLogin;
32326
32800
  const pending = await beginLogin({
32327
32801
  apiUrl: config2.apiUrl,
@@ -32544,6 +33018,7 @@ Waiting for approval...
32544
33018
  registered: receipt.registered,
32545
33019
  advertisement_generation: receipt.advertisement_generation,
32546
33020
  pending_attempts: Object.keys(receipt.attempts).length + (receipt.pending_claim_request ? 1 : 0),
33021
+ recovery: summarizeInferenceHostRuntimeRecovery(receipt),
32547
33022
  updated_at: receipt.updated_at
32548
33023
  } : null,
32549
33024
  agent_attempt: agentAttempt ? {
@@ -32855,7 +33330,7 @@ Waiting for approval...
32855
33330
  }
32856
33331
  return record2;
32857
33332
  };
32858
- agentOperationId = (kind) => `${kind}-${randomUUID()}`;
33333
+ agentOperationId = (kind) => `${kind}-${randomUUID2()}`;
32859
33334
  agentSession = async (config2, dependencies, warnings, signal) => {
32860
33335
  if (await readRevocationCheckpoint(config2)) {
32861
33336
  throw new Error("Inference host revocation recovery must finish first.");
@@ -33503,7 +33978,7 @@ var init_types = __esm({
33503
33978
  });
33504
33979
 
33505
33980
  // lib/agent-core/client.ts
33506
- import { randomUUID as randomUUID2 } from "node:crypto";
33981
+ import { randomUUID as randomUUID3 } from "node:crypto";
33507
33982
  function normalizeApiUrl(value) {
33508
33983
  const parsed = String(value || "").trim();
33509
33984
  if (!parsed) {
@@ -33765,7 +34240,7 @@ var init_client = __esm({
33765
34240
  return this.request("/trading/ai/runtime/decision", {
33766
34241
  method: "POST",
33767
34242
  profileId,
33768
- idempotencyKey: randomUUID2(),
34243
+ idempotencyKey: randomUUID3(),
33769
34244
  headers: { "x-client-runtime-lease": leaseToken },
33770
34245
  body: payload
33771
34246
  });
@@ -33774,7 +34249,7 @@ var init_client = __esm({
33774
34249
  return this.request("/trading/ai/runtime/trade-sync", {
33775
34250
  method: "POST",
33776
34251
  profileId,
33777
- idempotencyKey: randomUUID2(),
34252
+ idempotencyKey: randomUUID3(),
33778
34253
  headers: { "x-client-runtime-lease": leaseToken },
33779
34254
  body: payload
33780
34255
  });
@@ -33783,7 +34258,7 @@ var init_client = __esm({
33783
34258
  return this.request("/trading/ai/runtime/error", {
33784
34259
  method: "POST",
33785
34260
  profileId,
33786
- idempotencyKey: randomUUID2(),
34261
+ idempotencyKey: randomUUID3(),
33787
34262
  headers: { "x-client-runtime-lease": leaseToken },
33788
34263
  body: payload
33789
34264
  });
@@ -33795,7 +34270,7 @@ var init_client = __esm({
33795
34270
  return this.request("/trading/market-order", {
33796
34271
  method: "POST",
33797
34272
  profileId,
33798
- idempotencyKey: randomUUID2(),
34273
+ idempotencyKey: randomUUID3(),
33799
34274
  body: payload
33800
34275
  });
33801
34276
  }
@@ -33803,7 +34278,7 @@ var init_client = __esm({
33803
34278
  return this.request("/trading/limit-order", {
33804
34279
  method: "POST",
33805
34280
  profileId,
33806
- idempotencyKey: randomUUID2(),
34281
+ idempotencyKey: randomUUID3(),
33807
34282
  body: payload
33808
34283
  });
33809
34284
  }
@@ -33811,7 +34286,7 @@ var init_client = __esm({
33811
34286
  return this.request("/trading/cancel-order", {
33812
34287
  method: "POST",
33813
34288
  profileId,
33814
- idempotencyKey: randomUUID2(),
34289
+ idempotencyKey: randomUUID3(),
33815
34290
  body: payload
33816
34291
  });
33817
34292
  }
@@ -33830,7 +34305,7 @@ var init_client = __esm({
33830
34305
  return this.request("/trading/ai/start", {
33831
34306
  method: "POST",
33832
34307
  profileId,
33833
- idempotencyKey: randomUUID2(),
34308
+ idempotencyKey: randomUUID3(),
33834
34309
  body: payload
33835
34310
  });
33836
34311
  }
@@ -33838,7 +34313,7 @@ var init_client = __esm({
33838
34313
  return this.request("/trading/ai/stop", {
33839
34314
  method: "POST",
33840
34315
  profileId,
33841
- idempotencyKey: randomUUID2(),
34316
+ idempotencyKey: randomUUID3(),
33842
34317
  body: {}
33843
34318
  });
33844
34319
  }
@@ -33846,7 +34321,7 @@ var init_client = __esm({
33846
34321
  return this.request("/trading/ai/assistant/start", {
33847
34322
  method: "POST",
33848
34323
  profileId,
33849
- idempotencyKey: randomUUID2(),
34324
+ idempotencyKey: randomUUID3(),
33850
34325
  body: {}
33851
34326
  });
33852
34327
  }
@@ -33854,7 +34329,7 @@ var init_client = __esm({
33854
34329
  return this.request("/trading/ai/assistant/stop", {
33855
34330
  method: "POST",
33856
34331
  profileId,
33857
- idempotencyKey: randomUUID2(),
34332
+ idempotencyKey: randomUUID3(),
33858
34333
  body: {}
33859
34334
  });
33860
34335
  }
@@ -33862,7 +34337,7 @@ var init_client = __esm({
33862
34337
  return this.request("/trading/ai/runtime/session/start", {
33863
34338
  method: "POST",
33864
34339
  profileId,
33865
- idempotencyKey: randomUUID2(),
34340
+ idempotencyKey: randomUUID3(),
33866
34341
  body: payload
33867
34342
  });
33868
34343
  }
@@ -33873,7 +34348,7 @@ var init_client = __esm({
33873
34348
  return this.request("/trading/ai/runtime/session/stop", {
33874
34349
  method: "POST",
33875
34350
  profileId,
33876
- idempotencyKey: randomUUID2(),
34351
+ idempotencyKey: randomUUID3(),
33877
34352
  body: payload
33878
34353
  });
33879
34354
  }
@@ -33890,7 +34365,7 @@ var init_client = __esm({
33890
34365
  }).request("/trading/ai/runtime/session/stop", {
33891
34366
  method: "POST",
33892
34367
  profileId,
33893
- idempotencyKey: randomUUID2(),
34368
+ idempotencyKey: randomUUID3(),
33894
34369
  body: payload
33895
34370
  });
33896
34371
  }
@@ -33899,7 +34374,7 @@ var init_client = __esm({
33899
34374
  });
33900
34375
 
33901
34376
  // lib/agent-core/headless-runtime.ts
33902
- import { randomUUID as randomUUID3 } from "node:crypto";
34377
+ import { randomUUID as randomUUID4 } from "node:crypto";
33903
34378
  import { setTimeout as sleep } from "node:timers/promises";
33904
34379
  function objectOrNull2(value) {
33905
34380
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -33997,8 +34472,8 @@ async function runAndReportLocalWorkCycle(options, state, leaseToken, context) {
33997
34472
  return Boolean(result2.decision || tradeSync || result2.afterDecision);
33998
34473
  }
33999
34474
  async function startHeadlessRuntime(options) {
34000
- const runtimeSessionId = randomUUID3();
34001
- const deviceId = String(options.deviceId || "").trim() || randomUUID3();
34475
+ const runtimeSessionId = randomUUID4();
34476
+ const deviceId = String(options.deviceId || "").trim() || randomUUID4();
34002
34477
  const startResponse = await options.client.startRuntime(options.profileId, {
34003
34478
  session_id: runtimeSessionId,
34004
34479
  device_id: deviceId,
@@ -50616,7 +51091,7 @@ var headless_local_worker_exports = {};
50616
51091
  __export(headless_local_worker_exports, {
50617
51092
  createHeadlessLocalWorker: () => createHeadlessLocalWorker
50618
51093
  });
50619
- import { randomUUID as randomUUID4 } from "node:crypto";
51094
+ import { randomUUID as randomUUID5 } from "node:crypto";
50620
51095
  function objectOrNull3(value) {
50621
51096
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
50622
51097
  }
@@ -51029,7 +51504,7 @@ function createHeadlessLocalWorker(options) {
51029
51504
  const statusMatch = errorText.match(/\b([45]\d{2})\b/);
51030
51505
  const statusCode = statusMatch ? Number(statusMatch[1]) : null;
51031
51506
  const failedInvocation = normalizeAiInvocationTelemetry({
51032
- client_invocation_id: randomUUID4(),
51507
+ client_invocation_id: randomUUID5(),
51033
51508
  use_case: "trader",
51034
51509
  role: "primary",
51035
51510
  attempt_index: 0,
@@ -51085,7 +51560,7 @@ function createHeadlessLocalWorker(options) {
51085
51560
  billable_cached_input_tokens: normalizedUsage.cached_input_tokens
51086
51561
  };
51087
51562
  const invocation = normalizeAiInvocationTelemetry({
51088
- client_invocation_id: randomUUID4(),
51563
+ client_invocation_id: randomUUID5(),
51089
51564
  use_case: "trader",
51090
51565
  role: "primary",
51091
51566
  attempt_index: 0,
@@ -51290,7 +51765,7 @@ var vtx_exports = {};
51290
51765
  __export(vtx_exports, {
51291
51766
  runVtxCli: () => runVtxCli
51292
51767
  });
51293
- import { randomUUID as randomUUID5 } from "node:crypto";
51768
+ import { randomUUID as randomUUID6 } from "node:crypto";
51294
51769
  import { spawn as spawn7 } from "node:child_process";
51295
51770
  function render2(value, json2) {
51296
51771
  if (json2) {
@@ -51704,8 +52179,8 @@ async function runVtxCli(argv2, env = process.env) {
51704
52179
  });
51705
52180
  return { exitCode: 0, stdout: render2(redactCliOutput(response2), json2), stderr: "" };
51706
52181
  }
51707
- const runtimeSessionId = randomUUID5();
51708
- const deviceId = config2.runtimeDeviceId ?? randomUUID5();
52182
+ const runtimeSessionId = randomUUID6();
52183
+ const deviceId = config2.runtimeDeviceId ?? randomUUID6();
51709
52184
  const response = await client.startRuntime(profileId, {
51710
52185
  session_id: runtimeSessionId,
51711
52186
  device_id: deviceId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtxmacro/cli",
3
- "version": "2026.8.36",
3
+ "version": "2026.8.37",
4
4
  "description": "VTX Macro CLI, MCP server, and durable subscription inference host.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",