@vtxmacro/cli 2026.9.29 → 2026.9.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.
package/README.md CHANGED
@@ -16,16 +16,20 @@ verifies the Codex runtime's version and digest before use; it does not borrow a
16
16
  Codex binary from PATH or an editor extension.
17
17
  For Windows-login auto-start, install and configure the CLI from native Windows;
18
18
  a CLI installed only inside WSL cannot start the WSL virtual machine at login.
19
- New service installations use a private verified launcher. At login and every
20
- service start, it checks the official npm registry, verifies the exact release
21
- integrity and installed CLI version in a new versioned directory, starts it as a
22
- candidate, and selects it only after every configured worker reports ready. A
19
+ New service installations use a private verified launcher. At login, every
20
+ service start, and periodically while running, it checks the official npm registry
21
+ and verifies the exact release integrity and installed CLI version in a new
22
+ versioned directory. Before switching, it stops new admission and lets active
23
+ Provider calls and Agent turns finish while keeping heartbeats alive. It selects
24
+ the candidate only after every configured worker reports ready. A
23
25
  failed candidate is rejected and the last working runtime resumes. The service
24
26
  keeps only the active release and one rollback release. If the update check is
25
27
  unavailable, it keeps the last verified runtime. An intentionally stopped
26
28
  service stays stopped and makes no update request. Hosts installed before this
27
- automatic updater need one final normal npm update and service reinstall to
28
- adopt it.
29
+ private launcher need one final normal npm update and service reinstall to
30
+ adopt it. Startup-only launchers adopt background checking after their next normal
31
+ start activates a release that includes it; publishing a release cannot replace
32
+ an older process that is already running.
29
33
 
30
34
  ## Configure
31
35
 
@@ -16,7 +16,7 @@ import { fileURLToPath } from "node:url";
16
16
  // agent-cli-release.json
17
17
  var agent_cli_release_default = {
18
18
  package_name: "@vtxmacro/cli",
19
- package_version: "2026.9.29",
19
+ package_version: "2026.9.31",
20
20
  codex_package_name: "@openai/codex",
21
21
  codex_version: "0.153.3",
22
22
  copilot_sdk_package_name: "@github/copilot-sdk",
@@ -115,6 +115,121 @@ var agent_cli_release_default = {
115
115
  }
116
116
  };
117
117
 
118
+ // lib/runtime-policy.generated.json
119
+ var runtime_policy_generated_default = {
120
+ ui: {
121
+ auth_bootstrap_retry: {
122
+ max_attempts: 3,
123
+ jitter_milliseconds: 750,
124
+ base_delay_milliseconds: 500,
125
+ min_delay_milliseconds: 250,
126
+ max_delay_milliseconds: 1e4
127
+ }
128
+ },
129
+ ai_dashboard: {
130
+ market_news_bounds: {
131
+ max_items_min: 1,
132
+ max_items_max: 100,
133
+ lookback_hours_min: 1,
134
+ lookback_hours_max: 168,
135
+ max_items_steps: [
136
+ 1,
137
+ 2,
138
+ 3,
139
+ 4,
140
+ 5,
141
+ 6,
142
+ 7,
143
+ 8,
144
+ 9,
145
+ 10,
146
+ 15,
147
+ 20,
148
+ 25,
149
+ 30,
150
+ 40,
151
+ 50,
152
+ 60,
153
+ 75,
154
+ 90,
155
+ 100
156
+ ],
157
+ lookback_hours_steps: [
158
+ 1,
159
+ 2,
160
+ 4,
161
+ 8,
162
+ 12,
163
+ 24,
164
+ 48,
165
+ 72,
166
+ 168
167
+ ]
168
+ }
169
+ },
170
+ trading: {
171
+ engine: {
172
+ client_runtime: {
173
+ recovery: {
174
+ resume_retry_base_delay_ms: 1e3,
175
+ resume_retry_max_delay_ms: 3e4,
176
+ transient_load_min_delay_ms: 3e4,
177
+ transient_load_max_delay_ms: 12e4,
178
+ rate_limit_buffer_ms: 1e3,
179
+ rate_limit_max_delay_ms: 3e5,
180
+ engine_stop_settle_retry_delay_ms: 250,
181
+ recoverable_reset_settle_retry_delay_ms: 1500,
182
+ local_ai_preflight_timeout_ms: 3e3,
183
+ surge_stagger_max_ms: 6e4,
184
+ surge_stagger_min_ms: 2e3,
185
+ surge_stagger_min_candidates: 4,
186
+ stagger_deadline_ttl_ms: 3e5,
187
+ server_active_reprobe_delay_ms: 6e4,
188
+ transient_load_jitter_ms: 1e4,
189
+ cross_host_completed_ttl_ms: 15e3,
190
+ telemetry_dedupe_ttl_ms: 5e3,
191
+ browser_presence_heartbeat_interval_ms: 3e4,
192
+ host_liveness_heartbeat_interval_ms: 3e4,
193
+ host_liveness_recovery_nudge_ttl_ms: 1e4
194
+ }
195
+ }
196
+ }
197
+ },
198
+ external_inference: {
199
+ service: {
200
+ update_check_interval_ms: 3e5,
201
+ update_registry_timeout_ms: 15e3,
202
+ startup_max_attempts: 40,
203
+ startup_poll_interval_ms: 250,
204
+ restart_stable_uptime_ms: 6e4,
205
+ restart_backoff_base_ms: 1e3,
206
+ restart_backoff_max_ms: 3e4,
207
+ restart_exponent_max: 5
208
+ }
209
+ },
210
+ desktop: {
211
+ runtime_watchdog: {
212
+ ping_interval_milliseconds: 15e3,
213
+ telemetry_interval_milliseconds: 3e5
214
+ }
215
+ },
216
+ screener: {
217
+ resource_scheduler: {
218
+ client_lock_ttl_milliseconds: 12e4,
219
+ client_poll_interval_milliseconds: 250
220
+ }
221
+ },
222
+ deployment: {
223
+ cutover_buffer: {
224
+ target_timeout_ms: 3e3,
225
+ retired_timeout_ms: 45e3,
226
+ max_body_bytes: 33554432,
227
+ max_inflight: 16,
228
+ max_queue: 64
229
+ }
230
+ }
231
+ };
232
+
118
233
  // lib/inference-host/config.ts
119
234
  import { randomBytes } from "node:crypto";
120
235
  import { spawn } from "node:child_process";
@@ -983,6 +1098,7 @@ var UPDATE_ATTEMPTS = 3;
983
1098
  var UPDATE_RETRY_MS = 2e3;
984
1099
  var CANDIDATE_READINESS_TIMEOUT_MS = 10 * 60 * 1e3;
985
1100
  var SERVICE_DRAIN_COMMAND = "vtx-inference-host-service-drain-v1";
