@vtxmacro/cli 2026.8.30 → 2026.8.32

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 +26 -0
  2. package/bin/vtx.js +758 -210
  3. 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.30",
41
+ package_version: "2026.8.32",
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, 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, 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, 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, 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;
14379
14379
  var init_external_inference_contract = __esm({
14380
14380
  "lib/external-inference-contract.ts"() {
14381
14381
  "use strict";
@@ -15181,6 +15181,22 @@ var init_external_inference_contract = __esm({
15181
15181
  });
15182
15182
  }
15183
15183
  });
15184
+ providerWeeklyQuotaReadSchema = external_exports.strictObject({
15185
+ source: external_exports.literal("codex_app_server"),
15186
+ used_percent: external_exports.number().finite().min(0).max(1e6),
15187
+ resets_at: timestampSchema,
15188
+ observed_at: timestampSchema
15189
+ }).superRefine((value, context) => {
15190
+ const observedAt = Date.parse(value.observed_at);
15191
+ const resetsAt = Date.parse(value.resets_at);
15192
+ if (resetsAt <= observedAt || resetsAt > observedAt + 8 * 24 * 60 * 60 * 1e3) {
15193
+ context.addIssue({
15194
+ code: "custom",
15195
+ message: "weekly quota reset must follow observation within eight days",
15196
+ path: ["resets_at"]
15197
+ });
15198
+ }
15199
+ });
15184
15200
  hostHeartbeatRequestSchema = external_exports.strictObject({
15185
15201
  schema_version: external_exports.literal("external_inference_host_heartbeat_v1"),
15186
15202
  contract_version: external_exports.literal(EXTERNAL_INFERENCE_CONTRACT_VERSION),
@@ -15192,6 +15208,7 @@ var init_external_inference_contract = __esm({
15192
15208
  status: external_exports.enum(["healthy", "degraded", "draining"]),
15193
15209
  active_attempts: nonNegativeSafeIntegerSchema,
15194
15210
  available_slots: nonNegativeSafeIntegerSchema,
15211
+ provider_weekly_quota: providerWeeklyQuotaReadSchema.nullable().optional(),
15195
15212
  observed_at: timestampSchema
15196
15213
  });
15197
15214
  jobClaimRequestSchema = external_exports.strictObject({
@@ -15611,6 +15628,44 @@ var init_external_inference_contract = __esm({
15611
15628
  cancelled: nonNegativeSafeIntegerSchema,
15612
15629
  expired: nonNegativeSafeIntegerSchema
15613
15630
  });
15631
+ externalInferenceVolumeWindowReadSchema = external_exports.strictObject({
15632
+ window_seconds: positiveSafeIntegerSchema,
15633
+ requests_started: nonNegativeSafeIntegerSchema,
15634
+ profiles_with_requests: nonNegativeSafeIntegerSchema,
15635
+ attempts_succeeded: nonNegativeSafeIntegerSchema,
15636
+ attempts_failed: nonNegativeSafeIntegerSchema,
15637
+ rate_limited: nonNegativeSafeIntegerSchema,
15638
+ quota_exhausted: nonNegativeSafeIntegerSchema,
15639
+ usage_reported: nonNegativeSafeIntegerSchema,
15640
+ usage_unavailable: nonNegativeSafeIntegerSchema,
15641
+ input_tokens: nonNegativeSafeIntegerSchema,
15642
+ output_tokens: nonNegativeSafeIntegerSchema,
15643
+ total_tokens: nonNegativeSafeIntegerSchema,
15644
+ reasoning_tokens: nonNegativeSafeIntegerSchema,
15645
+ cached_input_tokens: nonNegativeSafeIntegerSchema
15646
+ });
15647
+ externalInferenceFailureCountReadSchema = external_exports.strictObject({
15648
+ failure_category: safeCodeSchema.nullable(),
15649
+ failure_code: safeCodeSchema,
15650
+ count: positiveSafeIntegerSchema,
15651
+ last_observed_at: timestampSchema
15652
+ });
15653
+ externalInferenceHostDiagnosticsReadSchema = external_exports.strictObject({
15654
+ host_id: identifierSchema,
15655
+ active_attempts: nonNegativeSafeIntegerSchema,
15656
+ queue: queueStatusReadSchema,
15657
+ last_second: externalInferenceVolumeWindowReadSchema,
15658
+ last_minute: externalInferenceVolumeWindowReadSchema,
15659
+ last_hour: externalInferenceVolumeWindowReadSchema,
15660
+ last_day: externalInferenceVolumeWindowReadSchema,
15661
+ last_week: externalInferenceVolumeWindowReadSchema,
15662
+ recent_failures: external_exports.array(externalInferenceFailureCountReadSchema).max(32)
15663
+ });
15664
+ externalInferenceProfileActivityReadSchema = external_exports.strictObject({
15665
+ last_hour: externalInferenceVolumeWindowReadSchema,
15666
+ last_day: externalInferenceVolumeWindowReadSchema,
15667
+ last_week: externalInferenceVolumeWindowReadSchema
15668
+ });
15614
15669
  externalInferenceStatusReadSchema = external_exports.strictObject({
15615
15670
  schema_version: external_exports.literal("external_inference_status_v1"),
15616
15671
  contract_version: external_exports.literal(EXTERNAL_INFERENCE_CONTRACT_VERSION),
@@ -15618,8 +15673,11 @@ var init_external_inference_contract = __esm({
15618
15673
  catalog_generation: generationSchema,
15619
15674
  hosts: external_exports.array(hostStatusReadSchema).max(MAX_EXTERNAL_INFERENCE_STATUS_HOSTS),
15620
15675
  bindings: external_exports.array(controllerBindingStatusReadSchema).max(512),
15676
+ binding_count: nonNegativeSafeIntegerSchema.optional().default(0),
15677
+ bindings_truncated: external_exports.boolean().optional().default(false),
15621
15678
  queue: queueStatusReadSchema,
15622
- outcomes: outcomeStatusReadSchema
15679
+ outcomes: outcomeStatusReadSchema,
15680
+ host_diagnostics: external_exports.array(externalInferenceHostDiagnosticsReadSchema).max(MAX_EXTERNAL_INFERENCE_STATUS_HOSTS).optional().default([])
15623
15681
  });
15624
15682
  nativeResearchEvidenceSourceSchema = external_exports.strictObject({
15625
15683
  source: external_exports.literal("native"),
@@ -16084,13 +16142,26 @@ import { spawn } from "node:child_process";
16084
16142
  import { constants } from "node:fs";
16085
16143
  import { lstat, mkdir, open, readFile, realpath, rename, rm } from "node:fs/promises";
16086
16144
  import { homedir } from "node:os";
16087
- import { dirname, join, relative, resolve } from "node:path";
16145
+ import { dirname, isAbsolute, join, relative, resolve } from "node:path";
16088
16146
  function inferenceBaseDir(env) {
16089
16147
  const configured = String(env.VTX_INFERENCE_HOST_HOME || "").trim();
16090
16148
  return configured || join(homedir(), ".vtx", "inference-host");
16091
16149
  }
16150
+ function normalizeInferenceHostInstanceName(raw) {
16151
+ const value = raw.trim().toLowerCase();
16152
+ if (!SAFE_INFERENCE_HOST_INSTANCE.test(value)) {
16153
+ throw new Error(
16154
+ "Inference host instance must be 1-48 lowercase letters, numbers, or hyphens, and cannot start or end with a hyphen."
16155
+ );
16156
+ }
16157
+ return value;
16158
+ }
16092
16159
  function resolveInferenceHostConfig(env = process.env) {
16093
16160
  const baseDir = inferenceBaseDir(env);
16161
+ const instanceName = normalizeInferenceHostInstanceName(
16162
+ String(env.VTX_INFERENCE_HOST_INSTANCE || DEFAULT_INFERENCE_HOST_INSTANCE)
16163
+ );
16164
+ const instanceDir = instanceName === DEFAULT_INFERENCE_HOST_INSTANCE ? baseDir : join(baseDir, "instances", instanceName);
16094
16165
  const rawMode = String(env.VTX_INFERENCE_HOST_CREDENTIAL_STORE || "os").trim().toLowerCase();
16095
16166
  if (rawMode !== "os" && rawMode !== "file") {
16096
16167
  throw new Error("VTX_INFERENCE_HOST_CREDENTIAL_STORE must be os or file.");
@@ -16103,15 +16174,36 @@ function resolveInferenceHostConfig(env = process.env) {
16103
16174
  "VTX_INFERENCE_HOST_CREDENTIAL_FILE requires explicit file credential-store mode."
16104
16175
  );
16105
16176
  }
16106
- const credentialFilePath = rawMode === "file" ? explicitCredentialPath || join(baseDir, "credentials.json") : null;
16177
+ const credentialFilePath = rawMode === "file" ? requireNamedInstancePath(
16178
+ instanceName,
16179
+ instanceDir,
16180
+ explicitCredentialPath,
16181
+ "VTX_INFERENCE_HOST_CREDENTIAL_FILE"
16182
+ ) || join(instanceDir, "credentials.json") : null;
16183
+ const explicitStatePath = requireNamedInstancePath(
16184
+ instanceName,
16185
+ instanceDir,
16186
+ String(env.VTX_INFERENCE_HOST_STATE_PATH || "").trim(),
16187
+ "VTX_INFERENCE_HOST_STATE_PATH"
16188
+ );
16189
+ const explicitLockPath = requireNamedInstancePath(
16190
+ instanceName,
16191
+ instanceDir,
16192
+ String(env.VTX_INFERENCE_HOST_LOCK_PATH || "").trim(),
16193
+ "VTX_INFERENCE_HOST_LOCK_PATH"
16194
+ );
16107
16195
  return {
16108
16196
  apiUrl: String(env.VTX_API_URL || "https://api.vtxmacro.com").replace(/\/+$/, ""),
16197
+ baseDir,
16198
+ instanceName,
16109
16199
  credentialStoreMode: rawMode,
16110
16200
  credentialFilePath,
16111
16201
  credentialStoreIdentity: credentialStoreIdentity(rawMode, credentialFilePath, env),
16112
- codexHomePath: join(baseDir, "codex-home"),
16113
- statePath: String(env.VTX_INFERENCE_HOST_STATE_PATH || "").trim() || join(baseDir, "state.json"),
16114
- processLockPath: String(env.VTX_INFERENCE_HOST_LOCK_PATH || "").trim() || join(baseDir, "host.lock")
16202
+ codexHomePath: join(instanceDir, "codex-home"),
16203
+ statePath: explicitStatePath || join(instanceDir, "state.json"),
16204
+ processLockPath: explicitLockPath || join(instanceDir, "host.lock"),
16205
+ supervisorStatePath: join(baseDir, "state.json"),
16206
+ supervisorProcessLockPath: join(baseDir, "host.lock")
16115
16207
  };
16116
16208
  }
16117
16209
  function assertLocalState(value) {
@@ -16488,7 +16580,7 @@ async function acquireInferenceHostProcessLock(path, dependencies = {}) {
16488
16580
  }
16489
16581
  };
16490
16582
  }
16491
- var INFERENCE_CREDENTIAL_NAMESPACE, MAX_INFERENCE_PRIVATE_FILE_BYTES, WINDOWS_PRIVATE_ACL_SCRIPT, windowsPrivateAclInvocation, runWindowsPrivateAcl, windowsAclCache, windowsAclIdentity, aclCacheKey, secureWindowsPrivatePath, invalidateWindowsAclCache, rebindHardenedWindowsAclAfterRename, rebindHardenedWindowsDirectoryAfterOwnedMutation, isDefaultWindowsPrivatePath, secureExistingWindowsPath, linuxProcessIdentity, windowsProcessIdentity, darwinProcessIdentity, runSmallIdentityCommand, cachedSystemBootIdentity, readInferenceSystemBootIdentity, defaultProcessIdentity, defaultCurrentProcessIdentity, processLockObservationCache, inferenceProcessIdentitiesMatch, credentialStoreIdentity;
16583
+ var INFERENCE_CREDENTIAL_NAMESPACE, MAX_INFERENCE_PRIVATE_FILE_BYTES, WINDOWS_PRIVATE_ACL_SCRIPT, windowsPrivateAclInvocation, runWindowsPrivateAcl, windowsAclCache, windowsAclIdentity, aclCacheKey, secureWindowsPrivatePath, invalidateWindowsAclCache, rebindHardenedWindowsAclAfterRename, rebindHardenedWindowsDirectoryAfterOwnedMutation, isDefaultWindowsPrivatePath, secureExistingWindowsPath, linuxProcessIdentity, windowsProcessIdentity, darwinProcessIdentity, runSmallIdentityCommand, cachedSystemBootIdentity, readInferenceSystemBootIdentity, defaultProcessIdentity, defaultCurrentProcessIdentity, processLockObservationCache, inferenceProcessIdentitiesMatch, DEFAULT_INFERENCE_HOST_INSTANCE, SAFE_INFERENCE_HOST_INSTANCE, requireNamedInstancePath, credentialStoreIdentity;
16492
16584
  var init_config = __esm({
16493
16585
  "lib/inference-host/config.ts"() {
16494
16586
  "use strict";
@@ -16845,6 +16937,16 @@ if(-not $userFull) { throw 'private ACL does not grant the current user full con
16845
16937
  };
16846
16938
  processLockObservationCache = /* @__PURE__ */ new Map();
16847
16939
  inferenceProcessIdentitiesMatch = (stored, observed) => stored === observed;
16940
+ DEFAULT_INFERENCE_HOST_INSTANCE = "default";
16941
+ SAFE_INFERENCE_HOST_INSTANCE = /^[a-z0-9](?:[a-z0-9-]{0,46}[a-z0-9])?$/u;
16942
+ requireNamedInstancePath = (instanceName, instanceDir, rawPath, label) => {
16943
+ if (!rawPath || instanceName === DEFAULT_INFERENCE_HOST_INSTANCE) return rawPath;
16944
+ const relativePath = relative(resolve(instanceDir), resolve(rawPath));
16945
+ if (relativePath === ".." || relativePath.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || isAbsolute(relativePath)) {
16946
+ throw new Error(`${label} for a named instance must stay inside its isolated instance directory.`);
16947
+ }
16948
+ return rawPath;
16949
+ };
16848
16950
  credentialStoreIdentity = (mode, filePath, env) => {
16849
16951
  if (mode === "file") {
16850
16952
  if (!filePath) throw new Error("Inference credential fallback path is unavailable.");
@@ -19029,7 +19131,7 @@ var init_agent_state = __esm({
19029
19131
  import { spawn as spawn3 } from "node:child_process";
19030
19132
  import { chmod, lstat as lstat2, open as open2, readFile as readFile2, rename as rename2, rm as rm2, writeFile } from "node:fs/promises";
19031
19133
  import { tmpdir } from "node:os";
19032
- import { dirname as dirname2, isAbsolute, join as join2, resolve as resolve2, sep } from "node:path";
19134
+ import { dirname as dirname2, isAbsolute as isAbsolute2, join as join2, resolve as resolve2, sep } from "node:path";
19033
19135
  import { createInterface } from "node:readline";
19034
19136
  async function loginCodexSubscription(options) {
19035
19137
  const session = await CodexAppServerSession.start(options);
@@ -19137,6 +19239,7 @@ var init_codex_app_server = __esm({
19137
19239
  this.httpStatusCode = options.httpStatusCode ?? null;
19138
19240
  this.retryAtMs = options.retryAtMs ?? null;
19139
19241
  this.usage = options.usage ?? null;
19242
+ this.rateLimits = options.rateLimits ?? null;
19140
19243
  }
19141
19244
  };
19142
19245
  objectOrNull = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -19726,7 +19829,10 @@ child.once('close', async () => {
19726
19829
  title: "VTX External Inference Host",
19727
19830
  version: "external_inference.v1"
19728
19831
  },
19729
- capabilities: { experimentalApi: true }
19832
+ capabilities: {
19833
+ experimentalApi: true,
19834
+ optOutNotificationMethods: ["thread/started"]
19835
+ }
19730
19836
  }, {
19731
19837
  timeoutMs: Math.max(1, options.deadlineAtMs - Date.now()),
19732
19838
  signal: options.signal
@@ -20297,7 +20403,7 @@ child.once('close', async () => {
20297
20403
  const resolvedHome = resolve2(this.codexHome);
20298
20404
  const resolvedThreadPath = resolve2(threadPath);
20299
20405
  const resolvedSessionsRoot = join2(resolvedHome, "sessions");
20300
- if (!threadId || !threadPath || !isAbsolute(threadPath) || !resolvedThreadPath.startsWith(`${resolvedSessionsRoot}${sep}`) || !resolvedThreadPath.endsWith(".jsonl") || thread?.ephemeral !== false || thread?.modelProvider !== "openai" || !Array.isArray(thread?.turns) || thread.turns.length !== 0 || result2.model !== options.requestedModel || result2.modelProvider !== "openai" || result2.serviceTier !== "default" || result2.reasoningEffort !== options.requestedReasoningEffort || String(result2.cwd || "") !== options.workspacePath || !Array.isArray(result2.runtimeWorkspaceRoots) || result2.runtimeWorkspaceRoots.length !== 0 || !Array.isArray(result2.instructionSources) || result2.instructionSources.length !== 0 || result2.approvalPolicy !== "never" || result2.approvalsReviewer !== "user" || sandbox?.type !== "readOnly" || sandbox.networkAccess !== false || permission?.id !== CODEX_INFERENCE_PERMISSION_PROFILE || permission.extends !== null) {
20406
+ if (!threadId || !threadPath || !isAbsolute2(threadPath) || !resolvedThreadPath.startsWith(`${resolvedSessionsRoot}${sep}`) || !resolvedThreadPath.endsWith(".jsonl") || thread?.ephemeral !== false || thread?.modelProvider !== "openai" || !Array.isArray(thread?.turns) || thread.turns.length !== 0 || result2.model !== options.requestedModel || result2.modelProvider !== "openai" || result2.serviceTier !== "default" || result2.reasoningEffort !== options.requestedReasoningEffort || String(result2.cwd || "") !== options.workspacePath || !Array.isArray(result2.runtimeWorkspaceRoots) || result2.runtimeWorkspaceRoots.length !== 0 || !Array.isArray(result2.instructionSources) || result2.instructionSources.length !== 0 || result2.approvalPolicy !== "never" || result2.approvalsReviewer !== "user" || sandbox?.type !== "readOnly" || sandbox.networkAccess !== false || permission?.id !== CODEX_INFERENCE_PERMISSION_PROFILE || permission.extends !== null) {
20301
20407
  throw new CodexAppServerError({
20302
20408
  message: "Codex thread isolation or effective settings were invalid.",
20303
20409
  category: "adapter",
@@ -20353,6 +20459,19 @@ child.once('close', async () => {
20353
20459
  });
20354
20460
  const startedAt = Date.now();
20355
20461
  const unsubscribe = this.subscribe(({ method, params }) => {
20462
+ if (method === "thread/started") {
20463
+ const startedThread = objectOrNull(params.thread);
20464
+ if (!String(startedThread?.id || "")) {
20465
+ terminalReject(new CodexAppServerError({
20466
+ message: "Codex returned an invalid thread lifecycle notification.",
20467
+ category: "adapter",
20468
+ code: "invalid_thread_started_notification",
20469
+ retryable: false,
20470
+ dispatchOutcome: requestWritten ? turnId ? "confirmed_dispatched" : "outcome_unknown" : "not_dispatched"
20471
+ }));
20472
+ }
20473
+ return;
20474
+ }
20356
20475
  const eventThreadId = String(params.threadId || "");
20357
20476
  const scopedMethod = method.startsWith("thread/") || method.startsWith("turn/") || method.startsWith("item/") || method.startsWith("rawResponse") || method === "model/rerouted";
20358
20477
  if (scopedMethod && !eventThreadId) {
@@ -20632,6 +20751,7 @@ child.once('close', async () => {
20632
20751
  );
20633
20752
  const observedUsage = threadUsage ?? rawResponseUsage;
20634
20753
  let retryAtMs = null;
20754
+ let observedRateLimits = null;
20635
20755
  if (failure.code === "quota_exceeded" || failure.httpStatusCode === 429) {
20636
20756
  const observedAtMs = Date.now();
20637
20757
  let accountLimitReached = failure.code === "quota_exceeded";
@@ -20641,9 +20761,12 @@ child.once('close', async () => {
20641
20761
  observedAtMs + 5e3
20642
20762
  );
20643
20763
  if (metadataDeadlineAtMs <= observedAtMs) throw new Error("rate-limit deadline elapsed");
20644
- const rateLimits = await this.readAccountRateLimits(metadataDeadlineAtMs, request.signal);
20645
- accountLimitReached = accountLimitReached || codexAccountRateLimitReached(rateLimits);
20646
- retryAtMs = codexRateLimitRetryAtMs(rateLimits);
20764
+ observedRateLimits = await this.readAccountRateLimits(
20765
+ metadataDeadlineAtMs,
20766
+ request.signal
20767
+ );
20768
+ accountLimitReached = accountLimitReached || codexAccountRateLimitReached(observedRateLimits);
20769
+ retryAtMs = codexRateLimitRetryAtMs(observedRateLimits);
20647
20770
  } catch {
20648
20771
  }
20649
20772
  if (accountLimitReached) {
@@ -20673,7 +20796,8 @@ child.once('close', async () => {
20673
20796
  dispatchOutcome: "confirmed_dispatched",
20674
20797
  httpStatusCode: failure.httpStatusCode,
20675
20798
  retryAtMs,
20676
- usage: observedUsage
20799
+ usage: observedUsage,
20800
+ rateLimits: observedRateLimits
20677
20801
  });
20678
20802
  }
20679
20803
  const items = Array.isArray(terminal.items) ? terminal.items.map(objectOrNull) : [];
@@ -20992,7 +21116,7 @@ import {
20992
21116
  } from "node:fs/promises";
20993
21117
  import { randomBytes as randomBytes3 } from "node:crypto";
20994
21118
  import { tmpdir as tmpdir2 } from "node:os";
20995
- import { isAbsolute as isAbsolute2, join as join4, relative as relative2, resolve as resolve3, sep as sep2 } from "node:path";
21119
+ import { isAbsolute as isAbsolute3, join as join4, relative as relative2, resolve as resolve3, sep as sep2 } from "node:path";
20996
21120
  var MAX_PROMPT_BYTES2, CODEX_MODEL_NAME_PATTERN, CODEX_REASONING_EFFORT_PATTERN, tokenUsageReceiptSchema, turnResultReceiptSchema, terminalReceiptSchema, recoveryCheckpointSchema, recoveryFileSchema, FileCodexAttemptRecoveryStore, utf8Bytes, assertAttemptActive, tomlString, permissionConfig, ensureDedicatedCodexHome, createIsolatedCodexAttemptResources, createIsolatedCodexHostResources, validateAttemptInput, isStrictDescendant, assertRecoveryResourceScope, removeRecoveredThread, confirmGuardianTerminatedForRecovery, reconcileCodexAttemptRecovery, CodexSubscriptionAdapter;
20997
21121
  var init_codex_adapter = __esm({
20998
21122
  "lib/inference-host/codex-adapter.ts"() {
@@ -21323,7 +21447,7 @@ var init_codex_adapter = __esm({
21323
21447
  const parentPath = resolve3(parent);
21324
21448
  const candidatePath = resolve3(candidate);
21325
21449
  const scoped = relative2(parentPath, candidatePath);
21326
- return scoped.length > 0 && !scoped.startsWith(`..${sep2}`) && scoped !== ".." && !isAbsolute2(scoped);
21450
+ return scoped.length > 0 && !scoped.startsWith(`..${sep2}`) && scoped !== ".." && !isAbsolute3(scoped);
21327
21451
  };
21328
21452
  assertRecoveryResourceScope = (checkpoint) => {
21329
21453
  const temporaryRoot = resolve3(tmpdir2());
@@ -21413,7 +21537,7 @@ var init_codex_adapter = __esm({
21413
21537
  }
21414
21538
  }
21415
21539
  if (checkpoint.threadPath !== null) {
21416
- if (!isAbsolute2(checkpoint.threadPath) || !isStrictDescendant(
21540
+ if (!isAbsolute3(checkpoint.threadPath) || !isStrictDescendant(
21417
21541
  checkpoint.threadPath,
21418
21542
  sessionsRoot
21419
21543
  ) || !checkpoint.threadPath.endsWith(".jsonl")) {
@@ -21505,6 +21629,23 @@ var init_codex_adapter = __esm({
21505
21629
  this.adapterClosePromise = null;
21506
21630
  this.dependencies = dependencies;
21507
21631
  }
21632
+ async readAccountRateLimits(deadlineAtMs, signal) {
21633
+ const codexHome = this.dependencies.codexHome;
21634
+ if (!codexHome) {
21635
+ throw new CodexAppServerError({
21636
+ message: "Codex home is required to read account rate limits.",
21637
+ category: "adapter",
21638
+ code: "codex_home_required",
21639
+ retryable: false
21640
+ });
21641
+ }
21642
+ const durableSession = await this.acquireDurableSession(
21643
+ codexHome,
21644
+ deadlineAtMs,
21645
+ signal
21646
+ );
21647
+ return await durableSession.session.readAccountRateLimits(deadlineAtMs, signal);
21648
+ }
21508
21649
  async startDurableSession(codexHome, deadlineAtMs) {
21509
21650
  const binary = await (this.dependencies.resolveBinary ?? resolvePinnedCodexBinary)();
21510
21651
  const guardianManaged = Boolean(
@@ -28680,7 +28821,7 @@ function createDefaultInferenceHostRunnerDependencies(options) {
28680
28821
  envelopePublicKey: options.envelopePublicKey
28681
28822
  };
28682
28823
  }
28683
- 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_ATTEMPT_HEARTBEAT_MS, DEFAULT_DRAIN_TIMEOUT_MS, DEFAULT_REMOTE_RETRY_LIMIT, DEFAULT_MAX_CONCURRENCY, MIN_SLEEP_MS, DEFAULT_CODEX_ACCOUNT_COOLDOWN_MS, MIN_CLAIM_START_WINDOW_MS, buildCodexInferenceAdvertisedModels, FileInferenceHostRuntimeReceiptStore, InferenceHostRecoveryRequiredError, InferenceHostRunnerError, defaultSleep, abortError, defaultRegisterSignalHandlers, safeInteger, exactObjectKeys, validIso, validateAttemptReceipt, validateRuntimeReceipt, sha256, stableOperationId, buildAttemptStartRequest, isoAt, finitePositiveOption, validateRunnerOptions, assertLocalCredentialIdentity, retryableRemoteError, remoteRetryAfterMs, controlPlaneFatal, defaultValidateOutput, objectRecord, positiveUtf8ByteLimit, assertCompilableOutputSchema, parseOutputContract, safeFailureCode, exactJson2, runnerIdentityMismatch, assertAdvertisementResult, assertHostHeartbeatResult, assertClaimResult, assertStartResult, assertJobHeartbeatResult, assertTerminalResult, classifyFailure, ambiguousHeartbeatTransport, reportedTokenUsage, reportedUsage, InferenceHostRunner;
28824
+ var import_ajv, RUNTIME_RECEIPT_SCHEMA_VERSION, ATTEMPT_RECEIPT_SCHEMA_VERSION, DEFAULT_ADVERTISEMENT_TTL_MS, DEFAULT_ADVERTISEMENT_REFRESH_LEAD_MS, DEFAULT_HOST_HEARTBEAT_MS, DEFAULT_PROVIDER_RATE_LIMIT_REFRESH_MS, DEFAULT_ATTEMPT_HEARTBEAT_MS, DEFAULT_DRAIN_TIMEOUT_MS, DEFAULT_REMOTE_RETRY_LIMIT, MIN_SLEEP_MS, DEFAULT_CODEX_ACCOUNT_COOLDOWN_MS, MIN_CLAIM_START_WINDOW_MS, UNBOUNDED_AVAILABLE_SLOTS, buildCodexInferenceAdvertisedModels, FileInferenceHostRuntimeReceiptStore, InferenceHostRecoveryRequiredError, InferenceHostRunnerError, defaultSleep, abortError, defaultRegisterSignalHandlers, safeInteger, exactObjectKeys, validIso, validateAttemptReceipt, validateRuntimeReceipt, sha256, stableOperationId, buildAttemptStartRequest, isoAt, providerWeeklyQuotaFromRateLimits, finitePositiveOption, validateRunnerOptions, assertLocalCredentialIdentity, retryableRemoteError, remoteRetryAfterMs, controlPlaneFatal, defaultValidateOutput, objectRecord, positiveUtf8ByteLimit, assertCompilableOutputSchema, parseOutputContract, safeFailureCode, exactJson2, runnerIdentityMismatch, assertAdvertisementResult, assertHostHeartbeatResult, assertClaimResult, assertStartResult, assertJobHeartbeatResult, assertTerminalResult, classifyFailure, ambiguousHeartbeatTransport, reportedTokenUsage, reportedUsage, InferenceHostRunner;
28684
28825
  var init_runner = __esm({
28685
28826
  "lib/inference-host/runner.ts"() {
28686
28827
  "use strict";
@@ -28697,13 +28838,14 @@ var init_runner = __esm({
28697
28838
  DEFAULT_ADVERTISEMENT_TTL_MS = 12e4;
28698
28839
  DEFAULT_ADVERTISEMENT_REFRESH_LEAD_MS = 3e4;
28699
28840
  DEFAULT_HOST_HEARTBEAT_MS = 3e3;
28841
+ DEFAULT_PROVIDER_RATE_LIMIT_REFRESH_MS = 6e4;
28700
28842
  DEFAULT_ATTEMPT_HEARTBEAT_MS = 3e3;
28701
28843
  DEFAULT_DRAIN_TIMEOUT_MS = 3e4;
28702
28844
  DEFAULT_REMOTE_RETRY_LIMIT = 3;
28703
- DEFAULT_MAX_CONCURRENCY = 1;
28704
28845
  MIN_SLEEP_MS = 10;
28705
28846
  DEFAULT_CODEX_ACCOUNT_COOLDOWN_MS = 5 * 60 * 1e3;
28706
28847
  MIN_CLAIM_START_WINDOW_MS = 5e3;
28848
+ UNBOUNDED_AVAILABLE_SLOTS = Number.MAX_SAFE_INTEGER;
28707
28849
  buildCodexInferenceAdvertisedModels = (capabilities, adapterRuntimeVersion) => {
28708
28850
  const visible = capabilities.filter((capability) => !capability.hidden);
28709
28851
  if (visible.length === 0 || visible.length > 64) {
@@ -28957,6 +29099,19 @@ var init_runner = __esm({
28957
29099
  started_at: startedAt
28958
29100
  });
28959
29101
  isoAt = (milliseconds) => new Date(milliseconds).toISOString();
29102
+ providerWeeklyQuotaFromRateLimits = (rateLimits, observedAtMs, currentTimeMs = observedAtMs) => {
29103
+ if (!rateLimits) return null;
29104
+ for (const window2 of [rateLimits.rateLimits.primary, rateLimits.rateLimits.secondary]) {
29105
+ if (window2?.windowDurationMins !== 7 * 24 * 60 || window2.resetsAt === null || window2.resetsAt * 1e3 <= currentTimeMs) continue;
29106
+ return {
29107
+ source: "codex_app_server",
29108
+ used_percent: window2.usedPercent,
29109
+ resets_at: isoAt(window2.resetsAt * 1e3),
29110
+ observed_at: isoAt(observedAtMs)
29111
+ };
29112
+ }
29113
+ return null;
29114
+ };
28960
29115
  finitePositiveOption = (value, fallback, label) => {
28961
29116
  const result2 = value ?? fallback;
28962
29117
  if (!Number.isSafeInteger(result2) || result2 < 1) {
@@ -28992,11 +29147,7 @@ var init_runner = __esm({
28992
29147
  options.codexModelCapabilities,
28993
29148
  options.adapterRuntimeVersion
28994
29149
  ),
28995
- maxConcurrency: finitePositiveOption(
28996
- options.maxConcurrency,
28997
- DEFAULT_MAX_CONCURRENCY,
28998
- "Maximum concurrency"
28999
- ),
29150
+ maxConcurrency: options.maxConcurrency === null || options.maxConcurrency === void 0 ? null : finitePositiveOption(options.maxConcurrency, 1, "Maximum concurrency"),
29000
29151
  advertisementTtlMs,
29001
29152
  advertisementRefreshLeadMs,
29002
29153
  hostHeartbeatMs: finitePositiveOption(
@@ -29004,6 +29155,11 @@ var init_runner = __esm({
29004
29155
  DEFAULT_HOST_HEARTBEAT_MS,
29005
29156
  "Host heartbeat interval"
29006
29157
  ),
29158
+ providerRateLimitRefreshMs: finitePositiveOption(
29159
+ options.providerRateLimitRefreshMs,
29160
+ DEFAULT_PROVIDER_RATE_LIMIT_REFRESH_MS,
29161
+ "Provider rate-limit refresh interval"
29162
+ ),
29007
29163
  attemptHeartbeatMs: finitePositiveOption(
29008
29164
  options.attemptHeartbeatMs,
29009
29165
  DEFAULT_ATTEMPT_HEARTBEAT_MS,
@@ -29334,6 +29490,20 @@ var init_runner = __esm({
29334
29490
  "Inference host login is required before the runner can start."
29335
29491
  );
29336
29492
  }
29493
+ const emitDiagnostic = (event, fields = {}) => {
29494
+ try {
29495
+ this.options.emitDiagnosticEvent?.({
29496
+ schema_version: "vtx_inference_host_diagnostic_v1",
29497
+ at: isoAt(now()),
29498
+ event,
29499
+ instance_name: this.options.instanceName ?? "default",
29500
+ host_id: localState.host_id,
29501
+ display_name: this.options.displayName,
29502
+ ...fields
29503
+ });
29504
+ } catch {
29505
+ }
29506
+ };
29337
29507
  const accountKey = inferenceCredentialAccountKey({
29338
29508
  issuer: localState.issuer,
29339
29509
  clientId: localState.client_id,
@@ -29530,6 +29700,10 @@ var init_runner = __esm({
29530
29700
  if (!wasRegistered && receipt.registered && (!receipt.advertisement_expires_at || Date.parse(receipt.advertisement_expires_at) - now() <= settings.advertisementRefreshLeadMs)) {
29531
29701
  await publishAdvertisement("healthy", true);
29532
29702
  }
29703
+ let providerRetryAtMs = this.options.codexRateLimits && codexAccountRateLimitReached(this.options.codexRateLimits) ? codexRateLimitRetryAtMs(this.options.codexRateLimits) ?? now() + DEFAULT_CODEX_ACCOUNT_COOLDOWN_MS : null;
29704
+ let providerCooldownReason = providerRetryAtMs === null ? null : "provider_limit_snapshot";
29705
+ let providerRateLimits = this.options.codexRateLimits ?? null;
29706
+ let providerRateLimitsObservedAtMs = now();
29533
29707
  const hostHeartbeat = async (status, requestOptions = {
29534
29708
  signal: this.options.signal
29535
29709
  }, retryRemote = true, allowOfflineProjection = false, definitiveReseedAttempt = 0) => {
@@ -29544,7 +29718,12 @@ var init_runner = __esm({
29544
29718
  sequence,
29545
29719
  status,
29546
29720
  active_attempts: active.size,
29547
- available_slots: Math.max(0, settings.maxConcurrency - active.size),
29721
+ available_slots: settings.maxConcurrency === null ? UNBOUNDED_AVAILABLE_SLOTS - active.size : Math.max(0, settings.maxConcurrency - active.size),
29722
+ provider_weekly_quota: providerWeeklyQuotaFromRateLimits(
29723
+ providerRateLimits,
29724
+ providerRateLimitsObservedAtMs,
29725
+ now()
29726
+ ),
29548
29727
  observed_at: isoAt(now())
29549
29728
  };
29550
29729
  receipt = {
@@ -29586,10 +29765,21 @@ var init_runner = __esm({
29586
29765
  await hostHeartbeat("healthy");
29587
29766
  let nextHostHeartbeatAt = now() + settings.hostHeartbeatMs;
29588
29767
  let nextAdvertisementAttemptAt = 0;
29589
- let providerRetryAtMs = this.options.codexRateLimits && codexAccountRateLimitReached(this.options.codexRateLimits) ? codexRateLimitRetryAtMs(this.options.codexRateLimits) ?? now() + DEFAULT_CODEX_ACCOUNT_COOLDOWN_MS : null;
29768
+ let nextProviderRateLimitRefreshAt = now() + settings.providerRateLimitRefreshMs;
29590
29769
  let nextClaimAt = Math.max(now(), providerRetryAtMs ?? 0);
29591
29770
  let pendingClaimPromotions = 0;
29592
29771
  let onceClaimed = false;
29772
+ emitDiagnostic("runtime_started", {
29773
+ max_concurrency: settings.maxConcurrency,
29774
+ active_attempts: active.size,
29775
+ provider_cooldown_reason: providerCooldownReason,
29776
+ provider_cooldown_until: providerRetryAtMs === null ? null : isoAt(providerRetryAtMs),
29777
+ provider_weekly_quota: providerWeeklyQuotaFromRateLimits(
29778
+ providerRateLimits,
29779
+ providerRateLimitsObservedAtMs,
29780
+ now()
29781
+ )
29782
+ });
29593
29783
  const launchClaim = (claim, recovery, claimRequest) => {
29594
29784
  if (!recovery) {
29595
29785
  claimed += 1;
@@ -29633,10 +29823,37 @@ var init_runner = __esm({
29633
29823
  attemptHeartbeatMs: settings.attemptHeartbeatMs,
29634
29824
  sleep: sleep4,
29635
29825
  now,
29636
- onProviderCooldown: (retryAtMs) => {
29826
+ onProviderCooldown: ({ retryAtMs, reason, rateLimits }) => {
29637
29827
  if (!Number.isSafeInteger(retryAtMs) || retryAtMs <= now()) return;
29828
+ const priorRetryAtMs = providerRetryAtMs;
29638
29829
  providerRetryAtMs = Math.max(providerRetryAtMs ?? 0, retryAtMs);
29639
29830
  nextClaimAt = Math.max(nextClaimAt, providerRetryAtMs);
29831
+ providerCooldownReason = reason;
29832
+ if (rateLimits) {
29833
+ providerRateLimits = rateLimits;
29834
+ providerRateLimitsObservedAtMs = now();
29835
+ }
29836
+ emitDiagnostic(
29837
+ priorRetryAtMs === null ? "provider_cooldown_started" : "provider_cooldown_extended",
29838
+ {
29839
+ reason,
29840
+ cooldown_until: isoAt(providerRetryAtMs),
29841
+ active_attempts: active.size,
29842
+ provider_weekly_quota: providerWeeklyQuotaFromRateLimits(
29843
+ providerRateLimits,
29844
+ providerRateLimitsObservedAtMs,
29845
+ now()
29846
+ )
29847
+ }
29848
+ );
29849
+ },
29850
+ onAttemptOutcome: (outcome) => {
29851
+ emitDiagnostic("attempt_finished", {
29852
+ ...outcome,
29853
+ job_id: claim.job_id,
29854
+ attempt_id: attemptId,
29855
+ active_attempts: active.size
29856
+ });
29640
29857
  },
29641
29858
  resumeReceipt: recovery,
29642
29859
  claimRequest,
@@ -29653,6 +29870,14 @@ var init_runner = __esm({
29653
29870
  attemptControllers.delete(attemptId);
29654
29871
  });
29655
29872
  active.set(attemptId, promise2);
29873
+ emitDiagnostic("attempt_started", {
29874
+ job_id: claim.job_id,
29875
+ attempt_id: attemptId,
29876
+ attempt_index: claim.attempt_index,
29877
+ requested_model: claim.requested_model,
29878
+ requested_reasoning_effort: claim.requested_reasoning_effort,
29879
+ active_attempts: active.size
29880
+ });
29656
29881
  return attemptId;
29657
29882
  };
29658
29883
  const removeRecoveredAttempt = async (attemptId) => {
@@ -29693,12 +29918,56 @@ var init_runner = __esm({
29693
29918
  }
29694
29919
  recoveryQueue.push(recovery);
29695
29920
  }
29696
- while (!drainRequested && recoveryQueue.length > 0 && active.size < settings.maxConcurrency) {
29921
+ while (!drainRequested && recoveryQueue.length > 0 && (settings.maxConcurrency === null || active.size < settings.maxConcurrency)) {
29697
29922
  const recovery = recoveryQueue.shift();
29698
29923
  launchClaim(recovery.claim, recovery);
29699
29924
  }
29700
29925
  while (!drainRequested) {
29701
29926
  const currentTime = now();
29927
+ if (providerRetryAtMs !== null && currentTime >= providerRetryAtMs && providerCooldownReason !== null) {
29928
+ emitDiagnostic("provider_cooldown_cleared", {
29929
+ reason: providerCooldownReason,
29930
+ active_attempts: active.size,
29931
+ provider_weekly_quota: providerWeeklyQuotaFromRateLimits(
29932
+ providerRateLimits,
29933
+ providerRateLimitsObservedAtMs,
29934
+ currentTime
29935
+ )
29936
+ });
29937
+ providerRetryAtMs = null;
29938
+ providerCooldownReason = null;
29939
+ nextClaimAt = Math.min(nextClaimAt, currentTime);
29940
+ }
29941
+ if (this.dependencies.codexAdapter.readAccountRateLimits && currentTime >= nextProviderRateLimitRefreshAt) {
29942
+ try {
29943
+ providerRateLimits = await this.dependencies.codexAdapter.readAccountRateLimits(
29944
+ Date.now() + Math.min(2e3, settings.hostHeartbeatMs),
29945
+ this.options.signal
29946
+ );
29947
+ providerRateLimitsObservedAtMs = now();
29948
+ const refreshedRetryAt = codexAccountRateLimitReached(providerRateLimits) ? codexRateLimitRetryAtMs(providerRateLimits) ?? currentTime + DEFAULT_CODEX_ACCOUNT_COOLDOWN_MS : null;
29949
+ if (refreshedRetryAt !== null && refreshedRetryAt > currentTime) {
29950
+ providerRetryAtMs = Math.max(providerRetryAtMs ?? 0, refreshedRetryAt);
29951
+ providerCooldownReason = "provider_limit_snapshot";
29952
+ nextClaimAt = Math.max(nextClaimAt, providerRetryAtMs);
29953
+ }
29954
+ emitDiagnostic("provider_rate_limits_refreshed", {
29955
+ provider_weekly_quota: providerWeeklyQuotaFromRateLimits(
29956
+ providerRateLimits,
29957
+ providerRateLimitsObservedAtMs,
29958
+ now()
29959
+ ),
29960
+ active_attempts: active.size
29961
+ });
29962
+ } catch (error48) {
29963
+ emitDiagnostic("provider_rate_limits_refresh_failed", {
29964
+ failure_category: error48 instanceof CodexAppServerError ? error48.category : "adapter",
29965
+ failure_code: error48 instanceof CodexAppServerError ? error48.code : "provider_rate_limits_refresh_failed"
29966
+ });
29967
+ } finally {
29968
+ nextProviderRateLimitRefreshAt = now() + settings.providerRateLimitRefreshMs;
29969
+ }
29970
+ }
29702
29971
  const expiresAt = receipt.advertisement_expires_at ? Date.parse(receipt.advertisement_expires_at) : 0;
29703
29972
  if (currentTime >= nextAdvertisementAttemptAt && expiresAt - currentTime <= settings.advertisementRefreshLeadMs) {
29704
29973
  try {
@@ -29741,11 +30010,11 @@ var init_runner = __esm({
29741
30010
  nextHostHeartbeatAt = now() + settings.hostHeartbeatMs;
29742
30011
  }
29743
30012
  }
29744
- while (recoveryQueue.length > 0 && active.size < settings.maxConcurrency) {
30013
+ while (recoveryQueue.length > 0 && (settings.maxConcurrency === null || active.size < settings.maxConcurrency)) {
29745
30014
  const recovery = recoveryQueue.shift();
29746
30015
  launchClaim(recovery.claim, recovery);
29747
30016
  }
29748
- while (!drainRequested && recoveryQueue.length === 0 && (!this.options.once || !onceClaimed) && active.size < settings.maxConcurrency && pendingClaimPromotions === 0 && now() >= nextClaimAt) {
30017
+ while (!drainRequested && recoveryQueue.length === 0 && (!this.options.once || !onceClaimed) && (settings.maxConcurrency === null || active.size < settings.maxConcurrency) && pendingClaimPromotions === 0 && now() >= nextClaimAt) {
29749
30018
  if (!receipt.pending_claim_request) {
29750
30019
  const claimSequence = receipt.claim_sequence + 1;
29751
30020
  const pendingClaimRequest = {
@@ -29864,6 +30133,13 @@ var init_runner = __esm({
29864
30133
  requestDrain("drain_timeout");
29865
30134
  }
29866
30135
  }
30136
+ emitDiagnostic("runtime_stopped", {
30137
+ claimed,
30138
+ completed,
30139
+ failed,
30140
+ drain_reason: drainReason,
30141
+ active_attempts: active.size
30142
+ });
29867
30143
  return { claimed, completed, failed, drain_reason: drainReason };
29868
30144
  } finally {
29869
30145
  removeAbortListener();
@@ -30209,13 +30485,27 @@ var init_runner = __esm({
30209
30485
  );
30210
30486
  }
30211
30487
  await removeAttempt();
30488
+ options.onAttemptOutcome?.({
30489
+ outcome: "completed",
30490
+ failure_category: null,
30491
+ failure_code: null,
30492
+ retryable: null,
30493
+ dispatch_outcome: completion.outcome.dispatch_outcome,
30494
+ response_outcome: completion.outcome.response_outcome,
30495
+ latency_ms: completion.latency_ms,
30496
+ usage: completion.usage
30497
+ });
30212
30498
  return "completed";
30213
30499
  } catch (error48) {
30214
30500
  if (error48 instanceof InferenceHostRunnerError && error48.code === "terminal_outcome_unconfirmed") {
30215
30501
  throw error48;
30216
30502
  }
30217
30503
  if (error48 instanceof CodexAppServerError && error48.retryAtMs !== null && ["quota_exceeded", "codex_rate_limited"].includes(error48.code)) {
30218
- options.onProviderCooldown?.(error48.retryAtMs);
30504
+ options.onProviderCooldown?.({
30505
+ retryAtMs: error48.retryAtMs,
30506
+ reason: error48.code === "quota_exceeded" ? "quota_exceeded" : "codex_rate_limited",
30507
+ rateLimits: error48.rateLimits
30508
+ });
30219
30509
  }
30220
30510
  const classified = classifyFailure(heartbeatFailure ?? error48, adapterResult);
30221
30511
  const heartbeatAmbiguous = ambiguousHeartbeatTransport(heartbeatFailure);
@@ -30295,6 +30585,16 @@ var init_runner = __esm({
30295
30585
  );
30296
30586
  }
30297
30587
  await removeAttempt();
30588
+ options.onAttemptOutcome?.({
30589
+ outcome: "failed",
30590
+ failure_category: failureRequest.failure_category,
30591
+ failure_code: failureRequest.failure_code,
30592
+ retryable: failureRequest.retryable,
30593
+ dispatch_outcome: failureRequest.outcome.dispatch_outcome,
30594
+ response_outcome: failureRequest.outcome.response_outcome,
30595
+ latency_ms: adapterResult?.latencyMs ?? null,
30596
+ usage: failureRequest.usage
30597
+ });
30298
30598
  return "failed";
30299
30599
  } finally {
30300
30600
  heartbeatStopped = true;
@@ -30335,7 +30635,7 @@ import { createWriteStream, readFileSync } from "node:fs";
30335
30635
  import { access as access3, chmod as chmod3, mkdir as mkdir3, readFile as readFile5, rm as rm4, writeFile as writeFile2 } from "node:fs/promises";
30336
30636
  import { homedir as homedir2 } from "node:os";
30337
30637
  import { dirname as dirname4, join as join5, resolve as resolve4 } from "node:path";
30338
- var SERVICE_NAME, SYSTEMD_UNIT, LAUNCHD_LABEL, SERVICE_COOPERATIVE_STOP_SECONDS, isWindowsSubsystemForLinux, inferenceHostServiceManifestPath, inferenceHostServiceDesiredPath, inferenceHostServiceLogPath, xmlEscape, plistEscape, systemdQuote, defaultRunCommand, managerName, assertManifest, assertDesiredState, readInferenceHostServiceManifest, readInferenceHostServiceDesired, readDesiredAcrossAtomicReplacement, writeDesired, runtimeEnvironment, serviceArguments, windowsOwnedCommandLine, vbScriptString, windowsServiceLauncher, windowsTaskXml, systemdUnit, launchAgentPlist, InferenceHostServiceManager, appendServiceLog, spawnServiceChild, runInferenceHostServiceSupervisor;
30638
+ var SERVICE_NAME, SYSTEMD_UNIT, LAUNCHD_LABEL, SERVICE_COOPERATIVE_STOP_SECONDS, inferenceHostServiceChildEnvironment, isWindowsSubsystemForLinux, inferenceHostServiceManifestPath, inferenceHostServiceDesiredPath, inferenceHostServiceLogPath, xmlEscape, plistEscape, systemdQuote, defaultRunCommand, managerName, assertRuntimeEnvironment, withoutConcurrencyLimit, assertServicePath, assertWorker, assertManifest, assertDesiredState, readInferenceHostServiceManifest, readInferenceHostServiceDesired, readDesiredAcrossAtomicReplacement, writeDesired, runtimeEnvironment, serviceArguments, windowsOwnedCommandLine, vbScriptString, windowsServiceLauncher, windowsTaskXml, systemdUnit, launchAgentPlist, InferenceHostServiceManager, appendServiceLog, spawnServiceChild, runInferenceHostServiceSupervisor;
30339
30639
  var init_service = __esm({
30340
30640
  "lib/inference-host/service.ts"() {
30341
30641
  "use strict";
@@ -30344,6 +30644,15 @@ var init_service = __esm({
30344
30644
  SYSTEMD_UNIT = "vtx-inference-host.service";
30345
30645
  LAUNCHD_LABEL = "com.vtxmacro.inference-host";
30346
30646
  SERVICE_COOPERATIVE_STOP_SECONDS = 75;
30647
+ inferenceHostServiceChildEnvironment = (runtimeEnvironment2, inheritedEnvironment = process.env) => {
30648
+ const environment = { ...inheritedEnvironment };
30649
+ for (const key of Object.keys(environment)) {
30650
+ if (key === "VTX_API_URL" || key.startsWith("VTX_INFERENCE_HOST_")) {
30651
+ delete environment[key];
30652
+ }
30653
+ }
30654
+ return { ...environment, ...runtimeEnvironment2 };
30655
+ };
30347
30656
  isWindowsSubsystemForLinux = (env = process.env, kernelRelease) => Boolean(
30348
30657
  String(env.WSL_INTEROP || "").trim() || String(env.WSL_DISTRO_NAME || "").trim() || /microsoft/iu.test(kernelRelease ?? (() => {
30349
30658
  try {
@@ -30353,9 +30662,9 @@ var init_service = __esm({
30353
30662
  }
30354
30663
  })())
30355
30664
  );
30356
- inferenceHostServiceManifestPath = (config2) => `${config2.statePath}.service.json`;
30357
- inferenceHostServiceDesiredPath = (config2) => `${config2.statePath}.service-desired.json`;
30358
- inferenceHostServiceLogPath = (config2) => `${config2.statePath}.service.log`;
30665
+ inferenceHostServiceManifestPath = (config2) => `${config2.supervisorStatePath}.service.json`;
30666
+ inferenceHostServiceDesiredPath = (config2) => `${config2.supervisorStatePath}.service-desired.json`;
30667
+ inferenceHostServiceLogPath = (config2) => `${config2.supervisorStatePath}.service.log`;
30359
30668
  xmlEscape = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
30360
30669
  plistEscape = xmlEscape;
30361
30670
  systemdQuote = (value) => `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("%", "%%")}"`;
@@ -30385,29 +30694,118 @@ var init_service = __esm({
30385
30694
  if (platform === "linux") return "systemd-user";
30386
30695
  throw new Error(`Inference-host background service is unsupported on ${platform}.`);
30387
30696
  };
30697
+ assertRuntimeEnvironment = (value) => {
30698
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
30699
+ throw new Error("Inference-host service runtime environment is invalid.");
30700
+ }
30701
+ for (const [key, entry] of Object.entries(value)) {
30702
+ if (!key.startsWith("VTX_") || typeof entry !== "string") {
30703
+ throw new Error("Inference-host service runtime environment is invalid.");
30704
+ }
30705
+ }
30706
+ return value;
30707
+ };
30708
+ withoutConcurrencyLimit = (environment) => {
30709
+ const { VTX_INFERENCE_HOST_MAX_CONCURRENCY: _removed, ...rest } = environment;
30710
+ return rest;
30711
+ };
30712
+ assertServicePath = (value) => {
30713
+ if (typeof value !== "string" || !value || /[\r\n\0]/u.test(value)) {
30714
+ throw new Error("Inference-host service manifest paths contain control characters.");
30715
+ }
30716
+ return value;
30717
+ };
30718
+ assertWorker = (value) => {
30719
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
30720
+ throw new Error("Inference-host service worker is invalid.");
30721
+ }
30722
+ const record2 = value;
30723
+ if (typeof record2.instance_name !== "string" || !/^[a-z0-9](?:[a-z0-9-]{0,46}[a-z0-9])?$/u.test(record2.instance_name) || record2.adapter !== "codex" || typeof record2.display_name !== "string" || !record2.display_name || record2.max_concurrency !== null && (!Number.isSafeInteger(record2.max_concurrency) || Number(record2.max_concurrency) < 1) || record2.authenticated_account_email !== null && typeof record2.authenticated_account_email !== "string" || record2.authenticated_account_plan !== null && typeof record2.authenticated_account_plan !== "string") {
30724
+ throw new Error("Inference-host service worker is invalid.");
30725
+ }
30726
+ return {
30727
+ instance_name: record2.instance_name,
30728
+ adapter: "codex",
30729
+ display_name: record2.display_name,
30730
+ max_concurrency: record2.max_concurrency === null ? null : Number(record2.max_concurrency),
30731
+ authenticated_account_email: record2.authenticated_account_email,
30732
+ authenticated_account_plan: record2.authenticated_account_plan,
30733
+ runtime_environment: assertRuntimeEnvironment(record2.runtime_environment)
30734
+ };
30735
+ };
30388
30736
  assertManifest = (value) => {
30389
30737
  if (!value || typeof value !== "object" || Array.isArray(value)) {
30390
30738
  throw new Error("Inference-host service manifest is invalid.");
30391
30739
  }
30392
30740
  const record2 = value;
30393
- if (record2.schema_version !== "vtx_inference_service_v1" || record2.adapter !== "codex" || typeof record2.installed_at !== "string" || !Number.isFinite(Date.parse(record2.installed_at)) || typeof record2.executable !== "string" || !record2.executable || typeof record2.script !== "string" || !record2.script || typeof record2.display_name !== "string" || !record2.display_name || typeof record2.log_path !== "string" || !record2.log_path || !record2.runtime_environment || typeof record2.runtime_environment !== "object" || Array.isArray(record2.runtime_environment)) {
30394
- throw new Error("Inference-host service manifest is invalid.");
30741
+ if (record2.schema_version === "vtx_inference_service_v1") {
30742
+ const legacy = record2;
30743
+ if (legacy.adapter !== "codex" || typeof legacy.installed_at !== "string" || !Number.isFinite(Date.parse(legacy.installed_at)) || typeof legacy.executable !== "string" || !legacy.executable || typeof legacy.script !== "string" || !legacy.script || typeof legacy.display_name !== "string" || !legacy.display_name || typeof legacy.log_path !== "string" || !legacy.log_path) throw new Error("Inference-host service manifest is invalid.");
30744
+ const runtime_environment = withoutConcurrencyLimit(
30745
+ assertRuntimeEnvironment(legacy.runtime_environment)
30746
+ );
30747
+ const worker = assertWorker({
30748
+ instance_name: "default",
30749
+ adapter: "codex",
30750
+ display_name: legacy.display_name,
30751
+ max_concurrency: null,
30752
+ authenticated_account_email: null,
30753
+ authenticated_account_plan: null,
30754
+ runtime_environment
30755
+ });
30756
+ return {
30757
+ schema_version: "vtx_inference_service_v3",
30758
+ installed_at: legacy.installed_at,
30759
+ executable: assertServicePath(legacy.executable),
30760
+ script: assertServicePath(legacy.script),
30761
+ log_path: assertServicePath(legacy.log_path),
30762
+ workers: [worker]
30763
+ };
30395
30764
  }
30396
- for (const value2 of [
30397
- record2.executable,
30398
- record2.script,
30399
- record2.log_path
30400
- ]) {
30401
- if (typeof value2 === "string" && /[\r\n\0]/u.test(value2)) {
30402
- throw new Error("Inference-host service manifest paths contain control characters.");
30765
+ if (record2.schema_version === "vtx_inference_service_v2") {
30766
+ const legacy = record2;
30767
+ if (typeof legacy.installed_at !== "string" || !Number.isFinite(Date.parse(legacy.installed_at)) || typeof legacy.executable !== "string" || !legacy.executable || typeof legacy.script !== "string" || !legacy.script || typeof legacy.log_path !== "string" || !legacy.log_path || !Array.isArray(legacy.workers) || legacy.workers.length < 1) {
30768
+ throw new Error("Inference-host service manifest is invalid.");
30769
+ }
30770
+ const workers2 = legacy.workers.map((worker) => {
30771
+ if (!worker || typeof worker !== "object" || Array.isArray(worker)) {
30772
+ throw new Error("Inference-host service worker is invalid.");
30773
+ }
30774
+ const legacyWorker = worker;
30775
+ if (!Number.isSafeInteger(legacyWorker.max_concurrency) || Number(legacyWorker.max_concurrency) < 1) {
30776
+ throw new Error("Inference-host service worker is invalid.");
30777
+ }
30778
+ return assertWorker({
30779
+ ...legacyWorker,
30780
+ max_concurrency: null,
30781
+ runtime_environment: withoutConcurrencyLimit(
30782
+ assertRuntimeEnvironment(legacyWorker.runtime_environment)
30783
+ )
30784
+ });
30785
+ });
30786
+ if (new Set(workers2.map((worker) => worker.instance_name)).size !== workers2.length) {
30787
+ throw new Error("Inference-host service worker names must be unique.");
30403
30788
  }
30789
+ return {
30790
+ schema_version: "vtx_inference_service_v3",
30791
+ installed_at: legacy.installed_at,
30792
+ executable: assertServicePath(legacy.executable),
30793
+ script: assertServicePath(legacy.script),
30794
+ log_path: assertServicePath(legacy.log_path),
30795
+ workers: workers2
30796
+ };
30404
30797
  }
30405
- for (const [key, entry] of Object.entries(record2.runtime_environment)) {
30406
- if (!key.startsWith("VTX_") || typeof entry !== "string") {
30407
- throw new Error("Inference-host service runtime environment is invalid.");
30408
- }
30798
+ if (record2.schema_version !== "vtx_inference_service_v3" || typeof record2.installed_at !== "string" || !Number.isFinite(Date.parse(record2.installed_at)) || typeof record2.executable !== "string" || !record2.executable || typeof record2.script !== "string" || !record2.script || typeof record2.log_path !== "string" || !record2.log_path || !Array.isArray(record2.workers) || record2.workers.length < 1) {
30799
+ throw new Error("Inference-host service manifest is invalid.");
30409
30800
  }
30410
- return record2;
30801
+ assertServicePath(record2.executable);
30802
+ assertServicePath(record2.script);
30803
+ assertServicePath(record2.log_path);
30804
+ const workers = record2.workers.map(assertWorker);
30805
+ if (new Set(workers.map((worker) => worker.instance_name)).size !== workers.length) {
30806
+ throw new Error("Inference-host service worker names must be unique.");
30807
+ }
30808
+ return { ...record2, workers };
30411
30809
  };
30412
30810
  assertDesiredState = (value) => {
30413
30811
  if (!value || typeof value !== "object" || Array.isArray(value)) {
@@ -30459,14 +30857,18 @@ var init_service = __esm({
30459
30857
  }, null, 2)}
30460
30858
  `);
30461
30859
  };
30462
- runtimeEnvironment = (config2) => {
30860
+ runtimeEnvironment = (config2, maxConcurrency) => {
30463
30861
  const result2 = {
30464
30862
  VTX_API_URL: config2.apiUrl,
30465
- VTX_INFERENCE_HOST_HOME: dirname4(config2.codexHomePath),
30863
+ VTX_INFERENCE_HOST_HOME: config2.baseDir,
30864
+ VTX_INFERENCE_HOST_INSTANCE: config2.instanceName,
30466
30865
  VTX_INFERENCE_HOST_CREDENTIAL_STORE: config2.credentialStoreMode,
30467
30866
  VTX_INFERENCE_HOST_STATE_PATH: config2.statePath,
30468
30867
  VTX_INFERENCE_HOST_LOCK_PATH: config2.processLockPath
30469
30868
  };
30869
+ if (maxConcurrency !== null) {
30870
+ result2.VTX_INFERENCE_HOST_MAX_CONCURRENCY = String(maxConcurrency);
30871
+ }
30470
30872
  if (config2.credentialFilePath) {
30471
30873
  result2.VTX_INFERENCE_HOST_CREDENTIAL_FILE = config2.credentialFilePath;
30472
30874
  }
@@ -30581,7 +30983,7 @@ WantedBy=default.target
30581
30983
  return inferenceHostServiceLogPath(this.config);
30582
30984
  }
30583
30985
  controlLockPath() {
30584
- return `${this.config.processLockPath}.service-control`;
30986
+ return `${this.config.supervisorProcessLockPath}.service-control`;
30585
30987
  }
30586
30988
  async withControlLock(operation) {
30587
30989
  let lock2 = null;
@@ -30617,12 +31019,12 @@ WantedBy=default.target
30617
31019
  ];
30618
31020
  }
30619
31021
  definitionPath() {
30620
- if (this.platform === "win32") return `${this.config.statePath}.service-task.xml`;
31022
+ if (this.platform === "win32") return `${this.config.supervisorStatePath}.service-task.xml`;
30621
31023
  if (this.platform === "darwin") return join5(this.home, "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
30622
31024
  return join5(this.home, ".config", "systemd", "user", SYSTEMD_UNIT);
30623
31025
  }
30624
31026
  windowsLauncherPath() {
30625
- return `${this.config.statePath}.service-launcher.vbs`;
31027
+ return `${this.config.supervisorStatePath}.service-launcher.vbs`;
30626
31028
  }
30627
31029
  async managerCommand(action) {
30628
31030
  const definition = this.definitionPath();
@@ -30665,37 +31067,19 @@ WantedBy=default.target
30665
31067
  }
30666
31068
  throw new Error("Background service did not reach an active state within 10 seconds.");
30667
31069
  }
30668
- async install(options) {
30669
- return await this.withControlLock(async () => await this.installUnlocked(options));
30670
- }
30671
- async installUnlocked(options) {
30672
- if (await readInferenceHostServiceManifest(this.manifestPath())) {
30673
- await this.uninstallUnlocked();
30674
- }
30675
- await access3(this.executable);
30676
- await access3(this.script);
30677
- const manifest = assertManifest({
30678
- schema_version: "vtx_inference_service_v1",
30679
- installed_at: this.now().toISOString(),
30680
- adapter: options.adapter,
30681
- executable: this.executable,
30682
- script: this.script,
30683
- display_name: options.displayName,
30684
- log_path: this.logPath(),
30685
- runtime_environment: runtimeEnvironment(this.config)
30686
- });
30687
- const args = serviceArguments(this.script, this.manifestPath());
30688
- const definition = this.platform === "win32" ? windowsTaskXml(this.windowsLauncherPath(), this.windowsDirectory, this.username) : this.platform === "darwin" ? launchAgentPlist(this.executable, args, this.logPath()) : systemdUnit(this.executable, args);
31070
+ async registerManifestUnlocked(manifest, desiredRunning) {
31071
+ const args = serviceArguments(manifest.script, this.manifestPath());
31072
+ const definition = this.platform === "win32" ? windowsTaskXml(this.windowsLauncherPath(), this.windowsDirectory, this.username) : this.platform === "darwin" ? launchAgentPlist(manifest.executable, args, manifest.log_path) : systemdUnit(manifest.executable, args);
30689
31073
  let managerInstallAttempted = false;
30690
31074
  try {
30691
31075
  await writeAtomicInferencePrivateFile(this.manifestPath(), `${JSON.stringify(manifest, null, 2)}
30692
31076
  `);
30693
- await writeDesired(this.desiredPath(), options.startImmediately !== false, this.now());
31077
+ await writeDesired(this.desiredPath(), desiredRunning, this.now());
30694
31078
  await mkdir3(dirname4(this.definitionPath()), { recursive: true, mode: 448 });
30695
31079
  if (this.platform === "win32") {
30696
31080
  await writeAtomicInferencePrivateFile(
30697
31081
  this.windowsLauncherPath(),
30698
- windowsServiceLauncher(this.executable, args)
31082
+ windowsServiceLauncher(manifest.executable, args)
30699
31083
  );
30700
31084
  await writeFile2(this.definitionPath(), `\uFEFF${definition}`, {
30701
31085
  encoding: "utf16le",
@@ -30712,7 +31096,7 @@ WantedBy=default.target
30712
31096
  managerInstallAttempted = true;
30713
31097
  const installed = await this.managerCommand("install");
30714
31098
  if (installed.exitCode !== 0) throw new Error(`Background service installation failed: ${installed.stderr.trim()}`);
30715
- if (options.startImmediately !== false) {
31099
+ if (desiredRunning) {
30716
31100
  const started = await this.managerCommand("start");
30717
31101
  if (started.exitCode !== 0 && !/already running|in progress/iu.test(`${started.stdout}
30718
31102
  ${started.stderr}`)) {
@@ -30720,6 +31104,7 @@ ${started.stderr}`)) {
30720
31104
  }
30721
31105
  await this.waitForManagerActive();
30722
31106
  }
31107
+ return await this.status();
30723
31108
  } catch (error48) {
30724
31109
  await writeDesired(this.desiredPath(), false, this.now()).catch(() => void 0);
30725
31110
  if (managerInstallAttempted) {
@@ -30747,7 +31132,83 @@ ${cleanup.stderr}`)) {
30747
31132
  }
30748
31133
  throw error48;
30749
31134
  }
30750
- return await this.status();
31135
+ }
31136
+ async replaceManifestUnlocked(next, desiredRunning) {
31137
+ const previous = await readInferenceHostServiceManifest(this.manifestPath());
31138
+ const previousDesired = previous ? await readInferenceHostServiceDesired(this.desiredPath()) : false;
31139
+ if (previous) await this.uninstallUnlocked();
31140
+ try {
31141
+ return await this.registerManifestUnlocked(next, desiredRunning);
31142
+ } catch (error48) {
31143
+ if (!previous) throw error48;
31144
+ try {
31145
+ await this.registerManifestUnlocked(previous, previousDesired);
31146
+ } catch (rollbackError) {
31147
+ throw new Error(
31148
+ "Inference-host service reconfiguration failed and the previous supervisor could not be restored.",
31149
+ { cause: new AggregateError([error48, rollbackError]) }
31150
+ );
31151
+ }
31152
+ throw new Error(
31153
+ "Inference-host service reconfiguration failed; the previous supervisor was restored.",
31154
+ { cause: error48 }
31155
+ );
31156
+ }
31157
+ }
31158
+ async install(options) {
31159
+ return await this.withControlLock(async () => await this.installUnlocked(options));
31160
+ }
31161
+ async installUnlocked(options) {
31162
+ const existing = await readInferenceHostServiceManifest(this.manifestPath());
31163
+ const normalizedEmail = options.authenticatedAccountEmail?.trim().toLowerCase() || null;
31164
+ const otherWorkers = existing?.workers.filter(
31165
+ (worker) => worker.instance_name !== this.config.instanceName
31166
+ ) ?? [];
31167
+ if (otherWorkers.length > 0 && (!normalizedEmail || otherWorkers.some((worker) => !worker.authenticated_account_email))) {
31168
+ throw new Error(
31169
+ "Cannot prove that every installed instance uses a distinct ChatGPT subscription. Reinstall instances with live account identity before adding another."
31170
+ );
31171
+ }
31172
+ if (normalizedEmail && otherWorkers.some((worker) => worker.authenticated_account_email?.trim().toLowerCase() === normalizedEmail)) {
31173
+ throw new Error(
31174
+ "This ChatGPT subscription is already installed under another inference-host instance."
31175
+ );
31176
+ }
31177
+ if (this.config.credentialFilePath && otherWorkers.some((worker) => {
31178
+ const otherPath = worker.runtime_environment.VTX_INFERENCE_HOST_CREDENTIAL_FILE;
31179
+ return otherPath && resolve4(otherPath) === resolve4(this.config.credentialFilePath);
31180
+ })) {
31181
+ throw new Error(
31182
+ "Each inference-host instance must use a different private credential file."
31183
+ );
31184
+ }
31185
+ await access3(this.executable);
31186
+ await access3(this.script);
31187
+ const maxConcurrency = options.maxConcurrency ?? null;
31188
+ const workers = [
31189
+ ...existing?.workers.filter((worker) => worker.instance_name !== this.config.instanceName) ?? [],
31190
+ {
31191
+ instance_name: this.config.instanceName,
31192
+ adapter: options.adapter,
31193
+ display_name: options.displayName,
31194
+ max_concurrency: maxConcurrency,
31195
+ authenticated_account_email: normalizedEmail,
31196
+ authenticated_account_plan: options.authenticatedAccountPlan ?? null,
31197
+ runtime_environment: runtimeEnvironment(this.config, maxConcurrency)
31198
+ }
31199
+ ].sort((left, right) => left.instance_name.localeCompare(right.instance_name));
31200
+ const manifest = assertManifest({
31201
+ schema_version: "vtx_inference_service_v3",
31202
+ installed_at: this.now().toISOString(),
31203
+ executable: this.executable,
31204
+ script: this.script,
31205
+ log_path: this.logPath(),
31206
+ workers
31207
+ });
31208
+ return await this.replaceManifestUnlocked(
31209
+ manifest,
31210
+ options.startImmediately !== false
31211
+ );
30751
31212
  }
30752
31213
  async start() {
30753
31214
  return await this.withControlLock(async () => await this.startUnlocked());
@@ -30774,11 +31235,12 @@ ${result2.stderr}`)) {
30774
31235
  return await this.withControlLock(async () => await this.stopUnlocked());
30775
31236
  }
30776
31237
  async stopUnlocked() {
30777
- if (!await readInferenceHostServiceManifest(this.manifestPath())) {
31238
+ const manifest = await readInferenceHostServiceManifest(this.manifestPath());
31239
+ if (!manifest) {
30778
31240
  throw new Error("Inference-host service is not installed.");
30779
31241
  }
30780
31242
  await writeDesired(this.desiredPath(), false, this.now());
30781
- const serviceLockPath = `${this.config.processLockPath}.service`;
31243
+ const serviceLockPath = `${this.config.supervisorProcessLockPath}.service`;
30782
31244
  let serviceReleased = false;
30783
31245
  for (let attempt = 0; attempt < this.stopWaitAttempts; attempt += 1) {
30784
31246
  try {
@@ -30796,7 +31258,7 @@ ${result2.stderr}`)) {
30796
31258
  }
30797
31259
  if (!serviceReleased) {
30798
31260
  throw new Error(
30799
- `Inference-host worker did not stop cooperatively within ${SERVICE_COOPERATIVE_STOP_SECONDS} seconds; refusing forced termination while cleanup may be pending.`
31261
+ `Inference-host workers did not stop cooperatively within ${SERVICE_COOPERATIVE_STOP_SECONDS} seconds; refusing forced termination while cleanup may be pending.`
30800
31262
  );
30801
31263
  }
30802
31264
  const result2 = await this.managerCommand("stop");
@@ -30822,22 +31284,56 @@ ${result2.stderr}`)) {
30822
31284
  manager_active: managerActive,
30823
31285
  manager: managerName(this.platform),
30824
31286
  manager_state: result2.exitCode === 0 ? output3 || "installed" : "not-installed",
30825
- adapter: manifest?.adapter ?? null,
30826
- log_path: manifest?.log_path ?? this.logPath()
31287
+ adapter: manifest ? "codex" : null,
31288
+ log_path: manifest?.log_path ?? this.logPath(),
31289
+ workers: manifest?.workers.map((worker) => ({
31290
+ instance_name: worker.instance_name,
31291
+ display_name: worker.display_name,
31292
+ max_concurrency: worker.max_concurrency,
31293
+ authenticated_account_email: worker.authenticated_account_email,
31294
+ authenticated_account_plan: worker.authenticated_account_plan
31295
+ })) ?? []
30827
31296
  };
30828
31297
  }
30829
- async logs(lines = 100) {
31298
+ async logs(lines = 100, instanceName) {
30830
31299
  const manifest = await readInferenceHostServiceManifest(this.manifestPath());
30831
31300
  const path = manifest?.log_path ?? this.logPath();
30832
31301
  try {
30833
31302
  const contents = await readFile5(path, "utf8");
30834
- return `${contents.trimEnd().split(/\r?\n/u).slice(-lines).join("\n")}
30835
- `;
31303
+ const logLines = contents.trimEnd().split(/\r?\n/u);
31304
+ const filtered = instanceName ? logLines.filter((line) => {
31305
+ try {
31306
+ const parsed = JSON.parse(line);
31307
+ return parsed.instance_name === instanceName || Array.isArray(parsed.instances) && parsed.instances.includes(instanceName);
31308
+ } catch {
31309
+ return false;
31310
+ }
31311
+ }) : logLines;
31312
+ return filtered.length > 0 ? `${filtered.slice(-lines).join("\n")}
31313
+ ` : "";
30836
31314
  } catch (error48) {
30837
31315
  if (error48.code === "ENOENT") return "";
30838
31316
  throw error48;
30839
31317
  }
30840
31318
  }
31319
+ async uninstallInstance(instanceName) {
31320
+ return await this.withControlLock(async () => {
31321
+ const manifest = await readInferenceHostServiceManifest(this.manifestPath());
31322
+ if (!manifest) throw new Error("Inference-host service is not installed.");
31323
+ if (!manifest.workers.some((worker) => worker.instance_name === instanceName)) {
31324
+ throw new Error(`Inference-host instance ${instanceName} is not installed.`);
31325
+ }
31326
+ const remaining = manifest.workers.filter(
31327
+ (worker) => worker.instance_name !== instanceName
31328
+ );
31329
+ if (remaining.length === 0) return await this.uninstallUnlocked();
31330
+ return await this.replaceManifestUnlocked({
31331
+ ...manifest,
31332
+ installed_at: this.now().toISOString(),
31333
+ workers: remaining
31334
+ }, await readInferenceHostServiceDesired(this.desiredPath()));
31335
+ });
31336
+ }
30841
31337
  async uninstall() {
30842
31338
  return await this.withControlLock(async () => await this.uninstallUnlocked());
30843
31339
  }
@@ -30867,7 +31363,8 @@ ${result2.stderr}`)) {
30867
31363
  manager: managerName(this.platform),
30868
31364
  manager_state: "not-installed",
30869
31365
  adapter: null,
30870
- log_path: manifest.log_path
31366
+ log_path: manifest.log_path,
31367
+ workers: []
30871
31368
  };
30872
31369
  }
30873
31370
  };
@@ -30880,39 +31377,74 @@ ${result2.stderr}`)) {
30880
31377
  `, resolvePromise);
30881
31378
  });
30882
31379
  };
30883
- spawnServiceChild = async (manifest, signal) => {
31380
+ spawnServiceChild = async (manifest, worker, signal) => {
30884
31381
  const args = [
30885
31382
  manifest.script,
30886
31383
  "inference-host",
30887
31384
  "run",
30888
31385
  "--json",
30889
31386
  "--display-name",
30890
- manifest.display_name
31387
+ worker.display_name
30891
31388
  ];
30892
31389
  const startedAt = Date.now();
30893
31390
  const log = createWriteStream(manifest.log_path, { flags: "a", mode: 384 });
30894
31391
  return await new Promise((resolvePromise, reject) => {
30895
31392
  let stdout = "";
31393
+ const pending = { stdout: "", stderr: "" };
31394
+ const writeTaggedOutput = (stream, text, flush = false) => {
31395
+ const lines = `${pending[stream]}${text}`.split(/\r?\n/u);
31396
+ const remainder = lines.pop() ?? "";
31397
+ pending[stream] = flush ? "" : remainder;
31398
+ if (flush && remainder) lines.push(remainder);
31399
+ if (flush && lines.at(-1) === "") lines.pop();
31400
+ for (const line of lines) {
31401
+ if (!line) continue;
31402
+ try {
31403
+ const parsed = JSON.parse(line);
31404
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
31405
+ log.write(`${JSON.stringify({
31406
+ ...parsed,
31407
+ instance_name: worker.instance_name
31408
+ })}
31409
+ `);
31410
+ continue;
31411
+ }
31412
+ } catch {
31413
+ }
31414
+ log.write(`${JSON.stringify({
31415
+ at: (/* @__PURE__ */ new Date()).toISOString(),
31416
+ event: "worker_output",
31417
+ instance_name: worker.instance_name,
31418
+ stream,
31419
+ message: line.slice(0, 4096)
31420
+ })}
31421
+ `);
31422
+ }
31423
+ };
30896
31424
  const child = spawn5(manifest.executable, args, {
30897
- env: { ...process.env, ...manifest.runtime_environment },
31425
+ env: inferenceHostServiceChildEnvironment(worker.runtime_environment),
30898
31426
  windowsHide: true,
30899
31427
  stdio: ["ignore", "pipe", "pipe"]
30900
31428
  });
30901
31429
  child.stdout.on("data", (chunk) => {
30902
31430
  const text = chunk.toString("utf8");
30903
- log.write(text);
31431
+ writeTaggedOutput("stdout", text);
30904
31432
  stdout = `${stdout}${text}`.slice(-65536);
30905
31433
  });
30906
- child.stderr.on("data", (chunk) => log.write(chunk));
31434
+ child.stderr.on("data", (chunk) => writeTaggedOutput("stderr", chunk.toString("utf8")));
30907
31435
  const onAbort = () => child.kill("SIGTERM");
30908
31436
  signal.addEventListener("abort", onAbort, { once: true });
30909
31437
  child.once("error", (error48) => {
30910
31438
  signal.removeEventListener("abort", onAbort);
31439
+ writeTaggedOutput("stdout", "", true);
31440
+ writeTaggedOutput("stderr", "", true);
30911
31441
  log.end();
30912
31442
  reject(error48);
30913
31443
  });
30914
31444
  child.once("exit", (code) => {
30915
31445
  signal.removeEventListener("abort", onAbort);
31446
+ writeTaggedOutput("stdout", "", true);
31447
+ writeTaggedOutput("stderr", "", true);
30916
31448
  log.end();
30917
31449
  let drainReason = null;
30918
31450
  for (const line of stdout.trim().split("\n").reverse()) {
@@ -30938,64 +31470,72 @@ ${result2.stderr}`)) {
30938
31470
  runInferenceHostServiceSupervisor = async (manifestPath, options = {}) => {
30939
31471
  const manifest = await readInferenceHostServiceManifest(manifestPath);
30940
31472
  if (!manifest) throw new Error("Inference-host service manifest is missing.");
30941
- const desiredPath = `${manifest.runtime_environment.VTX_INFERENCE_HOST_STATE_PATH}.service-desired.json`;
31473
+ const desiredPath = manifestPath.replace(/\.service\.json$/u, ".service-desired.json");
30942
31474
  const signal = options.signal ?? new AbortController().signal;
30943
31475
  const sleep4 = options.sleep ?? (async (milliseconds) => {
30944
31476
  await new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds));
30945
31477
  });
30946
31478
  const launch = options.runWorker ?? options.spawnChild ?? spawnServiceChild;
30947
- let failures = 0;
30948
- await appendServiceLog(manifest.log_path, "service_supervisor_started", { adapter: manifest.adapter });
30949
- while (!signal.aborted && await readDesiredAcrossAtomicReplacement(desiredPath)) {
30950
- try {
30951
- const workerController = new AbortController();
30952
- const forwardAbort = () => workerController.abort();
30953
- signal.addEventListener("abort", forwardAbort, { once: true });
30954
- let workerComplete = false;
30955
- let monitorError = null;
30956
- const desiredMonitor = (async () => {
30957
- while (!workerComplete && !workerController.signal.aborted) {
30958
- await sleep4(500);
30959
- if (!await readDesiredAcrossAtomicReplacement(desiredPath)) {
30960
- workerController.abort();
30961
- break;
30962
- }
30963
- }
30964
- })().catch((error48) => {
30965
- monitorError = error48;
30966
- workerController.abort();
30967
- });
30968
- let result2;
31479
+ await appendServiceLog(manifest.log_path, "service_supervisor_started", {
31480
+ adapter: "codex",
31481
+ instances: manifest.workers.map((worker) => worker.instance_name)
31482
+ });
31483
+ const superviseWorker = async (worker) => {
31484
+ let failures = 0;
31485
+ while (!signal.aborted && await readDesiredAcrossAtomicReplacement(desiredPath)) {
30969
31486
  try {
30970
- result2 = await launch(manifest, workerController.signal);
30971
- } finally {
30972
- workerComplete = true;
30973
- workerController.abort();
30974
- signal.removeEventListener("abort", forwardAbort);
30975
- await desiredMonitor;
30976
- }
30977
- if (monitorError) throw monitorError;
30978
- if (signal.aborted || !await readDesiredAcrossAtomicReplacement(desiredPath)) break;
30979
- failures = result2.uptimeMs >= 6e4 ? 0 : failures + 1;
30980
- const retryAfterMs = Math.min(1e3 * 2 ** Math.min(failures, 5), 3e4);
30981
- await appendServiceLog(manifest.log_path, "worker_exited", {
30982
- exit_code: result2.exitCode,
30983
- uptime_ms: result2.uptimeMs,
30984
- drain_reason: result2.drainReason ?? null,
30985
- retry_after_ms: retryAfterMs
30986
- });
30987
- await sleep4(retryAfterMs);
30988
- } catch (error48) {
30989
- if (signal.aborted) break;
30990
- failures += 1;
30991
- const retryAfterMs = Math.min(1e3 * 2 ** Math.min(failures, 5), 3e4);
30992
- await appendServiceLog(manifest.log_path, "worker_launch_failed", {
30993
- error: error48 instanceof Error ? error48.message : "unknown",
30994
- retry_after_ms: retryAfterMs
30995
- });
30996
- await sleep4(retryAfterMs);
31487
+ const workerController = new AbortController();
31488
+ const forwardAbort = () => workerController.abort();
31489
+ signal.addEventListener("abort", forwardAbort, { once: true });
31490
+ let workerComplete = false;
31491
+ let monitorError = null;
31492
+ const desiredMonitor = (async () => {
31493
+ while (!workerComplete && !workerController.signal.aborted) {
31494
+ await sleep4(500);
31495
+ if (!await readDesiredAcrossAtomicReplacement(desiredPath)) {
31496
+ workerController.abort();
31497
+ break;
31498
+ }
31499
+ }
31500
+ })().catch((error48) => {
31501
+ monitorError = error48;
31502
+ workerController.abort();
31503
+ });
31504
+ let result2;
31505
+ try {
31506
+ result2 = await launch(manifest, worker, workerController.signal);
31507
+ } finally {
31508
+ workerComplete = true;
31509
+ workerController.abort();
31510
+ signal.removeEventListener("abort", forwardAbort);
31511
+ await desiredMonitor;
31512
+ }
31513
+ if (monitorError) throw monitorError;
31514
+ if (signal.aborted || !await readDesiredAcrossAtomicReplacement(desiredPath)) break;
31515
+ failures = result2.uptimeMs >= 6e4 ? 0 : failures + 1;
31516
+ const retryAfterMs = Math.min(1e3 * 2 ** Math.min(failures, 5), 3e4);
31517
+ await appendServiceLog(manifest.log_path, "worker_exited", {
31518
+ instance_name: worker.instance_name,
31519
+ exit_code: result2.exitCode,
31520
+ uptime_ms: result2.uptimeMs,
31521
+ drain_reason: result2.drainReason ?? null,
31522
+ retry_after_ms: retryAfterMs
31523
+ });
31524
+ await sleep4(retryAfterMs);
31525
+ } catch (error48) {
31526
+ if (signal.aborted) break;
31527
+ failures += 1;
31528
+ const retryAfterMs = Math.min(1e3 * 2 ** Math.min(failures, 5), 3e4);
31529
+ await appendServiceLog(manifest.log_path, "worker_launch_failed", {
31530
+ instance_name: worker.instance_name,
31531
+ error: error48 instanceof Error ? error48.message : "unknown",
31532
+ retry_after_ms: retryAfterMs
31533
+ });
31534
+ await sleep4(retryAfterMs);
31535
+ }
30997
31536
  }
30998
- }
31537
+ };
31538
+ await Promise.all(manifest.workers.map(superviseWorker));
30999
31539
  await appendServiceLog(manifest.log_path, "service_supervisor_stopped");
31000
31540
  };
31001
31541
  }
@@ -31085,7 +31625,7 @@ async function runInferenceHostCli(argv2, env = process.env, dependencies = {})
31085
31625
  };
31086
31626
  }
31087
31627
  }
31088
- 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, parsePositiveInteger, parseInferenceHostArgs, defaultOpenBrowser, defaultRegisterLifecycleSignalHandlers, lifecycleCancellation, configuredCredentialStore, accountKeyForState, assertCredentialMatchesState2, revocationCheckpointForCredential, assertRevocationCheckpointMatchesLocalIdentity, fileExistsPrivately, inspectCodexAuthentication, clearLocalRuntimeArtifacts, assertDurableServiceUninstalled, assertLocalRuntimeArtifactsMayBeDiscarded, defaultRunForeground, login, codexLogin, codexLogout, localStatus, doctor, cleanupLogin, runHost, defaultReadStdin, parseAgentStdin, agentOperationId, agentSession, agentConnect, agentRun, agentNext, agentComplete, agentFail, withAgentCommandLock, serviceCommand, hasExplicitCredentialStoreConfiguration, commandUsesInstalledServiceCredentials, resolveInferenceHostCommandConfig;
31628
+ var INFERENCE_HOST_CLI_VERSION, runtimeReceiptPath, codexRecoveryPath, codexGuardianReceiptRoot, revocationCheckpointPath, foregroundHostLockPath, AGENT_HEARTBEAT_INTERVAL_MS, AGENT_HEARTBEAT_MAX_RETRY_DELAY_MS, retryableAgentHeartbeatError, assertRevocationCheckpoint, readRevocationCheckpoint, writeRevocationCheckpoint, clearRevocationCheckpoint, render, INFERENCE_HOST_HELP, parseHostConcurrency, parseInferenceHostArgs, defaultOpenBrowser, defaultRegisterLifecycleSignalHandlers, lifecycleCancellation, configuredCredentialStore, accountKeyForState, assertCredentialMatchesState2, revocationCheckpointForCredential, assertRevocationCheckpointMatchesLocalIdentity, fileExistsPrivately, inspectCodexAuthentication, clearLocalRuntimeArtifacts, assertDurableServiceUninstalled, assertLocalRuntimeArtifactsMayBeDiscarded, defaultRunForeground, login, codexLogin, codexLogout, localStatus, doctor, cleanupLogin, runHost, defaultReadStdin, parseAgentStdin, agentOperationId, agentSession, agentConnect, agentRun, agentNext, agentComplete, agentFail, withAgentCommandLock, serviceCommand, hasExplicitCredentialStoreConfiguration, commandUsesInstalledServiceCredentials, resolveInferenceHostCommandConfig;
31089
31629
  var init_cli = __esm({
31090
31630
  "lib/inference-host/cli.ts"() {
31091
31631
  "use strict";
@@ -31189,6 +31729,8 @@ Commands:
31189
31729
 
31190
31730
  Common options:
31191
31731
  --json Emit machine-readable JSON
31732
+ --instance NAME Target an isolated local subscription instance (default: default)
31733
+ --max-concurrency N Optional positive-integer Codex slot limit (default: unlimited)
31192
31734
  --help, -h Show this help
31193
31735
 
31194
31736
  If the OS credential store cannot retain the VTX grant, set
@@ -31196,23 +31738,29 @@ VTX_INFERENCE_HOST_CREDENTIAL_STORE=file before login to use the supported
31196
31738
  private-file fallback. See https://vtxmacro.com/insights#subscription-inference.
31197
31739
 
31198
31740
  Durable service:
31199
- vtx inference-host service install
31741
+ vtx inference-host login --instance codex-1
31742
+ vtx inference-host codex-login --instance codex-1
31743
+ vtx inference-host service install --instance codex-1
31744
+ vtx inference-host login --instance codex-2
31745
+ vtx inference-host codex-login --instance codex-2
31746
+ vtx inference-host service install --instance codex-2
31747
+ vtx inference-host service uninstall --instance codex-2
31200
31748
  vtx inference-host service <start|stop|status|logs|uninstall>
31201
31749
  `;
31202
- parsePositiveInteger = (raw, label) => {
31750
+ parseHostConcurrency = (raw, label) => {
31203
31751
  const value = Number(raw);
31204
- if (value !== 1) {
31205
- throw new Error(`${label} must be exactly 1 for the subscription-backed Codex host.`);
31752
+ if (!Number.isSafeInteger(value) || value < 1) {
31753
+ throw new Error(`${label} must be a positive safe integer.`);
31206
31754
  }
31207
31755
  return value;
31208
31756
  };
31209
31757
  parseInferenceHostArgs = (argv2, env) => {
31210
31758
  let json2 = String(env.VTX_OUTPUT_JSON || "").trim().toLowerCase() === "true";
31211
31759
  let once = false;
31212
- let maxConcurrency = parsePositiveInteger(
31213
- String(env.VTX_INFERENCE_HOST_MAX_CONCURRENCY || "1"),
31214
- "VTX_INFERENCE_HOST_MAX_CONCURRENCY"
31215
- );
31760
+ const configuredConcurrency = String(env.VTX_INFERENCE_HOST_MAX_CONCURRENCY || "").trim();
31761
+ let maxConcurrency = configuredConcurrency ? parseHostConcurrency(configuredConcurrency, "VTX_INFERENCE_HOST_MAX_CONCURRENCY") : null;
31762
+ let instanceName = String(env.VTX_INFERENCE_HOST_INSTANCE || "default").trim();
31763
+ let instanceExplicit = Boolean(String(env.VTX_INFERENCE_HOST_INSTANCE || "").trim());
31216
31764
  let displayName = String(env.VTX_INFERENCE_HOST_DISPLAY_NAME || "").trim() || "Codex subscription host";
31217
31765
  let displayNameExplicit = Boolean(String(env.VTX_INFERENCE_HOST_DISPLAY_NAME || "").trim());
31218
31766
  let adapter = null;
@@ -31236,7 +31784,7 @@ Durable service:
31236
31784
  if (argument === "--max-concurrency") {
31237
31785
  const raw = argv2[index + 1];
31238
31786
  if (!raw) throw new Error("--max-concurrency requires a value.");
31239
- maxConcurrency = parsePositiveInteger(raw, "--max-concurrency");
31787
+ maxConcurrency = parseHostConcurrency(raw, "--max-concurrency");
31240
31788
  index += 1;
31241
31789
  continue;
31242
31790
  }
@@ -31248,7 +31796,7 @@ Durable service:
31248
31796
  index += 1;
31249
31797
  continue;
31250
31798
  }
31251
- if (["--adapter", "--model", "--model-label", "--effort", "--wait-seconds", "--lines", "--service-manifest"].includes(argument)) {
31799
+ if (["--adapter", "--model", "--model-label", "--effort", "--wait-seconds", "--lines", "--service-manifest", "--instance"].includes(argument)) {
31252
31800
  const raw = argv2[index + 1];
31253
31801
  if (!raw?.trim()) throw new Error(`${argument} requires a value.`);
31254
31802
  if (argument === "--adapter") adapter = raw.trim();
@@ -31268,6 +31816,10 @@ Durable service:
31268
31816
  }
31269
31817
  }
31270
31818
  if (argument === "--service-manifest") serviceManifestPath = resolve5(raw.trim());
31819
+ if (argument === "--instance") {
31820
+ instanceName = raw.trim();
31821
+ instanceExplicit = true;
31822
+ }
31271
31823
  index += 1;
31272
31824
  continue;
31273
31825
  }
@@ -31295,6 +31847,8 @@ Durable service:
31295
31847
  json: json2,
31296
31848
  once,
31297
31849
  maxConcurrency,
31850
+ instanceName,
31851
+ instanceExplicit,
31298
31852
  displayName,
31299
31853
  adapter,
31300
31854
  modelId,
@@ -31406,9 +31960,10 @@ Durable service:
31406
31960
  await clearInferenceHostLocalState(config2.statePath);
31407
31961
  };
31408
31962
  assertDurableServiceUninstalled = async (config2) => {
31409
- if (await readInferenceHostServiceManifest(`${config2.statePath}.service.json`)) {
31963
+ const manifest = await readInferenceHostServiceManifest(`${config2.supervisorStatePath}.service.json`);
31964
+ if (manifest?.workers.some((worker) => worker.instance_name === config2.instanceName)) {
31410
31965
  throw new Error(
31411
- "Uninstall the durable inference-host service before logout, revoke, or Codex logout."
31966
+ `Uninstall the durable inference-host service before changing credentials for instance ${config2.instanceName}.`
31412
31967
  );
31413
31968
  }
31414
31969
  };
@@ -31459,6 +32014,7 @@ Durable service:
31459
32014
  ...dependencies,
31460
32015
  acquireProcessLock: async () => borrowedProcessLock
31461
32016
  }, {
32017
+ instanceName: options.config.instanceName,
31462
32018
  displayName: options.displayName,
31463
32019
  authenticatedAccountEmail: options.authenticatedAccountEmail,
31464
32020
  authenticatedAccountPlan: options.authenticatedAccountPlan,
@@ -31468,6 +32024,7 @@ Durable service:
31468
32024
  adapterRuntimeVersion: INFERENCE_HOST_CLI_VERSION,
31469
32025
  maxConcurrency: options.maxConcurrency,
31470
32026
  once: options.once,
32027
+ emitDiagnosticEvent: options.emitDiagnosticEvent,
31471
32028
  signal: options.signal
31472
32029
  }).run();
31473
32030
  };
@@ -31969,6 +32526,10 @@ Waiting for approval...
31969
32526
  once: parsed.once,
31970
32527
  env,
31971
32528
  processLock,
32529
+ emitDiagnosticEvent: parsed.json ? (event) => (dependencies.emitStdout ?? ((text) => {
32530
+ process.stdout.write(text);
32531
+ }))(`${JSON.stringify(event)}
32532
+ `) : void 0,
31972
32533
  signal: cancellation.signal
31973
32534
  });
31974
32535
  return {
@@ -32436,55 +32997,19 @@ Waiting for approval...
32436
32997
  if (!parsed.serviceManifestPath) throw new Error("Internal service manifest path is required.");
32437
32998
  const serviceManifest = await readInferenceHostServiceManifest(parsed.serviceManifestPath);
32438
32999
  if (!serviceManifest) throw new Error("Inference-host service manifest is missing.");
32439
- const serviceConfig = resolveInferenceHostConfig({
33000
+ const supervisorConfig = resolveInferenceHostConfig({
32440
33001
  ...env,
32441
- ...serviceManifest.runtime_environment
33002
+ ...serviceManifest.workers[0].runtime_environment
32442
33003
  });
32443
33004
  const serviceLock = await acquireInferenceHostProcessLock(
32444
- `${serviceConfig.processLockPath}.service`
33005
+ `${supervisorConfig.supervisorProcessLockPath}.service`
32445
33006
  );
32446
33007
  const cancellation = lifecycleCancellation(dependencies);
32447
33008
  try {
32448
33009
  await (dependencies.runServiceSupervisor ?? runInferenceHostServiceSupervisor)(
32449
33010
  parsed.serviceManifestPath,
32450
33011
  {
32451
- signal: cancellation.signal,
32452
- runWorker: async (manifest, signal) => {
32453
- const startedAt = Date.now();
32454
- const serviceEnv = { ...env, ...manifest.runtime_environment };
32455
- const serviceConfig2 = resolveInferenceHostConfig(serviceEnv);
32456
- const unregister = (abort) => {
32457
- const onAbort = () => abort("SIGTERM");
32458
- signal.addEventListener("abort", onAbort, { once: true });
32459
- return () => signal.removeEventListener("abort", onAbort);
32460
- };
32461
- const result2 = await runHost(serviceConfig2, {
32462
- ...parsed,
32463
- command: "run",
32464
- serviceAction: null,
32465
- serviceManifestPath: null,
32466
- displayName: manifest.display_name,
32467
- once: false
32468
- }, serviceEnv, {
32469
- ...dependencies,
32470
- registerLifecycleSignalHandlers: unregister
32471
- }, warnings);
32472
- let drainReason = null;
32473
- try {
32474
- const summary = JSON.parse(result2.stdout);
32475
- if (summary && typeof summary === "object" && !Array.isArray(summary) && typeof summary.drain_reason === "string" && /^[a-z0-9_]{1,96}$/u.test(
32476
- summary.drain_reason
32477
- )) {
32478
- drainReason = summary.drain_reason;
32479
- }
32480
- } catch {
32481
- }
32482
- return {
32483
- exitCode: result2.exitCode,
32484
- uptimeMs: Date.now() - startedAt,
32485
- drainReason
32486
- };
32487
- }
33012
+ signal: cancellation.signal
32488
33013
  }
32489
33014
  );
32490
33015
  return { exitCode: 0, stdout: "", stderr: "" };
@@ -32518,12 +33043,18 @@ Waiting for approval...
32518
33043
  throw new Error("Run vtx inference-host codex-login before installing the automated Codex service.");
32519
33044
  }
32520
33045
  const binary = await (dependencies.resolveBinary ?? resolvePinnedCodexBinary)(env);
32521
- await (dependencies.preflightCodex ?? preflightCodexSubscription)({
33046
+ const preflight = await (dependencies.preflightCodex ?? preflightCodexSubscription)({
32522
33047
  binary,
32523
33048
  codexHome: config2.codexHomePath,
32524
33049
  deadlineAtMs: Date.now() + 3e4
32525
33050
  });
32526
- const status = await manager.install({ adapter: "codex", displayName: parsed.displayName });
33051
+ const status = await manager.install({
33052
+ adapter: "codex",
33053
+ displayName: parsed.displayName,
33054
+ maxConcurrency: parsed.maxConcurrency,
33055
+ authenticatedAccountEmail: preflight?.authenticated_account_email ?? null,
33056
+ authenticatedAccountPlan: preflight?.authenticated_account_plan ?? null
33057
+ });
32527
33058
  return {
32528
33059
  exitCode: 0,
32529
33060
  stdout: render({ status: "service_installed", ...status }, parsed.json),
@@ -32535,7 +33066,14 @@ Waiting for approval...
32535
33066
  throw new Error("Service adapter and model options are accepted only by service install.");
32536
33067
  }
32537
33068
  if (action === "logs") {
32538
- return { exitCode: 0, stdout: await manager.logs(parsed.lines), stderr: "" };
33069
+ return {
33070
+ exitCode: 0,
33071
+ stdout: await manager.logs(
33072
+ parsed.lines,
33073
+ parsed.instanceExplicit ? config2.instanceName : void 0
33074
+ ),
33075
+ stderr: ""
33076
+ };
32539
33077
  }
32540
33078
  if (action === "start") {
32541
33079
  return { exitCode: 0, stdout: render({ status: "service_started", ...await manager.start() }, parsed.json), stderr: "" };
@@ -32549,7 +33087,8 @@ Waiting for approval...
32549
33087
  return { exitCode: lifecycleMatches ? 0 : 1, stdout: render(status, parsed.json), stderr: "" };
32550
33088
  }
32551
33089
  if (action === "uninstall") {
32552
- return { exitCode: 0, stdout: render({ status: "service_uninstalled", ...await manager.uninstall() }, parsed.json), stderr: "" };
33090
+ const status = parsed.instanceExplicit ? await manager.uninstallInstance(config2.instanceName) : await manager.uninstall();
33091
+ return { exitCode: 0, stdout: render({ status: "service_uninstalled", ...status }, parsed.json), stderr: "" };
32553
33092
  }
32554
33093
  throw new Error("Usage: vtx inference-host service <install|start|stop|status|logs|uninstall>.");
32555
33094
  };
@@ -32558,16 +33097,20 @@ Waiting for approval...
32558
33097
  );
32559
33098
  commandUsesInstalledServiceCredentials = (parsed) => parsed.command === "status" || parsed.command === "doctor" || parsed.command === "service" && parsed.serviceAction === "install";
32560
33099
  resolveInferenceHostCommandConfig = async (parsed, env) => {
32561
- const baseConfig = resolveInferenceHostConfig(env);
33100
+ const selectedEnv = {
33101
+ ...env,
33102
+ VTX_INFERENCE_HOST_INSTANCE: parsed.instanceName
33103
+ };
33104
+ const baseConfig = resolveInferenceHostConfig(selectedEnv);
32562
33105
  if (!commandUsesInstalledServiceCredentials(parsed) || hasExplicitCredentialStoreConfiguration(env)) {
32563
33106
  return baseConfig;
32564
33107
  }
32565
- const manifestPath = `${baseConfig.statePath}.service.json`;
33108
+ const manifestPath = `${baseConfig.supervisorStatePath}.service.json`;
32566
33109
  const manifest = await readInferenceHostServiceManifest(manifestPath);
32567
33110
  if (!manifest) return baseConfig;
32568
33111
  const installedConfig = resolveInferenceHostConfig({
32569
- ...env,
32570
- ...manifest.runtime_environment
33112
+ ...selectedEnv,
33113
+ ...manifest.workers.find((worker) => worker.instance_name === baseConfig.instanceName)?.runtime_environment
32571
33114
  });
32572
33115
  if (resolve5(installedConfig.statePath) !== resolve5(baseConfig.statePath)) {
32573
33116
  throw new Error(
@@ -49994,7 +50537,11 @@ function buildHeadlessExchangeConfig(input) {
49994
50537
  user_fills_ttl_seconds: readCacheNumberField("user_fills_ttl_seconds"),
49995
50538
  stale_if_429_max_age_seconds: readCacheNumberField("stale_if_429_max_age_seconds")
49996
50539
  },
49997
- client_runtime_hyperliquid_case_sensitive_symbols: Array.isArray(input.client_runtime_hyperliquid_case_sensitive_symbols) ? input.client_runtime_hyperliquid_case_sensitive_symbols : null
50540
+ client_runtime_hyperliquid_case_sensitive_symbols: Array.isArray(input.client_runtime_hyperliquid_case_sensitive_symbols) ? input.client_runtime_hyperliquid_case_sensitive_symbols : null,
50541
+ client_runtime_hyperliquid_perp_dexs: input.client_runtime_hyperliquid_perp_dexs ? {
50542
+ enabled: input.client_runtime_hyperliquid_perp_dexs.enabled === true,
50543
+ allowed_dexes: Array.isArray(input.client_runtime_hyperliquid_perp_dexs.allowed_dexes) ? input.client_runtime_hyperliquid_perp_dexs.allowed_dexes.map((dex) => String(dex).trim()).filter(Boolean) : []
50544
+ } : void 0
49998
50545
  };
49999
50546
  }
50000
50547
  async function fetchLocalHyperliquidSnapshot(input) {
@@ -50010,7 +50557,7 @@ async function fetchLocalHyperliquidSnapshot(input) {
50010
50557
  symbol: input.symbol,
50011
50558
  tickerPrice: 0,
50012
50559
  leverage,
50013
- aggregatePerpDexs: false
50560
+ aggregatePerpDexs: true
50014
50561
  });
50015
50562
  const clientExchangeSnapshot = buildClientExchangeSnapshot({
50016
50563
  walletAddress,
@@ -51041,7 +51588,8 @@ var INFERENCE_HOST_VALUE_OPTIONS = /* @__PURE__ */ new Set([
51041
51588
  "--effort",
51042
51589
  "--wait-seconds",
51043
51590
  "--lines",
51044
- "--service-manifest"
51591
+ "--service-manifest",
51592
+ "--instance"
51045
51593
  ]);
51046
51594
  var isInferenceHostCliInvocation = (argv2) => {
51047
51595
  for (let index = 0; index < argv2.length; index += 1) {