@vtxmacro/cli 2026.8.37 → 2026.8.39

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 (3) hide show
  1. package/README.md +10 -10
  2. package/bin/vtx.js +411 -65
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -58,10 +58,10 @@ exposed. Before starting a Trader, confirm that the VTX AI page shows the
58
58
  intended authenticated ChatGPT email and plan. The host reads the live Codex
59
59
  account window: a reached limit pauses new dispatch until its reported reset,
60
60
  while a transient throttle uses a short bounded cooldown.
61
- The selected AI model row shows the open VTX profile's rolling 1-hour, 24-hour,
62
- and 7-day request and reported-token activity for that subscription path.
63
- Account-wide quota percentages remain in safe host diagnostics instead of the
64
- profile-scoped selector.
61
+ The selected AI model row estimates the open VTX profile's attributable
62
+ membership burn rate for that subscription, expressed as equivalent percentage
63
+ points per hour, day, and seven days. Account-wide quota percentages remain in
64
+ safe host diagnostics instead of the profile-scoped selector.
65
65
 
66
66
  ```bash
67
67
  vtx inference-host login
@@ -86,12 +86,12 @@ distinct. Use `service uninstall --instance <name>` to remove one worker;
86
86
  unqualified `service uninstall` removes the whole supervisor. The legacy
87
87
  unqualified host commands continue to target `default`.
88
88
 
89
- For one VTX account and adapter, the selected host is tried first and other
90
- compatible memberships follow in setup order. Quota/credits exhaustion,
91
- unusable authentication, and exhausted recoverable provider failures can
92
- advance the same logical call. VTX never changes provider, model, effort, or
93
- response mode, and never cascades a bad request, policy rejection, or uncertain
94
- dispatch outcome.
89
+ For each VTX profile and lane, **Subscription #1** is tried first and later
90
+ compatible subscriptions follow in the saved Account cascade order.
91
+ Quota/credits exhaustion, unusable authentication, and exhausted recoverable
92
+ provider failures can advance the same logical call. VTX never changes
93
+ provider, model, effort, or response mode, and never cascades a bad request,
94
+ policy rejection, or uncertain dispatch outcome.
95
95
 
96
96
  Automated hosts do not impose a subscription-specific profile-count or
97
97
  concurrency limit by default. Set an explicit positive integer with
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.37",
41
+ package_version: "2026.8.39",
42
42
  codex_package_name: "@openai/codex",
43
43
  codex_version: "0.147.0",
44
44
  platforms: {
@@ -14375,7 +14375,7 @@ var init_zod = __esm({
14375
14375
  });
14376
14376
 
14377
14377
  // lib/external-inference-contract.ts
14378
- 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, 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;
14378
+ 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;
14379
14379
  var init_external_inference_contract = __esm({
14380
14380
  "lib/external-inference-contract.ts"() {
14381
14381
  "use strict";
@@ -14646,6 +14646,15 @@ var init_external_inference_contract = __esm({
14646
14646
  response_mode_hint: responseModeSchema,
14647
14647
  binding_state: external_exports.literal("rebind_required")
14648
14648
  });
14649
+ orderedFallbackHostIdsSchema = external_exports.array(identifierSchema).max(127).refine(
14650
+ (values) => new Set(values).size === values.length,
14651
+ { message: "fallback host IDs must be unique" }
14652
+ );
14653
+ externalInferenceFallbackHostIdsSchema = external_exports.strictObject({
14654
+ main: orderedFallbackHostIdsSchema,
14655
+ review: orderedFallbackHostIdsSchema,
14656
+ screener: orderedFallbackHostIdsSchema
14657
+ });
14649
14658
  liveModelControllerSchema = external_exports.discriminatedUnion("kind", [
14650
14659
  nativeModelControllerSchema,
14651
14660
  externalAgentModelControllerSchema
@@ -16245,6 +16254,109 @@ function resolveInferenceHostConfig(env = process.env) {
16245
16254
  supervisorProcessLockPath: join(baseDir, "host.lock")
16246
16255
  };
16247
16256
  }
16257
+ async function writeInferenceHostCredentialContext(config2) {
16258
+ const context = credentialContextForConfig(config2);
16259
+ await writeAtomicInferencePrivateFile(
16260
+ inferenceHostCredentialContextPath(config2),
16261
+ `${JSON.stringify(context, null, 2)}
16262
+ `
16263
+ );
16264
+ }
16265
+ async function writeInferenceHostCredentialContextTransition(config2, previous, candidate = credentialContextForConfig(config2), operation = "selection", phase = "prepared") {
16266
+ const transition = {
16267
+ schema_version: "vtx_inference_credential_context_transition_v1",
16268
+ operation,
16269
+ phase,
16270
+ previous,
16271
+ candidate
16272
+ };
16273
+ await writeAtomicInferencePrivateFile(
16274
+ inferenceHostCredentialContextTransitionPath(config2),
16275
+ `${JSON.stringify(transition, null, 2)}
16276
+ `
16277
+ );
16278
+ }
16279
+ async function readInferenceHostCredentialContextTransition(config2) {
16280
+ const raw = await readInferencePrivateFile(
16281
+ inferenceHostCredentialContextTransitionPath(config2),
16282
+ "Inference host credential context transition"
16283
+ );
16284
+ if (raw === null) return null;
16285
+ let value;
16286
+ try {
16287
+ value = JSON.parse(raw);
16288
+ } catch {
16289
+ throw new Error("Inference host credential context transition is not valid JSON.");
16290
+ }
16291
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
16292
+ throw new Error("Inference host credential context transition is invalid.");
16293
+ }
16294
+ const record2 = value;
16295
+ const expectedKeys = ["schema_version", "operation", "phase", "previous", "candidate"];
16296
+ if (Object.keys(record2).sort().join("\0") !== expectedKeys.sort().join("\0") || record2.schema_version !== "vtx_inference_credential_context_transition_v1" || record2.operation !== "selection" && record2.operation !== "cleanup" || record2.phase !== "prepared" && record2.phase !== "committed") {
16297
+ throw new Error("Inference host credential context transition is invalid.");
16298
+ }
16299
+ return {
16300
+ schema_version: "vtx_inference_credential_context_transition_v1",
16301
+ operation: record2.operation,
16302
+ phase: record2.phase,
16303
+ previous: record2.previous === null ? null : assertInferenceHostCredentialContext(record2.previous, config2),
16304
+ candidate: record2.candidate === null ? null : assertInferenceHostCredentialContext(record2.candidate, config2)
16305
+ };
16306
+ }
16307
+ async function clearInferenceHostCredentialContextTransition(config2) {
16308
+ await clearInferencePrivateFile(
16309
+ inferenceHostCredentialContextTransitionPath(config2),
16310
+ "Inference host credential context transition"
16311
+ );
16312
+ }
16313
+ async function clearInferenceHostCredentialContext(config2) {
16314
+ await clearInferencePrivateFile(
16315
+ inferenceHostCredentialContextPath(config2),
16316
+ "Inference host credential context"
16317
+ );
16318
+ }
16319
+ async function readInferenceHostCredentialContext(config2) {
16320
+ const raw = await readInferencePrivateFile(
16321
+ inferenceHostCredentialContextPath(config2),
16322
+ "Inference host credential context"
16323
+ );
16324
+ if (raw === null) return null;
16325
+ let value;
16326
+ try {
16327
+ value = JSON.parse(raw);
16328
+ } catch {
16329
+ throw new Error("Inference host credential context is not valid JSON.");
16330
+ }
16331
+ return assertInferenceHostCredentialContext(value, config2);
16332
+ }
16333
+ function assertInferenceHostCredentialContext(value, config2) {
16334
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
16335
+ throw new Error("Inference host credential context is invalid.");
16336
+ }
16337
+ const record2 = value;
16338
+ const expectedKeys = [
16339
+ "schema_version",
16340
+ "instance_name",
16341
+ "credential_store_mode",
16342
+ "credential_file_path"
16343
+ ];
16344
+ if (Object.keys(record2).sort().join("\0") !== expectedKeys.sort().join("\0") || record2.schema_version !== "vtx_inference_credential_context_v1" || record2.instance_name !== config2.instanceName || record2.credential_store_mode !== "os" && record2.credential_store_mode !== "file" || (record2.credential_store_mode === "os" ? record2.credential_file_path !== null : typeof record2.credential_file_path !== "string" || !record2.credential_file_path)) {
16345
+ throw new Error("Inference host credential context is invalid.");
16346
+ }
16347
+ const credentialFilePath = record2.credential_store_mode === "file" ? requireNamedInstancePath(
16348
+ config2.instanceName,
16349
+ config2.instanceName === DEFAULT_INFERENCE_HOST_INSTANCE ? config2.baseDir : join(config2.baseDir, "instances", config2.instanceName),
16350
+ record2.credential_file_path,
16351
+ "Stored inference credential path"
16352
+ ) : null;
16353
+ return {
16354
+ schema_version: "vtx_inference_credential_context_v1",
16355
+ instance_name: config2.instanceName,
16356
+ credential_store_mode: record2.credential_store_mode,
16357
+ credential_file_path: credentialFilePath
16358
+ };
16359
+ }
16248
16360
  function assertLocalState(value) {
16249
16361
  if (!value || typeof value !== "object" || Array.isArray(value)) {
16250
16362
  throw new Error("Inference host local state is invalid.");
@@ -16619,7 +16731,7 @@ async function acquireInferenceHostProcessLock(path, dependencies = {}) {
16619
16731
  }
16620
16732
  };
16621
16733
  }
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;
16734
+ 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;
16623
16735
  var init_config = __esm({
16624
16736
  "lib/inference-host/config.ts"() {
16625
16737
  "use strict";
@@ -17138,6 +17250,17 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
17138
17250
  if (process.platform === "darwin") return "os:macos-keychain";
17139
17251
  return `os:${process.platform}:unsupported`;
17140
17252
  };
17253
+ inferenceHostCredentialContextPath = (config2) => join(
17254
+ config2.instanceName === DEFAULT_INFERENCE_HOST_INSTANCE ? config2.baseDir : join(config2.baseDir, "instances", config2.instanceName),
17255
+ "credential-context.json"
17256
+ );
17257
+ inferenceHostCredentialContextTransitionPath = (config2) => `${inferenceHostCredentialContextPath(config2)}.pending`;
17258
+ credentialContextForConfig = (config2) => ({
17259
+ schema_version: "vtx_inference_credential_context_v1",
17260
+ instance_name: config2.instanceName,
17261
+ credential_store_mode: config2.credentialStoreMode,
17262
+ credential_file_path: config2.credentialStoreMode === "file" ? resolve(config2.credentialFilePath) : null
17263
+ });
17141
17264
  }
17142
17265
  });
17143
17266
 
@@ -19382,7 +19505,7 @@ async function logoutCodexSubscription(options) {
19382
19505
  await session.close();
19383
19506
  }
19384
19507
  }
19385
- var CODEX_INFERENCE_PERMISSION_PROFILE, CODEX_REASONING_CONTENT_MAX_UTF8_BYTES, CODEX_REASONING_SUMMARY_MAX_UTF8_BYTES, CODEX_ACCOUNT_PLAN_TYPES, CodexAppServerError, objectOrNull, finiteToken, tokenUsageFromBreakdown, usageFromNotification, validPlanType, nonnegativeSafeIntegerOrNull, nonnegativeFiniteNumberOrNull, parseRateLimitWindow, RATE_LIMIT_REACHED_TYPES, DEFAULT_CODEX_QUOTA_COOLDOWN_MS, MAX_CODEX_QUOTA_COOLDOWN_MS, CODEX_TRANSIENT_RATE_LIMIT_COOLDOWN_MS, parseRateLimitSnapshot, codexRateLimitRetryAtMs, codexAccountRateLimitReached, forbiddenMethod, forbiddenTerminalItem, classifyCodexTurnFailure, scrubbedCodexEnvironment, killWindowsProcessTree, appServerArgs, GUARDIAN_SCRIPT, WINDOWS_RECEIPT_REPLACE_ERROR_CODES, replaceCodexGuardianReceiptFile, writeCodexGuardianSpawnIntent, parseGuardianReceipt, readCodexGuardianReceipt, waitForCodexGuardianState, defaultSpawn, CodexAppServerSession;
19508
+ var CODEX_INFERENCE_PERMISSION_PROFILE, CODEX_REASONING_CONTENT_MAX_UTF8_BYTES, CODEX_REASONING_SUMMARY_MAX_UTF8_BYTES, CODEX_ACCOUNT_PLAN_TYPES, CodexAppServerError, objectOrNull, finiteToken, tokenUsageFromBreakdown, usageFromNotification, validPlanType, nonnegativeSafeIntegerOrNull, nonnegativeFiniteNumberOrNull, parseRateLimitWindow, RATE_LIMIT_REACHED_TYPES, DEFAULT_CODEX_QUOTA_COOLDOWN_MS, MAX_CODEX_QUOTA_COOLDOWN_MS, CODEX_TRANSIENT_RATE_LIMIT_COOLDOWN_MS, parseRateLimitSnapshot, codexRateLimitRetryAtMs, codexAccountRateLimitReached, forbiddenMethod, forbiddenTerminalItem, classifyCodexTurnFailure, scrubbedCodexEnvironment, killWindowsProcessTree, appServerArgs, GUARDIAN_SCRIPT, WINDOWS_RECEIPT_REPLACE_ERROR_CODES, MAX_RETIRED_RESPONSE_IDS, LATE_RESPONSE_SAFE_METHODS, replaceCodexGuardianReceiptFile, writeCodexGuardianSpawnIntent, parseGuardianReceipt, readCodexGuardianReceipt, waitForCodexGuardianState, defaultSpawn, CodexAppServerSession;
19386
19509
  var init_codex_app_server = __esm({
19387
19510
  "lib/inference-host/codex-app-server.ts"() {
19388
19511
  "use strict";
@@ -19845,6 +19968,8 @@ child.once('close', async () => {
19845
19968
  "ENOTEMPTY",
19846
19969
  "EPERM"
19847
19970
  ]);
19971
+ MAX_RETIRED_RESPONSE_IDS = 256;
19972
+ LATE_RESPONSE_SAFE_METHODS = /* @__PURE__ */ new Set(["account/rateLimits/read"]);
19848
19973
  replaceCodexGuardianReceiptFile = async (temporaryPath, receiptPath, options = {}) => {
19849
19974
  const platform = options.platform ?? process.platform;
19850
19975
  const renameFile = options.renameFile ?? rename2;
@@ -19967,6 +20092,7 @@ child.once('close', async () => {
19967
20092
  CodexAppServerSession = class _CodexAppServerSession {
19968
20093
  constructor(options) {
19969
20094
  this.pending = /* @__PURE__ */ new Map();
20095
+ this.retiredResponseIds = /* @__PURE__ */ new Map();
19970
20096
  this.notificationListeners = /* @__PURE__ */ new Set();
19971
20097
  this.fatalListeners = /* @__PURE__ */ new Set();
19972
20098
  this.accountLoginCompletions = /* @__PURE__ */ new Map();
@@ -20105,6 +20231,9 @@ child.once('close', async () => {
20105
20231
  if (typeof id2 === "number" && Number.isSafeInteger(id2) && !("method" in message)) {
20106
20232
  const pending = this.pending.get(id2);
20107
20233
  if (!pending) {
20234
+ const retiredMethod = this.retiredResponseIds.get(id2);
20235
+ this.retiredResponseIds.delete(id2);
20236
+ if (retiredMethod && LATE_RESPONSE_SAFE_METHODS.has(retiredMethod)) return;
20108
20237
  this.failTransport("unexpected_response_id");
20109
20238
  return;
20110
20239
  }
@@ -20169,6 +20298,14 @@ child.once('close', async () => {
20169
20298
  this.pending.clear();
20170
20299
  for (const listener of this.fatalListeners) listener(this.fatalError);
20171
20300
  }
20301
+ retireResponseId(id2, method) {
20302
+ this.retiredResponseIds.set(id2, method);
20303
+ while (this.retiredResponseIds.size > MAX_RETIRED_RESPONSE_IDS) {
20304
+ const oldest = this.retiredResponseIds.keys().next().value;
20305
+ if (typeof oldest !== "number") break;
20306
+ this.retiredResponseIds.delete(oldest);
20307
+ }
20308
+ }
20172
20309
  write(message) {
20173
20310
  if (this.closed || this.fatalError || this.process.exitCode !== null) {
20174
20311
  throw this.fatalError ?? new CodexAppServerError({
@@ -20207,6 +20344,7 @@ child.once('close', async () => {
20207
20344
  const pending = { resolve: resolve6, reject, timer: null, abortCleanup: null };
20208
20345
  pending.timer = setTimeout(() => {
20209
20346
  this.pending.delete(id2);
20347
+ this.retireResponseId(id2, method);
20210
20348
  pending.abortCleanup?.();
20211
20349
  reject(new CodexAppServerError({
20212
20350
  message: "Codex app-server request timed out.",
@@ -20218,6 +20356,7 @@ child.once('close', async () => {
20218
20356
  if (options.signal) {
20219
20357
  const onAbort = () => {
20220
20358
  this.pending.delete(id2);
20359
+ this.retireResponseId(id2, method);
20221
20360
  if (pending.timer) clearTimeout(pending.timer);
20222
20361
  pending.abortCleanup?.();
20223
20362
  reject(new CodexAppServerError({
@@ -22110,9 +22249,21 @@ var init_codex_adapter = __esm({
22110
22249
  dispatchOutcome: checkpoint.dispatchOutcome
22111
22250
  });
22112
22251
  }
22113
- const receipt = await readCodexGuardianReceipt(checkpoint.processReceiptPath);
22114
- if (checkpoint.processState === "terminated" || receipt === null || receipt.state === "terminated") {
22115
- await rm3(checkpoint.processReceiptPath, { force: true });
22252
+ let sharedReceiptStillRequired = true;
22253
+ if (recoveryHooks.loadAll) {
22254
+ try {
22255
+ const checkpoints = await recoveryHooks.loadAll();
22256
+ const receiptPath = resolve3(checkpoint.processReceiptPath);
22257
+ sharedReceiptStillRequired = Object.values(checkpoints).some((candidate) => candidate.attemptId !== attemptId && !candidate.cleanupConfirmed && candidate.processReceiptPath !== null && resolve3(candidate.processReceiptPath) === receiptPath);
22258
+ } catch {
22259
+ sharedReceiptStillRequired = true;
22260
+ }
22261
+ }
22262
+ if (!sharedReceiptStillRequired) {
22263
+ const receipt = await readCodexGuardianReceipt(checkpoint.processReceiptPath);
22264
+ if (checkpoint.processState === "terminated" || receipt === null || receipt.state === "terminated") {
22265
+ await rm3(checkpoint.processReceiptPath, { force: true });
22266
+ }
22116
22267
  }
22117
22268
  }
22118
22269
  await recoveryHooks.clear(attemptId);
@@ -31510,9 +31661,9 @@ ${result2.stderr}`)) {
31510
31661
  const runtime = await this.waitForManifestApplied(restoredManifest);
31511
31662
  return await this.status(runtime);
31512
31663
  }