1101
+ var SERVICE_QUIESCE_COMMAND = "vtx-inference-host-service-quiesce-v1";
986
1102
  var safePath = (value, label) => {
987
1103
  if (typeof value !== "string" || !value || /[\r\n\0]/u.test(value)) {
988
1104
  throw new Error(`Inference-host bootstrap ${label} is invalid.`);
@@ -1127,7 +1243,11 @@ var runInferenceHostBootstrapNpmCommand = async (executable, args, platform = pr
1127
1243
  var defaultRunSupervisor = (executable, args) => {
1128
1244
  const child = spawn2(executable, [...args], {
1129
1245
  windowsHide: true,
1130
- env: { ...process.env, VTX_INFERENCE_HOST_SERVICE_CHILD: "1" },
1246
+ env: {
1247
+ ...process.env,
1248
+ VTX_INFERENCE_HOST_SERVICE_CHILD: "1",
1249
+ VTX_INFERENCE_HOST_BOOTSTRAP_VERSION: agent_cli_release_default.package_version
1250
+ },
1131
1251
  stdio: ["pipe", "inherit", "inherit"]
1132
1252
  });
1133
1253
  const completion = new Promise((resolvePromise, reject) => {
@@ -1139,7 +1259,11 @@ var defaultRunSupervisor = (executable, args) => {
1139
1259
  return {
1140
1260
  completion,
1141
1261
  requestDrain: () => {
1142
- child.stdin.end(`${SERVICE_DRAIN_COMMAND}
1262
+ child.stdin.write(`${SERVICE_DRAIN_COMMAND}
1263
+ `);
1264
+ },
1265
+ requestQuiesce: () => {
1266
+ child.stdin.write(`${SERVICE_QUIESCE_COMMAND}
1143
1267
  `);
1144
1268
  }
1145
1269
  };
@@ -1153,9 +1277,12 @@ var appendBootstrapLog = async (path, event, fields = {}) => {
1153
1277
  { encoding: "utf8", mode: 384 }
1154
1278
  );
1155
1279
  };
1156
- var fetchLatestRelease = async (fetchImpl) => {
1280
+ var fetchLatestRelease = async (fetchImpl, signal) => {
1157
1281
  const response = await fetchImpl(`${OFFICIAL_NPM_REGISTRY}/%40vtxmacro%2Fcli/latest`, {
1158
- signal: AbortSignal.timeout(1e4)
1282
+ signal: AbortSignal.any([
1283
+ AbortSignal.timeout(runtime_policy_generated_default.external_inference.service.update_registry_timeout_ms),
1284
+ ...signal ? [signal] : []
1285
+ ])
1159
1286
  });
1160
1287
  if (!response.ok) throw new Error("registry_request_failed");
1161
1288
  const value = await response.json();
@@ -1323,10 +1450,12 @@ var updateSelection = async (manifestPath, manifest, dependencies) => {
1323
1450
  let lastError = null;
1324
1451
  for (let attempt = 0; attempt < UPDATE_ATTEMPTS; attempt += 1) {
1325
1452
  try {
1326
- latest = await fetchLatestRelease(dependencies.fetch);
1453
+ if (dependencies.signal?.aborted) throw new Error("service_update_cancelled");
1454
+ latest = await fetchLatestRelease(dependencies.fetch, dependencies.signal);
1327
1455
  break;
1328
1456
  } catch (error) {
1329
1457
  lastError = error;
1458
+ if (dependencies.signal?.aborted) throw error;
1330
1459
  if (attempt + 1 < UPDATE_ATTEMPTS) await dependencies.sleep(UPDATE_RETRY_MS);
1331
1460
  }
1332
1461
  }
@@ -1340,7 +1469,7 @@ var updateSelection = async (manifestPath, manifest, dependencies) => {
1340
1469
  }
1341
1470
  const rejection = await parseRejection(manifestPath);
1342
1471
  if (rejection?.package_version === latest.version) return { active, candidate: null };
1343
- if (!await readDesired(manifestPath)) return { active, candidate: null };
1472
+ if (dependencies.signal?.aborted || !await readDesired(manifestPath)) return { active, candidate: null };
1344
1473
  return {
1345
1474
  active,
1346
1475
  candidate: await stageRelease(
@@ -1353,21 +1482,23 @@ var updateSelection = async (manifestPath, manifest, dependencies) => {
1353
1482
  )
1354
1483
  };
1355
1484
  };
1356
- var waitForCandidateReadiness = async (manifestPath, manifest, candidate, signal, sleep, timeoutMs) => {
1485
+ var waitForCandidateReadiness = async (manifestPath, candidate, signal, timeoutMs) => {
1357
1486
  const deadline = Date.now() + timeoutMs;
1358
1487
  while (!signal.aborted && Date.now() < deadline) {
1488
+ if (!await readDesired(manifestPath)) return "aborted";
1359
1489
  try {
1490
+ const currentManifest = parseManifest(JSON.parse(await readFile2(manifestPath, "utf8")));
1360
1491
  const value = JSON.parse(await readFile2(runtimePathFor(manifestPath), "utf8"));
1361
1492
  if (value && typeof value === "object" && !Array.isArray(value)) {
1362
1493
  const runtime = value;
1363
1494
  const workers = Array.isArray(runtime.workers) ? runtime.workers : [];
1364
1495
  const workerNames = workers.map((worker) => worker && typeof worker === "object" && !Array.isArray(worker) ? String(worker.instance_name ?? "") : "").sort();
1365
1496
  const everyWorkerRunning = workers.every((worker) => worker && typeof worker === "object" && !Array.isArray(worker) && worker.state === "running");
1366
- if (runtime.schema_version === "vtx_inference_service_runtime_v1" && runtime.manifest_generation === manifest.generation && typeof runtime.updated_at === "string" && Date.parse(runtime.updated_at) >= Date.parse(candidate.selected_at) && JSON.stringify(workerNames) === JSON.stringify(manifest.worker_names) && everyWorkerRunning) return "ready";
1497
+ if (runtime.schema_version === "vtx_inference_service_runtime_v1" && runtime.manifest_generation === currentManifest.generation && typeof runtime.updated_at === "string" && Date.parse(runtime.updated_at) >= Date.parse(candidate.selected_at) && JSON.stringify(workerNames) === JSON.stringify(currentManifest.worker_names) && everyWorkerRunning) return "ready";
1367
1498
  }
1368
1499
  } catch {
1369
1500
  }
1370
- await sleep(250);
1501
+ await waitForUpdateCheck(250, signal);
1371
1502
  }
1372
1503
  return signal.aborted ? "aborted" : "timeout";
1373
1504
  };
@@ -1399,6 +1530,18 @@ var supervisorArguments = (script, manifestPath) => [
1399
1530
  "--service-manifest",
1400
1531
  manifestPath
1401
1532
  ];
1533
+ var waitForUpdateCheck = async (milliseconds, signal) => {
1534
+ if (signal.aborted) return;
1535
+ await new Promise((resolvePromise) => {
1536
+ const finish = () => {
1537
+ clearTimeout(timer);
1538
+ signal.removeEventListener("abort", finish);
1539
+ resolvePromise();
1540
+ };
1541
+ const timer = setTimeout(finish, milliseconds);
1542
+ signal.addEventListener("abort", finish, { once: true });
1543
+ });
1544
+ };
1402
1545
  var runInferenceHostServiceBootstrap = async (manifestPathValue, dependencies = {}, lockPathValue) => {
1403
1546
  const manifestPath = resolve2(manifestPathValue);
1404
1547
  const lockPath = safePath(
@@ -1406,127 +1549,217 @@ var runInferenceHostServiceBootstrap = async (manifestPathValue, dependencies =
1406
1549
  "lock path"
1407
1550
  );
1408
1551
  const lock = await (dependencies.acquireProcessLock ?? acquireInferenceHostProcessLock)(lockPath);
1552
+ const lifetime = new AbortController();
1553
+ const live = { supervisor: null };
1554
+ const stop = () => {
1555
+ lifetime.abort();
1556
+ try {
1557
+ live.supervisor?.requestDrain();
1558
+ } catch {
1559
+ }
1560
+ };
1561
+ dependencies.signal?.addEventListener("abort", stop, { once: true });
1562
+ process.on("SIGTERM", stop);
1563
+ process.on("SIGINT", stop);
1564
+ if (dependencies.signal?.aborted) stop();
1409
1565
  try {
1410
- const manifest = parseManifest(JSON.parse(await readFile2(manifestPath, "utf8")));
1411
- if (!await readDesired(manifestPath)) {
1566
+ let manifest = parseManifest(JSON.parse(await readFile2(manifestPath, "utf8")));
1567
+ const shouldRun = async () => !lifetime.signal.aborted && await readDesired(manifestPath);
1568
+ if (!await shouldRun()) {
1412
1569
  await appendBootstrapLog(manifest.log_path, "service_bootstrap_stopped");
1413
1570
  return 0;
1414
1571
  }
1415
1572
  const now = dependencies.now ?? (() => /* @__PURE__ */ new Date());
1416
1573
  const sleep = dependencies.sleep ?? (async (milliseconds) => {
1417
- await new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds));
1574
+ await waitForUpdateCheck(milliseconds, lifetime.signal);
1418
1575
  });
1419
1576
  const runSupervisor = dependencies.runSupervisor ?? defaultRunSupervisor;
1420
- const selectedBeforeUpdate = await parseSelection(manifestPath);
1421
- let updatePlan = { active: selectedBeforeUpdate, candidate: null };
1422
- try {
1423
- updatePlan = await updateSelection(manifestPath, manifest, {
1424
- fetch: dependencies.fetch ?? fetch,
1425
- now,
1426
- platform: dependencies.platform ?? process.platform,
1427
- runCommand: dependencies.runCommand ?? defaultRunCommand,
1428
- sleep
1429
- });
1430
- } catch (error) {
1431
- await appendBootstrapLog(manifest.log_path, "service_update_deferred", {
1432
- reason: error instanceof Error ? error.message.slice(0, 160) : "unknown"
1433
- });
1434
- }
1435
- if (!await readDesired(manifestPath)) {
1436
- await appendBootstrapLog(manifest.log_path, "service_bootstrap_stopped");
1437
- return 0;
1438
- }
1577
+ const updateDependencies = {
1578
+ fetch: dependencies.fetch ?? fetch,
1579
+ now,
1580
+ platform: dependencies.platform ?? process.platform,
1581
+ runCommand: dependencies.runCommand ?? defaultRunCommand,
1582
+ sleep,
1583
+ signal: lifetime.signal
1584
+ };
1585
+ const checkUpdate = async () => {
1586
+ manifest = parseManifest(JSON.parse(await readFile2(manifestPath, "utf8")));
1587
+ const active = await parseSelection(manifestPath);
1588
+ try {
1589
+ return await updateSelection(manifestPath, manifest, updateDependencies);
1590
+ } catch (error) {
1591
+ await appendBootstrapLog(manifest.log_path, "service_update_deferred", {
1592
+ reason: error instanceof Error ? error.message.slice(0, 160) : "unknown"
1593
+ });
1594
+ return { active, candidate: null };
1595
+ }
1596
+ };
1439
1597
  const launch = async (selection) => {
1598
+ manifest = parseManifest(JSON.parse(await readFile2(manifestPath, "utf8")));
1440
1599
  const script = selection?.script ?? manifest.script;
1441
1600
  await access(manifest.executable);
1442
1601
  await access(script);
1602
+ if (!await shouldRun()) return null;
1443
1603
  await appendBootstrapLog(manifest.log_path, "service_bootstrap_launch", {
1444
1604
  package_version: selection?.package_version ?? agent_cli_release_default.package_version
1445
1605
  });
1446
- return await runSupervisor(
1447
- manifest.executable,
1448
- supervisorArguments(script, manifestPath)
1449
- ).completion;
1606
+ if (!await shouldRun()) return null;
1607
+ live.supervisor = runSupervisor(manifest.executable, supervisorArguments(script, manifestPath));
1608
+ return live.supervisor;
1450
1609
  };
1451
- if (!updatePlan.candidate) return await launch(updatePlan.active);
1452
- const candidate = updatePlan.candidate;
1453
- await appendBootstrapLog(manifest.log_path, "service_release_candidate_started", {
1454
- package_version: candidate.package_version
1455
- });
1456
- let candidateSupervisor = null;
1457
- let candidateFailure = "candidate_launch_failed";
1458
- let candidateExitCode = 1;
1459
- const readinessController = new AbortController();
1460
- try {
1461
- candidateSupervisor = runSupervisor(
1462
- manifest.executable,
1463
- supervisorArguments(candidate.script, manifestPath)
1464
- );
1465
- const first = await Promise.race([
1466
- candidateSupervisor.completion.then(
1467
- (exitCode) => ({ kind: "exit", exitCode }),
1468
- () => ({ kind: "error", exitCode: 1 })
1469
- ),
1470
- waitForCandidateReadiness(
1471
- manifestPath,
1472
- manifest,
1473
- candidate,
1474
- readinessController.signal,
1475
- sleep,
1476
- dependencies.readinessTimeoutMs ?? CANDIDATE_READINESS_TIMEOUT_MS
1477
- ).then((status) => ({ kind: status, exitCode: 1 }))
1478
- ]);
1479
- if (first.kind === "ready") {
1480
- readinessController.abort();
1481
- await atomicWriteSelection(manifestPath, candidate);
1482
- await rm2(rejectionPathFor(manifestPath), { force: true }).catch(() => void 0);
1483
- await prunePrivateReleases(manifestPath, [candidate, updatePlan.active]).catch(
1484
- async () => await appendBootstrapLog(
1485
- manifest.log_path,
1486
- "service_release_cleanup_deferred",
1487
- { package_version: candidate.package_version }
1488
- ).catch(() => void 0)
1489
- );
1490
- await appendBootstrapLog(manifest.log_path, "service_release_selected", {
1610
+ let updatePlan = await checkUpdate();
1611
+ while (await shouldRun()) {
1612
+ if (updatePlan.candidate) {
1613
+ const candidate = { ...updatePlan.candidate, selected_at: now().toISOString() };
1614
+ await rm2(runtimePathFor(manifestPath), { force: true });
1615
+ await appendBootstrapLog(manifest.log_path, "service_release_candidate_started", {
1491
1616
  package_version: candidate.package_version
1492
- }).catch(() => void 0);
1493
- return await candidateSupervisor.completion;
1494
- }
1495
- if (first.kind === "timeout") {
1496
- candidateFailure = "candidate_readiness_timeout";
1497
- candidateSupervisor.requestDrain();
1498
- candidateExitCode = await candidateSupervisor.completion.catch(() => 1);
1499
- } else {
1500
- candidateFailure = first.kind === "error" ? "candidate_launch_failed" : "candidate_exited_before_readiness";
1501
- candidateExitCode = first.exitCode;
1502
- }
1503
- } catch {
1504
- candidateFailure = "candidate_launch_failed";
1505
- candidateExitCode = 1;
1506
- if (candidateSupervisor) {
1617
+ });
1618
+ let candidateFailure = "candidate_launch_failed";
1619
+ let promoted = false;
1620
+ const readinessController = new AbortController();
1621
+ const abortReadiness = () => readinessController.abort();
1622
+ lifetime.signal.addEventListener("abort", abortReadiness, { once: true });
1507
1623
  try {
1508
- candidateSupervisor.requestDrain();
1624
+ const candidateSupervisor = await launch(candidate);
1625
+ if (!candidateSupervisor) return 0;
1626
+ const first = await Promise.race([
1627
+ candidateSupervisor.completion.then(
1628
+ (exitCode) => ({ kind: "exit", exitCode }),
1629
+ () => ({ kind: "error", exitCode: 1 })
1630
+ ),
1631
+ waitForCandidateReadiness(
1632
+ manifestPath,
1633
+ candidate,
1634
+ readinessController.signal,
1635
+ dependencies.readinessTimeoutMs ?? CANDIDATE_READINESS_TIMEOUT_MS
1636
+ ).then((status) => ({ kind: status, exitCode: 1 }))
1637
+ ]);
1638
+ if (first.kind === "ready" && await shouldRun()) {
1639
+ await atomicWriteSelection(manifestPath, candidate);
1640
+ await rm2(rejectionPathFor(manifestPath), { force: true }).catch(() => void 0);
1641
+ await prunePrivateReleases(manifestPath, [candidate, updatePlan.active]).catch(
1642
+ async () => await appendBootstrapLog(
1643
+ manifest.log_path,
1644
+ "service_release_cleanup_deferred",
1645
+ { package_version: candidate.package_version }
1646
+ ).catch(() => void 0)
1647
+ );
1648
+ await appendBootstrapLog(manifest.log_path, "service_release_selected", {
1649
+ package_version: candidate.package_version
1650
+ }).catch(() => void 0);
1651
+ updatePlan = { active: candidate, candidate: null };
1652
+ promoted = true;
1653
+ } else {
1654
+ candidateFailure = first.kind === "timeout" ? "candidate_readiness_timeout" : "candidate_exited_before_readiness";
1655
+ if (first.kind !== "exit" && first.kind !== "error") candidateSupervisor.requestDrain();
1656
+ await candidateSupervisor.completion.catch(() => 1);
1657
+ live.supervisor = null;
1658
+ }
1509
1659
  } catch {
1660
+ candidateFailure = "candidate_launch_failed";
1661
+ if (live.supervisor) {
1662
+ live.supervisor.requestDrain();
1663
+ await live.supervisor.completion.catch(() => 1);
1664
+ live.supervisor = null;
1665
+ }
1666
+ } finally {
1667
+ readinessController.abort();
1668
+ lifetime.signal.removeEventListener("abort", abortReadiness);
1669
+ }
1670
+ if (!await shouldRun()) {
1671
+ if (live.supervisor) {
1672
+ live.supervisor.requestDrain();
1673
+ await live.supervisor.completion.catch(() => 1);
1674
+ }
1675
+ return 0;
1676
+ }
1677
+ if (!promoted) {
1678
+ await writeRejection(manifestPath, {
1679
+ schema_version: "vtx_inference_service_release_rejection_v1",
1680
+ package_version: candidate.package_version,
1681
+ rejected_at: now().toISOString(),
1682
+ reason: candidateFailure
1683
+ });
1684
+ await prunePrivateReleases(manifestPath, [updatePlan.active]);
1685
+ await appendBootstrapLog(manifest.log_path, "service_release_rolled_back", {
1686
+ package_version: candidate.package_version,
1687
+ reason: candidateFailure
1688
+ });
1689
+ updatePlan = { active: updatePlan.active, candidate: null };
1510
1690
  }
1511
- candidateExitCode = await candidateSupervisor.completion.catch(() => 1);
1512
1691
  }
1513
- } finally {
1514
- readinessController.abort();
1692
+ const supervisor = live.supervisor ?? await launch(updatePlan.active);
1693
+ if (!supervisor) return 0;
1694
+ let exited = false;
1695
+ const completion = supervisor.completion.then(
1696
+ (exitCode) => {
1697
+ exited = true;
1698
+ return exitCode;
1699
+ },
1700
+ () => {
1701
+ exited = true;
1702
+ return 1;
1703
+ }
1704
+ );
1705
+ while (!exited) {
1706
+ const pollController = new AbortController();
1707
+ const abortPoll = () => pollController.abort();
1708
+ lifetime.signal.addEventListener("abort", abortPoll, { once: true });
1709
+ if (lifetime.signal.aborted) pollController.abort();
1710
+ try {
1711
+ await Promise.race([
1712
+ completion,
1713
+ waitForUpdateCheck(
1714
+ dependencies.updateCheckIntervalMs ?? runtime_policy_generated_default.external_inference.service.update_check_interval_ms,
1715
+ pollController.signal
1716
+ )
1717
+ ]);
1718
+ } finally {
1719
+ pollController.abort();
1720
+ lifetime.signal.removeEventListener("abort", abortPoll);
1721
+ }
1722
+ if (exited) {
1723
+ live.supervisor = null;
1724
+ return await completion;
1725
+ }
1726
+ if (!await shouldRun()) {
1727
+ supervisor.requestDrain();
1728
+ return await completion;
1729
+ }
1730
+ const next = await checkUpdate();
1731
+ if (exited) {
1732
+ live.supervisor = null;
1733
+ return await completion;
1734
+ }
1735
+ if (!await shouldRun()) {
1736
+ supervisor.requestDrain();
1737
+ return await completion;
1738
+ }
1739
+ if (!next.candidate) continue;
1740
+ await appendBootstrapLog(manifest.log_path, "service_release_quiescing", {
1741
+ package_version: next.candidate.package_version
1742
+ });
1743
+ supervisor.requestQuiesce();
1744
+ await completion;
1745
+ live.supervisor = null;
1746
+ updatePlan = next;
1747
+ break;
1748
+ }
1515
1749
  }
1516
- if (!await readDesired(manifestPath)) return candidateExitCode;
1517
- await writeRejection(manifestPath, {
1518
- schema_version: "vtx_inference_service_release_rejection_v1",
1519
- package_version: candidate.package_version,
1520
- rejected_at: now().toISOString(),
1521
- reason: candidateFailure
1522
- });
1523
- await prunePrivateReleases(manifestPath, [updatePlan.active]);
1524
- await appendBootstrapLog(manifest.log_path, "service_release_rolled_back", {
1525
- package_version: candidate.package_version,
1526
- reason: candidateFailure
1527
- });
1528
- return await launch(updatePlan.active);
1750
+ return 0;
1529
1751
  } finally {
1752
+ lifetime.abort();
1753
+ if (live.supervisor) {
1754
+ try {
1755
+ live.supervisor.requestDrain();
1756
+ } catch {
1757
+ }
1758
+ await live.supervisor.completion.catch(() => 1);
1759
+ }
1760
+ dependencies.signal?.removeEventListener("abort", stop);
1761
+ process.removeListener("SIGTERM", stop);
1762
+ process.removeListener("SIGINT", stop);
1530
1763
  await lock.release();
1531
1764
  }
1532
1765
  };
package/bin/vtx.js CHANGED
@@ -77,7 +77,7 @@ var init_agent_cli_release = __esm({
77
77
  "agent-cli-release.json"() {
78
78
  agent_cli_release_default = {
79
79
  package_name: "@vtxmacro/cli",
80
- package_version: "2026.9.29",
80
+ package_version: "2026.9.31",
81
81
  codex_package_name: "@openai/codex",
82
82
  codex_version: "0.153.3",
83
83
  copilot_sdk_package_name: "@github/copilot-sdk",
@@ -34407,13 +34407,30 @@ var init_grok_policy = __esm({
34407
34407
  import { createServer as createServer3 } from "node:http";
34408
34408
  import { createHash as createHash6, randomBytes as randomBytes5 } from "node:crypto";
34409
34409
  import { once } from "node:events";
34410
- var record3, count2, effort, safeCode2, GrokTransportProxy, createGrokTransportProxy;
34410
+ var diagnosticNames, diagnosticTypes, diagnosticIdentifier, record3, count2, effort, safeCode2, GrokTransportProxy, createGrokTransportProxy;
34411
34411
  var init_grok_transport_proxy = __esm({
34412
34412
  "lib/inference-host/grok-transport-proxy.ts"() {
34413
34413
  "use strict";
34414
34414
  init_define_VTX_EXO_POLICY();
34415
34415
  init_define_VTX_GROK_POLICY();
34416
34416
  init_define_VTX_PI_MODEL_POLICY();
34417
+ diagnosticNames = /* @__PURE__ */ new Set([
34418
+ "search_tool",
34419
+ "use_tool",
34420
+ "bash",
34421
+ "read_file",
34422
+ "write_file",
34423
+ "edit_file",
34424
+ "web_search",
34425
+ "vtx_get_data",
34426
+ "vtx_submit_decision",
34427
+ "vtx_decision_status"
34428
+ ]);
34429
+ diagnosticTypes = /* @__PURE__ */ new Set(["function", "web_search", "web_search_preview", "file_search", "computer_use"]);
34430
+ diagnosticIdentifier = (value, known) => {
34431
+ if (typeof value !== "string") return { label: value == null ? "missing" : "invalid", sha256: null };
34432
+ return known.has(value) ? { label: value, sha256: null } : { label: "redacted", sha256: createHash6("sha256").update(value).digest("hex") };
34433
+ };
34417
34434
  record3 = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
34418
34435
  count2 = (value) => Number.isSafeInteger(value) && Number(value) >= 0 ? Number(value) : null;
34419
34436
  effort = (value) => value == null ? "none" : String(value).toLowerCase();
@@ -34434,6 +34451,7 @@ var init_grok_transport_proxy = __esm({
34434
34451
  this.failureCode = null;
34435
34452
  this.rejectionStatus = null;
34436
34453
  this.terminalFailure = null;
34454
+ this.toolValidationFailure = null;
34437
34455
  const upstream = new URL(options.upstreamBaseUrl);
34438
34456
  if (upstream.protocol !== "https:" || upstream.username || upstream.password || upstream.search || upstream.hash || !options.requestedModel || typeof options.expectedSystemPrompt !== "string" || !Number.isSafeInteger(options.deadlineAtMs) || options.providerDispatchNotAfterMs !== void 0 && !Number.isSafeInteger(options.providerDispatchNotAfterMs) || !Number.isSafeInteger(options.maxRequestBytes) || options.maxRequestBytes < 1 || !Number.isSafeInteger(options.maxResponseBytes) || options.maxResponseBytes < 1 || new Set(options.allowedToolNames).size !== options.allowedToolNames.length || options.allowedToolNames.some((name) => !name)) throw new Error("grok_proxy_invalid_configuration");
34439
34457
  }
@@ -34471,6 +34489,31 @@ var init_grok_transport_proxy = __esm({
34471
34489
  throw new Error("server_account_context_expired_before_provider_dispatch");
34472
34490
  }
34473
34491
  }
34492
+ rejectTools(reason, payload, route) {
34493
+ const tools = Array.isArray(payload.tools) ? payload.tools : null;
34494
+ const diagnostic = {
34495
+ reason,
34496
+ route,
34497
+ tools_shape: tools ? "array" : payload.tools === null ? "null" : typeof payload.tools,
34498
+ tool_count: tools?.length ?? null,
34499
+ expected_tool_count: this.options.allowedToolNames.length,
34500
+ tools: (tools ?? []).slice(0, 8).map((value) => {
34501
+ const tool = record3(value);
34502
+ const type = diagnosticIdentifier(tool.type, diagnosticTypes);
34503
+ const name = diagnosticIdentifier(route === "/responses" ? tool.name : record3(tool.function).name, diagnosticNames);
34504
+ return { type: type.label, name: name.label, type_sha256: type.sha256, name_sha256: name.sha256 };
34505
+ }),
34506
+ tools_truncated: (tools?.length ?? 0) > 8,
34507
+ prior_dispatch: this.dispatched,
34508
+ completed_response_count: this.receiptValues.length
34509
+ };
34510
+ this.toolValidationFailure ??= diagnostic;
34511
+ try {
34512
+ this.options.onToolValidationFailure?.(diagnostic);
34513
+ } catch {
34514
+ }
34515
+ throw new Error(`grok_tool_isolation_${reason}`);
34516
+ }
34474
34517
  validatePayload(payload, route) {
34475
34518
  const requestedEffort = route === "/responses" ? record3(payload.reasoning).effort : payload.reasoning_effort;
34476
34519
  if (payload.model !== this.options.requestedModel || effort(requestedEffort) !== effort(this.options.requestedReasoningEffort)) {
@@ -34482,17 +34525,17 @@ var init_grok_transport_proxy = __esm({
34482
34525
  if (instructions.length !== 1 || instructions[0].role !== "system" || instructions[0].content !== this.options.expectedSystemPrompt || route === "/responses" && payload.instructions != null) {
34483
34526
  throw new Error("grok_system_prompt_mismatch");
34484
34527
  }
34485
- if (payload.tools != null && !Array.isArray(payload.tools)) throw new Error("grok_tool_isolation_failed");
34528
+ if (payload.tools != null && !Array.isArray(payload.tools)) this.rejectTools("invalid_tools", payload, route);
34486
34529
  const tools = payload.tools ?? [];
34487
34530
  const names2 = tools.map((value) => {
34488
34531
  const tool = record3(value);
34489
- if (tool.type !== "function") throw new Error("grok_tool_isolation_failed");
34532
+ if (tool.type !== "function") this.rejectTools("invalid_type", payload, route);
34490
34533
  const name = route === "/responses" ? tool.name : record3(tool.function).name;
34491
- if (typeof name !== "string") throw new Error("grok_tool_isolation_failed");
34534
+ if (typeof name !== "string") this.rejectTools("missing_name", payload, route);
34492
34535
  return name;
34493
34536
  });
34494
34537
  if (names2.sort().join("\0") !== [...this.options.allowedToolNames].sort().join("\0")) {
34495
- throw new Error("grok_tool_isolation_failed");
34538
+ this.rejectTools("unexpected_tools", payload, route);
34496
34539
  }
34497
34540
  }
34498
34541
  async handle(request, response) {
@@ -34622,8 +34665,15 @@ var init_grok_transport_proxy = __esm({
34622
34665
  if (!this.dispatched) this.pending = false;
34623
34666
  }
34624
34667
  controller.abort();
34625
- if (!response.headersSent) response.writeHead(502, { "x-should-retry": "false", "content-type": "application/json" });
34626
- response.end();
34668
+ if (!response.headersSent) {
34669
+ const localValidation = inference && this.toolValidationFailure !== null;
34670
+ response.writeHead(localValidation ? 400 : 502, { "x-should-retry": "false", "content-type": "application/json" });
34671
+ response.end(localValidation ? JSON.stringify({ error: {
34672
+ type: "vtx_host_validation_error",
34673
+ code: this.failureCode,
34674
+ message: "VTX blocked this Grok Build request locally because its tools did not match the allowed tool contract."
34675
+ } }) : void 0);
34676
+ } else response.end();
34627
34677
  } finally {
34628
34678
  clearTimeout(timer);
34629
34679
  this.options.signal?.removeEventListener("abort", abort);
@@ -35534,7 +35584,13 @@ var init_grok_adapter = __esm({
35534
35584
  providerDispatchNotAfterMs: provider?.providerDispatchNotAfterMs,
35535
35585
  onProviderDispatch: provider?.onProviderDispatch,
35536
35586
  signal: input.signal,
35537
- fetchImpl: this.options.fetchImpl
35587
+ fetchImpl: this.options.fetchImpl,
35588
+ onToolValidationFailure: (diagnostic) => this.options.emitDiagnosticEvent?.({
35589
+ event: "grok_tool_validation_failed",
35590
+ attempt_id: provider?.attemptId ?? null,
35591
+ control_mode: provider ? "provider" : "agent",
35592
+ ...diagnostic
35593
+ })
35538
35594
  });
35539
35595
  input.signal.throwIfAborted();
35540
35596
  session = new GrokAcpSession({
@@ -35647,7 +35703,7 @@ var init_grok_adapter = __esm({
35647
35703
  provider?.onTerminalDispatchOutcome?.(outcome);
35648
35704
  if (error48 instanceof CodexAppServerError) throw error48;
35649
35705
  throw new CodexAppServerError({
35650
- message: "Grok Build could not complete the inference contract.",
35706
+ message: proxy?.toolValidationFailure ? "VTX blocked an outgoing Grok Build request locally because its tools did not match the allowed tool contract." : "Grok Build could not complete the inference contract.",
35651
35707
  category: input.signal?.aborted ? "cancelled" : Date.now() >= input.deadlineAtMs ? "timeout" : proxy?.rejectionStatus === 401 || proxy?.rejectionStatus === 403 ? "auth" : "adapter",
35652
35708
  code: proxy?.failureCode ?? proxy?.terminalFailure?.code ?? (error48 instanceof Error && /^grok_[a-z0-9_]+$/u.test(error48.message) ? error48.message : "grok_adapter_failure"),
35653
35709
  httpStatusCode: proxy?.rejectionStatus,
@@ -37893,6 +37949,13 @@ var init_runner = __esm({
37893
37949
  };
37894
37950
  let removeAbortListener = () => {
37895
37951
  };
37952
+ let removeQuiesceListener = () => {
37953
+ };
37954
+ let quiesceRequested = Boolean(this.options.quiesceSignal?.aborted);
37955
+ let wakeQuiesce;
37956
+ const quiesceWake = new Promise((resolve12) => {
37957
+ wakeQuiesce = resolve12;
37958
+ });
37896
37959
  let drainRequested = false;
37897
37960
  let drainReason = "stopped";
37898
37961
  let receipt;
@@ -37931,6 +37994,13 @@ var init_runner = __esm({
37931
37994
  wakeDrain();
37932
37995
  };
37933
37996
  try {
37997
+ const onQuiesce = () => {
37998
+ quiesceRequested = true;
37999
+ wakeQuiesce();
38000
+ };
38001
+ this.options.quiesceSignal?.addEventListener("abort", onQuiesce, { once: true });
38002
+ removeQuiesceListener = () => this.options.quiesceSignal?.removeEventListener("abort", onQuiesce);
38003
+ if (quiesceRequested) return { claimed, completed, failed, drain_reason: "service_update" };
37934
38004
  unregisterSignals = (this.dependencies.registerSignalHandlers ?? defaultRegisterSignalHandlers)(requestDrain);
37935
38005
  if (this.options.signal) {
37936
38006
  const onAbort = () => requestDrain("aborted");
@@ -38033,8 +38103,9 @@ var init_runner = __esm({
38033
38103
  await mcp.initialize({ signal: this.options.signal });
38034
38104
  if (settings.agentRuntime) {
38035
38105
  const agentRuntimeSettings = settings.agentRuntime;
38106
+ const agentIdleSignal = this.options.quiesceSignal ? AbortSignal.any([agentAbort.signal, this.options.quiesceSignal]) : agentAbort.signal;
38036
38107
  agentLoop = (async () => {
38037
- while (!agentAbort.signal.aborted) {
38108
+ while (!agentAbort.signal.aborted && !quiesceRequested) {
38038
38109
  const existingFence = await readInferenceAgentFailureFence(
38039
38110
  agentRuntimeSettings.statePath
38040
38111
  );
@@ -38067,7 +38138,7 @@ var init_runner = __esm({
38067
38138
  });
38068
38139
  if (this.options.once) return;
38069
38140
  try {
38070
- await sleep4(Math.max(MIN_SLEEP_MS, retryAtMs - now()), agentAbort.signal);
38141
+ await sleep4(Math.max(MIN_SLEEP_MS, retryAtMs - now()), agentIdleSignal);
38071
38142
  } catch {
38072
38143
  return;
38073
38144
  }
@@ -38082,7 +38153,8 @@ var init_runner = __esm({
38082
38153
  statePath: agentRuntimeSettings.statePath,
38083
38154
  controlClient: createInferenceAgentControlClient(mcp),
38084
38155
  adapter: agentRuntimeSettings.adapter,
38085
- dataRequestTimeoutMs: agentRuntimeSettings.dataRequestTimeoutMs
38156
+ dataRequestTimeoutMs: agentRuntimeSettings.dataRequestTimeoutMs,
38157
+ quiesceSignal: this.options.quiesceSignal
38086
38158
  });
38087
38159
  try {
38088
38160
  if (this.options.once) {
@@ -38151,7 +38223,7 @@ var init_runner = __esm({
38151
38223
  );
38152
38224
  if (this.options.once || retryAtMs === null) return;
38153
38225
  try {
38154
- await sleep4(Math.max(MIN_SLEEP_MS, retryAtMs - now()), agentAbort.signal);
38226
+ await sleep4(Math.max(MIN_SLEEP_MS, retryAtMs - now()), agentIdleSignal);
38155
38227
  } catch {
38156
38228
  return;
38157
38229
  }
@@ -38167,7 +38239,9 @@ var init_runner = __esm({
38167
38239
  const retained = await readInferenceAgentRuntimeState(agentRuntimeSettings.statePath);
38168
38240
  if (!retained || retained.schema_version !== "vtx_inference_agent_runtime_v3" || !retained.pending_turn?.terminal_receipt) return;
38169
38241
  if (this.options.once) return;
38170
- await sleep4(1e3, agentAbort.signal);
38242
+ await sleep4(1e3, agentIdleSignal).catch((error49) => {
38243
+ if (!quiesceRequested) throw error49;
38244
+ });
38171
38245
  continue;
38172
38246
  }
38173
38247
  if (error48 instanceof InferenceHostRunnerError && error48.code === "agent_terminal_failure_fenced") {
@@ -38179,7 +38253,9 @@ var init_runner = __esm({
38179
38253
  });
38180
38254
  if (this.options.once) return;
38181
38255
  try {
38182
- await sleep4(1e3, agentAbort.signal);
38256
+ await sleep4(1e3, agentIdleSignal).catch((error49) => {
38257
+ if (!quiesceRequested) throw error49;
38258
+ });
38183
38259
  } catch {
38184
38260
  return;
38185
38261
  }
@@ -39010,11 +39086,11 @@ var init_runner = __esm({
39010
39086
  )
39011
39087
  });
39012
39088
  }
39013
- while (!drainRequested && recoveryQueue.length > 0 && (settings.maxConcurrency === null || active.size < settings.maxConcurrency)) {
39089
+ while (!drainRequested && !quiesceRequested && recoveryQueue.length > 0 && (settings.maxConcurrency === null || active.size < settings.maxConcurrency)) {
39014
39090
  const recovery = recoveryQueue.shift();
39015
39091
  launchClaim(recovery.claim, recovery);
39016
39092
  }
39017
- while (!drainRequested) {
39093
+ while (!drainRequested && !quiesceRequested) {
39018
39094
  const currentTime = now();
39019
39095
  if (providerFailureCooldownReason !== null && currentTime >= providerFailureRetryAtMs) {
39020
39096
  emitDiagnostic("provider_cooldown_cleared", {
@@ -39138,12 +39214,12 @@ var init_runner = __esm({
39138
39214
  nextClaimAt = Math.max(nextClaimAt, nextAdvertisementAttemptAt);
39139
39215
  }
39140
39216
  }
39141
- while (recoveryQueue.length > 0 && (settings.maxConcurrency === null || active.size < settings.maxConcurrency)) {
39217
+ while (!quiesceRequested && recoveryQueue.length > 0 && (settings.maxConcurrency === null || active.size < settings.maxConcurrency)) {
39142
39218
  const recovery = recoveryQueue.shift();
39143
39219
  launchClaim(recovery.claim, recovery);
39144
39220
  }
39145
39221
  let claimOpportunities = 0;
39146
- while (!drainRequested && (!yieldRenewalToClaim || claimOpportunities === 0) && recoveryQueue.length === 0 && (!this.options.once || !onceClaimed) && (settings.maxConcurrency === null || active.size < settings.maxConcurrency) && pendingClaimPromotions === 0 && now() >= Math.max(
39222
+ while (!drainRequested && !quiesceRequested && (!yieldRenewalToClaim || claimOpportunities === 0) && recoveryQueue.length === 0 && (!this.options.once || !onceClaimed) && (settings.maxConcurrency === null || active.size < settings.maxConcurrency) && pendingClaimPromotions === 0 && now() >= Math.max(
39147
39223
  nextClaimAt,
39148
39224
  claimAvailabilityBackoff?.untilMs ?? 0,
39149
39225
  providerRetryAtMs ?? 0,
@@ -39165,6 +39241,7 @@ var init_runner = __esm({
39165
39241
  let claim;
39166
39242
  let claimTiming;
39167
39243
  const claimCallStartedAtMs = now();
39244
+ if (quiesceRequested) break;
39168
39245
  try {
39169
39246
  claimOpportunities += 1;
39170
39247
  if (claimPreparation.unsent === request) claimPreparation.unsent = null;
@@ -39415,9 +39492,14 @@ var init_runner = __esm({
39415
39492
  await Promise.race([
39416
39493
  sleep4(Math.max(MIN_SLEEP_MS, wakeAt - now())),
39417
39494
  drainSignal,
39495
+ quiesceWake,
39418
39496
  ...active.values()
39419
39497
  ]);
39420
39498
  }
39499
+ if (quiesceRequested && !drainRequested) {
39500
+ await Promise.race([Promise.allSettled([...active.values(), agentLoop]), drainSignal]);
39501
+ if (!drainRequested) requestDrain("service_update");
39502
+ }
39421
39503
  stopHostHeartbeatLoop();
39422
39504
  if (providerDispatchWatchdogDrainRequested) {
39423
39505
  await settlesWithin(hostHeartbeatLoop, MIN_SLEEP_MS);
@@ -39475,6 +39557,7 @@ var init_runner = __esm({
39475
39557
  await hostHeartbeatLoop;
39476
39558
  }
39477
39559
  removeAbortListener();
39560
+ removeQuiesceListener();
39478
39561
  unregisterSignals();
39479
39562
  try {
39480
39563
  await this.dependencies.codexAdapter.close?.();
@@ -40508,9 +40591,9 @@ var init_runner = __esm({
40508
40591
  async run(signal) {
40509
40592
  let exitedNormally = false;
40510
40593
  try {
40511
- while (!this.stopped && !signal?.aborted) {
40594
+ while (!this.stopped && !signal?.aborted && !this.options.quiesceSignal?.aborted) {
40512
40595
  const nextWakeAt = await this.runOnce(signal);
40513
- if (this.stopped || signal?.aborted) break;
40596
+ if (this.stopped || signal?.aborted || this.options.quiesceSignal?.aborted) break;
40514
40597
  await this.waitUntilWake(nextWakeAt, signal);
40515
40598
  }
40516
40599
  exitedNormally = true;
@@ -40533,6 +40616,7 @@ var init_runner = __esm({
40533
40616
  }
40534
40617
  async runOnce(signal) {
40535
40618
  const nowIso = () => new Date(this.now()).toISOString();
40619
+ if (this.options.quiesceSignal?.aborted) return this.now();
40536
40620
  const recoveredState = await readInferenceAgentRuntimeState(this.options.statePath);
40537
40621
  const failureFence = await readInferenceAgentFailureFence(this.options.statePath);
40538
40622
  if (failureFence?.retry_at === null) {
@@ -40588,6 +40672,7 @@ var init_runner = __esm({
40588
40672
  }
40589
40673
  }
40590
40674
  if (!state) {
40675
+ if (this.options.quiesceSignal?.aborted) return this.now();
40591
40676
  const assignment2 = await this.options.controlClient.nextAssignment({
40592
40677
  operation_id: randomUUID7(),
40593
40678
  host_id: this.options.hostId,
@@ -40690,6 +40775,7 @@ var init_runner = __esm({
40690
40775
  await this.handleCancelledAssignment(state, signal);
40691
40776
  return this.now() + (this.options.idlePollMs ?? 5e3);
40692
40777
  }
40778
+ if (this.options.quiesceSignal?.aborted) return this.now();
40693
40779
  assignment = {
40694
40780
  ...assignment,
40695
40781
  lease_expires_at: preTurnHeartbeat.lease_expires_at
@@ -41085,9 +41171,14 @@ var init_runner = __esm({
41085
41171
  }
41086
41172
  async waitUntilWake(nextWakeAtMs, signal) {
41087
41173
  const heartbeatIntervalMs = this.options.heartbeatIntervalMs ?? 3e3;
41088
- while (!this.stopped && !signal?.aborted && this.now() < nextWakeAtMs) {
41089
- await this.sleep(Math.min(heartbeatIntervalMs, nextWakeAtMs - this.now()), signal);
41090
- if (this.stopped || signal?.aborted) return "stopped";
41174
+ while (!this.stopped && !signal?.aborted && !this.options.quiesceSignal?.aborted && this.now() < nextWakeAtMs) {
41175
+ const waitSignal = this.options.quiesceSignal ? AbortSignal.any([this.options.quiesceSignal, ...signal ? [signal] : []]) : signal;
41176
+ try {
41177
+ await this.sleep(Math.min(heartbeatIntervalMs, nextWakeAtMs - this.now()), waitSignal);
41178
+ } catch (error48) {
41179
+ if (!this.options.quiesceSignal?.aborted) throw error48;
41180
+ }
41181
+ if (this.stopped || signal?.aborted || this.options.quiesceSignal?.aborted) return "stopped";
41091
41182
  if (this.now() >= nextWakeAtMs) return "ready";
41092
41183
  const state = await readInferenceAgentRuntimeState(this.options.statePath);
41093
41184
  if (state?.schema_version !== "vtx_inference_agent_runtime_v3" || state.adapter_id !== this.options.adapterId) return "cancelled";
@@ -41112,7 +41203,7 @@ var init_runner = __esm({
41112
41203
  updated_at: requestedAt
41113
41204
  });
41114
41205
  }
41115
- return this.stopped || signal?.aborted ? "stopped" : "ready";
41206
+ return this.stopped || signal?.aborted || this.options.quiesceSignal?.aborted ? "stopped" : "ready";
41116
41207
  }
41117
41208
  async releaseForHostStop(reasonCode = "host_stopped") {
41118
41209
  const recovered = await readInferenceAgentRuntimeState(this.options.statePath);
@@ -41239,6 +41330,8 @@ var init_runtime_policy_generated = __esm({
41239
41330
  },
41240
41331
  external_inference: {
41241
41332
  service: {
41333
+ update_check_interval_ms: 3e5,
41334
+ update_registry_timeout_ms: 15e3,
41242
41335
  startup_max_attempts: 40,
41243
41336
  startup_poll_interval_ms: 250,
41244
41337
  restart_stable_uptime_ms: 6e4,
@@ -41279,13 +41372,14 @@ import { createWriteStream, readFileSync } from "node:fs";
41279
41372
  import { access as access4, chmod as chmod3, mkdir as mkdir3, readFile as readFile9, rm as rm10, writeFile as writeFile4 } from "node:fs/promises";
41280
41373
  import { homedir as homedir2 } from "node:os";
41281
41374
  import { dirname as dirname12, join as join13, resolve as resolve10 } from "node:path";
41282
- var SERVICE_NAME, SYSTEMD_UNIT, LAUNCHD_LABEL, SERVICE_COOPERATIVE_STOP_SECONDS, SERVICE_UPDATE_STARTUP_SECONDS, INFERENCE_HOST_SERVICE_DRAIN_COMMAND, inferenceHostServiceChildEnvironment, isWindowsSubsystemForLinux, inferenceHostServiceManifestPath, inferenceHostServiceDesiredPath, inferenceHostServiceLogPath, inferenceHostServiceRuntimePath, xmlEscape, plistEscape, systemdQuote, defaultRunCommand, managerName, assertRuntimeEnvironment, withoutConcurrencyLimit, assertServicePath, manifestGeneration, assertWorker, assertManifest, assertServiceRuntimeState, assertDesiredState, readInferenceHostServiceManifest, readManifestAcrossAtomicReplacement, readInferenceHostServiceRuntime, readRuntimeAcrossAtomicReplacement, readInferenceHostServiceDesired, readDesiredAcrossAtomicReplacement, writeDesired, runtimeEnvironment, serviceBootstrapArguments, windowsOwnedCommandLine, vbScriptString, windowsServiceLauncher, windowsTaskXml, systemdUnit, launchAgentPlist, sameWorker, sameServiceDefinition, sameWorkerSet, InferenceHostServiceManager, appendServiceLog, spawnInferenceHostServiceChild, runInferenceHostServiceSupervisor;
41375
+ var SERVICE_NAME, SYSTEMD_UNIT, LAUNCHD_LABEL, SERVICE_COOPERATIVE_STOP_SECONDS, SERVICE_UPDATE_STARTUP_SECONDS, INFERENCE_HOST_SERVICE_DRAIN_COMMAND, INFERENCE_HOST_SERVICE_QUIESCE_COMMAND, InferenceHostBootstrapRestartRequiredError, inferenceHostServiceChildEnvironment, isWindowsSubsystemForLinux, inferenceHostServiceManifestPath, inferenceHostServiceDesiredPath, inferenceHostServiceLogPath, inferenceHostServiceRuntimePath, xmlEscape, plistEscape, systemdQuote, defaultRunCommand, managerName, assertRuntimeEnvironment, withoutConcurrencyLimit, assertServicePath, manifestGeneration, assertWorker, assertManifest, assertServiceRuntimeState, assertDesiredState, readInferenceHostServiceManifest, readManifestAcrossAtomicReplacement, readInferenceHostServiceRuntime, readRuntimeAcrossAtomicReplacement, readInferenceHostServiceDesired, readDesiredAcrossAtomicReplacement, writeDesired, runtimeEnvironment, serviceBootstrapArguments, windowsOwnedCommandLine, vbScriptString, windowsServiceLauncher, windowsTaskXml, systemdUnit, launchAgentPlist, sameWorker, sameServiceDefinition, sameWorkerSet, InferenceHostServiceManager, appendServiceLog, spawnInferenceHostServiceChild, runInferenceHostServiceSupervisor;
41283
41376
  var init_service = __esm({
41284
41377
  "lib/inference-host/service.ts"() {
41285
41378
  "use strict";
41286
41379
  init_define_VTX_EXO_POLICY();
41287
41380
  init_define_VTX_GROK_POLICY();
41288
41381
  init_define_VTX_PI_MODEL_POLICY();
41382
+ init_agent_cli_release();
41289
41383
  init_runtime_policy_generated();
41290
41384
  init_durable_adapter();
41291
41385
  init_config();
@@ -41295,6 +41389,12 @@ var init_service = __esm({
41295
41389
  SERVICE_COOPERATIVE_STOP_SECONDS = 75;
41296
41390
  SERVICE_UPDATE_STARTUP_SECONDS = 600;
41297
41391
  INFERENCE_HOST_SERVICE_DRAIN_COMMAND = "vtx-inference-host-service-drain-v1";
41392
+ INFERENCE_HOST_SERVICE_QUIESCE_COMMAND = "vtx-inference-host-service-quiesce-v1";
41393
+ InferenceHostBootstrapRestartRequiredError = class extends Error {
41394
+ constructor() {
41395
+ super("The verified service bootstrap is ready for restart.");
41396
+ }
41397
+ };
41298
41398
  inferenceHostServiceChildEnvironment = (runtimeEnvironment2, inheritedEnvironment = process.env) => {
41299
41399
  const environment = { ...inheritedEnvironment };
41300
41400
  for (const key of Object.keys(environment)) {
@@ -42414,7 +42514,7 @@ ${result2.stderr}`)) {
42414
42514
  });
42415
42515
  };
42416
42516
  spawnInferenceHostServiceChild = async (manifest, worker, signal, onReady = () => {
42417
- }) => {
42517
+ }, quiesceSignal) => {
42418
42518
  const args = [
42419
42519
  manifest.script,
42420
42520
  "inference-host",
@@ -42476,6 +42576,12 @@ ${result2.stderr}`)) {
42476
42576
  child.stderr.on("data", (chunk) => writeTaggedOutput("stderr", chunk.toString("utf8")));
42477
42577
  child.stdin.on("error", () => {
42478
42578
  });
42579
+ const onQuiesce = () => {
42580
+ child.stdin.write(`${INFERENCE_HOST_SERVICE_QUIESCE_COMMAND}
42581
+ `);
42582
+ };
42583
+ if (quiesceSignal?.aborted) onQuiesce();
42584
+ else quiesceSignal?.addEventListener("abort", onQuiesce, { once: true });
42479
42585
  const onAbort = () => {
42480
42586
  child.stdin.end(`${INFERENCE_HOST_SERVICE_DRAIN_COMMAND}
42481
42587
  `);
@@ -42489,6 +42595,7 @@ ${result2.stderr}`)) {
42489
42595
  else signal.addEventListener("abort", onAbort, { once: true });
42490
42596
  child.once("error", (error48) => {
42491
42597
  signal.removeEventListener("abort", onAbort);
42598
+ quiesceSignal?.removeEventListener("abort", onQuiesce);
42492
42599
  if (cooperativeStopTimer) clearTimeout(cooperativeStopTimer);
42493
42600
  writeTaggedOutput("stdout", "", true);
42494
42601
  writeTaggedOutput("stderr", "", true);
@@ -42497,6 +42604,7 @@ ${result2.stderr}`)) {
42497
42604
  });
42498
42605
  child.once("exit", (code) => {
42499
42606
  signal.removeEventListener("abort", onAbort);
42607
+ quiesceSignal?.removeEventListener("abort", onQuiesce);
42500
42608
  if (cooperativeStopTimer) clearTimeout(cooperativeStopTimer);
42501
42609
  writeTaggedOutput("stdout", "", true);
42502
42610
  writeTaggedOutput("stderr", "", true);
@@ -42532,6 +42640,38 @@ ${result2.stderr}`)) {
42532
42640
  const desiredPath = manifestPath.replace(/\.service\.json$/u, ".service-desired.json");
42533
42641
  const runtimePath = manifestPath.replace(/\.service\.json$/u, ".service-runtime.json");
42534
42642
  const signal = options.signal ?? new AbortController().signal;
42643
+ const migrationQuiesce = new AbortController();
42644
+ const quiesceSignal = options.quiesceSignal ? AbortSignal.any([options.quiesceSignal, migrationQuiesce.signal]) : migrationQuiesce.signal;
42645
+ let bootstrapRestartRequired = false;
42646
+ let bootstrapMigrationErrorReported = false;
42647
+ const migrateSelectedBootstrap = async () => {
42648
+ if (!serviceScript || !options.controlLockPath || bootstrapRestartRequired || (options.bootstrapVersion ?? process.env.VTX_INFERENCE_HOST_BOOTSTRAP_VERSION) === agent_cli_release_default.package_version || quiesceSignal.aborted || signal.aborted) return;
42649
+ let controlLock;
42650
+ try {
42651
+ controlLock = await acquireInferenceHostProcessLock(options.controlLockPath);
42652
+ } catch (error48) {
42653
+ if (/already owns/iu.test(error48 instanceof Error ? error48.message : "")) return;
42654
+ throw error48;
42655
+ }
42656
+ try {
42657
+ const current = await readManifestAcrossAtomicReplacement(manifestPath);
42658
+ if (!current || current.generation !== appliedManifest.generation || !await readDesiredAcrossAtomicReplacement(desiredPath) || signal.aborted) return;
42659
+ let selected;
42660
+ try {
42661
+ selected = JSON.parse(await readFile9(manifestPath.replace(/\.service\.json$/u, ".service-release.json"), "utf8"));
42662
+ } catch (error48) {
42663
+ if (error48.code === "ENOENT") return;
42664
+ throw error48;
42665
+ }
42666
+ if (selected.schema_version !== "vtx_inference_service_release_v1" || selected.package_name !== "@vtxmacro/cli" || selected.package_version !== agent_cli_release_default.package_version || typeof selected.script !== "string" || resolve10(selected.script) !== serviceScript) return;
42667
+ const bytes3 = await readFile9(join13(dirname12(serviceScript), "vtx-service-bootstrap.js"), "utf8");
42668
+ await writeAtomicInferencePrivateFile(manifestPath.replace(/\.service\.json$/u, ".service-bootstrap.js"), bytes3);
42669
+ bootstrapRestartRequired = true;
42670
+ migrationQuiesce.abort();
42671
+ } finally {
42672
+ await controlLock.release();
42673
+ }
42674
+ };
42535
42675
  const sleep4 = options.sleep ?? (async (milliseconds) => {
42536
42676
  await new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds));
42537
42677
  });
@@ -42585,7 +42725,7 @@ ${result2.stderr}`)) {
42585
42725
  };
42586
42726
  const superviseWorker = async (serviceManifest, record4) => {
42587
42727
  let failures = 0;
42588
- while (!signal.aborted && !record4.controller.signal.aborted && await readDesiredAcrossAtomicReplacement(desiredPath)) {
42728
+ while (!signal.aborted && !quiesceSignal.aborted && !record4.controller.signal.aborted && await readDesiredAcrossAtomicReplacement(desiredPath)) {
42589
42729
  try {
42590
42730
  await updateRecord(record4, "starting");
42591
42731
  const attemptController = new AbortController();
@@ -42610,7 +42750,8 @@ ${result2.stderr}`)) {
42610
42750
  await updateRecord(record4, "running");
42611
42751
  }
42612
42752
  })();
42613
- }
42753
+ },
42754
+ quiesceSignal
42614
42755
  );
