@vtxmacro/cli 2026.8.35 → 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 +821 -206
  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.35",
41
+ package_version: "2026.8.37",
42
42
  codex_package_name: "@openai/codex",
43
43
  codex_version: "0.147.0",
44
44
  platforms: {
@@ -16619,66 +16619,86 @@ async function acquireInferenceHostProcessLock(path, dependencies = {}) {
16619
16619
  }
16620
16620
  };
16621
16621
  }
16622
- var INFERENCE_CREDENTIAL_NAMESPACE, MAX_INFERENCE_PRIVATE_FILE_BYTES, WINDOWS_PRIVATE_ACL_SCRIPT, windowsPrivateAclInvocation, runWindowsPrivateAcl, windowsAclCache, windowsAclIdentity, aclCacheKey, secureWindowsPrivatePath, invalidateWindowsAclCache, rebindHardenedWindowsAclAfterRename, rebindHardenedWindowsDirectoryAfterOwnedMutation, isDefaultWindowsPrivatePath, secureExistingWindowsPath, linuxProcessIdentity, windowsProcessIdentity, darwinProcessIdentity, runSmallIdentityCommand, cachedSystemBootIdentity, readInferenceSystemBootIdentity, defaultProcessIdentity, defaultCurrentProcessIdentity, processLockObservationCache, inferenceProcessIdentitiesMatch, DEFAULT_INFERENCE_HOST_INSTANCE, SAFE_INFERENCE_HOST_INSTANCE, requireNamedInstancePath, credentialStoreIdentity;
16622
+ var INFERENCE_CREDENTIAL_NAMESPACE, MAX_INFERENCE_PRIVATE_FILE_BYTES, WINDOWS_PRIVATE_ACL_BROKER_SCRIPT, windowsPrivateAclBrokerInvocation, WindowsPrivateAclBroker, defaultWindowsPrivateAclBroker, runWindowsPrivateAcl, windowsAclCache, windowsAclIdentity, aclCacheKey, secureWindowsPrivatePath, invalidateWindowsAclCache, rebindHardenedWindowsAclAfterRename, rebindHardenedWindowsDirectoryAfterOwnedMutation, isDefaultWindowsPrivatePath, secureExistingWindowsPath, linuxProcessIdentity, windowsProcessIdentity, darwinProcessIdentity, runSmallIdentityCommand, cachedSystemBootIdentity, readInferenceSystemBootIdentity, defaultProcessIdentity, defaultCurrentProcessIdentity, processLockObservationCache, inferenceProcessIdentitiesMatch, DEFAULT_INFERENCE_HOST_INSTANCE, SAFE_INFERENCE_HOST_INSTANCE, requireNamedInstancePath, credentialStoreIdentity;
16623
16623
  var init_config = __esm({
16624
16624
  "lib/inference-host/config.ts"() {
16625
16625
  "use strict";
16626
16626
  INFERENCE_CREDENTIAL_NAMESPACE = "vtxmacro-insights-inference";
16627
16627
  MAX_INFERENCE_PRIVATE_FILE_BYTES = 16 * 1024 * 1024;
16628
- WINDOWS_PRIVATE_ACL_SCRIPT = `$ErrorActionPreference='Stop'
16629
- $path=[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($env:VTX_PRIVATE_PATH_B64))
16630
- $kind=$env:VTX_PRIVATE_PATH_KIND
16631
- $mode=$env:VTX_PRIVATE_ACL_MODE
16632
- if(-not (Test-Path -LiteralPath $path)) { [Console]::Out.Write('missing'); exit 3 }
16633
- $current=[Security.Principal.WindowsIdentity]::GetCurrent().User
16634
- $system=New-Object Security.Principal.SecurityIdentifier('S-1-5-18')
16635
- $admins=New-Object Security.Principal.SecurityIdentifier('S-1-5-32-544')
16636
- $allowed=@($current.Value,$system.Value,$admins.Value)
16637
- if($mode -eq 'harden') {
16638
- $acl=Get-Acl -LiteralPath $path
16639
- $existingOwner=(New-Object Security.Principal.NTAccount($acl.Owner)).Translate([Security.Principal.SecurityIdentifier]).Value
16640
- if($existingOwner -ne $current.Value) { throw 'private ACL owner is invalid' }
16641
- $acl.SetAccessRuleProtection($true,$false)
16642
- foreach($existingRule in @($acl.Access)) {
16643
- [void]$acl.RemoveAccessRuleSpecific($existingRule)
16644
- }
16645
- $inherit=if($kind -eq 'directory') {
16646
- [Security.AccessControl.InheritanceFlags]'ContainerInherit,ObjectInherit'
16647
- } else { [Security.AccessControl.InheritanceFlags]::None }
16648
- foreach($sid in @($current,$system,$admins)) {
16649
- $rule=New-Object Security.AccessControl.FileSystemAccessRule(
16650
- $sid,
16651
- [Security.AccessControl.FileSystemRights]::FullControl,
16652
- $inherit,
16653
- [Security.AccessControl.PropagationFlags]::None,
16654
- [Security.AccessControl.AccessControlType]::Allow
16655
- )
16656
- [void]$acl.AddAccessRule($rule)
16657
- }
16658
- (Get-Item -LiteralPath $path).SetAccessControl($acl)
16659
- }
16660
- $actual=Get-Acl -LiteralPath $path
16661
- $owner=(New-Object Security.Principal.NTAccount($actual.Owner)).Translate([Security.Principal.SecurityIdentifier]).Value
16662
- if($owner -ne $current.Value) { throw 'private ACL owner is invalid' }
16663
- if($mode -eq 'verify-owner') { [Console]::Out.Write('ok'); exit 0 }
16664
- if(-not $actual.AreAccessRulesProtected) { throw 'private ACL inheritance is invalid' }
16665
- $userFull=$false
16666
- foreach($rule in $actual.Access) {
16667
- $sid=$rule.IdentityReference.Translate([Security.Principal.SecurityIdentifier]).Value
16668
- if($rule.IsInherited -or $allowed -notcontains $sid -or $rule.AccessControlType -ne [Security.AccessControl.AccessControlType]::Allow) {
16669
- throw 'private ACL contains an unauthorized access rule'
16670
- }
16671
- if($sid -eq $current.Value -and (($rule.FileSystemRights -band [Security.AccessControl.FileSystemRights]::FullControl) -eq [Security.AccessControl.FileSystemRights]::FullControl)) { $userFull=$true }
16672
- }
16673
- if(-not $userFull) { throw 'private ACL does not grant the current user full control' }
16674
- [Console]::Out.Write('ok')`;
16675
- windowsPrivateAclInvocation = (path, kind, mode) => ({
16628
+ WINDOWS_PRIVATE_ACL_BROKER_SCRIPT = `$ErrorActionPreference='Stop'
16629
+ $ProgressPreference='SilentlyContinue'
16630
+ while(($line=[Console]::In.ReadLine()) -ne $null) {
16631
+ $requestId=$null
16632
+ try {
16633
+ if($line.Length -lt 2 -or $line.Length -gt 65536) { throw 'invalid request size' }
16634
+ $request=$line | ConvertFrom-Json
16635
+ $names=@($request.PSObject.Properties.Name | Sort-Object)
16636
+ if(($names -join ',') -ne 'id,kind,mode,path_b64') { throw 'invalid request shape' }
16637
+ if($request.id -isnot [long] -and $request.id -isnot [int]) { throw 'invalid request id' }
16638
+ $requestId=[long]$request.id
16639
+ if($requestId -lt 1) { throw 'invalid request id' }
16640
+ $kind=[string]$request.kind
16641
+ $mode=[string]$request.mode
16642
+ if($kind -ne 'directory' -and $kind -ne 'file') { throw 'invalid path kind' }
16643
+ if($mode -ne 'harden' -and $mode -ne 'verify' -and $mode -ne 'verify-owner') { throw 'invalid ACL mode' }
16644
+ $path=[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String([string]$request.path_b64))
16645
+ if([string]::IsNullOrWhiteSpace($path) -or $path.Length -gt 32767 -or $path.Contains([char]0)) { throw 'invalid path' }
16646
+ if(-not (Test-Path -LiteralPath $path)) {
16647
+ [Console]::Out.WriteLine((@{id=$requestId;status='missing'} | ConvertTo-Json -Compress))
16648
+ continue
16649
+ }
16650
+ $current=[Security.Principal.WindowsIdentity]::GetCurrent().User
16651
+ $system=New-Object Security.Principal.SecurityIdentifier('S-1-5-18')
16652
+ $admins=New-Object Security.Principal.SecurityIdentifier('S-1-5-32-544')
16653
+ $allowed=@($current.Value,$system.Value,$admins.Value)
16654
+ if($mode -eq 'harden') {
16655
+ $acl=Get-Acl -LiteralPath $path
16656
+ $existingOwner=(New-Object Security.Principal.NTAccount($acl.Owner)).Translate([Security.Principal.SecurityIdentifier]).Value
16657
+ if($existingOwner -ne $current.Value) { throw 'private ACL owner is invalid' }
16658
+ $acl.SetAccessRuleProtection($true,$false)
16659
+ foreach($existingRule in @($acl.Access)) { [void]$acl.RemoveAccessRuleSpecific($existingRule) }
16660
+ $inherit=if($kind -eq 'directory') {
16661
+ [Security.AccessControl.InheritanceFlags]'ContainerInherit,ObjectInherit'
16662
+ } else { [Security.AccessControl.InheritanceFlags]::None }
16663
+ foreach($sid in @($current,$system,$admins)) {
16664
+ $rule=New-Object Security.AccessControl.FileSystemAccessRule(
16665
+ $sid,
16666
+ [Security.AccessControl.FileSystemRights]::FullControl,
16667
+ $inherit,
16668
+ [Security.AccessControl.PropagationFlags]::None,
16669
+ [Security.AccessControl.AccessControlType]::Allow
16670
+ )
16671
+ [void]$acl.AddAccessRule($rule)
16672
+ }
16673
+ (Get-Item -LiteralPath $path).SetAccessControl($acl)
16674
+ }
16675
+ $actual=Get-Acl -LiteralPath $path
16676
+ $owner=(New-Object Security.Principal.NTAccount($actual.Owner)).Translate([Security.Principal.SecurityIdentifier]).Value
16677
+ if($owner -ne $current.Value) { throw 'private ACL owner is invalid' }
16678
+ if($mode -ne 'verify-owner') {
16679
+ if(-not $actual.AreAccessRulesProtected) { throw 'private ACL inheritance is invalid' }
16680
+ $userFull=$false
16681
+ foreach($rule in $actual.Access) {
16682
+ $sid=$rule.IdentityReference.Translate([Security.Principal.SecurityIdentifier]).Value
16683
+ if($rule.IsInherited -or $allowed -notcontains $sid -or $rule.AccessControlType -ne [Security.AccessControl.AccessControlType]::Allow) {
16684
+ throw 'private ACL contains an unauthorized access rule'
16685
+ }
16686
+ if($sid -eq $current.Value -and (($rule.FileSystemRights -band [Security.AccessControl.FileSystemRights]::FullControl) -eq [Security.AccessControl.FileSystemRights]::FullControl)) { $userFull=$true }
16687
+ }
16688
+ if(-not $userFull) { throw 'private ACL does not grant the current user full control' }
16689
+ }
16690
+ [Console]::Out.WriteLine((@{id=$requestId;status='ok'} | ConvertTo-Json -Compress))
16691
+ } catch {
16692
+ [Console]::Out.WriteLine((@{id=$requestId;status='error'} | ConvertTo-Json -Compress))
16693
+ }
16694
+ }`;
16695
+ windowsPrivateAclBrokerInvocation = () => ({
16676
16696
  args: [
16677
16697
  "-NoLogo",
16678
16698
  "-NoProfile",
16679
16699
  "-NonInteractive",
16680
16700
  "-EncodedCommand",
16681
- Buffer.from(WINDOWS_PRIVATE_ACL_SCRIPT, "utf16le").toString("base64")
16701
+ Buffer.from(WINDOWS_PRIVATE_ACL_BROKER_SCRIPT, "utf16le").toString("base64")
16682
16702
  ],
16683
16703
  env: {
16684
16704
  NODE_ENV: process.env.NODE_ENV,
@@ -16688,49 +16708,169 @@ if(-not $userFull) { throw 'private ACL does not grant the current user full con
16688
16708
  PATHEXT: process.env.PATHEXT,
16689
16709
  PSModulePath: process.env.PSModulePath,
16690
16710
  TEMP: process.env.TEMP,
16691
- TMP: process.env.TMP,
16692
- VTX_PRIVATE_PATH_B64: Buffer.from(path, "utf8").toString("base64"),
16693
- VTX_PRIVATE_PATH_KIND: kind,
16694
- VTX_PRIVATE_ACL_MODE: mode
16711
+ TMP: process.env.TMP
16695
16712
  }
16696
16713
  });
16697
- runWindowsPrivateAcl = async (path, kind, mode) => await new Promise((resolvePromise, reject) => {
16698
- const invocation = windowsPrivateAclInvocation(path, kind, mode);
16699
- const child = spawn("powershell.exe", [...invocation.args], {
16700
- env: invocation.env,
16701
- windowsHide: true,
16702
- stdio: ["ignore", "pipe", "pipe"]
16703
- });
16704
- const stdout = [];
16705
- const stderr = [];
16706
- let settled = false;
16707
- const timer = setTimeout(() => child.kill("SIGKILL"), 15e3);
16708
- child.stdout.on("data", (chunk) => stdout.push(Buffer.from(chunk)));
16709
- child.stderr.on("data", (chunk) => stderr.push(Buffer.from(chunk)));
16710
- const settle = (error48) => {
16711
- if (settled) return;
16712
- settled = true;
16713
- clearTimeout(timer);
16714
- if (error48) reject(error48);
16715
- else resolvePromise();
16716
- };
16717
- child.once("error", (error48) => settle(error48));
16718
- child.once("exit", (code) => {
16719
- const output3 = Buffer.concat(stdout).toString("utf8").trim();
16720
- if (code === 0 && output3 === "ok") {
16721
- settle();
16714
+ WindowsPrivateAclBroker = class {
16715
+ constructor(dependencies = {}) {
16716
+ this.child = null;
16717
+ this.requestSequence = 0;
16718
+ this.queue = Promise.resolve();
16719
+ this.pending = null;
16720
+ this.outputBuffer = "";
16721
+ this.idleTimer = null;
16722
+ this.run = async (path, kind, mode) => {
16723
+ const operation = this.queue.then(
16724
+ () => this.dispatch(path, kind, mode),
16725
+ () => this.dispatch(path, kind, mode)
16726
+ );
16727
+ this.queue = operation.then(() => void 0, () => void 0);
16728
+ await operation;
16729
+ };
16730
+ this.requestTimeoutMs = dependencies.requestTimeoutMs ?? 15e3;
16731
+ this.idleShutdownMs = dependencies.idleShutdownMs ?? 3e4;
16732
+ this.spawnProcess = dependencies.spawnProcess ?? (() => {
16733
+ const invocation = windowsPrivateAclBrokerInvocation();
16734
+ return spawn("powershell.exe", [...invocation.args], {
16735
+ env: invocation.env,
16736
+ windowsHide: true,
16737
+ stdio: ["pipe", "pipe", "pipe"]
16738
+ });
16739
+ });
16740
+ }
16741
+ scheduleIdleShutdown() {
16742
+ if (this.idleTimer) clearTimeout(this.idleTimer);
16743
+ this.idleTimer = setTimeout(() => {
16744
+ this.idleTimer = null;
16745
+ const child = this.child;
16746
+ if (!child || this.pending) return;
16747
+ this.child = null;
16748
+ this.outputBuffer = "";
16749
+ child.stdin.end();
16750
+ const force = setTimeout(() => child.kill("SIGKILL"), 1e3);
16751
+ force.unref();
16752
+ child.once("exit", () => clearTimeout(force));
16753
+ }, this.idleShutdownMs);
16754
+ this.idleTimer.unref();
16755
+ }
16756
+ failChild(child, error48) {
16757
+ if (this.child !== child) return;
16758
+ this.child = null;
16759
+ this.outputBuffer = "";
16760
+ if (this.idleTimer) {
16761
+ clearTimeout(this.idleTimer);
16762
+ this.idleTimer = null;
16763
+ }
16764
+ const pending = this.pending;
16765
+ this.pending = null;
16766
+ if (pending) {
16767
+ clearTimeout(pending.timer);
16768
+ pending.reject(error48);
16769
+ }
16770
+ child.kill("SIGKILL");
16771
+ }
16772
+ acceptLine(child, line) {
16773
+ const pending = this.pending;
16774
+ if (!pending || !line || line.length > 4096) {
16775
+ this.failChild(child, new Error("Windows private ACL broker returned malformed output."));
16722
16776
  return;
16723
16777
  }
16724
- const detail = Buffer.concat(stderr).toString("utf8").trim();
16725
- if (code === 3 && output3 === "missing" || /GetAcl_PathNotFound|Cannot find path .* because it does not exist/iu.test(detail)) {
16778
+ let response;
16779
+ try {
16780
+ const parsed = JSON.parse(line);
16781
+ if (Object.keys(parsed).sort().join("\0") !== "id\0status" || parsed.id !== pending.id || !["ok", "missing", "error"].includes(String(parsed.status))) throw new Error("invalid response");
16782
+ response = parsed;
16783
+ } catch {
16784
+ this.failChild(child, new Error("Windows private ACL broker returned malformed output."));
16785
+ return;
16786
+ }
16787
+ this.pending = null;
16788
+ clearTimeout(pending.timer);
16789
+ if (response.status === "ok") {
16790
+ pending.resolve();
16791
+ } else if (response.status === "missing") {
16726
16792
  const error48 = new Error("Windows private path no longer exists.");
16727
16793
  error48.code = "ENOENT";
16728
- settle(error48);
16729
- return;
16794
+ pending.reject(error48);
16795
+ } else {
16796
+ pending.reject(new Error(`Windows private ACL ${pending.mode} failed.`));
16730
16797
  }
16731
- settle(new Error(`Windows private ACL ${mode} failed${detail ? `: ${detail}` : "."}`));
16732
- });
16733
- });
16798
+ this.scheduleIdleShutdown();
16799
+ }
16800
+ ensureChild() {
16801
+ if (this.child && this.child.exitCode === null && !this.child.killed) return this.child;
16802
+ const child = this.spawnProcess();
16803
+ this.child = child;
16804
+ this.outputBuffer = "";
16805
+ child.stdout.setEncoding("utf8");
16806
+ child.stdout.on("data", (chunk) => {
16807
+ if (this.child !== child) return;
16808
+ this.outputBuffer += chunk;
16809
+ if (this.outputBuffer.length > 65536) {
16810
+ this.failChild(child, new Error("Windows private ACL broker exceeded its output limit."));
16811
+ return;
16812
+ }
16813
+ while (this.child === child) {
16814
+ const newline = this.outputBuffer.indexOf("\n");
16815
+ if (newline < 0) break;
16816
+ const line = this.outputBuffer.slice(0, newline).trim();
16817
+ this.outputBuffer = this.outputBuffer.slice(newline + 1);
16818
+ this.acceptLine(child, line);
16819
+ }
16820
+ });
16821
+ child.stderr.on("data", () => void 0);
16822
+ child.once("error", () => {
16823
+ this.failChild(child, new Error("Windows private ACL broker failed to start."));
16824
+ });
16825
+ child.once("exit", () => {
16826
+ this.failChild(child, new Error("Windows private ACL broker exited before confirmation."));
16827
+ });
16828
+ child.unref();
16829
+ for (const stream of [child.stdin, child.stdout, child.stderr]) {
16830
+ stream.unref?.();
16831
+ }
16832
+ return child;
16833
+ }
16834
+ async dispatch(path, kind, mode) {
16835
+ if (this.idleTimer) {
16836
+ clearTimeout(this.idleTimer);
16837
+ this.idleTimer = null;
16838
+ }
16839
+ const id2 = ++this.requestSequence;
16840
+ const request = `${JSON.stringify({
16841
+ id: id2,
16842
+ kind,
16843
+ mode,
16844
+ path_b64: Buffer.from(path, "utf8").toString("base64")
16845
+ })}
16846
+ `;
16847
+ if (Buffer.byteLength(request, "utf8") > 65536) {
16848
+ throw new Error("Windows private ACL broker request exceeds its input limit.");
16849
+ }
16850
+ const child = this.ensureChild();
16851
+ await new Promise((resolvePromise, reject) => {
16852
+ const timer = setTimeout(() => {
16853
+ this.failChild(child, new Error("Windows private ACL broker request timed out."));
16854
+ }, this.requestTimeoutMs);
16855
+ this.pending = { id: id2, mode, resolve: resolvePromise, reject, timer };
16856
+ child.stdin.write(request, "utf8", (error48) => {
16857
+ if (error48) {
16858
+ this.failChild(child, new Error("Windows private ACL broker request failed."));
16859
+ }
16860
+ });
16861
+ });
16862
+ }
16863
+ close() {
16864
+ if (this.idleTimer) clearTimeout(this.idleTimer);
16865
+ this.idleTimer = null;
16866
+ const child = this.child;
16867
+ this.child = null;
16868
+ this.outputBuffer = "";
16869
+ if (child) child.stdin.end();
16870
+ }
16871
+ };
16872
+ defaultWindowsPrivateAclBroker = new WindowsPrivateAclBroker();
16873
+ runWindowsPrivateAcl = async (path, kind, mode) => await defaultWindowsPrivateAclBroker.run(path, kind, mode);
16734
16874
  windowsAclCache = /* @__PURE__ */ new Map();
16735
16875
  windowsAclIdentity = async (path) => {
16736
16876
  const metadata = await lstat(path, { bigint: true });
@@ -28907,7 +29047,7 @@ function createDefaultInferenceHostRunnerDependencies(options) {
28907
29047
  envelopePublicKey: options.envelopePublicKey
28908
29048
  };
28909
29049
  }
28910
- 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;
28911
29051
  var init_runner = __esm({
28912
29052
  "lib/inference-host/runner.ts"() {
28913
29053
  "use strict";
@@ -28964,6 +29104,35 @@ var init_runner = __esm({
28964
29104
  }
28965
29105
  return models;
28966
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
+ };
28967
29136
  FileInferenceHostRuntimeReceiptStore = class {
28968
29137
  constructor(path, readPrivateFile = readInferencePrivateFile) {
28969
29138
  this.path = path;
@@ -29604,6 +29773,22 @@ var init_runner = __esm({
29604
29773
  } catch {
29605
29774
  }
29606
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
+ };
29607
29792
  const accountKey = inferenceCredentialAccountKey({
29608
29793
  issuer: localState.issuer,
29609
29794
  clientId: localState.client_id,
@@ -29869,17 +30054,6 @@ var init_runner = __esm({
29869
30054
  let nextClaimAt = Math.max(now(), providerRetryAtMs ?? 0);
29870
30055
  let pendingClaimPromotions = 0;
29871
30056
  let onceClaimed = false;
29872
- emitDiagnostic("runtime_started", {
29873
- max_concurrency: settings.maxConcurrency,
29874
- active_attempts: active.size,
29875
- provider_cooldown_reason: providerCooldownReason,
29876
- provider_cooldown_until: providerRetryAtMs === null ? null : isoAt(providerRetryAtMs),
29877
- provider_weekly_quota: providerWeeklyQuotaFromRateLimits(
29878
- providerRateLimits,
29879
- providerRateLimitsObservedAtMs,
29880
- now()
29881
- )
29882
- });
29883
30057
  const recoveryQueue = [];
29884
30058
  const launchClaim = (claim, recovery, claimRequest) => {
29885
30059
  if (!recovery) {
@@ -29974,6 +30148,10 @@ var init_runner = __esm({
29974
30148
  }
29975
30149
  }).catch((error48) => {
29976
30150
  failed += 1;
30151
+ const recovery2 = receipt.attempts[attemptId];
30152
+ if (recovery2?.phase === "terminal_pending") {
30153
+ emitTerminalRecoveryFailure(recovery2, error48);
30154
+ }
29977
30155
  requestDrain(controlPlaneFatal(error48) ? "authority_lost" : "attempt_terminal_unconfirmed");
29978
30156
  }).finally(() => {
29979
30157
  settleClaimPromotion();
@@ -30029,6 +30207,7 @@ var init_runner = __esm({
30029
30207
  await this.dependencies.codexAdapter.acknowledgeAttempt?.(recovery.attempt_id);
30030
30208
  await removeRecoveredAttempt(recovery.attempt_id);
30031
30209
  } catch (error48) {
30210
+ emitTerminalRecoveryFailure(recovery, error48);
30032
30211
  requestDrain(controlPlaneFatal(error48) ? "authority_lost" : "attempt_terminal_unconfirmed");
30033
30212
  break;
30034
30213
  }
@@ -30036,6 +30215,19 @@ var init_runner = __esm({
30036
30215
  }
30037
30216
  recoveryQueue.push(recovery);
30038
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
+ }
30039
30231
  while (!drainRequested && recoveryQueue.length > 0 && (settings.maxConcurrency === null || active.size < settings.maxConcurrency)) {
30040
30232
  const recovery = recoveryQueue.shift();
30041
30233
  launchClaim(recovery.claim, recovery);
@@ -30768,11 +30960,12 @@ var init_runner = __esm({
30768
30960
 
30769
30961
  // lib/inference-host/service.ts
30770
30962
  import { spawn as spawn5 } from "node:child_process";
30963
+ import { randomUUID } from "node:crypto";
30771
30964
  import { createWriteStream, readFileSync } from "node:fs";
30772
30965
  import { access as access3, chmod as chmod3, mkdir as mkdir3, readFile as readFile5, rm as rm4, writeFile as writeFile2 } from "node:fs/promises";
30773
30966
  import { homedir as homedir2 } from "node:os";
30774
30967
  import { dirname as dirname4, join as join5, resolve as resolve4 } from "node:path";
30775
- 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;
30776
30969
  var init_service = __esm({
30777
30970
  "lib/inference-host/service.ts"() {
30778
30971
  "use strict";
@@ -30781,6 +30974,7 @@ var init_service = __esm({
30781
30974
  SYSTEMD_UNIT = "vtx-inference-host.service";
30782
30975
  LAUNCHD_LABEL = "com.vtxmacro.inference-host";
30783
30976
  SERVICE_COOPERATIVE_STOP_SECONDS = 75;
30977
+ INFERENCE_HOST_SERVICE_DRAIN_COMMAND = "vtx-inference-host-service-drain-v1";
30784
30978
  inferenceHostServiceChildEnvironment = (runtimeEnvironment2, inheritedEnvironment = process.env) => {
30785
30979
  const environment = { ...inheritedEnvironment };
30786
30980
  for (const key of Object.keys(environment)) {
@@ -30788,7 +30982,11 @@ var init_service = __esm({
30788
30982
  delete environment[key];
30789
30983
  }
30790
30984
  }
30791
- return { ...environment, ...runtimeEnvironment2 };
30985
+ return {
30986
+ ...environment,
30987
+ ...runtimeEnvironment2,
30988
+ VTX_INFERENCE_HOST_SERVICE_CHILD: "1"
30989
+ };
30792
30990
  };
30793
30991
  isWindowsSubsystemForLinux = (env = process.env, kernelRelease) => Boolean(
30794
30992
  String(env.WSL_INTEROP || "").trim() || String(env.WSL_DISTRO_NAME || "").trim() || /microsoft/iu.test(kernelRelease ?? (() => {
@@ -30802,6 +31000,7 @@ var init_service = __esm({
30802
31000
  inferenceHostServiceManifestPath = (config2) => `${config2.supervisorStatePath}.service.json`;
30803
31001
  inferenceHostServiceDesiredPath = (config2) => `${config2.supervisorStatePath}.service-desired.json`;
30804
31002
  inferenceHostServiceLogPath = (config2) => `${config2.supervisorStatePath}.service.log`;
31003
+ inferenceHostServiceRuntimePath = (config2) => `${config2.supervisorStatePath}.service-runtime.json`;
30805
31004
  xmlEscape = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
30806
31005
  plistEscape = xmlEscape;
30807
31006
  systemdQuote = (value) => `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("%", "%%")}"`;
@@ -30852,6 +31051,7 @@ var init_service = __esm({
30852
31051
  }
30853
31052
  return value;
30854
31053
  };
31054
+ manifestGeneration = (value, installedAt) => typeof value === "string" && /^[A-Za-z0-9._:-]{1,160}$/u.test(value) ? value : `legacy:${installedAt}`;
30855
31055
  assertWorker = (value) => {
30856
31056
  if (!value || typeof value !== "object" || Array.isArray(value)) {
30857
31057
  throw new Error("Inference-host service worker is invalid.");
@@ -30892,6 +31092,7 @@ var init_service = __esm({
30892
31092
  });
30893
31093
  return {
30894
31094
  schema_version: "vtx_inference_service_v3",
31095
+ generation: manifestGeneration(void 0, legacy.installed_at),
30895
31096
  installed_at: legacy.installed_at,
30896
31097
  executable: assertServicePath(legacy.executable),
30897
31098
  script: assertServicePath(legacy.script),
@@ -30925,6 +31126,7 @@ var init_service = __esm({
30925
31126
  }
30926
31127
  return {
30927
31128
  schema_version: "vtx_inference_service_v3",
31129
+ generation: manifestGeneration(void 0, legacy.installed_at),
30928
31130
  installed_at: legacy.installed_at,
30929
31131
  executable: assertServicePath(legacy.executable),
30930
31132
  script: assertServicePath(legacy.script),
@@ -30942,6 +31144,34 @@ var init_service = __esm({
30942
31144
  if (new Set(workers.map((worker) => worker.instance_name)).size !== workers.length) {
30943
31145
  throw new Error("Inference-host service worker names must be unique.");
30944
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
+ });
30945
31175
  return { ...record2, workers };
30946
31176
  };
30947
31177
  assertDesiredState = (value) => {
@@ -30964,6 +31194,42 @@ var init_service = __esm({
30964
31194
  throw error48;
30965
31195
  }
30966
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
+ };
30967
31233
  readInferenceHostServiceDesired = async (path) => {
30968
31234
  const raw = await readInferencePrivateFile(path, "Inference-host service desired state");
30969
31235
  if (raw === null) return false;
@@ -31086,6 +31352,9 @@ WantedBy=default.target
31086
31352
  <key>StandardErrorPath</key><string>${plistEscape(logPath)}</string>
31087
31353
  </dict></plist>
31088
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]));
31089
31358
  InferenceHostServiceManager = class {
31090
31359
  constructor(config2, dependencies = {}) {
31091
31360
  this.config = config2;
@@ -31102,6 +31371,8 @@ WantedBy=default.target
31102
31371
  });
31103
31372
  this.stopWaitAttempts = dependencies.stopWaitAttempts ?? SERVICE_COOPERATIVE_STOP_SECONDS * 4;
31104
31373
  this.startWaitAttempts = dependencies.startWaitAttempts ?? 40;
31374
+ this.reconcileWaitAttempts = dependencies.reconcileWaitAttempts ?? SERVICE_COOPERATIVE_STOP_SECONDS * 4;
31375
+ this.confirmInitialReadiness = dependencies.confirmInitialReadiness ?? true;
31105
31376
  this.acquireProcessLock = dependencies.acquireProcessLock ?? acquireInferenceHostProcessLock;
31106
31377
  managerName(this.platform);
31107
31378
  if (dependencies.platform === void 0 && this.platform === "linux" && isWindowsSubsystemForLinux()) {
@@ -31119,6 +31390,9 @@ WantedBy=default.target
31119
31390
  logPath() {
31120
31391
  return inferenceHostServiceLogPath(this.config);
31121
31392
  }
31393
+ runtimePath() {
31394
+ return inferenceHostServiceRuntimePath(this.config);
31395
+ }
31122
31396
  controlLockPath() {
31123
31397
  return `${this.config.supervisorProcessLockPath}.service-control`;
31124
31398
  }
@@ -31204,7 +31478,77 @@ WantedBy=default.target
31204
31478
  }
31205
31479
  throw new Error("Background service did not reach an active state within 10 seconds.");
31206
31480
  }
31207
- 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) {
31208
31552
  const args = serviceArguments(manifest.script, this.manifestPath());
31209
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);
31210
31554
  let managerInstallAttempted = false;
@@ -31240,6 +31584,9 @@ ${started.stderr}`)) {
31240
31584
  throw new Error(`Background service start failed: ${started.stderr.trim()}`);
31241
31585
  }
31242
31586
  await this.waitForManagerActive();
31587
+ if (this.confirmInitialReadiness) {
31588
+ await this.waitForManifestApplied(manifest, targetInstanceName);
31589
+ }
31243
31590
  }
31244
31591
  return await this.status();
31245
31592
  } catch (error48) {
@@ -31264,22 +31611,80 @@ ${cleanup.stderr}`)) {
31264
31611
  }
31265
31612
  await rm4(this.manifestPath(), { force: true }).catch(() => void 0);
31266
31613
  await rm4(this.desiredPath(), { force: true }).catch(() => void 0);
31614
+ await rm4(this.runtimePath(), { force: true }).catch(() => void 0);
31267
31615
  if (this.platform === "linux") {
31268
31616
  await this.runCommand("systemctl", ["--user", "daemon-reload"]).catch(() => void 0);
31269
31617
  }
31270
31618
  throw error48;
31271
31619
  }
31272
31620
  }
31273
- async replaceManifestUnlocked(next, desiredRunning) {
31621
+ async replaceManifestUnlocked(next, desiredRunning, targetInstanceName) {
31274
31622
  const previous = await readInferenceHostServiceManifest(this.manifestPath());
31275
31623
  const previousDesired = previous ? await readInferenceHostServiceDesired(this.desiredPath()) : false;
31276
- 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
+ }
31277
31676
  try {
31278
- return await this.registerManifestUnlocked(next, desiredRunning);
31677
+ return await this.registerManifestUnlocked(next, desiredRunning, targetInstanceName);
31279
31678
  } catch (error48) {
31280
31679
  if (!previous) throw error48;
31281
31680
  try {
31282
- 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
+ );
31283
31688
  } catch (rollbackError) {
31284
31689
  throw new Error(
31285
31690
  "Inference-host service reconfiguration failed and the previous supervisor could not be restored.",
@@ -31336,6 +31741,7 @@ ${cleanup.stderr}`)) {
31336
31741
  ].sort((left, right) => left.instance_name.localeCompare(right.instance_name));
31337
31742
  const manifest = assertManifest({
31338
31743
  schema_version: "vtx_inference_service_v3",
31744
+ generation: randomUUID(),
31339
31745
  installed_at: this.now().toISOString(),
31340
31746
  executable: this.executable,
31341
31747
  script: this.script,
@@ -31344,7 +31750,8 @@ ${cleanup.stderr}`)) {
31344
31750
  });
31345
31751
  return await this.replaceManifestUnlocked(
31346
31752
  manifest,
31347
- options.startImmediately !== false
31753
+ options.startImmediately !== false,
31754
+ this.config.instanceName
31348
31755
  );
31349
31756
  }
31350
31757
  async start() {
@@ -31376,41 +31783,56 @@ ${result2.stderr}`)) {
31376
31783
  if (!manifest) {
31377
31784
  throw new Error("Inference-host service is not installed.");
31378
31785
  }
31786
+ const restoreOnFailure = await readInferenceHostServiceDesired(this.desiredPath());
31379
31787
  await writeDesired(this.desiredPath(), false, this.now());
31380
- const serviceLockPath = `${this.config.supervisorProcessLockPath}.service`;
31381
- let serviceReleased = false;
31382
- for (let attempt = 0; attempt < this.stopWaitAttempts; attempt += 1) {
31383
- try {
31384
- const probe = await this.acquireProcessLock(serviceLockPath);
31385
- await probe.release();
31386
- serviceReleased = true;
31387
- break;
31388
- } catch (error48) {
31389
- if (error48 instanceof Error && error48.message.includes("Another inference host process already owns")) {
31390
- await this.sleep(250);
31391
- 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;
31392
31803
  }
31393
- throw error48;
31394
31804
  }
31395
- }
31396
- if (!serviceReleased) {
31397
- throw new Error(
31398
- `Inference-host workers did not stop cooperatively within ${SERVICE_COOPERATIVE_STOP_SECONDS} seconds; refusing forced termination while cleanup may be pending.`
31399
- );
31400
- }
31401
- const result2 = await this.managerCommand("stop");
31402
- 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}
31403
31812
  ${result2.stderr}`)) {
31404
- throw new Error(`Background service stop failed: ${result2.stderr.trim()}`);
31405
- }
31406
- const status = await this.status();
31407
- if (status.manager_active) {
31408
- 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;
31409
31831
  }
31410
- return status;
31411
31832
  }
31412
- async status() {
31833
+ async status(confirmedRuntime) {
31413
31834
  const manifest = await readInferenceHostServiceManifest(this.manifestPath());
31835
+ const runtime = confirmedRuntime?.manifest_generation === manifest?.generation ? confirmedRuntime : await readRuntimeAcrossAtomicReplacement(this.runtimePath()).catch(() => null);
31414
31836
  const desired = await readInferenceHostServiceDesired(this.desiredPath());
31415
31837
  const result2 = await this.managerCommand("status");
31416
31838
  const output3 = result2.stdout.trim();
@@ -31428,7 +31850,8 @@ ${result2.stderr}`)) {
31428
31850
  display_name: worker.display_name,
31429
31851
  max_concurrency: worker.max_concurrency,
31430
31852
  authenticated_account_email: worker.authenticated_account_email,
31431
- 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"
31432
31855
  })) ?? []
31433
31856
  };
31434
31857
  }
@@ -31466,6 +31889,7 @@ ${result2.stderr}`)) {
31466
31889
  if (remaining.length === 0) return await this.uninstallUnlocked();
31467
31890
  return await this.replaceManifestUnlocked({
31468
31891
  ...manifest,
31892
+ generation: randomUUID(),
31469
31893
  installed_at: this.now().toISOString(),
31470
31894
  workers: remaining
31471
31895
  }, await readInferenceHostServiceDesired(this.desiredPath()));
@@ -31493,6 +31917,7 @@ ${result2.stderr}`)) {
31493
31917
  if (this.platform === "linux") await this.runCommand("systemctl", ["--user", "daemon-reload"]);
31494
31918
  await rm4(this.manifestPath(), { force: true });
31495
31919
  await rm4(this.desiredPath(), { force: true });
31920
+ await rm4(this.runtimePath(), { force: true });
31496
31921
  return {
31497
31922
  installed: false,
31498
31923
  desired_running: false,
@@ -31514,7 +31939,8 @@ ${result2.stderr}`)) {
31514
31939
  `, resolvePromise);
31515
31940
  });
31516
31941
  };
31517
- spawnServiceChild = async (manifest, worker, signal) => {
31942
+ spawnInferenceHostServiceChild = async (manifest, worker, signal, onReady = () => {
31943
+ }) => {
31518
31944
  const args = [
31519
31945
  manifest.script,
31520
31946
  "inference-host",
@@ -31527,6 +31953,8 @@ ${result2.stderr}`)) {
31527
31953
  const log = createWriteStream(manifest.log_path, { flags: "a", mode: 384 });
31528
31954
  return await new Promise((resolvePromise, reject) => {
31529
31955
  let stdout = "";
31956
+ let cooperativeStopTimedOut = false;
31957
+ let cooperativeStopTimer = null;
31530
31958
  const pending = { stdout: "", stderr: "" };
31531
31959
  const writeTaggedOutput = (stream, text, flush = false) => {
31532
31960
  const lines = `${pending[stream]}${text}`.split(/\r?\n/u);
@@ -31539,6 +31967,7 @@ ${result2.stderr}`)) {
31539
31967
  try {
31540
31968
  const parsed = JSON.parse(line);
31541
31969
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
31970
+ if (parsed.event === "runtime_started") onReady();
31542
31971
  log.write(`${JSON.stringify({
31543
31972
  ...parsed,
31544
31973
  instance_name: worker.instance_name
@@ -31561,7 +31990,7 @@ ${result2.stderr}`)) {
31561
31990
  const child = spawn5(manifest.executable, args, {
31562
31991
  env: inferenceHostServiceChildEnvironment(worker.runtime_environment),
31563
31992
  windowsHide: true,
31564
- stdio: ["ignore", "pipe", "pipe"]
31993
+ stdio: ["pipe", "pipe", "pipe"]
31565
31994
  });
31566
31995
  child.stdout.on("data", (chunk) => {
31567
31996
  const text = chunk.toString("utf8");
@@ -31569,10 +31998,22 @@ ${result2.stderr}`)) {
31569
31998
  stdout = `${stdout}${text}`.slice(-65536);
31570
31999
  });
31571
32000
  child.stderr.on("data", (chunk) => writeTaggedOutput("stderr", chunk.toString("utf8")));
31572
- const onAbort = () => child.kill("SIGTERM");
31573
- 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 });
31574
32014
  child.once("error", (error48) => {
31575
32015
  signal.removeEventListener("abort", onAbort);
32016
+ if (cooperativeStopTimer) clearTimeout(cooperativeStopTimer);
31576
32017
  writeTaggedOutput("stdout", "", true);
31577
32018
  writeTaggedOutput("stderr", "", true);
31578
32019
  log.end();
@@ -31580,6 +32021,7 @@ ${result2.stderr}`)) {
31580
32021
  });
31581
32022
  child.once("exit", (code) => {
31582
32023
  signal.removeEventListener("abort", onAbort);
32024
+ if (cooperativeStopTimer) clearTimeout(cooperativeStopTimer);
31583
32025
  writeTaggedOutput("stdout", "", true);
31584
32026
  writeTaggedOutput("stderr", "", true);
31585
32027
  log.end();
@@ -31599,81 +32041,231 @@ ${result2.stderr}`)) {
31599
32041
  resolvePromise({
31600
32042
  exitCode: code ?? 1,
31601
32043
  uptimeMs: Date.now() - startedAt,
31602
- drainReason
32044
+ drainReason,
32045
+ cooperativeStopTimedOut
31603
32046
  });
31604
32047
  });
31605
32048
  });
31606
32049
  };
31607
32050
  runInferenceHostServiceSupervisor = async (manifestPath, options = {}) => {
31608
- const manifest = await readInferenceHostServiceManifest(manifestPath);
31609
- 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.");
31610
32053
  const desiredPath = manifestPath.replace(/\.service\.json$/u, ".service-desired.json");
32054
+ const runtimePath = manifestPath.replace(/\.service\.json$/u, ".service-runtime.json");
31611
32055
  const signal = options.signal ?? new AbortController().signal;
31612
32056
  const sleep4 = options.sleep ?? (async (milliseconds) => {
31613
32057
  await new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds));
31614
32058
  });
31615
- const launch = options.runWorker ?? options.spawnChild ?? spawnServiceChild;
31616
- 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", {
31617
32061
  adapter: "codex",
31618
- instances: manifest.workers.map((worker) => worker.instance_name)
32062
+ instances: initialManifest.workers.map((worker) => worker.instance_name)
31619
32063
  });
31620
- 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) => {
31621
32108
  let failures = 0;
31622
- while (!signal.aborted && await readDesiredAcrossAtomicReplacement(desiredPath)) {
32109
+ while (!signal.aborted && !record2.controller.signal.aborted && await readDesiredAcrossAtomicReplacement(desiredPath)) {
31623
32110
  try {
31624
- const workerController = new AbortController();
31625
- const forwardAbort = () => workerController.abort();
32111
+ await updateRecord(record2, "starting");
32112
+ const attemptController = new AbortController();
32113
+ const forwardAbort = () => attemptController.abort();
31626
32114
  signal.addEventListener("abort", forwardAbort, { once: true });
31627
- let workerComplete = false;
31628
- let monitorError = null;
31629
- const desiredMonitor = (async () => {
31630
- while (!workerComplete && !workerController.signal.aborted) {
31631
- await sleep4(500);
31632
- if (!await readDesiredAcrossAtomicReplacement(desiredPath)) {
31633
- workerController.abort();
31634
- break;
31635
- }
31636
- }
31637
- })().catch((error48) => {
31638
- monitorError = error48;
31639
- workerController.abort();
31640
- });
32115
+ record2.controller.signal.addEventListener("abort", forwardAbort, { once: true });
31641
32116
  let result2;
32117
+ let launchSettled = false;
32118
+ let readinessTask = Promise.resolve();
32119
+ let readinessSignalled = false;
31642
32120
  try {
31643
- 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
+ );
31644
32136
  } finally {
31645
- workerComplete = true;
31646
- workerController.abort();
32137
+ launchSettled = true;
32138
+ attemptController.abort();
31647
32139
  signal.removeEventListener("abort", forwardAbort);
31648
- await desiredMonitor;
32140
+ record2.controller.signal.removeEventListener("abort", forwardAbort);
32141
+ await readinessTask;
32142
+ }
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");
31649
32149
  }
31650
- if (monitorError) throw monitorError;
31651
- if (signal.aborted || !await readDesiredAcrossAtomicReplacement(desiredPath)) break;
32150
+ if (signal.aborted || record2.controller.signal.aborted || !await readDesiredAcrossAtomicReplacement(desiredPath)) break;
31652
32151
  failures = result2.uptimeMs >= 6e4 ? 0 : failures + 1;
31653
32152
  const retryAfterMs = Math.min(1e3 * 2 ** Math.min(failures, 5), 3e4);
31654
- await appendServiceLog(manifest.log_path, "worker_exited", {
31655
- 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,
31656
32156
  exit_code: result2.exitCode,
31657
32157
  uptime_ms: result2.uptimeMs,
31658
32158
  drain_reason: result2.drainReason ?? null,
31659
32159
  retry_after_ms: retryAfterMs
31660
32160
  });
31661
- await sleep4(retryAfterMs);
32161
+ await waitRetry(retryAfterMs, record2.controller);
31662
32162
  } catch (error48) {
31663
- 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;
31664
32165
  failures += 1;
31665
32166
  const retryAfterMs = Math.min(1e3 * 2 ** Math.min(failures, 5), 3e4);
31666
- await appendServiceLog(manifest.log_path, "worker_launch_failed", {
31667
- 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,
31668
32170
  error: error48 instanceof Error ? error48.message : "unknown",
31669
32171
  retry_after_ms: retryAfterMs
31670
32172
  });
31671
- 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);
31672
32241
  }
31673
32242
  }
32243
+ await persistRuntime();
31674
32244
  };
31675
- await Promise.all(manifest.workers.map(superviseWorker));
31676
- 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
+ }
31677
32269
  };
31678
32270
  }
31679
32271
  });
@@ -31682,9 +32274,10 @@ ${result2.stderr}`)) {
31682
32274
  var cli_exports = {};
31683
32275
  __export(cli_exports, {
31684
32276
  INFERENCE_HOST_CLI_VERSION: () => INFERENCE_HOST_CLI_VERSION,
32277
+ registerInferenceHostServiceControlInput: () => registerInferenceHostServiceControlInput,
31685
32278
  runInferenceHostCli: () => runInferenceHostCli
31686
32279
  });
31687
- import { randomUUID } from "node:crypto";
32280
+ import { randomUUID as randomUUID2 } from "node:crypto";
31688
32281
  import { spawn as spawn6 } from "node:child_process";
31689
32282
  import { lstat as lstat4, realpath as realpath4, rm as rm5 } from "node:fs/promises";
31690
32283
  import { join as join6, resolve as resolve5 } from "node:path";
@@ -31762,7 +32355,7 @@ async function runInferenceHostCli(argv2, env = process.env, dependencies = {})
31762
32355
  };
31763
32356
  }
31764
32357
  }
31765
- 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;
31766
32359
  var init_cli = __esm({
31767
32360
  "lib/inference-host/cli.ts"() {
31768
32361
  "use strict";
@@ -32007,14 +32600,35 @@ Durable service:
32007
32600
  });
32008
32601
  child.unref();
32009
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
+ };
32010
32620
  defaultRegisterLifecycleSignalHandlers = (abort) => {
32011
32621
  const onSigint = () => abort("SIGINT");
32012
32622
  const onSigterm = () => abort("SIGTERM");
32013
32623
  process.on("SIGINT", onSigint);
32014
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
+ };
32015
32628
  return () => {
32016
32629
  process.off("SIGINT", onSigint);
32017
32630
  process.off("SIGTERM", onSigterm);
32631
+ unregisterControlInput();
32018
32632
  };
32019
32633
  };
32020
32634
  lifecycleCancellation = (dependencies) => {
@@ -32181,7 +32795,7 @@ Durable service:
32181
32795
  throw new Error("Inference host credential recovery is required before login.");
32182
32796
  }
32183
32797
  const keyPair = generateExternalInferenceEnvelopeKeyPair();
32184
- const hostId = randomUUID();
32798
+ const hostId = randomUUID2();
32185
32799
  const beginLogin = dependencies.beginLogin ?? beginInferenceOAuthLogin;
32186
32800
  const pending = await beginLogin({
32187
32801
  apiUrl: config2.apiUrl,
@@ -32404,6 +33018,7 @@ Waiting for approval...
32404
33018
  registered: receipt.registered,
32405
33019
  advertisement_generation: receipt.advertisement_generation,
32406
33020
  pending_attempts: Object.keys(receipt.attempts).length + (receipt.pending_claim_request ? 1 : 0),
33021
+ recovery: summarizeInferenceHostRuntimeRecovery(receipt),
32407
33022
  updated_at: receipt.updated_at
32408
33023
  } : null,
32409
33024
  agent_attempt: agentAttempt ? {
@@ -32715,7 +33330,7 @@ Waiting for approval...
32715
33330
  }
32716
33331
  return record2;
32717
33332
  };
32718
- agentOperationId = (kind) => `${kind}-${randomUUID()}`;
33333
+ agentOperationId = (kind) => `${kind}-${randomUUID2()}`;
32719
33334
  agentSession = async (config2, dependencies, warnings, signal) => {
32720
33335
  if (await readRevocationCheckpoint(config2)) {
32721
33336
  throw new Error("Inference host revocation recovery must finish first.");
@@ -33363,7 +33978,7 @@ var init_types = __esm({
33363
33978
  });
33364
33979
 
33365
33980
  // lib/agent-core/client.ts
33366
- import { randomUUID as randomUUID2 } from "node:crypto";
33981
+ import { randomUUID as randomUUID3 } from "node:crypto";
33367
33982
  function normalizeApiUrl(value) {
33368
33983
  const parsed = String(value || "").trim();
33369
33984
  if (!parsed) {
@@ -33625,7 +34240,7 @@ var init_client = __esm({
33625
34240
  return this.request("/trading/ai/runtime/decision", {
33626
34241
  method: "POST",
33627
34242
  profileId,
33628
- idempotencyKey: randomUUID2(),
34243
+ idempotencyKey: randomUUID3(),
33629
34244
  headers: { "x-client-runtime-lease": leaseToken },
33630
34245
  body: payload
33631
34246
  });
@@ -33634,7 +34249,7 @@ var init_client = __esm({
33634
34249
  return this.request("/trading/ai/runtime/trade-sync", {
33635
34250
  method: "POST",
33636
34251
  profileId,
33637
- idempotencyKey: randomUUID2(),
34252
+ idempotencyKey: randomUUID3(),
33638
34253
  headers: { "x-client-runtime-lease": leaseToken },
33639
34254
  body: payload
33640
34255
  });
@@ -33643,7 +34258,7 @@ var init_client = __esm({
33643
34258
  return this.request("/trading/ai/runtime/error", {
33644
34259
  method: "POST",
33645
34260
  profileId,
33646
- idempotencyKey: randomUUID2(),
34261
+ idempotencyKey: randomUUID3(),
33647
34262
  headers: { "x-client-runtime-lease": leaseToken },
33648
34263
  body: payload
33649
34264
  });
@@ -33655,7 +34270,7 @@ var init_client = __esm({
33655
34270
  return this.request("/trading/market-order", {
33656
34271
  method: "POST",
33657
34272
  profileId,
33658
- idempotencyKey: randomUUID2(),
34273
+ idempotencyKey: randomUUID3(),
33659
34274
  body: payload
33660
34275
  });
33661
34276
  }
@@ -33663,7 +34278,7 @@ var init_client = __esm({
33663
34278
  return this.request("/trading/limit-order", {
33664
34279
  method: "POST",
33665
34280
  profileId,
33666
- idempotencyKey: randomUUID2(),
34281
+ idempotencyKey: randomUUID3(),
33667
34282
  body: payload
33668
34283
  });
33669
34284
  }
@@ -33671,7 +34286,7 @@ var init_client = __esm({
33671
34286
  return this.request("/trading/cancel-order", {
33672
34287
  method: "POST",
33673
34288
  profileId,
33674
- idempotencyKey: randomUUID2(),
34289
+ idempotencyKey: randomUUID3(),
33675
34290
  body: payload
33676
34291
  });
33677
34292
  }
@@ -33690,7 +34305,7 @@ var init_client = __esm({
33690
34305
  return this.request("/trading/ai/start", {
33691
34306
  method: "POST",
33692
34307
  profileId,
33693
- idempotencyKey: randomUUID2(),
34308
+ idempotencyKey: randomUUID3(),
33694
34309
  body: payload
33695
34310
  });
33696
34311
  }
@@ -33698,7 +34313,7 @@ var init_client = __esm({
33698
34313
  return this.request("/trading/ai/stop", {
33699
34314
  method: "POST",
33700
34315
  profileId,
33701
- idempotencyKey: randomUUID2(),
34316
+ idempotencyKey: randomUUID3(),
33702
34317
  body: {}
33703
34318
  });
33704
34319
  }
@@ -33706,7 +34321,7 @@ var init_client = __esm({
33706
34321
  return this.request("/trading/ai/assistant/start", {
33707
34322
  method: "POST",
33708
34323
  profileId,
33709
- idempotencyKey: randomUUID2(),
34324
+ idempotencyKey: randomUUID3(),
33710
34325
  body: {}
33711
34326
  });
33712
34327
  }
@@ -33714,7 +34329,7 @@ var init_client = __esm({
33714
34329
  return this.request("/trading/ai/assistant/stop", {
33715
34330
  method: "POST",
33716
34331
  profileId,
33717
- idempotencyKey: randomUUID2(),
34332
+ idempotencyKey: randomUUID3(),
33718
34333
  body: {}
33719
34334
  });
33720
34335
  }
@@ -33722,7 +34337,7 @@ var init_client = __esm({
33722
34337
  return this.request("/trading/ai/runtime/session/start", {
33723
34338
  method: "POST",
33724
34339
  profileId,
33725
- idempotencyKey: randomUUID2(),
34340
+ idempotencyKey: randomUUID3(),
33726
34341
  body: payload
33727
34342
  });
33728
34343
  }
@@ -33733,7 +34348,7 @@ var init_client = __esm({
33733
34348
  return this.request("/trading/ai/runtime/session/stop", {
33734
34349
  method: "POST",
33735
34350
  profileId,
33736
- idempotencyKey: randomUUID2(),
34351
+ idempotencyKey: randomUUID3(),
33737
34352
  body: payload
33738
34353
  });
33739
34354
  }
@@ -33750,7 +34365,7 @@ var init_client = __esm({
33750
34365
  }).request("/trading/ai/runtime/session/stop", {
33751
34366
  method: "POST",
33752
34367
  profileId,
33753
- idempotencyKey: randomUUID2(),
34368
+ idempotencyKey: randomUUID3(),
33754
34369
  body: payload
33755
34370
  });
33756
34371
  }
@@ -33759,7 +34374,7 @@ var init_client = __esm({
33759
34374
  });
33760
34375
 
33761
34376
  // lib/agent-core/headless-runtime.ts
33762
- import { randomUUID as randomUUID3 } from "node:crypto";
34377
+ import { randomUUID as randomUUID4 } from "node:crypto";
33763
34378
  import { setTimeout as sleep } from "node:timers/promises";
33764
34379
  function objectOrNull2(value) {
33765
34380
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -33857,8 +34472,8 @@ async function runAndReportLocalWorkCycle(options, state, leaseToken, context) {
33857
34472
  return Boolean(result2.decision || tradeSync || result2.afterDecision);
33858
34473
  }
33859
34474
  async function startHeadlessRuntime(options) {
33860
- const runtimeSessionId = randomUUID3();
33861
- const deviceId = String(options.deviceId || "").trim() || randomUUID3();
34475
+ const runtimeSessionId = randomUUID4();
34476
+ const deviceId = String(options.deviceId || "").trim() || randomUUID4();
33862
34477
  const startResponse = await options.client.startRuntime(options.profileId, {
33863
34478
  session_id: runtimeSessionId,
33864
34479
  device_id: deviceId,
@@ -50476,7 +51091,7 @@ var headless_local_worker_exports = {};
50476
51091
  __export(headless_local_worker_exports, {
50477
51092
  createHeadlessLocalWorker: () => createHeadlessLocalWorker
50478
51093
  });
50479
- import { randomUUID as randomUUID4 } from "node:crypto";
51094
+ import { randomUUID as randomUUID5 } from "node:crypto";
50480
51095
  function objectOrNull3(value) {
50481
51096
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
50482
51097
  }
@@ -50889,7 +51504,7 @@ function createHeadlessLocalWorker(options) {
50889
51504
  const statusMatch = errorText.match(/\b([45]\d{2})\b/);
50890
51505
  const statusCode = statusMatch ? Number(statusMatch[1]) : null;
50891
51506
  const failedInvocation = normalizeAiInvocationTelemetry({
50892
- client_invocation_id: randomUUID4(),
51507
+ client_invocation_id: randomUUID5(),
50893
51508
  use_case: "trader",
50894
51509
  role: "primary",
50895
51510
  attempt_index: 0,
@@ -50945,7 +51560,7 @@ function createHeadlessLocalWorker(options) {
50945
51560
  billable_cached_input_tokens: normalizedUsage.cached_input_tokens
50946
51561
  };
50947
51562
  const invocation = normalizeAiInvocationTelemetry({
50948
- client_invocation_id: randomUUID4(),
51563
+ client_invocation_id: randomUUID5(),
50949
51564
  use_case: "trader",
50950
51565
  role: "primary",
50951
51566
  attempt_index: 0,
@@ -51150,7 +51765,7 @@ var vtx_exports = {};
51150
51765
  __export(vtx_exports, {
51151
51766
  runVtxCli: () => runVtxCli
51152
51767
  });
51153
- import { randomUUID as randomUUID5 } from "node:crypto";
51768
+ import { randomUUID as randomUUID6 } from "node:crypto";
51154
51769
  import { spawn as spawn7 } from "node:child_process";
51155
51770
  function render2(value, json2) {
51156
51771
  if (json2) {
@@ -51564,8 +52179,8 @@ async function runVtxCli(argv2, env = process.env) {
51564
52179
  });
51565
52180
  return { exitCode: 0, stdout: render2(redactCliOutput(response2), json2), stderr: "" };
51566
52181
  }
51567
- const runtimeSessionId = randomUUID5();
51568
- const deviceId = config2.runtimeDeviceId ?? randomUUID5();
52182
+ const runtimeSessionId = randomUUID6();
52183
+ const deviceId = config2.runtimeDeviceId ?? randomUUID6();
51569
52184
  const response = await client.startRuntime(profileId, {
51570
52185
  session_id: runtimeSessionId,
51571
52186
  device_id: deviceId,