31513
- async waitForManifestApplied(manifest, targetInstanceName) {
31664
+ async waitForManifestApplied(manifest, targetInstanceName, maxAttempts = this.reconcileWaitAttempts) {
31514
31665
  let consecutiveReadyObservations = 0;
31515
- for (let attempt = 0; attempt < this.reconcileWaitAttempts; attempt += 1) {
31666
+ for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
31516
31667
  let runtime = null;
31517
31668
  try {
31518
31669
  runtime = await readRuntimeAcrossAtomicReplacement(this.runtimePath());
@@ -31648,7 +31799,12 @@ ${cleanup.stderr}`)) {
31648
31799
  previous,
31649
31800
  targetInstanceName && previous.workers.some(
31650
31801
  (worker) => worker.instance_name === targetInstanceName
31651
- ) ? targetInstanceName : void 0
31802
+ ) ? targetInstanceName : void 0,
31803
+ // A failed generation may still be draining while the manager
31804
+ // restores the previous manifest. Never give rollback less than
31805
+ // a bounded 100-observation confirmation window, even when a
31806
+ // caller intentionally shortens forward-readiness checks.
31807
+ Math.max(this.reconcileWaitAttempts, 100)
31652
31808
  );
31653
31809
  } catch (rollbackError) {
31654
31810
  throw new Error(
@@ -32288,7 +32444,7 @@ async function runInferenceHostCli(argv2, env = process.env, dependencies = {})
32288
32444
  return { exitCode: 0, stdout: INFERENCE_HOST_HELP, stderr: "" };
32289
32445
  }
32290
32446
  const parsed = parseInferenceHostArgs(argv2, env);
32291
- const config2 = await resolveInferenceHostCommandConfig(parsed, env);
32447
+ const config2 = await resolveInferenceHostCommandConfig(parsed, env, dependencies);
32292
32448
  if (parsed.command === "login") {
32293
32449
  return await login(config2, parsed, env, dependencies, warnings);
32294
32450
  }
@@ -32355,7 +32511,7 @@ async function runInferenceHostCli(argv2, env = process.env, dependencies = {})
32355
32511
  };
32356
32512
  }
32357
32513
  }
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;
32514
+ var INFERENCE_HOST_CLI_VERSION, runtimeReceiptPath, codexRecoveryPath, codexGuardianReceiptRoot, revocationCheckpointPath, foregroundHostLockPath, credentialLifecycleLockPath, 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, restoreInferenceHostCredentialContext, recoverInferenceHostCredentialContextTransition, 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, credentialEnvironmentForIdentity, commandRequiresRetainedCredentialStore, commandUsesInstalledServiceCredentials, resolveInferenceHostCommandConfig;
32359
32515
  var init_cli = __esm({
32360
32516
  "lib/inference-host/cli.ts"() {
32361
32517
  "use strict";
@@ -32379,6 +32535,7 @@ var init_cli = __esm({
32379
32535
  codexGuardianReceiptRoot = (config2) => `${config2.statePath}.codex-processes`;
32380
32536
  revocationCheckpointPath = (config2) => `${config2.statePath}.revoke.json`;
32381
32537
  foregroundHostLockPath = (config2) => `${config2.processLockPath}.foreground`;
32538
+ credentialLifecycleLockPath = (config2) => `${inferenceHostCredentialContextPath(config2)}.lock`;
32382
32539
  AGENT_HEARTBEAT_INTERVAL_MS = 3e3;
32383
32540
  AGENT_HEARTBEAT_MAX_RETRY_DELAY_MS = 9e3;
32384
32541
  retryableAgentHeartbeatError = (error48) => {
@@ -32647,6 +32804,36 @@ Durable service:
32647
32804
  };
32648
32805
  };
32649
32806
  configuredCredentialStore = (config2, dependencies, warn) => dependencies.createCredentialStore?.(config2, warn) ?? createConfiguredInferenceCredentialStore(config2, { warn });
32807
+ restoreInferenceHostCredentialContext = async (config2, previous) => {
32808
+ if (!previous) {
32809
+ await clearInferenceHostCredentialContext(config2);
32810
+ return;
32811
+ }
32812
+ await writeInferenceHostCredentialContext({
32813
+ ...config2,
32814
+ credentialStoreMode: previous.credential_store_mode,
32815
+ credentialFilePath: previous.credential_file_path
32816
+ });
32817
+ };
32818
+ recoverInferenceHostCredentialContextTransition = async (config2, selectedEnv, dependencies) => {
32819
+ const transition = await readInferenceHostCredentialContextTransition(config2);
32820
+ if (!transition) return;
32821
+ const localState = await readInferenceHostLocalState(config2.statePath);
32822
+ const candidateRecovery = transition.candidate ? await configuredCredentialStore(
32823
+ resolveInferenceHostConfig({
32824
+ ...selectedEnv,
32825
+ VTX_INFERENCE_HOST_CREDENTIAL_STORE: transition.candidate.credential_store_mode,
32826
+ VTX_INFERENCE_HOST_CREDENTIAL_FILE: transition.candidate.credential_file_path ?? void 0
32827
+ }),
32828
+ dependencies,
32829
+ () => void 0
32830
+ ).readRecovery?.() ?? null : null;
32831
+ await restoreInferenceHostCredentialContext(
32832
+ config2,
32833
+ transition.operation === "selection" ? localState || candidateRecovery ? transition.candidate : transition.previous : transition.phase === "committed" && !localState ? null : transition.previous
32834
+ );
32835
+ await clearInferenceHostCredentialContextTransition(config2);
32836
+ };
32650
32837
  accountKeyForState = (state) => inferenceCredentialAccountKey({
32651
32838
  issuer: state.issuer,
32652
32839
  clientId: state.client_id,
@@ -32780,8 +32967,15 @@ Durable service:
32780
32967
  }).run();
32781
32968
  };
32782
32969
  login = async (config2, parsed, env, dependencies, warnings) => {
32783
- const lock2 = await acquireInferenceHostProcessLock(config2.processLockPath);
32970
+ const credentialLock = await acquireInferenceHostProcessLock(
32971
+ credentialLifecycleLockPath(config2)
32972
+ );
32973
+ let lock2 = null;
32974
+ let previousCredentialContext;
32975
+ let selectedStore = null;
32976
+ let localStateCommitted = false;
32784
32977
  try {
32978
+ lock2 = await acquireInferenceHostProcessLock(config2.processLockPath);
32785
32979
  if (await readRevocationCheckpoint(config2)) {
32786
32980
  throw new Error("Inference host revocation recovery must finish before login.");
32787
32981
  }
@@ -32791,18 +32985,41 @@ Durable service:
32791
32985
  const store = configuredCredentialStore(config2, dependencies, (message) => {
32792
32986
  warnings.push(message);
32793
32987
  });
32988
+ selectedStore = store;
32989
+ if (!store.writeRecovery || !store.removeRecovery || !store.readRecovery) {
32990
+ throw new Error("Inference host credential store does not support durable login recovery.");
32991
+ }
32794
32992
  if (await store.readRecovery?.()) {
32795
32993
  throw new Error("Inference host credential recovery is required before login.");
32796
32994
  }
32995
+ previousCredentialContext = await readInferenceHostCredentialContext(config2);
32996
+ await writeInferenceHostCredentialContextTransition(config2, previousCredentialContext);
32997
+ await writeInferenceHostCredentialContext(config2);
32797
32998
  const keyPair = generateExternalInferenceEnvelopeKeyPair();
32798
32999
  const hostId = randomUUID2();
32799
33000
  const beginLogin = dependencies.beginLogin ?? beginInferenceOAuthLogin;
33001
+ const loginStore = {
33002
+ kind: store.kind,
33003
+ read: store.read.bind(store),
33004
+ remove: store.remove.bind(store),
33005
+ readRecovery: store.readRecovery.bind(store),
33006
+ writeRecovery: store.writeRecovery.bind(store),
33007
+ removeRecovery: store.removeRecovery.bind(store),
33008
+ write: async (accountKey, credential, options) => {
33009
+ await store.writeRecovery(accountKey, credential);
33010
+ try {
33011
+ await store.write(accountKey, credential, options);
33012
+ } catch (error48) {
33013
+ throw error48;
33014
+ }
33015
+ }
33016
+ };
32800
33017
  const pending = await beginLogin({
32801
33018
  apiUrl: config2.apiUrl,
32802
33019
  hostId,
32803
33020
  x25519PrivateKey: keyPair.private_key,
32804
33021
  keyGeneration: 1,
32805
- store
33022
+ store: loginStore
32806
33023
  });
32807
33024
  const emitStdout = dependencies.emitStdout ?? ((text) => {
32808
33025
  process.stdout.write(text);
@@ -32846,6 +33063,7 @@ Waiting for approval...
32846
33063
  store
32847
33064
  });
32848
33065
  remoteCleanupConfirmed = true;
33066
+ await store.removeRecovery?.();
32849
33067
  } catch {
32850
33068
  if (!store.writeRecovery) {
32851
33069
  throw new Error(
@@ -32864,6 +33082,9 @@ Waiting for approval...
32864
33082
  }
32865
33083
  throw stateError;
32866
33084
  }
33085
+ localStateCommitted = true;
33086
+ await store.removeRecovery?.();
33087
+ await clearInferenceHostCredentialContextTransition(config2);
32867
33088
  return {
32868
33089
  exitCode: 0,
32869
33090
  stdout: render({
@@ -32876,8 +33097,18 @@ Waiting for approval...
32876
33097
  stderr: warnings.length > 0 ? `${warnings.join("\n")}
32877
33098
  ` : ""
32878
33099
  };
33100
+ } catch (error48) {
33101
+ const recovery = await selectedStore?.readRecovery?.() ?? null;
33102
+ if (!recovery && !localStateCommitted && previousCredentialContext !== void 0) {
33103
+ await restoreInferenceHostCredentialContext(config2, previousCredentialContext);
33104
+ await clearInferenceHostCredentialContextTransition(config2);
33105
+ } else if (recovery) {
33106
+ await clearInferenceHostCredentialContextTransition(config2);
33107
+ }
33108
+ throw error48;
32879
33109
  } finally {
32880
- await lock2.release();
33110
+ await lock2?.release();
33111
+ await credentialLock.release();
32881
33112
  }
32882
33113
  };
32883
33114
  codexLogin = async (config2, parsed, env, dependencies) => {
@@ -33111,10 +33342,14 @@ Waiting for approval...
33111
33342
  };
33112
33343
  };
33113
33344
  cleanupLogin = async (config2, parsed, dependencies, warnings, revoke) => {
33114
- await assertDurableServiceUninstalled(config2);
33115
- const lock2 = await acquireInferenceHostProcessLock(config2.processLockPath);
33345
+ const credentialLock = await acquireInferenceHostProcessLock(
33346
+ credentialLifecycleLockPath(config2)
33347
+ );
33348
+ let lock2 = null;
33116
33349
  let keeperLock = null;
33117
33350
  try {
33351
+ await assertDurableServiceUninstalled(config2);
33352
+ lock2 = await acquireInferenceHostProcessLock(config2.processLockPath);
33118
33353
  try {
33119
33354
  keeperLock = await acquireInferenceHostProcessLock(foregroundHostLockPath(config2));
33120
33355
  } catch (error48) {
@@ -33141,8 +33376,35 @@ Waiting for approval...
33141
33376
  state,
33142
33377
  oauthRecovery
33143
33378
  );
33379
+ }
33380
+ let cleanupTransitionStaged = false;
33381
+ const stageCleanupTransition = async () => {
33382
+ if (cleanupTransitionStaged) return;
33383
+ const selectedContext = await readInferenceHostCredentialContext(config2);
33384
+ await writeInferenceHostCredentialContextTransition(
33385
+ config2,
33386
+ selectedContext,
33387
+ null,
33388
+ "cleanup",
33389
+ "prepared"
33390
+ );
33391
+ cleanupTransitionStaged = true;
33392
+ };
33393
+ const commitCleanupTransition = async () => {
33394
+ await writeInferenceHostCredentialContextTransition(
33395
+ config2,
33396
+ await readInferenceHostCredentialContext(config2),
33397
+ null,
33398
+ "cleanup",
33399
+ "committed"
33400
+ );
33401
+ };
33402
+ if (revocationCheckpoint) {
33403
+ await stageCleanupTransition();
33404
+ await commitCleanupTransition();
33144
33405
  } else if (oauthRecovery) {
33145
33406
  if (revoke) {
33407
+ await stageCleanupTransition();
33146
33408
  const metadata = await (dependencies.discoverOAuth ?? discoverInferenceOAuth)(config2.apiUrl);
33147
33409
  await (dependencies.revokeCredential ?? revokeInferenceCredential)({
33148
33410
  metadata,
@@ -33157,9 +33419,12 @@ Waiting for approval...
33157
33419
  config2.credentialStoreIdentity
33158
33420
  );
33159
33421
  await writeRevocationCheckpoint(config2, revocationCheckpoint);
33422
+ await commitCleanupTransition();
33160
33423
  } else {
33424
+ await stageCleanupTransition();
33161
33425
  await store.remove(oauthRecovery.accountKey);
33162
33426
  await store.removeRecovery?.();
33427
+ await commitCleanupTransition();
33163
33428
  }
33164
33429
  } else if (state) {
33165
33430
  const accountKey = accountKeyForState(state);
@@ -33169,6 +33434,7 @@ Waiting for approval...
33169
33434
  if (!credential) {
33170
33435
  throw new Error("Inference host credential is unavailable; remote revoke cannot be confirmed.");
33171
33436
  }
33437
+ await stageCleanupTransition();
33172
33438
  const metadata = await (dependencies.discoverOAuth ?? discoverInferenceOAuth)(config2.apiUrl);
33173
33439
  await (dependencies.revokeCredential ?? revokeInferenceCredential)({
33174
33440
  metadata,
@@ -33183,11 +33449,16 @@ Waiting for approval...
33183
33449
  config2.credentialStoreIdentity
33184
33450
  );
33185
33451
  await writeRevocationCheckpoint(config2, revocationCheckpoint);
33452
+ await commitCleanupTransition();
33186
33453
  } else {
33454
+ await stageCleanupTransition();
33187
33455
  await store.remove(accountKey);
33456
+ await commitCleanupTransition();
33188
33457
  }
33189
33458
  } else if (!revoke && store.kind === "file") {
33459
+ await stageCleanupTransition();
33190
33460
  await store.remove("");
33461
+ await commitCleanupTransition();
33191
33462
  } else if (revoke) {
33192
33463
  throw new Error("Inference host is not logged in; there is no exact grant to revoke.");
33193
33464
  }
@@ -33207,14 +33478,18 @@ Waiting for approval...
33207
33478
  { cause: error48 }
33208
33479
  );
33209
33480
  }
33210
- await clearLocalRuntimeArtifacts(config2);
33211
33481
  await store.remove(revocationCheckpoint.account_key);
33212
33482
  if (revocationCheckpoint.remove_credential_recovery) {
33213
33483
  await store.removeRecovery?.();
33214
33484
  }
33485
+ await (dependencies.clearLocalRuntimeArtifacts ?? clearLocalRuntimeArtifacts)(config2);
33486
+ await clearInferenceHostCredentialContext(config2);
33215
33487
  await clearRevocationCheckpoint(config2);
33488
+ await clearInferenceHostCredentialContextTransition(config2);
33216
33489
  } else {
33217
- await clearLocalRuntimeArtifacts(config2);
33490
+ await (dependencies.clearLocalRuntimeArtifacts ?? clearLocalRuntimeArtifacts)(config2);
33491
+ await clearInferenceHostCredentialContext(config2);
33492
+ await clearInferenceHostCredentialContextTransition(config2);
33218
33493
  }
33219
33494
  return {
33220
33495
  exitCode: 0,
@@ -33228,7 +33503,8 @@ Waiting for approval...
33228
33503
  };
33229
33504
  } finally {
33230
33505
  await keeperLock?.release();
33231
- await lock2.release();
33506
+ await lock2?.release();
33507
+ await credentialLock.release();
33232
33508
  }
33233
33509
  };
33234
33510
  runHost = async (config2, parsed, env, dependencies, warnings) => {
@@ -33775,44 +34051,52 @@ Waiting for approval...
33775
34051
  dependencies.serviceDependencies
33776
34052
  ) ?? new InferenceHostServiceManager(config2, dependencies.serviceDependencies);
33777
34053
  if (action === "install") {
33778
- const adapter = parsed.adapter || "codex";
33779
- if (adapter !== "codex") {
33780
- throw new Error(
33781
- `Adapter ${adapter} does not have a supported durable service integration. Use foreground agent-run instead.`
33782
- );
33783
- }
33784
- const state = await readInferenceHostLocalState(config2.statePath);
33785
- if (!state) throw new Error("Run vtx inference-host login before installing the service.");
33786
- const store = configuredCredentialStore(config2, dependencies, (message) => warnings.push(message));
33787
- const credential = await store.read(accountKeyForState(state));
33788
- if (!credential) throw new Error("Inference-host credential is missing. Run login again.");
33789
- assertCredentialMatchesState2(state, credential);
33790
- if (parsed.modelId || parsed.modelLabel || parsed.reasoningEffort) {
33791
- throw new Error("Automated Codex service does not accept agent model or effort options.");
33792
- }
33793
- const auth = await inspectCodexAuthentication(config2);
33794
- if (!auth.present || !auth.private) {
33795
- throw new Error("Run vtx inference-host codex-login before installing the automated Codex service.");
33796
- }
33797
- const binary = await (dependencies.resolveBinary ?? resolvePinnedCodexBinary)(env);
33798
- const preflight = await (dependencies.preflightCodex ?? preflightCodexSubscription)({
33799
- binary,
33800
- codexHome: config2.codexHomePath,
33801
- deadlineAtMs: Date.now() + 3e4
33802
- });
33803
- const status = await manager.install({
33804
- adapter: "codex",
33805
- displayName: parsed.displayName,
33806
- maxConcurrency: parsed.maxConcurrency,
33807
- authenticatedAccountEmail: preflight?.authenticated_account_email ?? null,
33808
- authenticatedAccountPlan: preflight?.authenticated_account_plan ?? null
33809
- });
33810
- return {
33811
- exitCode: 0,
33812
- stdout: render({ status: "service_installed", ...status }, parsed.json),
33813
- stderr: warnings.length > 0 ? `${warnings.join("\n")}
34054
+ const credentialLock = await acquireInferenceHostProcessLock(
34055
+ credentialLifecycleLockPath(config2)
34056
+ );
34057
+ try {
34058
+ const adapter = parsed.adapter || "codex";
34059
+ if (adapter !== "codex") {
34060
+ throw new Error(
34061
+ `Adapter ${adapter} does not have a supported durable service integration. Use foreground agent-run instead.`
34062
+ );
34063
+ }
34064
+ const state = await readInferenceHostLocalState(config2.statePath);
34065
+ if (!state) throw new Error("Run vtx inference-host login before installing the service.");
34066
+ const store = configuredCredentialStore(config2, dependencies, (message) => warnings.push(message));
34067
+ const credential = await store.read(accountKeyForState(state));
34068
+ if (!credential) throw new Error("Inference-host credential is missing. Run login again.");
34069
+ assertCredentialMatchesState2(state, credential);
34070
+ if (parsed.modelId || parsed.modelLabel || parsed.reasoningEffort) {
34071
+ throw new Error("Automated Codex service does not accept agent model or effort options.");
34072
+ }
34073
+ const auth = await inspectCodexAuthentication(config2);
34074
+ if (!auth.present || !auth.private) {
34075
+ throw new Error("Run vtx inference-host codex-login before installing the automated Codex service.");
34076
+ }
34077
+ const binary = await (dependencies.resolveBinary ?? resolvePinnedCodexBinary)(env);
34078
+ const preflight = await (dependencies.preflightCodex ?? preflightCodexSubscription)({
34079
+ binary,
34080
+ codexHome: config2.codexHomePath,
34081
+ deadlineAtMs: Date.now() + 3e4
34082
+ });
34083
+ const status = await manager.install({
34084
+ adapter: "codex",
34085
+ displayName: parsed.displayName,
34086
+ maxConcurrency: parsed.maxConcurrency,
34087
+ authenticatedAccountEmail: preflight?.authenticated_account_email ?? null,
34088
+ authenticatedAccountPlan: preflight?.authenticated_account_plan ?? null
34089
+ });
34090
+ await writeInferenceHostCredentialContext(config2);
34091
+ return {
34092
+ exitCode: 0,
34093
+ stdout: render({ status: "service_installed", ...status }, parsed.json),
34094
+ stderr: warnings.length > 0 ? `${warnings.join("\n")}
33814
34095
  ` : ""
33815
- };
34096
+ };
34097
+ } finally {
34098
+ await credentialLock.release();
34099
+ }
33816
34100
  }
33817
34101
  if (parsed.adapter || parsed.modelId || parsed.modelLabel || parsed.reasoningEffort) {
33818
34102
  throw new Error("Service adapter and model options are accepted only by service install.");
@@ -33839,36 +34123,98 @@ Waiting for approval...
33839
34123
  return { exitCode: lifecycleMatches ? 0 : 1, stdout: render(status, parsed.json), stderr: "" };
33840
34124
  }
33841
34125
  if (action === "uninstall") {
33842
- const status = parsed.instanceExplicit ? await manager.uninstallInstance(config2.instanceName) : await manager.uninstall();
33843
- return { exitCode: 0, stdout: render({ status: "service_uninstalled", ...status }, parsed.json), stderr: "" };
34126
+ const credentialLock = await acquireInferenceHostProcessLock(
34127
+ credentialLifecycleLockPath(config2)
34128
+ );
34129
+ try {
34130
+ if (parsed.instanceExplicit) {
34131
+ const retained = await readInferenceHostCredentialContext(config2);
34132
+ const retainedIdentity = retained ? resolveInferenceHostConfig({
34133
+ ...env,
34134
+ VTX_INFERENCE_HOST_INSTANCE: config2.instanceName,
34135
+ VTX_INFERENCE_HOST_CREDENTIAL_STORE: retained.credential_store_mode,
34136
+ VTX_INFERENCE_HOST_CREDENTIAL_FILE: retained.credential_file_path ?? void 0
34137
+ }).credentialStoreIdentity : null;
34138
+ if (retainedIdentity !== config2.credentialStoreIdentity) {
34139
+ await writeInferenceHostCredentialContext(config2);
34140
+ }
34141
+ }
34142
+ const status = parsed.instanceExplicit ? await manager.uninstallInstance(config2.instanceName) : await manager.uninstall();
34143
+ return { exitCode: 0, stdout: render({ status: "service_uninstalled", ...status }, parsed.json), stderr: "" };
34144
+ } finally {
34145
+ await credentialLock.release();
34146
+ }
33844
34147
  }
33845
34148
  throw new Error("Usage: vtx inference-host service <install|start|stop|status|logs|uninstall>.");
33846
34149
  };
33847
34150
  hasExplicitCredentialStoreConfiguration = (env) => Boolean(
33848
34151
  String(env.VTX_INFERENCE_HOST_CREDENTIAL_STORE || "").trim() || String(env.VTX_INFERENCE_HOST_CREDENTIAL_FILE || "").trim()
33849
34152
  );
33850
- commandUsesInstalledServiceCredentials = (parsed) => parsed.command === "status" || parsed.command === "doctor" || parsed.command === "service" && parsed.serviceAction === "install";
33851
- resolveInferenceHostCommandConfig = async (parsed, env) => {
34153
+ credentialEnvironmentForIdentity = (identity) => {
34154
+ if (identity.startsWith("file:")) {
34155
+ const path = identity.slice("file:".length);
34156
+ if (!path) throw new Error("Inference host credential store identity is invalid.");
34157
+ return {
34158
+ VTX_INFERENCE_HOST_CREDENTIAL_STORE: "file",
34159
+ VTX_INFERENCE_HOST_CREDENTIAL_FILE: path
34160
+ };
34161
+ }
34162
+ if (identity.startsWith("os:")) {
34163
+ return {
34164
+ VTX_INFERENCE_HOST_CREDENTIAL_STORE: "os",
34165
+ VTX_INFERENCE_HOST_CREDENTIAL_FILE: void 0
34166
+ };
34167
+ }
34168
+ throw new Error("Inference host credential store identity is invalid.");
34169
+ };
34170
+ commandRequiresRetainedCredentialStore = (parsed) => parsed.command === "logout" || parsed.command === "revoke" || parsed.command === "service" && parsed.instanceExplicit && (parsed.serviceAction === "install" || parsed.serviceAction === "uninstall");
34171
+ commandUsesInstalledServiceCredentials = (parsed) => parsed.command === "login" || parsed.command === "logout" || parsed.command === "revoke" || parsed.command === "status" || parsed.command === "doctor" || parsed.command === "service" && parsed.serviceAction === "install" || parsed.command === "service" && parsed.serviceAction === "uninstall" && parsed.instanceExplicit;
34172
+ resolveInferenceHostCommandConfig = async (parsed, env, dependencies) => {
33852
34173
  const selectedEnv = {
33853
34174
  ...env,
33854
34175
  VTX_INFERENCE_HOST_INSTANCE: parsed.instanceName
33855
34176
  };
33856
34177
  const baseConfig = resolveInferenceHostConfig(selectedEnv);
33857
- if (!commandUsesInstalledServiceCredentials(parsed) || hasExplicitCredentialStoreConfiguration(env)) {
33858
- return baseConfig;
34178
+ if (!commandUsesInstalledServiceCredentials(parsed)) return baseConfig;
34179
+ if (await readInferenceHostCredentialContextTransition(baseConfig)) {
34180
+ const credentialLock = await acquireInferenceHostProcessLock(
34181
+ credentialLifecycleLockPath(baseConfig)
34182
+ );
34183
+ try {
34184
+ await recoverInferenceHostCredentialContextTransition(baseConfig, selectedEnv, dependencies);
34185
+ } finally {
34186
+ await credentialLock.release();
34187
+ }
33859
34188
  }
33860
34189
  const manifestPath = `${baseConfig.supervisorStatePath}.service.json`;
33861
34190
  const manifest = await readInferenceHostServiceManifest(manifestPath);
33862
- if (!manifest) return baseConfig;
34191
+ const installedEnvironment = manifest?.workers.find(
34192
+ (worker) => worker.instance_name === baseConfig.instanceName
34193
+ )?.runtime_environment;
34194
+ const persistedContext = installedEnvironment ? null : await readInferenceHostCredentialContext(baseConfig);
34195
+ const revocationCheckpoint = !installedEnvironment && !persistedContext && parsed.command === "revoke" ? await readRevocationCheckpoint(baseConfig) : null;
34196
+ const retainedEnvironment = installedEnvironment ?? (persistedContext ? {
34197
+ VTX_INFERENCE_HOST_CREDENTIAL_STORE: persistedContext.credential_store_mode,
34198
+ VTX_INFERENCE_HOST_CREDENTIAL_FILE: persistedContext.credential_file_path ?? void 0
34199
+ } : revocationCheckpoint ? credentialEnvironmentForIdentity(revocationCheckpoint.credential_store_identity) : null);
34200
+ if (!retainedEnvironment) return baseConfig;
33863
34201
  const installedConfig = resolveInferenceHostConfig({
33864
34202
  ...selectedEnv,
33865
- ...manifest.workers.find((worker) => worker.instance_name === baseConfig.instanceName)?.runtime_environment
34203
+ ...retainedEnvironment
33866
34204
  });
33867
34205
  if (resolve5(installedConfig.statePath) !== resolve5(baseConfig.statePath)) {
33868
34206
  throw new Error(
33869
34207
  "Installed inference-host service manifest does not match the requested local state path."
33870
34208
  );
33871
34209
  }
34210
+ if (hasExplicitCredentialStoreConfiguration(env)) {
34211
+ if (commandRequiresRetainedCredentialStore(parsed) && baseConfig.credentialStoreIdentity !== installedConfig.credentialStoreIdentity) {
34212
+ throw new Error(
34213
+ "Explicit inference credential store does not match the retained store for this host. Log out or revoke with the retained store before switching."
34214
+ );
34215
+ }
34216
+ return baseConfig;
34217
+ }
33872
34218
  return installedConfig;
33873
34219
  };
33874
34220
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtxmacro/cli",
3
- "version": "2026.8.37",
3
+ "version": "2026.8.39",
4
4
  "description": "VTX Macro CLI, MCP server, and durable subscription inference host.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",