42615
42756
  } finally {
42616
42757
  launchSettled = true;
@@ -42626,7 +42767,7 @@ ${result2.stderr}`)) {
42626
42767
  });
42627
42768
  throw new Error("worker_cooperative_stop_timeout");
42628
42769
  }
42629
- if (signal.aborted || record4.controller.signal.aborted || !await readDesiredAcrossAtomicReplacement(desiredPath)) break;
42770
+ if (signal.aborted || quiesceSignal.aborted || record4.controller.signal.aborted || !await readDesiredAcrossAtomicReplacement(desiredPath)) break;
42630
42771
  failures = result2.uptimeMs >= runtime_policy_generated_default.external_inference.service.restart_stable_uptime_ms ? 0 : failures + 1;
42631
42772
  const retryAfterMs = Math.min(runtime_policy_generated_default.external_inference.service.restart_backoff_base_ms * 2 ** Math.min(failures, runtime_policy_generated_default.external_inference.service.restart_exponent_max), runtime_policy_generated_default.external_inference.service.restart_backoff_max_ms);
42632
42773
  await updateRecord(record4, "failed", "worker_exited");
@@ -42640,7 +42781,7 @@ ${result2.stderr}`)) {
42640
42781
  await waitRetry(retryAfterMs, record4.controller);
42641
42782
  } catch (error48) {
42642
42783
  if (error48 instanceof Error && error48.message === "worker_cooperative_stop_timeout") throw error48;
42643
- if (signal.aborted || record4.controller.signal.aborted) break;
42784
+ if (signal.aborted || quiesceSignal.aborted || record4.controller.signal.aborted) break;
42644
42785
  failures += 1;
42645
42786
  const retryAfterMs = Math.min(runtime_policy_generated_default.external_inference.service.restart_backoff_base_ms * 2 ** Math.min(failures, runtime_policy_generated_default.external_inference.service.restart_exponent_max), runtime_policy_generated_default.external_inference.service.restart_backoff_max_ms);
42646
42787
  await updateRecord(record4, "failed", "worker_launch_failed");
@@ -42723,14 +42864,41 @@ ${result2.stderr}`)) {
42723
42864
  };
42724
42865
  await reconcile(initialManifest);
42725
42866
  try {
42726
- while (!signal.aborted) {
42727
- while (!signal.aborted && await readDesiredAcrossAtomicReplacement(desiredPath)) {
42867
+ while (!signal.aborted && !quiesceSignal.aborted) {
42868
+ while (!signal.aborted && !quiesceSignal.aborted && await readDesiredAcrossAtomicReplacement(desiredPath)) {
42869
+ try {
42870
+ await migrateSelectedBootstrap();
42871
+ } catch {
42872
+ if (!bootstrapMigrationErrorReported) {
42873
+ bootstrapMigrationErrorReported = true;
42874
+ await appendServiceLog(appliedManifest.log_path, "service_bootstrap_migration_deferred");
42875
+ }
42876
+ }
42877
+ if (quiesceSignal.aborted) break;
42728
42878
  const storedNext = await readManifestAcrossAtomicReplacement(manifestPath);
42729
42879
  if (!storedNext) throw new Error("Inference-host service manifest is missing.");
42730
42880
  const next = withServiceScript(storedNext);
42731
42881
  if (next.generation !== appliedManifest.generation && next.generation !== rejectedManifestGeneration) await reconcile(next);
42732
42882
  await sleep4(250);
42733
42883
  }
42884
+ if (quiesceSignal.aborted && !signal.aborted) {
42885
+ let finished = false;
42886
+ const completion = Promise.allSettled([...workers.values()].map((record4) => record4.promise)).then((results) => {
42887
+ finished = true;
42888
+ return results;
42889
+ });
42890
+ while (!finished && !signal.aborted && await readDesiredAcrossAtomicReplacement(desiredPath)) {
42891
+ await Promise.race([completion, sleep4(250)]);
42892
+ }
42893
+ if (!finished) {
42894
+ await drainWorkers();
42895
+ break;
42896
+ }
42897
+ const settled = await completion;
42898
+ const failure2 = settled.find((result2) => result2.status === "rejected");
42899
+ if (failure2) throw failure2.reason;
42900
+ break;
42901
+ }
42734
42902
  await drainWorkers();
42735
42903
  if (signal.aborted || !await readDesiredAcrossAtomicReplacement(desiredPath)) break;
42736
42904
  const storedRestored = await readManifestAcrossAtomicReplacement(manifestPath);
@@ -42747,6 +42915,9 @@ ${result2.stderr}`)) {
42747
42915
  await runtimeWriteChain;
42748
42916
  await appendServiceLog(appliedManifest.log_path, "service_supervisor_stopped");
42749
42917
  }
