@vtxmacro/cli 2026.8.29 → 2026.8.31

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 +16 -0
  2. package/bin/vtx.js +438 -205
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -71,6 +71,22 @@ vtx inference-host codex-logout
71
71
  vtx inference-host revoke
72
72
  ```
73
73
 
74
+ To connect another Codex subscription to the same VTX account, repeat the
75
+ login and service-install commands with a stable local name such as
76
+ `--instance codex-2`. Each named instance has an isolated VTX grant, Codex
77
+ home, credential file, runtime state, recovery state, and worker; one per-user
78
+ OS supervisor runs all installed instances. Use `service status --json` to
79
+ inspect the installed workers. The installer rejects duplicate authenticated
80
+ ChatGPT emails and fails closed when it cannot prove multiple subscriptions are
81
+ distinct. Use `service uninstall --instance <name>` to remove one worker;
82
+ unqualified `service uninstall` removes the whole supervisor. The legacy
83
+ unqualified host commands continue to target `default`.
84
+
85
+ Automated hosts use three concurrent slots per subscription by default. Set a
86
+ bounded value from 1 through 8 with `--max-concurrency`. Slots share the live
87
+ subscription rate-limit gate and cooldown; this does not guarantee capacity
88
+ beyond the authenticated account's current entitlement.
89
+
74
90
  This durable host uses a separate least-privilege `insights:inference`
75
91
  OAuth grant. It also keeps its ChatGPT subscription login in a dedicated private
76
92
  Codex home. Neither credential is copied from or widens the ordinary CLI or Codex
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.29",
41
+ package_version: "2026.8.31",
42
42
  codex_package_name: "@openai/codex",
43
43
  codex_version: "0.147.0",
44
44
  platforms: {
@@ -16084,13 +16084,26 @@ import { spawn } from "node:child_process";
16084
16084
  import { constants } from "node:fs";
16085
16085
  import { lstat, mkdir, open, readFile, realpath, rename, rm } from "node:fs/promises";
16086
16086
  import { homedir } from "node:os";
16087
- import { dirname, join, relative, resolve } from "node:path";
16087
+ import { dirname, isAbsolute, join, relative, resolve } from "node:path";
16088
16088
  function inferenceBaseDir(env) {
16089
16089
  const configured = String(env.VTX_INFERENCE_HOST_HOME || "").trim();
16090
16090
  return configured || join(homedir(), ".vtx", "inference-host");
16091
16091
  }
16092
+ function normalizeInferenceHostInstanceName(raw) {
16093
+ const value = raw.trim().toLowerCase();
16094
+ if (!SAFE_INFERENCE_HOST_INSTANCE.test(value)) {
16095
+ throw new Error(
16096
+ "Inference host instance must be 1-48 lowercase letters, numbers, or hyphens, and cannot start or end with a hyphen."
16097
+ );
16098
+ }
16099
+ return value;
16100
+ }
16092
16101
  function resolveInferenceHostConfig(env = process.env) {
16093
16102
  const baseDir = inferenceBaseDir(env);
16103
+ const instanceName = normalizeInferenceHostInstanceName(
16104
+ String(env.VTX_INFERENCE_HOST_INSTANCE || DEFAULT_INFERENCE_HOST_INSTANCE)
16105
+ );
16106
+ const instanceDir = instanceName === DEFAULT_INFERENCE_HOST_INSTANCE ? baseDir : join(baseDir, "instances", instanceName);
16094
16107
  const rawMode = String(env.VTX_INFERENCE_HOST_CREDENTIAL_STORE || "os").trim().toLowerCase();
16095
16108
  if (rawMode !== "os" && rawMode !== "file") {
16096
16109
  throw new Error("VTX_INFERENCE_HOST_CREDENTIAL_STORE must be os or file.");
@@ -16103,15 +16116,36 @@ function resolveInferenceHostConfig(env = process.env) {
16103
16116
  "VTX_INFERENCE_HOST_CREDENTIAL_FILE requires explicit file credential-store mode."
16104
16117
  );
16105
16118
  }
16106
- const credentialFilePath = rawMode === "file" ? explicitCredentialPath || join(baseDir, "credentials.json") : null;
16119
+ const credentialFilePath = rawMode === "file" ? requireNamedInstancePath(
16120
+ instanceName,
16121
+ instanceDir,
16122
+ explicitCredentialPath,
16123
+ "VTX_INFERENCE_HOST_CREDENTIAL_FILE"
16124
+ ) || join(instanceDir, "credentials.json") : null;
16125
+ const explicitStatePath = requireNamedInstancePath(
16126
+ instanceName,
16127
+ instanceDir,
16128
+ String(env.VTX_INFERENCE_HOST_STATE_PATH || "").trim(),
16129
+ "VTX_INFERENCE_HOST_STATE_PATH"
16130
+ );
16131
+ const explicitLockPath = requireNamedInstancePath(
16132
+ instanceName,
16133
+ instanceDir,
16134
+ String(env.VTX_INFERENCE_HOST_LOCK_PATH || "").trim(),
16135
+ "VTX_INFERENCE_HOST_LOCK_PATH"
16136
+ );
16107
16137
  return {
16108
16138
  apiUrl: String(env.VTX_API_URL || "https://api.vtxmacro.com").replace(/\/+$/, ""),
16139
+ baseDir,
16140
+ instanceName,
16109
16141
  credentialStoreMode: rawMode,
16110
16142
  credentialFilePath,
16111
16143
  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")
16144
+ codexHomePath: join(instanceDir, "codex-home"),
16145
+ statePath: explicitStatePath || join(instanceDir, "state.json"),
16146
+ processLockPath: explicitLockPath || join(instanceDir, "host.lock"),
16147
+ supervisorStatePath: join(baseDir, "state.json"),
16148
+ supervisorProcessLockPath: join(baseDir, "host.lock")
16115
16149
  };
16116
16150
  }
16117
16151
  function assertLocalState(value) {
@@ -16488,12 +16522,14 @@ async function acquireInferenceHostProcessLock(path, dependencies = {}) {
16488
16522
  }
16489
16523
  };
16490
16524
  }
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;
16525
+ var INFERENCE_CREDENTIAL_NAMESPACE, MAX_INFERENCE_PRIVATE_FILE_BYTES, DEFAULT_CODEX_HOST_CONCURRENCY, MAX_CODEX_HOST_CONCURRENCY, 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
16526
  var init_config = __esm({
16493
16527
  "lib/inference-host/config.ts"() {
16494
16528
  "use strict";
16495
16529
  INFERENCE_CREDENTIAL_NAMESPACE = "vtxmacro-insights-inference";
16496
16530
  MAX_INFERENCE_PRIVATE_FILE_BYTES = 16 * 1024 * 1024;
16531
+ DEFAULT_CODEX_HOST_CONCURRENCY = 3;
16532
+ MAX_CODEX_HOST_CONCURRENCY = 8;
16497
16533
  WINDOWS_PRIVATE_ACL_SCRIPT = `$ErrorActionPreference='Stop'
16498
16534
  $path=[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($env:VTX_PRIVATE_PATH_B64))
16499
16535
  $kind=$env:VTX_PRIVATE_PATH_KIND
@@ -16845,6 +16881,16 @@ if(-not $userFull) { throw 'private ACL does not grant the current user full con
16845
16881
  };
16846
16882
  processLockObservationCache = /* @__PURE__ */ new Map();
16847
16883
  inferenceProcessIdentitiesMatch = (stored, observed) => stored === observed;
