@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 +10 -6
- package/bin/vtx-service-bootstrap.js +341 -108
- package/bin/vtx.js +243 -48
- package/package.json +1 -1
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
|
|
20
|
-
service start, it checks the official npm registry
|
|
21
|
-
integrity and installed CLI version in a new
|
|
22
|
-
|
|
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
|
-
|
|
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.
|
|
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: {
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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,
|
|
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 ===
|
|
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
|
|
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
|
-
|
|
1411
|
-
|
|
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
|
|
1574
|
+
await waitForUpdateCheck(milliseconds, lifetime.signal);
|
|
1418
1575
|
});
|
|
1419
1576
|
const runSupervisor = dependencies.runSupervisor ?? defaultRunSupervisor;
|
|
1420
|
-
const
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
})
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
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
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
).completion;
|
|
1606
|
+
if (!await shouldRun()) return null;
|
|
1607
|
+
live.supervisor = runSupervisor(manifest.executable, supervisorArguments(script, manifestPath));
|
|
1608
|
+
return live.supervisor;
|
|
1450
1609
|
};
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
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
|
-
})
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
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
|
|
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
|
-
|
|
1514
|
-
|
|
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
|
-
|
|
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.
|
|
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))
|
|
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")
|
|
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")
|
|
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
|
-
|
|
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)
|
|
34626
|
-
|
|
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()),
|
|
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()),
|
|
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,
|
|
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,
|
|
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
|
-
|
|
41090
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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,
|