42918
+ if (bootstrapRestartRequired && !signal.aborted && await readDesiredAcrossAtomicReplacement(desiredPath)) {
42919
+ throw new InferenceHostBootstrapRestartRequiredError();
42920
+ }
42750
42921
  };
42751
42922
  }
42752
42923
  });
@@ -43242,7 +43413,7 @@ Durable service:
43242
43413
  });
43243
43414
  child.unref();
43244
43415
  };
43245
- registerInferenceHostServiceControlInput = (input, abort) => {
43416
+ registerInferenceHostServiceControlInput = (input, abort, quiesce) => {
43246
43417
  let pendingControlInput = "";
43247
43418
  const onControlInput = (chunk) => {
43248
43419
  pendingControlInput = `${pendingControlInput}${chunk.toString()}`.slice(-4096);
@@ -43250,6 +43421,8 @@ Durable service:
43250
43421
  pendingControlInput = lines.pop() ?? "";
43251
43422
  if (lines.some((line) => line === INFERENCE_HOST_SERVICE_DRAIN_COMMAND)) {
43252
43423
  abort();
43424
+ } else if (lines.some((line) => line === INFERENCE_HOST_SERVICE_QUIESCE_COMMAND)) {
43425
+ quiesce?.();
43253
43426
  }
43254
43427
  };
43255
43428
  input.on("data", onControlInput);
@@ -43259,13 +43432,13 @@ Durable service:
43259
43432
  input.pause();
43260
43433
  };
43261
43434
  };