16884
+ DEFAULT_INFERENCE_HOST_INSTANCE = "default";
16885
+ SAFE_INFERENCE_HOST_INSTANCE = /^[a-z0-9](?:[a-z0-9-]{0,46}[a-z0-9])?$/u;
16886
+ requireNamedInstancePath = (instanceName, instanceDir, rawPath, label) => {
16887
+ if (!rawPath || instanceName === DEFAULT_INFERENCE_HOST_INSTANCE) return rawPath;
16888
+ const relativePath = relative(resolve(instanceDir), resolve(rawPath));
16889
+ if (relativePath === ".." || relativePath.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || isAbsolute(relativePath)) {
16890
+ throw new Error(`${label} for a named instance must stay inside its isolated instance directory.`);
16891
+ }
16892
+ return rawPath;
16893
+ };
16848
16894
  credentialStoreIdentity = (mode, filePath, env) => {
16849
16895
  if (mode === "file") {
16850
16896
  if (!filePath) throw new Error("Inference credential fallback path is unavailable.");
@@ -19029,7 +19075,7 @@ var init_agent_state = __esm({
19029
19075
  import { spawn as spawn3 } from "node:child_process";
19030
19076
  import { chmod, lstat as lstat2, open as open2, readFile as readFile2, rename as rename2, rm as rm2, writeFile } from "node:fs/promises";
19031
19077
  import { tmpdir } from "node:os";
19032
- import { dirname as dirname2, isAbsolute, join as join2, resolve as resolve2, sep } from "node:path";
19078
+ import { dirname as dirname2, isAbsolute as isAbsolute2, join as join2, resolve as resolve2, sep } from "node:path";
19033
19079
  import { createInterface } from "node:readline";
19034
19080
  async function loginCodexSubscription(options) {
19035
19081
  const session = await CodexAppServerSession.start(options);
@@ -19101,12 +19147,13 @@ async function logoutCodexSubscription(options) {
19101
19147
  await session.close();
19102
19148
  }
19103
19149
  }
19104
- var CODEX_INFERENCE_PERMISSION_PROFILE, CODEX_REASONING_SUMMARY_MAX_UTF8_BYTES, CODEX_ACCOUNT_PLAN_TYPES, CodexAppServerError, objectOrNull, finiteToken, tokenUsageFromBreakdown, usageFromNotification, validPlanType, nonnegativeSafeIntegerOrNull, nonnegativeFiniteNumberOrNull, parseRateLimitWindow, RATE_LIMIT_REACHED_TYPES, DEFAULT_CODEX_QUOTA_COOLDOWN_MS, MAX_CODEX_QUOTA_COOLDOWN_MS, CODEX_TRANSIENT_RATE_LIMIT_COOLDOWN_MS, parseRateLimitSnapshot, codexRateLimitRetryAtMs, codexAccountRateLimitReached, forbiddenMethod, forbiddenTerminalItem, classifyCodexTurnFailure, scrubbedCodexEnvironment, killWindowsProcessTree, appServerArgs, GUARDIAN_SCRIPT, WINDOWS_RECEIPT_REPLACE_ERROR_CODES, replaceCodexGuardianReceiptFile, writeCodexGuardianSpawnIntent, parseGuardianReceipt, readCodexGuardianReceipt, waitForCodexGuardianState, defaultSpawn, CodexAppServerSession;
19150
+ var CODEX_INFERENCE_PERMISSION_PROFILE, CODEX_REASONING_CONTENT_MAX_UTF8_BYTES, CODEX_REASONING_SUMMARY_MAX_UTF8_BYTES, CODEX_ACCOUNT_PLAN_TYPES, CodexAppServerError, objectOrNull, finiteToken, tokenUsageFromBreakdown, usageFromNotification, validPlanType, nonnegativeSafeIntegerOrNull, nonnegativeFiniteNumberOrNull, parseRateLimitWindow, RATE_LIMIT_REACHED_TYPES, DEFAULT_CODEX_QUOTA_COOLDOWN_MS, MAX_CODEX_QUOTA_COOLDOWN_MS, CODEX_TRANSIENT_RATE_LIMIT_COOLDOWN_MS, parseRateLimitSnapshot, codexRateLimitRetryAtMs, codexAccountRateLimitReached, forbiddenMethod, forbiddenTerminalItem, classifyCodexTurnFailure, scrubbedCodexEnvironment, killWindowsProcessTree, appServerArgs, GUARDIAN_SCRIPT, WINDOWS_RECEIPT_REPLACE_ERROR_CODES, replaceCodexGuardianReceiptFile, writeCodexGuardianSpawnIntent, parseGuardianReceipt, readCodexGuardianReceipt, waitForCodexGuardianState, defaultSpawn, CodexAppServerSession;
19105
19151
  var init_codex_app_server = __esm({
19106
19152
  "lib/inference-host/codex-app-server.ts"() {
19107
19153
  "use strict";
19108
19154
  init_config();
19109
19155
  CODEX_INFERENCE_PERMISSION_PROFILE = "vtx_inference_readonly";
19156
+ CODEX_REASONING_CONTENT_MAX_UTF8_BYTES = 32768;
19110
19157
  CODEX_REASONING_SUMMARY_MAX_UTF8_BYTES = 32768;
19111
19158
  CODEX_ACCOUNT_PLAN_TYPES = [
19112
19159
  "free",
@@ -19725,7 +19772,10 @@ child.once('close', async () => {
19725
19772
  title: "VTX External Inference Host",
19726
19773
  version: "external_inference.v1"
19727
19774
  },
19728
- capabilities: { experimentalApi: true }
19775
+ capabilities: {
19776
+ experimentalApi: true,
19777
+ optOutNotificationMethods: ["thread/started"]
19778
+ }
19729
19779
  }, {
19730
19780
  timeoutMs: Math.max(1, options.deadlineAtMs - Date.now()),
19731
19781
  signal: options.signal
@@ -20259,8 +20309,8 @@ child.once('close', async () => {
20259
20309
  developerInstructions: options.systemPrompt,
20260
20310
  config: {
20261
20311
  model_reasoning_effort: options.requestedReasoningEffort,
20262
- model_reasoning_summary: "detailed",
20263
- show_raw_agent_reasoning: false,
20312
+ model_reasoning_summary: "none",
20313
+ show_raw_agent_reasoning: true,
20264
20314
  hide_agent_reasoning: false,
20265
20315
  web_search: "disabled",
20266
20316
  features: {
@@ -20296,7 +20346,7 @@ child.once('close', async () => {
20296
20346
  const resolvedHome = resolve2(this.codexHome);
20297
20347
  const resolvedThreadPath = resolve2(threadPath);
20298
20348
  const resolvedSessionsRoot = join2(resolvedHome, "sessions");
20299
- 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) {
20349
+ 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) {
20300
20350
  throw new CodexAppServerError({
20301
20351
  message: "Codex thread isolation or effective settings were invalid.",
20302
20352
  category: "adapter",
@@ -20332,7 +20382,7 @@ child.once('close', async () => {
20332
20382
  let terminal = null;
20333
20383
  let completedReasoningObserved = false;
20334
20384
  let completedReasoningSequence = 0;
20335
- const completedReasoningSummaries = /* @__PURE__ */ new Map();
20385
+ const completedReasoningContent = /* @__PURE__ */ new Map();
20336
20386
  let terminalResolve;
20337
20387
  let terminalReject;
20338
20388
  const terminalPromise = new Promise((resolve6, reject) => {
@@ -20352,6 +20402,19 @@ child.once('close', async () => {
20352
20402
  });
20353
20403
  const startedAt = Date.now();
20354
20404
  const unsubscribe = this.subscribe(({ method, params }) => {
20405
+ if (method === "thread/started") {
20406
+ const startedThread = objectOrNull(params.thread);
20407
+ if (!String(startedThread?.id || "")) {
20408
+ terminalReject(new CodexAppServerError({
20409
+ message: "Codex returned an invalid thread lifecycle notification.",
20410
+ category: "adapter",
20411
+ code: "invalid_thread_started_notification",
20412
+ retryable: false,
20413
+ dispatchOutcome: requestWritten ? turnId ? "confirmed_dispatched" : "outcome_unknown" : "not_dispatched"
20414
+ }));
20415
+ }
20416
+ return;
20417
+ }
20355
20418
  const eventThreadId = String(params.threadId || "");
20356
20419
  const scopedMethod = method.startsWith("thread/") || method.startsWith("turn/") || method.startsWith("item/") || method.startsWith("rawResponse") || method === "model/rerouted";
20357
20420
  if (scopedMethod && !eventThreadId) {
@@ -20400,7 +20463,7 @@ child.once('close', async () => {
20400
20463
  if (method === "thread/settings/updated") {
20401
20464
  const settings = objectOrNull(params.threadSettings);
20402
20465
  const permission = objectOrNull(settings?.activePermissionProfile);
20403
- if (!settings || settings.model !== request.requestedModel || settings.modelProvider !== "openai" || settings.effort !== request.requestedReasoningEffort || settings.summary !== "detailed" || String(settings.cwd || "") !== request.workspacePath || settings.approvalPolicy !== "never" || permission?.id !== CODEX_INFERENCE_PERMISSION_PROFILE || permission.extends !== null) {
20466
+ if (!settings || settings.model !== request.requestedModel || settings.modelProvider !== "openai" || settings.effort !== request.requestedReasoningEffort || settings.summary !== "none" || String(settings.cwd || "") !== request.workspacePath || settings.approvalPolicy !== "never" || permission?.id !== CODEX_INFERENCE_PERMISSION_PROFILE || permission.extends !== null) {
20404
20467
  terminalReject(new CodexAppServerError({
20405
20468
  message: "Codex effective turn settings changed after dispatch.",
20406
20469
  category: "adapter",
@@ -20456,9 +20519,9 @@ child.once('close', async () => {
20456
20519
  completedReasoningObserved = true;
20457
20520
  const itemId = String(item.id || params.itemId || "").trim();
20458
20521
  const itemKey = itemId ? `id:${itemId}` : `sequence:${completedReasoningSequence++}`;
20459
- completedReasoningSummaries.set(
20522
+ completedReasoningContent.set(
20460
20523
  itemKey,
20461
- Array.isArray(item.summary) ? item.summary : []
20524
+ Array.isArray(item.content) ? item.content : []
20462
20525
  );
20463
20526
  }
20464
20527
  }
@@ -20500,7 +20563,7 @@ child.once('close', async () => {
20500
20563
  model: request.requestedModel,
20501
20564
  serviceTier: "default",
20502
20565
  effort: request.requestedReasoningEffort,
20503
- summary: "detailed",
20566
+ summary: "none",
20504
20567
  outputSchema: request.outputSchema
20505
20568
  }, {
20506
20569
  timeoutMs: remaining(),
@@ -20689,10 +20752,10 @@ child.once('close', async () => {
20689
20752
  const fallbackMessages = items.filter((item) => item?.type === "agentMessage" && item.phase == null);
20690
20753
  const answerItem = finalMessages.at(-1) ?? fallbackMessages.at(-1);
20691
20754
  const text = typeof answerItem?.text === "string" ? answerItem.text : "";
20692
- const reasoningSummarySections = completedReasoningObserved ? [...completedReasoningSummaries.values()] : items.flatMap((item) => item?.type === "reasoning" && Array.isArray(item.summary) ? [item.summary] : []);
20693
- const reasoningSummaryParts = reasoningSummarySections.flatMap((summary) => summary.filter((part) => typeof part === "string" && part.trim().length > 0));
20694
- const reasoningSummaryCandidate = reasoningSummaryParts.length > 0 ? reasoningSummaryParts.join("\n\n") : null;
20695
- const reasoningSummary = reasoningSummaryCandidate !== null && Buffer.byteLength(reasoningSummaryCandidate, "utf8") <= CODEX_REASONING_SUMMARY_MAX_UTF8_BYTES ? reasoningSummaryCandidate : null;
20755
+ const reasoningContentSections = completedReasoningObserved ? [...completedReasoningContent.values()] : items.flatMap((item) => item?.type === "reasoning" && Array.isArray(item.content) ? [item.content] : []);
20756
+ const reasoningContentParts = reasoningContentSections.flatMap((content) => content.filter((part) => typeof part === "string" && part.trim().length > 0));
20757
+ const reasoningContentCandidate = reasoningContentParts.length > 0 ? reasoningContentParts.join("\n\n") : null;
20758
+ const reasoningContent = reasoningContentCandidate !== null && Buffer.byteLength(reasoningContentCandidate, "utf8") <= CODEX_REASONING_CONTENT_MAX_UTF8_BYTES ? reasoningContentCandidate : null;
20696
20759
  const usage = threadUsage ?? rawResponseUsage;
20697
20760
  const responseIdentity = adapterResponseId ?? turnId;
20698
20761
  if (!text || !usage || !responseIdentity) {
@@ -20706,7 +20769,8 @@ child.once('close', async () => {
20706
20769
  }
20707
20770
  return {
20708
20771
  text,
20709
- reasoningSummary,
20772
+ reasoningContent,
20773
+ reasoningSummary: null,
20710
20774
  requestedModel: request.requestedModel,
20711
20775
  effectiveModel: request.thread.effectiveModel,
20712
20776
  requestedReasoningEffort: request.requestedReasoningEffort,
@@ -20990,7 +21054,7 @@ import {
20990
21054
  } from "node:fs/promises";
20991
21055
  import { randomBytes as randomBytes3 } from "node:crypto";
20992
21056
  import { tmpdir as tmpdir2 } from "node:os";
20993
- import { isAbsolute as isAbsolute2, join as join4, relative as relative2, resolve as resolve3, sep as sep2 } from "node:path";
21057
+ import { isAbsolute as isAbsolute3, join as join4, relative as relative2, resolve as resolve3, sep as sep2 } from "node:path";
20994
21058
  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;
20995
21059
  var init_codex_adapter = __esm({
20996
21060
  "lib/inference-host/codex-adapter.ts"() {
@@ -21013,6 +21077,7 @@ var init_codex_adapter = __esm({
21013
21077
  });
21014
21078
  turnResultReceiptSchema = external_exports.strictObject({
21015
21079
  text: external_exports.string().min(1).max(MAX_PROMPT_BYTES2),
21080
+ reasoningContent: external_exports.string().min(1).max(CODEX_REASONING_CONTENT_MAX_UTF8_BYTES).nullable().optional().transform((value) => value ?? null),
21016
21081
  reasoningSummary: external_exports.string().min(1).max(CODEX_REASONING_SUMMARY_MAX_UTF8_BYTES).nullable().optional().transform((value) => value ?? null),
21017
21082
  requestedModel: external_exports.string().regex(CODEX_MODEL_NAME_PATTERN),
21018
21083
  effectiveModel: external_exports.string().regex(CODEX_MODEL_NAME_PATTERN),
@@ -21025,7 +21090,7 @@ var init_codex_adapter = __esm({
21025
21090
  timeToFirstTokenMs: external_exports.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).nullable(),
21026
21091
  terminalStatus: external_exports.literal("completed")
21027
21092
  }).superRefine((value, context) => {
21028
- if (value.reasoningSummary != null && utf8Bytes(value.reasoningSummary) > CODEX_REASONING_SUMMARY_MAX_UTF8_BYTES || value.usage.cachedInputTokens > value.usage.inputTokens || value.usage.reasoningOutputTokens > value.usage.outputTokens || value.usage.totalTokens !== value.usage.inputTokens + value.usage.outputTokens || value.effectiveModel !== value.requestedModel || value.effectiveReasoningEffort !== value.requestedReasoningEffort) {
21093
+ if (value.reasoningContent != null && utf8Bytes(value.reasoningContent) > CODEX_REASONING_CONTENT_MAX_UTF8_BYTES || value.reasoningSummary != null && utf8Bytes(value.reasoningSummary) > CODEX_REASONING_SUMMARY_MAX_UTF8_BYTES || value.usage.cachedInputTokens > value.usage.inputTokens || value.usage.reasoningOutputTokens > value.usage.outputTokens || value.usage.totalTokens !== value.usage.inputTokens + value.usage.outputTokens || value.effectiveModel !== value.requestedModel || value.effectiveReasoningEffort !== value.requestedReasoningEffort) {
21029
21094
  context.addIssue({ code: "custom", message: "Invalid Codex recovery usage or identity." });
21030
21095
  }
21031
21096
  });
@@ -21320,7 +21385,7 @@ var init_codex_adapter = __esm({
21320
21385
  const parentPath = resolve3(parent);
21321
21386
  const candidatePath = resolve3(candidate);
21322
21387
  const scoped = relative2(parentPath, candidatePath);
21323
- return scoped.length > 0 && !scoped.startsWith(`..${sep2}`) && scoped !== ".." && !isAbsolute2(scoped);
21388
+ return scoped.length > 0 && !scoped.startsWith(`..${sep2}`) && scoped !== ".." && !isAbsolute3(scoped);
21324
21389
  };
21325
21390
  assertRecoveryResourceScope = (checkpoint) => {
21326
21391
  const temporaryRoot = resolve3(tmpdir2());
@@ -21410,7 +21475,7 @@ var init_codex_adapter = __esm({
21410
21475
  }
21411
21476
  }
21412
21477
  if (checkpoint.threadPath !== null) {
21413
- if (!isAbsolute2(checkpoint.threadPath) || !isStrictDescendant(
21478
+ if (!isAbsolute3(checkpoint.threadPath) || !isStrictDescendant(
21414
21479
  checkpoint.threadPath,
21415
21480
  sessionsRoot
21416
21481
  ) || !checkpoint.threadPath.endsWith(".jsonl")) {
@@ -28989,11 +29054,20 @@ var init_runner = __esm({
28989
29054
  options.codexModelCapabilities,
28990
29055
  options.adapterRuntimeVersion
28991
29056
  ),
28992
- maxConcurrency: finitePositiveOption(
28993
- options.maxConcurrency,
28994
- DEFAULT_MAX_CONCURRENCY,
28995
- "Maximum concurrency"
28996
- ),
29057
+ maxConcurrency: (() => {
29058
+ const value = finitePositiveOption(
29059
+ options.maxConcurrency,
29060
+ DEFAULT_MAX_CONCURRENCY,
29061
+ "Maximum concurrency"
29062
+ );
29063
+ if (value > MAX_CODEX_HOST_CONCURRENCY) {
29064
+ throw new InferenceHostRunnerError(
29065
+ "invalid_configuration",
29066
+ `Maximum concurrency must not exceed ${MAX_CODEX_HOST_CONCURRENCY}.`
29067
+ );
29068
+ }
29069
+ return value;
29070
+ })(),
28997
29071
  advertisementTtlMs,
28998
29072
  advertisementRefreshLeadMs,
28999
29073
  hostHeartbeatMs: finitePositiveOption(
@@ -29107,6 +29181,8 @@ var init_runner = __esm({
29107
29181
  providerResponseSchemaVersion: null,
29108
29182
  providerResponseMaxUtf8Bytes: null,
29109
29183
  providerResponseTextMaxUtf8Bytes: null,
29184
+ providerReasoningContentMaxUtf8Bytes: null,
29185
+ providerReasoningContentSupported: false,
29110
29186
  providerReasoningSummaryMaxUtf8Bytes: null,
29111
29187
  providerReasoningSummarySupported: false
29112
29188
  };
@@ -29116,6 +29192,7 @@ var init_runner = __esm({
29116
29192
  const properties = objectRecord(outputSchema.properties);
29117
29193
  const schemaVersionProperty = objectRecord(properties?.schema_version);
29118
29194
  const responseTextProperty = objectRecord(properties?.response_text);
29195
+ const reasoningContentProperty = objectRecord(properties?.reasoning_content);
29119
29196
  const reasoningSummaryProperty = objectRecord(properties?.reasoning_summary);
29120
29197
  const schemaVersion = schemaVersionProperty?.const;
29121
29198
  const requiredFields = outputSchema.required;
@@ -29125,18 +29202,22 @@ var init_runner = __esm({
29125
29202
  "finish_reason",
29126
29203
  "refusal_status"
29127
29204
  ];
29205
+ const providerReasoningContentSupported = reasoningContentProperty !== null;
29128
29206
  const providerReasoningSummarySupported = reasoningSummaryProperty !== null;
29129
- const expectedFields = providerReasoningSummarySupported ? [...baseExpectedFields, "reasoning_summary"] : baseExpectedFields;
29207
+ const expectedFields = providerReasoningContentSupported ? [...baseExpectedFields, "reasoning_content"] : providerReasoningSummarySupported ? [...baseExpectedFields, "reasoning_summary"] : baseExpectedFields;
29130
29208
  const responseMaxUtf8Bytes = positiveUtf8ByteLimit(
29131
29209
  outputSchema["x-vtx-max-utf8-bytes"]
29132
29210
  );
29133
29211
  const responseTextMaxUtf8Bytes = positiveUtf8ByteLimit(
29134
29212
  responseTextProperty?.["x-vtx-max-utf8-bytes"]
29135
29213
  );
29214
+ const reasoningContentMaxUtf8Bytes = positiveUtf8ByteLimit(
29215
+ reasoningContentProperty?.["x-vtx-max-utf8-bytes"]
29216
+ );
29136
29217
  const reasoningSummaryMaxUtf8Bytes = positiveUtf8ByteLimit(
29137
29218
  reasoningSummaryProperty?.["x-vtx-max-utf8-bytes"]
29138
29219
  );
29139
- if (outputSchema.type !== "object" || outputSchema.additionalProperties !== false || !Array.isArray(requiredFields) || !requiredFields.every((field) => typeof field === "string") || [...requiredFields].sort().join("\0") !== expectedFields.sort().join("\0") || properties === null || !exactObjectKeys(properties, expectedFields) || adapterSchema === null || typeof schemaVersion !== "string" || schemaVersion !== jobInput.output_schema_version || responseTextMaxUtf8Bytes === null || providerReasoningSummarySupported && reasoningSummaryMaxUtf8Bytes === null || responseMaxUtf8Bytes === null) {
29220
+ if (outputSchema.type !== "object" || outputSchema.additionalProperties !== false || !Array.isArray(requiredFields) || !requiredFields.every((field) => typeof field === "string") || [...requiredFields].sort().join("\0") !== expectedFields.sort().join("\0") || properties === null || !exactObjectKeys(properties, expectedFields) || adapterSchema === null || typeof schemaVersion !== "string" || schemaVersion !== jobInput.output_schema_version || responseTextMaxUtf8Bytes === null || providerReasoningContentSupported && reasoningContentMaxUtf8Bytes === null || providerReasoningSummarySupported && reasoningSummaryMaxUtf8Bytes === null || providerReasoningContentSupported && providerReasoningSummarySupported || responseMaxUtf8Bytes === null) {
29140
29221
  throw new InferenceHostRunnerError(
29141
29222
  "invalid_output_schema",
29142
29223
  "The provider-response output schema does not match the canonical envelope structure."
@@ -29151,6 +29232,8 @@ var init_runner = __esm({
29151
29232
  providerResponseSchemaVersion: schemaVersion,
29152
29233
  providerResponseMaxUtf8Bytes: responseMaxUtf8Bytes,
29153
29234
  providerResponseTextMaxUtf8Bytes: responseTextMaxUtf8Bytes,
29235
+ providerReasoningContentMaxUtf8Bytes: reasoningContentMaxUtf8Bytes,
29236
+ providerReasoningContentSupported,
29154
29237
  providerReasoningSummaryMaxUtf8Bytes: reasoningSummaryMaxUtf8Bytes,
29155
29238
  providerReasoningSummarySupported
29156
29239
  };
@@ -30085,6 +30168,12 @@ var init_runner = __esm({
30085
30168
  "Codex response text exceeds the immutable provider-response UTF-8 byte limit."
30086
30169
  );
30087
30170
  }
30171
+ if (outputContract.providerReasoningContentSupported && outputContract.providerReasoningContentMaxUtf8Bytes !== null && adapterResult.reasoningContent !== null && Buffer.byteLength(adapterResult.reasoningContent, "utf8") > outputContract.providerReasoningContentMaxUtf8Bytes) {
30172
+ throw new InferenceHostRunnerError(
30173
+ "output_schema_reasoning_content_too_large",
30174
+ "Codex reasoning content exceeds the immutable provider-response UTF-8 byte limit."
30175
+ );
30176
+ }
30088
30177
  if (outputContract.providerReasoningSummarySupported && outputContract.providerReasoningSummaryMaxUtf8Bytes !== null && adapterResult.reasoningSummary !== null && Buffer.byteLength(adapterResult.reasoningSummary, "utf8") > outputContract.providerReasoningSummaryMaxUtf8Bytes) {
30089
30178
  throw new InferenceHostRunnerError(
30090
30179
  "output_schema_reasoning_summary_too_large",
@@ -30096,6 +30185,7 @@ var init_runner = __esm({
30096
30185
  response_text: adapterResult.text,
30097
30186
  finish_reason: finishReason,
30098
30187
  refusal_status: refusalStatus,
30188
+ ...outputContract.providerReasoningContentSupported ? { reasoning_content: adapterResult.reasoningContent } : {},
30099
30189
  ...outputContract.providerReasoningSummarySupported ? { reasoning_summary: adapterResult.reasoningSummary } : {}
30100
30190
  };
30101
30191
  validateOutput(outputContract.providerResponseSchema, providerResponse);
@@ -30316,7 +30406,7 @@ import { createWriteStream, readFileSync } from "node:fs";
30316
30406
  import { access as access3, chmod as chmod3, mkdir as mkdir3, readFile as readFile5, rm as rm4, writeFile as writeFile2 } from "node:fs/promises";
30317
30407
  import { homedir as homedir2 } from "node:os";
30318
30408
  import { dirname as dirname4, join as join5, resolve as resolve4 } from "node:path";
30319
- 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;
30409
+ var SERVICE_NAME, SYSTEMD_UNIT, LAUNCHD_LABEL, SERVICE_COOPERATIVE_STOP_SECONDS, isWindowsSubsystemForLinux, inferenceHostServiceManifestPath, inferenceHostServiceDesiredPath, inferenceHostServiceLogPath, xmlEscape, plistEscape, systemdQuote, defaultRunCommand, managerName, assertRuntimeEnvironment, assertServicePath, assertWorker, assertManifest, assertDesiredState, readInferenceHostServiceManifest, readInferenceHostServiceDesired, readDesiredAcrossAtomicReplacement, writeDesired, runtimeEnvironment, serviceArguments, windowsOwnedCommandLine, vbScriptString, windowsServiceLauncher, windowsTaskXml, systemdUnit, launchAgentPlist, InferenceHostServiceManager, appendServiceLog, spawnServiceChild, runInferenceHostServiceSupervisor;
30320
30410
  var init_service = __esm({
30321
30411
  "lib/inference-host/service.ts"() {
30322
30412
  "use strict";
@@ -30334,9 +30424,9 @@ var init_service = __esm({
30334
30424
  }
30335
30425
  })())
30336
30426
  );
30337
- inferenceHostServiceManifestPath = (config2) => `${config2.statePath}.service.json`;
30338
- inferenceHostServiceDesiredPath = (config2) => `${config2.statePath}.service-desired.json`;
30339
- inferenceHostServiceLogPath = (config2) => `${config2.statePath}.service.log`;
30427
+ inferenceHostServiceManifestPath = (config2) => `${config2.supervisorStatePath}.service.json`;
30428
+ inferenceHostServiceDesiredPath = (config2) => `${config2.supervisorStatePath}.service-desired.json`;
30429
+ inferenceHostServiceLogPath = (config2) => `${config2.supervisorStatePath}.service.log`;
30340
30430
  xmlEscape = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
30341
30431
  plistEscape = xmlEscape;
30342
30432
  systemdQuote = (value) => `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("%", "%%")}"`;
@@ -30366,29 +30456,79 @@ var init_service = __esm({
30366
30456
  if (platform === "linux") return "systemd-user";
30367
30457
  throw new Error(`Inference-host background service is unsupported on ${platform}.`);
30368
30458
  };
30459
+ assertRuntimeEnvironment = (value) => {
30460
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
30461
+ throw new Error("Inference-host service runtime environment is invalid.");
30462
+ }
30463
+ for (const [key, entry] of Object.entries(value)) {
30464
+ if (!key.startsWith("VTX_") || typeof entry !== "string") {
30465
+ throw new Error("Inference-host service runtime environment is invalid.");
30466
+ }
30467
+ }
30468
+ return value;
30469
+ };
30470
+ assertServicePath = (value) => {
30471
+ if (typeof value !== "string" || !value || /[\r\n\0]/u.test(value)) {
30472
+ throw new Error("Inference-host service manifest paths contain control characters.");
30473
+ }
30474
+ return value;
30475
+ };
30476
+ assertWorker = (value) => {
30477
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
30478
+ throw new Error("Inference-host service worker is invalid.");
30479
+ }
30480
+ const record2 = value;
30481
+ 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 || !Number.isSafeInteger(record2.max_concurrency) || Number(record2.max_concurrency) < 1 || Number(record2.max_concurrency) > MAX_CODEX_HOST_CONCURRENCY || record2.authenticated_account_email !== null && typeof record2.authenticated_account_email !== "string" || record2.authenticated_account_plan !== null && typeof record2.authenticated_account_plan !== "string") {
30482
+ throw new Error("Inference-host service worker is invalid.");
30483
+ }
30484
+ return {
30485
+ instance_name: record2.instance_name,
30486
+ adapter: "codex",
30487
+ display_name: record2.display_name,
30488
+ max_concurrency: Number(record2.max_concurrency),
30489
+ authenticated_account_email: record2.authenticated_account_email,
30490
+ authenticated_account_plan: record2.authenticated_account_plan,
30491
+ runtime_environment: assertRuntimeEnvironment(record2.runtime_environment)
30492
+ };
30493
+ };
30369
30494
  assertManifest = (value) => {
30370
30495
  if (!value || typeof value !== "object" || Array.isArray(value)) {
30371
30496
  throw new Error("Inference-host service manifest is invalid.");
30372
30497
  }
30373
30498
  const record2 = value;
30374
- 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)) {
30499
+ if (record2.schema_version === "vtx_inference_service_v1") {
30500
+ const legacy = record2;
30501
+ 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.");
30502
+ const runtime_environment = assertRuntimeEnvironment(legacy.runtime_environment);
30503
+ const worker = assertWorker({
30504
+ instance_name: "default",
30505
+ adapter: "codex",
30506
+ display_name: legacy.display_name,
30507
+ max_concurrency: Number(runtime_environment.VTX_INFERENCE_HOST_MAX_CONCURRENCY || 1),
30508
+ authenticated_account_email: null,
30509
+ authenticated_account_plan: null,
30510
+ runtime_environment
30511
+ });
30512
+ return {
30513
+ schema_version: "vtx_inference_service_v2",
30514
+ installed_at: legacy.installed_at,
30515
+ executable: assertServicePath(legacy.executable),
30516
+ script: assertServicePath(legacy.script),
30517
+ log_path: assertServicePath(legacy.log_path),
30518
+ workers: [worker]
30519
+ };
30520
+ }
30521
+ if (record2.schema_version !== "vtx_inference_service_v2" || 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) {
30375
30522
  throw new Error("Inference-host service manifest is invalid.");
30376
30523
  }
30377
- for (const value2 of [
30378
- record2.executable,
30379
- record2.script,
30380
- record2.log_path
30381
- ]) {
30382
- if (typeof value2 === "string" && /[\r\n\0]/u.test(value2)) {
30383
- throw new Error("Inference-host service manifest paths contain control characters.");
30384
- }
30524
+ assertServicePath(record2.executable);
30525
+ assertServicePath(record2.script);
30526
+ assertServicePath(record2.log_path);
30527
+ const workers = record2.workers.map(assertWorker);
30528
+ if (new Set(workers.map((worker) => worker.instance_name)).size !== workers.length) {
30529
+ throw new Error("Inference-host service worker names must be unique.");
30385
30530
  }
30386
- for (const [key, entry] of Object.entries(record2.runtime_environment)) {
30387
- if (!key.startsWith("VTX_") || typeof entry !== "string") {
30388
- throw new Error("Inference-host service runtime environment is invalid.");
30389
- }
30390
- }
30391
- return record2;
30531
+ return { ...record2, workers };
30392
30532
  };
30393
30533
  assertDesiredState = (value) => {
30394
30534
  if (!value || typeof value !== "object" || Array.isArray(value)) {
@@ -30440,13 +30580,15 @@ var init_service = __esm({
30440
30580
  }, null, 2)}
30441
30581
  `);
30442
30582
  };
30443
- runtimeEnvironment = (config2) => {
30583
+ runtimeEnvironment = (config2, maxConcurrency) => {
30444
30584
  const result2 = {
30445
30585
  VTX_API_URL: config2.apiUrl,
30446
- VTX_INFERENCE_HOST_HOME: dirname4(config2.codexHomePath),
30586
+ VTX_INFERENCE_HOST_HOME: config2.baseDir,
30587
+ VTX_INFERENCE_HOST_INSTANCE: config2.instanceName,
30447
30588
  VTX_INFERENCE_HOST_CREDENTIAL_STORE: config2.credentialStoreMode,
30448
30589
  VTX_INFERENCE_HOST_STATE_PATH: config2.statePath,
30449
- VTX_INFERENCE_HOST_LOCK_PATH: config2.processLockPath
30590
+ VTX_INFERENCE_HOST_LOCK_PATH: config2.processLockPath,
30591
+ VTX_INFERENCE_HOST_MAX_CONCURRENCY: String(maxConcurrency)
30450
30592
  };
30451
30593
  if (config2.credentialFilePath) {
30452
30594
  result2.VTX_INFERENCE_HOST_CREDENTIAL_FILE = config2.credentialFilePath;
@@ -30562,7 +30704,7 @@ WantedBy=default.target
30562
30704
  return inferenceHostServiceLogPath(this.config);
30563
30705
  }
30564
30706
  controlLockPath() {
30565
- return `${this.config.processLockPath}.service-control`;
30707
+ return `${this.config.supervisorProcessLockPath}.service-control`;
30566
30708
  }
30567
30709
  async withControlLock(operation) {
30568
30710
  let lock2 = null;
@@ -30598,12 +30740,12 @@ WantedBy=default.target
30598
30740
  ];
30599
30741
  }
30600
30742
  definitionPath() {
30601
- if (this.platform === "win32") return `${this.config.statePath}.service-task.xml`;
30743
+ if (this.platform === "win32") return `${this.config.supervisorStatePath}.service-task.xml`;
30602
30744
  if (this.platform === "darwin") return join5(this.home, "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
30603
30745
  return join5(this.home, ".config", "systemd", "user", SYSTEMD_UNIT);
30604
30746
  }
30605
30747
  windowsLauncherPath() {
30606
- return `${this.config.statePath}.service-launcher.vbs`;
30748
+ return `${this.config.supervisorStatePath}.service-launcher.vbs`;
30607
30749
  }
30608
30750
  async managerCommand(action) {
30609
30751
  const definition = this.definitionPath();
@@ -30646,37 +30788,19 @@ WantedBy=default.target
30646
30788
  }
30647
30789
  throw new Error("Background service did not reach an active state within 10 seconds.");
30648
30790
  }
30649
- async install(options) {
30650
- return await this.withControlLock(async () => await this.installUnlocked(options));
30651
- }
30652
- async installUnlocked(options) {
30653
- if (await readInferenceHostServiceManifest(this.manifestPath())) {
30654
- await this.uninstallUnlocked();
30655
- }
30656
- await access3(this.executable);
30657
- await access3(this.script);
30658
- const manifest = assertManifest({
30659
- schema_version: "vtx_inference_service_v1",
30660
- installed_at: this.now().toISOString(),
30661
- adapter: options.adapter,
30662
- executable: this.executable,
30663
- script: this.script,
30664
- display_name: options.displayName,
30665
- log_path: this.logPath(),
30666
- runtime_environment: runtimeEnvironment(this.config)
30667
- });
30668
- const args = serviceArguments(this.script, this.manifestPath());
30669
- 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);
30791
+ async registerManifestUnlocked(manifest, desiredRunning) {
30792
+ const args = serviceArguments(manifest.script, this.manifestPath());
30793
+ 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);
30670
30794
  let managerInstallAttempted = false;
30671
30795
  try {
30672
30796
  await writeAtomicInferencePrivateFile(this.manifestPath(), `${JSON.stringify(manifest, null, 2)}
30673
30797
  `);
30674
- await writeDesired(this.desiredPath(), options.startImmediately !== false, this.now());
30798
+ await writeDesired(this.desiredPath(), desiredRunning, this.now());
30675
30799
  await mkdir3(dirname4(this.definitionPath()), { recursive: true, mode: 448 });
30676
30800
  if (this.platform === "win32") {
30677
30801
  await writeAtomicInferencePrivateFile(
30678
30802
  this.windowsLauncherPath(),
30679
- windowsServiceLauncher(this.executable, args)
30803
+ windowsServiceLauncher(manifest.executable, args)
30680
30804
  );
30681
30805
  await writeFile2(this.definitionPath(), `\uFEFF${definition}`, {
30682
30806
  encoding: "utf16le",
@@ -30693,7 +30817,7 @@ WantedBy=default.target
30693
30817
  managerInstallAttempted = true;
30694
30818
  const installed = await this.managerCommand("install");
30695
30819
  if (installed.exitCode !== 0) throw new Error(`Background service installation failed: ${installed.stderr.trim()}`);
30696
- if (options.startImmediately !== false) {
30820
+ if (desiredRunning) {
30697
30821
  const started = await this.managerCommand("start");
30698
30822
  if (started.exitCode !== 0 && !/already running|in progress/iu.test(`${started.stdout}
30699
30823
  ${started.stderr}`)) {
@@ -30701,6 +30825,7 @@ ${started.stderr}`)) {
30701
30825
  }
30702
30826
  await this.waitForManagerActive();
30703
30827
  }
30828
+ return await this.status();
30704
30829
  } catch (error48) {
30705
30830
  await writeDesired(this.desiredPath(), false, this.now()).catch(() => void 0);
30706
30831
  if (managerInstallAttempted) {
@@ -30728,7 +30853,83 @@ ${cleanup.stderr}`)) {
30728
30853
  }
30729
30854
  throw error48;
30730
30855
  }
30731
- return await this.status();
30856
+ }
30857
+ async replaceManifestUnlocked(next, desiredRunning) {
30858
+ const previous = await readInferenceHostServiceManifest(this.manifestPath());
30859
+ const previousDesired = previous ? await readInferenceHostServiceDesired(this.desiredPath()) : false;
30860
+ if (previous) await this.uninstallUnlocked();
30861
+ try {
30862
+ return await this.registerManifestUnlocked(next, desiredRunning);
30863
+ } catch (error48) {
30864
+ if (!previous) throw error48;
30865
+ try {
30866
+ await this.registerManifestUnlocked(previous, previousDesired);
30867
+ } catch (rollbackError) {
30868
+ throw new Error(
30869
+ "Inference-host service reconfiguration failed and the previous supervisor could not be restored.",
30870
+ { cause: new AggregateError([error48, rollbackError]) }
30871
+ );
30872
+ }
30873
+ throw new Error(
30874
+ "Inference-host service reconfiguration failed; the previous supervisor was restored.",
30875
+ { cause: error48 }
30876
+ );
30877
+ }
30878
+ }
30879
+ async install(options) {
30880
+ return await this.withControlLock(async () => await this.installUnlocked(options));
30881
+ }
30882
+ async installUnlocked(options) {
30883
+ const existing = await readInferenceHostServiceManifest(this.manifestPath());
30884
+ const normalizedEmail = options.authenticatedAccountEmail?.trim().toLowerCase() || null;
30885
+ const otherWorkers = existing?.workers.filter(
30886
+ (worker) => worker.instance_name !== this.config.instanceName
30887
+ ) ?? [];
30888
+ if (otherWorkers.length > 0 && (!normalizedEmail || otherWorkers.some((worker) => !worker.authenticated_account_email))) {
30889
+ throw new Error(
30890
+ "Cannot prove that every installed instance uses a distinct ChatGPT subscription. Reinstall instances with live account identity before adding another."
30891
+ );
30892
+ }
30893
+ if (normalizedEmail && otherWorkers.some((worker) => worker.authenticated_account_email?.trim().toLowerCase() === normalizedEmail)) {
30894
+ throw new Error(
30895
+ "This ChatGPT subscription is already installed under another inference-host instance."
30896
+ );
30897
+ }
30898
+ if (this.config.credentialFilePath && otherWorkers.some((worker) => {
30899
+ const otherPath = worker.runtime_environment.VTX_INFERENCE_HOST_CREDENTIAL_FILE;
30900
+ return otherPath && resolve4(otherPath) === resolve4(this.config.credentialFilePath);
30901
+ })) {
30902
+ throw new Error(
30903
+ "Each inference-host instance must use a different private credential file."
30904
+ );
30905
+ }
30906
+ await access3(this.executable);
30907
+ await access3(this.script);
30908
+ const maxConcurrency = options.maxConcurrency ?? 1;
30909
+ const workers = [
30910
+ ...existing?.workers.filter((worker) => worker.instance_name !== this.config.instanceName) ?? [],
30911
+ {
30912
+ instance_name: this.config.instanceName,
30913
+ adapter: options.adapter,
30914
+ display_name: options.displayName,
30915
+ max_concurrency: maxConcurrency,
30916
+ authenticated_account_email: normalizedEmail,
30917
+ authenticated_account_plan: options.authenticatedAccountPlan ?? null,
30918
+ runtime_environment: runtimeEnvironment(this.config, maxConcurrency)
30919
+ }
30920
+ ].sort((left, right) => left.instance_name.localeCompare(right.instance_name));
30921
+ const manifest = assertManifest({
30922
+ schema_version: "vtx_inference_service_v2",
30923
+ installed_at: this.now().toISOString(),
30924
+ executable: this.executable,
30925
+ script: this.script,
30926
+ log_path: this.logPath(),
30927
+ workers
30928
+ });
30929
+ return await this.replaceManifestUnlocked(
30930
+ manifest,
30931
+ options.startImmediately !== false
30932
+ );
30732
30933
  }
30733
30934
  async start() {
30734
30935
  return await this.withControlLock(async () => await this.startUnlocked());
@@ -30755,11 +30956,12 @@ ${result2.stderr}`)) {
30755
30956
  return await this.withControlLock(async () => await this.stopUnlocked());
30756
30957
  }
30757
30958
  async stopUnlocked() {
30758
- if (!await readInferenceHostServiceManifest(this.manifestPath())) {
30959
+ const manifest = await readInferenceHostServiceManifest(this.manifestPath());
30960
+ if (!manifest) {
30759
30961
  throw new Error("Inference-host service is not installed.");
30760
30962
  }
30761
30963
  await writeDesired(this.desiredPath(), false, this.now());
30762
- const serviceLockPath = `${this.config.processLockPath}.service`;
30964
+ const serviceLockPath = `${this.config.supervisorProcessLockPath}.service`;
30763
30965
  let serviceReleased = false;
30764
30966
  for (let attempt = 0; attempt < this.stopWaitAttempts; attempt += 1) {
30765
30967
  try {
@@ -30777,7 +30979,7 @@ ${result2.stderr}`)) {
30777
30979
  }
30778
30980
  if (!serviceReleased) {
30779
30981
  throw new Error(
30780
- `Inference-host worker did not stop cooperatively within ${SERVICE_COOPERATIVE_STOP_SECONDS} seconds; refusing forced termination while cleanup may be pending.`
30982
+ `Inference-host workers did not stop cooperatively within ${SERVICE_COOPERATIVE_STOP_SECONDS} seconds; refusing forced termination while cleanup may be pending.`
30781
30983
  );
30782
30984
  }
30783
30985
  const result2 = await this.managerCommand("stop");
@@ -30803,8 +31005,15 @@ ${result2.stderr}`)) {
30803
31005
  manager_active: managerActive,
30804
31006
  manager: managerName(this.platform),
30805
31007
  manager_state: result2.exitCode === 0 ? output3 || "installed" : "not-installed",
30806
- adapter: manifest?.adapter ?? null,
30807
- log_path: manifest?.log_path ?? this.logPath()
31008
+ adapter: manifest ? "codex" : null,
31009
+ log_path: manifest?.log_path ?? this.logPath(),
31010
+ workers: manifest?.workers.map((worker) => ({
31011
+ instance_name: worker.instance_name,
31012
+ display_name: worker.display_name,
31013
+ max_concurrency: worker.max_concurrency,
31014
+ authenticated_account_email: worker.authenticated_account_email,
31015
+ authenticated_account_plan: worker.authenticated_account_plan
31016
+ })) ?? []
30808
31017
  };
30809
31018
  }
30810
31019
  async logs(lines = 100) {
@@ -30819,6 +31028,24 @@ ${result2.stderr}`)) {
30819
31028
  throw error48;
30820
31029
  }
30821
31030
  }
31031
+ async uninstallInstance(instanceName) {
31032
+ return await this.withControlLock(async () => {
31033
+ const manifest = await readInferenceHostServiceManifest(this.manifestPath());
31034
+ if (!manifest) throw new Error("Inference-host service is not installed.");
31035
+ if (!manifest.workers.some((worker) => worker.instance_name === instanceName)) {
31036
+ throw new Error(`Inference-host instance ${instanceName} is not installed.`);
31037
+ }
31038
+ const remaining = manifest.workers.filter(
31039
+ (worker) => worker.instance_name !== instanceName
31040
+ );
31041
+ if (remaining.length === 0) return await this.uninstallUnlocked();
31042
+ return await this.replaceManifestUnlocked({
31043
+ ...manifest,
31044
+ installed_at: this.now().toISOString(),
31045
+ workers: remaining
31046
+ }, await readInferenceHostServiceDesired(this.desiredPath()));
31047
+ });
31048
+ }
30822
31049
  async uninstall() {
30823
31050
  return await this.withControlLock(async () => await this.uninstallUnlocked());
30824
31051
  }
@@ -30848,7 +31075,8 @@ ${result2.stderr}`)) {
30848
31075
  manager: managerName(this.platform),
30849
31076
  manager_state: "not-installed",
30850
31077
  adapter: null,
30851
- log_path: manifest.log_path
31078
+ log_path: manifest.log_path,
31079
+ workers: []
30852
31080
  };
30853
31081
  }
30854
31082
  };
@@ -30861,21 +31089,21 @@ ${result2.stderr}`)) {
30861
31089
  `, resolvePromise);
30862
31090
  });
30863
31091
  };
30864
- spawnServiceChild = async (manifest, signal) => {
31092
+ spawnServiceChild = async (manifest, worker, signal) => {
30865
31093
  const args = [
30866
31094
  manifest.script,
30867
31095
  "inference-host",
30868
31096
  "run",
30869
31097
  "--json",
30870
31098
  "--display-name",
30871
- manifest.display_name
31099
+ worker.display_name
30872
31100
  ];
30873
31101
  const startedAt = Date.now();
30874
31102
  const log = createWriteStream(manifest.log_path, { flags: "a", mode: 384 });
30875
31103
  return await new Promise((resolvePromise, reject) => {
30876
31104
  let stdout = "";
30877
31105
  const child = spawn5(manifest.executable, args, {
30878
- env: { ...process.env, ...manifest.runtime_environment },
31106
+ env: { ...process.env, ...worker.runtime_environment },
30879
31107
  windowsHide: true,
30880
31108
  stdio: ["ignore", "pipe", "pipe"]
30881
31109
  });
@@ -30919,64 +31147,72 @@ ${result2.stderr}`)) {
30919
31147
  runInferenceHostServiceSupervisor = async (manifestPath, options = {}) => {
30920
31148
  const manifest = await readInferenceHostServiceManifest(manifestPath);
30921
31149
  if (!manifest) throw new Error("Inference-host service manifest is missing.");
30922
- const desiredPath = `${manifest.runtime_environment.VTX_INFERENCE_HOST_STATE_PATH}.service-desired.json`;
31150
+ const desiredPath = manifestPath.replace(/\.service\.json$/u, ".service-desired.json");
30923
31151
  const signal = options.signal ?? new AbortController().signal;
30924
31152
  const sleep4 = options.sleep ?? (async (milliseconds) => {
30925
31153
  await new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds));
30926
31154
  });
30927
31155
  const launch = options.runWorker ?? options.spawnChild ?? spawnServiceChild;
30928
- let failures = 0;
30929
- await appendServiceLog(manifest.log_path, "service_supervisor_started", { adapter: manifest.adapter });
30930
- while (!signal.aborted && await readDesiredAcrossAtomicReplacement(desiredPath)) {
30931
- try {
30932
- const workerController = new AbortController();
30933
- const forwardAbort = () => workerController.abort();
30934
- signal.addEventListener("abort", forwardAbort, { once: true });
30935
- let workerComplete = false;
30936
- let monitorError = null;
30937
- const desiredMonitor = (async () => {
30938
- while (!workerComplete && !workerController.signal.aborted) {
30939
- await sleep4(500);
30940
- if (!await readDesiredAcrossAtomicReplacement(desiredPath)) {
30941
- workerController.abort();
30942
- break;
30943
- }
30944
- }
30945
- })().catch((error48) => {
30946
- monitorError = error48;
30947
- workerController.abort();
30948
- });
30949
- let result2;
31156
+ await appendServiceLog(manifest.log_path, "service_supervisor_started", {
31157
+ adapter: "codex",
31158
+ instances: manifest.workers.map((worker) => worker.instance_name)
31159
+ });
31160
+ const superviseWorker = async (worker) => {
31161
+ let failures = 0;
31162
+ while (!signal.aborted && await readDesiredAcrossAtomicReplacement(desiredPath)) {
30950
31163
  try {
30951
- result2 = await launch(manifest, workerController.signal);
30952
- } finally {
30953
- workerComplete = true;
30954
- workerController.abort();
30955
- signal.removeEventListener("abort", forwardAbort);
30956
- await desiredMonitor;
30957
- }
30958
- if (monitorError) throw monitorError;
30959
- if (signal.aborted || !await readDesiredAcrossAtomicReplacement(desiredPath)) break;
30960
- failures = result2.uptimeMs >= 6e4 ? 0 : failures + 1;
30961
- const retryAfterMs = Math.min(1e3 * 2 ** Math.min(failures, 5), 3e4);
30962
- await appendServiceLog(manifest.log_path, "worker_exited", {
30963
- exit_code: result2.exitCode,
30964
- uptime_ms: result2.uptimeMs,
30965
- drain_reason: result2.drainReason ?? null,
30966
- retry_after_ms: retryAfterMs
30967
- });
30968
- await sleep4(retryAfterMs);
30969
- } catch (error48) {
30970
- if (signal.aborted) break;
30971
- failures += 1;
30972
- const retryAfterMs = Math.min(1e3 * 2 ** Math.min(failures, 5), 3e4);
30973
- await appendServiceLog(manifest.log_path, "worker_launch_failed", {
30974
- error: error48 instanceof Error ? error48.message : "unknown",
30975
- retry_after_ms: retryAfterMs
30976
- });
30977
- await sleep4(retryAfterMs);
31164
+ const workerController = new AbortController();
31165
+ const forwardAbort = () => workerController.abort();
31166
+ signal.addEventListener("abort", forwardAbort, { once: true });
31167
+ let workerComplete = false;
31168
+ let monitorError = null;
31169
+ const desiredMonitor = (async () => {
31170
+ while (!workerComplete && !workerController.signal.aborted) {
31171
+ await sleep4(500);
31172
+ if (!await readDesiredAcrossAtomicReplacement(desiredPath)) {
31173
+ workerController.abort();
31174
+ break;
31175
+ }
31176
+ }
31177
+ })().catch((error48) => {
31178
+ monitorError = error48;
31179
+ workerController.abort();
31180
+ });
31181
+ let result2;
31182
+ try {
31183
+ result2 = await launch(manifest, worker, workerController.signal);
31184
+ } finally {
31185
+ workerComplete = true;
31186
+ workerController.abort();
31187
+ signal.removeEventListener("abort", forwardAbort);
31188
+ await desiredMonitor;
31189
+ }
31190
+ if (monitorError) throw monitorError;
31191
+ if (signal.aborted || !await readDesiredAcrossAtomicReplacement(desiredPath)) break;
31192
+ failures = result2.uptimeMs >= 6e4 ? 0 : failures + 1;
31193
+ const retryAfterMs = Math.min(1e3 * 2 ** Math.min(failures, 5), 3e4);
31194
+ await appendServiceLog(manifest.log_path, "worker_exited", {
31195
+ instance_name: worker.instance_name,
31196
+ exit_code: result2.exitCode,
31197
+ uptime_ms: result2.uptimeMs,
31198
+ drain_reason: result2.drainReason ?? null,
31199
+ retry_after_ms: retryAfterMs
31200
+ });
31201
+ await sleep4(retryAfterMs);
31202
+ } catch (error48) {
31203
+ if (signal.aborted) break;
31204
+ failures += 1;
31205
+ const retryAfterMs = Math.min(1e3 * 2 ** Math.min(failures, 5), 3e4);
31206
+ await appendServiceLog(manifest.log_path, "worker_launch_failed", {
31207
+ instance_name: worker.instance_name,
31208
+ error: error48 instanceof Error ? error48.message : "unknown",
31209
+ retry_after_ms: retryAfterMs
31210
+ });
31211
+ await sleep4(retryAfterMs);
31212
+ }
30978
31213
  }
30979
- }
31214
+ };
31215
+ await Promise.all(manifest.workers.map(superviseWorker));
30980
31216
  await appendServiceLog(manifest.log_path, "service_supervisor_stopped");
30981
31217
  };
30982
31218
  }
@@ -31066,7 +31302,7 @@ async function runInferenceHostCli(argv2, env = process.env, dependencies = {})
31066
31302
  };
31067
31303
  }
31068
31304
  }
31069
- 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;
31305
+ 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;
31070
31306
  var init_cli = __esm({
31071
31307
  "lib/inference-host/cli.ts"() {
31072
31308
  "use strict";
@@ -31170,6 +31406,8 @@ Commands:
31170
31406
 
31171
31407
  Common options:
31172
31408
  --json Emit machine-readable JSON
31409
+ --instance NAME Target an isolated local subscription instance (default: default)
31410
+ --max-concurrency N Automated Codex slots for this subscription (1-8; default: 3)
31173
31411
  --help, -h Show this help
31174
31412
 
31175
31413
  If the OS credential store cannot retain the VTX grant, set
@@ -31177,23 +31415,31 @@ VTX_INFERENCE_HOST_CREDENTIAL_STORE=file before login to use the supported
31177
31415
  private-file fallback. See https://vtxmacro.com/insights#subscription-inference.
31178
31416
 
31179
31417
  Durable service:
31180
- vtx inference-host service install
31418
+ vtx inference-host login --instance codex-1
31419
+ vtx inference-host codex-login --instance codex-1
31420
+ vtx inference-host service install --instance codex-1
31421
+ vtx inference-host login --instance codex-2
31422
+ vtx inference-host codex-login --instance codex-2
31423
+ vtx inference-host service install --instance codex-2
31424
+ vtx inference-host service uninstall --instance codex-2
31181
31425
  vtx inference-host service <start|stop|status|logs|uninstall>
31182
31426
  `;
31183
- parsePositiveInteger = (raw, label) => {
31427
+ parseHostConcurrency = (raw, label) => {
31184
31428
  const value = Number(raw);
31185
- if (value !== 1) {
31186
- throw new Error(`${label} must be exactly 1 for the subscription-backed Codex host.`);
31429
+ if (!Number.isSafeInteger(value) || value < 1 || value > MAX_CODEX_HOST_CONCURRENCY) {
31430
+ throw new Error(`${label} must be an integer from 1 through ${MAX_CODEX_HOST_CONCURRENCY}.`);
31187
31431
  }
31188
31432
  return value;
31189
31433
  };
31190
31434
  parseInferenceHostArgs = (argv2, env) => {
31191
31435
  let json2 = String(env.VTX_OUTPUT_JSON || "").trim().toLowerCase() === "true";
31192
31436
  let once = false;
31193
- let maxConcurrency = parsePositiveInteger(
31194
- String(env.VTX_INFERENCE_HOST_MAX_CONCURRENCY || "1"),
31437
+ let maxConcurrency = parseHostConcurrency(
31438
+ String(env.VTX_INFERENCE_HOST_MAX_CONCURRENCY || DEFAULT_CODEX_HOST_CONCURRENCY),
31195
31439
  "VTX_INFERENCE_HOST_MAX_CONCURRENCY"
31196
31440
  );
31441
+ let instanceName = String(env.VTX_INFERENCE_HOST_INSTANCE || "default").trim();
31442
+ let instanceExplicit = Boolean(String(env.VTX_INFERENCE_HOST_INSTANCE || "").trim());
31197
31443
  let displayName = String(env.VTX_INFERENCE_HOST_DISPLAY_NAME || "").trim() || "Codex subscription host";
31198
31444
  let displayNameExplicit = Boolean(String(env.VTX_INFERENCE_HOST_DISPLAY_NAME || "").trim());
31199
31445
  let adapter = null;
@@ -31217,7 +31463,7 @@ Durable service:
31217
31463
  if (argument === "--max-concurrency") {
31218
31464
  const raw = argv2[index + 1];
31219
31465
  if (!raw) throw new Error("--max-concurrency requires a value.");
31220
- maxConcurrency = parsePositiveInteger(raw, "--max-concurrency");
31466
+ maxConcurrency = parseHostConcurrency(raw, "--max-concurrency");
31221
31467
  index += 1;
31222
31468
  continue;
31223
31469
  }
@@ -31229,7 +31475,7 @@ Durable service:
31229
31475
  index += 1;
31230
31476
  continue;
31231
31477
  }
31232
- if (["--adapter", "--model", "--model-label", "--effort", "--wait-seconds", "--lines", "--service-manifest"].includes(argument)) {
31478
+ if (["--adapter", "--model", "--model-label", "--effort", "--wait-seconds", "--lines", "--service-manifest", "--instance"].includes(argument)) {
31233
31479
  const raw = argv2[index + 1];
31234
31480
  if (!raw?.trim()) throw new Error(`${argument} requires a value.`);
31235
31481
  if (argument === "--adapter") adapter = raw.trim();
@@ -31249,6 +31495,10 @@ Durable service:
31249
31495
  }
31250
31496
  }
31251
31497
  if (argument === "--service-manifest") serviceManifestPath = resolve5(raw.trim());
31498
+ if (argument === "--instance") {
31499
+ instanceName = raw.trim();
31500
+ instanceExplicit = true;
31501
+ }
31252
31502
  index += 1;
31253
31503
  continue;
31254
31504
  }
@@ -31276,6 +31526,8 @@ Durable service:
31276
31526
  json: json2,
31277
31527
  once,
31278
31528
  maxConcurrency,
31529
+ instanceName,
31530
+ instanceExplicit,
31279
31531
  displayName,
31280
31532
  adapter,
31281
31533
  modelId,
@@ -31387,9 +31639,10 @@ Durable service:
31387
31639
  await clearInferenceHostLocalState(config2.statePath);
31388
31640
  };
31389
31641
  assertDurableServiceUninstalled = async (config2) => {
31390
- if (await readInferenceHostServiceManifest(`${config2.statePath}.service.json`)) {
31642
+ const manifest = await readInferenceHostServiceManifest(`${config2.supervisorStatePath}.service.json`);
31643
+ if (manifest?.workers.some((worker) => worker.instance_name === config2.instanceName)) {
31391
31644
  throw new Error(
31392
- "Uninstall the durable inference-host service before logout, revoke, or Codex logout."
31645
+ `Uninstall the durable inference-host service before changing credentials for instance ${config2.instanceName}.`
31393
31646
  );
31394
31647
  }
31395
31648
  };
@@ -32417,55 +32670,19 @@ Waiting for approval...
32417
32670
  if (!parsed.serviceManifestPath) throw new Error("Internal service manifest path is required.");
32418
32671
  const serviceManifest = await readInferenceHostServiceManifest(parsed.serviceManifestPath);
32419
32672
  if (!serviceManifest) throw new Error("Inference-host service manifest is missing.");
32420
- const serviceConfig = resolveInferenceHostConfig({
32673
+ const supervisorConfig = resolveInferenceHostConfig({
32421
32674
  ...env,
32422
- ...serviceManifest.runtime_environment
32675
+ ...serviceManifest.workers[0].runtime_environment
32423
32676
  });
32424
32677
  const serviceLock = await acquireInferenceHostProcessLock(
32425
- `${serviceConfig.processLockPath}.service`
32678
+ `${supervisorConfig.supervisorProcessLockPath}.service`
32426
32679
  );
32427
32680
  const cancellation = lifecycleCancellation(dependencies);
32428
32681
  try {
32429
32682
  await (dependencies.runServiceSupervisor ?? runInferenceHostServiceSupervisor)(
32430
32683
  parsed.serviceManifestPath,
32431
32684
  {
32432
- signal: cancellation.signal,
32433
- runWorker: async (manifest, signal) => {
32434
- const startedAt = Date.now();
32435
- const serviceEnv = { ...env, ...manifest.runtime_environment };
32436
- const serviceConfig2 = resolveInferenceHostConfig(serviceEnv);
32437
- const unregister = (abort) => {
32438
- const onAbort = () => abort("SIGTERM");
32439
- signal.addEventListener("abort", onAbort, { once: true });
32440
- return () => signal.removeEventListener("abort", onAbort);
32441
- };
32442
- const result2 = await runHost(serviceConfig2, {
32443
- ...parsed,
32444
- command: "run",
32445
- serviceAction: null,
32446
- serviceManifestPath: null,
32447
- displayName: manifest.display_name,
32448
- once: false
32449
- }, serviceEnv, {
32450
- ...dependencies,
32451
- registerLifecycleSignalHandlers: unregister
32452
- }, warnings);
32453
- let drainReason = null;
32454
- try {
32455
- const summary = JSON.parse(result2.stdout);
32456
- if (summary && typeof summary === "object" && !Array.isArray(summary) && typeof summary.drain_reason === "string" && /^[a-z0-9_]{1,96}$/u.test(
32457
- summary.drain_reason
32458
- )) {
32459
- drainReason = summary.drain_reason;
32460
- }
32461
- } catch {
32462
- }
32463
- return {
32464
- exitCode: result2.exitCode,
32465
- uptimeMs: Date.now() - startedAt,
32466
- drainReason
32467
- };
32468
- }
32685
+ signal: cancellation.signal
32469
32686
  }
32470
32687
  );
32471
32688
  return { exitCode: 0, stdout: "", stderr: "" };
@@ -32499,12 +32716,18 @@ Waiting for approval...
32499
32716
  throw new Error("Run vtx inference-host codex-login before installing the automated Codex service.");
32500
32717
  }
32501
32718
  const binary = await (dependencies.resolveBinary ?? resolvePinnedCodexBinary)(env);
32502
- await (dependencies.preflightCodex ?? preflightCodexSubscription)({
32719
+ const preflight = await (dependencies.preflightCodex ?? preflightCodexSubscription)({
32503
32720
  binary,
32504
32721
  codexHome: config2.codexHomePath,
32505
32722
  deadlineAtMs: Date.now() + 3e4
32506
32723
  });
32507
- const status = await manager.install({ adapter: "codex", displayName: parsed.displayName });
32724
+ const status = await manager.install({
32725
+ adapter: "codex",
32726
+ displayName: parsed.displayName,
32727
+ maxConcurrency: parsed.maxConcurrency,
32728
+ authenticatedAccountEmail: preflight?.authenticated_account_email ?? null,
32729
+ authenticatedAccountPlan: preflight?.authenticated_account_plan ?? null
32730
+ });
32508
32731
  return {
32509
32732
  exitCode: 0,
32510
32733
  stdout: render({ status: "service_installed", ...status }, parsed.json),
@@ -32530,7 +32753,8 @@ Waiting for approval...
32530
32753
  return { exitCode: lifecycleMatches ? 0 : 1, stdout: render(status, parsed.json), stderr: "" };
32531
32754
  }
32532
32755
  if (action === "uninstall") {
32533
- return { exitCode: 0, stdout: render({ status: "service_uninstalled", ...await manager.uninstall() }, parsed.json), stderr: "" };
32756
+ const status = parsed.instanceExplicit ? await manager.uninstallInstance(config2.instanceName) : await manager.uninstall();
32757
+ return { exitCode: 0, stdout: render({ status: "service_uninstalled", ...status }, parsed.json), stderr: "" };
32534
32758
  }
32535
32759
  throw new Error("Usage: vtx inference-host service <install|start|stop|status|logs|uninstall>.");
32536
32760
  };
@@ -32539,16 +32763,20 @@ Waiting for approval...
32539
32763
  );
32540
32764
  commandUsesInstalledServiceCredentials = (parsed) => parsed.command === "status" || parsed.command === "doctor" || parsed.command === "service" && parsed.serviceAction === "install";
32541
32765
  resolveInferenceHostCommandConfig = async (parsed, env) => {
32542
- const baseConfig = resolveInferenceHostConfig(env);
32766
+ const selectedEnv = {
32767
+ ...env,
32768
+ VTX_INFERENCE_HOST_INSTANCE: parsed.instanceName
32769
+ };
32770
+ const baseConfig = resolveInferenceHostConfig(selectedEnv);
32543
32771
  if (!commandUsesInstalledServiceCredentials(parsed) || hasExplicitCredentialStoreConfiguration(env)) {
32544
32772
  return baseConfig;
32545
32773
  }
32546
- const manifestPath = `${baseConfig.statePath}.service.json`;
32774
+ const manifestPath = `${baseConfig.supervisorStatePath}.service.json`;
32547
32775
  const manifest = await readInferenceHostServiceManifest(manifestPath);
32548
32776
  if (!manifest) return baseConfig;
32549
32777
  const installedConfig = resolveInferenceHostConfig({
32550
- ...env,
32551
- ...manifest.runtime_environment
32778
+ ...selectedEnv,
32779
+ ...manifest.workers.find((worker) => worker.instance_name === baseConfig.instanceName)?.runtime_environment
32552
32780
  });
32553
32781
  if (resolve5(installedConfig.statePath) !== resolve5(baseConfig.statePath)) {
32554
32782
  throw new Error(
@@ -49975,7 +50203,11 @@ function buildHeadlessExchangeConfig(input) {
49975
50203
  user_fills_ttl_seconds: readCacheNumberField("user_fills_ttl_seconds"),
49976
50204
  stale_if_429_max_age_seconds: readCacheNumberField("stale_if_429_max_age_seconds")
49977
50205
  },
49978
- client_runtime_hyperliquid_case_sensitive_symbols: Array.isArray(input.client_runtime_hyperliquid_case_sensitive_symbols) ? input.client_runtime_hyperliquid_case_sensitive_symbols : null
50206
+ client_runtime_hyperliquid_case_sensitive_symbols: Array.isArray(input.client_runtime_hyperliquid_case_sensitive_symbols) ? input.client_runtime_hyperliquid_case_sensitive_symbols : null,
50207
+ client_runtime_hyperliquid_perp_dexs: input.client_runtime_hyperliquid_perp_dexs ? {
50208
+ enabled: input.client_runtime_hyperliquid_perp_dexs.enabled === true,
50209
+ 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) : []
50210
+ } : void 0
49979
50211
  };
49980
50212
  }
49981
50213
  async function fetchLocalHyperliquidSnapshot(input) {
@@ -49991,7 +50223,7 @@ async function fetchLocalHyperliquidSnapshot(input) {
49991
50223
  symbol: input.symbol,
49992
50224
  tickerPrice: 0,
49993
50225
  leverage,
49994
- aggregatePerpDexs: false
50226
+ aggregatePerpDexs: true
49995
50227
  });
49996
50228
  const clientExchangeSnapshot = buildClientExchangeSnapshot({
49997
50229
  walletAddress,
@@ -51022,7 +51254,8 @@ var INFERENCE_HOST_VALUE_OPTIONS = /* @__PURE__ */ new Set([
51022
51254
  "--effort",
51023
51255
  "--wait-seconds",
51024
51256
  "--lines",
51025
- "--service-manifest"
51257
+ "--service-manifest",
51258
+ "--instance"
51026
51259
  ]);
51027
51260
  var isInferenceHostCliInvocation = (argv2) => {
51028
51261
  for (let index = 0; index < argv2.length; index += 1) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtxmacro/cli",
3
- "version": "2026.8.29",
3
+ "version": "2026.8.31",
4
4
  "description": "VTX Macro CLI, MCP server, and durable subscription inference host.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",