@kynver-app/runtime 0.1.99 → 0.1.102

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/dist/index.js CHANGED
@@ -119,6 +119,9 @@ function readMaybeFile(file) {
119
119
  function sleepMs(ms) {
120
120
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
121
121
  }
122
+ function sleepMsAsync(ms) {
123
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
124
+ }
122
125
  function isPidAlive(pid) {
123
126
  if (!pid) return false;
124
127
  try {
@@ -803,7 +806,6 @@ function readMemAvailableBytes(meminfoText) {
803
806
 
804
807
  // src/resource-gate.ts
805
808
  import path9 from "node:path";
806
- import { readFileSync as readFileSync9 } from "node:fs";
807
809
 
808
810
  // src/disk-gate.ts
809
811
  import { statfsSync as statfsSync2 } from "node:fs";
@@ -828,23 +830,23 @@ function isWslHost() {
828
830
  function observeWslHostDisk(options = {}) {
829
831
  const wsl = options.forceWsl === void 0 ? isWslHost() : options.forceWsl;
830
832
  if (!wsl) return null;
831
- const path71 = options.wslHostMount?.trim() || process.env.KYNVER_WSL_HOST_MOUNT?.trim() || DEFAULT_WSL_HOST_MOUNT;
833
+ const path73 = options.wslHostMount?.trim() || process.env.KYNVER_WSL_HOST_MOUNT?.trim() || DEFAULT_WSL_HOST_MOUNT;
832
834
  const warnBelowBytes = options.wslHostFreeWarnBytes ?? DEFAULT_WSL_HOST_WARN_FREE_BYTES;
833
835
  const criticalBelowBytes = options.wslHostFreeCriticalBytes ?? DEFAULT_WSL_HOST_CRITICAL_FREE_BYTES;
834
836
  const statfs = options.statfs ?? statfsSync;
835
837
  let stats;
836
838
  try {
837
- stats = statfs(path71);
839
+ stats = statfs(path73);
838
840
  } catch (error) {
839
841
  return {
840
842
  ok: false,
841
- path: path71,
843
+ path: path73,
842
844
  freeBytes: 0,
843
845
  totalBytes: 0,
844
846
  usedPercent: 100,
845
847
  warnBelowBytes,
846
848
  criticalBelowBytes,
847
- reason: `Windows host disk probe failed at ${path71}: ${error.message}`,
849
+ reason: `Windows host disk probe failed at ${path73}: ${error.message}`,
848
850
  probeError: error.message
849
851
  };
850
852
  }
@@ -858,11 +860,11 @@ function observeWslHostDisk(options = {}) {
858
860
  let reason = null;
859
861
  if (!ok) {
860
862
  const tag = criticalFree ? "critical" : "warning";
861
- reason = `Windows host disk ${path71} at ${tag}: ${freeGiB} GiB free (<${(criticalFree ? criticalBelowBytes : warnBelowBytes) / 1024 / 1024 / 1024} GiB); WSL VHDX cannot grow safely. ${summarizeWslRecoverySteps()}`;
863
+ reason = `Windows host disk ${path73} at ${tag}: ${freeGiB} GiB free (<${(criticalFree ? criticalBelowBytes : warnBelowBytes) / 1024 / 1024 / 1024} GiB); WSL VHDX cannot grow safely. ${summarizeWslRecoverySteps()}`;
862
864
  }
863
865
  return {
864
866
  ok,
865
- path: path71,
867
+ path: path73,
866
868
  freeBytes,
867
869
  totalBytes,
868
870
  usedPercent,
@@ -882,12 +884,12 @@ var DEFAULT_CRITICAL_FREE_BYTES = 15 * 1024 * 1024 * 1024;
882
884
  var DEFAULT_MAX_USED_PERCENT = 80;
883
885
  var DEFAULT_HARD_MAX_USED_PERCENT = 90;
884
886
  function observeRunnerDiskGate(input = {}) {
885
- const path71 = input.diskPath?.trim() || "/";
887
+ const path73 = input.diskPath?.trim() || "/";
886
888
  const warnBelowBytes = input.diskFreeWarnBytes ?? DEFAULT_WARN_FREE_BYTES;
887
889
  const criticalBelowBytes = input.diskFreeCriticalBytes ?? DEFAULT_CRITICAL_FREE_BYTES;
888
890
  const maxUsedPercent = input.diskMaxUsedPercent ?? DEFAULT_MAX_USED_PERCENT;
889
891
  const hardMaxUsedPercent = input.diskHardMaxUsedPercent ?? DEFAULT_HARD_MAX_USED_PERCENT;
890
- const stats = statfsSync2(path71);
892
+ const stats = statfsSync2(path73);
891
893
  const freeBytes = Number(stats.bavail) * Number(stats.bsize);
892
894
  const totalBytes = Number(stats.blocks) * Number(stats.bsize);
893
895
  const usedPercent = totalBytes > 0 ? (totalBytes - freeBytes) / totalBytes * 100 : 100;
@@ -910,7 +912,7 @@ function observeRunnerDiskGate(input = {}) {
910
912
  }
911
913
  return {
912
914
  ok,
913
- path: path71,
915
+ path: path73,
914
916
  freeBytes,
915
917
  totalBytes,
916
918
  usedPercent,
@@ -1044,6 +1046,9 @@ function listRunWorkerNames(run) {
1044
1046
  return [...names];
1045
1047
  }
1046
1048
 
1049
+ // src/harness-worker-active.ts
1050
+ import { readFileSync as readFileSync9 } from "node:fs";
1051
+
1047
1052
  // src/heartbeat.ts
1048
1053
  import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:fs";
1049
1054
 
@@ -2127,6 +2132,9 @@ function computeAttention(input) {
2127
2132
  return { state: "blocked", reason: input.completionBlocker };
2128
2133
  }
2129
2134
  if (input.finalResult) {
2135
+ if (input.localOnly && hasMergedTargetPrReconciliation(input.finalResult)) {
2136
+ return { state: "done", reason: "local-only worker superseded by merged PR" };
2137
+ }
2130
2138
  const landingSnapshot = {
2131
2139
  finalResult: input.finalResult,
2132
2140
  changedFiles: input.changedFiles ?? [],
@@ -2192,9 +2200,24 @@ function computeAttention(input) {
2192
2200
  }
2193
2201
  return { state: "ok", reason: "recent activity" };
2194
2202
  }
2203
+ function hasMergedTargetPrReconciliation(value) {
2204
+ let record3 = null;
2205
+ if (typeof value === "string") record3 = extractEmbeddedWorkerFinalResultRecord(value);
2206
+ else if (value && typeof value === "object" && !Array.isArray(value)) record3 = value;
2207
+ if (!record3) return false;
2208
+ const raw = record3.targetPrReconciliation ?? record3.target_pr_reconciliation;
2209
+ if (!Array.isArray(raw)) return false;
2210
+ return raw.some((item) => {
2211
+ if (!item || typeof item !== "object" || Array.isArray(item)) return false;
2212
+ return String(item.outcome ?? "").trim() === "merged";
2213
+ });
2214
+ }
2195
2215
  function resolveFinalResult(worker, parsedFinalResult, heartbeat) {
2196
- if (parsedFinalResult) return parsedFinalResult;
2197
2216
  const ackSnapshot = worker.completionSnapshot?.finalResult;
2217
+ if (worker.completionAckSource === "local-pr-merged-reconcile" && ackSnapshot !== void 0 && ackSnapshot !== null) {
2218
+ return ackSnapshot;
2219
+ }
2220
+ if (parsedFinalResult) return parsedFinalResult;
2198
2221
  if (ackSnapshot !== void 0 && ackSnapshot !== null) return ackSnapshot;
2199
2222
  return terminalFinalResultFromHeartbeat(heartbeat);
2200
2223
  }
@@ -2241,7 +2264,8 @@ function computeWorkerStatus(worker, options = {}) {
2241
2264
  gitAncestry,
2242
2265
  completionBlocker,
2243
2266
  landingContract,
2244
- prUrl: worker.repairTargetPrUrl ?? worker.taskPrUrl ?? null
2267
+ prUrl: worker.repairTargetPrUrl ?? worker.taskPrUrl ?? null,
2268
+ localOnly: worker.localOnly === true
2245
2269
  });
2246
2270
  const workerStatusLabel = completionBlocker || attention.state === "blocked" ? "blocked" : completionAcknowledged || attention.state === "done" ? "done" : finalResult ? "exited" : alive ? "running" : "exited";
2247
2271
  return {
@@ -2295,6 +2319,33 @@ function deriveRunStatus(fallback, workers) {
2295
2319
  return fallback;
2296
2320
  }
2297
2321
 
2322
+ // src/harness-worker-active.ts
2323
+ function pidCommandLine(pid) {
2324
+ if (!pid || process.platform !== "linux") return null;
2325
+ try {
2326
+ return readFileSync9(`/proc/${pid}/cmdline`, "utf8").replace(/\0/g, " ");
2327
+ } catch {
2328
+ return null;
2329
+ }
2330
+ }
2331
+ function workerProcessMatchesRecord(worker) {
2332
+ if (!worker.pid || process.platform !== "linux") return true;
2333
+ const cmdline = pidCommandLine(worker.pid);
2334
+ if (!cmdline) return false;
2335
+ const probes = [worker.worktreePath, worker.workerDir, worker.heartbeatPath].filter(
2336
+ (value) => typeof value === "string" && value.trim().length > 0
2337
+ );
2338
+ return probes.some((probe) => cmdline.includes(probe));
2339
+ }
2340
+ function isActiveHarnessWorker(worker) {
2341
+ if (typeof worker.completionBlocker === "string" && worker.completionBlocker.trim()) {
2342
+ return false;
2343
+ }
2344
+ const status = computeWorkerStatus(worker);
2345
+ if (status.alive && !workerProcessMatchesRecord(worker)) return false;
2346
+ return status.alive && !status.finalResult && status.attention.state !== "done";
2347
+ }
2348
+
2298
2349
  // src/resource-gate.ts
2299
2350
  var DEFAULT_PER_WORKER_MEM_BYTES = 500 * 1024 * 1024;
2300
2351
  var DEFAULT_MEM_RESERVE_BYTES = 4 * 1024 * 1024 * 1024;
@@ -2337,31 +2388,6 @@ function computeAutoMaxWorkers(totalMemBytes, opts = {}) {
2337
2388
  function readAvailableMemBytes() {
2338
2389
  return readMemAvailableBytes();
2339
2390
  }
2340
- function pidCommandLine(pid) {
2341
- if (!pid || process.platform !== "linux") return null;
2342
- try {
2343
- return readFileSync9(`/proc/${pid}/cmdline`, "utf8").replace(/\0/g, " ");
2344
- } catch {
2345
- return null;
2346
- }
2347
- }
2348
- function workerProcessMatchesRecord(worker) {
2349
- if (!worker.pid || process.platform !== "linux") return true;
2350
- const cmdline = pidCommandLine(worker.pid);
2351
- if (!cmdline) return false;
2352
- const probes = [worker.worktreePath, worker.workerDir, worker.heartbeatPath].filter(
2353
- (value) => typeof value === "string" && value.trim().length > 0
2354
- );
2355
- return probes.some((probe) => cmdline.includes(probe));
2356
- }
2357
- function isActiveHarnessWorker(worker) {
2358
- if (typeof worker.completionBlocker === "string" && worker.completionBlocker.trim()) {
2359
- return false;
2360
- }
2361
- const status = computeWorkerStatus(worker);
2362
- if (status.alive && !workerProcessMatchesRecord(worker)) return false;
2363
- return status.alive && !status.finalResult && status.attention.state !== "done";
2364
- }
2365
2391
  function countActiveWorkersForRun(run) {
2366
2392
  let active = 0;
2367
2393
  for (const name of listRunWorkerNames(run)) {
@@ -5408,8 +5434,8 @@ function dirtyPathsCoveredByDisposableRemoval(changedFiles, removed) {
5408
5434
  if (removed.length === 0) return false;
5409
5435
  const removedSet = new Set(removed.map((p) => normalizeRelativePath(p)));
5410
5436
  return material.every((line) => {
5411
- const path71 = normalizeRelativePath(pathFromGitStatusLine(line));
5412
- return removedSet.has(path71);
5437
+ const path73 = normalizeRelativePath(pathFromGitStatusLine(line));
5438
+ return removedSet.has(path73);
5413
5439
  });
5414
5440
  }
5415
5441
 
@@ -6717,7 +6743,7 @@ function collectRunActiveHarnessWorkers(runId) {
6717
6743
  path24.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
6718
6744
  void 0
6719
6745
  );
6720
- if (!worker?.taskId || !workerProcessMatchesRecord(worker)) continue;
6746
+ if (!worker?.taskId || !isActiveHarnessWorker(worker)) continue;
6721
6747
  out.push({
6722
6748
  runId: run.id,
6723
6749
  workerName: name,
@@ -7578,7 +7604,7 @@ async function dispatchRun(args) {
7578
7604
  );
7579
7605
  }
7580
7606
  const attempt = Number(task.attempt) || 1;
7581
- if (attempt >= retryLimits.maxTaskAttempts) {
7607
+ if (attempt > retryLimits.maxTaskAttempts) {
7582
7608
  return abortClaimedSpawn(
7583
7609
  task,
7584
7610
  `task attempt ${attempt} exceeds KYNVER_MAX_TASK_ATTEMPTS (${retryLimits.maxTaskAttempts})`
@@ -7714,7 +7740,12 @@ async function dispatchRun(args) {
7714
7740
  const admissionExhaustion = readAdmissionExhaustion(result);
7715
7741
  const capacityIdle = admissionExhaustion?.capacityIdle === true || startedCount === 0 && Number(result.resourceGate?.slotsAvailable) > 0;
7716
7742
  if (capacityIdle && admissionExhaustion?.summary) {
7717
- console.error(`[dispatch] ${admissionExhaustion.summary}`);
7743
+ const retryCeiling = admissionExhaustion.skipReasonCounts?.retry_ceiling_exceeded ?? 0;
7744
+ const recovery = admissionExhaustion.overAttemptIdleRecovery;
7745
+ const recoveryNote = recovery?.attempted === true ? `; over_attempt_recovery minted=${recovery.minted ?? 0} started=${recovery.started ?? 0}` : retryCeiling > 0 ? "; over_attempt_recovery not attempted" : "";
7746
+ console.error(
7747
+ `[dispatch] ${admissionExhaustion.summary}${retryCeiling > 0 ? `; retry_ceiling_exceeded=${retryCeiling}` : ""}${recoveryNote}`
7748
+ );
7718
7749
  }
7719
7750
  const summary = {
7720
7751
  runId: run.id,
@@ -8299,14 +8330,14 @@ function applyProductionDatabaseToProcess(options = {}) {
8299
8330
 
8300
8331
  // src/worktree.ts
8301
8332
  import { existsSync as existsSync29, mkdirSync as mkdirSync6 } from "node:fs";
8302
- import path37 from "node:path";
8333
+ import path38 from "node:path";
8303
8334
 
8304
8335
  // src/run-list.ts
8305
8336
  import { existsSync as existsSync28, readFileSync as readFileSync15 } from "node:fs";
8306
- import path36 from "node:path";
8337
+ import path37 from "node:path";
8307
8338
 
8308
8339
  // src/stale-reconcile.ts
8309
- import path35 from "node:path";
8340
+ import path36 from "node:path";
8310
8341
 
8311
8342
  // src/finalize.ts
8312
8343
  import path30 from "node:path";
@@ -8941,6 +8972,193 @@ function reconcileWorkerMetadataCli() {
8941
8972
  );
8942
8973
  }
8943
8974
 
8975
+ // src/local-pr-attention-reconcile.ts
8976
+ import { execFileSync as execFileSync2 } from "node:child_process";
8977
+ import path35 from "node:path";
8978
+ function normalizePrUrl3(url) {
8979
+ const m = url.trim().match(/github\.com\/([^/]+\/[^/]+)\/(?:pull|pulls)\/(\d+)/i);
8980
+ if (!m) return null;
8981
+ return `https://github.com/${m[1]}/pull/${m[2]}`;
8982
+ }
8983
+ function prNumberFromUrl(url) {
8984
+ const m = normalizePrUrl3(url)?.match(/\/pull\/(\d+)$/);
8985
+ if (!m) return null;
8986
+ const n = Number.parseInt(m[1], 10);
8987
+ return Number.isFinite(n) ? n : null;
8988
+ }
8989
+ function extractText(value) {
8990
+ if (value == null) return "";
8991
+ if (typeof value === "string") return value;
8992
+ try {
8993
+ return JSON.stringify(value);
8994
+ } catch {
8995
+ return "";
8996
+ }
8997
+ }
8998
+ function extractPrNumbersFromText(text) {
8999
+ const out = /* @__PURE__ */ new Set();
9000
+ for (const match of text.matchAll(/github\.com\/[^/\s)>"']+\/[^/\s)>"']+\/(?:pull|pulls)\/(\d+)/gi)) {
9001
+ const n = Number.parseInt(match[1], 10);
9002
+ if (Number.isFinite(n)) out.add(n);
9003
+ }
9004
+ for (const match of text.matchAll(/\bPR\s*#?\s*(\d{2,})\b/gi)) {
9005
+ const n = Number.parseInt(match[1], 10);
9006
+ if (Number.isFinite(n)) out.add(n);
9007
+ }
9008
+ for (const match of text.matchAll(/\bpr[-_]?(\d{2,})\b/gi)) {
9009
+ const n = Number.parseInt(match[1], 10);
9010
+ if (Number.isFinite(n)) out.add(n);
9011
+ }
9012
+ return [...out];
9013
+ }
9014
+ function extractTargetPrReconciliation(value) {
9015
+ let record3 = null;
9016
+ if (typeof value === "string") record3 = extractEmbeddedWorkerFinalResultRecord(value);
9017
+ else if (value && typeof value === "object" && !Array.isArray(value)) record3 = value;
9018
+ if (!record3) return [];
9019
+ const raw = record3.targetPrReconciliation ?? record3.target_pr_reconciliation;
9020
+ if (!Array.isArray(raw)) return [];
9021
+ const out = [];
9022
+ for (const item of raw) {
9023
+ if (!item || typeof item !== "object" || Array.isArray(item)) continue;
9024
+ const row = item;
9025
+ const prUrl = normalizePrUrl3(String(row.prUrl ?? row.pr_url ?? ""));
9026
+ const outcome = String(row.outcome ?? "").trim();
9027
+ if (!prUrl || outcome !== "merged") continue;
9028
+ out.push({
9029
+ prUrl,
9030
+ mergeCommit: typeof row.mergeCommit === "string" ? row.mergeCommit : typeof row.merge_commit === "string" ? row.merge_commit : null,
9031
+ reason: typeof row.reason === "string" ? row.reason : null
9032
+ });
9033
+ }
9034
+ return out;
9035
+ }
9036
+ function defaultLookupPr(input) {
9037
+ try {
9038
+ const repo = execFileSync2("git", ["config", "--get", "remote.origin.url"], {
9039
+ cwd: input.repoDir,
9040
+ encoding: "utf8",
9041
+ stdio: ["ignore", "pipe", "ignore"]
9042
+ }).trim();
9043
+ const repoMatch = repo.match(/github\.com[:/]([^/]+\/[^/.]+)(?:\.git)?$/i);
9044
+ const repoSlug = repoMatch?.[1];
9045
+ if (!repoSlug) return null;
9046
+ const raw = execFileSync2(
9047
+ "gh",
9048
+ ["pr", "view", String(input.prNumber), "--repo", repoSlug, "--json", "state,mergedAt,mergeCommit,url"],
9049
+ { cwd: input.repoDir, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
9050
+ );
9051
+ const parsed = JSON.parse(raw);
9052
+ return {
9053
+ prUrl: normalizePrUrl3(parsed.url ?? `https://github.com/${repoSlug}/pull/${input.prNumber}`) ?? `https://github.com/${repoSlug}/pull/${input.prNumber}`,
9054
+ state: parsed.state ?? "",
9055
+ mergedAt: parsed.mergedAt ?? null,
9056
+ mergeCommit: parsed.mergeCommit?.oid ?? null
9057
+ };
9058
+ } catch {
9059
+ return null;
9060
+ }
9061
+ }
9062
+ function finalResultForWorker(worker) {
9063
+ const heartbeat = parseHeartbeat(worker.heartbeatPath);
9064
+ return worker.completionSnapshot?.finalResult ?? terminalFinalResultFromHeartbeat(heartbeat);
9065
+ }
9066
+ function collectMergedEvidenceByPr(run) {
9067
+ const byPr = /* @__PURE__ */ new Map();
9068
+ const runDir2 = runDirectory(run.id);
9069
+ for (const name of listRunWorkerNames(run)) {
9070
+ const worker = readJson(
9071
+ path35.join(runDir2, "workers", safeSlug(name), "worker.json"),
9072
+ void 0
9073
+ );
9074
+ if (!worker) continue;
9075
+ for (const evidence of extractTargetPrReconciliation(finalResultForWorker(worker))) {
9076
+ const n = prNumberFromUrl(evidence.prUrl);
9077
+ if (n != null && !byPr.has(n)) byPr.set(n, evidence);
9078
+ }
9079
+ }
9080
+ return byPr;
9081
+ }
9082
+ function candidatePrNumbers(worker, statusFinalResult) {
9083
+ const heartbeat = parseHeartbeat(worker.heartbeatPath);
9084
+ const text = [
9085
+ worker.name,
9086
+ worker.repairTargetPrUrl,
9087
+ worker.taskPrUrl,
9088
+ worker.branch,
9089
+ heartbeat.lastHeartbeatSummary,
9090
+ extractText(statusFinalResult)
9091
+ ].filter(Boolean).join("\n");
9092
+ return extractPrNumbersFromText(text);
9093
+ }
9094
+ function isLocalOnlyAttentionNoise(worker) {
9095
+ return worker.localOnly === true && !worker.taskId && !worker.agentOsId;
9096
+ }
9097
+ function reconcileLocalOnlyMergedPrAttention(options = {}) {
9098
+ const lookupPr = options.lookupPr ?? defaultLookupPr;
9099
+ const outcomes = [];
9100
+ const liveLookupCache = /* @__PURE__ */ new Map();
9101
+ for (const run of listRunRecords()) {
9102
+ const mergedByPr = collectMergedEvidenceByPr(run);
9103
+ const runDir2 = runDirectory(run.id);
9104
+ for (const name of listRunWorkerNames(run)) {
9105
+ const workerPath = path35.join(runDir2, "workers", safeSlug(name), "worker.json");
9106
+ const worker = readJson(workerPath, void 0);
9107
+ if (!worker || !isLocalOnlyAttentionNoise(worker)) continue;
9108
+ const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
9109
+ if (status.attention.state !== "needs_attention") continue;
9110
+ let merged = null;
9111
+ for (const prNumber of candidatePrNumbers(worker, status.finalResult)) {
9112
+ merged = mergedByPr.get(prNumber) ?? null;
9113
+ if (!merged) {
9114
+ const repoDir = run.repo || worker.worktreePath;
9115
+ const cacheKey = `${repoDir}#${prNumber}`;
9116
+ const live = liveLookupCache.has(cacheKey) ? liveLookupCache.get(cacheKey) ?? null : lookupPr({ repoDir, prNumber });
9117
+ if (!liveLookupCache.has(cacheKey)) liveLookupCache.set(cacheKey, live);
9118
+ if (live && (live.state === "MERGED" || live.mergedAt)) {
9119
+ merged = { prUrl: live.prUrl, mergeCommit: live.mergeCommit ?? null, reason: "GitHub reports PR merged" };
9120
+ }
9121
+ }
9122
+ if (merged) break;
9123
+ }
9124
+ if (!merged) {
9125
+ outcomes.push({ runId: run.id, worker: name, action: "skipped", reason: "no merged PR evidence" });
9126
+ continue;
9127
+ }
9128
+ const now = (/* @__PURE__ */ new Date()).toISOString();
9129
+ worker.status = "done";
9130
+ worker.completionSnapshot = {
9131
+ prUrl: merged.prUrl,
9132
+ summary: `Local-only worker superseded by merged PR ${merged.prUrl}`,
9133
+ finalResult: {
9134
+ summary: `Local-only repair/salvage worker superseded by merged PR ${merged.prUrl}`,
9135
+ targetPrReconciliation: [
9136
+ {
9137
+ prUrl: merged.prUrl,
9138
+ outcome: "merged",
9139
+ mergeCommit: merged.mergeCommit ?? null,
9140
+ reason: merged.reason ?? "PR already merged; local-only worker no longer requires attention"
9141
+ }
9142
+ ]
9143
+ }
9144
+ };
9145
+ worker.completionAckSource = "local-pr-merged-reconcile";
9146
+ worker.reconciledAt = now;
9147
+ worker.reconcileReason = "local-only needs_attention superseded by merged PR";
9148
+ saveWorker(run.id, worker);
9149
+ outcomes.push({
9150
+ runId: run.id,
9151
+ worker: name,
9152
+ action: "marked_done",
9153
+ reason: worker.reconcileReason,
9154
+ prUrl: merged.prUrl,
9155
+ mergeCommit: merged.mergeCommit ?? null
9156
+ });
9157
+ }
9158
+ }
9159
+ return { workers: outcomes };
9160
+ }
9161
+
8944
9162
  // src/stale-reconcile.ts
8945
9163
  var STALE_RECONCILE_HEARTBEAT_MS = 15 * 60 * 1e3;
8946
9164
  function staleReconcileDisabled() {
@@ -8949,13 +9167,14 @@ function staleReconcileDisabled() {
8949
9167
  function reconcileStaleWorkers() {
8950
9168
  const metadataReconcile = reconcileWorkerMetadata();
8951
9169
  if (staleReconcileDisabled()) {
8952
- return { workers: [], finalizedRuns: finalizeStaleRuns(), metadataReconcile };
9170
+ const localPrAttentionReconcile2 = reconcileLocalOnlyMergedPrAttention();
9171
+ return { workers: [], finalizedRuns: finalizeStaleRuns(), metadataReconcile, localPrAttentionReconcile: localPrAttentionReconcile2 };
8953
9172
  }
8954
9173
  const outcomes = [];
8955
9174
  const now = Date.now();
8956
9175
  for (const run of listRunRecords()) {
8957
9176
  for (const name of listRunWorkerNames(run)) {
8958
- const workerPath = path35.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
9177
+ const workerPath = path36.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
8959
9178
  const worker = readJson(workerPath, void 0);
8960
9179
  if (!worker || worker.status !== "running") {
8961
9180
  outcomes.push({
@@ -9027,7 +9246,8 @@ function reconcileStaleWorkers() {
9027
9246
  });
9028
9247
  }
9029
9248
  }
9030
- return { workers: outcomes, finalizedRuns: finalizeStaleRuns(), metadataReconcile };
9249
+ const localPrAttentionReconcile = reconcileLocalOnlyMergedPrAttention();
9250
+ return { workers: outcomes, finalizedRuns: finalizeStaleRuns(), metadataReconcile, localPrAttentionReconcile };
9031
9251
  }
9032
9252
  function reconcileRunsCli() {
9033
9253
  const result = reconcileStaleWorkers();
@@ -9038,6 +9258,10 @@ function reconcileRunsCli() {
9038
9258
  acc[row.action] = (acc[row.action] ?? 0) + 1;
9039
9259
  return acc;
9040
9260
  }, {});
9261
+ const localPrAttentionTotals = result.localPrAttentionReconcile.workers.reduce((acc, row) => {
9262
+ acc[row.action] = (acc[row.action] ?? 0) + 1;
9263
+ return acc;
9264
+ }, {});
9041
9265
  const runRetentionTotals = result.metadataReconcile.runMetadataRetention.runs.reduce((acc, row) => {
9042
9266
  acc[row.action] = (acc[row.action] ?? 0) + 1;
9043
9267
  return acc;
@@ -9055,10 +9279,15 @@ function reconcileRunsCli() {
9055
9279
  total: result.metadataReconcile.runMetadataRetention.runs.length
9056
9280
  }
9057
9281
  },
9282
+ localPrAttentionReconcile: {
9283
+ totals: localPrAttentionTotals,
9284
+ total: result.localPrAttentionReconcile.workers.length
9285
+ },
9058
9286
  finalizedRuns: result.finalizedRuns.length,
9059
9287
  details: {
9060
9288
  workers: result.workers,
9061
9289
  metadataReconcile: result.metadataReconcile.workers,
9290
+ localPrAttentionReconcile: result.localPrAttentionReconcile.workers,
9062
9291
  runMetadataRetention: result.metadataReconcile.runMetadataRetention.runs,
9063
9292
  finalizedRuns: result.finalizedRuns
9064
9293
  }
@@ -9079,7 +9308,7 @@ function heartbeatByteLength(heartbeatPath) {
9079
9308
  }
9080
9309
  }
9081
9310
  function workerEvidence(run, workerName) {
9082
- const workerPath = path36.join(runDirectory(run.id), "workers", safeSlug(workerName), "worker.json");
9311
+ const workerPath = path37.join(runDirectory(run.id), "workers", safeSlug(workerName), "worker.json");
9083
9312
  const worker = readJson(workerPath, void 0);
9084
9313
  if (!worker) {
9085
9314
  return {
@@ -9136,7 +9365,7 @@ function aggregateRunAttention(workers) {
9136
9365
  function countOpenWorkers(run) {
9137
9366
  let open = 0;
9138
9367
  for (const name of listRunWorkerNames(run)) {
9139
- const workerPath = path36.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
9368
+ const workerPath = path37.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
9140
9369
  const worker = readJson(workerPath, void 0);
9141
9370
  if (!worker) continue;
9142
9371
  const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
@@ -9212,7 +9441,7 @@ function createRun(args) {
9212
9441
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
9213
9442
  workers: {}
9214
9443
  };
9215
- writeJson(path37.join(dir, "run.json"), run);
9444
+ writeJson(path38.join(dir, "run.json"), run);
9216
9445
  console.log(JSON.stringify({ runId: id, runDir: dir, repo, base, baseCommit }, null, 2));
9217
9446
  }
9218
9447
  function listRuns() {
@@ -9223,8 +9452,32 @@ function failExists(message) {
9223
9452
  process.exit(1);
9224
9453
  }
9225
9454
 
9455
+ // src/run-resolve.ts
9456
+ function resolveHarnessRunByName(runName) {
9457
+ const name = runName.trim();
9458
+ if (!name) return null;
9459
+ const rows = buildRunListRows();
9460
+ return rows.find((row) => row.name === name && row.status !== "completed") ?? null;
9461
+ }
9462
+ function resolveHarnessRunCli(args) {
9463
+ const name = String(required(String(args.name || ""), "--name"));
9464
+ const hit = resolveHarnessRunByName(name);
9465
+ console.log(
9466
+ JSON.stringify(
9467
+ {
9468
+ runId: hit?.id ?? null,
9469
+ name,
9470
+ status: hit?.status ?? null,
9471
+ effectiveStatus: hit?.effectiveStatus ?? null
9472
+ },
9473
+ null,
9474
+ 2
9475
+ )
9476
+ );
9477
+ }
9478
+
9226
9479
  // src/sweep.ts
9227
- import path38 from "node:path";
9480
+ import path39 from "node:path";
9228
9481
  async function sweepRun(args) {
9229
9482
  const pipeline = args.pipeline === true || args.pipeline === "true";
9230
9483
  try {
@@ -9237,7 +9490,7 @@ async function sweepRun(args) {
9237
9490
  const releasedLocalOrphans = [];
9238
9491
  for (const name of Object.keys(run.workers || {})) {
9239
9492
  const worker = readJson(
9240
- path38.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
9493
+ path39.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
9241
9494
  void 0
9242
9495
  );
9243
9496
  if (!worker || !worker.dispatched || !worker.taskId) continue;
@@ -9286,17 +9539,17 @@ async function sweepRun(args) {
9286
9539
 
9287
9540
  // src/harness-storage-snapshot.ts
9288
9541
  import { existsSync as existsSync31, readdirSync as readdirSync9, statSync as statSync7 } from "node:fs";
9289
- import path40 from "node:path";
9542
+ import path41 from "node:path";
9290
9543
 
9291
9544
  // src/cleanup-dir-size.ts
9292
- import { execFileSync as execFileSync2 } from "node:child_process";
9545
+ import { execFileSync as execFileSync3 } from "node:child_process";
9293
9546
  import { existsSync as existsSync30, readdirSync as readdirSync8, statSync as statSync6 } from "node:fs";
9294
- import path39 from "node:path";
9547
+ import path40 from "node:path";
9295
9548
  var DEFAULT_DU_TIMEOUT_MS = 2500;
9296
9549
  function directorySizeBytesDu(root, timeoutMs = DEFAULT_DU_TIMEOUT_MS) {
9297
9550
  if (!existsSync30(root)) return 0;
9298
9551
  try {
9299
- const out = execFileSync2("du", ["-sb", root], {
9552
+ const out = execFileSync3("du", ["-sb", root], {
9300
9553
  encoding: "utf8",
9301
9554
  timeout: timeoutMs,
9302
9555
  stdio: ["ignore", "pipe", "ignore"]
@@ -9325,7 +9578,7 @@ function directorySizeBytes(root, maxEntries = 5e4) {
9325
9578
  }
9326
9579
  for (const name of entries) {
9327
9580
  if (seen++ > maxEntries) return null;
9328
- const full = path39.join(current, name);
9581
+ const full = path40.join(current, name);
9329
9582
  let st;
9330
9583
  try {
9331
9584
  st = statSync6(full);
@@ -9377,7 +9630,7 @@ function harnessStorageSnapshot(opts = {}) {
9377
9630
  for (const runEntry of entries) {
9378
9631
  if (!runEntry.isDirectory()) continue;
9379
9632
  runCount += 1;
9380
- const runPath = path40.join(worktreesDir, runEntry.name);
9633
+ const runPath = path41.join(worktreesDir, runEntry.name);
9381
9634
  try {
9382
9635
  const st = statSync7(runPath);
9383
9636
  oldestMs = oldestMs === null ? st.mtimeMs : Math.min(oldestMs, st.mtimeMs);
@@ -9412,10 +9665,10 @@ function harnessStorageSnapshot(opts = {}) {
9412
9665
  }
9413
9666
 
9414
9667
  // src/cleanup.ts
9415
- import path52 from "node:path";
9668
+ import path53 from "node:path";
9416
9669
 
9417
9670
  // src/cleanup-guards.ts
9418
- import path41 from "node:path";
9671
+ import path42 from "node:path";
9419
9672
 
9420
9673
  // src/cleanup-build-cache-paths.ts
9421
9674
  var HARNESS_BUILD_CACHE_RELATIVE_PATHS = [
@@ -9556,7 +9809,7 @@ function skipWorktreeRemoval(input) {
9556
9809
  function skipDependencyCacheRemoval(input) {
9557
9810
  const { indexed, nodeModulesAgeMs, ageMs, worktreePath, activeWorktreePaths, diskPressure } = input;
9558
9811
  if (!diskPressure && ageMs < nodeModulesAgeMs) return "below_age_threshold";
9559
- if (activeWorktreePaths.has(path41.resolve(worktreePath))) return "active_worker";
9812
+ if (activeWorktreePaths.has(path42.resolve(worktreePath))) return "active_worker";
9560
9813
  if (indexed && isWorkerProcessLive(indexed)) return "active_worker";
9561
9814
  if (indexed && indexedWorktreeHasMaterialChanges(indexed)) return "dirty_worktree";
9562
9815
  return null;
@@ -9583,11 +9836,11 @@ var LIVE_SKIP_REASONS = /* @__PURE__ */ new Set([
9583
9836
  function collectPreservedLivePaths(actions, skips) {
9584
9837
  const out = [];
9585
9838
  const seen = /* @__PURE__ */ new Set();
9586
- const push = (path71, reason, detail) => {
9587
- const key = `${path71}\0${reason}`;
9839
+ const push = (path73, reason, detail) => {
9840
+ const key = `${path73}\0${reason}`;
9588
9841
  if (seen.has(key) || out.length >= MAX_PRESERVED_LIVE_PATH_SAMPLES) return;
9589
9842
  seen.add(key);
9590
- out.push({ path: path71, reason, ...detail ? { detail } : {} });
9843
+ out.push({ path: path73, reason, ...detail ? { detail } : {} });
9591
9844
  };
9592
9845
  for (const skip2 of skips) {
9593
9846
  if (!LIVE_SKIP_REASONS.has(skip2.reason)) continue;
@@ -9603,11 +9856,11 @@ function collectPreservedLivePaths(actions, skips) {
9603
9856
 
9604
9857
  // src/cleanup-run-directory.ts
9605
9858
  import { existsSync as existsSync33, readdirSync as readdirSync11, statSync as statSync9 } from "node:fs";
9606
- import path43 from "node:path";
9859
+ import path44 from "node:path";
9607
9860
 
9608
9861
  // src/cleanup-active-worktrees.ts
9609
9862
  import { existsSync as existsSync32, readdirSync as readdirSync10, statSync as statSync8 } from "node:fs";
9610
- import path42 from "node:path";
9863
+ import path43 from "node:path";
9611
9864
  function workerHasRecentHarnessActivity(worker, now) {
9612
9865
  const paths = [worker.heartbeatPath, worker.stdoutPath, worker.stderrPath];
9613
9866
  for (const target of paths) {
@@ -9633,11 +9886,11 @@ function collectActiveWorktreeGuards(harnessRoots, now = Date.now()) {
9633
9886
  let runHasLive = false;
9634
9887
  for (const name of Object.keys(run.workers || {})) {
9635
9888
  const worker = readJson(
9636
- path42.join(runDirectoryAt(harnessRoot, run.id), "workers", safeSlug(name), "worker.json"),
9889
+ path43.join(runDirectoryAt(harnessRoot, run.id), "workers", safeSlug(name), "worker.json"),
9637
9890
  void 0
9638
9891
  );
9639
9892
  if (!worker?.worktreePath) continue;
9640
- const worktreePath = path42.resolve(worker.worktreePath);
9893
+ const worktreePath = path43.resolve(worker.worktreePath);
9641
9894
  if (!isActiveHarnessWorker2(worker, now)) continue;
9642
9895
  runHasLive = true;
9643
9896
  activeWorktreePaths.add(worktreePath);
@@ -9665,7 +9918,7 @@ function pathAgeMs(target, now) {
9665
9918
  }
9666
9919
  }
9667
9920
  function loadRunStatus(harnessRoot, runId) {
9668
- const runPath = path43.join(harnessRoot, "runs", runId, "run.json");
9921
+ const runPath = path44.join(harnessRoot, "runs", runId, "run.json");
9669
9922
  if (!existsSync33(runPath)) return null;
9670
9923
  return readJson(runPath, null);
9671
9924
  }
@@ -9701,7 +9954,7 @@ function scanStaleRunDirectoryCandidates(opts) {
9701
9954
  if (!runEntry.isDirectory()) continue;
9702
9955
  const runId = runEntry.name;
9703
9956
  if (opts.runIdFilter && runId !== opts.runIdFilter) continue;
9704
- const runPath = path43.join(opts.worktreesDir, runId);
9957
+ const runPath = path44.join(opts.worktreesDir, runId);
9705
9958
  if (!runDirectoryIsEmpty(runPath)) continue;
9706
9959
  candidates.push({
9707
9960
  kind: "remove_run_directory",
@@ -9755,20 +10008,20 @@ function pathHasForeignOwnedEntry(targetPath, maxEntries = 32) {
9755
10008
 
9756
10009
  // src/cleanup-privileged-remove.ts
9757
10010
  import { spawnSync as spawnSync5 } from "node:child_process";
9758
- import path45 from "node:path";
10011
+ import path46 from "node:path";
9759
10012
 
9760
10013
  // src/cleanup-harness-path-validate.ts
9761
- import path44 from "node:path";
10014
+ import path45 from "node:path";
9762
10015
  function isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, cacheDirName) {
9763
- const resolved = path44.resolve(targetPath);
9764
- const suffix = `${path44.sep}${cacheDirName}`;
10016
+ const resolved = path45.resolve(targetPath);
10017
+ const suffix = `${path45.sep}${cacheDirName}`;
9765
10018
  const cachePath = resolved.endsWith(suffix) ? resolved : null;
9766
10019
  if (!cachePath) return "path_outside_harness";
9767
- const rel = path44.relative(worktreesDir, cachePath);
9768
- if (rel.startsWith("..") || path44.isAbsolute(rel)) return "path_outside_harness";
9769
- const parts = rel.split(path44.sep);
10020
+ const rel = path45.relative(worktreesDir, cachePath);
10021
+ if (rel.startsWith("..") || path45.isAbsolute(rel)) return "path_outside_harness";
10022
+ const parts = rel.split(path45.sep);
9770
10023
  if (parts.length < 3 || parts[parts.length - 1] !== cacheDirName) return "path_outside_harness";
9771
- if (!resolved.startsWith(path44.resolve(harnessRoot))) return "path_outside_harness";
10024
+ if (!resolved.startsWith(path45.resolve(harnessRoot))) return "path_outside_harness";
9772
10025
  return null;
9773
10026
  }
9774
10027
  function isHarnessNodeModulesPath(targetPath, harnessRoot, worktreesDir) {
@@ -9778,16 +10031,16 @@ function isHarnessNextCachePath(targetPath, harnessRoot, worktreesDir) {
9778
10031
  return isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, ".next");
9779
10032
  }
9780
10033
  function isHarnessBuildCachePath(targetPath, harnessRoot, worktreesDir) {
9781
- const resolved = path44.resolve(targetPath);
9782
- const relToWt = path44.relative(worktreesDir, resolved);
9783
- if (relToWt.startsWith("..") || path44.isAbsolute(relToWt)) return "path_outside_harness";
9784
- const parts = relToWt.split(path44.sep);
10034
+ const resolved = path45.resolve(targetPath);
10035
+ const relToWt = path45.relative(worktreesDir, resolved);
10036
+ if (relToWt.startsWith("..") || path45.isAbsolute(relToWt)) return "path_outside_harness";
10037
+ const parts = relToWt.split(path45.sep);
9785
10038
  if (parts.length < 3) return "path_outside_harness";
9786
- if (!resolved.startsWith(path44.resolve(harnessRoot))) return "path_outside_harness";
10039
+ if (!resolved.startsWith(path45.resolve(harnessRoot))) return "path_outside_harness";
9787
10040
  return null;
9788
10041
  }
9789
10042
  function isHarnessGeneratedCachePath(targetPath, harnessRoot, worktreesDir) {
9790
- const resolved = path44.resolve(targetPath);
10043
+ const resolved = path45.resolve(targetPath);
9791
10044
  return isHarnessNodeModulesPath(resolved, harnessRoot, worktreesDir) === null || isHarnessNextCachePath(resolved, harnessRoot, worktreesDir) === null || isHarnessBuildCachePath(resolved, harnessRoot, worktreesDir) === null;
9792
10045
  }
9793
10046
 
@@ -9821,12 +10074,12 @@ function tryPrivilegedReclaimHarnessCache(targetPath, harnessRoot, worktreesDir)
9821
10074
  "chown",
9822
10075
  "-R",
9823
10076
  `${effectiveUid}:${effectiveGid}`,
9824
- path45.resolve(targetPath)
10077
+ path46.resolve(targetPath)
9825
10078
  ]);
9826
10079
  if (chown.ok) {
9827
10080
  return { ok: true, method: "chown_then_rm" };
9828
10081
  }
9829
- const rm = runSudoNonInteractive(["rm", "-rf", path45.resolve(targetPath)]);
10082
+ const rm = runSudoNonInteractive(["rm", "-rf", path46.resolve(targetPath)]);
9830
10083
  if (rm.ok) {
9831
10084
  return { ok: true, method: "sudo_rm" };
9832
10085
  }
@@ -10028,7 +10281,7 @@ function removeWorktree(candidate, execute) {
10028
10281
 
10029
10282
  // src/cleanup-scan.ts
10030
10283
  import { existsSync as existsSync36, readdirSync as readdirSync13, statSync as statSync10 } from "node:fs";
10031
- import path46 from "node:path";
10284
+ import path47 from "node:path";
10032
10285
  function pathAgeMs2(target, now) {
10033
10286
  try {
10034
10287
  const mtime = statSync10(target).mtimeMs;
@@ -10038,16 +10291,16 @@ function pathAgeMs2(target, now) {
10038
10291
  }
10039
10292
  }
10040
10293
  function isPathInside(child, parent) {
10041
- const rel = path46.relative(parent, child);
10042
- return rel === "" || !rel.startsWith("..") && !path46.isAbsolute(rel);
10294
+ const rel = path47.relative(parent, child);
10295
+ return rel === "" || !rel.startsWith("..") && !path47.isAbsolute(rel);
10043
10296
  }
10044
10297
  function collectBuildCacheForWorktree(worktreePath, opts, seen, meta) {
10045
10298
  const out = [];
10046
10299
  for (const rel of HARNESS_BUILD_CACHE_RELATIVE_PATHS) {
10047
10300
  if (rel === ".next") continue;
10048
- const target = path46.join(worktreePath, rel);
10301
+ const target = path47.join(worktreePath, rel);
10049
10302
  if (!existsSync36(target)) continue;
10050
- const resolved = path46.resolve(target);
10303
+ const resolved = path47.resolve(target);
10051
10304
  if (seen.has(resolved)) continue;
10052
10305
  if (!isPathInside(resolved, opts.harnessRoot)) continue;
10053
10306
  seen.add(resolved);
@@ -10079,10 +10332,10 @@ function scanBuildCacheCandidates(opts) {
10079
10332
  if (!opts.includeOrphans || !existsSync36(opts.worktreesDir)) return candidates;
10080
10333
  for (const runEntry of readdirSync13(opts.worktreesDir, { withFileTypes: true })) {
10081
10334
  if (!runEntry.isDirectory()) continue;
10082
- const runPath = path46.join(opts.worktreesDir, runEntry.name);
10335
+ const runPath = path47.join(opts.worktreesDir, runEntry.name);
10083
10336
  for (const workerEntry of readdirSync13(runPath, { withFileTypes: true })) {
10084
10337
  if (!workerEntry.isDirectory()) continue;
10085
- const worktreePath = path46.join(runPath, workerEntry.name);
10338
+ const worktreePath = path47.join(runPath, workerEntry.name);
10086
10339
  candidates.push(
10087
10340
  ...collectBuildCacheForWorktree(worktreePath, opts, seen, {
10088
10341
  runId: runEntry.name,
@@ -10120,12 +10373,12 @@ function scanWorktreeCandidates(opts) {
10120
10373
  if (!orphanEnabled || !existsSync36(opts.worktreesDir)) return candidates;
10121
10374
  const indexedPaths = /* @__PURE__ */ new Set();
10122
10375
  for (const entry of opts.index.values()) {
10123
- indexedPaths.add(path46.resolve(entry.worktreePath));
10376
+ indexedPaths.add(path47.resolve(entry.worktreePath));
10124
10377
  }
10125
10378
  for (const runEntry of readdirSync13(opts.worktreesDir, { withFileTypes: true })) {
10126
10379
  if (!runEntry.isDirectory()) continue;
10127
10380
  if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
10128
- const runPath = path46.join(opts.worktreesDir, runEntry.name);
10381
+ const runPath = path47.join(opts.worktreesDir, runEntry.name);
10129
10382
  let workerEntries;
10130
10383
  try {
10131
10384
  workerEntries = readdirSync13(runPath, { withFileTypes: true });
@@ -10134,7 +10387,7 @@ function scanWorktreeCandidates(opts) {
10134
10387
  }
10135
10388
  for (const workerEntry of workerEntries) {
10136
10389
  if (!workerEntry.isDirectory()) continue;
10137
- const worktreePath = path46.resolve(path46.join(runPath, workerEntry.name));
10390
+ const worktreePath = path47.resolve(path47.join(runPath, workerEntry.name));
10138
10391
  if (seen.has(worktreePath)) continue;
10139
10392
  if (indexedPaths.has(worktreePath)) continue;
10140
10393
  if (!isPathInside(worktreePath, opts.harnessRoot)) continue;
@@ -10154,7 +10407,7 @@ function scanWorktreeCandidates(opts) {
10154
10407
 
10155
10408
  // src/cleanup-dependency-scan.ts
10156
10409
  import { existsSync as existsSync37, readdirSync as readdirSync14, statSync as statSync11 } from "node:fs";
10157
- import path47 from "node:path";
10410
+ import path48 from "node:path";
10158
10411
  var DEPENDENCY_CACHE_DIRS = [
10159
10412
  { dirName: "node_modules", kind: "remove_node_modules" },
10160
10413
  { dirName: ".next", kind: "remove_next_cache" }
@@ -10168,12 +10421,12 @@ function pathAgeMs3(target, now) {
10168
10421
  }
10169
10422
  }
10170
10423
  function isPathInside2(child, parent) {
10171
- const rel = path47.relative(parent, child);
10172
- return rel === "" || !rel.startsWith("..") && !path47.isAbsolute(rel);
10424
+ const rel = path48.relative(parent, child);
10425
+ return rel === "" || !rel.startsWith("..") && !path48.isAbsolute(rel);
10173
10426
  }
10174
10427
  function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
10175
10428
  if (!existsSync37(targetPath)) return;
10176
- const resolved = path47.resolve(targetPath);
10429
+ const resolved = path48.resolve(targetPath);
10177
10430
  if (seen.has(resolved)) return;
10178
10431
  if (!isPathInside2(resolved, opts.harnessRoot)) return;
10179
10432
  seen.add(resolved);
@@ -10190,7 +10443,7 @@ function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
10190
10443
  }
10191
10444
  function scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, meta) {
10192
10445
  for (const entry of DEPENDENCY_CACHE_DIRS) {
10193
- pushCandidate2(candidates, seen, opts, path47.join(worktreePath, entry.dirName), entry.kind, meta);
10446
+ pushCandidate2(candidates, seen, opts, path48.join(worktreePath, entry.dirName), entry.kind, meta);
10194
10447
  }
10195
10448
  }
10196
10449
  function scanDependencyCacheCandidates(opts) {
@@ -10208,7 +10461,7 @@ function scanDependencyCacheCandidates(opts) {
10208
10461
  for (const runEntry of readdirSync14(opts.worktreesDir, { withFileTypes: true })) {
10209
10462
  if (!runEntry.isDirectory()) continue;
10210
10463
  if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
10211
- const runPath = path47.join(opts.worktreesDir, runEntry.name);
10464
+ const runPath = path48.join(opts.worktreesDir, runEntry.name);
10212
10465
  let workerEntries;
10213
10466
  try {
10214
10467
  workerEntries = readdirSync14(runPath, { withFileTypes: true });
@@ -10217,7 +10470,7 @@ function scanDependencyCacheCandidates(opts) {
10217
10470
  }
10218
10471
  for (const workerEntry of workerEntries) {
10219
10472
  if (!workerEntry.isDirectory()) continue;
10220
- const worktreePath = path47.join(runPath, workerEntry.name);
10473
+ const worktreePath = path48.join(runPath, workerEntry.name);
10221
10474
  scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, {
10222
10475
  runId: runEntry.name,
10223
10476
  worker: workerEntry.name
@@ -10229,7 +10482,7 @@ function scanDependencyCacheCandidates(opts) {
10229
10482
 
10230
10483
  // src/cleanup-duplicate-worktrees.ts
10231
10484
  import { existsSync as existsSync38, statSync as statSync12 } from "node:fs";
10232
- import path48 from "node:path";
10485
+ import path49 from "node:path";
10233
10486
  function pathAgeMs4(target, now) {
10234
10487
  try {
10235
10488
  const mtime = statSync12(target).mtimeMs;
@@ -10259,8 +10512,8 @@ function parseWorktreePorcelain(output) {
10259
10512
  return records;
10260
10513
  }
10261
10514
  function isUnderWorktreesDir(worktreePath, worktreesDir) {
10262
- const rel = path48.relative(path48.resolve(worktreesDir), path48.resolve(worktreePath));
10263
- return rel !== "" && !rel.startsWith("..") && !path48.isAbsolute(rel);
10515
+ const rel = path49.relative(path49.resolve(worktreesDir), path49.resolve(worktreePath));
10516
+ return rel !== "" && !rel.startsWith("..") && !path49.isAbsolute(rel);
10264
10517
  }
10265
10518
  function isCleanWorktree(worktreePath, repoRoot) {
10266
10519
  try {
@@ -10276,11 +10529,11 @@ function scanDuplicateWorktreeCandidates(opts) {
10276
10529
  if (!opts.includeOrphans || !existsSync38(opts.worktreesDir)) return [];
10277
10530
  const repos = /* @__PURE__ */ new Set();
10278
10531
  for (const entry of opts.index.values()) {
10279
- if (entry.run.repo) repos.add(path48.resolve(entry.run.repo));
10532
+ if (entry.run.repo) repos.add(path49.resolve(entry.run.repo));
10280
10533
  }
10281
10534
  const indexedPaths = /* @__PURE__ */ new Set();
10282
10535
  for (const entry of opts.index.values()) {
10283
- indexedPaths.add(path48.resolve(entry.worktreePath));
10536
+ indexedPaths.add(path49.resolve(entry.worktreePath));
10284
10537
  }
10285
10538
  const candidates = [];
10286
10539
  const seen = /* @__PURE__ */ new Set();
@@ -10293,15 +10546,15 @@ function scanDuplicateWorktreeCandidates(opts) {
10293
10546
  }
10294
10547
  const worktrees = parseWorktreePorcelain(porcelain);
10295
10548
  for (const wt of worktrees) {
10296
- const resolved = path48.resolve(wt.path);
10297
- if (resolved === path48.resolve(repoRoot)) continue;
10549
+ const resolved = path49.resolve(wt.path);
10550
+ if (resolved === path49.resolve(repoRoot)) continue;
10298
10551
  if (!isUnderWorktreesDir(resolved, opts.worktreesDir)) continue;
10299
10552
  if (indexedPaths.has(resolved)) continue;
10300
10553
  if (seen.has(resolved)) continue;
10301
10554
  if (!existsSync38(resolved)) continue;
10302
10555
  if (!isCleanWorktree(resolved, repoRoot)) continue;
10303
- const rel = path48.relative(opts.worktreesDir, resolved);
10304
- const parts = rel.split(path48.sep);
10556
+ const rel = path49.relative(opts.worktreesDir, resolved);
10557
+ const parts = rel.split(path49.sep);
10305
10558
  const runId = parts[0];
10306
10559
  const worker = parts[1] ?? "unknown";
10307
10560
  seen.add(resolved);
@@ -10320,12 +10573,12 @@ function scanDuplicateWorktreeCandidates(opts) {
10320
10573
  }
10321
10574
 
10322
10575
  // src/cleanup-worktree-index.ts
10323
- import path49 from "node:path";
10576
+ import path50 from "node:path";
10324
10577
  function buildWorktreeIndexAt(harnessRoot) {
10325
10578
  const index = /* @__PURE__ */ new Map();
10326
10579
  for (const run of listRunRecordsForHarnessRoot(harnessRoot)) {
10327
10580
  for (const name of Object.keys(run.workers || {})) {
10328
- const workerPath = path49.join(
10581
+ const workerPath = path50.join(
10329
10582
  runDirectoryAt(harnessRoot, run.id),
10330
10583
  "workers",
10331
10584
  safeSlug(name),
@@ -10333,9 +10586,9 @@ function buildWorktreeIndexAt(harnessRoot) {
10333
10586
  );
10334
10587
  const worker = readJson(workerPath, void 0);
10335
10588
  if (!worker?.worktreePath) continue;
10336
- index.set(path49.resolve(worker.worktreePath), {
10589
+ index.set(path50.resolve(worker.worktreePath), {
10337
10590
  harnessRoot,
10338
- worktreePath: path49.resolve(worker.worktreePath),
10591
+ worktreePath: path50.resolve(worker.worktreePath),
10339
10592
  runId: run.id,
10340
10593
  workerName: name,
10341
10594
  run,
@@ -10405,14 +10658,14 @@ function resolvePipelineHarnessRetention(runId) {
10405
10658
 
10406
10659
  // src/cleanup-orphan-safety.ts
10407
10660
  import { existsSync as existsSync39, statSync as statSync13 } from "node:fs";
10408
- import path50 from "node:path";
10661
+ import path51 from "node:path";
10409
10662
  var DEFAULT_HEARTBEAT_FRESH_MS = 30 * 60 * 1e3;
10410
10663
  function assessOrphanWorktreeSafety(input) {
10411
10664
  const now = input.now ?? Date.now();
10412
10665
  const heartbeatFreshMs = input.heartbeatFreshMs ?? DEFAULT_HEARTBEAT_FRESH_MS;
10413
10666
  if (!existsSync39(input.worktreePath)) return null;
10414
10667
  if (input.runId && input.workerName) {
10415
- const heartbeatPath = path50.join(
10668
+ const heartbeatPath = path51.join(
10416
10669
  input.harnessRoot,
10417
10670
  "runs",
10418
10671
  input.runId,
@@ -10426,7 +10679,7 @@ function assessOrphanWorktreeSafety(input) {
10426
10679
  } catch {
10427
10680
  }
10428
10681
  }
10429
- const gitDir = path50.join(input.worktreePath, ".git");
10682
+ const gitDir = path51.join(input.worktreePath, ".git");
10430
10683
  if (!existsSync39(gitDir)) return null;
10431
10684
  const porcelain = gitCapture(input.worktreePath, ["status", "--porcelain"]);
10432
10685
  if (porcelain.status !== 0) return "pr_or_unmerged_commits";
@@ -10458,10 +10711,10 @@ function assessOrphanWorktreeSafety(input) {
10458
10711
  // src/cleanup-harness-roots.ts
10459
10712
  import { existsSync as existsSync40 } from "node:fs";
10460
10713
  import { homedir as homedir13 } from "node:os";
10461
- import path51 from "node:path";
10714
+ import path52 from "node:path";
10462
10715
  var WELL_KNOWN_HARNESS_SCAN_ROOTS = [
10463
10716
  "/var/tmp/kynver-harness",
10464
- path51.join(homedir13(), ".openclaw", "harness")
10717
+ path52.join(homedir13(), ".openclaw", "harness")
10465
10718
  ];
10466
10719
  function addRoot(seen, roots, candidate) {
10467
10720
  if (!candidate?.trim()) return;
@@ -10483,7 +10736,7 @@ function resolveHarnessScanRoots(options = {}) {
10483
10736
  for (const candidate of extra ?? []) addRoot(seen, roots, candidate);
10484
10737
  if (shouldScanWellKnownRoots(options)) {
10485
10738
  for (const candidate of WELL_KNOWN_HARNESS_SCAN_ROOTS) {
10486
- const resolved = path51.resolve(candidate);
10739
+ const resolved = path52.resolve(candidate);
10487
10740
  if (!seen.has(resolved) && existsSync40(resolved)) addRoot(seen, roots, resolved);
10488
10741
  }
10489
10742
  }
@@ -10638,9 +10891,9 @@ function mergeWorktreeIndexes(scanRoots) {
10638
10891
  }
10639
10892
  function worktreePathForCandidate(candidate, worktreesDir) {
10640
10893
  if (candidate.runId && candidate.worker) {
10641
- return path52.join(worktreesDir, candidate.runId, candidate.worker);
10894
+ return path53.join(worktreesDir, candidate.runId, candidate.worker);
10642
10895
  }
10643
- return path52.resolve(candidate.path, "..");
10896
+ return path53.resolve(candidate.path, "..");
10644
10897
  }
10645
10898
  function runHarnessCleanup(options = {}) {
10646
10899
  let retention = resolveHarnessRetention(options);
@@ -10665,7 +10918,7 @@ function runHarnessCleanup(options = {}) {
10665
10918
  for (const harnessRoot of paths.scanRoots) {
10666
10919
  if (atSweepCap()) break;
10667
10920
  emitCleanupProgress("root", harnessRoot);
10668
- const worktreesDir = path52.join(harnessRoot, "worktrees");
10921
+ const worktreesDir = path53.join(harnessRoot, "worktrees");
10669
10922
  const scanOpts = {
10670
10923
  harnessRoot,
10671
10924
  worktreesDir,
@@ -10678,7 +10931,7 @@ function runHarnessCleanup(options = {}) {
10678
10931
  };
10679
10932
  for (const raw of scanDependencyCacheCandidates(scanOpts)) {
10680
10933
  if (atSweepCap()) break;
10681
- const resolved = path52.resolve(raw.path);
10934
+ const resolved = path53.resolve(raw.path);
10682
10935
  if (processedPaths.has(resolved)) continue;
10683
10936
  processedPaths.add(resolved);
10684
10937
  const candidate = { ...raw, path: resolved };
@@ -10689,7 +10942,7 @@ function runHarnessCleanup(options = {}) {
10689
10942
  continue;
10690
10943
  }
10691
10944
  const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
10692
- const indexed = index.get(path52.resolve(worktreePath)) ?? null;
10945
+ const indexed = index.get(path53.resolve(worktreePath)) ?? null;
10693
10946
  const guardReason = skipDependencyCacheRemoval({
10694
10947
  indexed,
10695
10948
  includeOrphans: true,
@@ -10713,7 +10966,7 @@ function runHarnessCleanup(options = {}) {
10713
10966
  }
10714
10967
  for (const raw of scanBuildCacheCandidates(scanOpts)) {
10715
10968
  if (atSweepCap()) break;
10716
- const resolved = path52.resolve(raw.path);
10969
+ const resolved = path53.resolve(raw.path);
10717
10970
  if (processedPaths.has(resolved)) continue;
10718
10971
  processedPaths.add(resolved);
10719
10972
  const candidate = { ...raw, path: resolved };
@@ -10724,7 +10977,7 @@ function runHarnessCleanup(options = {}) {
10724
10977
  continue;
10725
10978
  }
10726
10979
  const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
10727
- const indexed = index.get(path52.resolve(worktreePath)) ?? null;
10980
+ const indexed = index.get(path53.resolve(worktreePath)) ?? null;
10728
10981
  const guardReason = skipBuildCacheRemoval({
10729
10982
  indexed,
10730
10983
  includeOrphans: true,
@@ -10754,11 +11007,11 @@ function runHarnessCleanup(options = {}) {
10754
11007
  const worktreeSeen = /* @__PURE__ */ new Set();
10755
11008
  for (const raw of worktreeCandidates) {
10756
11009
  if (atSweepCap()) break;
10757
- const resolved = path52.resolve(raw.path);
11010
+ const resolved = path53.resolve(raw.path);
10758
11011
  if (worktreeSeen.has(resolved)) continue;
10759
11012
  worktreeSeen.add(resolved);
10760
11013
  const candidate = { ...raw, path: resolved };
10761
- const indexed = index.get(path52.resolve(candidate.path)) ?? null;
11014
+ const indexed = index.get(path53.resolve(candidate.path)) ?? null;
10762
11015
  const orphanSafety = indexed ? null : assessOrphanWorktreeSafety({
10763
11016
  worktreePath: candidate.path,
10764
11017
  harnessRoot,
@@ -10768,7 +11021,7 @@ function runHarnessCleanup(options = {}) {
10768
11021
  });
10769
11022
  const guardSkip = skipWorktreeRemoval({
10770
11023
  indexed,
10771
- worktreePath: path52.resolve(candidate.path),
11024
+ worktreePath: path53.resolve(candidate.path),
10772
11025
  includeOrphans: retention.includeOrphans,
10773
11026
  worktreesAgeMs: retention.worktreesAgeMs,
10774
11027
  terminalWorktreesAgeMs: retention.terminalWorktreesAgeMs,
@@ -10800,11 +11053,11 @@ function runHarnessCleanup(options = {}) {
10800
11053
  now: paths.now
10801
11054
  })) {
10802
11055
  if (atSweepCap()) break;
10803
- const resolved = path52.resolve(raw.path);
11056
+ const resolved = path53.resolve(raw.path);
10804
11057
  if (processedPaths.has(resolved)) continue;
10805
11058
  processedPaths.add(resolved);
10806
11059
  const candidate = { ...raw, path: resolved };
10807
- const runId = candidate.runId ?? path52.basename(resolved);
11060
+ const runId = candidate.runId ?? path53.basename(resolved);
10808
11061
  const dirSkip = skipRunDirectoryRemoval({
10809
11062
  harnessRoot,
10810
11063
  runId,
@@ -10943,7 +11196,7 @@ import { fileURLToPath as fileURLToPath5 } from "node:url";
10943
11196
 
10944
11197
  // src/discard-disposable.ts
10945
11198
  import { existsSync as existsSync41, rmSync as rmSync4 } from "node:fs";
10946
- import path53 from "node:path";
11199
+ import path54 from "node:path";
10947
11200
  function normalizeRelativePath2(value) {
10948
11201
  const normalized = value.replace(/\\/g, "/").replace(/^\.\//, "").trim();
10949
11202
  if (!normalized || normalized.startsWith("/") || normalized.includes("..")) {
@@ -10965,12 +11218,12 @@ function discardDisposableArtifacts(args) {
10965
11218
  if (paths.length === 0) {
10966
11219
  return { ok: false, removed: [], reason: "requires at least one --path" };
10967
11220
  }
10968
- const worktreeRoot = path53.resolve(worker.worktreePath);
11221
+ const worktreeRoot = path54.resolve(worker.worktreePath);
10969
11222
  const removed = [];
10970
11223
  for (const raw of paths) {
10971
11224
  const rel = normalizeRelativePath2(raw);
10972
- const abs = path53.resolve(worktreeRoot, rel);
10973
- if (!abs.startsWith(worktreeRoot + path53.sep) && abs !== worktreeRoot) {
11225
+ const abs = path54.resolve(worktreeRoot, rel);
11226
+ if (!abs.startsWith(worktreeRoot + path54.sep) && abs !== worktreeRoot) {
10974
11227
  return { ok: false, removed, reason: `path escapes worktree: ${raw}` };
10975
11228
  }
10976
11229
  if (!existsSync41(abs)) {
@@ -11037,7 +11290,7 @@ function validateDaemonInstallIdentity(config = loadUserConfig(), env = process.
11037
11290
  // src/cron/cron-env.ts
11038
11291
  import { existsSync as existsSync42 } from "node:fs";
11039
11292
  import { homedir as homedir14 } from "node:os";
11040
- import path54 from "node:path";
11293
+ import path55 from "node:path";
11041
11294
  function envFlag3(name, defaultValue) {
11042
11295
  const raw = process.env[name]?.trim().toLowerCase();
11043
11296
  if (!raw) return defaultValue;
@@ -11053,7 +11306,7 @@ function envInt(name, fallback, min = 1) {
11053
11306
  function defaultKynverCronStorePath() {
11054
11307
  const explicit = process.env.KYNVER_CRON_STORE_PATH?.trim() || process.env.OPENCLAW_CRON_STORE_PATH?.trim();
11055
11308
  if (explicit) return explicit;
11056
- return path54.join(homedir14(), ".kynver", "agent-os-cron.json");
11309
+ return path55.join(homedir14(), ".kynver", "agent-os-cron.json");
11057
11310
  }
11058
11311
  function defaultKynverCronStatePath(storePath = defaultKynverCronStorePath()) {
11059
11312
  const explicit = process.env.KYNVER_CRON_TICK_STATE_PATH?.trim();
@@ -11295,7 +11548,7 @@ async function loadCronJobs(storePath = defaultKynverCronStorePath()) {
11295
11548
  // src/cron/cron-tick-state.ts
11296
11549
  import { randomBytes } from "node:crypto";
11297
11550
  import { promises as fs4 } from "node:fs";
11298
- import path55 from "node:path";
11551
+ import path56 from "node:path";
11299
11552
  var EMPTY = { version: 1, jobs: {} };
11300
11553
  async function readFileIfExists2(filePath) {
11301
11554
  try {
@@ -11322,7 +11575,7 @@ async function loadCronTickState(statePath) {
11322
11575
  return parseCronTickState(raw);
11323
11576
  }
11324
11577
  async function writeStateAtomic(statePath, state) {
11325
- await fs4.mkdir(path55.dirname(statePath), { recursive: true });
11578
+ await fs4.mkdir(path56.dirname(statePath), { recursive: true });
11326
11579
  const suffix = randomBytes(6).toString("hex");
11327
11580
  const tmp = `${statePath}.tmp-${process.pid}-${Date.now()}-${suffix}`;
11328
11581
  await fs4.writeFile(tmp, `${JSON.stringify(state, null, 2)}
@@ -11499,7 +11752,7 @@ async function runKynverCronTick(opts = {}) {
11499
11752
  }
11500
11753
 
11501
11754
  // src/pipeline-tick.ts
11502
- import path57 from "node:path";
11755
+ import path58 from "node:path";
11503
11756
 
11504
11757
  // src/pipeline-dispatch.ts
11505
11758
  var RESERVED_REVIEW_STARTS = 1;
@@ -11630,7 +11883,7 @@ function resolvePipelineMaxStarts(resourceGate, operatorTick) {
11630
11883
  }
11631
11884
 
11632
11885
  // src/plan-progress-daemon-sync.ts
11633
- import path56 from "node:path";
11886
+ import path57 from "node:path";
11634
11887
 
11635
11888
  // src/plan-progress-sync.ts
11636
11889
  async function syncPlanProgress(args) {
@@ -11654,7 +11907,7 @@ async function syncActiveWorkerPlanProgress(runId, args) {
11654
11907
  const outcomes = [];
11655
11908
  for (const name of Object.keys(run.workers || {})) {
11656
11909
  const worker = readJson(
11657
- path56.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
11910
+ path57.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
11658
11911
  void 0
11659
11912
  );
11660
11913
  if (!worker?.dispatched || !worker.taskId) continue;
@@ -11716,7 +11969,7 @@ async function completeFinishedWorkers(runId, args) {
11716
11969
  const outcomes = [];
11717
11970
  for (const name of Object.keys(run.workers || {})) {
11718
11971
  const worker = readJson(
11719
- path57.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
11972
+ path58.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
11720
11973
  void 0
11721
11974
  );
11722
11975
  if (!worker?.taskId || worker.localOnly) continue;
@@ -11863,6 +12116,15 @@ async function runPipelineTick(args) {
11863
12116
  var DEFAULT_INTERVAL_MS = 6e4;
11864
12117
  var IDLE_INTERVAL_MS = 5 * 6e4;
11865
12118
  var MAX_IDLE_STREAK = 10;
12119
+ var SLEEP_POLL_MS = 250;
12120
+ async function awaitDaemonBackoff(ms, isStopping) {
12121
+ let remaining = ms;
12122
+ while (remaining > 0 && !isStopping()) {
12123
+ const step = Math.min(SLEEP_POLL_MS, remaining);
12124
+ await sleepMsAsync(step);
12125
+ remaining -= step;
12126
+ }
12127
+ }
11866
12128
  async function runDaemon(args) {
11867
12129
  const runId = String(required(String(args.run || ""), "--run"));
11868
12130
  const agentOsId = String(required(String(args.agentOsId || loadUserConfig().agentOsId || ""), "--agent-os-id"));
@@ -11920,17 +12182,17 @@ async function runDaemon(args) {
11920
12182
  idleStreak = 0;
11921
12183
  }
11922
12184
  const backoff = idleStreak >= MAX_IDLE_STREAK ? IDLE_INTERVAL_MS : intervalMs;
11923
- await sleepMs(backoff);
12185
+ await awaitDaemonBackoff(backoff, () => stopping);
11924
12186
  } catch (error) {
11925
12187
  console.error(JSON.stringify({ event: "daemon_tick_error", error: error.message }));
11926
- await sleepMs(intervalMs);
12188
+ await awaitDaemonBackoff(intervalMs, () => stopping);
11927
12189
  }
11928
12190
  }
11929
12191
  console.error(JSON.stringify({ event: "daemon_stop", runId, agentOsId }));
11930
12192
  }
11931
12193
 
11932
12194
  // src/plan-progress.ts
11933
- import path61 from "node:path";
12195
+ import path62 from "node:path";
11934
12196
 
11935
12197
  // src/bounded-build/constants.ts
11936
12198
  var DEFAULT_BUILD_MEM_BUDGET_BYTES = 1536 * 1024 * 1024;
@@ -12073,16 +12335,16 @@ import {
12073
12335
  unlinkSync as unlinkSync4,
12074
12336
  writeFileSync as writeFileSync5
12075
12337
  } from "node:fs";
12076
- import path59 from "node:path";
12338
+ import path60 from "node:path";
12077
12339
 
12078
12340
  // src/heavy-verification/paths.ts
12079
12341
  import { mkdirSync as mkdirSync7 } from "node:fs";
12080
- import path58 from "node:path";
12342
+ import path59 from "node:path";
12081
12343
  function resolveHeavyVerificationRoot() {
12082
- return path58.join(resolveKynverStateRoot(), "heavy-verification");
12344
+ return path59.join(resolveKynverStateRoot(), "heavy-verification");
12083
12345
  }
12084
12346
  function heavyVerificationSlotsDir() {
12085
- return path58.join(resolveHeavyVerificationRoot(), "slots");
12347
+ return path59.join(resolveHeavyVerificationRoot(), "slots");
12086
12348
  }
12087
12349
  function ensureHeavyVerificationDirs() {
12088
12350
  const dir = heavyVerificationSlotsDir();
@@ -12111,7 +12373,7 @@ function indexedSlotId(index) {
12111
12373
  return `slot-${index}`;
12112
12374
  }
12113
12375
  function slotFilePath(slotId, slotsDir = heavyVerificationSlotsDir()) {
12114
- return path59.join(slotsDir, `${slotId}.json`);
12376
+ return path60.join(slotsDir, `${slotId}.json`);
12115
12377
  }
12116
12378
  function readSlotRecord(filePath) {
12117
12379
  if (!existsSync44(filePath)) return null;
@@ -12150,7 +12412,7 @@ function reclaimStaleHeavyVerificationSlots(opts = {}) {
12150
12412
  let reclaimed = 0;
12151
12413
  for (const name of readdirSync15(slotsDir)) {
12152
12414
  if (!name.endsWith(".json")) continue;
12153
- const filePath = path59.join(slotsDir, name);
12415
+ const filePath = path60.join(slotsDir, name);
12154
12416
  const before = existsSync44(filePath);
12155
12417
  reclaimStaleSlot(filePath, staleMs);
12156
12418
  if (before && !existsSync44(filePath)) reclaimed += 1;
@@ -12164,7 +12426,7 @@ function listActiveHeavyVerificationSlots(opts = {}) {
12164
12426
  const active = [];
12165
12427
  for (const name of readdirSync15(slotsDir)) {
12166
12428
  if (!name.endsWith(".json")) continue;
12167
- const record3 = readSlotRecord(path59.join(slotsDir, name));
12429
+ const record3 = readSlotRecord(path60.join(slotsDir, name));
12168
12430
  if (record3 && !slotIsStale(record3, staleMs)) active.push(record3);
12169
12431
  }
12170
12432
  return active;
@@ -12284,11 +12546,11 @@ function waitForHeavyVerificationSlot(command, timeoutMs, pollMs = 2e3, opts = {
12284
12546
  }
12285
12547
 
12286
12548
  // src/harness-worktree-build-guard.ts
12287
- import path60 from "node:path";
12549
+ import path61 from "node:path";
12288
12550
  function isPathUnderHarnessWorktree(cwd) {
12289
12551
  const worktreesDir = harnessWorktreesDir(resolveHarnessRoot());
12290
- const rel = path60.relative(worktreesDir, path60.resolve(cwd));
12291
- return rel.length > 0 && !rel.startsWith("..") && !path60.isAbsolute(rel);
12552
+ const rel = path61.relative(worktreesDir, path61.resolve(cwd));
12553
+ return rel.length > 0 && !rel.startsWith("..") && !path61.isAbsolute(rel);
12292
12554
  }
12293
12555
  function assessHarnessWorktreeBuildGuard(cwd) {
12294
12556
  if (!isPathUnderHarnessWorktree(cwd)) return { ok: true };
@@ -12500,7 +12762,7 @@ async function emitPlanProgress(args) {
12500
12762
  }
12501
12763
  function verifyPlanLocal(args) {
12502
12764
  const worktree = required(args.worktree ? String(args.worktree) : void 0, "worktree");
12503
- const cwd = path61.resolve(worktree);
12765
+ const cwd = path62.resolve(worktree);
12504
12766
  const summary = runHarnessVerifyCommands(cwd);
12505
12767
  const emitJson = args.json === true || args.json === "true";
12506
12768
  const payload = { passed: summary.passed, worktree: cwd, steps: summary.steps };
@@ -12549,9 +12811,9 @@ async function verifyPlan(args) {
12549
12811
  }
12550
12812
 
12551
12813
  // src/harness-verify-cli.ts
12552
- import path62 from "node:path";
12814
+ import path63 from "node:path";
12553
12815
  function runHarnessVerifyCli(args) {
12554
- const cwd = path62.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
12816
+ const cwd = path63.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
12555
12817
  const emitJson = args.json === true || args.json === "true" || args.emitJson === true || args.emitJson === "true";
12556
12818
  const commands = [];
12557
12819
  const rawCmd = args.command;
@@ -12988,7 +13250,7 @@ ${text.slice(0, 800)}`,
12988
13250
  }
12989
13251
 
12990
13252
  // src/monitor/monitor.service.ts
12991
- import path64 from "node:path";
13253
+ import path65 from "node:path";
12992
13254
 
12993
13255
  // src/monitor/monitor.classify.ts
12994
13256
  function classifyWorkerHealth(input) {
@@ -13041,10 +13303,10 @@ function classifyWorkerHealth(input) {
13041
13303
 
13042
13304
  // src/monitor/monitor.store.ts
13043
13305
  import { existsSync as existsSync45, mkdirSync as mkdirSync9, readdirSync as readdirSync16, unlinkSync as unlinkSync5 } from "node:fs";
13044
- import path63 from "node:path";
13306
+ import path64 from "node:path";
13045
13307
  function monitorsDir() {
13046
13308
  const { harnessRoot } = getHarnessPaths();
13047
- const dir = path63.join(harnessRoot, "monitors");
13309
+ const dir = path64.join(harnessRoot, "monitors");
13048
13310
  mkdirSync9(dir, { recursive: true });
13049
13311
  return dir;
13050
13312
  }
@@ -13052,7 +13314,7 @@ function monitorIdFor(runId, workerName) {
13052
13314
  return workerName ? `${safeSlug(runId)}--${safeSlug(workerName)}` : safeSlug(runId);
13053
13315
  }
13054
13316
  function monitorPath(monitorId) {
13055
- return path63.join(monitorsDir(), `${monitorId}.json`);
13317
+ return path64.join(monitorsDir(), `${monitorId}.json`);
13056
13318
  }
13057
13319
  function loadMonitorSession(monitorId) {
13058
13320
  return readJson(monitorPath(monitorId), void 0);
@@ -13073,7 +13335,7 @@ function listMonitorSessions() {
13073
13335
  for (const name of readdirSync16(dir)) {
13074
13336
  if (!name.endsWith(".json")) continue;
13075
13337
  const session = readJson(
13076
- path63.join(dir, name),
13338
+ path64.join(dir, name),
13077
13339
  void 0
13078
13340
  );
13079
13341
  if (!session?.monitorId) continue;
@@ -13164,7 +13426,7 @@ async function fetchTaskLeasesForWorkers(input) {
13164
13426
  // src/monitor/monitor.service.ts
13165
13427
  function workerRecord2(runId, name) {
13166
13428
  return readJson(
13167
- path64.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
13429
+ path65.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
13168
13430
  void 0
13169
13431
  );
13170
13432
  }
@@ -13371,17 +13633,17 @@ async function runMonitorLoop(args) {
13371
13633
  // src/monitor/monitor-spawn.ts
13372
13634
  import { spawn as spawn6 } from "node:child_process";
13373
13635
  import { closeSync as closeSync8, existsSync as existsSync46, openSync as openSync8 } from "node:fs";
13374
- import path65 from "node:path";
13636
+ import path66 from "node:path";
13375
13637
  import { fileURLToPath as fileURLToPath4 } from "node:url";
13376
13638
  function resolveDefaultCliPath2() {
13377
- return path65.join(fileURLToPath4(new URL(".", import.meta.url)), "cli.js");
13639
+ return path66.join(fileURLToPath4(new URL(".", import.meta.url)), "cli.js");
13378
13640
  }
13379
13641
  function spawnMonitorSidecar(opts) {
13380
13642
  const cliPath = opts.cliPath ?? resolveDefaultCliPath2();
13381
13643
  if (!existsSync46(cliPath)) return void 0;
13382
13644
  const monitorId = monitorIdFor(opts.runId, opts.workerName);
13383
13645
  const { harnessRoot } = getHarnessPaths();
13384
- const logPath = path65.join(harnessRoot, "monitors", `${monitorId}.log`);
13646
+ const logPath = path66.join(harnessRoot, "monitors", `${monitorId}.log`);
13385
13647
  let logFd;
13386
13648
  try {
13387
13649
  logFd = openSync8(logPath, "a");
@@ -13501,7 +13763,7 @@ async function monitorTickCli(args) {
13501
13763
  }
13502
13764
 
13503
13765
  // src/post-restart-unblock.ts
13504
- import path66 from "node:path";
13766
+ import path67 from "node:path";
13505
13767
  function skip(runId, worker, taskId, agentOsId, leaseOwner, reason) {
13506
13768
  return { runId, worker, taskId, agentOsId, leaseOwner, action: "skipped", reason };
13507
13769
  }
@@ -13514,7 +13776,7 @@ async function postRestartUnblock(args) {
13514
13776
  const errors = [];
13515
13777
  for (const run of listRunRecords()) {
13516
13778
  for (const name of Object.keys(run.workers ?? {})) {
13517
- const workerPath = path66.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
13779
+ const workerPath = path67.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
13518
13780
  const worker = readJson(workerPath, void 0);
13519
13781
  if (!worker) {
13520
13782
  skipped.push(skip(run.id, name, "", "", "", "worker.json missing"));
@@ -13626,9 +13888,9 @@ async function postRestartUnblockCli(args) {
13626
13888
  }
13627
13889
 
13628
13890
  // src/default-repo-cli.ts
13629
- import path67 from "node:path";
13891
+ import path68 from "node:path";
13630
13892
  import { homedir as homedir15 } from "node:os";
13631
- var CONFIG_FILE2 = path67.join(homedir15(), ".kynver", "config.json");
13893
+ var CONFIG_FILE2 = path68.join(homedir15(), ".kynver", "config.json");
13632
13894
  function ensureDefaultRepo(opts) {
13633
13895
  const existing = loadUserConfig();
13634
13896
  const resolved = resolveDefaultRepo({ ...opts, config: existing });
@@ -13709,12 +13971,12 @@ function summarizeResolvedDefaultRepo(resolved) {
13709
13971
  }
13710
13972
 
13711
13973
  // src/doctor/runtime-takeover.ts
13712
- import path69 from "node:path";
13974
+ import path70 from "node:path";
13713
13975
 
13714
13976
  // src/doctor/runtime-takeover.probes.ts
13715
13977
  import { accessSync, constants, existsSync as existsSync47, readFileSync as readFileSync19 } from "node:fs";
13716
13978
  import { homedir as homedir16 } from "node:os";
13717
- import path68 from "node:path";
13979
+ import path69 from "node:path";
13718
13980
  import { spawnSync as spawnSync10 } from "node:child_process";
13719
13981
  function captureCommand(bin, args) {
13720
13982
  try {
@@ -13756,10 +14018,10 @@ var defaultRuntimeTakeoverProbes = {
13756
14018
  commandOnPath: (bin) => captureCommand(process.platform === "win32" ? "where" : "which", [bin]),
13757
14019
  kynverVersion: (bin) => captureCommand(bin, ["--version"]),
13758
14020
  loadConfig: () => loadUserConfig(),
13759
- configFilePath: () => path68.join(homedir16(), ".kynver", "config.json"),
13760
- credentialsFilePath: () => path68.join(homedir16(), ".kynver", "credentials"),
14021
+ configFilePath: () => path69.join(homedir16(), ".kynver", "config.json"),
14022
+ credentialsFilePath: () => path69.join(homedir16(), ".kynver", "credentials"),
13761
14023
  readCredentials: () => {
13762
- const credPath = path68.join(homedir16(), ".kynver", "credentials");
14024
+ const credPath = path69.join(homedir16(), ".kynver", "credentials");
13763
14025
  if (!existsSync47(credPath)) {
13764
14026
  return { hasApiKey: false };
13765
14027
  }
@@ -13794,7 +14056,7 @@ var defaultRuntimeTakeoverProbes = {
13794
14056
  })()
13795
14057
  }),
13796
14058
  harnessRoot: () => resolveHarnessRoot(),
13797
- legacyOpenclawHarnessRoot: () => path68.join(homedir16(), ".openclaw", "harness"),
14059
+ legacyOpenclawHarnessRoot: () => path69.join(homedir16(), ".openclaw", "harness"),
13798
14060
  pathExists: (target) => existsSync47(target),
13799
14061
  pathWritable: (target) => isWritable(target)
13800
14062
  };
@@ -14201,8 +14463,8 @@ function assessVercelDeployEvidence(probes) {
14201
14463
  }
14202
14464
  function assessHarnessDirs(probes) {
14203
14465
  const harnessRoot = probes.harnessRoot();
14204
- const runsDir = path69.join(harnessRoot, "runs");
14205
- const worktreesDir = path69.join(harnessRoot, "worktrees");
14466
+ const runsDir = path70.join(harnessRoot, "runs");
14467
+ const worktreesDir = path70.join(harnessRoot, "worktrees");
14206
14468
  const displayHarnessRoot = redactHomePath(harnessRoot);
14207
14469
  const displayRunsDir = redactHomePath(runsDir);
14208
14470
  const displayWorktreesDir = redactHomePath(worktreesDir);
@@ -14466,9 +14728,9 @@ function applySchedulerCutoverAttestation(config) {
14466
14728
  }
14467
14729
 
14468
14730
  // src/scheduler-cutover-cli.ts
14469
- import path70 from "node:path";
14731
+ import path71 from "node:path";
14470
14732
  import { homedir as homedir17 } from "node:os";
14471
- var CONFIG_FILE3 = path70.join(homedir17(), ".kynver", "config.json");
14733
+ var CONFIG_FILE3 = path71.join(homedir17(), ".kynver", "config.json");
14472
14734
  function runSchedulerCutoverCheckCli(json = false) {
14473
14735
  const config = loadUserConfig();
14474
14736
  const report = assessSchedulerCutover(config);
@@ -14605,6 +14867,157 @@ async function runCronTickCli(args) {
14605
14867
  );
14606
14868
  }
14607
14869
 
14870
+ // src/lane/landing-maintainer-tick.ts
14871
+ import os9 from "node:os";
14872
+
14873
+ // src/lane/lane-spec.ts
14874
+ var LANDING_MAINTAINER_LANE_SPEC = {
14875
+ slug: "landing-maintainer",
14876
+ originCron: "maintain-8-blocker-and-pr-landing-workers",
14877
+ defaultRepo: "Totalsolutionsync/Kynver",
14878
+ landScript: "scripts/agent-os-land-pr.mjs",
14879
+ landScriptArgs: ["--skip-not-ready"]
14880
+ };
14881
+
14882
+ // src/lane/landing-maintainer-local.ts
14883
+ import { spawnSync as spawnSync11 } from "node:child_process";
14884
+ import path72 from "node:path";
14885
+ function runLandingWrapper(prNumber, repoRoot, execute) {
14886
+ const script = path72.join(repoRoot, LANDING_MAINTAINER_LANE_SPEC.landScript);
14887
+ const args = [script, String(prNumber), ...LANDING_MAINTAINER_LANE_SPEC.landScriptArgs];
14888
+ if (!execute) {
14889
+ return {
14890
+ action: { kind: "land_pr", prNumber, reason: "dry-run" },
14891
+ executed: false,
14892
+ exitCode: 0,
14893
+ stdout: `dry-run: node ${args.join(" ")}`,
14894
+ stderr: ""
14895
+ };
14896
+ }
14897
+ const result = spawnSync11("node", args, {
14898
+ cwd: repoRoot,
14899
+ encoding: "utf8",
14900
+ timeout: 10 * 60 * 1e3
14901
+ });
14902
+ return {
14903
+ action: { kind: "land_pr", prNumber, reason: "landing wrapper invoked" },
14904
+ executed: true,
14905
+ exitCode: result.status,
14906
+ stdout: result.stdout ?? "",
14907
+ stderr: result.stderr ?? ""
14908
+ };
14909
+ }
14910
+ function resolveLandingMaintainerRepoRoot(args) {
14911
+ const explicit = args.repoPath ? String(args.repoPath).trim() : "";
14912
+ if (explicit) return path72.resolve(explicit);
14913
+ const resolved = resolveDefaultRepo();
14914
+ return resolved?.repo ?? process.cwd();
14915
+ }
14916
+
14917
+ // src/lane/landing-maintainer-tick.ts
14918
+ async function runLandingMaintainerLaneTick(args) {
14919
+ const agentOsId = String(required(String(args.agentOsId || ""), "--agent-os-id"));
14920
+ const repoSlug = String(args.repo || LANDING_MAINTAINER_LANE_SPEC.defaultRepo).trim();
14921
+ const fleet = args.fleet === true || args.fleet === "true";
14922
+ const execute = args.execute !== false && args.execute !== "false";
14923
+ const runId = args.run ? String(args.run) : void 0;
14924
+ const resourceGate = observeRunnerResourceGate({
14925
+ runId: runId ?? "fleet-lane-tick"
14926
+ });
14927
+ const boxCapacity = {
14928
+ ...buildBoxResourceSnapshotFromGate(resourceGate, {
14929
+ harnessRunId: runId,
14930
+ boxKind: resolveBoxKindFromConfig(loadUserConfig()),
14931
+ hostLabel: os9.hostname()
14932
+ }),
14933
+ providerHealthy: resourceGate.ok,
14934
+ authorizedForRepair: resourceGate.ok,
14935
+ authorizedForLanding: resourceGate.ok,
14936
+ systemHealthBlockers: resourceGate.ok ? [] : [resourceGate.reason ?? "resource_gate_blocked"],
14937
+ actionableWorkers: resourceGate.activeWorkers
14938
+ };
14939
+ const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
14940
+ const secret = await resolveCallbackSecretWithMint(
14941
+ args.secret ? String(args.secret) : void 0,
14942
+ agentOsId,
14943
+ { baseUrl: base }
14944
+ );
14945
+ const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/fleet-landing-maintainer/tick`;
14946
+ const res = await postJson(url, secret, {
14947
+ repo: repoSlug,
14948
+ fleet,
14949
+ execute,
14950
+ runId,
14951
+ boxCapacity
14952
+ });
14953
+ const coordinator = res.response;
14954
+ const localOutcomes = [];
14955
+ const repoRoot = resolveLandingMaintainerRepoRoot(args);
14956
+ const actions = Array.isArray(coordinator?.localActions) ? coordinator.localActions : [];
14957
+ const holderBoxId = boxCapacity.boxId;
14958
+ for (const action of actions) {
14959
+ if (action.kind === "land_pr" && typeof action.prNumber === "number") {
14960
+ const outcome = runLandingWrapper(action.prNumber, repoRoot, execute);
14961
+ localOutcomes.push({
14962
+ action: outcome.action,
14963
+ executed: outcome.executed,
14964
+ exitCode: outcome.exitCode
14965
+ });
14966
+ if (execute && outcome.executed) {
14967
+ const merged = outcome.exitCode === 0;
14968
+ const prUrl = typeof action.prUrl === "string" && action.prUrl.trim() ? action.prUrl.trim() : `https://github.com/${repoSlug}/pull/${action.prNumber}`;
14969
+ try {
14970
+ await postJson(
14971
+ `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/fleet-landing-maintainer/outcome`,
14972
+ secret,
14973
+ { repo: repoSlug, prUrl, holderBoxId, merged }
14974
+ );
14975
+ } catch {
14976
+ }
14977
+ }
14978
+ continue;
14979
+ }
14980
+ localOutcomes.push({
14981
+ action,
14982
+ executed: false,
14983
+ exitCode: null
14984
+ });
14985
+ }
14986
+ return {
14987
+ repo: repoSlug,
14988
+ fleet,
14989
+ execute,
14990
+ coordinator,
14991
+ localOutcomes
14992
+ };
14993
+ }
14994
+
14995
+ // src/lane/lane-tick-cli.ts
14996
+ async function runLaneTickCli(args, laneFromArgv) {
14997
+ const lane = String(laneFromArgv ?? args.lane ?? "").trim();
14998
+ if (lane !== "landing-maintainer") {
14999
+ console.error(`unknown lane: ${lane || "(none)"}`);
15000
+ process.exit(1);
15001
+ }
15002
+ const result = await runLandingMaintainerLaneTick(args);
15003
+ if (args.json === true || args.json === "true") {
15004
+ console.log(JSON.stringify(result, null, 2));
15005
+ return;
15006
+ }
15007
+ console.log(
15008
+ [
15009
+ `fleet landing-maintainer tick`,
15010
+ `repo=${result.repo}`,
15011
+ `fleet=${result.fleet}`,
15012
+ `execute=${result.execute}`,
15013
+ `localOutcomes=${result.localOutcomes.length}`
15014
+ ].join(" ")
15015
+ );
15016
+ for (const row of result.localOutcomes) {
15017
+ console.log(` ${row.action.kind} pr=${row.action.prNumber ?? "-"} executed=${row.executed}`);
15018
+ }
15019
+ }
15020
+
14608
15021
  // src/cli.ts
14609
15022
  function isHelpFlag(arg) {
14610
15023
  return arg === "help" || arg === "--help" || arg === "-h";
@@ -14625,6 +15038,7 @@ function usage(code = 0) {
14625
15038
  " kynver daemon --run RUN_ID --agent-os-id AOS_ID [--execute] [--interval-ms MS]",
14626
15039
  " kynver run create [--repo /path/repo] [--name name] [--base origin/main]",
14627
15040
  " kynver run list",
15041
+ " kynver run resolve --name RUN_NAME",
14628
15042
  " kynver run status --run RUN_ID [--json] [--compact]",
14629
15043
  " kynver run dispatch --run RUN_ID --agent-os-id AOS_ID [--base-url URL] [--secret SECRET] [--execute] [--lane any|implementation|review|landing] [--target-task-id TASK_ID] [--executor harness] [--max-starts 1] [--lease-ms MS] [--owned path[,path]] [--model claude-opus-4-8] [--disk-path /] [--reconcile-stale-blockers]",
14630
15044
  " kynver run sweep --run RUN_ID --agent-os-id AOS_ID [--base-url URL] [--secret SECRET] [--grace-ms MS]",
@@ -14660,6 +15074,7 @@ function usage(code = 0) {
14660
15074
  " kynver scheduler attest-cutover [--json]",
14661
15075
  " kynver cron status [--json]",
14662
15076
  " kynver cron tick [--agent-os-id AOS_ID] [--json]",
15077
+ " kynver lane tick landing-maintainer [--fleet] [--repo OWNER/NAME] [--agent-os-id AOS_ID] [--execute] [--json]",
14663
15078
  " kynver board contract [--agent-os-id ID] [--base-url URL] [--since ISO] [--limit N]"
14664
15079
  ].join("\n")
14665
15080
  );
@@ -14671,7 +15086,7 @@ async function main(argv = process.argv.slice(2)) {
14671
15086
  const scope = argv.shift();
14672
15087
  let action;
14673
15088
  let rest;
14674
- if (scope === "run" || scope === "worker" || scope === "plan" || scope === "runner" || scope === "harness" || scope === "monitor" || scope === "doctor" || scope === "config" || scope === "scheduler" || scope === "cron" || scope === "board") {
15089
+ if (scope === "run" || scope === "worker" || scope === "plan" || scope === "runner" || scope === "harness" || scope === "monitor" || scope === "doctor" || scope === "config" || scope === "scheduler" || scope === "cron" || scope === "lane" || scope === "board") {
14675
15090
  action = argv.shift();
14676
15091
  rest = argv;
14677
15092
  } else {
@@ -14728,11 +15143,16 @@ async function main(argv = process.argv.slice(2)) {
14728
15143
  if (scope === "cron" && action === "tick") {
14729
15144
  return void await runCronTickCli(args);
14730
15145
  }
15146
+ if (scope === "lane" && action === "tick") {
15147
+ const laneName = rest.shift();
15148
+ return void await runLaneTickCli(parseArgs(rest), laneName);
15149
+ }
14731
15150
  if (scope === "board" && action === "contract") {
14732
15151
  return void await runCommandCenterContractCli(args);
14733
15152
  }
14734
15153
  if (scope === "run" && action === "create") return createRun(args);
14735
15154
  if (scope === "run" && action === "list") return listRuns();
15155
+ if (scope === "run" && action === "resolve") return resolveHarnessRunCli(args);
14736
15156
  if (scope === "run" && action === "status") return runStatus(args);
14737
15157
  if (scope === "run" && action === "dispatch") return void await dispatchRun(args);
14738
15158
  if (scope === "run" && action === "sweep") return void await sweepRun(args);
@@ -15226,6 +15646,7 @@ export {
15226
15646
  hasNestedRunsSegment,
15227
15647
  hashPlanBody,
15228
15648
  hermesCodexProvider,
15649
+ isActiveHarnessWorker,
15229
15650
  isClaudeFamilyProvider,
15230
15651
  isDashboardVercelUrl,
15231
15652
  isEngagementRequiredSkip,
@@ -15289,6 +15710,7 @@ export {
15289
15710
  readMemAvailableBytes,
15290
15711
  readProductionDbKeysFromEnvFile,
15291
15712
  reclaimStaleHeavyVerificationSlots,
15713
+ reconcileLocalOnlyMergedPrAttention,
15292
15714
  reconcileRunsCli,
15293
15715
  reconcileStaleWorkers,
15294
15716
  reconcileWorkerMetadata,
@@ -15307,6 +15729,8 @@ export {
15307
15729
  resolveConfiguredWorkerProvider,
15308
15730
  resolveDefaultRepo,
15309
15731
  resolveHarnessRoot,
15732
+ resolveHarnessRunByName,
15733
+ resolveHarnessRunCli,
15310
15734
  resolveHeavyVerificationMaxConcurrent,
15311
15735
  resolveOpenAiCodexRetryBudget,
15312
15736
  resolveOrchestrationPolicyMode,