43262
- defaultRegisterLifecycleSignalHandlers = (abort) => {
43435
+ defaultRegisterLifecycleSignalHandlers = (abort, quiesce) => {
43263
43436
  const onSigint = () => abort("SIGINT");
43264
43437
  const onSigterm = () => abort("SIGTERM");
43265
43438
  process.on("SIGINT", onSigint);
43266
43439
  process.on("SIGTERM", onSigterm);
43267
43440
  const serviceChild = process.env.VTX_INFERENCE_HOST_SERVICE_CHILD === "1";
43268
- const unregisterControlInput = serviceChild ? registerInferenceHostServiceControlInput(process.stdin, onSigterm) : () => {
43441
+ const unregisterControlInput = serviceChild ? registerInferenceHostServiceControlInput(process.stdin, onSigterm, quiesce) : () => {
43269
43442
  };
43270
43443
  return () => {
43271
43444
  process.off("SIGINT", onSigint);
@@ -43275,12 +43448,14 @@ Durable service:
43275
43448
  };
43276
43449
  lifecycleCancellation = (dependencies) => {
43277
43450
  const controller = new AbortController();
43451
+ const quiesceController = new AbortController();
43278
43452
  let unregistered = false;
43279
43453
  const unregisterSignalHandlers = (dependencies.registerLifecycleSignalHandlers ?? defaultRegisterLifecycleSignalHandlers)(() => {
43280
43454
  if (!controller.signal.aborted) controller.abort();
43281
- });
43455
+ }, () => quiesceController.abort());
43282
43456
  return {
43283
43457
  signal: controller.signal,
43458
+ quiesceSignal: quiesceController.signal,
43284
43459
  unregister: () => {
43285
43460
  if (unregistered) return;
43286
43461
  unregistered = true;
@@ -43486,11 +43661,12 @@ Durable service:
43486
43661
  adapter: agentAdapter,
43487
43662
  dataRequestTimeoutMs: await loadInferenceAgentDataRequestTimeoutMs()
43488
43663
  },
43489
- signal: options.signal
43664
+ signal: options.signal,
43665
+ quiesceSignal: options.quiesceSignal
43490
43666
  }).run();
43491
43667
  };
