@algosuite/vo-mcp 0.2.0-beta.49 → 0.2.0-beta.51
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 +2 -0
- package/dist/cli.js +595 -187
- package/dist/cli.js.map +4 -4
- package/dist/index.js +452 -157
- package/dist/index.js.map +4 -4
- package/dist/runner-cli.js +539 -98
- package/dist/runner-cli.js.map +4 -4
- package/dist/runner-supervisor.js +100 -32
- package/dist/runner-supervisor.js.map +4 -4
- package/package.json +1 -1
package/dist/runner-cli.js
CHANGED
|
@@ -1138,28 +1138,28 @@ async function assertManagedRoot(rootPath, worktreeRoot, fsApi) {
|
|
|
1138
1138
|
throw new Error(`[vo-mcp runner] dependency cleanup refused outside the task root: ${rootPath}`);
|
|
1139
1139
|
}
|
|
1140
1140
|
if (!await pathExists(rootPath, fsApi)) return false;
|
|
1141
|
-
const
|
|
1142
|
-
if (
|
|
1141
|
+
const stat3 = await fsApi.lstat(rootPath);
|
|
1142
|
+
if (stat3.isSymbolicLink()) {
|
|
1143
1143
|
throw new Error(`[vo-mcp runner] dependency cleanup refused symbolic ownership root: ${rootPath}`);
|
|
1144
1144
|
}
|
|
1145
|
-
if (!
|
|
1145
|
+
if (!stat3.isDirectory()) {
|
|
1146
1146
|
throw new Error(`[vo-mcp runner] dependency cleanup refused non-directory ownership root: ${rootPath}`);
|
|
1147
1147
|
}
|
|
1148
1148
|
return true;
|
|
1149
1149
|
}
|
|
1150
|
-
async function removeReparsePoint(target,
|
|
1150
|
+
async function removeReparsePoint(target, stat3, fsApi) {
|
|
1151
1151
|
try {
|
|
1152
|
-
if (
|
|
1152
|
+
if (stat3.isDirectory()) {
|
|
1153
1153
|
await fsApi.rmdir(target);
|
|
1154
1154
|
return;
|
|
1155
1155
|
}
|
|
1156
1156
|
await fsApi.unlink(target);
|
|
1157
1157
|
} catch (error) {
|
|
1158
|
-
if (
|
|
1158
|
+
if (stat3.isDirectory() && ["ENOTDIR", "EPERM", "EISDIR", "EACCES"].includes(error?.code)) {
|
|
1159
1159
|
await fsApi.unlink(target);
|
|
1160
1160
|
return;
|
|
1161
1161
|
}
|
|
1162
|
-
if (!
|
|
1162
|
+
if (!stat3.isDirectory() && ["EPERM", "EISDIR", "EACCES"].includes(error?.code)) {
|
|
1163
1163
|
await fsApi.rmdir(target);
|
|
1164
1164
|
return;
|
|
1165
1165
|
}
|
|
@@ -1187,17 +1187,17 @@ async function walkManagedRoots(ownership, options, onLink) {
|
|
|
1187
1187
|
for (const entry of await fsApi.readdir(current, { withFileTypes: true })) {
|
|
1188
1188
|
if (entry.name === ".git") continue;
|
|
1189
1189
|
const child = path.join(current, entry.name);
|
|
1190
|
-
const
|
|
1190
|
+
const stat3 = await fsApi.lstat(child);
|
|
1191
1191
|
scannedEntries += 1;
|
|
1192
1192
|
if (scannedEntries > scanLimit) {
|
|
1193
1193
|
throw new Error(`[vo-mcp runner] dependency cleanup scan limit exceeded inside ${rootPath}`);
|
|
1194
1194
|
}
|
|
1195
1195
|
await maybeYield(yieldState);
|
|
1196
|
-
if (
|
|
1197
|
-
await onLink(child,
|
|
1196
|
+
if (stat3.isSymbolicLink()) {
|
|
1197
|
+
await onLink(child, stat3, normalized, fsApi);
|
|
1198
1198
|
continue;
|
|
1199
1199
|
}
|
|
1200
|
-
if (
|
|
1200
|
+
if (stat3.isDirectory()) {
|
|
1201
1201
|
stack.push(child);
|
|
1202
1202
|
}
|
|
1203
1203
|
}
|
|
@@ -1236,8 +1236,8 @@ function snapshotDependencyOwnership(ownership) {
|
|
|
1236
1236
|
}
|
|
1237
1237
|
async function detachDependencyLinks(ownership, options = {}) {
|
|
1238
1238
|
let removedLinks = 0;
|
|
1239
|
-
const result = await walkManagedRoots(ownership, options, async (target,
|
|
1240
|
-
await removeReparsePoint(target,
|
|
1239
|
+
const result = await walkManagedRoots(ownership, options, async (target, stat3, _normalized, fsApi) => {
|
|
1240
|
+
await removeReparsePoint(target, stat3, fsApi);
|
|
1241
1241
|
removedLinks += 1;
|
|
1242
1242
|
});
|
|
1243
1243
|
return { ...result, removedLinks };
|
|
@@ -1671,8 +1671,8 @@ async function inspectCanonicalNodeModulesHealth({
|
|
|
1671
1671
|
if (!await pathExists3(nodeModulesDir, fsApi)) {
|
|
1672
1672
|
issues.push(`missing root node_modules: ${nodeModulesDir}`);
|
|
1673
1673
|
} else {
|
|
1674
|
-
const
|
|
1675
|
-
if (
|
|
1674
|
+
const stat3 = await fsApi.lstat(nodeModulesDir);
|
|
1675
|
+
if (stat3.isSymbolicLink() || !stat3.isDirectory()) {
|
|
1676
1676
|
issues.push(`root node_modules must be a real directory: ${nodeModulesDir}`);
|
|
1677
1677
|
}
|
|
1678
1678
|
}
|
|
@@ -1695,8 +1695,8 @@ async function inspectCanonicalNodeModulesHealth({
|
|
|
1695
1695
|
issues.push(`missing workspace node_modules: ${workspaceNodeModules}`);
|
|
1696
1696
|
continue;
|
|
1697
1697
|
}
|
|
1698
|
-
const
|
|
1699
|
-
if (
|
|
1698
|
+
const stat3 = await fsApi.lstat(workspaceNodeModules);
|
|
1699
|
+
if (stat3.isSymbolicLink() || !stat3.isDirectory()) {
|
|
1700
1700
|
issues.push(`workspace node_modules must be a real directory: ${workspaceNodeModules}`);
|
|
1701
1701
|
}
|
|
1702
1702
|
}
|
|
@@ -1936,12 +1936,12 @@ async function copyDirRecursive(sourceDir, targetDir, fsApi, yieldState) {
|
|
|
1936
1936
|
}
|
|
1937
1937
|
async function materializeEntry(sourceEntry, targetEntry, canonicalRoot, worktreeRoot, options) {
|
|
1938
1938
|
await options.beforeEntry?.(sourceEntry, targetEntry);
|
|
1939
|
-
const
|
|
1940
|
-
if (
|
|
1939
|
+
const stat3 = await options.fsApi.lstat(sourceEntry);
|
|
1940
|
+
if (stat3.isDirectory() && !stat3.isSymbolicLink() && path4.basename(sourceEntry) === ".bin") {
|
|
1941
1941
|
await copyDirRecursive(sourceEntry, targetEntry, options.fsApi, options.yieldState);
|
|
1942
1942
|
return;
|
|
1943
1943
|
}
|
|
1944
|
-
if (
|
|
1944
|
+
if (stat3.isDirectory() && !stat3.isSymbolicLink() && path4.basename(sourceEntry).startsWith("@")) {
|
|
1945
1945
|
await options.fsApi.mkdir(targetEntry, { recursive: true });
|
|
1946
1946
|
for (const nested of await options.fsApi.readdir(sourceEntry, { withFileTypes: true })) {
|
|
1947
1947
|
await maybeYield2(options.yieldState);
|
|
@@ -1955,7 +1955,7 @@ async function materializeEntry(sourceEntry, targetEntry, canonicalRoot, worktre
|
|
|
1955
1955
|
}
|
|
1956
1956
|
return;
|
|
1957
1957
|
}
|
|
1958
|
-
if (
|
|
1958
|
+
if (stat3.isSymbolicLink() || stat3.isDirectory()) {
|
|
1959
1959
|
const resolvedTarget = await resolveEntryTarget(sourceEntry, canonicalRoot, worktreeRoot, options.fsApi);
|
|
1960
1960
|
await ensureLinkedDirectory(resolvedTarget, targetEntry, options.fsApi);
|
|
1961
1961
|
return;
|
|
@@ -2660,12 +2660,12 @@ async function recoverManagedCanonicalResidue(root, options = {}) {
|
|
|
2660
2660
|
const symlinks = [];
|
|
2661
2661
|
for (const relative of paths.untracked) {
|
|
2662
2662
|
const source = canonicalPath(root, relative);
|
|
2663
|
-
const
|
|
2664
|
-
if (
|
|
2663
|
+
const stat3 = await fsp7.lstat(source);
|
|
2664
|
+
if (stat3.isSymbolicLink()) {
|
|
2665
2665
|
symlinks.push({ path: relative, target: await fsp7.readlink(source) });
|
|
2666
2666
|
continue;
|
|
2667
2667
|
}
|
|
2668
|
-
if (!
|
|
2668
|
+
if (!stat3.isFile()) {
|
|
2669
2669
|
throw new Error(`canonical recovery refuses unsupported untracked entry: ${relative}`);
|
|
2670
2670
|
}
|
|
2671
2671
|
const target = canonicalPath(path8.join(quarantineDir, "untracked"), relative);
|
|
@@ -3335,6 +3335,34 @@ var init_installation_token = __esm({
|
|
|
3335
3335
|
}
|
|
3336
3336
|
});
|
|
3337
3337
|
|
|
3338
|
+
// ../../scripts/virtual-office/code-runner/control-plane-promote.mjs
|
|
3339
|
+
async function promoteDraftPrRequest(req, prNumber, automationContext, onUnauthorized = () => {
|
|
3340
|
+
}) {
|
|
3341
|
+
const res = await req("POST", "/api/v1/admin/pr/promote-draft", { prNumber, automationContext }, { timeoutMs: 6e4 });
|
|
3342
|
+
if (res.status === 401) {
|
|
3343
|
+
onUnauthorized();
|
|
3344
|
+
throw new Error("promote-draft unauthorized (401)");
|
|
3345
|
+
}
|
|
3346
|
+
const json = await res.json().catch(() => ({}));
|
|
3347
|
+
if (res.ok && json?.ok === true) {
|
|
3348
|
+
return {
|
|
3349
|
+
status: json.promoted === true ? json.auto_merge_disarmed === true ? "promoted (auto-merge disarmed)" : "promoted" : json.already_ready === true ? "already_ready" : "unchanged",
|
|
3350
|
+
headSha: typeof json.head_sha === "string" ? json.head_sha : null,
|
|
3351
|
+
reason: typeof json.blocked_reason === "string" ? json.blocked_reason : null
|
|
3352
|
+
};
|
|
3353
|
+
}
|
|
3354
|
+
const code = typeof json?.error === "string" ? json.error : null;
|
|
3355
|
+
const err = new Error(`promote-draft failed: HTTP ${res.status}${code ? ` (${code})` : ""}${json?.reason ? ` \u2014 ${json.reason}` : ""}`);
|
|
3356
|
+
err.status = res.status;
|
|
3357
|
+
err.code = code;
|
|
3358
|
+
throw err;
|
|
3359
|
+
}
|
|
3360
|
+
var init_control_plane_promote = __esm({
|
|
3361
|
+
"../../scripts/virtual-office/code-runner/control-plane-promote.mjs"() {
|
|
3362
|
+
"use strict";
|
|
3363
|
+
}
|
|
3364
|
+
});
|
|
3365
|
+
|
|
3338
3366
|
// ../../scripts/virtual-office/code-runner/control-plane-task-list.mjs
|
|
3339
3367
|
async function listAllPrOpenedTasks(request) {
|
|
3340
3368
|
const tasks = [];
|
|
@@ -3397,7 +3425,9 @@ async function resumeCodeTaskRequest(req, taskId, { automaticRateLimit = false,
|
|
|
3397
3425
|
throw err;
|
|
3398
3426
|
}
|
|
3399
3427
|
const json = await res.json();
|
|
3400
|
-
|
|
3428
|
+
const task = json && json.task ? json.task : null;
|
|
3429
|
+
if (task && typeof json.deduplicated === "boolean") Object.defineProperty(task, "deduplicated", { value: json.deduplicated, enumerable: false });
|
|
3430
|
+
return task;
|
|
3401
3431
|
}
|
|
3402
3432
|
var init_control_plane_resume = __esm({
|
|
3403
3433
|
"../../scripts/virtual-office/code-runner/control-plane-resume.mjs"() {
|
|
@@ -3482,6 +3512,60 @@ var init_control_plane_merge = __esm({
|
|
|
3482
3512
|
}
|
|
3483
3513
|
});
|
|
3484
3514
|
|
|
3515
|
+
// ../../scripts/virtual-office/code-runner/control-plane-weekly-tokens.mjs
|
|
3516
|
+
async function postWeeklyTokensRequest(taskReq, { operatorId, runnerId, tokens, claudeWeeklyPct, claudeWeeklyResetsAt }, onUnauthorized = () => {
|
|
3517
|
+
}) {
|
|
3518
|
+
const body = {
|
|
3519
|
+
operator_id: operatorId,
|
|
3520
|
+
runner_id: runnerId,
|
|
3521
|
+
input_tokens: tokens.input_tokens,
|
|
3522
|
+
output_tokens: tokens.output_tokens,
|
|
3523
|
+
cache_creation_tokens: tokens.cache_creation_tokens,
|
|
3524
|
+
cache_read_tokens: tokens.cache_read_tokens
|
|
3525
|
+
};
|
|
3526
|
+
if (typeof claudeWeeklyPct === "number") {
|
|
3527
|
+
body.claude_weekly_pct = claudeWeeklyPct;
|
|
3528
|
+
}
|
|
3529
|
+
if (claudeWeeklyResetsAt !== void 0) {
|
|
3530
|
+
body.claude_weekly_resets_at = claudeWeeklyResetsAt;
|
|
3531
|
+
}
|
|
3532
|
+
const res = await taskReq("POST", "/api/v1/weekly-tokens", body);
|
|
3533
|
+
if (res.status === 401) {
|
|
3534
|
+
onUnauthorized();
|
|
3535
|
+
throw new Error("weekly-tokens unauthorized (401)");
|
|
3536
|
+
}
|
|
3537
|
+
if (!res.ok) throw new Error(`weekly-tokens failed: HTTP ${res.status}`);
|
|
3538
|
+
return true;
|
|
3539
|
+
}
|
|
3540
|
+
var init_control_plane_weekly_tokens = __esm({
|
|
3541
|
+
"../../scripts/virtual-office/code-runner/control-plane-weekly-tokens.mjs"() {
|
|
3542
|
+
"use strict";
|
|
3543
|
+
}
|
|
3544
|
+
});
|
|
3545
|
+
|
|
3546
|
+
// ../../scripts/virtual-office/code-runner/control-plane-telemetry-relay.mjs
|
|
3547
|
+
async function relayTelemetryEventsRequest(req, { events, source }, onUnauthorized = () => {
|
|
3548
|
+
}) {
|
|
3549
|
+
const res = await req("POST", "/api/v1/telemetry/relay", { events, ...source ? { source } : {} }, {
|
|
3550
|
+
timeoutMs: TELEMETRY_RELAY_TIMEOUT_MS
|
|
3551
|
+
});
|
|
3552
|
+
if (res.status === 401) onUnauthorized();
|
|
3553
|
+
let body = null;
|
|
3554
|
+
try {
|
|
3555
|
+
body = await res.json();
|
|
3556
|
+
} catch {
|
|
3557
|
+
body = null;
|
|
3558
|
+
}
|
|
3559
|
+
return { status: res.status, body };
|
|
3560
|
+
}
|
|
3561
|
+
var TELEMETRY_RELAY_TIMEOUT_MS;
|
|
3562
|
+
var init_control_plane_telemetry_relay = __esm({
|
|
3563
|
+
"../../scripts/virtual-office/code-runner/control-plane-telemetry-relay.mjs"() {
|
|
3564
|
+
"use strict";
|
|
3565
|
+
TELEMETRY_RELAY_TIMEOUT_MS = 3e4;
|
|
3566
|
+
}
|
|
3567
|
+
});
|
|
3568
|
+
|
|
3485
3569
|
// ../../scripts/virtual-office/code-runner/claim-gate-notice.mjs
|
|
3486
3570
|
function describeClaimGate(gate) {
|
|
3487
3571
|
if (!gate || gate.allowed !== false) return null;
|
|
@@ -3658,9 +3742,22 @@ function createControlPlaneClient({
|
|
|
3658
3742
|
cachedFirebaseToken = null;
|
|
3659
3743
|
throw new Error("enqueue unauthorized (401)");
|
|
3660
3744
|
}
|
|
3661
|
-
if (!res.ok)
|
|
3745
|
+
if (!res.ok) {
|
|
3746
|
+
let code = null;
|
|
3747
|
+
try {
|
|
3748
|
+
const errBody = await res.json();
|
|
3749
|
+
code = typeof errBody?.error === "string" ? errBody.error : null;
|
|
3750
|
+
} catch {
|
|
3751
|
+
}
|
|
3752
|
+
const err = new Error(`enqueue failed: HTTP ${res.status}${code ? ` (${code})` : ""}`);
|
|
3753
|
+
err.status = res.status;
|
|
3754
|
+
err.code = code;
|
|
3755
|
+
throw err;
|
|
3756
|
+
}
|
|
3662
3757
|
const json = await res.json();
|
|
3663
|
-
|
|
3758
|
+
const task = json && json.task ? json.task : null;
|
|
3759
|
+
if (task && typeof json.deduplicated === "boolean") Object.defineProperty(task, "deduplicated", { value: json.deduplicated, enumerable: false });
|
|
3760
|
+
return task;
|
|
3664
3761
|
},
|
|
3665
3762
|
/**
|
|
3666
3763
|
* Resume a failed/cancelled/max-turn partial code-task. The PR watcher uses
|
|
@@ -3676,6 +3773,10 @@ function createControlPlaneClient({
|
|
|
3676
3773
|
* The server inspects the current diff, applies deterministic blockers, runs
|
|
3677
3774
|
* consensus, records a receipt, and direct-merges only the inspected SHA.
|
|
3678
3775
|
*/
|
|
3776
|
+
/** F35: promote a PARTIAL draft to READY via the plane (admin-only; server re-checks; never merges). */
|
|
3777
|
+
promoteDraftPr: (prNumber, automationContext) => promoteDraftPrRequest(req, prNumber, automationContext, () => {
|
|
3778
|
+
cachedFirebaseToken = null;
|
|
3779
|
+
}),
|
|
3679
3780
|
async mergeVerifiedPr(prNumber, automationContext) {
|
|
3680
3781
|
return mergeVerifiedPrRequest(
|
|
3681
3782
|
req,
|
|
@@ -3739,38 +3840,21 @@ function createControlPlaneClient({
|
|
|
3739
3840
|
if (!res.ok) throw new Error(`knowledge-context failed: HTTP ${res.status}`);
|
|
3740
3841
|
return res.json();
|
|
3741
3842
|
},
|
|
3843
|
+
/** Weekly Claude token usage report — see control-plane-weekly-tokens.mjs. */
|
|
3844
|
+
async postWeeklyTokens(report) {
|
|
3845
|
+
return postWeeklyTokensRequest(taskReq, report, () => {
|
|
3846
|
+
cachedFirebaseToken = null;
|
|
3847
|
+
});
|
|
3848
|
+
},
|
|
3742
3849
|
/**
|
|
3743
|
-
*
|
|
3744
|
-
*
|
|
3745
|
-
*
|
|
3746
|
-
* is named explicitly. Best-effort; throws on a non-2xx so the caller can
|
|
3747
|
-
* log + move on.
|
|
3748
|
-
*
|
|
3749
|
-
* `tokens` = { input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens }.
|
|
3750
|
-
* Optional: `claudeWeeklyPct` (number) + `claudeWeeklyResetsAt` (ISO string | null).
|
|
3850
|
+
* Relay a batch of this machine's local vo-mcp events to vo-telemetry via
|
|
3851
|
+
* the control plane (telemetry-forwarder.mjs). Returns { status, body };
|
|
3852
|
+
* the forwarder owns backoff/disable policy. See control-plane-telemetry-relay.mjs.
|
|
3751
3853
|
*/
|
|
3752
|
-
async
|
|
3753
|
-
|
|
3754
|
-
operator_id: operatorId,
|
|
3755
|
-
runner_id: runnerId2,
|
|
3756
|
-
input_tokens: tokens.input_tokens,
|
|
3757
|
-
output_tokens: tokens.output_tokens,
|
|
3758
|
-
cache_creation_tokens: tokens.cache_creation_tokens,
|
|
3759
|
-
cache_read_tokens: tokens.cache_read_tokens
|
|
3760
|
-
};
|
|
3761
|
-
if (typeof claudeWeeklyPct === "number") {
|
|
3762
|
-
body.claude_weekly_pct = claudeWeeklyPct;
|
|
3763
|
-
}
|
|
3764
|
-
if (claudeWeeklyResetsAt !== void 0) {
|
|
3765
|
-
body.claude_weekly_resets_at = claudeWeeklyResetsAt;
|
|
3766
|
-
}
|
|
3767
|
-
const res = await taskReq("POST", "/api/v1/weekly-tokens", body);
|
|
3768
|
-
if (res.status === 401) {
|
|
3854
|
+
async relayTelemetryEvents(batch) {
|
|
3855
|
+
return relayTelemetryEventsRequest(req, batch, () => {
|
|
3769
3856
|
cachedFirebaseToken = null;
|
|
3770
|
-
|
|
3771
|
-
}
|
|
3772
|
-
if (!res.ok) throw new Error(`weekly-tokens failed: HTTP ${res.status}`);
|
|
3773
|
-
return true;
|
|
3857
|
+
});
|
|
3774
3858
|
},
|
|
3775
3859
|
/**
|
|
3776
3860
|
* Send a liveness heartbeat (M2). The control-plane upserts it under the
|
|
@@ -3900,10 +3984,13 @@ var init_control_plane_client = __esm({
|
|
|
3900
3984
|
"../../scripts/virtual-office/code-runner/control-plane-client.mjs"() {
|
|
3901
3985
|
"use strict";
|
|
3902
3986
|
init_installation_token();
|
|
3987
|
+
init_control_plane_promote();
|
|
3903
3988
|
init_control_plane_task_list();
|
|
3904
3989
|
init_control_plane_resume();
|
|
3905
3990
|
init_control_plane_autonomous_admission();
|
|
3906
3991
|
init_control_plane_merge();
|
|
3992
|
+
init_control_plane_weekly_tokens();
|
|
3993
|
+
init_control_plane_telemetry_relay();
|
|
3907
3994
|
init_claim_gate_notice();
|
|
3908
3995
|
cachedFirebaseToken = null;
|
|
3909
3996
|
ClaimAuthorityChangedError = class extends Error {
|
|
@@ -6690,8 +6777,8 @@ async function acquireLock(lockFile, { now = Date.now, sleep: sleep3 = delay } =
|
|
|
6690
6777
|
stale = !alive || !Number.isFinite(created) || now() - created > LOCK_STALE_MS;
|
|
6691
6778
|
} catch {
|
|
6692
6779
|
try {
|
|
6693
|
-
const
|
|
6694
|
-
stale = now() -
|
|
6780
|
+
const stat3 = await fsp10.stat(lockFile);
|
|
6781
|
+
stale = now() - stat3.mtimeMs > LOCK_INIT_GRACE_MS;
|
|
6695
6782
|
} catch {
|
|
6696
6783
|
stale = false;
|
|
6697
6784
|
}
|
|
@@ -8043,6 +8130,22 @@ var init_publish_async = __esm({
|
|
|
8043
8130
|
}
|
|
8044
8131
|
});
|
|
8045
8132
|
|
|
8133
|
+
// ../../scripts/virtual-office/code-runner/headless-execution-contract.mjs
|
|
8134
|
+
var HEADLESS_EXECUTION_CONTRACT;
|
|
8135
|
+
var init_headless_execution_contract = __esm({
|
|
8136
|
+
"../../scripts/virtual-office/code-runner/headless-execution-contract.mjs"() {
|
|
8137
|
+
"use strict";
|
|
8138
|
+
HEADLESS_EXECUTION_CONTRACT = [
|
|
8139
|
+
"Command execution in this headless session: ONLY commands that start with `pnpm` are pre-authorized",
|
|
8140
|
+
"(e.g. `pnpm exec vitest run <file>`, `pnpm run roadmap:board`, `pnpm exec node scripts/<x>.mjs`,",
|
|
8141
|
+
"`pnpm exec tsc --noEmit -p <dir>`); any other Bash command (`node \u2026`, `npx \u2026`, `git \u2026`, `gh \u2026`) is",
|
|
8142
|
+
"auto-denied \u2014 that is expected, not a broken environment. Wrap what you need as `pnpm exec <cmd>`.",
|
|
8143
|
+
"Roadmap-board drift (`check-roadmap-board-coverage` red): run `pnpm run roadmap:board` and keep the",
|
|
8144
|
+
"regenerated `public/roadmap-progress.json` + `cloud-run/vo-control-plane/data/roadmap-progress.json`."
|
|
8145
|
+
].join("\n");
|
|
8146
|
+
}
|
|
8147
|
+
});
|
|
8148
|
+
|
|
8046
8149
|
// ../../scripts/virtual-office/code-runner/skill-catalog.mjs
|
|
8047
8150
|
import { readdirSync as readdirSync3, readFileSync as readFileSync8, statSync as statSync4 } from "node:fs";
|
|
8048
8151
|
import { dirname as dirname7, join as join10 } from "node:path";
|
|
@@ -8147,6 +8250,7 @@ function buildDispatchOnboarding({ repo = "Algosuite-ai/Nexus" } = {}) {
|
|
|
8147
8250
|
" - Finishing IS shipping here. The moment you finish, the HQ runner commits your changes, pushes a branch, and opens the PR FOR you \u2014 that is its job, not yours. You are not being asked to stop short of shipping; you are being asked to hand off the last mile.",
|
|
8148
8251
|
" - Leave your changes as UNCOMMITTED edits in this worktree. That is the hand-off mechanism, not a lesser outcome.",
|
|
8149
8252
|
" - Do NOT run git (no commit, no branch, no checkout) and do NOT run `gh` / open a PR yourself. You are sandboxed to file edits; git/gh commands will be denied, and committing your work moves it where the runner cannot see it (your change would be silently discarded).",
|
|
8253
|
+
` - ${HEADLESS_EXECUTION_CONTRACT.split("\n").join("\n ")}`,
|
|
8150
8254
|
' - When the task is done, hand off: your final message should summarize what you changed; the runner detects your edited files and creates the PR. Do not treat "hand off" as "leave it unfinished" \u2014 finish the work completely first.',
|
|
8151
8255
|
" - If a git or `gh` command is DENIED, that is EXPECTED and CORRECT \u2014 it means the runner will handle publishing. Do NOT retry it, do NOT try a different git/gh invocation, and do NOT wait for an approval that will not come. STOP immediately with your edits uncommitted. (Agents that retried a denied `gh pr create` burned ~25 minutes of usage and their finished fix was lost.)",
|
|
8152
8256
|
" - Do NOT create scratch files \u2014 no drafted PR body, no notes/TODO/plan files, nothing under tmp/ or named pr-body*/pr-description*. The runner writes the PR body itself; the worktree should contain ONLY the real file changes the task requires. (Stray scratch files have leaked into PRs.)",
|
|
@@ -8184,6 +8288,7 @@ var MANDATORY_READS, NON_NEGOTIABLES;
|
|
|
8184
8288
|
var init_dispatch_onboarding = __esm({
|
|
8185
8289
|
"../../scripts/virtual-office/code-runner/dispatch-onboarding.mjs"() {
|
|
8186
8290
|
"use strict";
|
|
8291
|
+
init_headless_execution_contract();
|
|
8187
8292
|
init_skill_catalog();
|
|
8188
8293
|
MANDATORY_READS = [
|
|
8189
8294
|
"CLAUDE.md (repo root \u2014 Claude-specific rules; Claude Code sessions have it AUTO-LOADED \u2014 do NOT Read it again there, that re-spends ~18K tokens; Codex/Cursor/other agents must READ it)",
|
|
@@ -8740,8 +8845,8 @@ function selectDueEntries({ entries = [], now, alreadyDispatched = /* @__PURE__
|
|
|
8740
8845
|
const entryAtMs = new Date(at).getTime();
|
|
8741
8846
|
if (Number.isFinite(entryAtMs)) {
|
|
8742
8847
|
const attemptCount = typeof attempts === "number" ? attempts : 0;
|
|
8743
|
-
const
|
|
8744
|
-
const dueAtMs = entryAtMs +
|
|
8848
|
+
const backoffMs2 = NULL_RESUME_AFTER_BACKOFF_MS * Math.pow(2, attemptCount);
|
|
8849
|
+
const dueAtMs = entryAtMs + backoffMs2;
|
|
8745
8850
|
isDue = nowMs >= dueAtMs;
|
|
8746
8851
|
}
|
|
8747
8852
|
} else {
|
|
@@ -8905,6 +9010,303 @@ var init_rate_limit_resume_scheduler = __esm({
|
|
|
8905
9010
|
}
|
|
8906
9011
|
});
|
|
8907
9012
|
|
|
9013
|
+
// ../../scripts/virtual-office/code-runner/telemetry-forwarder.mjs
|
|
9014
|
+
import { homedir as homedir8 } from "node:os";
|
|
9015
|
+
import { dirname as dirname9, join as join13 } from "node:path";
|
|
9016
|
+
import { mkdir as mkdir2, open, readFile as readFile3, stat as stat2, writeFile as writeFile3 } from "node:fs/promises";
|
|
9017
|
+
function defaultEventsPath(env2 = process.env) {
|
|
9018
|
+
const p = String(env2.VO_MCP_EVENTS_PATH || "").trim();
|
|
9019
|
+
return p || join13(homedir8(), ".claude", "vo-mcp-events.jsonl");
|
|
9020
|
+
}
|
|
9021
|
+
function defaultStatePath(env2 = process.env) {
|
|
9022
|
+
const p = String(env2.VO_MCP_EVENTS_FORWARD_STATE || "").trim();
|
|
9023
|
+
return p || join13(homedir8(), ".claude", "vo-mcp-events-forward-state.json");
|
|
9024
|
+
}
|
|
9025
|
+
function splitCompleteLines(buf) {
|
|
9026
|
+
const lines = [];
|
|
9027
|
+
let start = 0;
|
|
9028
|
+
let consumedBytes = 0;
|
|
9029
|
+
for (; ; ) {
|
|
9030
|
+
const nl = buf.indexOf(10, start);
|
|
9031
|
+
if (nl === -1) break;
|
|
9032
|
+
lines.push({ text: buf.subarray(start, nl).toString("utf8"), bytes: nl - start + 1 });
|
|
9033
|
+
consumedBytes = nl + 1;
|
|
9034
|
+
start = nl + 1;
|
|
9035
|
+
}
|
|
9036
|
+
return { lines, consumedBytes };
|
|
9037
|
+
}
|
|
9038
|
+
function backoffMs(streak, baseMs) {
|
|
9039
|
+
if (streak <= 0) return 0;
|
|
9040
|
+
return Math.min(baseMs * 2 ** Math.min(streak - 1, 20), MAX_BACKOFF_MS);
|
|
9041
|
+
}
|
|
9042
|
+
async function loadState(path22) {
|
|
9043
|
+
try {
|
|
9044
|
+
const parsed = JSON.parse(await readFile3(path22, "utf8"));
|
|
9045
|
+
if (parsed && typeof parsed === "object" && Number.isInteger(parsed.byte_offset) && parsed.byte_offset >= 0) {
|
|
9046
|
+
return { ...parsed, byte_offset: parsed.byte_offset };
|
|
9047
|
+
}
|
|
9048
|
+
} catch {
|
|
9049
|
+
}
|
|
9050
|
+
return { byte_offset: 0, last_event_id: null, forwarded_total: 0, rejected_total: 0, rejected_event_ids: [] };
|
|
9051
|
+
}
|
|
9052
|
+
async function saveState(path22, state) {
|
|
9053
|
+
await mkdir2(dirname9(path22), { recursive: true });
|
|
9054
|
+
await writeFile3(path22, JSON.stringify(state, null, 2), "utf8");
|
|
9055
|
+
}
|
|
9056
|
+
async function readNewBytes(path22, offset, max) {
|
|
9057
|
+
const st = await stat2(path22);
|
|
9058
|
+
if (st.size <= offset) return { buf: Buffer.alloc(0), size: st.size };
|
|
9059
|
+
const length = Math.min(st.size - offset, max);
|
|
9060
|
+
const fh = await open(path22, "r");
|
|
9061
|
+
try {
|
|
9062
|
+
const buf = Buffer.alloc(length);
|
|
9063
|
+
const { bytesRead } = await fh.read(buf, 0, length, offset);
|
|
9064
|
+
return { buf: buf.subarray(0, bytesRead), size: st.size };
|
|
9065
|
+
} finally {
|
|
9066
|
+
await fh.close();
|
|
9067
|
+
}
|
|
9068
|
+
}
|
|
9069
|
+
function createTelemetryForwarder({
|
|
9070
|
+
client,
|
|
9071
|
+
cfg = {},
|
|
9072
|
+
env: env2 = process.env,
|
|
9073
|
+
log: log2 = () => {
|
|
9074
|
+
},
|
|
9075
|
+
runnerInstanceId = "",
|
|
9076
|
+
now = () => Date.now(),
|
|
9077
|
+
eventsPath = defaultEventsPath(env2),
|
|
9078
|
+
statePath: statePath2 = defaultStatePath(env2)
|
|
9079
|
+
} = {}) {
|
|
9080
|
+
const optedOut = env2.VO_MCP_EVENTS_FORWARD === "0";
|
|
9081
|
+
const capable = typeof client?.relayTelemetryEvents === "function";
|
|
9082
|
+
const intervalSec = Math.max(MIN_FORWARD_INTERVAL_SEC, Number(env2.VO_MCP_EVENTS_FORWARD_SEC) || DEFAULT_FORWARD_INTERVAL_SEC);
|
|
9083
|
+
const maxPerRun = Math.max(MAX_EVENTS_PER_BATCH, Number(env2.VO_MCP_EVENTS_FORWARD_MAX_PER_MIN) || DEFAULT_MAX_EVENTS_PER_MIN);
|
|
9084
|
+
const intervalMs = intervalSec * 1e3;
|
|
9085
|
+
const enabled = capable && !optedOut;
|
|
9086
|
+
const disabledReason = optedOut ? "VO_MCP_EVENTS_FORWARD=0" : capable ? "" : "client has no relayTelemetryEvents";
|
|
9087
|
+
let running = false;
|
|
9088
|
+
let nextRunAt = 0;
|
|
9089
|
+
let failStreak = 0;
|
|
9090
|
+
const servedOperators = Array.isArray(cfg.servedOperators) ? cfg.servedOperators.filter((v) => typeof v === "string" && v).slice(0, 100) : [];
|
|
9091
|
+
const source = {
|
|
9092
|
+
runner_id: cfg.runnerId || "unknown",
|
|
9093
|
+
...runnerInstanceId ? { runner_instance_id: runnerInstanceId } : {},
|
|
9094
|
+
...servedOperators.length > 0 ? { served_operator_ids: servedOperators } : {}
|
|
9095
|
+
};
|
|
9096
|
+
log2(enabled ? `telemetry forwarder ON \u2014 ${eventsPath} \u2192 control-plane relay every ${intervalSec}s (\u2264${maxPerRun}/run; VO_MCP_EVENTS_FORWARD=0 to disable)` : `telemetry forwarder OFF (${disabledReason})`);
|
|
9097
|
+
function pauseForbidden(reason) {
|
|
9098
|
+
nextRunAt = now() + FORBIDDEN_PAUSE_MS;
|
|
9099
|
+
log2(`telemetry forwarder PAUSED ${Math.round(FORBIDDEN_PAUSE_MS / 36e5)}h \u2014 relay refused this runner (${reason}); enroll the operator (VO_TELEMETRY_RELAY_OPERATOR_IDS / HQ_WHITEBOARD_OPERATOR_IDS) or set VO_MCP_EVENTS_FORWARD=0`);
|
|
9100
|
+
}
|
|
9101
|
+
function scheduleFailure(reasonMs) {
|
|
9102
|
+
failStreak += 1;
|
|
9103
|
+
const delay2 = Math.max(reasonMs || 0, backoffMs(failStreak, intervalMs));
|
|
9104
|
+
nextRunAt = now() + delay2;
|
|
9105
|
+
return delay2;
|
|
9106
|
+
}
|
|
9107
|
+
async function runOnce() {
|
|
9108
|
+
const receipt = { forwarded: 0, duplicate: 0, rejected: 0, malformed: 0, halted: null, batches: 0 };
|
|
9109
|
+
let state = await loadState(statePath2);
|
|
9110
|
+
let read;
|
|
9111
|
+
try {
|
|
9112
|
+
read = await readNewBytes(eventsPath, state.byte_offset, MAX_READ_BYTES_PER_RUN);
|
|
9113
|
+
} catch (err) {
|
|
9114
|
+
if (err && err.code === "ENOENT") {
|
|
9115
|
+
nextRunAt = now() + intervalMs;
|
|
9116
|
+
return { ...receipt, status: "no-file" };
|
|
9117
|
+
}
|
|
9118
|
+
log2(`telemetry forwarder read failed: ${err.message}`);
|
|
9119
|
+
scheduleFailure();
|
|
9120
|
+
return { ...receipt, status: "read-error" };
|
|
9121
|
+
}
|
|
9122
|
+
if (read.size < state.byte_offset) {
|
|
9123
|
+
log2(`telemetry forwarder: events file rotated/truncated (size ${read.size} < cursor ${state.byte_offset}); restarting cursor at 0`);
|
|
9124
|
+
state = { ...state, byte_offset: 0 };
|
|
9125
|
+
await saveState(statePath2, state);
|
|
9126
|
+
read = await readNewBytes(eventsPath, 0, MAX_READ_BYTES_PER_RUN).catch(() => ({ buf: Buffer.alloc(0), size: 0 }));
|
|
9127
|
+
}
|
|
9128
|
+
const { lines } = splitCompleteLines(read.buf);
|
|
9129
|
+
if (lines.length === 0) {
|
|
9130
|
+
nextRunAt = now() + intervalMs;
|
|
9131
|
+
return { ...receipt, status: "idle" };
|
|
9132
|
+
}
|
|
9133
|
+
const pending = [];
|
|
9134
|
+
let cursor = state.byte_offset;
|
|
9135
|
+
let leadingConsumed = 0;
|
|
9136
|
+
for (const line of lines) {
|
|
9137
|
+
if (pending.length >= maxPerRun) break;
|
|
9138
|
+
if (line.text.trim().length === 0) {
|
|
9139
|
+
if (pending.length === 0) leadingConsumed += line.bytes;
|
|
9140
|
+
else pending[pending.length - 1].bytes += line.bytes;
|
|
9141
|
+
continue;
|
|
9142
|
+
}
|
|
9143
|
+
try {
|
|
9144
|
+
pending.push({ event: JSON.parse(line.text), bytes: line.bytes });
|
|
9145
|
+
} catch {
|
|
9146
|
+
receipt.malformed += 1;
|
|
9147
|
+
if (pending.length === 0) leadingConsumed += line.bytes;
|
|
9148
|
+
else pending[pending.length - 1].bytes += line.bytes;
|
|
9149
|
+
}
|
|
9150
|
+
}
|
|
9151
|
+
cursor += leadingConsumed;
|
|
9152
|
+
if (pending.length === 0) {
|
|
9153
|
+
state = { ...state, byte_offset: cursor };
|
|
9154
|
+
await saveState(statePath2, state);
|
|
9155
|
+
nextRunAt = now() + intervalMs;
|
|
9156
|
+
return { ...receipt, status: "idle" };
|
|
9157
|
+
}
|
|
9158
|
+
let halted = false;
|
|
9159
|
+
let batchSize = MAX_EVENTS_PER_BATCH;
|
|
9160
|
+
for (let i = 0; i < pending.length && !halted; ) {
|
|
9161
|
+
const batch = pending.slice(i, i + batchSize);
|
|
9162
|
+
let res;
|
|
9163
|
+
try {
|
|
9164
|
+
res = await client.relayTelemetryEvents({ events: batch.map((p) => p.event), source });
|
|
9165
|
+
} catch (err) {
|
|
9166
|
+
receipt.halted = `network: ${err && err.message ? err.message : String(err)}`.slice(0, 200);
|
|
9167
|
+
halted = true;
|
|
9168
|
+
break;
|
|
9169
|
+
}
|
|
9170
|
+
receipt.batches += 1;
|
|
9171
|
+
const body = res && res.body && typeof res.body === "object" ? res.body : {};
|
|
9172
|
+
if (res.status === 413) {
|
|
9173
|
+
if (batch.length > 1) {
|
|
9174
|
+
batchSize = 1;
|
|
9175
|
+
continue;
|
|
9176
|
+
}
|
|
9177
|
+
const item = batch[0];
|
|
9178
|
+
cursor += item.bytes;
|
|
9179
|
+
i += 1;
|
|
9180
|
+
receipt.rejected += 1;
|
|
9181
|
+
const id = typeof item.event?.event_id === "string" ? item.event.event_id : `offset:${cursor - item.bytes}`;
|
|
9182
|
+
state = {
|
|
9183
|
+
...state,
|
|
9184
|
+
byte_offset: cursor,
|
|
9185
|
+
rejected_total: (state.rejected_total || 0) + 1,
|
|
9186
|
+
rejected_event_ids: [...state.rejected_event_ids || [], `${id}:payload_too_large`].slice(-REJECTED_IDS_KEEP)
|
|
9187
|
+
};
|
|
9188
|
+
await saveState(statePath2, state);
|
|
9189
|
+
continue;
|
|
9190
|
+
}
|
|
9191
|
+
i += batch.length;
|
|
9192
|
+
if (res.status === 403) {
|
|
9193
|
+
pauseForbidden(body.error || "HTTP 403");
|
|
9194
|
+
receipt.halted = "forbidden";
|
|
9195
|
+
halted = true;
|
|
9196
|
+
break;
|
|
9197
|
+
}
|
|
9198
|
+
if (res.status === 503 && (body.error === "ingest_disabled" || body.error === "relay_unconfigured")) {
|
|
9199
|
+
const retrySec = Number(body.retry_after_sec) > 0 ? Number(body.retry_after_sec) : 600;
|
|
9200
|
+
receipt.halted = body.error;
|
|
9201
|
+
halted = true;
|
|
9202
|
+
scheduleFailure(retrySec * 1e3);
|
|
9203
|
+
break;
|
|
9204
|
+
}
|
|
9205
|
+
if (res.status !== 200 || body.ok !== true || !Array.isArray(body.results)) {
|
|
9206
|
+
receipt.halted = `HTTP ${res.status}${body.error ? ` ${body.error}` : ""}`;
|
|
9207
|
+
halted = true;
|
|
9208
|
+
break;
|
|
9209
|
+
}
|
|
9210
|
+
let applied = 0;
|
|
9211
|
+
let batchForwarded = 0;
|
|
9212
|
+
let batchRejected = 0;
|
|
9213
|
+
for (const r of body.results) {
|
|
9214
|
+
const item = batch[r.index];
|
|
9215
|
+
if (!item || r.index !== applied) break;
|
|
9216
|
+
if (r.status === "upstream_error") {
|
|
9217
|
+
receipt.halted = `upstream_error${r.error ? ` ${r.error}` : ""}`;
|
|
9218
|
+
halted = true;
|
|
9219
|
+
break;
|
|
9220
|
+
}
|
|
9221
|
+
cursor += item.bytes;
|
|
9222
|
+
applied += 1;
|
|
9223
|
+
if (r.status === "written") {
|
|
9224
|
+
receipt.forwarded += 1;
|
|
9225
|
+
batchForwarded += 1;
|
|
9226
|
+
} else if (r.status === "duplicate") receipt.duplicate += 1;
|
|
9227
|
+
else if (r.status === "rejected") {
|
|
9228
|
+
receipt.rejected += 1;
|
|
9229
|
+
batchRejected += 1;
|
|
9230
|
+
const id = typeof item.event?.event_id === "string" ? item.event.event_id : `offset:${cursor - item.bytes}`;
|
|
9231
|
+
state.rejected_event_ids = [...state.rejected_event_ids || [], `${id}:${r.error || "rejected"}`].slice(-REJECTED_IDS_KEEP);
|
|
9232
|
+
}
|
|
9233
|
+
if (typeof r.event_id === "string") state.last_event_id = r.event_id;
|
|
9234
|
+
}
|
|
9235
|
+
if (body.complete !== true && !halted) {
|
|
9236
|
+
receipt.halted = "incomplete-batch";
|
|
9237
|
+
halted = true;
|
|
9238
|
+
}
|
|
9239
|
+
if (applied < batch.length && !halted) {
|
|
9240
|
+
receipt.halted = "short-results";
|
|
9241
|
+
halted = true;
|
|
9242
|
+
}
|
|
9243
|
+
state = {
|
|
9244
|
+
...state,
|
|
9245
|
+
byte_offset: cursor,
|
|
9246
|
+
forwarded_total: (state.forwarded_total || 0) + batchForwarded,
|
|
9247
|
+
rejected_total: (state.rejected_total || 0) + batchRejected,
|
|
9248
|
+
last_forward_at: new Date(now()).toISOString(),
|
|
9249
|
+
last_error: receipt.halted
|
|
9250
|
+
};
|
|
9251
|
+
await saveState(statePath2, state);
|
|
9252
|
+
}
|
|
9253
|
+
if (halted) {
|
|
9254
|
+
state = { ...state, byte_offset: cursor, last_error: receipt.halted, last_halt_at: new Date(now()).toISOString() };
|
|
9255
|
+
await saveState(statePath2, state).catch(() => {
|
|
9256
|
+
});
|
|
9257
|
+
}
|
|
9258
|
+
const totals = { forwarded: state.forwarded_total || 0, rejected: state.rejected_total || 0 };
|
|
9259
|
+
if (halted && receipt.halted !== "forbidden" && receipt.halted !== "ingest_disabled" && receipt.halted !== "relay_unconfigured") {
|
|
9260
|
+
const delay2 = scheduleFailure();
|
|
9261
|
+
log2(`telemetry forwarder: halted (${receipt.halted}); cursor=${cursor}B; retry in ${Math.round(delay2 / 1e3)}s`);
|
|
9262
|
+
} else if (!halted) {
|
|
9263
|
+
failStreak = 0;
|
|
9264
|
+
nextRunAt = now() + intervalMs;
|
|
9265
|
+
log2(`telemetry forwarder: relayed ${receipt.batches} batch(es); cursor=${cursor}B; totals forwarded=${totals.forwarded} rejected=${totals.rejected}${receipt.malformed ? ` malformed=${receipt.malformed}` : ""}`);
|
|
9266
|
+
} else {
|
|
9267
|
+
log2(`telemetry forwarder: paused (${receipt.halted}); cursor=${cursor}B`);
|
|
9268
|
+
}
|
|
9269
|
+
return { ...receipt, status: halted ? "halted" : "ok", cursor, totals };
|
|
9270
|
+
}
|
|
9271
|
+
return {
|
|
9272
|
+
get enabled() {
|
|
9273
|
+
return enabled;
|
|
9274
|
+
},
|
|
9275
|
+
get disabledReason() {
|
|
9276
|
+
return disabledReason;
|
|
9277
|
+
},
|
|
9278
|
+
intervalMs,
|
|
9279
|
+
/** Fire-and-forget; safe to call every daemon loop iteration. */
|
|
9280
|
+
tick() {
|
|
9281
|
+
if (!enabled || running) return void 0;
|
|
9282
|
+
const t = now();
|
|
9283
|
+
if (t < nextRunAt) return void 0;
|
|
9284
|
+
running = true;
|
|
9285
|
+
return runOnce().catch((err) => {
|
|
9286
|
+
log2(`telemetry forwarder tick failed: ${err && err.message ? err.message : String(err)}`);
|
|
9287
|
+
scheduleFailure();
|
|
9288
|
+
}).finally(() => {
|
|
9289
|
+
running = false;
|
|
9290
|
+
});
|
|
9291
|
+
},
|
|
9292
|
+
runOnce
|
|
9293
|
+
};
|
|
9294
|
+
}
|
|
9295
|
+
var DEFAULT_FORWARD_INTERVAL_SEC, MIN_FORWARD_INTERVAL_SEC, DEFAULT_MAX_EVENTS_PER_MIN, MAX_EVENTS_PER_BATCH, MAX_READ_BYTES_PER_RUN, MAX_BACKOFF_MS, REJECTED_IDS_KEEP, FORBIDDEN_PAUSE_MS;
|
|
9296
|
+
var init_telemetry_forwarder = __esm({
|
|
9297
|
+
"../../scripts/virtual-office/code-runner/telemetry-forwarder.mjs"() {
|
|
9298
|
+
"use strict";
|
|
9299
|
+
DEFAULT_FORWARD_INTERVAL_SEC = 60;
|
|
9300
|
+
MIN_FORWARD_INTERVAL_SEC = 15;
|
|
9301
|
+
DEFAULT_MAX_EVENTS_PER_MIN = 500;
|
|
9302
|
+
MAX_EVENTS_PER_BATCH = 25;
|
|
9303
|
+
MAX_READ_BYTES_PER_RUN = 4 * 1024 * 1024;
|
|
9304
|
+
MAX_BACKOFF_MS = 30 * 60 * 1e3;
|
|
9305
|
+
REJECTED_IDS_KEEP = 100;
|
|
9306
|
+
FORBIDDEN_PAUSE_MS = 6 * 60 * 60 * 1e3;
|
|
9307
|
+
}
|
|
9308
|
+
});
|
|
9309
|
+
|
|
8908
9310
|
// ../../scripts/virtual-office/code-runner/loop-ticks.mjs
|
|
8909
9311
|
function makeLoopTicks({
|
|
8910
9312
|
client,
|
|
@@ -8934,7 +9336,11 @@ function makeLoopTicks({
|
|
|
8934
9336
|
getAccountUsage = () => [],
|
|
8935
9337
|
// Injectable for tests; default to the real scheduler + wall clock.
|
|
8936
9338
|
runResumeScheduler = runScheduler,
|
|
8937
|
-
now: nowFn = () => Date.now()
|
|
9339
|
+
now: nowFn = () => Date.now(),
|
|
9340
|
+
// Cursor-lessons §6 (2026-08-16): forwards this machine's vo-mcp events to
|
|
9341
|
+
// the control-plane telemetry relay (telemetry-forwarder.mjs). Inert when the
|
|
9342
|
+
// client lacks relayTelemetryEvents (test fakes) or VO_MCP_EVENTS_FORWARD=0.
|
|
9343
|
+
telemetryForwarder = createTelemetryForwarder({ client, cfg, env: env2, log: log2, runnerInstanceId, now: nowFn })
|
|
8938
9344
|
}) {
|
|
8939
9345
|
let lastSessionForward = 0;
|
|
8940
9346
|
let lastHeartbeat = 0;
|
|
@@ -9046,6 +9452,7 @@ function makeLoopTicks({
|
|
|
9046
9452
|
resumeRunning = false;
|
|
9047
9453
|
});
|
|
9048
9454
|
}
|
|
9455
|
+
telemetryForwarder.tick();
|
|
9049
9456
|
return Promise.all(heartbeatCompletions).then(() => void 0);
|
|
9050
9457
|
};
|
|
9051
9458
|
}
|
|
@@ -9055,6 +9462,7 @@ var init_loop_ticks = __esm({
|
|
|
9055
9462
|
"use strict";
|
|
9056
9463
|
init_session_spool_forwarder();
|
|
9057
9464
|
init_rate_limit_resume_scheduler();
|
|
9465
|
+
init_telemetry_forwarder();
|
|
9058
9466
|
HEARTBEAT_MS = 6e4;
|
|
9059
9467
|
DEFAULT_RESUME_SCHEDULE_SEC = 300;
|
|
9060
9468
|
}
|
|
@@ -10051,7 +10459,7 @@ async function scheduleCoordinationRetry({
|
|
|
10051
10459
|
const attemptsKey = kind === "resume" ? "resumeAttempts" : "fixAttempts";
|
|
10052
10460
|
entry[errorsKey] = (entry[errorsKey] || 0) + 1;
|
|
10053
10461
|
entry[attemptsKey] = Math.max(0, (entry[attemptsKey] || 1) - 1);
|
|
10054
|
-
const delay2 = Math.min(
|
|
10462
|
+
const delay2 = Math.min(MAX_BACKOFF_MS2, 3e4 * 2 ** Math.min(7, entry[errorsKey] - 1));
|
|
10055
10463
|
entry.nextRetryAt = now() + delay2;
|
|
10056
10464
|
if (entry[errorsKey] >= 3 && !entry.coordinationEscalatedAt && typeof reportBlocker === "function") {
|
|
10057
10465
|
try {
|
|
@@ -10066,12 +10474,12 @@ async function scheduleCoordinationRetry({
|
|
|
10066
10474
|
}
|
|
10067
10475
|
log2(`watch: pr #${prNumber} ${kind} coordination failed ${entry[errorsKey]}x; retry in ${Math.round(delay2 / 1e3)}s: ${boundedErrorMessage(error)}`);
|
|
10068
10476
|
}
|
|
10069
|
-
var
|
|
10477
|
+
var MAX_BACKOFF_MS2, MERGE_ENQUEUE_TTL_MS, TERMINAL_RESUME_REFUSALS;
|
|
10070
10478
|
var init_watcher_coordination = __esm({
|
|
10071
10479
|
"../../scripts/virtual-office/code-runner/watcher-coordination.mjs"() {
|
|
10072
10480
|
"use strict";
|
|
10073
10481
|
init_error_message();
|
|
10074
|
-
|
|
10482
|
+
MAX_BACKOFF_MS2 = 60 * 60 * 1e3;
|
|
10075
10483
|
MERGE_ENQUEUE_TTL_MS = 2 * 60 * 60 * 1e3;
|
|
10076
10484
|
TERMINAL_RESUME_REFUSALS = Object.freeze([
|
|
10077
10485
|
"automatic_continuation_budget_too_small",
|
|
@@ -10081,6 +10489,9 @@ var init_watcher_coordination = __esm({
|
|
|
10081
10489
|
"continuation_spend_unmeasured",
|
|
10082
10490
|
"continuation_lineage_incomplete",
|
|
10083
10491
|
"continuation_lineage_invalid",
|
|
10492
|
+
// F45 (2026-08-17): the plane refuses to resume an ANCESTOR once a newer attempt exists — for a
|
|
10493
|
+
// caller holding this task id no retry can succeed; resume the newest attempt instead.
|
|
10494
|
+
"continuation_latest_attempt_required",
|
|
10084
10495
|
"cancelled_not_automatically_resumable",
|
|
10085
10496
|
"not_resumable"
|
|
10086
10497
|
]);
|
|
@@ -10089,12 +10500,12 @@ var init_watcher_coordination = __esm({
|
|
|
10089
10500
|
|
|
10090
10501
|
// ../../scripts/virtual-office/code-runner/watcher-state.mjs
|
|
10091
10502
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
10092
|
-
import { mkdir as
|
|
10093
|
-
import { dirname as
|
|
10503
|
+
import { mkdir as mkdir3, open as open2, readFile as readFile4, rename, unlink as unlink2 } from "node:fs/promises";
|
|
10504
|
+
import { dirname as dirname10 } from "node:path";
|
|
10094
10505
|
async function readWatcherState(stateFile) {
|
|
10095
10506
|
let raw;
|
|
10096
10507
|
try {
|
|
10097
|
-
raw = await
|
|
10508
|
+
raw = await readFile4(stateFile, "utf8");
|
|
10098
10509
|
} catch (error) {
|
|
10099
10510
|
if (error?.code === "ENOENT") return {};
|
|
10100
10511
|
throw error;
|
|
@@ -10106,12 +10517,12 @@ async function readWatcherState(stateFile) {
|
|
|
10106
10517
|
return parsed;
|
|
10107
10518
|
}
|
|
10108
10519
|
async function writeWatcherState(stateFile, state) {
|
|
10109
|
-
const directory =
|
|
10110
|
-
await
|
|
10520
|
+
const directory = dirname10(stateFile);
|
|
10521
|
+
await mkdir3(directory, { recursive: true });
|
|
10111
10522
|
const temp = `${stateFile}.${process.pid}.${randomUUID3()}.tmp`;
|
|
10112
10523
|
let handle;
|
|
10113
10524
|
try {
|
|
10114
|
-
handle = await
|
|
10525
|
+
handle = await open2(temp, "wx");
|
|
10115
10526
|
await handle.writeFile(JSON.stringify(state, null, 2), "utf8");
|
|
10116
10527
|
await handle.sync();
|
|
10117
10528
|
await handle.close();
|
|
@@ -10283,6 +10694,8 @@ function buildCiFixPrompt({ prNumber, repo, branch, headSha, failedChecks, prPat
|
|
|
10283
10694
|
"in your summary). Your work ships: finish the fix completely, leave UNCOMMITTED edits,",
|
|
10284
10695
|
"and the runner opens the PR for you. A denied git/gh is EXPECTED; do NOT retry it.",
|
|
10285
10696
|
"",
|
|
10697
|
+
HEADLESS_EXECUTION_CONTRACT,
|
|
10698
|
+
"",
|
|
10286
10699
|
"## Bounded source PR patch excerpt",
|
|
10287
10700
|
"```diff",
|
|
10288
10701
|
prPatch || "[No excerpt available; the complete exact source is still materialized in the worktree.]",
|
|
@@ -10298,6 +10711,7 @@ var init_ci_fix_prompt = __esm({
|
|
|
10298
10711
|
"../../scripts/virtual-office/code-runner/ci-fix-prompt.mjs"() {
|
|
10299
10712
|
"use strict";
|
|
10300
10713
|
init_superseded_pr_source();
|
|
10714
|
+
init_headless_execution_contract();
|
|
10301
10715
|
}
|
|
10302
10716
|
});
|
|
10303
10717
|
|
|
@@ -10334,7 +10748,9 @@ async function readCommitCiViaRest(repo, sha, { run, env: env2 }) {
|
|
|
10334
10748
|
name: String(r?.name || ""),
|
|
10335
10749
|
status: String(r?.status || ""),
|
|
10336
10750
|
conclusion: r?.conclusion == null ? null : String(r.conclusion),
|
|
10337
|
-
...r?.details_url ? { detailsUrl: String(r.details_url) } : {}
|
|
10751
|
+
...r?.details_url ? { detailsUrl: String(r.details_url) } : {},
|
|
10752
|
+
...r?.started_at ? { startedAt: String(r.started_at) } : {},
|
|
10753
|
+
...r?.completed_at ? { completedAt: String(r.completed_at) } : {}
|
|
10338
10754
|
});
|
|
10339
10755
|
}
|
|
10340
10756
|
if (runs.length === 0) break;
|
|
@@ -10404,6 +10820,11 @@ var init_pr_watcher_github = __esm({
|
|
|
10404
10820
|
|
|
10405
10821
|
// ../../scripts/virtual-office/code-runner/enqueue-autonomous-code-task.mjs
|
|
10406
10822
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
10823
|
+
function isDefiniteRefusal(err) {
|
|
10824
|
+
const status = Number(err?.status);
|
|
10825
|
+
if (Number.isFinite(status) && status >= 400 && status < 500) return true;
|
|
10826
|
+
return status === 503 && typeof err?.code === "string" && UNAVAILABLE_WITH_CODE.has(err.code);
|
|
10827
|
+
}
|
|
10407
10828
|
async function enqueueAutonomousCodeTask(client, task, log2 = () => {
|
|
10408
10829
|
}) {
|
|
10409
10830
|
const requestedBudgetUsd = task?.max_budget_usd;
|
|
@@ -10426,17 +10847,29 @@ async function enqueueAutonomousCodeTask(client, task, log2 = () => {
|
|
|
10426
10847
|
if (admission?.allowed !== true) {
|
|
10427
10848
|
throw new Error(`autonomous dispatch blocked: ${admission?.reason || "admission denied"}`);
|
|
10428
10849
|
}
|
|
10429
|
-
|
|
10850
|
+
try {
|
|
10851
|
+
return await client.enqueueCodeTask({ ...task, autonomous_reservation_id: reservationId });
|
|
10852
|
+
} catch (err) {
|
|
10853
|
+
if (isDefiniteRefusal(err)) {
|
|
10854
|
+
try {
|
|
10855
|
+
await client.releaseAutonomousDispatchBudget(reservationId);
|
|
10856
|
+
} catch {
|
|
10857
|
+
}
|
|
10858
|
+
}
|
|
10859
|
+
throw err;
|
|
10860
|
+
}
|
|
10430
10861
|
}
|
|
10862
|
+
var UNAVAILABLE_WITH_CODE;
|
|
10431
10863
|
var init_enqueue_autonomous_code_task = __esm({
|
|
10432
10864
|
"../../scripts/virtual-office/code-runner/enqueue-autonomous-code-task.mjs"() {
|
|
10433
10865
|
"use strict";
|
|
10866
|
+
UNAVAILABLE_WITH_CODE = /* @__PURE__ */ new Set(["repair_state_unavailable", "inspection_unavailable", "verify_unavailable"]);
|
|
10434
10867
|
}
|
|
10435
10868
|
});
|
|
10436
10869
|
|
|
10437
10870
|
// ../../scripts/virtual-office/code-runner/pr-watcher.mjs
|
|
10438
|
-
import { homedir as
|
|
10439
|
-
import { join as
|
|
10871
|
+
import { homedir as homedir9 } from "node:os";
|
|
10872
|
+
import { join as join14 } from "node:path";
|
|
10440
10873
|
function parsePrCiStatus(view) {
|
|
10441
10874
|
const state = (view && typeof view.state === "string" ? view.state : "UNKNOWN").toUpperCase();
|
|
10442
10875
|
const rollup = view && Array.isArray(view.statusCheckRollup) ? view.statusCheckRollup : [];
|
|
@@ -10782,7 +11215,7 @@ var init_pr_watcher = __esm({
|
|
|
10782
11215
|
init_watcher_state();
|
|
10783
11216
|
init_superseded_pr_source();
|
|
10784
11217
|
init_ci_fix_prompt();
|
|
10785
|
-
DEFAULT_STATE_FILE =
|
|
11218
|
+
DEFAULT_STATE_FILE = join14(homedir9(), ".vo", "dispatched-prs.json");
|
|
10786
11219
|
FAIL_CONCLUSIONS = /* @__PURE__ */ new Set([
|
|
10787
11220
|
"FAILURE",
|
|
10788
11221
|
"TIMED_OUT",
|
|
@@ -12026,8 +12459,8 @@ var init_classify_task = __esm({
|
|
|
12026
12459
|
|
|
12027
12460
|
// ../../scripts/virtual-office/code-runner/auto-router/effort-policy.mjs
|
|
12028
12461
|
import { readFileSync as readFileSync9 } from "node:fs";
|
|
12029
|
-
import { homedir as
|
|
12030
|
-
import { join as
|
|
12462
|
+
import { homedir as homedir10 } from "node:os";
|
|
12463
|
+
import { join as join15 } from "node:path";
|
|
12031
12464
|
function difficultyToRung(difficulty, thresholds) {
|
|
12032
12465
|
const b = thresholds.rungBounds;
|
|
12033
12466
|
if (difficulty >= b.R5) return "R5";
|
|
@@ -12104,7 +12537,7 @@ var init_effort_policy = __esm({
|
|
|
12104
12537
|
init_meta_model_catalog();
|
|
12105
12538
|
RUNG_ORDER = ["R1", "R2", "R3", "R4", "R5"];
|
|
12106
12539
|
rungIndex = (rung) => RUNG_ORDER.indexOf(rung);
|
|
12107
|
-
DEFAULT_CODEX_MODELS_CACHE =
|
|
12540
|
+
DEFAULT_CODEX_MODELS_CACHE = join15(homedir10(), ".codex", "models_cache.json");
|
|
12108
12541
|
}
|
|
12109
12542
|
});
|
|
12110
12543
|
|
|
@@ -12235,8 +12668,8 @@ var init_role_cost_shadow = __esm({
|
|
|
12235
12668
|
|
|
12236
12669
|
// ../../scripts/virtual-office/code-runner/auto-router/auto-router.mjs
|
|
12237
12670
|
import { readFileSync as readFileSync10, appendFileSync, mkdirSync as mkdirSync8 } from "node:fs";
|
|
12238
|
-
import { homedir as
|
|
12239
|
-
import { join as
|
|
12671
|
+
import { homedir as homedir11 } from "node:os";
|
|
12672
|
+
import { join as join16, dirname as dirname11 } from "node:path";
|
|
12240
12673
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
12241
12674
|
function getAutoRouterMode(env2 = process.env) {
|
|
12242
12675
|
const raw = String(env2.VO_CODE_RUNNER_AUTO_ROUTER || "").trim().toLowerCase();
|
|
@@ -12244,8 +12677,8 @@ function getAutoRouterMode(env2 = process.env) {
|
|
|
12244
12677
|
}
|
|
12245
12678
|
function loadThresholds() {
|
|
12246
12679
|
if (!cachedThresholds) {
|
|
12247
|
-
const here =
|
|
12248
|
-
cachedThresholds = JSON.parse(readFileSync10(
|
|
12680
|
+
const here = dirname11(fileURLToPath7(import.meta.url));
|
|
12681
|
+
cachedThresholds = JSON.parse(readFileSync10(join16(here, "thresholds.json"), "utf8"));
|
|
12249
12682
|
}
|
|
12250
12683
|
return cachedThresholds;
|
|
12251
12684
|
}
|
|
@@ -12311,9 +12744,9 @@ function formatDecisionReason(decision, maxLen = 480) {
|
|
|
12311
12744
|
const s = `[${decision.routerVersion}] ${decision.taskClass} d=${decision.difficulty} c=${decision.confidence} \u2192 ${decision.rung}/${decision.tier}${decision.effort ? ` effort=${decision.effort}` : ""} turns=${decision.maxTurns} $${decision.maxBudgetUsd}${decision.flags.length ? ` [${decision.flags.join(",")}]` : ""} :: ${decision.reasons.join("; ")}`;
|
|
12312
12745
|
return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
|
|
12313
12746
|
}
|
|
12314
|
-
function appendDecisionFallback(decision, { path: path22 = DECISION_FALLBACK_PATH, append = appendFileSync, mkdir:
|
|
12747
|
+
function appendDecisionFallback(decision, { path: path22 = DECISION_FALLBACK_PATH, append = appendFileSync, mkdir: mkdir5 = mkdirSync8, task, thresholds, roleCostInputs } = {}) {
|
|
12315
12748
|
try {
|
|
12316
|
-
|
|
12749
|
+
mkdir5(dirname11(path22), { recursive: true });
|
|
12317
12750
|
append(path22, `${JSON.stringify(decision)}
|
|
12318
12751
|
`, "utf8");
|
|
12319
12752
|
if (isRouterDecision(decision)) {
|
|
@@ -12337,7 +12770,7 @@ var init_auto_router = __esm({
|
|
|
12337
12770
|
init_effort_policy();
|
|
12338
12771
|
init_role_cost_shadow();
|
|
12339
12772
|
ROUTER_VERSION = "0.1.0";
|
|
12340
|
-
DECISION_FALLBACK_PATH =
|
|
12773
|
+
DECISION_FALLBACK_PATH = join16(homedir11(), ".claude", "vo-auto-router-decisions.jsonl");
|
|
12341
12774
|
MODES = /* @__PURE__ */ new Set(["off", "shadow", "on"]);
|
|
12342
12775
|
cachedThresholds = null;
|
|
12343
12776
|
isRouterDecision = (d) => Boolean(d && typeof d === "object" && typeof d.taskClass === "string" && typeof d.confidence === "number");
|
|
@@ -12737,7 +13170,7 @@ function safeBaseEnv(env2 = {}) {
|
|
|
12737
13170
|
}
|
|
12738
13171
|
return result;
|
|
12739
13172
|
}
|
|
12740
|
-
function buildAgentProcessEnv(env2, { agent = "agent", runnerId = "vo-runner", taskId = "task", githubReadToken = null, swarmAdmission = null } = {}) {
|
|
13173
|
+
function buildAgentProcessEnv(env2, { agent = "agent", runnerId = "vo-runner", taskId = "task", repo = null, githubReadToken = null, swarmAdmission = null } = {}) {
|
|
12741
13174
|
const base = safeBaseEnv(env2);
|
|
12742
13175
|
if (swarmAdmission) {
|
|
12743
13176
|
for (const key of Object.keys(base)) {
|
|
@@ -12749,6 +13182,8 @@ function buildAgentProcessEnv(env2, { agent = "agent", runnerId = "vo-runner", t
|
|
|
12749
13182
|
base.GH_TOKEN = githubReadToken;
|
|
12750
13183
|
base.GITHUB_TOKEN = githubReadToken;
|
|
12751
13184
|
}
|
|
13185
|
+
if (typeof taskId === "string" && taskId && taskId !== "task") base.VO_CODE_TASK_ID = taskId;
|
|
13186
|
+
if (typeof repo === "string" && /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u.test(repo)) base.VO_CODE_TASK_REPO = repo;
|
|
12752
13187
|
if (String(env2?.AGENT_ID || "").trim()) return { ...base, AGENT_ID: env2.AGENT_ID };
|
|
12753
13188
|
const generated = [
|
|
12754
13189
|
"vo",
|
|
@@ -12818,6 +13253,12 @@ var init_agent_process_env = __esm({
|
|
|
12818
13253
|
// NOT a credential and NOT an authorization input: it names a tier, it never
|
|
12819
13254
|
// grants one, and it carries no key material (see the module's header rule).
|
|
12820
13255
|
"VO_SWARM_TIER_BINDING",
|
|
13256
|
+
// Increased-moat staging flags (2026-08-16): a fleet machine's runner env
|
|
13257
|
+
// decides whether its agents' vo-mcp ALSO sends verdicts through the moat
|
|
13258
|
+
// (shadow) or lets the moat win (authoritative). Per-machine, default absent.
|
|
13259
|
+
"VO_CONSENSUS_MOAT_SHADOW",
|
|
13260
|
+
"VO_CONSENSUS_MOAT_SHADOW_PCT",
|
|
13261
|
+
"VO_CONSENSUS_MOAT_AUTHORITATIVE",
|
|
12821
13262
|
// Where the swarm SPAWN LEDGER lives (SWARM_LEDGER_DIR_ENV in
|
|
12822
13263
|
// packages/vo-mcp/src/swarm/spawn-ledger.ts). The ledger is the durable,
|
|
12823
13264
|
// host-shared counter that bounds the TOTAL spawns under one swarm_id; the
|
|
@@ -14053,9 +14494,9 @@ function recoveryLedgerCandidates(repo, clonesRoot2) {
|
|
|
14053
14494
|
path21.join(canonical, ".agent-worktrees", "recovery-ledger.jsonl")
|
|
14054
14495
|
];
|
|
14055
14496
|
}
|
|
14056
|
-
async function readLedger(file,
|
|
14497
|
+
async function readLedger(file, readFile6) {
|
|
14057
14498
|
try {
|
|
14058
|
-
return String(await
|
|
14499
|
+
return String(await readFile6(file, "utf8")).split(/\r?\n/).filter(Boolean).flatMap((line) => {
|
|
14059
14500
|
try {
|
|
14060
14501
|
return [JSON.parse(line)];
|
|
14061
14502
|
} catch {
|
|
@@ -14068,7 +14509,7 @@ async function readLedger(file, readFile5) {
|
|
|
14068
14509
|
}
|
|
14069
14510
|
async function findPreservedRecovery(task, {
|
|
14070
14511
|
clonesRoot: clonesRoot2 = process.env.VO_CODE_RUNNER_CLONES_ROOT || "",
|
|
14071
|
-
readFile:
|
|
14512
|
+
readFile: readFile6 = fsp13.readFile,
|
|
14072
14513
|
exists = fs13.existsSync,
|
|
14073
14514
|
alreadyPublished = preservedHeadAlreadyOnBranch,
|
|
14074
14515
|
log: log2 = () => {
|
|
@@ -14079,7 +14520,7 @@ async function findPreservedRecovery(task, {
|
|
|
14079
14520
|
if (!originalTaskId) return null;
|
|
14080
14521
|
if (task.pr_branch && !recoveryTaskId(task.prompt)) return null;
|
|
14081
14522
|
for (const ledgerPath of recoveryLedgerCandidates(task.repo, clonesRoot2)) {
|
|
14082
|
-
const entries = await readLedger(ledgerPath,
|
|
14523
|
+
const entries = await readLedger(ledgerPath, readFile6);
|
|
14083
14524
|
const resolved = entries.some((entry) => RESOLVED_RECOVERY_TYPES.has(entry.type) && entry.taskId === originalTaskId);
|
|
14084
14525
|
const preserved = [...entries].reverse().find((entry) => entry.taskId === originalTaskId && entry.worktreeDir);
|
|
14085
14526
|
if (!resolved && preserved && exists(preserved.worktreeDir)) {
|
|
@@ -14420,9 +14861,9 @@ var init_cancellation_probe = __esm({
|
|
|
14420
14861
|
});
|
|
14421
14862
|
|
|
14422
14863
|
// ../../scripts/virtual-office/code-runner/detached-economics-spool.mjs
|
|
14423
|
-
import { homedir as
|
|
14424
|
-
import { dirname as
|
|
14425
|
-
import { mkdir as
|
|
14864
|
+
import { homedir as homedir12 } from "node:os";
|
|
14865
|
+
import { dirname as dirname12, join as join17 } from "node:path";
|
|
14866
|
+
import { mkdir as mkdir4, readFile as readFile5, rename as rename2, writeFile as writeFile4 } from "node:fs/promises";
|
|
14426
14867
|
function withLock(operation) {
|
|
14427
14868
|
const result = serialized.then(operation, operation);
|
|
14428
14869
|
serialized = result.then(() => void 0, () => void 0);
|
|
@@ -14430,7 +14871,7 @@ function withLock(operation) {
|
|
|
14430
14871
|
}
|
|
14431
14872
|
async function readEntries(file) {
|
|
14432
14873
|
try {
|
|
14433
|
-
const parsed = JSON.parse(await
|
|
14874
|
+
const parsed = JSON.parse(await readFile5(file, "utf8"));
|
|
14434
14875
|
if (!Array.isArray(parsed)) throw new Error("detached economics spool is not an array");
|
|
14435
14876
|
return parsed;
|
|
14436
14877
|
} catch (error) {
|
|
@@ -14439,9 +14880,9 @@ async function readEntries(file) {
|
|
|
14439
14880
|
}
|
|
14440
14881
|
}
|
|
14441
14882
|
async function writeEntries(file, entries) {
|
|
14442
|
-
await
|
|
14883
|
+
await mkdir4(dirname12(file), { recursive: true });
|
|
14443
14884
|
const temp = `${file}.${process.pid}.tmp`;
|
|
14444
|
-
await
|
|
14885
|
+
await writeFile4(temp, `${JSON.stringify(entries)}
|
|
14445
14886
|
`, "utf8");
|
|
14446
14887
|
await rename2(temp, file);
|
|
14447
14888
|
}
|
|
@@ -14485,7 +14926,7 @@ var DEFAULT_FILE, serialized;
|
|
|
14485
14926
|
var init_detached_economics_spool = __esm({
|
|
14486
14927
|
"../../scripts/virtual-office/code-runner/detached-economics-spool.mjs"() {
|
|
14487
14928
|
"use strict";
|
|
14488
|
-
DEFAULT_FILE =
|
|
14929
|
+
DEFAULT_FILE = join17(homedir12(), ".vo", "detached-run-economics.json");
|
|
14489
14930
|
serialized = Promise.resolve();
|
|
14490
14931
|
}
|
|
14491
14932
|
});
|
|
@@ -14886,7 +15327,7 @@ async function processOneTask(client, task, cfg, runnerInstanceId, swarmAdmissio
|
|
|
14886
15327
|
maxBudgetUsd: sel.agent === "claude" ? effectiveMaxBudgetUsd : void 0,
|
|
14887
15328
|
researchHarness: methodology?.shape === "research",
|
|
14888
15329
|
// Workflow grant only for research-shaped tasks
|
|
14889
|
-
env: buildAgentProcessEnv(process.env, { agent: sel.agent, runnerId: cfg.runnerId, taskId: id, githubReadToken: agentGithubReadToken, swarmAdmission }),
|
|
15330
|
+
env: buildAgentProcessEnv(process.env, { agent: sel.agent, runnerId: cfg.runnerId, taskId: id, repo: task.repo, githubReadToken: agentGithubReadToken, swarmAdmission }),
|
|
14890
15331
|
// swarmAdmission mints VO_SWARM_TIER_BINDING: ONE tier decision for this task's whole agent tree
|
|
14891
15332
|
sandbox,
|
|
14892
15333
|
onProgress: (text, checkpoint) => {
|