@vtxmacro/cli 2026.8.20 → 2026.8.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/bin/vtx.js +240 -63
  2. package/package.json +1 -1
package/bin/vtx.js CHANGED
@@ -38,7 +38,7 @@ var init_agent_cli_release = __esm({
38
38
  "agent-cli-release.json"() {
39
39
  agent_cli_release_default = {
40
40
  package_name: "@vtxmacro/cli",
41
- package_version: "2026.8.20",
41
+ package_version: "2026.8.21",
42
42
  codex_package_name: "@openai/codex",
43
43
  codex_version: "0.147.0",
44
44
  platforms: {
@@ -19837,6 +19837,9 @@ child.once('close', async () => {
19837
19837
  this.fatalListeners.add(listener);
19838
19838
  return () => this.fatalListeners.delete(listener);
19839
19839
  }
19840
+ isUsable() {
19841
+ return !this.closed && this.fatalError === null && this.process.exitCode === null;
19842
+ }
19840
19843
  async readAccountSnapshot(deadlineAtMs, signal, refreshToken = false) {
19841
19844
  const result2 = await this.request("account/read", { refreshToken }, {
19842
19845
  timeoutMs: Math.max(1, deadlineAtMs - Date.now()),
@@ -20196,6 +20199,30 @@ child.once('close', async () => {
20196
20199
  });
20197
20200
  const startedAt = Date.now();
20198
20201
  const unsubscribe = this.subscribe(({ method, params }) => {
20202
+ const eventThreadId = String(params.threadId || "");
20203
+ const scopedMethod = method.startsWith("thread/") || method.startsWith("turn/") || method.startsWith("item/") || method.startsWith("rawResponse") || method === "model/rerouted";
20204
+ if (scopedMethod && !eventThreadId) {
20205
+ terminalReject(new CodexAppServerError({
20206
+ message: "Codex notification omitted its thread scope.",
20207
+ category: "adapter",
20208
+ code: "missing_thread_notification_scope",
20209
+ retryable: false,
20210
+ dispatchOutcome: requestWritten ? turnId ? "confirmed_dispatched" : "outcome_unknown" : "not_dispatched"
20211
+ }));
20212
+ return;
20213
+ }
20214
+ if (scopedMethod && eventThreadId !== request.thread.threadId) {
20215
+ if (!this.threads.has(eventThreadId)) {
20216
+ terminalReject(new CodexAppServerError({
20217
+ message: "Codex notification crossed thread scope.",
20218
+ category: "adapter",
20219
+ code: "cross_thread_notification",
20220
+ retryable: false,
20221
+ dispatchOutcome: turnId ? "confirmed_dispatched" : "outcome_unknown"
20222
+ }));
20223
+ }
20224
+ return;
20225
+ }
20199
20226
  if (forbiddenMethod(method)) {
20200
20227
  terminalReject(new CodexAppServerError({
20201
20228
  message: "Codex attempted to use forbidden child authority.",
@@ -20231,18 +20258,6 @@ child.once('close', async () => {
20231
20258
  return;
20232
20259
  }
20233
20260
  }
20234
- const eventThreadId = String(params.threadId || "");
20235
- const scopedMethod = method.startsWith("thread/") || method.startsWith("turn/") || method.startsWith("item/") || method.startsWith("rawResponse") || method === "model/rerouted";
20236
- if (scopedMethod && eventThreadId !== request.thread.threadId) {
20237
- terminalReject(new CodexAppServerError({
20238
- message: "Codex notification crossed thread scope.",
20239
- category: "adapter",
20240
- code: "cross_thread_notification",
20241
- retryable: false,
20242
- dispatchOutcome: turnId ? "confirmed_dispatched" : "outcome_unknown"
20243
- }));
20244
- return;
20245
- }
20246
20261
  const eventTurn = objectOrNull(params.turn);
20247
20262
  const eventTurnId = String(params.turnId || eventTurn?.id || "");
20248
20263
  const turnScopedMethod = method.startsWith("turn/") || method.startsWith("item/") || method.startsWith("rawResponse") || method === "model/rerouted";
@@ -20884,7 +20899,7 @@ var init_codex_adapter = __esm({
20884
20899
  context.addIssue({ code: "custom", message: "Codex recovery thread identity is incomplete." });
20885
20900
  }
20886
20901
  const guardianManaged = value.processToken !== null || value.processReceiptPath !== null;
20887
- if (value.processToken === null !== (value.processReceiptPath === null) || value.processState === "unmanaged" === guardianManaged || value.cleanupConfirmed && !["unmanaged", "terminated"].includes(value.processState)) {
20902
+ if (value.processToken === null !== (value.processReceiptPath === null) || value.processState === "unmanaged" === guardianManaged || value.cleanupConfirmed && value.processState === "spawn_intent") {
20888
20903
  context.addIssue({ code: "custom", message: "Codex recovery process identity is invalid." });
20889
20904
  }
20890
20905
  });
@@ -21293,8 +21308,114 @@ var init_codex_adapter = __esm({
21293
21308
  CodexSubscriptionAdapter = class {
21294
21309
  constructor(dependencies = {}) {
21295
21310
  this.inFlightAttempts = /* @__PURE__ */ new Map();
21311
+ this.durableSession = null;
21312
+ this.durableSessionStart = null;
21313
+ this.closing = false;
21314
+ this.adapterClosePromise = null;
21296
21315
  this.dependencies = dependencies;
21297
21316
  }
21317
+ async startDurableSession(codexHome, deadlineAtMs) {
21318
+ const binary = await (this.dependencies.resolveBinary ?? resolvePinnedCodexBinary)();
21319
+ const guardianManaged = Boolean(
21320
+ this.dependencies.recoveryHooks && !this.dependencies.spawnProcess
21321
+ );
21322
+ const bootIdentity = guardianManaged ? await (this.dependencies.readBootIdentity ?? readInferenceSystemBootIdentity)() : null;
21323
+ const processToken = guardianManaged ? randomBytes3(32).toString("hex") : null;
21324
+ const guardianReceiptRoot = guardianManaged ? this.dependencies.guardianReceiptRoot ?? join4(tmpdir2(), "vtx-codex-guardian-receipts") : null;
21325
+ if (guardianReceiptRoot) await ensureInferencePrivateDirectory(guardianReceiptRoot);
21326
+ const processReceiptPath = guardianReceiptRoot && processToken ? join4(guardianReceiptRoot, `${processToken}.json`) : null;
21327
+ const session = await CodexAppServerSession.start({
21328
+ binary,
21329
+ codexHome,
21330
+ deadlineAtMs,
21331
+ spawnProcess: this.dependencies.spawnProcess,
21332
+ guardian: processToken && processReceiptPath ? { processToken, receiptPath: processReceiptPath, bootIdentity: bootIdentity ?? void 0 } : void 0
21333
+ });
21334
+ return {
21335
+ session,
21336
+ codexHome,
21337
+ processToken,
21338
+ processReceiptPath,
21339
+ bootIdentity,
21340
+ terminated: false,
21341
+ closePromise: null
21342
+ };
21343
+ }
21344
+ async acquireDurableSession(codexHome, deadlineAtMs, signal) {
21345
+ if (this.closing) {
21346
+ throw new CodexAppServerError({
21347
+ message: "Codex adapter is closing.",
21348
+ category: "transport",
21349
+ code: "transport_closed",
21350
+ retryable: true
21351
+ });
21352
+ }
21353
+ if (this.durableSession && this.durableSession.codexHome === codexHome && this.durableSession.session.isUsable()) return this.durableSession;
21354
+ if (this.durableSession && this.durableSession.codexHome !== codexHome) {
21355
+ await this.invalidateDurableSession(this.durableSession);
21356
+ }
21357
+ if (this.durableSession && !this.durableSession.session.isUsable()) {
21358
+ await this.invalidateDurableSession(this.durableSession);
21359
+ }
21360
+ if (!this.durableSessionStart) {
21361
+ const start2 = this.startDurableSession(codexHome, deadlineAtMs);
21362
+ this.durableSessionStart = start2;
21363
+ void start2.then((session) => {
21364
+ if (this.durableSessionStart === start2) this.durableSession = session;
21365
+ }).finally(() => {
21366
+ if (this.durableSessionStart === start2) this.durableSessionStart = null;
21367
+ }).catch(() => void 0);
21368
+ }
21369
+ const start = this.durableSessionStart;
21370
+ return await new Promise((resolveSession, reject) => {
21371
+ let settled = false;
21372
+ const finish = (callback) => {
21373
+ if (settled) return;
21374
+ settled = true;
21375
+ clearTimeout(timer);
21376
+ signal?.removeEventListener("abort", onAbort);
21377
+ callback();
21378
+ };
21379
+ const onAbort = () => finish(() => reject(new CodexAppServerError({
21380
+ message: "Codex session acquisition was cancelled.",
21381
+ category: "cancelled",
21382
+ code: "cancelled",
21383
+ retryable: false
21384
+ })));
21385
+ const timer = setTimeout(() => finish(() => reject(new CodexAppServerError({
21386
+ message: "Codex session acquisition exceeded the immutable deadline.",
21387
+ category: "timeout",
21388
+ code: "deadline_exceeded",
21389
+ retryable: false
21390
+ }))), Math.max(1, deadlineAtMs - Date.now()));
21391
+ signal?.addEventListener("abort", onAbort, { once: true });
21392
+ if (signal?.aborted) {
21393
+ onAbort();
21394
+ return;
21395
+ }
21396
+ void start.then(
21397
+ (session) => finish(() => resolveSession(session)),
21398
+ (error48) => finish(() => reject(error48))
21399
+ );
21400
+ });
21401
+ }
21402
+ async invalidateDurableSession(session) {
21403
+ if (this.durableSession === session) this.durableSession = null;
21404
+ if (!session.closePromise) {
21405
+ session.closePromise = (async () => {
21406
+ await session.session.close();
21407
+ if (session.processToken && session.processReceiptPath) {
21408
+ await waitForCodexGuardianState(
21409
+ { processToken: session.processToken, receiptPath: session.processReceiptPath },
21410
+ "terminated",
21411
+ Date.now() + 5e3
21412
+ );
21413
+ }
21414
+ session.terminated = true;
21415
+ })();
21416
+ }
21417
+ await session.closePromise;
21418
+ }
21298
21419
  runAttempt(input) {
21299
21420
  const active = this.inFlightAttempts.get(input.attemptId);
21300
21421
  if (active) {
@@ -21352,10 +21473,37 @@ var init_codex_adapter = __esm({
21352
21473
  dispatchOutcome: checkpoint.dispatchOutcome
21353
21474
  });
21354
21475
  }
21355
- await rm3(checkpoint.processReceiptPath, { force: true });
21476
+ const receipt = await readCodexGuardianReceipt(checkpoint.processReceiptPath);
21477
+ if (checkpoint.processState === "terminated" || receipt === null || receipt.state === "terminated") {
21478
+ await rm3(checkpoint.processReceiptPath, { force: true });
21479
+ }
21356
21480
  }
21357
21481
  await recoveryHooks.clear(attemptId);
21358
21482
  }
21483
+ async close() {
21484
+ if (!this.adapterClosePromise) {
21485
+ this.closing = true;
21486
+ this.adapterClosePromise = (async () => {
21487
+ const sessions = /* @__PURE__ */ new Set();
21488
+ if (this.durableSession) sessions.add(this.durableSession);
21489
+ const starting = this.durableSessionStart;
21490
+ if (starting) {
21491
+ const started = await starting.catch(() => null);
21492
+ if (started) sessions.add(started);
21493
+ }
21494
+ await Promise.all(
21495
+ [...sessions].map((session) => this.invalidateDurableSession(session))
21496
+ );
21497
+ await Promise.allSettled(
21498
+ [...this.inFlightAttempts.values()].map((attempt) => attempt.promise)
21499
+ );
21500
+ for (const session of sessions) {
21501
+ if (session.processReceiptPath) await rm3(session.processReceiptPath, { force: true });
21502
+ }
21503
+ })();
21504
+ }
21505
+ await this.adapterClosePromise;
21506
+ }
21359
21507
  async executeAttempt(input) {
21360
21508
  const validatedInput = validateAttemptInput(input);
21361
21509
  const recoveryHooks = this.dependencies.recoveryHooks;
@@ -21406,6 +21554,7 @@ var init_codex_adapter = __esm({
21406
21554
  });
21407
21555
  }
21408
21556
  let resources = null;
21557
+ let sessionLease = null;
21409
21558
  let session = null;
21410
21559
  let threadId = null;
21411
21560
  let baseCheckpoint = null;
@@ -21413,22 +21562,20 @@ var init_codex_adapter = __esm({
21413
21562
  let currentDispatchOutcome = "not_dispatched";
21414
21563
  let completedResult = null;
21415
21564
  let caughtError = null;
21565
+ let sessionInvalidationError = null;
21416
21566
  try {
21417
- const binary = await (this.dependencies.resolveBinary ?? resolvePinnedCodexBinary)();
21418
21567
  resources = await (this.dependencies.createAttemptResources ?? ((attemptId) => createIsolatedCodexAttemptResources(
21419
21568
  attemptId,
21420
21569
  this.dependencies.codexHome
21421
21570
  )))(input.attemptId);
21422
21571
  await access2(resources.codexHome, fsConstants2.R_OK | fsConstants2.W_OK);
21423
21572
  await access2(resources.workspacePath, fsConstants2.R_OK);
21424
- const guardianManaged = Boolean(recoveryHooks && !this.dependencies.spawnProcess);
21425
- const bootIdentity = guardianManaged ? await (this.dependencies.readBootIdentity ?? readInferenceSystemBootIdentity)() : null;
21426
- const processToken = guardianManaged ? randomBytes3(32).toString("hex") : null;
21427
- const guardianReceiptRoot = guardianManaged ? this.dependencies.guardianReceiptRoot ?? join4(tmpdir2(), "vtx-codex-guardian-receipts") : null;
21428
- if (guardianReceiptRoot) {
21429
- await ensureInferencePrivateDirectory(guardianReceiptRoot);
21430
- }
21431
- const processReceiptPath = guardianReceiptRoot && processToken ? join4(guardianReceiptRoot, `${processToken}.json`) : null;
21573
+ sessionLease = await this.acquireDurableSession(
21574
+ resources.codexHome,
21575
+ input.deadlineAtMs,
21576
+ input.signal
21577
+ );
21578
+ session = sessionLease.session;
21432
21579
  baseCheckpoint = {
21433
21580
  schemaVersion: "vtx_codex_attempt_recovery_v1",
21434
21581
  attemptId: input.attemptId,
@@ -21438,10 +21585,10 @@ var init_codex_adapter = __esm({
21438
21585
  workspacePath: resources.workspacePath,
21439
21586
  threadId: null,
21440
21587
  threadPath: null,
21441
- processToken,
21442
- processReceiptPath,
21443
- processState: guardianManaged ? "spawn_intent" : "unmanaged",
21444
- bootIdentity,
21588
+ processToken: sessionLease.processToken,
21589
+ processReceiptPath: sessionLease.processReceiptPath,
21590
+ processState: sessionLease.processToken ? "running" : "unmanaged",
21591
+ bootIdentity: sessionLease.bootIdentity,
21445
21592
  cleanupConfirmed: false,
21446
21593
  adapterRequestId: null,
21447
21594
  adapterResponseId: null,
@@ -21450,19 +21597,6 @@ var init_codex_adapter = __esm({
21450
21597
  };
21451
21598
  latestCheckpoint = baseCheckpoint;
21452
21599
  await recoveryHooks?.save(baseCheckpoint);
21453
- session = await CodexAppServerSession.start({
21454
- binary,
21455
- codexHome: resources.codexHome,
21456
- deadlineAtMs: input.deadlineAtMs,
21457
- signal: input.signal,
21458
- spawnProcess: this.dependencies.spawnProcess,
21459
- guardian: processToken && processReceiptPath ? { processToken, receiptPath: processReceiptPath, bootIdentity: bootIdentity ?? void 0 } : void 0
21460
- });
21461
- if (guardianManaged) {
21462
- baseCheckpoint = { ...baseCheckpoint, processState: "running" };
21463
- latestCheckpoint = baseCheckpoint;
21464
- await recoveryHooks?.save(baseCheckpoint);
21465
- }
21466
21600
  await session.requireManagedChatGptAuth(input.deadlineAtMs, input.signal);
21467
21601
  await session.requireModel(
21468
21602
  input.requestedModel,
@@ -21522,6 +21656,20 @@ var init_codex_adapter = __esm({
21522
21656
  return result2;
21523
21657
  } catch (error48) {
21524
21658
  caughtError = error48;
21659
+ if (sessionLease && (!sessionLease.session.isUsable() || error48 instanceof CodexAppServerError && (error48.category === "transport" || [
21660
+ "rpc_rejected",
21661
+ "invalid_rpc_result",
21662
+ "rpc_timeout",
21663
+ "invalid_thread_provenance",
21664
+ "thread_cleanup_identity_missing",
21665
+ "thread_delete_unconfirmed"
21666
+ ].includes(error48.code)))) {
21667
+ try {
21668
+ await this.invalidateDurableSession(sessionLease);
21669
+ } catch (invalidationError) {
21670
+ sessionInvalidationError = invalidationError;
21671
+ }
21672
+ }
21525
21673
  if (error48 instanceof CodexAppServerError) {
21526
21674
  const dispatchOutcome = error48.dispatchOutcome === "not_dispatched" ? currentDispatchOutcome : error48.dispatchOutcome;
21527
21675
  if (baseCheckpoint && !completedResult) {
@@ -21584,7 +21732,7 @@ var init_codex_adapter = __esm({
21584
21732
  }
21585
21733
  throw classified;
21586
21734
  } finally {
21587
- let cleanupError = null;
21735
+ let cleanupError = sessionInvalidationError;
21588
21736
  if (session && threadId) {
21589
21737
  try {
21590
21738
  await session.deleteThread(threadId);
@@ -21592,23 +21740,9 @@ var init_codex_adapter = __esm({
21592
21740
  cleanupError = error48;
21593
21741
  }
21594
21742
  }
21595
- try {
21596
- await session?.close();
21597
- } catch (error48) {
21598
- cleanupError ??= error48;
21599
- }
21600
- if (latestCheckpoint && recoveryHooks) {
21743
+ if (sessionLease && cleanupError) {
21601
21744
  try {
21602
- if (latestCheckpoint.processToken && latestCheckpoint.processReceiptPath) {
21603
- await waitForCodexGuardianState(
21604
- {
21605
- processToken: latestCheckpoint.processToken,
21606
- receiptPath: latestCheckpoint.processReceiptPath
21607
- },
21608
- "terminated",
21609
- Date.now() + 5e3
21610
- );
21611
- }
21745
+ await this.invalidateDurableSession(sessionLease);
21612
21746
  } catch (error48) {
21613
21747
  cleanupError ??= error48;
21614
21748
  }
@@ -21623,7 +21757,7 @@ var init_codex_adapter = __esm({
21623
21757
  if (latestCheckpoint && recoveryHooks && cleanupError === null) {
21624
21758
  latestCheckpoint = {
21625
21759
  ...latestCheckpoint,
21626
- processState: latestCheckpoint.processToken ? "terminated" : "unmanaged",
21760
+ processState: latestCheckpoint.processToken ? sessionLease?.terminated ? "terminated" : "running" : "unmanaged",
21627
21761
  cleanupConfirmed: true
21628
21762
  };
21629
21763
  try {
@@ -29377,7 +29511,11 @@ var init_runner = __esm({
29377
29511
  } finally {
29378
29512
  removeAbortListener();
29379
29513
  unregisterSignals();
29380
- await lock2.release();
29514
+ try {
29515
+ await this.dependencies.codexAdapter.close?.();
29516
+ } finally {
29517
+ await lock2.release();
29518
+ }
29381
29519
  }
29382
29520
  }
29383
29521
  async persistReceipt(receipt, now) {
@@ -30382,11 +30520,18 @@ ${result2.stderr}`)) {
30382
30520
  const startedAt = Date.now();
30383
30521
  const log = createWriteStream(manifest.log_path, { flags: "a", mode: 384 });
30384
30522
  return await new Promise((resolvePromise, reject) => {
30523
+ let stdout = "";
30385
30524
  const child = spawn5(manifest.executable, args, {
30386
30525
  env: { ...process.env, ...manifest.runtime_environment },
30387
30526
  windowsHide: true,
30388
- stdio: ["ignore", log, log]
30527
+ stdio: ["ignore", "pipe", "pipe"]
30389
30528
  });
30529
+ child.stdout.on("data", (chunk) => {
30530
+ const text = chunk.toString("utf8");
30531
+ log.write(text);
30532
+ stdout = `${stdout}${text}`.slice(-65536);
30533
+ });
30534
+ child.stderr.on("data", (chunk) => log.write(chunk));
30390
30535
  const onAbort = () => child.kill("SIGTERM");
30391
30536
  signal.addEventListener("abort", onAbort, { once: true });
30392
30537
  child.once("error", (error48) => {
@@ -30397,7 +30542,24 @@ ${result2.stderr}`)) {
30397
30542
  child.once("exit", (code) => {
30398
30543
  signal.removeEventListener("abort", onAbort);
30399
30544
  log.end();
30400
- resolvePromise({ exitCode: code ?? 1, uptimeMs: Date.now() - startedAt });
30545
+ let drainReason = null;
30546
+ for (const line of stdout.trim().split("\n").reverse()) {
30547
+ try {
30548
+ const parsed = JSON.parse(line);
30549
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed) && typeof parsed.drain_reason === "string" && /^[a-z0-9_]{1,96}$/u.test(
30550
+ parsed.drain_reason
30551
+ )) {
30552
+ drainReason = parsed.drain_reason;
30553
+ break;
30554
+ }
30555
+ } catch {
30556
+ }
30557
+ }
30558
+ resolvePromise({
30559
+ exitCode: code ?? 1,
30560
+ uptimeMs: Date.now() - startedAt,
30561
+ drainReason
30562
+ });
30401
30563
  });
30402
30564
  });
30403
30565
  };
@@ -30447,6 +30609,7 @@ ${result2.stderr}`)) {
30447
30609
  await appendServiceLog(manifest.log_path, "worker_exited", {
30448
30610
  exit_code: result2.exitCode,
30449
30611
  uptime_ms: result2.uptimeMs,
30612
+ drain_reason: result2.drainReason ?? null,
30450
30613
  retry_after_ms: retryAfterMs
30451
30614
  });
30452
30615
  await sleep4(retryAfterMs);
@@ -31932,7 +32095,21 @@ Waiting for approval...
31932
32095
  ...dependencies,
31933
32096
  registerLifecycleSignalHandlers: unregister
31934
32097
  }, warnings);
31935
- return { exitCode: result2.exitCode, uptimeMs: Date.now() - startedAt };
32098
+ let drainReason = null;
32099
+ try {
32100
+ const summary = JSON.parse(result2.stdout);
32101
+ if (summary && typeof summary === "object" && !Array.isArray(summary) && typeof summary.drain_reason === "string" && /^[a-z0-9_]{1,96}$/u.test(
32102
+ summary.drain_reason
32103
+ )) {
32104
+ drainReason = summary.drain_reason;
32105
+ }
32106
+ } catch {
32107
+ }
32108
+ return {
32109
+ exitCode: result2.exitCode,
32110
+ uptimeMs: Date.now() - startedAt,
32111
+ drainReason
32112
+ };
31936
32113
  }
31937
32114
  }
31938
32115
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtxmacro/cli",
3
- "version": "2026.8.20",
3
+ "version": "2026.8.21",
4
4
  "description": "VTX Macro CLI, MCP server, and durable subscription inference host.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",