43492
- preparePortableDurableAdapter = async (adapterId, config2, signal) => {
43493
- const adapter = adapterId === "copilot" ? new CopilotSubscriptionAdapter() : adapterId === "grok-build" ? new GrokBuildAdapter({ stateRoot: `${config2.statePath}.grok`, grokHome: `${config2.statePath}.grok-home` }) : adapterId === "pi" ? new PiAdapter({
43668
+ preparePortableDurableAdapter = async (adapterId, config2, signal, emitDiagnosticEvent) => {
43669
+ const adapter = adapterId === "copilot" ? new CopilotSubscriptionAdapter() : adapterId === "grok-build" ? new GrokBuildAdapter({ stateRoot: `${config2.statePath}.grok`, grokHome: `${config2.statePath}.grok-home`, emitDiagnosticEvent }) : adapterId === "pi" ? new PiAdapter({
43494
43670
  credentialStore: createPiCredentialStore(config2),
43495
43671
  stateRoot: `${config2.statePath}.pi`,
43496
43672
  qualifiedModels: await loadPiModelPolicy()
@@ -43557,7 +43733,8 @@ Durable service:
43557
43733
  adapter: agentAdapter,
43558
43734
  dataRequestTimeoutMs: await loadInferenceAgentDataRequestTimeoutMs()
43559
43735
  },
43560
- signal: options.signal
43736
+ signal: options.signal,
43737
+ quiesceSignal: options.quiesceSignal
43561
43738
  }).run();
43562
43739
  };
43563
43740
  login = async (config2, parsed, env, dependencies, warnings) => {
@@ -44335,8 +44512,8 @@ Waiting for approval...
44335
44512
  const emitDiagnosticEvent = parsed.json ? (event) => (dependencies.emitStdout ?? ((text) => {
44336
44513
  process.stdout.write(text);
44337
44514
  }))(`${JSON.stringify(event)}
44338
- `) : adapterId === "codex" ? (event) => {
44339
- if (!["codex_app_server_closed", "codex_app_server_session_started"].includes(
44515
+ `) : adapterId === "codex" || adapterId === "grok-build" ? (event) => {
44516
+ if (!["codex_app_server_closed", "codex_app_server_session_started", "grok_tool_validation_failed"].includes(
44340
44517
  String(event.event ?? "")
44341
44518
  )) return;
44342
44519
  (dependencies.emitStderr ?? ((text) => {
@@ -44345,7 +44522,16 @@ Waiting for approval...
44345
44522
  `);
44346
44523
  } : void 0;
44347
44524
  if (adapterId !== "codex") {
44348
- const prepared = adapterId === "exo" ? await (dependencies.prepareExoAdapter ?? prepareExoAdapter)(config2, parsed.exoRoot, cancellation.signal) : await (dependencies.prepareDurableAdapter ?? preparePortableDurableAdapter)(adapterId, config2, cancellation.signal);
44525
+ const diagnosticState = await readInferenceHostLocalState(config2.statePath);
44526
+ const emitPortableDiagnostic = (event) => emitDiagnosticEvent?.({
44527
+ ...event,
44528
+ schema_version: "vtx_inference_host_diagnostic_v1",
44529
+ at: (/* @__PURE__ */ new Date()).toISOString(),
44530
+ instance_name: config2.instanceName,
44531
+ host_id: diagnosticState?.host_id ?? null,
44532
+ host_runtime_version: INFERENCE_HOST_CLI_VERSION
44533
+ });
44534
+ const prepared = adapterId === "exo" ? await (dependencies.prepareExoAdapter ?? prepareExoAdapter)(config2, parsed.exoRoot, cancellation.signal) : await (dependencies.prepareDurableAdapter ?? preparePortableDurableAdapter)(adapterId, config2, cancellation.signal, emitPortableDiagnostic);
44349
44535
  const summary = await (dependencies.runPortableDurableAdapter ?? defaultRunPortableDurableAdapter)({
44350
44536
  config: config2,
44351
44537
  credentialStore: store,
@@ -44355,7 +44541,8 @@ Waiting for approval...
44355
44541
  once: parsed.once,
44356
44542
  processLock,
44357
44543
  emitDiagnosticEvent,
44358
- signal: cancellation.signal
44544
+ signal: cancellation.signal,
44545
+ quiesceSignal: cancellation.quiesceSignal
44359
44546
  });
44360
44547
  return {
44361
44548
  exitCode: 0,
@@ -44395,7 +44582,8 @@ Waiting for approval...
44395
44582
  env,
44396
44583
  processLock,
44397
44584
  emitDiagnosticEvent,
44398
- signal: cancellation.signal
44585
+ signal: cancellation.signal,
44586
+ quiesceSignal: cancellation.quiesceSignal
44399
44587
  });
44400
44588
  return {
44401
44589
  exitCode: 0,
@@ -45719,10 +45907,15 @@ Waiting for approval...
45719
45907
  parsed.serviceManifestPath,
45720
45908
  {
45721
45909
  signal: cancellation.signal,
45722
- serviceScript: resolve11(process.argv[1] ?? "")
45910
+ quiesceSignal: cancellation.quiesceSignal,
45911
+ serviceScript: resolve11(process.argv[1] ?? ""),
45912
+ controlLockPath: `${supervisorConfig.supervisorProcessLockPath}.service-control`
45723
45913
  }
45724
45914
  );
45725
45915
  return { exitCode: 0, stdout: "", stderr: "" };
45916
+ } catch (error48) {
45917
+ if (error48 instanceof InferenceHostBootstrapRestartRequiredError) return { exitCode: 75, stdout: "", stderr: "" };
45918
+ throw error48;
45726
45919
  } finally {
45727
45920
  cancellation.unregister();
45728
45921
  await serviceLock.release();
@@ -49775,6 +49968,8 @@ var init_runtime_policy_generated2 = __esm({
49775
49968
  },
49776
49969
  external_inference: {
49777
49970
  service: {
49971
+ update_check_interval_ms: 3e5,
49972
+ update_registry_timeout_ms: 15e3,
49778
49973
  startup_max_attempts: 40,
49779
49974
  startup_poll_interval_ms: 250,
49780
49975
  restart_stable_uptime_ms: 6e4,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtxmacro/cli",
3
- "version": "2026.9.29",
3
+ "version": "2026.9.31",
4
4
  "description": "VTX Macro CLI, MCP server, and durable external inference host.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",