@vtxmacro/cli 2026.8.43 → 2026.8.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/bin/vtx.js +536 -104
  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.43",
41
+ package_version: "2026.8.45",
42
42
  codex_package_name: "@openai/codex",
43
43
  codex_version: "0.147.0",
44
44
  copilot_sdk_package_name: "@github/copilot-sdk",
@@ -14379,7 +14379,7 @@ var init_zod = __esm({
14379
14379
  });
14380
14380
 
14381
14381
  // lib/external-inference-contract.ts
14382
- var EXTERNAL_INFERENCE_CONTRACT_VERSION, EXTERNAL_INFERENCE_CONTROLLER_SCHEMA_VERSION, EXTERNAL_INFERENCE_ADVERTISEMENT_SCHEMA_VERSION, EXTERNAL_INFERENCE_ENVELOPE_SCHEMA_VERSION, EXTERNAL_INFERENCE_JOB_INPUT_SCHEMA_VERSION, LEGACY_EXTERNAL_INFERENCE_JOB_INPUT_SCHEMA_VERSION, EXTERNAL_INFERENCE_USAGE_SCHEMA_VERSION, EXTERNAL_INFERENCE_INPUT_HASH_DOMAIN, MAX_EXTERNAL_INFERENCE_STATUS_HOSTS, MAX_GENERATION, MAX_SAFE_INTEGER, MAX_PROMPT_BYTES, MAX_IMMUTABLE_JOB_INPUT_BYTES, MAX_CANDIDATE_PLAINTEXT_BYTES, MAX_CIPHERTEXT_CHARS, SHA256_INITIAL_STATE, SHA256_ROUND_CONSTANTS, rotateRight, sha256BytesHex, sha256Utf8Hex, computeImmutableJobInputSha256, identifierSchema, operationIdentifierSchema, sha256HexSchema, generationSchema, zeroBasedGenerationSchema, nonNegativeSafeIntegerSchema, positiveSafeIntegerSchema, sqlIntegerTokenCountSchema, timestampSchema, safeCodeSchema, externalInferenceAdapterIdSchema, reasoningEffortSchema, modelNameSchema, displayNameSchema, authenticatedAccountEmailSchema, protocolVersionSchema, base64UrlAlphabet, isCanonicalBase64Url, base64Url32BytesSchema, base64UrlNonceSchema, base64UrlCiphertextSchema, claimHandleSchema, boundedJsonTextSchema, boundedPromptTextSchema, laneSchema, botModeSchema, executionModeSchema, responseModeSchema, dispatchOutcomeSchema, responseOutcomeSchema, nativeModelControllerSchema, externalAgentModelControllerSchema, externalAgentUnboundControllerSchema, orderedFallbackHostIdsSchema, externalInferenceFallbackHostIdsSchema, liveModelControllerSchema, snapshotModelControllerSchema, modelIdentityMappingSchema, advertisedModelSchema, hostAdvertisementSchema, envelopeAadSchema, validateEnvelopeAad, commonEnvelopeShape, sealedKeyWrapEnvelopeSchema, sealedInputEnvelopeSchema, sealedCandidateEnvelopeSchema, sealedEnvelopeSchema, screenerCandidateRuntimeBindingSchema, screenerClientLeaseBindingSchema, screenerRuntimeBindingSchema, serverRuntimeBindingSchema, clientRuntimeBindingSchema, runtimeBindingSchema, immutableJobInputSchema, canonicalizeJsonValue, serializeImmutableJobInput, reportedTokenUsageSchema, unavailableTokenUsageSchema, tokenUsageSchema, agentConnectRequestSchema, agentNextRequestSchema, agentHeartbeatRequestSchema, emptyAgentNextResultSchema, claimedAgentNextResultSchema, agentNextResultSchema, agentCompleteRequestSchema, agentFailRequestSchema, requestedModelSelectionSchema, completedModelProvenanceSchema, attemptModelProvenanceSchema, providerWeeklyQuotaReadSchema, hostHeartbeatRequestSchema, jobClaimRequestSchema, emptyJobClaimSchema, claimedJobSchema, jobClaimResultSchema, attemptStartRequestSchema, jobHeartbeatRequestSchema, continueHeartbeatDirectiveSchema, cancelHeartbeatDirectiveSchema, jobHeartbeatResultSchema, adapterOutcomeEvidenceSchema, completionBaseShape, validateCompletion, providerResponseCompletionSchema, decisionCandidateCompletionSchema, jobCompleteRequestSchema, jobFailRequestSchema, jobCancelRequestSchema, jobCancellationReceiptSchema, jobDeliveryRequestSchema, deliveryBaseShape, validateDeliveryCandidate, providerResponseDeliverySchema, decisionCandidateDeliverySchema, jobDeliveryResultSchema, jobConsumptionRequestSchema, jobConsumptionReceiptSchema, jobStateSchema, jobStatusReadSchema, hostStatusReadSchema, controllerBindingStatusReadSchema, queueStatusReadSchema, outcomeStatusReadSchema, externalInferenceVolumeWindowReadSchema, externalInferenceFailureCountReadSchema, externalInferenceHostDiagnosticsReadSchema, externalInferenceProfileActivityReadSchema, externalInferenceStatusReadSchema, nativeResearchEvidenceSourceSchema, externalAgentResearchEvidenceSourceSchema, researchEvidenceSourceSchema, reportedResearchTokenUsageSchema, unavailableResearchTokenUsageSchema, researchTokenUsageSchema, externalInferenceProductLineageBaseShape, traderExternalInferenceProductLineageSchema, decisionChatExternalInferenceProductLineageSchema, screenerExternalInferenceProductLineageSchema, externalInferenceProductLineageSchema, normalProductLinkageSchema, evidenceMaterializationMarkerSchema, deliveredExternalAgentSourceIdentitySchema, deliveredRequestEvidenceSchema, researchEvidenceBaseShape, researchEvidenceBaseSchema, validateExactUtf8, validateResearchEvidence, providerResponseResearchEvidenceSchema, decisionCandidateResearchEvidenceSchema, researchEvidenceSchema;
14382
+ var EXTERNAL_INFERENCE_CONTRACT_VERSION, EXTERNAL_INFERENCE_CONTROLLER_SCHEMA_VERSION, EXTERNAL_INFERENCE_ADVERTISEMENT_SCHEMA_VERSION, EXTERNAL_INFERENCE_ENVELOPE_SCHEMA_VERSION, EXTERNAL_INFERENCE_JOB_INPUT_SCHEMA_VERSION, LEGACY_EXTERNAL_INFERENCE_JOB_INPUT_SCHEMA_VERSION, EXTERNAL_INFERENCE_USAGE_SCHEMA_VERSION, EXTERNAL_INFERENCE_INPUT_HASH_DOMAIN, MAX_EXTERNAL_INFERENCE_STATUS_HOSTS, MAX_GENERATION, MAX_SAFE_INTEGER, MAX_PROMPT_BYTES, MAX_IMMUTABLE_JOB_INPUT_BYTES, MAX_CANDIDATE_PLAINTEXT_BYTES, MAX_CIPHERTEXT_CHARS, SHA256_INITIAL_STATE, SHA256_ROUND_CONSTANTS, rotateRight, sha256BytesHex, sha256Utf8Hex, computeImmutableJobInputSha256, identifierSchema, operationIdentifierSchema, sha256HexSchema, generationSchema, zeroBasedGenerationSchema, nonNegativeSafeIntegerSchema, positiveSafeIntegerSchema, sqlIntegerTokenCountSchema, timestampSchema, safeCodeSchema, processExitCodeSchema, externalInferenceAdapterIdSchema, reasoningEffortSchema, modelNameSchema, displayNameSchema, authenticatedAccountEmailSchema, protocolVersionSchema, base64UrlAlphabet, isCanonicalBase64Url, base64Url32BytesSchema, base64UrlNonceSchema, base64UrlCiphertextSchema, claimHandleSchema, boundedJsonTextSchema, boundedPromptTextSchema, laneSchema, botModeSchema, executionModeSchema, responseModeSchema, dispatchOutcomeSchema, responseOutcomeSchema, nativeModelControllerSchema, externalAgentModelControllerSchema, externalAgentUnboundControllerSchema, orderedFallbackHostIdsSchema, externalInferenceFallbackHostIdsSchema, liveModelControllerSchema, snapshotModelControllerSchema, modelIdentityMappingSchema, advertisedModelSchema, hostAdvertisementSchema, envelopeAadSchema, validateEnvelopeAad, commonEnvelopeShape, sealedKeyWrapEnvelopeSchema, sealedInputEnvelopeSchema, sealedCandidateEnvelopeSchema, sealedEnvelopeSchema, screenerCandidateRuntimeBindingSchema, screenerClientLeaseBindingSchema, screenerRuntimeBindingSchema, serverRuntimeBindingSchema, clientRuntimeBindingSchema, runtimeBindingSchema, immutableJobInputSchema, canonicalizeJsonValue, serializeImmutableJobInput, reportedTokenUsageSchema, unavailableTokenUsageSchema, tokenUsageSchema, agentConnectRequestSchema, agentNextRequestSchema, agentHeartbeatRequestSchema, emptyAgentNextResultSchema, claimedAgentNextResultSchema, agentNextResultSchema, agentCompleteRequestSchema, agentFailRequestSchema, requestedModelSelectionSchema, completedModelProvenanceSchema, attemptModelProvenanceSchema, providerWeeklyQuotaReadSchema, hostHeartbeatRequestSchema, jobClaimRequestSchema, emptyJobClaimSchema, claimedJobSchema, jobClaimResultSchema, attemptStartRequestSchema, jobHeartbeatRequestSchema, continueHeartbeatDirectiveSchema, cancelHeartbeatDirectiveSchema, jobHeartbeatResultSchema, adapterOutcomeEvidenceSchema, completionBaseShape, validateCompletion, providerResponseCompletionSchema, decisionCandidateCompletionSchema, jobCompleteRequestSchema, codexProcessExitDiagnosticsSchema, jobFailRequestSchema, jobCancelRequestSchema, jobCancellationReceiptSchema, jobDeliveryRequestSchema, deliveryBaseShape, validateDeliveryCandidate, providerResponseDeliverySchema, decisionCandidateDeliverySchema, jobDeliveryResultSchema, jobConsumptionRequestSchema, jobConsumptionReceiptSchema, jobStateSchema, jobStatusReadSchema, hostStatusReadSchema, controllerBindingStatusReadSchema, queueStatusReadSchema, outcomeStatusReadSchema, externalInferenceVolumeWindowReadSchema, externalInferenceFailureCountReadSchema, externalInferenceHostDiagnosticsReadSchema, externalInferenceProfileActivityReadSchema, externalInferenceStatusReadSchema, nativeResearchEvidenceSourceSchema, externalAgentResearchEvidenceSourceSchema, researchEvidenceSourceSchema, reportedResearchTokenUsageSchema, unavailableResearchTokenUsageSchema, researchTokenUsageSchema, externalInferenceProductLineageBaseShape, traderExternalInferenceProductLineageSchema, decisionChatExternalInferenceProductLineageSchema, screenerExternalInferenceProductLineageSchema, externalInferenceProductLineageSchema, normalProductLinkageSchema, evidenceMaterializationMarkerSchema, deliveredExternalAgentSourceIdentitySchema, deliveredRequestEvidenceSchema, researchEvidenceBaseShape, researchEvidenceBaseSchema, validateExactUtf8, validateResearchEvidence, providerResponseResearchEvidenceSchema, decisionCandidateResearchEvidenceSchema, researchEvidenceSchema;
14383
14383
  var init_external_inference_contract = __esm({
14384
14384
  "lib/external-inference-contract.ts"() {
14385
14385
  "use strict";
@@ -14582,6 +14582,7 @@ var init_external_inference_contract = __esm({
14582
14582
  sqlIntegerTokenCountSchema = external_exports.number().int().min(0).max(2147483647);
14583
14583
  timestampSchema = external_exports.iso.datetime({ offset: true });
14584
14584
  safeCodeSchema = external_exports.string().min(1).max(96).regex(/^[a-z0-9][a-z0-9._-]{0,95}$/);
14585
+ processExitCodeSchema = external_exports.number().int().min(-(2 ** 31)).max(2 ** 31 - 1);
14585
14586
  externalInferenceAdapterIdSchema = external_exports.string().min(1).max(64).regex(/^[a-z0-9][a-z0-9._-]{0,63}$/);
14586
14587
  reasoningEffortSchema = external_exports.string().min(1).max(32).regex(/^[a-z][a-z0-9_-]{0,31}$/);
14587
14588
  modelNameSchema = external_exports.string().min(1).max(256);
@@ -15435,6 +15436,55 @@ var init_external_inference_contract = __esm({
15435
15436
  providerResponseCompletionSchema,
15436
15437
  decisionCandidateCompletionSchema
15437
15438
  ]);
15439
+ codexProcessExitDiagnosticsSchema = external_exports.strictObject({
15440
+ schema_version: external_exports.literal("external_inference_process_exit_v1"),
15441
+ runtime: external_exports.literal("codex_app_server"),
15442
+ source: external_exports.enum(["child_receipt", "direct_process", "guardian_process"]),
15443
+ session_generation: generationSchema,
15444
+ expected_shutdown: external_exports.boolean(),
15445
+ exit_class: external_exports.enum(["zero", "nonzero", "signal", "unknown"]),
15446
+ exit_code: processExitCodeSchema.nullable(),
15447
+ signal: safeCodeSchema.nullable(),
15448
+ uptime_ms: nonNegativeSafeIntegerSchema,
15449
+ pending_request_count: nonNegativeSafeIntegerSchema.nullable(),
15450
+ active_thread_count: nonNegativeSafeIntegerSchema.nullable(),
15451
+ stderr_nonempty: external_exports.boolean().nullable(),
15452
+ stderr_bytes_observed: external_exports.number().int().min(0).max(65536).nullable(),
15453
+ stderr_truncated: external_exports.boolean().nullable(),
15454
+ stderr_class: external_exports.enum([
15455
+ "unknown",
15456
+ "none",
15457
+ "out_of_memory",
15458
+ "runtime_fatal",
15459
+ "memory_fault",
15460
+ "pipe_or_connection",
15461
+ "unclassified"
15462
+ ])
15463
+ }).superRefine((value, context) => {
15464
+ const expectedExitClass = value.signal !== null ? "signal" : value.exit_code === null ? "unknown" : value.exit_code === 0 ? "zero" : "nonzero";
15465
+ if (value.exit_class !== expectedExitClass) {
15466
+ context.addIssue({ code: "custom", message: "process exit class does not match code and signal", path: ["exit_class"] });
15467
+ }
15468
+ if (value.signal !== null && value.exit_code !== null) {
15469
+ context.addIssue({ code: "custom", message: "signal exits cannot also report an exit code", path: ["exit_code"] });
15470
+ }
15471
+ if (value.stderr_class === "unknown") {
15472
+ if (value.stderr_nonempty !== null || value.stderr_bytes_observed !== null || value.stderr_truncated !== null) {
15473
+ context.addIssue({ code: "custom", message: "unknown stderr diagnostics must not infer observations", path: ["stderr_class"] });
15474
+ }
15475
+ } else if (value.stderr_nonempty === null || value.stderr_bytes_observed === null || value.stderr_truncated === null) {
15476
+ context.addIssue({ code: "custom", message: "observed stderr diagnostics must be complete", path: ["stderr_class"] });
15477
+ } else if (!value.stderr_nonempty) {
15478
+ if (value.stderr_bytes_observed !== 0 || value.stderr_truncated || value.stderr_class !== "none") {
15479
+ context.addIssue({ code: "custom", message: "empty stderr diagnostics are inconsistent", path: ["stderr_nonempty"] });
15480
+ }
15481
+ } else if (value.stderr_bytes_observed === 0 || value.stderr_class === "none") {
15482
+ context.addIssue({ code: "custom", message: "non-empty stderr diagnostics are inconsistent", path: ["stderr_nonempty"] });
15483
+ }
15484
+ if (value.stderr_truncated && value.stderr_bytes_observed !== 65536) {
15485
+ context.addIssue({ code: "custom", message: "truncated stderr diagnostics must report the observation cap", path: ["stderr_truncated"] });
15486
+ }
15487
+ });
15438
15488
  jobFailRequestSchema = external_exports.strictObject({
15439
15489
  schema_version: external_exports.literal("external_inference_job_fail_v1"),
15440
15490
  contract_version: external_exports.literal(EXTERNAL_INFERENCE_CONTRACT_VERSION),
@@ -15457,6 +15507,7 @@ var init_external_inference_contract = __esm({
15457
15507
  failure_category: safeCodeSchema,
15458
15508
  failure_code: safeCodeSchema,
15459
15509
  retryable: external_exports.boolean(),
15510
+ process_exit: codexProcessExitDiagnosticsSchema.nullable().optional(),
15460
15511
  membership_disposition: external_exports.enum([
15461
15512
  "retry_same_host",
15462
15513
  "cascade_cooldown",
@@ -15473,6 +15524,13 @@ var init_external_inference_contract = __esm({
15473
15524
  if (value.attempt_status !== expectedStatus) {
15474
15525
  context.addIssue({ code: "custom", message: "attempt_status must match dispatch outcome", path: ["attempt_status"] });
15475
15526
  }
15527
+ if (value.process_exit != null && (value.failure_category !== "transport" || value.failure_code !== "codex_process_closed" || value.process_exit.expected_shutdown)) {
15528
+ context.addIssue({
15529
+ code: "custom",
15530
+ message: "process exit diagnostics require an unexpected Codex process-close failure",
15531
+ path: ["process_exit"]
15532
+ });
15533
+ }
15476
15534
  if (value.outcome.dispatch_outcome === "outcome_unknown" && value.membership_disposition !== void 0 && value.membership_disposition !== "quarantine_ambiguous") {
15477
15535
  context.addIssue({ code: "custom", message: "outcome-unknown failures must be quarantined", path: ["membership_disposition"] });
15478
15536
  }
@@ -16196,7 +16254,7 @@ import { spawn } from "node:child_process";
16196
16254
  import { constants } from "node:fs";
16197
16255
  import { lstat, mkdir, open, readFile, realpath, rename, rm } from "node:fs/promises";
16198
16256
  import { homedir } from "node:os";
16199
- import { dirname, isAbsolute, join, relative, resolve } from "node:path";
16257
+ import { dirname, isAbsolute, join, relative, resolve, win32 as windowsPath } from "node:path";
16200
16258
  function inferenceBaseDir(env) {
16201
16259
  const configured = String(env.VTX_INFERENCE_HOST_HOME || "").trim();
16202
16260
  return configured || join(homedir(), ".vtx", "inference-host");
@@ -16737,7 +16795,7 @@ async function acquireInferenceHostProcessLock(path, dependencies = {}) {
16737
16795
  }
16738
16796
  };
16739
16797
  }
16740
- 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, inferenceHostCredentialContextPath, inferenceHostCredentialContextTransitionPath, credentialContextForConfig;
16798
+ var INFERENCE_CREDENTIAL_NAMESPACE, MAX_INFERENCE_PRIVATE_FILE_BYTES, WINDOWS_PRIVATE_ACL_BROKER_SCRIPT, WINDOWS_IDENTITY_COMMAND_TIMEOUT_MS, WINDOWS_PRIVATE_ACL_BROKER_STARTUP_TIMEOUT_MS, SMALL_IDENTITY_COMMAND_TIMEOUT_MS, windowsPowerShellEnvironment, windowsPrivateAclBrokerInvocation, WindowsPrivateAclBroker, defaultWindowsPrivateAclBroker, runWindowsPrivateAcl, windowsAclCache, windowsAclIdentity, aclCacheKey, secureWindowsPrivatePath, invalidateWindowsAclCache, rebindHardenedWindowsAclAfterRename, rebindHardenedWindowsDirectoryAfterOwnedMutation, isDefaultWindowsPrivatePath, secureExistingWindowsPath, linuxProcessIdentity, windowsProcessIdentityInvocation, windowsBootIdentityInvocation, windowsProcessIdentity, darwinProcessIdentity, runSmallIdentityCommand, cachedSystemBootIdentity, readInferenceSystemBootIdentity, defaultProcessIdentity, defaultCurrentProcessIdentity, processLockObservationCache, inferenceProcessIdentitiesMatch, DEFAULT_INFERENCE_HOST_INSTANCE, SAFE_INFERENCE_HOST_INSTANCE, requireNamedInstancePath, credentialStoreIdentity, inferenceHostCredentialContextPath, inferenceHostCredentialContextTransitionPath, credentialContextForConfig;
16741
16799
  var init_config = __esm({
16742
16800
  "lib/inference-host/config.ts"() {
16743
16801
  "use strict";
@@ -16747,6 +16805,7 @@ var init_config = __esm({
16747
16805
  $ProgressPreference='SilentlyContinue'
16748
16806
  while(($line=[Console]::In.ReadLine()) -ne $null) {
16749
16807
  $requestId=$null
16808
+ $failureStage='request_validation'
16750
16809
  try {
16751
16810
  if($line.Length -lt 2 -or $line.Length -gt 65536) { throw 'invalid request size' }
16752
16811
  $request=$line | ConvertFrom-Json
@@ -16761,18 +16820,25 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
16761
16820
  if($mode -ne 'harden' -and $mode -ne 'verify' -and $mode -ne 'verify-owner') { throw 'invalid ACL mode' }
16762
16821
  $path=[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String([string]$request.path_b64))
16763
16822
  if([string]::IsNullOrWhiteSpace($path) -or $path.Length -gt 32767 -or $path.Contains([char]0)) { throw 'invalid path' }
16823
+ $failureStage='path_lookup'
16764
16824
  if(-not (Test-Path -LiteralPath $path)) {
16765
16825
  [Console]::Out.WriteLine((@{id=$requestId;status='missing'} | ConvertTo-Json -Compress))
16766
16826
  continue
16767
16827
  }
16768
- $current=[Security.Principal.WindowsIdentity]::GetCurrent().User
16828
+ $failureStage='principal_resolution'
16829
+ $currentIdentity=[Security.Principal.WindowsIdentity]::GetCurrent()
16830
+ $current=$currentIdentity.User
16769
16831
  $system=New-Object Security.Principal.SecurityIdentifier('S-1-5-18')
16770
16832
  $admins=New-Object Security.Principal.SecurityIdentifier('S-1-5-32-544')
16833
+ $currentIsAdmin=(New-Object Security.Principal.WindowsPrincipal($currentIdentity)).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
16771
16834
  $allowed=@($current.Value,$system.Value,$admins.Value)
16772
16835
  if($mode -eq 'harden') {
16836
+ $failureStage='acl_read'
16773
16837
  $acl=Get-Acl -LiteralPath $path
16838
+ $failureStage='owner_validation'
16774
16839
  $existingOwner=(New-Object Security.Principal.NTAccount($acl.Owner)).Translate([Security.Principal.SecurityIdentifier]).Value
16775
- if($existingOwner -ne $current.Value) { throw 'private ACL owner is invalid' }
16840
+ if($existingOwner -ne $current.Value -and ($existingOwner -ne $admins.Value -or -not $currentIsAdmin)) { throw 'private ACL owner is invalid' }
16841
+ $failureStage='acl_mutation'
16776
16842
  $acl.SetAccessRuleProtection($true,$false)
16777
16843
  foreach($existingRule in @($acl.Access)) { [void]$acl.RemoveAccessRuleSpecific($existingRule) }
16778
16844
  $inherit=if($kind -eq 'directory') {
@@ -16788,11 +16854,14 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
16788
16854
  )
16789
16855
  [void]$acl.AddAccessRule($rule)
16790
16856
  }
16857
+ $failureStage='acl_write'
16791
16858
  (Get-Item -LiteralPath $path).SetAccessControl($acl)
16792
16859
  }
16860
+ $failureStage='acl_readback'
16793
16861
  $actual=Get-Acl -LiteralPath $path
16862
+ $failureStage='acl_verification'
16794
16863
  $owner=(New-Object Security.Principal.NTAccount($actual.Owner)).Translate([Security.Principal.SecurityIdentifier]).Value
16795
- if($owner -ne $current.Value) { throw 'private ACL owner is invalid' }
16864
+ if($owner -ne $current.Value -and ($owner -ne $admins.Value -or -not $currentIsAdmin)) { throw 'private ACL owner is invalid' }
16796
16865
  if($mode -ne 'verify-owner') {
16797
16866
  if(-not $actual.AreAccessRulesProtected) { throw 'private ACL inheritance is invalid' }
16798
16867
  $userFull=$false
@@ -16807,9 +16876,28 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
16807
16876
  }
16808
16877
  [Console]::Out.WriteLine((@{id=$requestId;status='ok'} | ConvertTo-Json -Compress))
16809
16878
  } catch {
16810
- [Console]::Out.WriteLine((@{id=$requestId;status='error'} | ConvertTo-Json -Compress))
16879
+ [Console]::Out.WriteLine((@{id=$requestId;status='error';error_code=$failureStage} | ConvertTo-Json -Compress))
16811
16880
  }
16812
16881
  }`;
16882
+ WINDOWS_IDENTITY_COMMAND_TIMEOUT_MS = 15e3;
16883
+ WINDOWS_PRIVATE_ACL_BROKER_STARTUP_TIMEOUT_MS = 45e3;
16884
+ SMALL_IDENTITY_COMMAND_TIMEOUT_MS = 5e3;
16885
+ windowsPowerShellEnvironment = (extra = {}) => {
16886
+ const sanitizedExtra = { ...extra };
16887
+ delete sanitizedExtra.PSModulePath;
16888
+ const windowsRoot = process.env.SystemRoot ?? process.env.WINDIR;
16889
+ return {
16890
+ ...sanitizedExtra,
16891
+ NODE_ENV: process.env.NODE_ENV,
16892
+ SystemRoot: process.env.SystemRoot,
16893
+ WINDIR: process.env.WINDIR,
16894
+ ComSpec: process.env.ComSpec,
16895
+ PATHEXT: process.env.PATHEXT,
16896
+ PSModulePath: windowsRoot ? windowsPath.join(windowsRoot, "System32", "WindowsPowerShell", "v1.0", "Modules") : void 0,
16897
+ TEMP: process.env.TEMP,
16898
+ TMP: process.env.TMP
16899
+ };
16900
+ };
16813
16901
  windowsPrivateAclBrokerInvocation = () => ({
16814
16902
  args: [
16815
16903
  "-NoLogo",
@@ -16818,16 +16906,7 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
16818
16906
  "-EncodedCommand",
16819
16907
  Buffer.from(WINDOWS_PRIVATE_ACL_BROKER_SCRIPT, "utf16le").toString("base64")
16820
16908
  ],
16821
- env: {
16822
- NODE_ENV: process.env.NODE_ENV,
16823
- SystemRoot: process.env.SystemRoot,
16824
- WINDIR: process.env.WINDIR,
16825
- ComSpec: process.env.ComSpec,
16826
- PATHEXT: process.env.PATHEXT,
16827
- PSModulePath: process.env.PSModulePath,
16828
- TEMP: process.env.TEMP,
16829
- TMP: process.env.TMP
16830
- }
16909
+ env: windowsPowerShellEnvironment()
16831
16910
  });
16832
16911
  WindowsPrivateAclBroker = class {
16833
16912
  constructor(dependencies = {}) {
@@ -16846,6 +16925,7 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
16846
16925
  await operation;
16847
16926
  };
16848
16927
  this.requestTimeoutMs = dependencies.requestTimeoutMs ?? 15e3;
16928
+ this.startupRequestTimeoutMs = dependencies.startupRequestTimeoutMs ?? WINDOWS_PRIVATE_ACL_BROKER_STARTUP_TIMEOUT_MS;
16849
16929
  this.idleShutdownMs = dependencies.idleShutdownMs ?? 3e4;
16850
16930
  this.spawnProcess = dependencies.spawnProcess ?? (() => {
16851
16931
  const invocation = windowsPrivateAclBrokerInvocation();
@@ -16896,7 +16976,19 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
16896
16976
  let response;
16897
16977
  try {
16898
16978
  const parsed = JSON.parse(line);
16899
- 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");
16979
+ const status = String(parsed.status);
16980
+ const expectedKeys = status === "error" ? "error_code\0id\0status" : "id\0status";
16981
+ if (Object.keys(parsed).sort().join("\0") !== expectedKeys || parsed.id !== pending.id || !["ok", "missing", "error"].includes(status) || status === "error" && ![
16982
+ "request_validation",
16983
+ "path_lookup",
16984
+ "principal_resolution",
16985
+ "acl_read",
16986
+ "owner_validation",
16987
+ "acl_mutation",
16988
+ "acl_write",
16989
+ "acl_readback",
16990
+ "acl_verification"
16991
+ ].includes(String(parsed.error_code))) throw new Error("invalid response");
16900
16992
  response = parsed;
16901
16993
  } catch {
16902
16994
  this.failChild(child, new Error("Windows private ACL broker returned malformed output."));
@@ -16911,12 +17003,16 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
16911
17003
  error48.code = "ENOENT";
16912
17004
  pending.reject(error48);
16913
17005
  } else {
16914
- pending.reject(new Error(`Windows private ACL ${pending.mode} failed.`));
17006
+ pending.reject(new Error(
17007
+ `Windows private ACL ${pending.mode} failed (${response.error_code}).`
17008
+ ));
16915
17009
  }
16916
17010
  this.scheduleIdleShutdown();
16917
17011
  }
16918
17012
  ensureChild() {
16919
- if (this.child && this.child.exitCode === null && !this.child.killed) return this.child;
17013
+ if (this.child && this.child.exitCode === null && !this.child.killed) {
17014
+ return { child: this.child, startedNow: false };
17015
+ }
16920
17016
  const child = this.spawnProcess();
16921
17017
  this.child = child;
16922
17018
  this.outputBuffer = "";
@@ -16947,7 +17043,7 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
16947
17043
  for (const stream of [child.stdin, child.stdout, child.stderr]) {
16948
17044
  stream.unref?.();
16949
17045
  }
16950
- return child;
17046
+ return { child, startedNow: true };
16951
17047
  }
16952
17048
  async dispatch(path, kind, mode) {
16953
17049
  if (this.idleTimer) {
@@ -16965,11 +17061,12 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
16965
17061
  if (Buffer.byteLength(request, "utf8") > 65536) {
16966
17062
  throw new Error("Windows private ACL broker request exceeds its input limit.");
16967
17063
  }
16968
- const child = this.ensureChild();
17064
+ const { child, startedNow } = this.ensureChild();
17065
+ const timeoutMs = startedNow ? this.startupRequestTimeoutMs : this.requestTimeoutMs;
16969
17066
  await new Promise((resolvePromise, reject) => {
16970
17067
  const timer = setTimeout(() => {
16971
17068
  this.failChild(child, new Error("Windows private ACL broker request timed out."));
16972
- }, this.requestTimeoutMs);
17069
+ }, timeoutMs);
16973
17070
  this.pending = { id: id2, mode, resolve: resolvePromise, reject, timer };
16974
17071
  child.stdin.write(request, "utf8", (error48) => {
16975
17072
  if (error48) {
@@ -17072,26 +17169,40 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
17072
17169
  return null;
17073
17170
  }
17074
17171
  };
17172
+ windowsProcessIdentityInvocation = (pid) => {
17173
+ const script = `$ErrorActionPreference='Stop';$p=[Diagnostics.Process]::GetProcessById([int]$env:VTX_PROCESS_PID);[Console]::Out.Write($p.StartTime.ToUniversalTime().ToFileTimeUtc())`;
17174
+ return {
17175
+ args: [
17176
+ "-NoLogo",
17177
+ "-NoProfile",
17178
+ "-NonInteractive",
17179
+ "-EncodedCommand",
17180
+ Buffer.from(script, "utf16le").toString("base64")
17181
+ ],
17182
+ env: windowsPowerShellEnvironment({ VTX_PROCESS_PID: String(pid) }),
17183
+ timeoutMs: WINDOWS_IDENTITY_COMMAND_TIMEOUT_MS
17184
+ };
17185
+ };
17186
+ windowsBootIdentityInvocation = () => {
17187
+ const script = `$ErrorActionPreference='Stop';$s=[System.Management.ManagementObjectSearcher]::new('SELECT LastBootUpTime FROM Win32_OperatingSystem');try{$c=$s.Get();$e=$c.GetEnumerator();if(-not $e.MoveNext()){throw 'boot identity unavailable'};$raw=[string]$e.Current['LastBootUpTime'];$v=[System.Management.ManagementDateTimeConverter]::ToDateTime($raw).ToUniversalTime().ToFileTimeUtc();[Console]::Out.Write($v)}finally{if($c){$c.Dispose()};$s.Dispose()}`;
17188
+ return {
17189
+ args: [
17190
+ "-NoLogo",
17191
+ "-NoProfile",
17192
+ "-NonInteractive",
17193
+ "-EncodedCommand",
17194
+ Buffer.from(script, "utf16le").toString("base64")
17195
+ ],
17196
+ env: windowsPowerShellEnvironment(),
17197
+ timeoutMs: WINDOWS_IDENTITY_COMMAND_TIMEOUT_MS
17198
+ };
17199
+ };
17075
17200
  windowsProcessIdentity = async (pid) => await new Promise((resolvePromise) => {
17076
- const script = `$ErrorActionPreference='Stop';$p=Get-Process -Id ([int]$env:VTX_PROCESS_PID);[Console]::Out.Write($p.StartTime.ToUniversalTime().ToFileTimeUtc())`;
17077
- const child = spawn("powershell.exe", [
17078
- "-NoLogo",
17079
- "-NoProfile",
17080
- "-NonInteractive",
17081
- "-EncodedCommand",
17082
- Buffer.from(script, "utf16le").toString("base64")
17083
- ], {
17201
+ const invocation = windowsProcessIdentityInvocation(pid);
17202
+ const child = spawn("powershell.exe", [...invocation.args], {
17084
17203
  windowsHide: true,
17085
17204
  stdio: ["ignore", "pipe", "ignore"],
17086
- env: {
17087
- NODE_ENV: process.env.NODE_ENV,
17088
- SystemRoot: process.env.SystemRoot,
17089
- WINDIR: process.env.WINDIR,
17090
- ComSpec: process.env.ComSpec,
17091
- PATHEXT: process.env.PATHEXT,
17092
- PSModulePath: process.env.PSModulePath,
17093
- VTX_PROCESS_PID: String(pid)
17094
- }
17205
+ env: invocation.env
17095
17206
  });
17096
17207
  const output3 = [];
17097
17208
  let settled = false;
@@ -17104,7 +17215,7 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
17104
17215
  const timer = setTimeout(() => {
17105
17216
  child.kill("SIGKILL");
17106
17217
  settle(null);
17107
- }, 5e3);
17218
+ }, invocation.timeoutMs);
17108
17219
  child.stdout.on("data", (chunk) => {
17109
17220
  if (output3.reduce((total, item) => total + item.byteLength, 0) + Buffer.byteLength(chunk) > 1024) {
17110
17221
  child.kill("SIGKILL");
@@ -17142,7 +17253,7 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
17142
17253
  settle(code === 0 && Number.isFinite(started) ? `darwin:${Math.floor(started / 1e3)}` : null);
17143
17254
  });
17144
17255
  });
17145
- runSmallIdentityCommand = async (command, args, env) => await new Promise((resolvePromise) => {
17256
+ runSmallIdentityCommand = async (command, args, env, timeoutMs = SMALL_IDENTITY_COMMAND_TIMEOUT_MS) => await new Promise((resolvePromise) => {
17146
17257
  const child = spawn(command, [...args], {
17147
17258
  windowsHide: true,
17148
17259
  stdio: ["ignore", "pipe", "ignore"],
@@ -17160,7 +17271,7 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
17160
17271
  const timer = setTimeout(() => {
17161
17272
  child.kill("SIGKILL");
17162
17273
  settle(null);
17163
- }, 5e3);
17274
+ }, timeoutMs);
17164
17275
  child.stdout.on("data", (chunk) => {
17165
17276
  bytes3 += Buffer.byteLength(chunk);
17166
17277
  if (bytes3 > 4096) {
@@ -17189,21 +17300,13 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
17189
17300
  identity = null;
17190
17301
  }
17191
17302
  } else if (platform === "win32") {
17192
- const script = `$ErrorActionPreference='Stop';$v=(Get-CimInstance Win32_OperatingSystem).LastBootUpTime.ToUniversalTime().ToFileTimeUtc();[Console]::Out.Write($v)`;
17193
- identity = await runSmallIdentityCommand("powershell.exe", [
17194
- "-NoLogo",
17195
- "-NoProfile",
17196
- "-NonInteractive",
17197
- "-EncodedCommand",
17198
- Buffer.from(script, "utf16le").toString("base64")
17199
- ], {
17200
- NODE_ENV: process.env.NODE_ENV,
17201
- SystemRoot: process.env.SystemRoot,
17202
- WINDIR: process.env.WINDIR,
17203
- ComSpec: process.env.ComSpec,
17204
- PATHEXT: process.env.PATHEXT,
17205
- PSModulePath: process.env.PSModulePath
17206
- });
17303
+ const invocation = windowsBootIdentityInvocation();
17304
+ identity = await runSmallIdentityCommand(
17305
+ "powershell.exe",
17306
+ invocation.args,
17307
+ invocation.env,
17308
+ invocation.timeoutMs
17309
+ );
17207
17310
  } else if (platform === "darwin") {
17208
17311
  identity = await runSmallIdentityCommand("sysctl", ["-n", "kern.boottime"], void 0);
17209
17312
  }
@@ -19548,6 +19651,7 @@ var init_codex_app_server = __esm({
19548
19651
  this.retryAtMs = options.retryAtMs ?? null;
19549
19652
  this.usage = options.usage ?? null;
19550
19653
  this.rateLimits = options.rateLimits ?? null;
19654
+ this.processExit = options.processExit ?? null;
19551
19655
  }
19552
19656
  };
19553
19657
  objectOrNull = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -19852,7 +19956,7 @@ var init_codex_app_server = __esm({
19852
19956
  };
19853
19957
  GUARDIAN_SCRIPT = String.raw`
19854
19958
  import { spawn } from 'node:child_process';
19855
- import { chmod, open, rename, rm, writeFile } from 'node:fs/promises';
19959
+ import { chmod, open, readFile, rename, rm, writeFile } from 'node:fs/promises';
19856
19960
  import { dirname } from 'node:path';
19857
19961
  const input = JSON.parse(Buffer.from(process.argv[1], 'base64url').toString('utf8'));
19858
19962
  // The guardian owns the process boundary for every attempt. Apply a private
@@ -19882,7 +19986,7 @@ const replaceReceipt = async (temporary) => {
19882
19986
  };
19883
19987
  let receiptWriteSequence = 0;
19884
19988
  let receiptWriteChain = Promise.resolve();
19885
- const writeReceipt = (state, childPid) => {
19989
+ const writeReceipt = (state, childPid, termination = null) => {
19886
19990
  const write = async () => {
19887
19991
  const body = JSON.stringify({
19888
19992
  schema_version: 'vtx_codex_guardian_v1',
@@ -19891,6 +19995,7 @@ const writeReceipt = (state, childPid) => {
19891
19995
  guardian_pid: process.pid,
19892
19996
  child_pid: childPid ?? null,
19893
19997
  boot_identity: input.bootIdentity,
19998
+ termination,
19894
19999
  updated_at: new Date().toISOString(),
19895
20000
  }) + '\n';
19896
20001
  receiptWriteSequence += 1;
@@ -19915,8 +20020,11 @@ const writeReceipt = (state, childPid) => {
19915
20020
  };
19916
20021
  let child = null;
19917
20022
  let shuttingDown = false;
20023
+ let shutdownRequested = false;
20024
+ let childStartedAt = null;
19918
20025
  let forceTimer = null;
19919
- const shutdown = () => {
20026
+ const stopChild = (expected) => {
20027
+ if (expected) shutdownRequested = true;
19920
20028
  if (shuttingDown) return;
19921
20029
  shuttingDown = true;
19922
20030
  if (!child) return;
@@ -19926,6 +20034,19 @@ const shutdown = () => {
19926
20034
  }, input.shutdownGraceMs);
19927
20035
  forceTimer.unref();
19928
20036
  };
20037
+ let shutdownStarted = false;
20038
+ const shutdown = async () => {
20039
+ if (shutdownStarted) return;
20040
+ shutdownStarted = true;
20041
+ let expected = false;
20042
+ try {
20043
+ expected = (await readFile(input.shutdownIntentPath, 'utf8')) === input.processToken;
20044
+ await rm(input.shutdownIntentPath, { force: true });
20045
+ } catch {
20046
+ expected = false;
20047
+ }
20048
+ stopChild(expected);
20049
+ };
19929
20050
  process.stdin.once('end', shutdown);
19930
20051
  process.stdin.once('close', shutdown);
19931
20052
  process.stdout.on('error', () => undefined);
@@ -19941,6 +20062,7 @@ child = spawn(input.binary, input.args, {
19941
20062
  windowsHide: true,
19942
20063
  detached: false,
19943
20064
  });
20065
+ childStartedAt = Date.now();
19944
20066
  child.stdin.on('error', () => undefined);
19945
20067
  process.stdin.pipe(child.stdin, { end: false });
19946
20068
  child.stdout.pipe(process.stdout);
@@ -19949,18 +20071,33 @@ child.once('spawn', async () => {
19949
20071
  try {
19950
20072
  await writeReceipt('running', child.pid ?? null);
19951
20073
  } catch {
19952
- shutdown();
20074
+ stopChild(false);
19953
20075
  }
19954
20076
  });
19955
- child.once('error', shutdown);
19956
- child.once('close', async () => {
20077
+ child.once('error', () => stopChild(false));
20078
+ child.once('close', async (code, signal) => {
19957
20079
  if (forceTimer) clearTimeout(forceTimer);
19958
20080
  // A pending promise alone does not keep Node's event loop alive after the
19959
20081
  // child and parent stdin have both closed. Hold one local handle until the
19960
20082
  // serialized terminal tombstone is durably replaced.
19961
20083
  const receiptKeepAlive = setInterval(() => undefined, 1_000);
19962
20084
  try {
19963
- await writeReceipt('terminated', child.pid ?? null);
20085
+ const normalizedSignal = typeof signal === 'string' && /^SIG[A-Z0-9]{1,28}$/.test(signal)
20086
+ ? signal.toLowerCase()
20087
+ : null;
20088
+ const normalizedCode = normalizedSignal === null && Number.isInteger(code)
20089
+ && code >= -(2 ** 31) && code <= (2 ** 31) - 1
20090
+ ? code
20091
+ : null;
20092
+ await writeReceipt('terminated', child.pid ?? null, {
20093
+ exit_code: normalizedCode,
20094
+ signal: normalizedSignal,
20095
+ expected: shutdownRequested,
20096
+ uptime_ms: Math.min(
20097
+ Math.max(0, Date.now() - (childStartedAt ?? Date.now())),
20098
+ Number.MAX_SAFE_INTEGER,
20099
+ ),
20100
+ });
19964
20101
  } finally {
19965
20102
  clearInterval(receiptKeepAlive);
19966
20103
  process.exit(0);
@@ -20012,6 +20149,7 @@ child.once('close', async () => {
20012
20149
  guardian_pid: null,
20013
20150
  child_pid: null,
20014
20151
  boot_identity: guardian.bootIdentity ?? null,
20152
+ termination: null,
20015
20153
  updated_at: (/* @__PURE__ */ new Date()).toISOString()
20016
20154
  })}
20017
20155
  `;
@@ -20036,12 +20174,15 @@ child.once('close', async () => {
20036
20174
  };
20037
20175
  parseGuardianReceipt = (raw) => {
20038
20176
  const value = JSON.parse(raw);
20039
- if (value.schema_version !== "vtx_codex_guardian_v1" || typeof value.process_token !== "string" || !/^[0-9a-f]{64}$/u.test(value.process_token) || !["spawn_intent", "starting", "running", "terminated"].includes(String(value.state)) || value.guardian_pid !== null && !Number.isSafeInteger(value.guardian_pid) || value.state === "spawn_intent" && value.guardian_pid !== null || value.state !== "spawn_intent" && !Number.isSafeInteger(value.guardian_pid) || value.child_pid !== null && !Number.isSafeInteger(value.child_pid) || value.boot_identity !== null && value.boot_identity !== void 0 && typeof value.boot_identity !== "string" || typeof value.updated_at !== "string" || !Number.isFinite(Date.parse(value.updated_at))) {
20177
+ const rawTermination = value.termination;
20178
+ const termination = rawTermination === null || rawTermination === void 0 ? null : objectOrNull(rawTermination);
20179
+ if (value.schema_version !== "vtx_codex_guardian_v1" || typeof value.process_token !== "string" || !/^[0-9a-f]{64}$/u.test(value.process_token) || !["spawn_intent", "starting", "running", "terminated"].includes(String(value.state)) || value.guardian_pid !== null && !Number.isSafeInteger(value.guardian_pid) || value.state === "spawn_intent" && value.guardian_pid !== null || value.state !== "spawn_intent" && !Number.isSafeInteger(value.guardian_pid) || value.child_pid !== null && !Number.isSafeInteger(value.child_pid) || value.boot_identity !== null && value.boot_identity !== void 0 && typeof value.boot_identity !== "string" || typeof value.updated_at !== "string" || !Number.isFinite(Date.parse(value.updated_at)) || rawTermination !== null && rawTermination !== void 0 && termination === null || termination !== null && (value.state !== "terminated" || termination.exit_code !== null && (!Number.isInteger(termination.exit_code) || Number(termination.exit_code) < -(2 ** 31) || Number(termination.exit_code) > 2 ** 31 - 1) || termination.signal !== null && (typeof termination.signal !== "string" || !/^sig[a-z0-9]{1,28}$/u.test(termination.signal)) || termination.signal !== null && termination.exit_code !== null || typeof termination.expected !== "boolean" || !Number.isSafeInteger(termination.uptime_ms) || Number(termination.uptime_ms) < 0)) {
20040
20180
  throw new Error("Codex guardian receipt is invalid.");
20041
20181
  }
20042
20182
  return {
20043
20183
  ...value,
20044
- boot_identity: typeof value.boot_identity === "string" ? value.boot_identity : null
20184
+ boot_identity: typeof value.boot_identity === "string" ? value.boot_identity : null,
20185
+ termination
20045
20186
  };
20046
20187
  };
20047
20188
  readCodexGuardianReceipt = async (path) => {
@@ -20085,6 +20226,7 @@ child.once('close', async () => {
20085
20226
  args,
20086
20227
  processToken: options.guardian.processToken,
20087
20228
  receiptPath: options.guardian.receiptPath,
20229
+ shutdownIntentPath: `${options.guardian.receiptPath}.shutdown-intent`,
20088
20230
  shutdownGraceMs: Math.max(25, options.guardian.shutdownGraceMs ?? 2e3),
20089
20231
  bootIdentity: options.guardian.bootIdentity
20090
20232
  }), "utf8").toString("base64url");
@@ -20106,13 +20248,33 @@ child.once('close', async () => {
20106
20248
  this.nextRequestId = 1;
20107
20249
  this.fatalError = null;
20108
20250
  this.closed = false;
20251
+ this.processCloseObserved = false;
20252
+ this.shutdownRequested = false;
20253
+ this.stderrBytesObserved = 0;
20254
+ this.stderrNonempty = false;
20255
+ this.stderrTruncated = false;
20256
+ this.stderrTail = "";
20257
+ this.stderrClass = "none";
20109
20258
  this.process = options.process;
20110
20259
  this.codexHome = options.codexHome;
20111
- this.process.stderr.resume();
20260
+ this.sessionGeneration = options.sessionGeneration;
20261
+ this.startedAtMs = Date.now();
20262
+ this.guardian = options.guardian;
20263
+ this.onProcessExit = options.onProcessExit;
20264
+ this.process.stderr.on("data", (chunk) => this.observeStderr(chunk));
20112
20265
  const lines = createInterface({ input: this.process.stdout, crlfDelay: Infinity });
20113
20266
  lines.on("line", (line) => this.handleLine(line));
20114
- this.process.once("error", () => this.failTransport("codex_process_error"));
20115
- this.process.once("close", () => this.failTransport("codex_process_closed"));
20267
+ this.process.once("error", () => {
20268
+ });
20269
+ this.process.once("close", (code, signal) => {
20270
+ void this.handleProcessClose(code, signal);
20271
+ });
20272
+ }
20273
+ static {
20274
+ this.STDERR_OBSERVATION_CAP_BYTES = 65536;
20275
+ }
20276
+ static {
20277
+ this.STDERR_CLASSIFICATION_TAIL_CHARS = 1024;
20116
20278
  }
20117
20279
  static async start(options) {
20118
20280
  const spawnProcess = options.spawnProcess ?? defaultSpawn;
@@ -20173,7 +20335,13 @@ child.once('close', async () => {
20173
20335
  });
20174
20336
  }
20175
20337
  }
20176
- const session = new _CodexAppServerSession({ process: child, codexHome: options.codexHome });
20338
+ const session = new _CodexAppServerSession({
20339
+ process: child,
20340
+ codexHome: options.codexHome,
20341
+ sessionGeneration: options.sessionGeneration ?? 1,
20342
+ guardian: guardian ?? null,
20343
+ onProcessExit: options.onProcessExit ?? null
20344
+ });
20177
20345
  try {
20178
20346
  const initialized = await session.request("initialize", {
20179
20347
  clientInfo: {
@@ -20288,13 +20456,14 @@ child.once('close', async () => {
20288
20456
  }
20289
20457
  for (const listener of this.notificationListeners) listener({ method, params });
20290
20458
  }
20291
- failTransport(code) {
20459
+ failTransport(code, processExit = null) {
20292
20460
  if (this.fatalError) return;
20293
20461
  this.fatalError = new CodexAppServerError({
20294
20462
  message: "Codex app-server transport became unavailable.",
20295
20463
  category: "transport",
20296
20464
  code,
20297
- retryable: true
20465
+ retryable: true,
20466
+ processExit
20298
20467
  });
20299
20468
  for (const pending of this.pending.values()) {
20300
20469
  if (pending.timer) clearTimeout(pending.timer);
@@ -20395,6 +20564,77 @@ child.once('close', async () => {
20395
20564
  this.fatalListeners.add(listener);
20396
20565
  return () => this.fatalListeners.delete(listener);
20397
20566
  }
20567
+ observeStderr(chunk) {
20568
+ const bytes3 = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, "utf8");
20569
+ if (bytes3.length === 0) return;
20570
+ this.stderrNonempty = true;
20571
+ const remaining = Math.max(
20572
+ 0,
20573
+ _CodexAppServerSession.STDERR_OBSERVATION_CAP_BYTES - this.stderrBytesObserved
20574
+ );
20575
+ if (remaining === 0) {
20576
+ this.stderrTruncated = true;
20577
+ return;
20578
+ }
20579
+ const observed = bytes3.subarray(0, remaining);
20580
+ this.stderrBytesObserved += observed.length;
20581
+ if (bytes3.length > remaining) this.stderrTruncated = true;
20582
+ this.stderrTail = `${this.stderrTail}${observed.toString("utf8")}`.slice(-_CodexAppServerSession.STDERR_CLASSIFICATION_TAIL_CHARS).toLowerCase();
20583
+ const classified = /heap out of memory|allocation failed|out of memory/u.test(this.stderrTail) ? "out_of_memory" : /fatal runtime error|fatal error|\bpanic(?:ked)?\b/u.test(this.stderrTail) ? "runtime_fatal" : /access violation|segmentation fault|\bsegfault\b/u.test(this.stderrTail) ? "memory_fault" : /broken pipe|econnreset|connection reset/u.test(this.stderrTail) ? "pipe_or_connection" : null;
20584
+ if (classified) this.stderrClass = classified;
20585
+ else if (this.stderrClass === "none") this.stderrClass = "unclassified";
20586
+ }
20587
+ async handleProcessClose(rawCode, rawSignal) {
20588
+ this.processCloseObserved = true;
20589
+ let termination = null;
20590
+ if (this.guardian) {
20591
+ try {
20592
+ const receipt = await readCodexGuardianReceipt(this.guardian.receiptPath);
20593
+ if (receipt?.process_token === this.guardian.processToken && receipt.state === "terminated") {
20594
+ termination = receipt.termination;
20595
+ }
20596
+ } catch {
20597
+ }
20598
+ }
20599
+ const diagnostics = this.processExitDiagnostics(rawCode, rawSignal, termination);
20600
+ if (this.guardian) {
20601
+ await rm2(`${this.guardian.receiptPath}.shutdown-intent`, { force: true }).catch(() => void 0);
20602
+ }
20603
+ try {
20604
+ this.onProcessExit?.(diagnostics);
20605
+ } catch {
20606
+ }
20607
+ this.failTransport("codex_process_closed", diagnostics);
20608
+ }
20609
+ processExitDiagnostics(rawCode, rawSignal, termination) {
20610
+ const source = termination ? "child_receipt" : this.guardian ? "guardian_process" : "direct_process";
20611
+ const reportedCode = termination?.exit_code ?? rawCode;
20612
+ const reportedSignal = termination?.signal ?? rawSignal;
20613
+ const signal = typeof reportedSignal === "string" && /^(?:SIG[A-Z0-9]{1,28}|sig[a-z0-9]{1,28})$/u.test(reportedSignal) ? reportedSignal.toLowerCase() : null;
20614
+ const exitCode = signal === null && typeof reportedCode === "number" && Number.isInteger(reportedCode) && reportedCode >= -(2 ** 31) && reportedCode <= 2 ** 31 - 1 ? reportedCode : null;
20615
+ const diagnostics = {
20616
+ schema_version: "external_inference_process_exit_v1",
20617
+ runtime: "codex_app_server",
20618
+ source,
20619
+ session_generation: this.sessionGeneration,
20620
+ expected_shutdown: termination?.expected ?? this.shutdownRequested,
20621
+ exit_class: signal !== null ? "signal" : exitCode === null ? "unknown" : exitCode === 0 ? "zero" : "nonzero",
20622
+ exit_code: exitCode,
20623
+ signal,
20624
+ uptime_ms: termination?.uptime_ms ?? Math.min(
20625
+ Math.max(0, Date.now() - this.startedAtMs),
20626
+ Number.MAX_SAFE_INTEGER
20627
+ ),
20628
+ pending_request_count: this.pending.size,
20629
+ active_thread_count: this.threads.size,
20630
+ stderr_nonempty: this.stderrNonempty,
20631
+ stderr_bytes_observed: this.stderrBytesObserved,
20632
+ stderr_truncated: this.stderrTruncated,
20633
+ stderr_class: this.stderrNonempty ? this.stderrClass : "none"
20634
+ };
20635
+ this.stderrTail = "";
20636
+ return diagnostics;
20637
+ }
20398
20638
  isUsable() {
20399
20639
  return !this.closed && this.fatalError === null && this.process.exitCode === null;
20400
20640
  }
@@ -20819,6 +21059,7 @@ child.once('close', async () => {
20819
21059
  code: error48.code,
20820
21060
  retryable: error48.retryable,
20821
21061
  dispatchOutcome: requestWritten ? "outcome_unknown" : "not_dispatched",
21062
+ processExit: error48.processExit,
20822
21063
  cause: error48
20823
21064
  }));
20824
21065
  });
@@ -21022,6 +21263,7 @@ child.once('close', async () => {
21022
21263
  code: error48.code,
21023
21264
  retryable: error48.retryable,
21024
21265
  dispatchOutcome: "outcome_unknown",
21266
+ processExit: error48.processExit,
21025
21267
  cause: error48
21026
21268
  });
21027
21269
  }
@@ -21274,6 +21516,20 @@ child.once('close', async () => {
21274
21516
  async close() {
21275
21517
  if (this.closed) return;
21276
21518
  this.closed = true;
21519
+ if (this.processCloseObserved || this.process.exitCode !== null) {
21520
+ if (this.guardian) {
21521
+ await rm2(`${this.guardian.receiptPath}.shutdown-intent`, { force: true }).catch(() => void 0);
21522
+ }
21523
+ return;
21524
+ }
21525
+ this.shutdownRequested = true;
21526
+ if (this.guardian) {
21527
+ await writeFile(
21528
+ `${this.guardian.receiptPath}.shutdown-intent`,
21529
+ this.guardian.processToken,
21530
+ { mode: 384 }
21531
+ ).catch(() => void 0);
21532
+ }
21277
21533
  this.process.stdin.end();
21278
21534
  const exited = await new Promise((resolve6) => {
21279
21535
  if (this.process.exitCode !== null) {
@@ -21482,11 +21738,12 @@ import {
21482
21738
  import { createHash as createHash4, randomBytes as randomBytes3 } from "node:crypto";
21483
21739
  import { tmpdir as tmpdir2 } from "node:os";
21484
21740
  import { isAbsolute as isAbsolute3, join as join4, relative as relative2, resolve as resolve3, sep as sep2 } from "node:path";
21485
- var MAX_PROMPT_BYTES2, CODEX_MODEL_NAME_PATTERN, CODEX_REASONING_EFFORT_PATTERN, codexRecoveryCheckpointProcessFingerprint, proveCodexSameBootQuiescence, tokenUsageReceiptSchema, turnResultReceiptSchema, terminalReceiptSchema, recoveryCheckpointSchema, recoveryFileSchema, FileCodexAttemptRecoveryStore, utf8Bytes, assertAttemptActive, tomlString, permissionConfig, ensureDedicatedCodexHome, createIsolatedCodexAttemptResources, createIsolatedCodexHostResources, validateAttemptInput, isStrictDescendant, assertRecoveryResourceScope, removeRecoveredThread, confirmGuardianTerminatedForRecovery, reconcileCodexAttemptRecovery, CodexSubscriptionAdapter;
21741
+ var MAX_PROMPT_BYTES2, CODEX_MODEL_NAME_PATTERN, CODEX_REASONING_EFFORT_PATTERN, codexRecoveryCheckpointProcessFingerprint, proveCodexSameBootQuiescence, tokenUsageReceiptSchema, turnResultReceiptSchema, terminalReceiptSchema, recoveryCheckpointSchema, recoveryFileSchema, FileCodexAttemptRecoveryStore, utf8Bytes, assertAttemptActive, tomlString, permissionConfig, ensureDedicatedCodexHome, createIsolatedCodexAttemptResources, createIsolatedCodexHostResources, validateAttemptInput, isStrictDescendant, assertRecoveryResourceScope, removeRecoveredThread, confirmGuardianTerminatedForRecovery, recoveredCodexProcessExit, reconcileCodexAttemptRecovery, CodexSubscriptionAdapter;
21486
21742
  var init_codex_adapter = __esm({
21487
21743
  "lib/inference-host/codex-adapter.ts"() {
21488
21744
  "use strict";
21489
21745
  init_zod();
21746
+ init_external_inference_contract();
21490
21747
  init_codex_app_server();
21491
21748
  init_codex_binary();
21492
21749
  init_config();
@@ -21561,7 +21818,8 @@ var init_codex_adapter = __esm({
21561
21818
  dispatchOutcome: external_exports.enum(["not_dispatched", "confirmed_dispatched", "outcome_unknown"]),
21562
21819
  httpStatusCode: external_exports.number().int().min(100).max(599).nullable().optional(),
21563
21820
  retryAtMs: external_exports.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).nullable().optional(),
21564
- usage: tokenUsageReceiptSchema.nullable().optional()
21821
+ usage: tokenUsageReceiptSchema.nullable().optional(),
21822
+ processExit: codexProcessExitDiagnosticsSchema.nullable().optional()
21565
21823
  })
21566
21824
  })
21567
21825
  ]);
@@ -21577,6 +21835,7 @@ var init_codex_adapter = __esm({
21577
21835
  processToken: external_exports.string().regex(/^[0-9a-f]{64}$/u).nullable(),
21578
21836
  processReceiptPath: external_exports.string().min(1).max(4096).nullable(),
21579
21837
  processState: external_exports.enum(["unmanaged", "spawn_intent", "running", "terminated"]),
21838
+ sessionGeneration: external_exports.number().int().positive().max(2147483647).nullable().optional(),
21580
21839
  bootIdentity: external_exports.string().min(1).max(4096).nullable().optional(),
21581
21840
  cleanupConfirmed: external_exports.boolean(),
21582
21841
  adapterRequestId: external_exports.string().min(1).max(512).nullable(),
@@ -21971,6 +22230,30 @@ var init_codex_adapter = __esm({
21971
22230
  throw error48;
21972
22231
  }
21973
22232
  };
22233
+ recoveredCodexProcessExit = (checkpoint, receipt) => {
22234
+ const termination = receipt?.termination;
22235
+ const sessionGeneration = checkpoint.sessionGeneration ?? null;
22236
+ if (!termination || termination.expected || sessionGeneration === null) return null;
22237
+ const signal = termination.signal;
22238
+ const exitCode = signal === null ? termination.exit_code : null;
22239
+ return {
22240
+ schema_version: "external_inference_process_exit_v1",
22241
+ runtime: "codex_app_server",
22242
+ source: "child_receipt",
22243
+ session_generation: sessionGeneration,
22244
+ expected_shutdown: false,
22245
+ exit_class: signal !== null ? "signal" : exitCode === null ? "unknown" : exitCode === 0 ? "zero" : "nonzero",
22246
+ exit_code: exitCode,
22247
+ signal,
22248
+ uptime_ms: termination.uptime_ms,
22249
+ pending_request_count: null,
22250
+ active_thread_count: null,
22251
+ stderr_nonempty: null,
22252
+ stderr_bytes_observed: null,
22253
+ stderr_truncated: null,
22254
+ stderr_class: "unknown"
22255
+ };
22256
+ };
21974
22257
  reconcileCodexAttemptRecovery = async (options) => {
21975
22258
  const attempts = await options.recoveryHooks.loadAll?.() ?? {};
21976
22259
  const currentBootIdentity = options.bootIdentity ?? await readInferenceSystemBootIdentity();
@@ -22011,11 +22294,27 @@ var init_codex_adapter = __esm({
22011
22294
  assertRecoveryResourceScope(checkpoint);
22012
22295
  await removeRecoveredThread(checkpoint, options.codexHome);
22013
22296
  await rm3(checkpoint.resourceRoot, { recursive: true, force: true });
22297
+ const recoveredProcessExit = recoveredCodexProcessExit(
22298
+ checkpoint,
22299
+ await readCodexGuardianReceipt(guardian.receiptPath)
22300
+ );
22301
+ const dispatchOutcome = crossedBoot ? "outcome_unknown" : checkpoint.dispatchOutcome;
22014
22302
  await options.recoveryHooks.save({
22015
22303
  ...checkpoint,
22016
22304
  processState: "terminated",
22017
22305
  cleanupConfirmed: true,
22018
- dispatchOutcome: crossedBoot ? "outcome_unknown" : checkpoint.dispatchOutcome
22306
+ dispatchOutcome,
22307
+ terminal: checkpoint.terminal ?? (recoveredProcessExit ? {
22308
+ kind: "failed",
22309
+ error: {
22310
+ message: "Codex app-server transport became unavailable.",
22311
+ category: "transport",
22312
+ code: "codex_process_closed",
22313
+ retryable: true,
22314
+ dispatchOutcome,
22315
+ processExit: recoveredProcessExit
22316
+ }
22317
+ } : null)
22019
22318
  });
22020
22319
  reconciled += 1;
22021
22320
  }
@@ -22029,8 +22328,15 @@ var init_codex_adapter = __esm({
22029
22328
  this.durableSessionStart = null;
22030
22329
  this.closing = false;
22031
22330
  this.adapterClosePromise = null;
22331
+ this.sessionGeneration = 0;
22032
22332
  this.dependencies = dependencies;
22033
22333
  }
22334
+ emitDiagnostic(event) {
22335
+ try {
22336
+ this.dependencies.emitDiagnosticEvent?.(event);
22337
+ } catch {
22338
+ }
22339
+ }
22034
22340
  async readAccountRateLimits(deadlineAtMs, signal) {
22035
22341
  const codexHome = this.dependencies.codexHome;
22036
22342
  if (!codexHome) {
@@ -22049,6 +22355,9 @@ var init_codex_adapter = __esm({
22049
22355
  return await durableSession.session.readAccountRateLimits(deadlineAtMs, signal);
22050
22356
  }
22051
22357
  async startDurableSession(codexHome, deadlineAtMs) {
22358
+ this.sessionGeneration = Math.min(this.sessionGeneration + 1, 2147483647);
22359
+ const sessionGeneration = this.sessionGeneration;
22360
+ const activeAttemptIds = /* @__PURE__ */ new Set();
22052
22361
  const binary = await (this.dependencies.resolveBinary ?? resolvePinnedCodexBinary)();
22053
22362
  const guardianManaged = Boolean(
22054
22363
  this.dependencies.recoveryHooks && !this.dependencies.spawnProcess
@@ -22063,10 +22372,26 @@ var init_codex_adapter = __esm({
22063
22372
  codexHome,
22064
22373
  deadlineAtMs,
22065
22374
  spawnProcess: this.dependencies.spawnProcess,
22066
- guardian: processToken && processReceiptPath ? { processToken, receiptPath: processReceiptPath, bootIdentity: bootIdentity ?? void 0 } : void 0
22375
+ guardian: processToken && processReceiptPath ? { processToken, receiptPath: processReceiptPath, bootIdentity: bootIdentity ?? void 0 } : void 0,
22376
+ sessionGeneration,
22377
+ onProcessExit: (diagnostics) => {
22378
+ this.emitDiagnostic({
22379
+ event: "codex_app_server_closed",
22380
+ process_exit: diagnostics,
22381
+ active_attempt_ids: [...activeAttemptIds].sort().slice(0, 64),
22382
+ active_attempt_ids_truncated: activeAttemptIds.size > 64
22383
+ });
22384
+ }
22385
+ });
22386
+ this.emitDiagnostic({
22387
+ event: "codex_app_server_session_started",
22388
+ session_generation: sessionGeneration,
22389
+ replacement: sessionGeneration > 1
22067
22390
  });
22068
22391
  return {
22069
22392
  session,
22393
+ sessionGeneration,
22394
+ activeAttemptIds,
22070
22395
  codexHome,
22071
22396
  capabilityPreflight: null,
22072
22397
  processToken,
@@ -22306,6 +22631,7 @@ var init_codex_adapter = __esm({
22306
22631
  const receipt = await readCodexGuardianReceipt(checkpoint.processReceiptPath);
22307
22632
  if (checkpoint.processState === "terminated" || receipt === null || receipt.state === "terminated") {
22308
22633
  await rm3(checkpoint.processReceiptPath, { force: true });
22634
+ await rm3(`${checkpoint.processReceiptPath}.shutdown-intent`, { force: true });
22309
22635
  }
22310
22636
  }
22311
22637
  }
@@ -22353,6 +22679,7 @@ var init_codex_adapter = __esm({
22353
22679
  for (const session of sessions) {
22354
22680
  if (session.processReceiptPath && unconfirmedReceiptPaths !== null && !unconfirmedReceiptPaths.has(resolve3(session.processReceiptPath))) {
22355
22681
  await rm3(session.processReceiptPath, { force: true });
22682
+ await rm3(`${session.processReceiptPath}.shutdown-intent`, { force: true });
22356
22683
  }
22357
22684
  }
22358
22685
  })();
@@ -22397,7 +22724,8 @@ var init_codex_adapter = __esm({
22397
22724
  dispatchOutcome: recovered.dispatchOutcome,
22398
22725
  httpStatusCode: recovered.httpStatusCode,
22399
22726
  retryAtMs: recovered.retryAtMs,
22400
- usage: recovered.usage
22727
+ usage: recovered.usage,
22728
+ processExit: recovered.processExit
22401
22729
  });
22402
22730
  }
22403
22731
  throw new CodexAppServerError({
@@ -22430,6 +22758,7 @@ var init_codex_adapter = __esm({
22430
22758
  input.deadlineAtMs,
22431
22759
  input.signal
22432
22760
  );
22761
+ sessionLease.activeAttemptIds.add(input.attemptId);
22433
22762
  session = sessionLease.session;
22434
22763
  baseCheckpoint = {
22435
22764
  schemaVersion: "vtx_codex_attempt_recovery_v1",
@@ -22443,6 +22772,7 @@ var init_codex_adapter = __esm({
22443
22772
  processToken: sessionLease.processToken,
22444
22773
  processReceiptPath: sessionLease.processReceiptPath,
22445
22774
  processState: sessionLease.processToken ? "running" : "unmanaged",
22775
+ sessionGeneration: sessionLease.sessionGeneration,
22446
22776
  bootIdentity: sessionLease.bootIdentity,
22447
22777
  cleanupConfirmed: false,
22448
22778
  adapterRequestId: null,
@@ -22541,7 +22871,8 @@ var init_codex_adapter = __esm({
22541
22871
  dispatchOutcome,
22542
22872
  httpStatusCode: error48.httpStatusCode,
22543
22873
  retryAtMs: error48.retryAtMs,
22544
- usage: error48.usage
22874
+ usage: error48.usage,
22875
+ processExit: error48.processExit
22545
22876
  }
22546
22877
  }
22547
22878
  };
@@ -22557,6 +22888,7 @@ var init_codex_adapter = __esm({
22557
22888
  httpStatusCode: error48.httpStatusCode,
22558
22889
  retryAtMs: error48.retryAtMs,
22559
22890
  usage: error48.usage,
22891
+ processExit: error48.processExit,
22560
22892
  cause: error48
22561
22893
  });
22562
22894
  }
@@ -22587,6 +22919,7 @@ var init_codex_adapter = __esm({
22587
22919
  }
22588
22920
  throw classified;
22589
22921
  } finally {
22922
+ sessionLease?.activeAttemptIds.delete(input.attemptId);
22590
22923
  const cleanup = async () => {
22591
22924
  let cleanupError = sessionInvalidationError;
22592
22925
  if (session && threadId) {
@@ -29717,7 +30050,7 @@ function createDefaultInferenceHostRunnerDependencies(options) {
29717
30050
  envelopePublicKey: options.envelopePublicKey
29718
30051
  };
29719
30052
  }
29720
- 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, buildInferenceAdvertisedModels, 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;
30053
+ 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, MIN_HOST_HEARTBEAT_GAP_DIAGNOSTIC_MS, HOST_HEARTBEAT_GAP_DIAGNOSTIC_FACTOR, DEFAULT_CODEX_ACCOUNT_COOLDOWN_MS, MIN_CLAIM_START_WINDOW_MS, MAX_ATTEMPT_START_RETRY_DELAY_MS, UNBOUNDED_AVAILABLE_SLOTS, buildInferenceAdvertisedModels, 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, attemptStartRetryFallbackMs, exactJson2, runnerIdentityMismatch, assertAdvertisementResult, assertHostHeartbeatResult, assertClaimResult, assertStartResult, assertJobHeartbeatResult, assertTerminalResult, classifyFailure, ambiguousHeartbeatTransport, reportedTokenUsage, reportedUsage, InferenceHostRunner;
29721
30054
  var init_runner = __esm({
29722
30055
  "lib/inference-host/runner.ts"() {
29723
30056
  "use strict";
@@ -29739,8 +30072,11 @@ var init_runner = __esm({
29739
30072
  DEFAULT_DRAIN_TIMEOUT_MS = 3e4;
29740
30073
  DEFAULT_REMOTE_RETRY_LIMIT = 3;
29741
30074
  MIN_SLEEP_MS = 10;
30075
+ MIN_HOST_HEARTBEAT_GAP_DIAGNOSTIC_MS = 1e4;
30076
+ HOST_HEARTBEAT_GAP_DIAGNOSTIC_FACTOR = 3;
29742
30077
  DEFAULT_CODEX_ACCOUNT_COOLDOWN_MS = 5 * 60 * 1e3;
29743
30078
  MIN_CLAIM_START_WINDOW_MS = 5e3;
30079
+ MAX_ATTEMPT_START_RETRY_DELAY_MS = 15e3;
29744
30080
  UNBOUNDED_AVAILABLE_SLOTS = Number.MAX_SAFE_INTEGER;
29745
30081
  buildInferenceAdvertisedModels = (capabilities, adapterRuntimeVersion, adapter = "codex", structuredOutput = true, sameAttemptRecovery = adapter === "codex") => {
29746
30082
  const visible = capabilities.filter((capability) => !capability.hidden);
@@ -30276,6 +30612,10 @@ var init_runner = __esm({
30276
30612
  const normalized = value.trim().toLowerCase().replace(/[^a-z0-9._-]+/gu, "_").slice(0, 96);
30277
30613
  return /^[a-z0-9]/u.test(normalized) ? normalized : fallback;
30278
30614
  };
30615
+ attemptStartRetryFallbackMs = (retryCount) => Math.min(
30616
+ 1e3 * 2 ** Math.min(Math.max(0, retryCount - 1), 4),
30617
+ MAX_ATTEMPT_START_RETRY_DELAY_MS
30618
+ );
30279
30619
  exactJson2 = (left, right) => JSON.stringify(left) === JSON.stringify(right);
30280
30620
  runnerIdentityMismatch = (operation) => {
30281
30621
  throw new InferenceHostRunnerError(
@@ -30729,6 +31069,26 @@ var init_runner = __esm({
30729
31069
  }
30730
31070
  };
30731
31071
  await hostHeartbeat("healthy");
31072
+ let lastHostHeartbeatSucceededAt = now();
31073
+ let hostHeartbeatGapOpen = false;
31074
+ const hostHeartbeatGapThresholdMs = Math.max(
31075
+ MIN_HOST_HEARTBEAT_GAP_DIAGNOSTIC_MS,
31076
+ settings.hostHeartbeatMs * HOST_HEARTBEAT_GAP_DIAGNOSTIC_FACTOR
31077
+ );
31078
+ const observeHostHeartbeatGap = (observedAt) => {
31079
+ const gapMs = Math.max(0, observedAt - lastHostHeartbeatSucceededAt);
31080
+ if (gapMs >= hostHeartbeatGapThresholdMs && !hostHeartbeatGapOpen) {
31081
+ hostHeartbeatGapOpen = true;
31082
+ emitDiagnostic("host_heartbeat_gap_detected", {
31083
+ gap_ms: gapMs,
31084
+ configured_interval_ms: settings.hostHeartbeatMs,
31085
+ diagnostic_threshold_ms: hostHeartbeatGapThresholdMs,
31086
+ active_attempts: active.size,
31087
+ host_heartbeat_sequence: receipt.host_heartbeat_sequence
31088
+ });
31089
+ }
31090
+ return gapMs;
31091
+ };
30732
31092
  let nextHostHeartbeatAt = now() + settings.hostHeartbeatMs;
30733
31093
  let nextAdvertisementAttemptAt = 0;
30734
31094
  let nextProviderRateLimitRefreshAt = now() + settings.providerRateLimitRefreshMs;
@@ -30736,6 +31096,7 @@ var init_runner = __esm({
30736
31096
  let pendingClaimPromotions = 0;
30737
31097
  let onceClaimed = false;
30738
31098
  const recoveryQueue = [];
31099
+ const attemptStartRetryCounts = /* @__PURE__ */ new Map();
30739
31100
  const launchClaim = (claim, recovery, claimRequest) => {
30740
31101
  if (!recovery) {
30741
31102
  claimed += 1;
@@ -30748,6 +31109,10 @@ var init_runner = __esm({
30748
31109
  claim.attempt_index,
30749
31110
  claim.input_sha256
30750
31111
  ]);
31112
+ const nextStartRetryCount = Math.min(
31113
+ (attemptStartRetryCounts.get(attemptId) ?? 0) + 1,
31114
+ Number.MAX_SAFE_INTEGER
31115
+ );
30751
31116
  const controller = new AbortController();
30752
31117
  attemptControllers.set(attemptId, controller);
30753
31118
  let claimPromotionSettled = claimRequest === void 0;
@@ -30812,13 +31177,27 @@ var init_runner = __esm({
30812
31177
  active_attempts: active.size
30813
31178
  });
30814
31179
  },
31180
+ startRetryCount: nextStartRetryCount,
31181
+ onAttemptStartRetry: (observation) => {
31182
+ attemptStartRetryCounts.set(attemptId, observation.retry_count);
31183
+ emitDiagnostic("attempt_start_retry_scheduled", {
31184
+ ...observation,
31185
+ job_id: claim.job_id,
31186
+ attempt_id: attemptId,
31187
+ active_attempts: active.size
31188
+ });
31189
+ },
30815
31190
  resumeReceipt: recovery,
30816
31191
  claimRequest,
30817
31192
  onClaimPersisted: settleClaimPromotion
30818
31193
  }).then((outcome) => {
30819
- if (outcome === "completed") completed += 1;
30820
- else if (outcome === "failed") failed += 1;
30821
- else {
31194
+ if (outcome === "completed") {
31195
+ attemptStartRetryCounts.delete(attemptId);
31196
+ completed += 1;
31197
+ } else if (outcome === "failed") {
31198
+ attemptStartRetryCounts.delete(attemptId);
31199
+ failed += 1;
31200
+ } else {
30822
31201
  const recovery2 = receipt.attempts[attemptId];
30823
31202
  if (recovery2?.phase !== "claimed") {
30824
31203
  throw new InferenceHostRecoveryRequiredError(
@@ -30828,6 +31207,7 @@ var init_runner = __esm({
30828
31207
  retryRecovery = recovery2;
30829
31208
  }
30830
31209
  }).catch((error48) => {
31210
+ attemptStartRetryCounts.delete(attemptId);
30831
31211
  failed += 1;
30832
31212
  const recovery2 = receipt.attempts[attemptId];
30833
31213
  if (recovery2?.phase === "terminal_pending") {
@@ -30840,22 +31220,19 @@ var init_runner = __esm({
30840
31220
  attemptControllers.delete(attemptId);
30841
31221
  if (retryRecovery) {
30842
31222
  recoveryQueue.push(retryRecovery);
30843
- emitDiagnostic("attempt_start_retry_scheduled", {
30844
- job_id: claim.job_id,
30845
- attempt_id: attemptId,
30846
- active_attempts: active.size
30847
- });
30848
31223
  }
30849
31224
  });
30850
31225
  active.set(attemptId, promise2);
30851
- emitDiagnostic("attempt_started", {
30852
- job_id: claim.job_id,
30853
- attempt_id: attemptId,
30854
- attempt_index: claim.attempt_index,
30855
- requested_model: claim.requested_model,
30856
- requested_reasoning_effort: claim.requested_reasoning_effort,
30857
- active_attempts: active.size
30858
- });
31226
+ if (!recovery) {
31227
+ emitDiagnostic("attempt_started", {
31228
+ job_id: claim.job_id,
31229
+ attempt_id: attemptId,
31230
+ attempt_index: claim.attempt_index,
31231
+ requested_model: claim.requested_model,
31232
+ requested_reasoning_effort: claim.requested_reasoning_effort,
31233
+ active_attempts: active.size
31234
+ });
31235
+ }
30859
31236
  return attemptId;
30860
31237
  };
30861
31238
  const removeRecoveredAttempt = async (attemptId) => {
@@ -30978,6 +31355,7 @@ var init_runner = __esm({
30978
31355
  }
30979
31356
  }
30980
31357
  if (currentTime >= nextHostHeartbeatAt) {
31358
+ observeHostHeartbeatGap(currentTime);
30981
31359
  try {
30982
31360
  const refreshPendingAcrossExpiry = receipt.pending_advertisement !== null && expiresAt <= currentTime && nextAdvertisementAttemptAt > currentTime;
30983
31361
  await hostHeartbeat(
@@ -30986,6 +31364,19 @@ var init_runner = __esm({
30986
31364
  false,
30987
31365
  refreshPendingAcrossExpiry
30988
31366
  );
31367
+ const heartbeatSucceededAt = now();
31368
+ const gapMs = observeHostHeartbeatGap(heartbeatSucceededAt);
31369
+ if (hostHeartbeatGapOpen) {
31370
+ emitDiagnostic("host_heartbeat_gap_recovered", {
31371
+ gap_ms: gapMs,
31372
+ configured_interval_ms: settings.hostHeartbeatMs,
31373
+ diagnostic_threshold_ms: hostHeartbeatGapThresholdMs,
31374
+ active_attempts: active.size,
31375
+ host_heartbeat_sequence: receipt.host_heartbeat_sequence
31376
+ });
31377
+ hostHeartbeatGapOpen = false;
31378
+ }
31379
+ lastHostHeartbeatSucceededAt = heartbeatSucceededAt;
30989
31380
  } catch (error48) {
30990
31381
  if (controlPlaneFatal(error48)) {
30991
31382
  requestDrain("authority_lost");
@@ -31262,10 +31653,28 @@ var init_runner = __esm({
31262
31653
  MIN_SLEEP_MS,
31263
31654
  attemptDeadlineAtMs - now() - MIN_CLAIM_START_WINDOW_MS
31264
31655
  );
31265
- await options.sleep(
31266
- Math.min(remoteRetryAfterMs(error48, 1e3), remainingMs),
31267
- signal
31656
+ const retryCount = Math.min(
31657
+ Math.max(1, options.startRetryCount),
31658
+ Number.MAX_SAFE_INTEGER
31268
31659
  );
31660
+ const retryDelayMs = Math.min(
31661
+ remoteRetryAfterMs(error48, attemptStartRetryFallbackMs(retryCount)),
31662
+ remainingMs
31663
+ );
31664
+ const rawErrorCode = error48 && typeof error48 === "object" && "code" in error48 ? String(error48.code) : "start_acknowledgement_failed";
31665
+ options.onAttemptStartRetry?.({
31666
+ failure_stage: "job_start_acknowledgement",
31667
+ error_code: safeFailureCode(rawErrorCode, "start_acknowledgement_failed"),
31668
+ http_status_code: error48 instanceof ExternalInferenceMcpError ? error48.httpStatusCode : null,
31669
+ retryable: retryableRemoteError(error48),
31670
+ definitively_not_applied: Boolean(
31671
+ error48 && typeof error48 === "object" && "definitivelyNotApplied" in error48 && error48.definitivelyNotApplied === true
31672
+ ),
31673
+ retry_after_ms: error48 instanceof ExternalInferenceMcpError ? error48.retryAfterMs : null,
31674
+ retry_count: retryCount,
31675
+ next_delay_ms: retryDelayMs
31676
+ });
31677
+ await options.sleep(retryDelayMs, signal);
31269
31678
  return "retry_claimed";
31270
31679
  }
31271
31680
  assertStartResult(startRequest, startResult);
@@ -31527,6 +31936,7 @@ var init_runner = __esm({
31527
31936
  });
31528
31937
  }
31529
31938
  const classified = classifyFailure(heartbeatFailure ?? error48, adapterResult);
31939
+ const processExit = heartbeatFailure === void 0 && error48 instanceof CodexAppServerError && error48.code === "codex_process_closed" && error48.processExit?.expected_shutdown === false ? error48.processExit : null;
31530
31940
  const heartbeatAmbiguous = ambiguousHeartbeatTransport(heartbeatFailure);
31531
31941
  const failure = heartbeatAmbiguous ? {
31532
31942
  ...classified,
@@ -31580,6 +31990,7 @@ var init_runner = __esm({
31580
31990
  failure_category: failure.category,
31581
31991
  failure_code: failure.code,
31582
31992
  retryable: failure.retryable,
31993
+ ...processExit ? { process_exit: processExit } : {},
31583
31994
  membership_disposition: membershipFailureDisposition({
31584
31995
  ...failure,
31585
31996
  dispatchOutcome
@@ -31614,6 +32025,7 @@ var init_runner = __esm({
31614
32025
  failure_category: failureRequest.failure_category,
31615
32026
  failure_code: failureRequest.failure_code,
31616
32027
  retryable: failureRequest.retryable,
32028
+ process_exit: failureRequest.process_exit ?? null,
31617
32029
  dispatch_outcome: failureRequest.outcome.dispatch_outcome,
31618
32030
  response_outcome: failureRequest.outcome.response_outcome,
31619
32031
  latency_ms: adapterResult?.latencyMs ?? null,
@@ -32983,8 +33395,8 @@ ${result2.stderr}`)) {
32983
33395
  return !nextWorker || !sameWorker(record2.worker, nextWorker);
32984
33396
  });
32985
33397
  for (const record2 of retiring) {
32986
- await updateRecord(record2, "draining");
32987
33398
  record2.controller.abort();
33399
+ await updateRecord(record2, "draining");
32988
33400
  }
32989
33401
  const settled = await Promise.allSettled(retiring.map((record2) => record2.promise));
32990
33402
  const failedDrain = settled.find(
@@ -33018,8 +33430,8 @@ ${result2.stderr}`)) {
33018
33430
  const drainWorkers = async () => {
33019
33431
  const draining = [...workers.values()];
33020
33432
  for (const record2 of draining) {
33021
- await updateRecord(record2, "draining");
33022
33433
  record2.controller.abort();
33434
+ await updateRecord(record2, "draining");
33023
33435
  }
33024
33436
  const settled = await Promise.allSettled(draining.map((record2) => record2.promise));
33025
33437
  const failedDrain = settled.find(
@@ -33607,6 +34019,17 @@ Durable service:
33607
34019
  }
33608
34020
  };
33609
34021
  defaultRunForeground = async (options) => {
34022
+ const localState = await readInferenceHostLocalState(options.config.statePath);
34023
+ const emitCodexDiagnostic = (event) => {
34024
+ options.emitDiagnosticEvent?.({
34025
+ ...event,
34026
+ schema_version: "vtx_inference_host_diagnostic_v1",
34027
+ at: (/* @__PURE__ */ new Date()).toISOString(),
34028
+ instance_name: options.config.instanceName,
34029
+ host_id: localState?.host_id ?? null,
34030
+ display_name: options.displayName
34031
+ });
34032
+ };
33610
34033
  const adapter = new CodexSubscriptionAdapter({
33611
34034
  resolveBinary: async () => options.verifiedBinary,
33612
34035
  createAttemptResources: options.hostResources.createAttemptResources,
@@ -33614,7 +34037,8 @@ Durable service:
33614
34037
  guardianReceiptRoot: codexGuardianReceiptRoot(options.config),
33615
34038
  recoveryHooks: new FileCodexAttemptRecoveryStore(
33616
34039
  codexRecoveryPath(options.config)
33617
- )
34040
+ ),
34041
+ emitDiagnosticEvent: emitCodexDiagnostic
33618
34042
  });
33619
34043
  const dependencies = createDefaultInferenceHostRunnerDependencies({
33620
34044
  apiUrl: options.config.apiUrl,
@@ -34291,7 +34715,15 @@ Waiting for approval...
34291
34715
  const emitDiagnosticEvent = parsed.json ? (event) => (dependencies.emitStdout ?? ((text) => {
34292
34716
  process.stdout.write(text);
34293
34717
  }))(`${JSON.stringify(event)}
34294
- `) : void 0;
34718
+ `) : adapterId === "codex" ? (event) => {
34719
+ if (!["codex_app_server_closed", "codex_app_server_session_started"].includes(
34720
+ String(event.event ?? "")
34721
+ )) return;
34722
+ (dependencies.emitStderr ?? ((text) => {
34723
+ process.stderr.write(text);
34724
+ }))(`[VTX inference-host] ${JSON.stringify(event)}
34725
+ `);
34726
+ } : void 0;
34295
34727
  if (adapterId !== "codex") {
34296
34728
  const prepared = await (dependencies.prepareDurableAdapter ?? preparePortableDurableAdapter)(
34297
34729
  adapterId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtxmacro/cli",
3
- "version": "2026.8.43",
3
+ "version": "2026.8.45",
4
4
  "description": "VTX Macro CLI, MCP server, and durable subscription inference host.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",