@algosuite/vo-mcp 0.2.0-beta.49 → 0.2.0-beta.50

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.
@@ -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 stat2 = await fsApi.lstat(rootPath);
1142
- if (stat2.isSymbolicLink()) {
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 (!stat2.isDirectory()) {
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, stat2, fsApi) {
1150
+ async function removeReparsePoint(target, stat3, fsApi) {
1151
1151
  try {
1152
- if (stat2.isDirectory()) {
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 (stat2.isDirectory() && ["ENOTDIR", "EPERM", "EISDIR", "EACCES"].includes(error?.code)) {
1158
+ if (stat3.isDirectory() && ["ENOTDIR", "EPERM", "EISDIR", "EACCES"].includes(error?.code)) {
1159
1159
  await fsApi.unlink(target);
1160
1160
  return;
1161
1161
  }
1162
- if (!stat2.isDirectory() && ["EPERM", "EISDIR", "EACCES"].includes(error?.code)) {
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 stat2 = await fsApi.lstat(child);
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 (stat2.isSymbolicLink()) {
1197
- await onLink(child, stat2, normalized, fsApi);
1196
+ if (stat3.isSymbolicLink()) {
1197
+ await onLink(child, stat3, normalized, fsApi);
1198
1198
  continue;
1199
1199
  }
1200
- if (stat2.isDirectory()) {
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, stat2, _normalized, fsApi) => {
1240
- await removeReparsePoint(target, stat2, fsApi);
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 stat2 = await fsApi.lstat(nodeModulesDir);
1675
- if (stat2.isSymbolicLink() || !stat2.isDirectory()) {
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 stat2 = await fsApi.lstat(workspaceNodeModules);
1699
- if (stat2.isSymbolicLink() || !stat2.isDirectory()) {
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 stat2 = await options.fsApi.lstat(sourceEntry);
1940
- if (stat2.isDirectory() && !stat2.isSymbolicLink() && path4.basename(sourceEntry) === ".bin") {
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 (stat2.isDirectory() && !stat2.isSymbolicLink() && path4.basename(sourceEntry).startsWith("@")) {
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 (stat2.isSymbolicLink() || stat2.isDirectory()) {
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 stat2 = await fsp7.lstat(source);
2664
- if (stat2.isSymbolicLink()) {
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 (!stat2.isFile()) {
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);
@@ -3482,6 +3482,60 @@ var init_control_plane_merge = __esm({
3482
3482
  }
3483
3483
  });
3484
3484
 
3485
+ // ../../scripts/virtual-office/code-runner/control-plane-weekly-tokens.mjs
3486
+ async function postWeeklyTokensRequest(taskReq, { operatorId, runnerId, tokens, claudeWeeklyPct, claudeWeeklyResetsAt }, onUnauthorized = () => {
3487
+ }) {
3488
+ const body = {
3489
+ operator_id: operatorId,
3490
+ runner_id: runnerId,
3491
+ input_tokens: tokens.input_tokens,
3492
+ output_tokens: tokens.output_tokens,
3493
+ cache_creation_tokens: tokens.cache_creation_tokens,
3494
+ cache_read_tokens: tokens.cache_read_tokens
3495
+ };
3496
+ if (typeof claudeWeeklyPct === "number") {
3497
+ body.claude_weekly_pct = claudeWeeklyPct;
3498
+ }
3499
+ if (claudeWeeklyResetsAt !== void 0) {
3500
+ body.claude_weekly_resets_at = claudeWeeklyResetsAt;
3501
+ }
3502
+ const res = await taskReq("POST", "/api/v1/weekly-tokens", body);
3503
+ if (res.status === 401) {
3504
+ onUnauthorized();
3505
+ throw new Error("weekly-tokens unauthorized (401)");
3506
+ }
3507
+ if (!res.ok) throw new Error(`weekly-tokens failed: HTTP ${res.status}`);
3508
+ return true;
3509
+ }
3510
+ var init_control_plane_weekly_tokens = __esm({
3511
+ "../../scripts/virtual-office/code-runner/control-plane-weekly-tokens.mjs"() {
3512
+ "use strict";
3513
+ }
3514
+ });
3515
+
3516
+ // ../../scripts/virtual-office/code-runner/control-plane-telemetry-relay.mjs
3517
+ async function relayTelemetryEventsRequest(req, { events, source }, onUnauthorized = () => {
3518
+ }) {
3519
+ const res = await req("POST", "/api/v1/telemetry/relay", { events, ...source ? { source } : {} }, {
3520
+ timeoutMs: TELEMETRY_RELAY_TIMEOUT_MS
3521
+ });
3522
+ if (res.status === 401) onUnauthorized();
3523
+ let body = null;
3524
+ try {
3525
+ body = await res.json();
3526
+ } catch {
3527
+ body = null;
3528
+ }
3529
+ return { status: res.status, body };
3530
+ }
3531
+ var TELEMETRY_RELAY_TIMEOUT_MS;
3532
+ var init_control_plane_telemetry_relay = __esm({
3533
+ "../../scripts/virtual-office/code-runner/control-plane-telemetry-relay.mjs"() {
3534
+ "use strict";
3535
+ TELEMETRY_RELAY_TIMEOUT_MS = 3e4;
3536
+ }
3537
+ });
3538
+
3485
3539
  // ../../scripts/virtual-office/code-runner/claim-gate-notice.mjs
3486
3540
  function describeClaimGate(gate) {
3487
3541
  if (!gate || gate.allowed !== false) return null;
@@ -3739,38 +3793,21 @@ function createControlPlaneClient({
3739
3793
  if (!res.ok) throw new Error(`knowledge-context failed: HTTP ${res.status}`);
3740
3794
  return res.json();
3741
3795
  },
3796
+ /** Weekly Claude token usage report — see control-plane-weekly-tokens.mjs. */
3797
+ async postWeeklyTokens(report) {
3798
+ return postWeeklyTokensRequest(taskReq, report, () => {
3799
+ cachedFirebaseToken = null;
3800
+ });
3801
+ },
3742
3802
  /**
3743
- * Report this machine's rolling-7-day Claude Code token usage (the real
3744
- * weekly-capacity gauge) PLUS the operator's real Claude weekly % (when
3745
- * available). The daemon authenticates as admin, so the target `operatorId`
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).
3803
+ * Relay a batch of this machine's local vo-mcp events to vo-telemetry via
3804
+ * the control plane (telemetry-forwarder.mjs). Returns { status, body };
3805
+ * the forwarder owns backoff/disable policy. See control-plane-telemetry-relay.mjs.
3751
3806
  */
3752
- async postWeeklyTokens({ operatorId, runnerId: runnerId2, tokens, claudeWeeklyPct, claudeWeeklyResetsAt }) {
3753
- const body = {
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) {
3807
+ async relayTelemetryEvents(batch) {
3808
+ return relayTelemetryEventsRequest(req, batch, () => {
3769
3809
  cachedFirebaseToken = null;
3770
- throw new Error("weekly-tokens unauthorized (401)");
3771
- }
3772
- if (!res.ok) throw new Error(`weekly-tokens failed: HTTP ${res.status}`);
3773
- return true;
3810
+ });
3774
3811
  },
3775
3812
  /**
3776
3813
  * Send a liveness heartbeat (M2). The control-plane upserts it under the
@@ -3904,6 +3941,8 @@ var init_control_plane_client = __esm({
3904
3941
  init_control_plane_resume();
3905
3942
  init_control_plane_autonomous_admission();
3906
3943
  init_control_plane_merge();
3944
+ init_control_plane_weekly_tokens();
3945
+ init_control_plane_telemetry_relay();
3907
3946
  init_claim_gate_notice();
3908
3947
  cachedFirebaseToken = null;
3909
3948
  ClaimAuthorityChangedError = class extends Error {
@@ -6690,8 +6729,8 @@ async function acquireLock(lockFile, { now = Date.now, sleep: sleep3 = delay } =
6690
6729
  stale = !alive || !Number.isFinite(created) || now() - created > LOCK_STALE_MS;
6691
6730
  } catch {
6692
6731
  try {
6693
- const stat2 = await fsp10.stat(lockFile);
6694
- stale = now() - stat2.mtimeMs > LOCK_INIT_GRACE_MS;
6732
+ const stat3 = await fsp10.stat(lockFile);
6733
+ stale = now() - stat3.mtimeMs > LOCK_INIT_GRACE_MS;
6695
6734
  } catch {
6696
6735
  stale = false;
6697
6736
  }
@@ -8740,8 +8779,8 @@ function selectDueEntries({ entries = [], now, alreadyDispatched = /* @__PURE__
8740
8779
  const entryAtMs = new Date(at).getTime();
8741
8780
  if (Number.isFinite(entryAtMs)) {
8742
8781
  const attemptCount = typeof attempts === "number" ? attempts : 0;
8743
- const backoffMs = NULL_RESUME_AFTER_BACKOFF_MS * Math.pow(2, attemptCount);
8744
- const dueAtMs = entryAtMs + backoffMs;
8782
+ const backoffMs2 = NULL_RESUME_AFTER_BACKOFF_MS * Math.pow(2, attemptCount);
8783
+ const dueAtMs = entryAtMs + backoffMs2;
8745
8784
  isDue = nowMs >= dueAtMs;
8746
8785
  }
8747
8786
  } else {
@@ -8905,6 +8944,303 @@ var init_rate_limit_resume_scheduler = __esm({
8905
8944
  }
8906
8945
  });
8907
8946
 
8947
+ // ../../scripts/virtual-office/code-runner/telemetry-forwarder.mjs
8948
+ import { homedir as homedir8 } from "node:os";
8949
+ import { dirname as dirname9, join as join13 } from "node:path";
8950
+ import { mkdir as mkdir2, open, readFile as readFile3, stat as stat2, writeFile as writeFile3 } from "node:fs/promises";
8951
+ function defaultEventsPath(env2 = process.env) {
8952
+ const p = String(env2.VO_MCP_EVENTS_PATH || "").trim();
8953
+ return p || join13(homedir8(), ".claude", "vo-mcp-events.jsonl");
8954
+ }
8955
+ function defaultStatePath(env2 = process.env) {
8956
+ const p = String(env2.VO_MCP_EVENTS_FORWARD_STATE || "").trim();
8957
+ return p || join13(homedir8(), ".claude", "vo-mcp-events-forward-state.json");
8958
+ }
8959
+ function splitCompleteLines(buf) {
8960
+ const lines = [];
8961
+ let start = 0;
8962
+ let consumedBytes = 0;
8963
+ for (; ; ) {
8964
+ const nl = buf.indexOf(10, start);
8965
+ if (nl === -1) break;
8966
+ lines.push({ text: buf.subarray(start, nl).toString("utf8"), bytes: nl - start + 1 });
8967
+ consumedBytes = nl + 1;
8968
+ start = nl + 1;
8969
+ }
8970
+ return { lines, consumedBytes };
8971
+ }
8972
+ function backoffMs(streak, baseMs) {
8973
+ if (streak <= 0) return 0;
8974
+ return Math.min(baseMs * 2 ** Math.min(streak - 1, 20), MAX_BACKOFF_MS);
8975
+ }
8976
+ async function loadState(path22) {
8977
+ try {
8978
+ const parsed = JSON.parse(await readFile3(path22, "utf8"));
8979
+ if (parsed && typeof parsed === "object" && Number.isInteger(parsed.byte_offset) && parsed.byte_offset >= 0) {
8980
+ return { ...parsed, byte_offset: parsed.byte_offset };
8981
+ }
8982
+ } catch {
8983
+ }
8984
+ return { byte_offset: 0, last_event_id: null, forwarded_total: 0, rejected_total: 0, rejected_event_ids: [] };
8985
+ }
8986
+ async function saveState(path22, state) {
8987
+ await mkdir2(dirname9(path22), { recursive: true });
8988
+ await writeFile3(path22, JSON.stringify(state, null, 2), "utf8");
8989
+ }
8990
+ async function readNewBytes(path22, offset, max) {
8991
+ const st = await stat2(path22);
8992
+ if (st.size <= offset) return { buf: Buffer.alloc(0), size: st.size };
8993
+ const length = Math.min(st.size - offset, max);
8994
+ const fh = await open(path22, "r");
8995
+ try {
8996
+ const buf = Buffer.alloc(length);
8997
+ const { bytesRead } = await fh.read(buf, 0, length, offset);
8998
+ return { buf: buf.subarray(0, bytesRead), size: st.size };
8999
+ } finally {
9000
+ await fh.close();
9001
+ }
9002
+ }
9003
+ function createTelemetryForwarder({
9004
+ client,
9005
+ cfg = {},
9006
+ env: env2 = process.env,
9007
+ log: log2 = () => {
9008
+ },
9009
+ runnerInstanceId = "",
9010
+ now = () => Date.now(),
9011
+ eventsPath = defaultEventsPath(env2),
9012
+ statePath: statePath2 = defaultStatePath(env2)
9013
+ } = {}) {
9014
+ const optedOut = env2.VO_MCP_EVENTS_FORWARD === "0";
9015
+ const capable = typeof client?.relayTelemetryEvents === "function";
9016
+ const intervalSec = Math.max(MIN_FORWARD_INTERVAL_SEC, Number(env2.VO_MCP_EVENTS_FORWARD_SEC) || DEFAULT_FORWARD_INTERVAL_SEC);
9017
+ const maxPerRun = Math.max(MAX_EVENTS_PER_BATCH, Number(env2.VO_MCP_EVENTS_FORWARD_MAX_PER_MIN) || DEFAULT_MAX_EVENTS_PER_MIN);
9018
+ const intervalMs = intervalSec * 1e3;
9019
+ const enabled = capable && !optedOut;
9020
+ const disabledReason = optedOut ? "VO_MCP_EVENTS_FORWARD=0" : capable ? "" : "client has no relayTelemetryEvents";
9021
+ let running = false;
9022
+ let nextRunAt = 0;
9023
+ let failStreak = 0;
9024
+ const servedOperators = Array.isArray(cfg.servedOperators) ? cfg.servedOperators.filter((v) => typeof v === "string" && v).slice(0, 100) : [];
9025
+ const source = {
9026
+ runner_id: cfg.runnerId || "unknown",
9027
+ ...runnerInstanceId ? { runner_instance_id: runnerInstanceId } : {},
9028
+ ...servedOperators.length > 0 ? { served_operator_ids: servedOperators } : {}
9029
+ };
9030
+ 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})`);
9031
+ function pauseForbidden(reason) {
9032
+ nextRunAt = now() + FORBIDDEN_PAUSE_MS;
9033
+ 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`);
9034
+ }
9035
+ function scheduleFailure(reasonMs) {
9036
+ failStreak += 1;
9037
+ const delay2 = Math.max(reasonMs || 0, backoffMs(failStreak, intervalMs));
9038
+ nextRunAt = now() + delay2;
9039
+ return delay2;
9040
+ }
9041
+ async function runOnce() {
9042
+ const receipt = { forwarded: 0, duplicate: 0, rejected: 0, malformed: 0, halted: null, batches: 0 };
9043
+ let state = await loadState(statePath2);
9044
+ let read;
9045
+ try {
9046
+ read = await readNewBytes(eventsPath, state.byte_offset, MAX_READ_BYTES_PER_RUN);
9047
+ } catch (err) {
9048
+ if (err && err.code === "ENOENT") {
9049
+ nextRunAt = now() + intervalMs;
9050
+ return { ...receipt, status: "no-file" };
9051
+ }
9052
+ log2(`telemetry forwarder read failed: ${err.message}`);
9053
+ scheduleFailure();
9054
+ return { ...receipt, status: "read-error" };
9055
+ }
9056
+ if (read.size < state.byte_offset) {
9057
+ log2(`telemetry forwarder: events file rotated/truncated (size ${read.size} < cursor ${state.byte_offset}); restarting cursor at 0`);
9058
+ state = { ...state, byte_offset: 0 };
9059
+ await saveState(statePath2, state);
9060
+ read = await readNewBytes(eventsPath, 0, MAX_READ_BYTES_PER_RUN).catch(() => ({ buf: Buffer.alloc(0), size: 0 }));
9061
+ }
9062
+ const { lines } = splitCompleteLines(read.buf);
9063
+ if (lines.length === 0) {
9064
+ nextRunAt = now() + intervalMs;
9065
+ return { ...receipt, status: "idle" };
9066
+ }
9067
+ const pending = [];
9068
+ let cursor = state.byte_offset;
9069
+ let leadingConsumed = 0;
9070
+ for (const line of lines) {
9071
+ if (pending.length >= maxPerRun) break;
9072
+ if (line.text.trim().length === 0) {
9073
+ if (pending.length === 0) leadingConsumed += line.bytes;
9074
+ else pending[pending.length - 1].bytes += line.bytes;
9075
+ continue;
9076
+ }
9077
+ try {
9078
+ pending.push({ event: JSON.parse(line.text), bytes: line.bytes });
9079
+ } catch {
9080
+ receipt.malformed += 1;
9081
+ if (pending.length === 0) leadingConsumed += line.bytes;
9082
+ else pending[pending.length - 1].bytes += line.bytes;
9083
+ }
9084
+ }
9085
+ cursor += leadingConsumed;
9086
+ if (pending.length === 0) {
9087
+ state = { ...state, byte_offset: cursor };
9088
+ await saveState(statePath2, state);
9089
+ nextRunAt = now() + intervalMs;
9090
+ return { ...receipt, status: "idle" };
9091
+ }
9092
+ let halted = false;
9093
+ let batchSize = MAX_EVENTS_PER_BATCH;
9094
+ for (let i = 0; i < pending.length && !halted; ) {
9095
+ const batch = pending.slice(i, i + batchSize);
9096
+ let res;
9097
+ try {
9098
+ res = await client.relayTelemetryEvents({ events: batch.map((p) => p.event), source });
9099
+ } catch (err) {
9100
+ receipt.halted = `network: ${err && err.message ? err.message : String(err)}`.slice(0, 200);
9101
+ halted = true;
9102
+ break;
9103
+ }
9104
+ receipt.batches += 1;
9105
+ const body = res && res.body && typeof res.body === "object" ? res.body : {};
9106
+ if (res.status === 413) {
9107
+ if (batch.length > 1) {
9108
+ batchSize = 1;
9109
+ continue;
9110
+ }
9111
+ const item = batch[0];
9112
+ cursor += item.bytes;
9113
+ i += 1;
9114
+ receipt.rejected += 1;
9115
+ const id = typeof item.event?.event_id === "string" ? item.event.event_id : `offset:${cursor - item.bytes}`;
9116
+ state = {
9117
+ ...state,
9118
+ byte_offset: cursor,
9119
+ rejected_total: (state.rejected_total || 0) + 1,
9120
+ rejected_event_ids: [...state.rejected_event_ids || [], `${id}:payload_too_large`].slice(-REJECTED_IDS_KEEP)
9121
+ };
9122
+ await saveState(statePath2, state);
9123
+ continue;
9124
+ }
9125
+ i += batch.length;
9126
+ if (res.status === 403) {
9127
+ pauseForbidden(body.error || "HTTP 403");
9128
+ receipt.halted = "forbidden";
9129
+ halted = true;
9130
+ break;
9131
+ }
9132
+ if (res.status === 503 && (body.error === "ingest_disabled" || body.error === "relay_unconfigured")) {
9133
+ const retrySec = Number(body.retry_after_sec) > 0 ? Number(body.retry_after_sec) : 600;
9134
+ receipt.halted = body.error;
9135
+ halted = true;
9136
+ scheduleFailure(retrySec * 1e3);
9137
+ break;
9138
+ }
9139
+ if (res.status !== 200 || body.ok !== true || !Array.isArray(body.results)) {
9140
+ receipt.halted = `HTTP ${res.status}${body.error ? ` ${body.error}` : ""}`;
9141
+ halted = true;
9142
+ break;
9143
+ }
9144
+ let applied = 0;
9145
+ let batchForwarded = 0;
9146
+ let batchRejected = 0;
9147
+ for (const r of body.results) {
9148
+ const item = batch[r.index];
9149
+ if (!item || r.index !== applied) break;
9150
+ if (r.status === "upstream_error") {
9151
+ receipt.halted = `upstream_error${r.error ? ` ${r.error}` : ""}`;
9152
+ halted = true;
9153
+ break;
9154
+ }
9155
+ cursor += item.bytes;
9156
+ applied += 1;
9157
+ if (r.status === "written") {
9158
+ receipt.forwarded += 1;
9159
+ batchForwarded += 1;
9160
+ } else if (r.status === "duplicate") receipt.duplicate += 1;
9161
+ else if (r.status === "rejected") {
9162
+ receipt.rejected += 1;
9163
+ batchRejected += 1;
9164
+ const id = typeof item.event?.event_id === "string" ? item.event.event_id : `offset:${cursor - item.bytes}`;
9165
+ state.rejected_event_ids = [...state.rejected_event_ids || [], `${id}:${r.error || "rejected"}`].slice(-REJECTED_IDS_KEEP);
9166
+ }
9167
+ if (typeof r.event_id === "string") state.last_event_id = r.event_id;
9168
+ }
9169
+ if (body.complete !== true && !halted) {
9170
+ receipt.halted = "incomplete-batch";
9171
+ halted = true;
9172
+ }
9173
+ if (applied < batch.length && !halted) {
9174
+ receipt.halted = "short-results";
9175
+ halted = true;
9176
+ }
9177
+ state = {
9178
+ ...state,
9179
+ byte_offset: cursor,
9180
+ forwarded_total: (state.forwarded_total || 0) + batchForwarded,
9181
+ rejected_total: (state.rejected_total || 0) + batchRejected,
9182
+ last_forward_at: new Date(now()).toISOString(),
9183
+ last_error: receipt.halted
9184
+ };
9185
+ await saveState(statePath2, state);
9186
+ }
9187
+ if (halted) {
9188
+ state = { ...state, byte_offset: cursor, last_error: receipt.halted, last_halt_at: new Date(now()).toISOString() };
9189
+ await saveState(statePath2, state).catch(() => {
9190
+ });
9191
+ }
9192
+ const totals = { forwarded: state.forwarded_total || 0, rejected: state.rejected_total || 0 };
9193
+ if (halted && receipt.halted !== "forbidden" && receipt.halted !== "ingest_disabled" && receipt.halted !== "relay_unconfigured") {
9194
+ const delay2 = scheduleFailure();
9195
+ log2(`telemetry forwarder: halted (${receipt.halted}); cursor=${cursor}B; retry in ${Math.round(delay2 / 1e3)}s`);
9196
+ } else if (!halted) {
9197
+ failStreak = 0;
9198
+ nextRunAt = now() + intervalMs;
9199
+ log2(`telemetry forwarder: relayed ${receipt.batches} batch(es); cursor=${cursor}B; totals forwarded=${totals.forwarded} rejected=${totals.rejected}${receipt.malformed ? ` malformed=${receipt.malformed}` : ""}`);
9200
+ } else {
9201
+ log2(`telemetry forwarder: paused (${receipt.halted}); cursor=${cursor}B`);
9202
+ }
9203
+ return { ...receipt, status: halted ? "halted" : "ok", cursor, totals };
9204
+ }
9205
+ return {
9206
+ get enabled() {
9207
+ return enabled;
9208
+ },
9209
+ get disabledReason() {
9210
+ return disabledReason;
9211
+ },
9212
+ intervalMs,
9213
+ /** Fire-and-forget; safe to call every daemon loop iteration. */
9214
+ tick() {
9215
+ if (!enabled || running) return void 0;
9216
+ const t = now();
9217
+ if (t < nextRunAt) return void 0;
9218
+ running = true;
9219
+ return runOnce().catch((err) => {
9220
+ log2(`telemetry forwarder tick failed: ${err && err.message ? err.message : String(err)}`);
9221
+ scheduleFailure();
9222
+ }).finally(() => {
9223
+ running = false;
9224
+ });
9225
+ },
9226
+ runOnce
9227
+ };
9228
+ }
9229
+ 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;
9230
+ var init_telemetry_forwarder = __esm({
9231
+ "../../scripts/virtual-office/code-runner/telemetry-forwarder.mjs"() {
9232
+ "use strict";
9233
+ DEFAULT_FORWARD_INTERVAL_SEC = 60;
9234
+ MIN_FORWARD_INTERVAL_SEC = 15;
9235
+ DEFAULT_MAX_EVENTS_PER_MIN = 500;
9236
+ MAX_EVENTS_PER_BATCH = 25;
9237
+ MAX_READ_BYTES_PER_RUN = 4 * 1024 * 1024;
9238
+ MAX_BACKOFF_MS = 30 * 60 * 1e3;
9239
+ REJECTED_IDS_KEEP = 100;
9240
+ FORBIDDEN_PAUSE_MS = 6 * 60 * 60 * 1e3;
9241
+ }
9242
+ });
9243
+
8908
9244
  // ../../scripts/virtual-office/code-runner/loop-ticks.mjs
8909
9245
  function makeLoopTicks({
8910
9246
  client,
@@ -8934,7 +9270,11 @@ function makeLoopTicks({
8934
9270
  getAccountUsage = () => [],
8935
9271
  // Injectable for tests; default to the real scheduler + wall clock.
8936
9272
  runResumeScheduler = runScheduler,
8937
- now: nowFn = () => Date.now()
9273
+ now: nowFn = () => Date.now(),
9274
+ // Cursor-lessons §6 (2026-08-16): forwards this machine's vo-mcp events to
9275
+ // the control-plane telemetry relay (telemetry-forwarder.mjs). Inert when the
9276
+ // client lacks relayTelemetryEvents (test fakes) or VO_MCP_EVENTS_FORWARD=0.
9277
+ telemetryForwarder = createTelemetryForwarder({ client, cfg, env: env2, log: log2, runnerInstanceId, now: nowFn })
8938
9278
  }) {
8939
9279
  let lastSessionForward = 0;
8940
9280
  let lastHeartbeat = 0;
@@ -9046,6 +9386,7 @@ function makeLoopTicks({
9046
9386
  resumeRunning = false;
9047
9387
  });
9048
9388
  }
9389
+ telemetryForwarder.tick();
9049
9390
  return Promise.all(heartbeatCompletions).then(() => void 0);
9050
9391
  };
9051
9392
  }
@@ -9055,6 +9396,7 @@ var init_loop_ticks = __esm({
9055
9396
  "use strict";
9056
9397
  init_session_spool_forwarder();
9057
9398
  init_rate_limit_resume_scheduler();
9399
+ init_telemetry_forwarder();
9058
9400
  HEARTBEAT_MS = 6e4;
9059
9401
  DEFAULT_RESUME_SCHEDULE_SEC = 300;
9060
9402
  }
@@ -10051,7 +10393,7 @@ async function scheduleCoordinationRetry({
10051
10393
  const attemptsKey = kind === "resume" ? "resumeAttempts" : "fixAttempts";
10052
10394
  entry[errorsKey] = (entry[errorsKey] || 0) + 1;
10053
10395
  entry[attemptsKey] = Math.max(0, (entry[attemptsKey] || 1) - 1);
10054
- const delay2 = Math.min(MAX_BACKOFF_MS, 3e4 * 2 ** Math.min(7, entry[errorsKey] - 1));
10396
+ const delay2 = Math.min(MAX_BACKOFF_MS2, 3e4 * 2 ** Math.min(7, entry[errorsKey] - 1));
10055
10397
  entry.nextRetryAt = now() + delay2;
10056
10398
  if (entry[errorsKey] >= 3 && !entry.coordinationEscalatedAt && typeof reportBlocker === "function") {
10057
10399
  try {
@@ -10066,12 +10408,12 @@ async function scheduleCoordinationRetry({
10066
10408
  }
10067
10409
  log2(`watch: pr #${prNumber} ${kind} coordination failed ${entry[errorsKey]}x; retry in ${Math.round(delay2 / 1e3)}s: ${boundedErrorMessage(error)}`);
10068
10410
  }
10069
- var MAX_BACKOFF_MS, MERGE_ENQUEUE_TTL_MS, TERMINAL_RESUME_REFUSALS;
10411
+ var MAX_BACKOFF_MS2, MERGE_ENQUEUE_TTL_MS, TERMINAL_RESUME_REFUSALS;
10070
10412
  var init_watcher_coordination = __esm({
10071
10413
  "../../scripts/virtual-office/code-runner/watcher-coordination.mjs"() {
10072
10414
  "use strict";
10073
10415
  init_error_message();
10074
- MAX_BACKOFF_MS = 60 * 60 * 1e3;
10416
+ MAX_BACKOFF_MS2 = 60 * 60 * 1e3;
10075
10417
  MERGE_ENQUEUE_TTL_MS = 2 * 60 * 60 * 1e3;
10076
10418
  TERMINAL_RESUME_REFUSALS = Object.freeze([
10077
10419
  "automatic_continuation_budget_too_small",
@@ -10089,12 +10431,12 @@ var init_watcher_coordination = __esm({
10089
10431
 
10090
10432
  // ../../scripts/virtual-office/code-runner/watcher-state.mjs
10091
10433
  import { randomUUID as randomUUID3 } from "node:crypto";
10092
- import { mkdir as mkdir2, open, readFile as readFile3, rename, unlink as unlink2 } from "node:fs/promises";
10093
- import { dirname as dirname9 } from "node:path";
10434
+ import { mkdir as mkdir3, open as open2, readFile as readFile4, rename, unlink as unlink2 } from "node:fs/promises";
10435
+ import { dirname as dirname10 } from "node:path";
10094
10436
  async function readWatcherState(stateFile) {
10095
10437
  let raw;
10096
10438
  try {
10097
- raw = await readFile3(stateFile, "utf8");
10439
+ raw = await readFile4(stateFile, "utf8");
10098
10440
  } catch (error) {
10099
10441
  if (error?.code === "ENOENT") return {};
10100
10442
  throw error;
@@ -10106,12 +10448,12 @@ async function readWatcherState(stateFile) {
10106
10448
  return parsed;
10107
10449
  }
10108
10450
  async function writeWatcherState(stateFile, state) {
10109
- const directory = dirname9(stateFile);
10110
- await mkdir2(directory, { recursive: true });
10451
+ const directory = dirname10(stateFile);
10452
+ await mkdir3(directory, { recursive: true });
10111
10453
  const temp = `${stateFile}.${process.pid}.${randomUUID3()}.tmp`;
10112
10454
  let handle;
10113
10455
  try {
10114
- handle = await open(temp, "wx");
10456
+ handle = await open2(temp, "wx");
10115
10457
  await handle.writeFile(JSON.stringify(state, null, 2), "utf8");
10116
10458
  await handle.sync();
10117
10459
  await handle.close();
@@ -10435,8 +10777,8 @@ var init_enqueue_autonomous_code_task = __esm({
10435
10777
  });
10436
10778
 
10437
10779
  // ../../scripts/virtual-office/code-runner/pr-watcher.mjs
10438
- import { homedir as homedir8 } from "node:os";
10439
- import { join as join13 } from "node:path";
10780
+ import { homedir as homedir9 } from "node:os";
10781
+ import { join as join14 } from "node:path";
10440
10782
  function parsePrCiStatus(view) {
10441
10783
  const state = (view && typeof view.state === "string" ? view.state : "UNKNOWN").toUpperCase();
10442
10784
  const rollup = view && Array.isArray(view.statusCheckRollup) ? view.statusCheckRollup : [];
@@ -10782,7 +11124,7 @@ var init_pr_watcher = __esm({
10782
11124
  init_watcher_state();
10783
11125
  init_superseded_pr_source();
10784
11126
  init_ci_fix_prompt();
10785
- DEFAULT_STATE_FILE = join13(homedir8(), ".vo", "dispatched-prs.json");
11127
+ DEFAULT_STATE_FILE = join14(homedir9(), ".vo", "dispatched-prs.json");
10786
11128
  FAIL_CONCLUSIONS = /* @__PURE__ */ new Set([
10787
11129
  "FAILURE",
10788
11130
  "TIMED_OUT",
@@ -12026,8 +12368,8 @@ var init_classify_task = __esm({
12026
12368
 
12027
12369
  // ../../scripts/virtual-office/code-runner/auto-router/effort-policy.mjs
12028
12370
  import { readFileSync as readFileSync9 } from "node:fs";
12029
- import { homedir as homedir9 } from "node:os";
12030
- import { join as join14 } from "node:path";
12371
+ import { homedir as homedir10 } from "node:os";
12372
+ import { join as join15 } from "node:path";
12031
12373
  function difficultyToRung(difficulty, thresholds) {
12032
12374
  const b = thresholds.rungBounds;
12033
12375
  if (difficulty >= b.R5) return "R5";
@@ -12104,7 +12446,7 @@ var init_effort_policy = __esm({
12104
12446
  init_meta_model_catalog();
12105
12447
  RUNG_ORDER = ["R1", "R2", "R3", "R4", "R5"];
12106
12448
  rungIndex = (rung) => RUNG_ORDER.indexOf(rung);
12107
- DEFAULT_CODEX_MODELS_CACHE = join14(homedir9(), ".codex", "models_cache.json");
12449
+ DEFAULT_CODEX_MODELS_CACHE = join15(homedir10(), ".codex", "models_cache.json");
12108
12450
  }
12109
12451
  });
12110
12452
 
@@ -12235,8 +12577,8 @@ var init_role_cost_shadow = __esm({
12235
12577
 
12236
12578
  // ../../scripts/virtual-office/code-runner/auto-router/auto-router.mjs
12237
12579
  import { readFileSync as readFileSync10, appendFileSync, mkdirSync as mkdirSync8 } from "node:fs";
12238
- import { homedir as homedir10 } from "node:os";
12239
- import { join as join15, dirname as dirname10 } from "node:path";
12580
+ import { homedir as homedir11 } from "node:os";
12581
+ import { join as join16, dirname as dirname11 } from "node:path";
12240
12582
  import { fileURLToPath as fileURLToPath7 } from "node:url";
12241
12583
  function getAutoRouterMode(env2 = process.env) {
12242
12584
  const raw = String(env2.VO_CODE_RUNNER_AUTO_ROUTER || "").trim().toLowerCase();
@@ -12244,8 +12586,8 @@ function getAutoRouterMode(env2 = process.env) {
12244
12586
  }
12245
12587
  function loadThresholds() {
12246
12588
  if (!cachedThresholds) {
12247
- const here = dirname10(fileURLToPath7(import.meta.url));
12248
- cachedThresholds = JSON.parse(readFileSync10(join15(here, "thresholds.json"), "utf8"));
12589
+ const here = dirname11(fileURLToPath7(import.meta.url));
12590
+ cachedThresholds = JSON.parse(readFileSync10(join16(here, "thresholds.json"), "utf8"));
12249
12591
  }
12250
12592
  return cachedThresholds;
12251
12593
  }
@@ -12311,9 +12653,9 @@ function formatDecisionReason(decision, maxLen = 480) {
12311
12653
  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
12654
  return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
12313
12655
  }
12314
- function appendDecisionFallback(decision, { path: path22 = DECISION_FALLBACK_PATH, append = appendFileSync, mkdir: mkdir4 = mkdirSync8, task, thresholds, roleCostInputs } = {}) {
12656
+ function appendDecisionFallback(decision, { path: path22 = DECISION_FALLBACK_PATH, append = appendFileSync, mkdir: mkdir5 = mkdirSync8, task, thresholds, roleCostInputs } = {}) {
12315
12657
  try {
12316
- mkdir4(dirname10(path22), { recursive: true });
12658
+ mkdir5(dirname11(path22), { recursive: true });
12317
12659
  append(path22, `${JSON.stringify(decision)}
12318
12660
  `, "utf8");
12319
12661
  if (isRouterDecision(decision)) {
@@ -12337,7 +12679,7 @@ var init_auto_router = __esm({
12337
12679
  init_effort_policy();
12338
12680
  init_role_cost_shadow();
12339
12681
  ROUTER_VERSION = "0.1.0";
12340
- DECISION_FALLBACK_PATH = join15(homedir10(), ".claude", "vo-auto-router-decisions.jsonl");
12682
+ DECISION_FALLBACK_PATH = join16(homedir11(), ".claude", "vo-auto-router-decisions.jsonl");
12341
12683
  MODES = /* @__PURE__ */ new Set(["off", "shadow", "on"]);
12342
12684
  cachedThresholds = null;
12343
12685
  isRouterDecision = (d) => Boolean(d && typeof d === "object" && typeof d.taskClass === "string" && typeof d.confidence === "number");
@@ -12737,7 +13079,7 @@ function safeBaseEnv(env2 = {}) {
12737
13079
  }
12738
13080
  return result;
12739
13081
  }
12740
- function buildAgentProcessEnv(env2, { agent = "agent", runnerId = "vo-runner", taskId = "task", githubReadToken = null, swarmAdmission = null } = {}) {
13082
+ function buildAgentProcessEnv(env2, { agent = "agent", runnerId = "vo-runner", taskId = "task", repo = null, githubReadToken = null, swarmAdmission = null } = {}) {
12741
13083
  const base = safeBaseEnv(env2);
12742
13084
  if (swarmAdmission) {
12743
13085
  for (const key of Object.keys(base)) {
@@ -12749,6 +13091,8 @@ function buildAgentProcessEnv(env2, { agent = "agent", runnerId = "vo-runner", t
12749
13091
  base.GH_TOKEN = githubReadToken;
12750
13092
  base.GITHUB_TOKEN = githubReadToken;
12751
13093
  }
13094
+ if (typeof taskId === "string" && taskId && taskId !== "task") base.VO_CODE_TASK_ID = taskId;
13095
+ if (typeof repo === "string" && /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u.test(repo)) base.VO_CODE_TASK_REPO = repo;
12752
13096
  if (String(env2?.AGENT_ID || "").trim()) return { ...base, AGENT_ID: env2.AGENT_ID };
12753
13097
  const generated = [
12754
13098
  "vo",
@@ -12818,6 +13162,12 @@ var init_agent_process_env = __esm({
12818
13162
  // NOT a credential and NOT an authorization input: it names a tier, it never
12819
13163
  // grants one, and it carries no key material (see the module's header rule).
12820
13164
  "VO_SWARM_TIER_BINDING",
13165
+ // Increased-moat staging flags (2026-08-16): a fleet machine's runner env
13166
+ // decides whether its agents' vo-mcp ALSO sends verdicts through the moat
13167
+ // (shadow) or lets the moat win (authoritative). Per-machine, default absent.
13168
+ "VO_CONSENSUS_MOAT_SHADOW",
13169
+ "VO_CONSENSUS_MOAT_SHADOW_PCT",
13170
+ "VO_CONSENSUS_MOAT_AUTHORITATIVE",
12821
13171
  // Where the swarm SPAWN LEDGER lives (SWARM_LEDGER_DIR_ENV in
12822
13172
  // packages/vo-mcp/src/swarm/spawn-ledger.ts). The ledger is the durable,
12823
13173
  // host-shared counter that bounds the TOTAL spawns under one swarm_id; the
@@ -14053,9 +14403,9 @@ function recoveryLedgerCandidates(repo, clonesRoot2) {
14053
14403
  path21.join(canonical, ".agent-worktrees", "recovery-ledger.jsonl")
14054
14404
  ];
14055
14405
  }
14056
- async function readLedger(file, readFile5) {
14406
+ async function readLedger(file, readFile6) {
14057
14407
  try {
14058
- return String(await readFile5(file, "utf8")).split(/\r?\n/).filter(Boolean).flatMap((line) => {
14408
+ return String(await readFile6(file, "utf8")).split(/\r?\n/).filter(Boolean).flatMap((line) => {
14059
14409
  try {
14060
14410
  return [JSON.parse(line)];
14061
14411
  } catch {
@@ -14068,7 +14418,7 @@ async function readLedger(file, readFile5) {
14068
14418
  }
14069
14419
  async function findPreservedRecovery(task, {
14070
14420
  clonesRoot: clonesRoot2 = process.env.VO_CODE_RUNNER_CLONES_ROOT || "",
14071
- readFile: readFile5 = fsp13.readFile,
14421
+ readFile: readFile6 = fsp13.readFile,
14072
14422
  exists = fs13.existsSync,
14073
14423
  alreadyPublished = preservedHeadAlreadyOnBranch,
14074
14424
  log: log2 = () => {
@@ -14079,7 +14429,7 @@ async function findPreservedRecovery(task, {
14079
14429
  if (!originalTaskId) return null;
14080
14430
  if (task.pr_branch && !recoveryTaskId(task.prompt)) return null;
14081
14431
  for (const ledgerPath of recoveryLedgerCandidates(task.repo, clonesRoot2)) {
14082
- const entries = await readLedger(ledgerPath, readFile5);
14432
+ const entries = await readLedger(ledgerPath, readFile6);
14083
14433
  const resolved = entries.some((entry) => RESOLVED_RECOVERY_TYPES.has(entry.type) && entry.taskId === originalTaskId);
14084
14434
  const preserved = [...entries].reverse().find((entry) => entry.taskId === originalTaskId && entry.worktreeDir);
14085
14435
  if (!resolved && preserved && exists(preserved.worktreeDir)) {
@@ -14420,9 +14770,9 @@ var init_cancellation_probe = __esm({
14420
14770
  });
14421
14771
 
14422
14772
  // ../../scripts/virtual-office/code-runner/detached-economics-spool.mjs
14423
- import { homedir as homedir11 } from "node:os";
14424
- import { dirname as dirname11, join as join16 } from "node:path";
14425
- import { mkdir as mkdir3, readFile as readFile4, rename as rename2, writeFile as writeFile3 } from "node:fs/promises";
14773
+ import { homedir as homedir12 } from "node:os";
14774
+ import { dirname as dirname12, join as join17 } from "node:path";
14775
+ import { mkdir as mkdir4, readFile as readFile5, rename as rename2, writeFile as writeFile4 } from "node:fs/promises";
14426
14776
  function withLock(operation) {
14427
14777
  const result = serialized.then(operation, operation);
14428
14778
  serialized = result.then(() => void 0, () => void 0);
@@ -14430,7 +14780,7 @@ function withLock(operation) {
14430
14780
  }
14431
14781
  async function readEntries(file) {
14432
14782
  try {
14433
- const parsed = JSON.parse(await readFile4(file, "utf8"));
14783
+ const parsed = JSON.parse(await readFile5(file, "utf8"));
14434
14784
  if (!Array.isArray(parsed)) throw new Error("detached economics spool is not an array");
14435
14785
  return parsed;
14436
14786
  } catch (error) {
@@ -14439,9 +14789,9 @@ async function readEntries(file) {
14439
14789
  }
14440
14790
  }
14441
14791
  async function writeEntries(file, entries) {
14442
- await mkdir3(dirname11(file), { recursive: true });
14792
+ await mkdir4(dirname12(file), { recursive: true });
14443
14793
  const temp = `${file}.${process.pid}.tmp`;
14444
- await writeFile3(temp, `${JSON.stringify(entries)}
14794
+ await writeFile4(temp, `${JSON.stringify(entries)}
14445
14795
  `, "utf8");
14446
14796
  await rename2(temp, file);
14447
14797
  }
@@ -14485,7 +14835,7 @@ var DEFAULT_FILE, serialized;
14485
14835
  var init_detached_economics_spool = __esm({
14486
14836
  "../../scripts/virtual-office/code-runner/detached-economics-spool.mjs"() {
14487
14837
  "use strict";
14488
- DEFAULT_FILE = join16(homedir11(), ".vo", "detached-run-economics.json");
14838
+ DEFAULT_FILE = join17(homedir12(), ".vo", "detached-run-economics.json");
14489
14839
  serialized = Promise.resolve();
14490
14840
  }
14491
14841
  });
@@ -14886,7 +15236,7 @@ async function processOneTask(client, task, cfg, runnerInstanceId, swarmAdmissio
14886
15236
  maxBudgetUsd: sel.agent === "claude" ? effectiveMaxBudgetUsd : void 0,
14887
15237
  researchHarness: methodology?.shape === "research",
14888
15238
  // Workflow grant only for research-shaped tasks
14889
- env: buildAgentProcessEnv(process.env, { agent: sel.agent, runnerId: cfg.runnerId, taskId: id, githubReadToken: agentGithubReadToken, swarmAdmission }),
15239
+ env: buildAgentProcessEnv(process.env, { agent: sel.agent, runnerId: cfg.runnerId, taskId: id, repo: task.repo, githubReadToken: agentGithubReadToken, swarmAdmission }),
14890
15240
  // swarmAdmission mints VO_SWARM_TIER_BINDING: ONE tier decision for this task's whole agent tree
14891
15241
  sandbox,
14892
15242
  onProgress: (text, checkpoint) => {