@vtxmacro/cli 2026.8.20 → 2026.8.22

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 +341 -103
  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.22",
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,115 @@ var init_codex_adapter = __esm({
21293
21308
  CodexSubscriptionAdapter = class {
21294
21309
  constructor(dependencies = {}) {
21295
21310
  this.inFlightAttempts = /* @__PURE__ */ new Map();
21311
+ this.attemptCleanups = /* @__PURE__ */ new Map();
21312
+ this.durableSession = null;
21313
+ this.durableSessionStart = null;
21314
+ this.closing = false;
21315
+ this.adapterClosePromise = null;
21296
21316
  this.dependencies = dependencies;
21297
21317
  }
21318
+ async startDurableSession(codexHome, deadlineAtMs) {
21319
+ const binary = await (this.dependencies.resolveBinary ?? resolvePinnedCodexBinary)();
21320
+ const guardianManaged = Boolean(
21321
+ this.dependencies.recoveryHooks && !this.dependencies.spawnProcess
21322
+ );
21323
+ const bootIdentity = guardianManaged ? await (this.dependencies.readBootIdentity ?? readInferenceSystemBootIdentity)() : null;
21324
+ const processToken = guardianManaged ? randomBytes3(32).toString("hex") : null;
21325
+ const guardianReceiptRoot = guardianManaged ? this.dependencies.guardianReceiptRoot ?? join4(tmpdir2(), "vtx-codex-guardian-receipts") : null;
21326
+ if (guardianReceiptRoot) await ensureInferencePrivateDirectory(guardianReceiptRoot);
21327
+ const processReceiptPath = guardianReceiptRoot && processToken ? join4(guardianReceiptRoot, `${processToken}.json`) : null;
21328
+ const session = await CodexAppServerSession.start({
21329
+ binary,
21330
+ codexHome,
21331
+ deadlineAtMs,
21332
+ spawnProcess: this.dependencies.spawnProcess,
21333
+ guardian: processToken && processReceiptPath ? { processToken, receiptPath: processReceiptPath, bootIdentity: bootIdentity ?? void 0 } : void 0
21334
+ });
21335
+ return {
21336
+ session,
21337
+ codexHome,
21338
+ processToken,
21339
+ processReceiptPath,
21340
+ bootIdentity,
21341
+ terminated: false,
21342
+ closePromise: null
21343
+ };
21344
+ }
21345
+ async acquireDurableSession(codexHome, deadlineAtMs, signal) {
21346
+ if (this.closing) {
21347
+ throw new CodexAppServerError({
21348
+ message: "Codex adapter is closing.",
21349
+ category: "transport",
21350
+ code: "transport_closed",
21351
+ retryable: true
21352
+ });
21353
+ }
21354
+ if (this.durableSession && this.durableSession.codexHome === codexHome && this.durableSession.session.isUsable()) return this.durableSession;
21355
+ if (this.durableSession && this.durableSession.codexHome !== codexHome) {
21356
+ await this.invalidateDurableSession(this.durableSession);
21357
+ }
21358
+ if (this.durableSession && !this.durableSession.session.isUsable()) {
21359
+ await this.invalidateDurableSession(this.durableSession);
21360
+ }
21361
+ if (!this.durableSessionStart) {
21362
+ const start2 = this.startDurableSession(codexHome, deadlineAtMs);
21363
+ this.durableSessionStart = start2;
21364
+ void start2.then((session) => {
21365
+ if (this.durableSessionStart === start2) this.durableSession = session;
21366
+ }).finally(() => {
21367
+ if (this.durableSessionStart === start2) this.durableSessionStart = null;
21368
+ }).catch(() => void 0);
21369
+ }
21370
+ const start = this.durableSessionStart;
21371
+ return await new Promise((resolveSession, reject) => {
21372
+ let settled = false;
21373
+ const finish = (callback) => {
21374
+ if (settled) return;
21375
+ settled = true;
21376
+ clearTimeout(timer);
21377
+ signal?.removeEventListener("abort", onAbort);
21378
+ callback();
21379
+ };
21380
+ const onAbort = () => finish(() => reject(new CodexAppServerError({
21381
+ message: "Codex session acquisition was cancelled.",
21382
+ category: "cancelled",
21383
+ code: "cancelled",
21384
+ retryable: false
21385
+ })));
21386
+ const timer = setTimeout(() => finish(() => reject(new CodexAppServerError({
21387
+ message: "Codex session acquisition exceeded the immutable deadline.",
21388
+ category: "timeout",
21389
+ code: "deadline_exceeded",
21390
+ retryable: false
21391
+ }))), Math.max(1, deadlineAtMs - Date.now()));
21392
+ signal?.addEventListener("abort", onAbort, { once: true });
21393
+ if (signal?.aborted) {
21394
+ onAbort();
21395
+ return;
21396
+ }
21397
+ void start.then(
21398
+ (session) => finish(() => resolveSession(session)),
21399
+ (error48) => finish(() => reject(error48))
21400
+ );
21401
+ });
21402
+ }
21403
+ async invalidateDurableSession(session) {
21404
+ if (this.durableSession === session) this.durableSession = null;
21405
+ if (!session.closePromise) {
21406
+ session.closePromise = (async () => {
21407
+ await session.session.close();
21408
+ if (session.processToken && session.processReceiptPath) {
21409
+ await waitForCodexGuardianState(
21410
+ { processToken: session.processToken, receiptPath: session.processReceiptPath },
21411
+ "terminated",
21412
+ Date.now() + 5e3
21413
+ );
21414
+ }
21415
+ session.terminated = true;
21416
+ })();
21417
+ }
21418
+ await session.closePromise;
21419
+ }
21298
21420
  runAttempt(input) {
21299
21421
  const active = this.inFlightAttempts.get(input.attemptId);
21300
21422
  if (active) {
@@ -21309,15 +21431,21 @@ var init_codex_adapter = __esm({
21309
21431
  }
21310
21432
  return active.promise;
21311
21433
  }
21312
- const promise2 = this.executeAttempt(input).finally(() => {
21313
- if (this.inFlightAttempts.get(input.attemptId)?.promise === promise2) {
21314
- this.inFlightAttempts.delete(input.attemptId);
21315
- }
21316
- });
21434
+ const promise2 = this.executeAttempt(input);
21317
21435
  this.inFlightAttempts.set(input.attemptId, {
21318
21436
  inputSha256: input.inputSha256,
21319
21437
  promise: promise2
21320
21438
  });
21439
+ void promise2.then(async () => {
21440
+ await this.attemptCleanups.get(input.attemptId)?.catch(() => void 0);
21441
+ if (this.inFlightAttempts.get(input.attemptId)?.promise === promise2) {
21442
+ this.inFlightAttempts.delete(input.attemptId);
21443
+ }
21444
+ }, () => {
21445
+ if (this.inFlightAttempts.get(input.attemptId)?.promise === promise2) {
21446
+ this.inFlightAttempts.delete(input.attemptId);
21447
+ }
21448
+ }).catch(() => void 0);
21321
21449
  return promise2;
21322
21450
  }
21323
21451
  async acknowledgeAttempt(attemptId) {
@@ -21329,8 +21457,27 @@ var init_codex_adapter = __esm({
21329
21457
  retryable: false
21330
21458
  });
21331
21459
  }
21460
+ const activeCleanup = this.attemptCleanups.get(attemptId);
21461
+ if (activeCleanup) {
21462
+ try {
21463
+ await activeCleanup;
21464
+ } catch (error48) {
21465
+ throw new CodexAppServerError({
21466
+ message: "Codex attempt cleanup could not be confirmed.",
21467
+ category: "adapter",
21468
+ code: "attempt_cleanup_unconfirmed",
21469
+ retryable: false,
21470
+ cause: error48
21471
+ });
21472
+ }
21473
+ }
21332
21474
  const recoveryHooks = this.dependencies.recoveryHooks;
21333
- if (!recoveryHooks) return;
21475
+ if (!recoveryHooks) {
21476
+ if (activeCleanup && this.attemptCleanups.get(attemptId) === activeCleanup) {
21477
+ this.attemptCleanups.delete(attemptId);
21478
+ }
21479
+ return;
21480
+ }
21334
21481
  const checkpoint = await recoveryHooks.load(attemptId);
21335
21482
  if (checkpoint && !checkpoint.cleanupConfirmed) {
21336
21483
  throw new CodexAppServerError({
@@ -21352,9 +21499,60 @@ var init_codex_adapter = __esm({
21352
21499
  dispatchOutcome: checkpoint.dispatchOutcome
21353
21500
  });
21354
21501
  }
21355
- await rm3(checkpoint.processReceiptPath, { force: true });
21502
+ const receipt = await readCodexGuardianReceipt(checkpoint.processReceiptPath);
21503
+ if (checkpoint.processState === "terminated" || receipt === null || receipt.state === "terminated") {
21504
+ await rm3(checkpoint.processReceiptPath, { force: true });
21505
+ }
21356
21506
  }
21357
21507
  await recoveryHooks.clear(attemptId);
21508
+ if (activeCleanup && this.attemptCleanups.get(attemptId) === activeCleanup) {
21509
+ this.attemptCleanups.delete(attemptId);
21510
+ }
21511
+ }
21512
+ async close() {
21513
+ if (!this.adapterClosePromise) {
21514
+ this.closing = true;
21515
+ this.adapterClosePromise = (async () => {
21516
+ const sessions = /* @__PURE__ */ new Set();
21517
+ if (this.durableSession) sessions.add(this.durableSession);
21518
+ const starting = this.durableSessionStart;
21519
+ if (starting) {
21520
+ const started = await starting.catch(() => null);
21521
+ if (started) sessions.add(started);
21522
+ }
21523
+ await Promise.all(
21524
+ [...sessions].map((session) => this.invalidateDurableSession(session))
21525
+ );
21526
+ await Promise.allSettled(
21527
+ [...this.inFlightAttempts.values()].map((attempt) => attempt.promise)
21528
+ );
21529
+ await Promise.allSettled(
21530
+ [...this.attemptCleanups.values()]
21531
+ );
21532
+ let unconfirmedReceiptPaths = /* @__PURE__ */ new Set();
21533
+ const recoveryHooks = this.dependencies.recoveryHooks;
21534
+ if (recoveryHooks) {
21535
+ if (!recoveryHooks.loadAll) {
21536
+ unconfirmedReceiptPaths = null;
21537
+ } else {
21538
+ try {
21539
+ const checkpoints = await recoveryHooks.loadAll();
21540
+ unconfirmedReceiptPaths = new Set(
21541
+ Object.values(checkpoints).filter((checkpoint) => !checkpoint.cleanupConfirmed && checkpoint.processReceiptPath !== null).map((checkpoint) => resolve3(checkpoint.processReceiptPath))
21542
+ );
21543
+ } catch {
21544
+ unconfirmedReceiptPaths = null;
21545
+ }
21546
+ }
21547
+ }
21548
+ for (const session of sessions) {
21549
+ if (session.processReceiptPath && unconfirmedReceiptPaths !== null && !unconfirmedReceiptPaths.has(resolve3(session.processReceiptPath))) {
21550
+ await rm3(session.processReceiptPath, { force: true });
21551
+ }
21552
+ }
21553
+ })();
21554
+ }
21555
+ await this.adapterClosePromise;
21358
21556
  }
21359
21557
  async executeAttempt(input) {
21360
21558
  const validatedInput = validateAttemptInput(input);
@@ -21406,6 +21604,7 @@ var init_codex_adapter = __esm({
21406
21604
  });
21407
21605
  }
21408
21606
  let resources = null;
21607
+ let sessionLease = null;
21409
21608
  let session = null;
21410
21609
  let threadId = null;
21411
21610
  let baseCheckpoint = null;
@@ -21413,22 +21612,20 @@ var init_codex_adapter = __esm({
21413
21612
  let currentDispatchOutcome = "not_dispatched";
21414
21613
  let completedResult = null;
21415
21614
  let caughtError = null;
21615
+ let sessionInvalidationError = null;
21416
21616
  try {
21417
- const binary = await (this.dependencies.resolveBinary ?? resolvePinnedCodexBinary)();
21418
21617
  resources = await (this.dependencies.createAttemptResources ?? ((attemptId) => createIsolatedCodexAttemptResources(
21419
21618
  attemptId,
21420
21619
  this.dependencies.codexHome
21421
21620
  )))(input.attemptId);
21422
21621
  await access2(resources.codexHome, fsConstants2.R_OK | fsConstants2.W_OK);
21423
21622
  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;
21623
+ sessionLease = await this.acquireDurableSession(
21624
+ resources.codexHome,
21625
+ input.deadlineAtMs,
21626
+ input.signal
21627
+ );
21628
+ session = sessionLease.session;
21432
21629
  baseCheckpoint = {
21433
21630
  schemaVersion: "vtx_codex_attempt_recovery_v1",
21434
21631
  attemptId: input.attemptId,
@@ -21438,10 +21635,10 @@ var init_codex_adapter = __esm({
21438
21635
  workspacePath: resources.workspacePath,
21439
21636
  threadId: null,
21440
21637
  threadPath: null,
21441
- processToken,
21442
- processReceiptPath,
21443
- processState: guardianManaged ? "spawn_intent" : "unmanaged",
21444
- bootIdentity,
21638
+ processToken: sessionLease.processToken,
21639
+ processReceiptPath: sessionLease.processReceiptPath,
21640
+ processState: sessionLease.processToken ? "running" : "unmanaged",
21641
+ bootIdentity: sessionLease.bootIdentity,
21445
21642
  cleanupConfirmed: false,
21446
21643
  adapterRequestId: null,
21447
21644
  adapterResponseId: null,
@@ -21450,19 +21647,6 @@ var init_codex_adapter = __esm({
21450
21647
  };
21451
21648
  latestCheckpoint = baseCheckpoint;
21452
21649
  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
21650
  await session.requireManagedChatGptAuth(input.deadlineAtMs, input.signal);
21467
21651
  await session.requireModel(
21468
21652
  input.requestedModel,
@@ -21517,11 +21701,23 @@ var init_codex_adapter = __esm({
21517
21701
  terminal: { kind: "completed", result: result2 }
21518
21702
  };
21519
21703
  await recoveryHooks?.save(latestCheckpoint);
21520
- await session.deleteThread(thread.threadId);
21521
- threadId = null;
21522
21704
  return result2;
21523
21705
  } catch (error48) {
21524
21706
  caughtError = error48;
21707
+ if (sessionLease && (!sessionLease.session.isUsable() || error48 instanceof CodexAppServerError && (error48.category === "transport" || [
21708
+ "rpc_rejected",
21709
+ "invalid_rpc_result",
21710
+ "rpc_timeout",
21711
+ "invalid_thread_provenance",
21712
+ "thread_cleanup_identity_missing",
21713
+ "thread_delete_unconfirmed"
21714
+ ].includes(error48.code)))) {
21715
+ try {
21716
+ await this.invalidateDurableSession(sessionLease);
21717
+ } catch (invalidationError) {
21718
+ sessionInvalidationError = invalidationError;
21719
+ }
21720
+ }
21525
21721
  if (error48 instanceof CodexAppServerError) {
21526
21722
  const dispatchOutcome = error48.dispatchOutcome === "not_dispatched" ? currentDispatchOutcome : error48.dispatchOutcome;
21527
21723
  if (baseCheckpoint && !completedResult) {
@@ -21584,64 +21780,63 @@ var init_codex_adapter = __esm({
21584
21780
  }
21585
21781
  throw classified;
21586
21782
  } finally {
21587
- let cleanupError = null;
21588
- if (session && threadId) {
21589
- try {
21590
- await session.deleteThread(threadId);
21591
- } catch (error48) {
21592
- cleanupError = error48;
21783
+ const cleanup = async () => {
21784
+ let cleanupError = sessionInvalidationError;
21785
+ if (session && threadId) {
21786
+ try {
21787
+ await session.deleteThread(threadId);
21788
+ } catch (error48) {
21789
+ cleanupError = error48;
21790
+ }
21593
21791
  }
21594
- }
21595
- try {
21596
- await session?.close();
21597
- } catch (error48) {
21598
- cleanupError ??= error48;
21599
- }
21600
- if (latestCheckpoint && recoveryHooks) {
21601
- 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
- );
21792
+ if (sessionLease && cleanupError) {
21793
+ try {
21794
+ await this.invalidateDurableSession(sessionLease);
21795
+ } catch (error48) {
21796
+ cleanupError ??= error48;
21611
21797
  }
21612
- } catch (error48) {
21613
- cleanupError ??= error48;
21614
21798
  }
21615
- }
21616
- if (resources && cleanupError === null) {
21617
- try {
21618
- await resources.cleanup();
21619
- } catch (error48) {
21620
- cleanupError = error48;
21799
+ if (resources && cleanupError === null) {
21800
+ try {
21801
+ await resources.cleanup();
21802
+ } catch (error48) {
21803
+ cleanupError = error48;
21804
+ }
21621
21805
  }
21622
- }
21623
- if (latestCheckpoint && recoveryHooks && cleanupError === null) {
21624
- latestCheckpoint = {
21625
- ...latestCheckpoint,
21626
- processState: latestCheckpoint.processToken ? "terminated" : "unmanaged",
21627
- cleanupConfirmed: true
21628
- };
21806
+ if (latestCheckpoint && recoveryHooks && cleanupError === null) {
21807
+ latestCheckpoint = {
21808
+ ...latestCheckpoint,
21809
+ processState: latestCheckpoint.processToken ? sessionLease?.terminated ? "terminated" : "running" : "unmanaged",
21810
+ cleanupConfirmed: true
21811
+ };
21812
+ try {
21813
+ await recoveryHooks.save(latestCheckpoint);
21814
+ } catch (error48) {
21815
+ cleanupError = error48;
21816
+ }
21817
+ }
21818
+ if (cleanupError) {
21819
+ throw new CodexAppServerError({
21820
+ message: "Codex attempt cleanup could not be confirmed.",
21821
+ category: "adapter",
21822
+ code: "attempt_cleanup_unconfirmed",
21823
+ retryable: false,
21824
+ dispatchOutcome: currentDispatchOutcome,
21825
+ cause: cleanupError
21826
+ });
21827
+ }
21828
+ };
21829
+ if (completedResult && caughtError === null) {
21830
+ const cleanupPromise = cleanup();
21831
+ void cleanupPromise.catch(() => void 0);
21832
+ this.attemptCleanups.set(input.attemptId, cleanupPromise);
21833
+ } else {
21629
21834
  try {
21630
- await recoveryHooks.save(latestCheckpoint);
21835
+ await cleanup();
21631
21836
  } catch (error48) {
21632
- cleanupError = error48;
21837
+ if (caughtError === null) throw error48;
21633
21838
  }
21634
21839
  }
21635
- if (cleanupError && caughtError === null) {
21636
- throw new CodexAppServerError({
21637
- message: "Codex attempt cleanup could not be confirmed.",
21638
- category: "adapter",
21639
- code: "attempt_cleanup_unconfirmed",
21640
- retryable: false,
21641
- dispatchOutcome: currentDispatchOutcome,
21642
- cause: cleanupError
21643
- });
21644
- }
21645
21840
  }
21646
21841
  }
21647
21842
  };
@@ -29377,7 +29572,11 @@ var init_runner = __esm({
29377
29572
  } finally {
29378
29573
  removeAbortListener();
29379
29574
  unregisterSignals();
29380
- await lock2.release();
29575
+ try {
29576
+ await this.dependencies.codexAdapter.close?.();
29577
+ } finally {
29578
+ await lock2.release();
29579
+ }
29381
29580
  }
29382
29581
  }
29383
29582
  async persistReceipt(receipt, now) {
@@ -30382,11 +30581,18 @@ ${result2.stderr}`)) {
30382
30581
  const startedAt = Date.now();
30383
30582
  const log = createWriteStream(manifest.log_path, { flags: "a", mode: 384 });
30384
30583
  return await new Promise((resolvePromise, reject) => {
30584
+ let stdout = "";
30385
30585
  const child = spawn5(manifest.executable, args, {
30386
30586
  env: { ...process.env, ...manifest.runtime_environment },
30387
30587
  windowsHide: true,
30388
- stdio: ["ignore", log, log]
30588
+ stdio: ["ignore", "pipe", "pipe"]
30389
30589
  });
30590
+ child.stdout.on("data", (chunk) => {
30591
+ const text = chunk.toString("utf8");
30592
+ log.write(text);
30593
+ stdout = `${stdout}${text}`.slice(-65536);
30594
+ });
30595
+ child.stderr.on("data", (chunk) => log.write(chunk));
30390
30596
  const onAbort = () => child.kill("SIGTERM");
30391
30597
  signal.addEventListener("abort", onAbort, { once: true });
30392
30598
  child.once("error", (error48) => {
@@ -30397,7 +30603,24 @@ ${result2.stderr}`)) {
30397
30603
  child.once("exit", (code) => {
30398
30604
  signal.removeEventListener("abort", onAbort);
30399
30605
  log.end();
30400
- resolvePromise({ exitCode: code ?? 1, uptimeMs: Date.now() - startedAt });
30606
+ let drainReason = null;
30607
+ for (const line of stdout.trim().split("\n").reverse()) {
30608
+ try {
30609
+ const parsed = JSON.parse(line);
30610
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed) && typeof parsed.drain_reason === "string" && /^[a-z0-9_]{1,96}$/u.test(
30611
+ parsed.drain_reason
30612
+ )) {
30613
+ drainReason = parsed.drain_reason;
30614
+ break;
30615
+ }
30616
+ } catch {
30617
+ }
30618
+ }
30619
+ resolvePromise({
30620
+ exitCode: code ?? 1,
30621
+ uptimeMs: Date.now() - startedAt,
30622
+ drainReason
30623
+ });
30401
30624
  });
30402
30625
  });
30403
30626
  };
@@ -30447,6 +30670,7 @@ ${result2.stderr}`)) {
30447
30670
  await appendServiceLog(manifest.log_path, "worker_exited", {
30448
30671
  exit_code: result2.exitCode,
30449
30672
  uptime_ms: result2.uptimeMs,
30673
+ drain_reason: result2.drainReason ?? null,
30450
30674
  retry_after_ms: retryAfterMs
30451
30675
  });
30452
30676
  await sleep4(retryAfterMs);
@@ -31932,7 +32156,21 @@ Waiting for approval...
31932
32156
  ...dependencies,
31933
32157
  registerLifecycleSignalHandlers: unregister
31934
32158
  }, warnings);
31935
- return { exitCode: result2.exitCode, uptimeMs: Date.now() - startedAt };
32159
+ let drainReason = null;
32160
+ try {
32161
+ const summary = JSON.parse(result2.stdout);
32162
+ if (summary && typeof summary === "object" && !Array.isArray(summary) && typeof summary.drain_reason === "string" && /^[a-z0-9_]{1,96}$/u.test(
32163
+ summary.drain_reason
32164
+ )) {
32165
+ drainReason = summary.drain_reason;
32166
+ }
32167
+ } catch {
32168
+ }
32169
+ return {
32170
+ exitCode: result2.exitCode,
32171
+ uptimeMs: Date.now() - startedAt,
32172
+ drainReason
32173
+ };
31936
32174
  }
31937
32175
  }
31938
32176
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtxmacro/cli",
3
- "version": "2026.8.20",
3
+ "version": "2026.8.22",
4
4
  "description": "VTX Macro CLI, MCP server, and durable subscription inference host.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",