@vtxmacro/cli 2026.9.28 → 2026.9.30

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
 
@@ -70,7 +74,7 @@ and VTX grant. VTX resolves the package-owned pinned Copilot platform runtime
70
74
  explicitly and rejects an `auto`-only catalog because it cannot prove the
71
75
  effective model.
72
76
 
73
- Grok Build is a durable preview using its package-owned official runtime and an
77
+ Grok Build is a durable adapter using its package-owned official runtime and an
74
78
  isolated local Grok OAuth session. Run `vtx inference-host grok-login --instance grok-1`
75
79
  after the normal VTX host login, then `vtx inference-host service install --adapter grok-build --instance grok-1`.
76
80
  Provider mode has no tools; Main Agent mode exposes only assignment-scoped VTX tools.
@@ -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.28",
19
+ package_version: "2026.9.30",
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.28",
80
+ package_version: "2026.9.30",
81
81
  codex_package_name: "@openai/codex",
82
82
  codex_version: "0.153.3",
83
83
  copilot_sdk_package_name: "@github/copilot-sdk",
@@ -34882,6 +34882,7 @@ var init_grok_runtime = __esm({
34882
34882
  env: {
34883
34883
  ...grokRuntimeEnvironment(options.grokHome),
34884
34884
  ...options.config ? { GROK_CONFIG: JSON.stringify(options.config) } : {},
34885
+ ...options.agentName ? { GROK_AGENT: options.agentName } : {},
34885
34886
  ...typeof grokObject(options.config?.endpoints).cli_chat_proxy_base_url === "string" ? { GROK_CLI_CHAT_PROXY_BASE_URL: String(grokObject(options.config?.endpoints).cli_chat_proxy_base_url) } : {}
34886
34887
  },
34887
34888
  stdio: ["pipe", "pipe", "pipe"]
@@ -35540,6 +35541,9 @@ var init_grok_adapter = __esm({
35540
35541
  binaryPath: binary.path,
35541
35542
  grokHome: this.grokHome,
35542
35543
  cwd: workspace,
35544
+ // Explicit native selection makes the ACP profile authoritative even
35545
+ // when the remote model catalog recommends a different strict harness.
35546
+ agentName: "grok-build",
35543
35547
  config: {
35544
35548
  endpoints: { cli_chat_proxy_base_url: proxy.baseUrl },
35545
35549
  features: { turn_summary: false, title_refresh: false, telemetry: false, memory: false }
@@ -35576,11 +35580,6 @@ var init_grok_adapter = __esm({
35576
35580
  _meta: metadata
35577
35581
  }, input.deadlineAtMs, input.signal);
35578
35582
  }
35579
- await session.request("session/set_model", {
35580
- sessionId,
35581
- modelId: input.requestedModel,
35582
- _meta: { reasoningEffort: input.requestedReasoningEffort }
35583
- }, input.deadlineAtMs, input.signal);
35584
35583
  assertSelection(created, input.requestedModel, input.requestedReasoningEffort);
35585
35584
  const thread = {
35586
35585
  threadId: sessionId,
@@ -37894,6 +37893,13 @@ var init_runner = __esm({
37894
37893
  };
37895
37894
  let removeAbortListener = () => {
37896
37895
  };
37896
+ let removeQuiesceListener = () => {
37897
+ };
37898
+ let quiesceRequested = Boolean(this.options.quiesceSignal?.aborted);
37899
+ let wakeQuiesce;
37900
+ const quiesceWake = new Promise((resolve12) => {
37901
+ wakeQuiesce = resolve12;
37902
+ });
37897
37903
  let drainRequested = false;
37898
37904
  let drainReason = "stopped";
37899
37905
  let receipt;
@@ -37932,6 +37938,13 @@ var init_runner = __esm({
37932
37938
  wakeDrain();
37933
37939
  };
37934
37940
  try {
37941
+ const onQuiesce = () => {
37942
+ quiesceRequested = true;
37943
+ wakeQuiesce();
37944
+ };
37945
+ this.options.quiesceSignal?.addEventListener("abort", onQuiesce, { once: true });
37946
+ removeQuiesceListener = () => this.options.quiesceSignal?.removeEventListener("abort", onQuiesce);
37947
+ if (quiesceRequested) return { claimed, completed, failed, drain_reason: "service_update" };
37935
37948
  unregisterSignals = (this.dependencies.registerSignalHandlers ?? defaultRegisterSignalHandlers)(requestDrain);
37936
37949
  if (this.options.signal) {
37937
37950
  const onAbort = () => requestDrain("aborted");
@@ -38034,8 +38047,9 @@ var init_runner = __esm({
38034
38047
  await mcp.initialize({ signal: this.options.signal });
38035
38048
  if (settings.agentRuntime) {
38036
38049
  const agentRuntimeSettings = settings.agentRuntime;
38050
+ const agentIdleSignal = this.options.quiesceSignal ? AbortSignal.any([agentAbort.signal, this.options.quiesceSignal]) : agentAbort.signal;
38037
38051
  agentLoop = (async () => {
38038
- while (!agentAbort.signal.aborted) {
38052
+ while (!agentAbort.signal.aborted && !quiesceRequested) {
38039
38053
  const existingFence = await readInferenceAgentFailureFence(
38040
38054
  agentRuntimeSettings.statePath
38041
38055
  );
@@ -38068,7 +38082,7 @@ var init_runner = __esm({
38068
38082
  });
38069
38083
  if (this.options.once) return;
38070
38084
  try {
38071
- await sleep4(Math.max(MIN_SLEEP_MS, retryAtMs - now()), agentAbort.signal);
38085
+ await sleep4(Math.max(MIN_SLEEP_MS, retryAtMs - now()), agentIdleSignal);
38072
38086
  } catch {
38073
38087
  return;
38074
38088
  }
@@ -38083,7 +38097,8 @@ var init_runner = __esm({
38083
38097
  statePath: agentRuntimeSettings.statePath,
38084
38098
  controlClient: createInferenceAgentControlClient(mcp),
38085
38099
  adapter: agentRuntimeSettings.adapter,
38086
- dataRequestTimeoutMs: agentRuntimeSettings.dataRequestTimeoutMs
38100
+ dataRequestTimeoutMs: agentRuntimeSettings.dataRequestTimeoutMs,
38101
+ quiesceSignal: this.options.quiesceSignal
38087
38102
  });
38088
38103
  try {
38089
38104
  if (this.options.once) {
@@ -38152,7 +38167,7 @@ var init_runner = __esm({
38152
38167
  );
38153
38168
  if (this.options.once || retryAtMs === null) return;
38154
38169
  try {
38155
- await sleep4(Math.max(MIN_SLEEP_MS, retryAtMs - now()), agentAbort.signal);
38170
+ await sleep4(Math.max(MIN_SLEEP_MS, retryAtMs - now()), agentIdleSignal);
38156
38171
  } catch {
38157
38172
  return;
38158
38173
  }
@@ -38168,7 +38183,9 @@ var init_runner = __esm({
38168
38183
  const retained = await readInferenceAgentRuntimeState(agentRuntimeSettings.statePath);
38169
38184
  if (!retained || retained.schema_version !== "vtx_inference_agent_runtime_v3" || !retained.pending_turn?.terminal_receipt) return;
38170
38185
  if (this.options.once) return;
38171
- await sleep4(1e3, agentAbort.signal);
38186
+ await sleep4(1e3, agentIdleSignal).catch((error49) => {
38187
+ if (!quiesceRequested) throw error49;
38188
+ });
38172
38189
  continue;
38173
38190
  }
38174
38191
  if (error48 instanceof InferenceHostRunnerError && error48.code === "agent_terminal_failure_fenced") {
@@ -38180,7 +38197,9 @@ var init_runner = __esm({
38180
38197
  });
38181
38198
  if (this.options.once) return;
38182
38199
  try {
38183
- await sleep4(1e3, agentAbort.signal);
38200
+ await sleep4(1e3, agentIdleSignal).catch((error49) => {
38201
+ if (!quiesceRequested) throw error49;
38202
+ });
38184
38203
  } catch {
38185
38204
  return;
38186
38205
  }
@@ -39011,11 +39030,11 @@ var init_runner = __esm({
39011
39030
  )
39012
39031
  });
39013
39032
  }
39014
- while (!drainRequested && recoveryQueue.length > 0 && (settings.maxConcurrency === null || active.size < settings.maxConcurrency)) {
39033
+ while (!drainRequested && !quiesceRequested && recoveryQueue.length > 0 && (settings.maxConcurrency === null || active.size < settings.maxConcurrency)) {
39015
39034
  const recovery = recoveryQueue.shift();
39016
39035
  launchClaim(recovery.claim, recovery);
39017
39036
  }
39018
- while (!drainRequested) {
39037
+ while (!drainRequested && !quiesceRequested) {
39019
39038
  const currentTime = now();
39020
39039
  if (providerFailureCooldownReason !== null && currentTime >= providerFailureRetryAtMs) {
39021
39040
  emitDiagnostic("provider_cooldown_cleared", {
@@ -39139,12 +39158,12 @@ var init_runner = __esm({
39139
39158
  nextClaimAt = Math.max(nextClaimAt, nextAdvertisementAttemptAt);
39140
39159
  }
39141
39160
  }
39142
- while (recoveryQueue.length > 0 && (settings.maxConcurrency === null || active.size < settings.maxConcurrency)) {
39161
+ while (!quiesceRequested && recoveryQueue.length > 0 && (settings.maxConcurrency === null || active.size < settings.maxConcurrency)) {
39143
39162
  const recovery = recoveryQueue.shift();
39144
39163
  launchClaim(recovery.claim, recovery);
39145
39164
  }
39146
39165
  let claimOpportunities = 0;
39147
- while (!drainRequested && (!yieldRenewalToClaim || claimOpportunities === 0) && recoveryQueue.length === 0 && (!this.options.once || !onceClaimed) && (settings.maxConcurrency === null || active.size < settings.maxConcurrency) && pendingClaimPromotions === 0 && now() >= Math.max(
39166
+ 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(
39148
39167
  nextClaimAt,
39149
39168
  claimAvailabilityBackoff?.untilMs ?? 0,
39150
39169
  providerRetryAtMs ?? 0,
@@ -39166,6 +39185,7 @@ var init_runner = __esm({
39166
39185
  let claim;
39167
39186
  let claimTiming;
39168
39187
  const claimCallStartedAtMs = now();
39188
+ if (quiesceRequested) break;
39169
39189
  try {
39170
39190
  claimOpportunities += 1;
39171
39191
  if (claimPreparation.unsent === request) claimPreparation.unsent = null;
@@ -39416,9 +39436,14 @@ var init_runner = __esm({
39416
39436
  await Promise.race([
39417
39437
  sleep4(Math.max(MIN_SLEEP_MS, wakeAt - now())),
39418
39438
  drainSignal,
39439
+ quiesceWake,
39419
39440
  ...active.values()
39420
39441
  ]);
39421
39442
  }
39443
+ if (quiesceRequested && !drainRequested) {
39444
+ await Promise.race([Promise.allSettled([...active.values(), agentLoop]), drainSignal]);
39445
+ if (!drainRequested) requestDrain("service_update");
39446
+ }
39422
39447
  stopHostHeartbeatLoop();
39423
39448
  if (providerDispatchWatchdogDrainRequested) {
39424
39449
  await settlesWithin(hostHeartbeatLoop, MIN_SLEEP_MS);
@@ -39476,6 +39501,7 @@ var init_runner = __esm({
39476
39501
  await hostHeartbeatLoop;
39477
39502
  }
39478
39503
  removeAbortListener();
39504
+ removeQuiesceListener();
39479
39505
  unregisterSignals();
39480
39506
  try {
39481
39507
  await this.dependencies.codexAdapter.close?.();
@@ -40509,9 +40535,9 @@ var init_runner = __esm({
40509
40535
  async run(signal) {
40510
40536
  let exitedNormally = false;
40511
40537
  try {
40512
- while (!this.stopped && !signal?.aborted) {
40538
+ while (!this.stopped && !signal?.aborted && !this.options.quiesceSignal?.aborted) {
40513
40539
  const nextWakeAt = await this.runOnce(signal);
40514
- if (this.stopped || signal?.aborted) break;
40540
+ if (this.stopped || signal?.aborted || this.options.quiesceSignal?.aborted) break;
40515
40541
  await this.waitUntilWake(nextWakeAt, signal);
40516
40542
  }
40517
40543
  exitedNormally = true;
@@ -40534,6 +40560,7 @@ var init_runner = __esm({
40534
40560
  }
40535
40561
  async runOnce(signal) {
40536
40562
  const nowIso = () => new Date(this.now()).toISOString();
40563
+ if (this.options.quiesceSignal?.aborted) return this.now();
40537
40564
  const recoveredState = await readInferenceAgentRuntimeState(this.options.statePath);
40538
40565
  const failureFence = await readInferenceAgentFailureFence(this.options.statePath);
40539
40566
  if (failureFence?.retry_at === null) {
@@ -40589,6 +40616,7 @@ var init_runner = __esm({
40589
40616
  }
40590
40617
  }
40591
40618
  if (!state) {
40619
+ if (this.options.quiesceSignal?.aborted) return this.now();
40592
40620
  const assignment2 = await this.options.controlClient.nextAssignment({
40593
40621
  operation_id: randomUUID7(),
40594
40622
  host_id: this.options.hostId,
@@ -40691,6 +40719,7 @@ var init_runner = __esm({
40691
40719
  await this.handleCancelledAssignment(state, signal);
40692
40720
  return this.now() + (this.options.idlePollMs ?? 5e3);
40693
40721
  }
40722
+ if (this.options.quiesceSignal?.aborted) return this.now();
40694
40723
  assignment = {
40695
40724
  ...assignment,
40696
40725
  lease_expires_at: preTurnHeartbeat.lease_expires_at
@@ -41086,9 +41115,14 @@ var init_runner = __esm({
41086
41115
  }
41087
41116
  async waitUntilWake(nextWakeAtMs, signal) {
41088
41117
  const heartbeatIntervalMs = this.options.heartbeatIntervalMs ?? 3e3;
41089
- while (!this.stopped && !signal?.aborted && this.now() < nextWakeAtMs) {
41090
- await this.sleep(Math.min(heartbeatIntervalMs, nextWakeAtMs - this.now()), signal);
41091
- if (this.stopped || signal?.aborted) return "stopped";
41118
+ while (!this.stopped && !signal?.aborted && !this.options.quiesceSignal?.aborted && this.now() < nextWakeAtMs) {
41119
+ const waitSignal = this.options.quiesceSignal ? AbortSignal.any([this.options.quiesceSignal, ...signal ? [signal] : []]) : signal;
41120
+ try {
41121
+ await this.sleep(Math.min(heartbeatIntervalMs, nextWakeAtMs - this.now()), waitSignal);
41122
+ } catch (error48) {
41123
+ if (!this.options.quiesceSignal?.aborted) throw error48;
41124
+ }
41125
+ if (this.stopped || signal?.aborted || this.options.quiesceSignal?.aborted) return "stopped";
41092
41126
  if (this.now() >= nextWakeAtMs) return "ready";
41093
41127
  const state = await readInferenceAgentRuntimeState(this.options.statePath);
41094
41128
  if (state?.schema_version !== "vtx_inference_agent_runtime_v3" || state.adapter_id !== this.options.adapterId) return "cancelled";
@@ -41113,7 +41147,7 @@ var init_runner = __esm({
41113
41147
  updated_at: requestedAt
41114
41148
  });
41115
41149
  }
41116
- return this.stopped || signal?.aborted ? "stopped" : "ready";
41150
+ return this.stopped || signal?.aborted || this.options.quiesceSignal?.aborted ? "stopped" : "ready";
41117
41151
  }
41118
41152
  async releaseForHostStop(reasonCode = "host_stopped") {
41119
41153
  const recovered = await readInferenceAgentRuntimeState(this.options.statePath);
@@ -41240,6 +41274,8 @@ var init_runtime_policy_generated = __esm({
41240
41274
  },
41241
41275
  external_inference: {
41242
41276
  service: {
41277
+ update_check_interval_ms: 3e5,
41278
+ update_registry_timeout_ms: 15e3,
41243
41279
  startup_max_attempts: 40,
41244
41280
  startup_poll_interval_ms: 250,
41245
41281
  restart_stable_uptime_ms: 6e4,
@@ -41280,13 +41316,14 @@ import { createWriteStream, readFileSync } from "node:fs";
41280
41316
  import { access as access4, chmod as chmod3, mkdir as mkdir3, readFile as readFile9, rm as rm10, writeFile as writeFile4 } from "node:fs/promises";
41281
41317
  import { homedir as homedir2 } from "node:os";
41282
41318
  import { dirname as dirname12, join as join13, resolve as resolve10 } from "node:path";
41283
- 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;
41319
+ 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;
41284
41320
  var init_service = __esm({
41285
41321
  "lib/inference-host/service.ts"() {
41286
41322
  "use strict";
41287
41323
  init_define_VTX_EXO_POLICY();
41288
41324
  init_define_VTX_GROK_POLICY();
41289
41325
  init_define_VTX_PI_MODEL_POLICY();
41326
+ init_agent_cli_release();
41290
41327
  init_runtime_policy_generated();
41291
41328
  init_durable_adapter();
41292
41329
  init_config();
@@ -41296,6 +41333,12 @@ var init_service = __esm({
41296
41333
  SERVICE_COOPERATIVE_STOP_SECONDS = 75;
41297
41334
  SERVICE_UPDATE_STARTUP_SECONDS = 600;
41298
41335
  INFERENCE_HOST_SERVICE_DRAIN_COMMAND = "vtx-inference-host-service-drain-v1";
41336
+ INFERENCE_HOST_SERVICE_QUIESCE_COMMAND = "vtx-inference-host-service-quiesce-v1";
41337
+ InferenceHostBootstrapRestartRequiredError = class extends Error {
41338
+ constructor() {
41339
+ super("The verified service bootstrap is ready for restart.");
41340
+ }
41341
+ };
41299
41342
  inferenceHostServiceChildEnvironment = (runtimeEnvironment2, inheritedEnvironment = process.env) => {
41300
41343
  const environment = { ...inheritedEnvironment };
41301
41344
  for (const key of Object.keys(environment)) {
@@ -42415,7 +42458,7 @@ ${result2.stderr}`)) {
42415
42458
  });
42416
42459
  };
42417
42460
  spawnInferenceHostServiceChild = async (manifest, worker, signal, onReady = () => {
42418
- }) => {
42461
+ }, quiesceSignal) => {
42419
42462
  const args = [
42420
42463
  manifest.script,
42421
42464
  "inference-host",
@@ -42477,6 +42520,12 @@ ${result2.stderr}`)) {
42477
42520
  child.stderr.on("data", (chunk) => writeTaggedOutput("stderr", chunk.toString("utf8")));
42478
42521
  child.stdin.on("error", () => {
42479
42522
  });
42523
+ const onQuiesce = () => {
42524
+ child.stdin.write(`${INFERENCE_HOST_SERVICE_QUIESCE_COMMAND}
42525
+ `);
42526
+ };
42527
+ if (quiesceSignal?.aborted) onQuiesce();
42528
+ else quiesceSignal?.addEventListener("abort", onQuiesce, { once: true });
42480
42529
  const onAbort = () => {
42481
42530
  child.stdin.end(`${INFERENCE_HOST_SERVICE_DRAIN_COMMAND}
42482
42531
  `);
@@ -42490,6 +42539,7 @@ ${result2.stderr}`)) {
42490
42539
  else signal.addEventListener("abort", onAbort, { once: true });
42491
42540
  child.once("error", (error48) => {
42492
42541
  signal.removeEventListener("abort", onAbort);
42542
+ quiesceSignal?.removeEventListener("abort", onQuiesce);
42493
42543
  if (cooperativeStopTimer) clearTimeout(cooperativeStopTimer);
42494
42544
  writeTaggedOutput("stdout", "", true);
42495
42545
  writeTaggedOutput("stderr", "", true);
@@ -42498,6 +42548,7 @@ ${result2.stderr}`)) {
42498
42548
  });
42499
42549
  child.once("exit", (code) => {
42500
42550
  signal.removeEventListener("abort", onAbort);
42551
+ quiesceSignal?.removeEventListener("abort", onQuiesce);
42501
42552
  if (cooperativeStopTimer) clearTimeout(cooperativeStopTimer);
42502
42553
  writeTaggedOutput("stdout", "", true);
42503
42554
  writeTaggedOutput("stderr", "", true);
@@ -42533,6 +42584,38 @@ ${result2.stderr}`)) {
42533
42584
  const desiredPath = manifestPath.replace(/\.service\.json$/u, ".service-desired.json");
42534
42585
  const runtimePath = manifestPath.replace(/\.service\.json$/u, ".service-runtime.json");
42535
42586
  const signal = options.signal ?? new AbortController().signal;
42587
+ const migrationQuiesce = new AbortController();
42588
+ const quiesceSignal = options.quiesceSignal ? AbortSignal.any([options.quiesceSignal, migrationQuiesce.signal]) : migrationQuiesce.signal;
42589
+ let bootstrapRestartRequired = false;
42590
+ let bootstrapMigrationErrorReported = false;
42591
+ const migrateSelectedBootstrap = async () => {
42592
+ 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;
42593
+ let controlLock;
42594
+ try {
42595
+ controlLock = await acquireInferenceHostProcessLock(options.controlLockPath);
42596
+ } catch (error48) {
42597
+ if (/already owns/iu.test(error48 instanceof Error ? error48.message : "")) return;
42598
+ throw error48;
42599
+ }
42600
+ try {
42601
+ const current = await readManifestAcrossAtomicReplacement(manifestPath);
42602
+ if (!current || current.generation !== appliedManifest.generation || !await readDesiredAcrossAtomicReplacement(desiredPath) || signal.aborted) return;
42603
+ let selected;
42604
+ try {
42605
+ selected = JSON.parse(await readFile9(manifestPath.replace(/\.service\.json$/u, ".service-release.json"), "utf8"));
42606
+ } catch (error48) {
42607
+ if (error48.code === "ENOENT") return;
42608
+ throw error48;
42609
+ }
42610
+ 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;
42611
+ const bytes3 = await readFile9(join13(dirname12(serviceScript), "vtx-service-bootstrap.js"), "utf8");
42612
+ await writeAtomicInferencePrivateFile(manifestPath.replace(/\.service\.json$/u, ".service-bootstrap.js"), bytes3);
42613
+ bootstrapRestartRequired = true;
42614
+ migrationQuiesce.abort();
42615
+ } finally {
42616
+ await controlLock.release();
42617
+ }
42618
+ };
42536
42619
  const sleep4 = options.sleep ?? (async (milliseconds) => {
42537
42620
  await new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds));
42538
42621
  });
@@ -42586,7 +42669,7 @@ ${result2.stderr}`)) {
42586
42669
  };
42587
42670
  const superviseWorker = async (serviceManifest, record4) => {
42588
42671
  let failures = 0;
42589
- while (!signal.aborted && !record4.controller.signal.aborted && await readDesiredAcrossAtomicReplacement(desiredPath)) {
42672
+ while (!signal.aborted && !quiesceSignal.aborted && !record4.controller.signal.aborted && await readDesiredAcrossAtomicReplacement(desiredPath)) {
42590
42673
  try {
42591
42674
  await updateRecord(record4, "starting");
42592
42675
  const attemptController = new AbortController();
@@ -42611,7 +42694,8 @@ ${result2.stderr}`)) {
42611
42694
  await updateRecord(record4, "running");
42612
42695
  }
42613
42696
  })();
42614
- }
42697
+ },
42698
+ quiesceSignal
42615
42699
  );
42616
42700
  } finally {
42617
42701
  launchSettled = true;
@@ -42627,7 +42711,7 @@ ${result2.stderr}`)) {
42627
42711
  });
42628
42712
  throw new Error("worker_cooperative_stop_timeout");
42629
42713
  }
42630
- if (signal.aborted || record4.controller.signal.aborted || !await readDesiredAcrossAtomicReplacement(desiredPath)) break;
42714
+ if (signal.aborted || quiesceSignal.aborted || record4.controller.signal.aborted || !await readDesiredAcrossAtomicReplacement(desiredPath)) break;
42631
42715
  failures = result2.uptimeMs >= runtime_policy_generated_default.external_inference.service.restart_stable_uptime_ms ? 0 : failures + 1;
42632
42716
  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);
42633
42717
  await updateRecord(record4, "failed", "worker_exited");
@@ -42641,7 +42725,7 @@ ${result2.stderr}`)) {
42641
42725
  await waitRetry(retryAfterMs, record4.controller);
42642
42726
  } catch (error48) {
42643
42727
  if (error48 instanceof Error && error48.message === "worker_cooperative_stop_timeout") throw error48;
42644
- if (signal.aborted || record4.controller.signal.aborted) break;
42728
+ if (signal.aborted || quiesceSignal.aborted || record4.controller.signal.aborted) break;
42645
42729
  failures += 1;
42646
42730
  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);
42647
42731
  await updateRecord(record4, "failed", "worker_launch_failed");
@@ -42724,14 +42808,41 @@ ${result2.stderr}`)) {
42724
42808
  };
42725
42809
  await reconcile(initialManifest);
42726
42810
  try {
42727
- while (!signal.aborted) {
42728
- while (!signal.aborted && await readDesiredAcrossAtomicReplacement(desiredPath)) {
42811
+ while (!signal.aborted && !quiesceSignal.aborted) {
42812
+ while (!signal.aborted && !quiesceSignal.aborted && await readDesiredAcrossAtomicReplacement(desiredPath)) {
42813
+ try {
42814
+ await migrateSelectedBootstrap();
42815
+ } catch {
42816
+ if (!bootstrapMigrationErrorReported) {
42817
+ bootstrapMigrationErrorReported = true;
42818
+ await appendServiceLog(appliedManifest.log_path, "service_bootstrap_migration_deferred");
42819
+ }
42820
+ }
42821
+ if (quiesceSignal.aborted) break;
42729
42822
  const storedNext = await readManifestAcrossAtomicReplacement(manifestPath);
42730
42823
  if (!storedNext) throw new Error("Inference-host service manifest is missing.");
42731
42824
  const next = withServiceScript(storedNext);
42732
42825
  if (next.generation !== appliedManifest.generation && next.generation !== rejectedManifestGeneration) await reconcile(next);
42733
42826
  await sleep4(250);
42734
42827
  }
42828
+ if (quiesceSignal.aborted && !signal.aborted) {
42829
+ let finished = false;
42830
+ const completion = Promise.allSettled([...workers.values()].map((record4) => record4.promise)).then((results) => {
42831
+ finished = true;
42832
+ return results;
42833
+ });
42834
+ while (!finished && !signal.aborted && await readDesiredAcrossAtomicReplacement(desiredPath)) {
42835
+ await Promise.race([completion, sleep4(250)]);
42836
+ }
42837
+ if (!finished) {
42838
+ await drainWorkers();
42839
+ break;
42840
+ }
42841
+ const settled = await completion;
42842
+ const failure2 = settled.find((result2) => result2.status === "rejected");
42843
+ if (failure2) throw failure2.reason;
42844
+ break;
42845
+ }
42735
42846
  await drainWorkers();
42736
42847
  if (signal.aborted || !await readDesiredAcrossAtomicReplacement(desiredPath)) break;
42737
42848
  const storedRestored = await readManifestAcrossAtomicReplacement(manifestPath);
@@ -42748,6 +42859,9 @@ ${result2.stderr}`)) {
42748
42859
  await runtimeWriteChain;
42749
42860
  await appendServiceLog(appliedManifest.log_path, "service_supervisor_stopped");
42750
42861
  }
42862
+ if (bootstrapRestartRequired && !signal.aborted && await readDesiredAcrossAtomicReplacement(desiredPath)) {
42863
+ throw new InferenceHostBootstrapRestartRequiredError();
42864
+ }
42751
42865
  };
42752
42866
  }
42753
42867
  });
@@ -43243,7 +43357,7 @@ Durable service:
43243
43357
  });
43244
43358
  child.unref();
43245
43359
  };
43246
- registerInferenceHostServiceControlInput = (input, abort) => {
43360
+ registerInferenceHostServiceControlInput = (input, abort, quiesce) => {
43247
43361
  let pendingControlInput = "";
43248
43362
  const onControlInput = (chunk) => {
43249
43363
  pendingControlInput = `${pendingControlInput}${chunk.toString()}`.slice(-4096);
@@ -43251,6 +43365,8 @@ Durable service:
43251
43365
  pendingControlInput = lines.pop() ?? "";
43252
43366
  if (lines.some((line) => line === INFERENCE_HOST_SERVICE_DRAIN_COMMAND)) {
43253
43367
  abort();
43368
+ } else if (lines.some((line) => line === INFERENCE_HOST_SERVICE_QUIESCE_COMMAND)) {
43369
+ quiesce?.();
43254
43370
  }
43255
43371
  };
43256
43372
  input.on("data", onControlInput);
@@ -43260,13 +43376,13 @@ Durable service:
43260
43376
  input.pause();
43261
43377
  };
43262
43378
  };
43263
- defaultRegisterLifecycleSignalHandlers = (abort) => {
43379
+ defaultRegisterLifecycleSignalHandlers = (abort, quiesce) => {
43264
43380
  const onSigint = () => abort("SIGINT");
43265
43381
  const onSigterm = () => abort("SIGTERM");
43266
43382
  process.on("SIGINT", onSigint);
43267
43383
  process.on("SIGTERM", onSigterm);
43268
43384
  const serviceChild = process.env.VTX_INFERENCE_HOST_SERVICE_CHILD === "1";
43269
- const unregisterControlInput = serviceChild ? registerInferenceHostServiceControlInput(process.stdin, onSigterm) : () => {
43385
+ const unregisterControlInput = serviceChild ? registerInferenceHostServiceControlInput(process.stdin, onSigterm, quiesce) : () => {
43270
43386
  };
43271
43387
  return () => {
43272
43388
  process.off("SIGINT", onSigint);
@@ -43276,12 +43392,14 @@ Durable service:
43276
43392
  };
43277
43393
  lifecycleCancellation = (dependencies) => {
43278
43394
  const controller = new AbortController();
43395
+ const quiesceController = new AbortController();
43279
43396
  let unregistered = false;
43280
43397
  const unregisterSignalHandlers = (dependencies.registerLifecycleSignalHandlers ?? defaultRegisterLifecycleSignalHandlers)(() => {
43281
43398
  if (!controller.signal.aborted) controller.abort();
43282
- });
43399
+ }, () => quiesceController.abort());
43283
43400
  return {
43284
43401
  signal: controller.signal,
43402
+ quiesceSignal: quiesceController.signal,
43285
43403
  unregister: () => {
43286
43404
  if (unregistered) return;
43287
43405
  unregistered = true;
@@ -43487,7 +43605,8 @@ Durable service:
43487
43605
  adapter: agentAdapter,
43488
43606
  dataRequestTimeoutMs: await loadInferenceAgentDataRequestTimeoutMs()
43489
43607
  },
43490
- signal: options.signal
43608
+ signal: options.signal,
43609
+ quiesceSignal: options.quiesceSignal
43491
43610
  }).run();
43492
43611
  };
43493
43612
  preparePortableDurableAdapter = async (adapterId, config2, signal) => {
@@ -43558,7 +43677,8 @@ Durable service:
43558
43677
  adapter: agentAdapter,
43559
43678
  dataRequestTimeoutMs: await loadInferenceAgentDataRequestTimeoutMs()
43560
43679
  },
43561
- signal: options.signal
43680
+ signal: options.signal,
43681
+ quiesceSignal: options.quiesceSignal
43562
43682
  }).run();
43563
43683
  };
43564
43684
  login = async (config2, parsed, env, dependencies, warnings) => {
@@ -44356,7 +44476,8 @@ Waiting for approval...
44356
44476
  once: parsed.once,
44357
44477
  processLock,
44358
44478
  emitDiagnosticEvent,
44359
- signal: cancellation.signal
44479
+ signal: cancellation.signal,
44480
+ quiesceSignal: cancellation.quiesceSignal
44360
44481
  });
44361
44482
  return {
44362
44483
  exitCode: 0,
@@ -44396,7 +44517,8 @@ Waiting for approval...
44396
44517
  env,
44397
44518
  processLock,
44398
44519
  emitDiagnosticEvent,
44399
- signal: cancellation.signal
44520
+ signal: cancellation.signal,
44521
+ quiesceSignal: cancellation.quiesceSignal
44400
44522
  });
44401
44523
  return {
44402
44524
  exitCode: 0,
@@ -45720,10 +45842,15 @@ Waiting for approval...
45720
45842
  parsed.serviceManifestPath,
45721
45843
  {
45722
45844
  signal: cancellation.signal,
45723
- serviceScript: resolve11(process.argv[1] ?? "")
45845
+ quiesceSignal: cancellation.quiesceSignal,
45846
+ serviceScript: resolve11(process.argv[1] ?? ""),
45847
+ controlLockPath: `${supervisorConfig.supervisorProcessLockPath}.service-control`
45724
45848
  }
45725
45849
  );
45726
45850
  return { exitCode: 0, stdout: "", stderr: "" };
45851
+ } catch (error48) {
45852
+ if (error48 instanceof InferenceHostBootstrapRestartRequiredError) return { exitCode: 75, stdout: "", stderr: "" };
45853
+ throw error48;
45727
45854
  } finally {
45728
45855
  cancellation.unregister();
45729
45856
  await serviceLock.release();
@@ -49776,6 +49903,8 @@ var init_runtime_policy_generated2 = __esm({
49776
49903
  },
49777
49904
  external_inference: {
49778
49905
  service: {
49906
+ update_check_interval_ms: 3e5,
49907
+ update_registry_timeout_ms: 15e3,
49779
49908
  startup_max_attempts: 40,
49780
49909
  startup_poll_interval_ms: 250,
49781
49910
  restart_stable_uptime_ms: 6e4,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtxmacro/cli",
3
- "version": "2026.9.28",
3
+ "version": "2026.9.30",
4
4
  "description": "VTX Macro CLI, MCP server, and durable external inference host.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",