@kynver-app/runtime 0.1.99 → 0.1.103

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,
@@ -7341,6 +7367,11 @@ function readAdmissionExhaustion(result) {
7341
7367
  if (!raw || typeof raw !== "object") return null;
7342
7368
  return raw;
7343
7369
  }
7370
+ function readDispatchSkipDrain(result) {
7371
+ const raw = result.dispatchSkipDrain;
7372
+ if (!raw || typeof raw !== "object") return null;
7373
+ return raw;
7374
+ }
7344
7375
  function readHarnessWorkerContext(decision) {
7345
7376
  const raw = decision.harnessWorkerContext;
7346
7377
  if (!raw || typeof raw !== "object") return null;
@@ -7504,6 +7535,7 @@ async function dispatchRun(args) {
7504
7535
  const result = first.result;
7505
7536
  if (dryRun) {
7506
7537
  const admissionExhaustion2 = readAdmissionExhaustion(result);
7538
+ const dispatchSkipDrain2 = readDispatchSkipDrain(result);
7507
7539
  const summary2 = {
7508
7540
  runId: run.id,
7509
7541
  agentOsId,
@@ -7522,7 +7554,8 @@ async function dispatchRun(args) {
7522
7554
  pagesScanned: result.pagesScanned ?? null,
7523
7555
  candidatesExhausted: result.candidatesExhausted ?? null,
7524
7556
  capacityIdle: admissionExhaustion2?.capacityIdle === true,
7525
- admissionExhaustion: admissionExhaustion2
7557
+ admissionExhaustion: admissionExhaustion2,
7558
+ dispatchSkipDrain: dispatchSkipDrain2
7526
7559
  };
7527
7560
  if (pipeline) return { ok: true, ...summary2 };
7528
7561
  console.log(JSON.stringify(summary2, null, 2));
@@ -7578,7 +7611,7 @@ async function dispatchRun(args) {
7578
7611
  );
7579
7612
  }
7580
7613
  const attempt = Number(task.attempt) || 1;
7581
- if (attempt >= retryLimits.maxTaskAttempts) {
7614
+ if (attempt > retryLimits.maxTaskAttempts) {
7582
7615
  return abortClaimedSpawn(
7583
7616
  task,
7584
7617
  `task attempt ${attempt} exceeds KYNVER_MAX_TASK_ATTEMPTS (${retryLimits.maxTaskAttempts})`
@@ -7712,9 +7745,16 @@ async function dispatchRun(args) {
7712
7745
  }
7713
7746
  const startedCount = outcomes.filter((o) => o.started).length;
7714
7747
  const admissionExhaustion = readAdmissionExhaustion(result);
7715
- const capacityIdle = admissionExhaustion?.capacityIdle === true || startedCount === 0 && Number(result.resourceGate?.slotsAvailable) > 0;
7748
+ const dispatchSkipDrain = readDispatchSkipDrain(result);
7749
+ const capacityIdle = startedCount === 0 && (admissionExhaustion?.capacityIdle === true || Number(result.resourceGate?.slotsAvailable) > 0);
7716
7750
  if (capacityIdle && admissionExhaustion?.summary) {
7717
- console.error(`[dispatch] ${admissionExhaustion.summary}`);
7751
+ const retryCeiling = admissionExhaustion.skipReasonCounts?.retry_ceiling_exceeded ?? 0;
7752
+ const recovery = result.overAttemptIdleRecovery ?? admissionExhaustion.overAttemptIdleRecovery;
7753
+ const recoveryNote = recovery?.attempted === true ? `; over_attempt_recovery minted=${recovery.minted ?? 0} started=${recovery.started ?? 0}` : retryCeiling > 0 ? "; over_attempt_recovery not attempted" : "";
7754
+ const drainNote = dispatchSkipDrain ? `; dispatch_skip_drain scanned=${dispatchSkipDrain.scanned ?? 0} advanced=${dispatchSkipDrain.advanced ?? 0}` : "";
7755
+ console.error(
7756
+ `[dispatch] ${admissionExhaustion.summary}${retryCeiling > 0 ? `; retry_ceiling_exceeded=${retryCeiling}` : ""}${recoveryNote}${drainNote}`
7757
+ );
7718
7758
  }
7719
7759
  const summary = {
7720
7760
  runId: run.id,
@@ -7724,6 +7764,7 @@ async function dispatchRun(args) {
7724
7764
  startedCount,
7725
7765
  capacityIdle,
7726
7766
  admissionExhaustion,
7767
+ dispatchSkipDrain,
7727
7768
  outcomes,
7728
7769
  skipped: skipped.map((d) => ({
7729
7770
  taskId: d.task.id,
@@ -8299,14 +8340,14 @@ function applyProductionDatabaseToProcess(options = {}) {
8299
8340
 
8300
8341
  // src/worktree.ts
8301
8342
  import { existsSync as existsSync29, mkdirSync as mkdirSync6 } from "node:fs";
8302
- import path37 from "node:path";
8343
+ import path38 from "node:path";
8303
8344
 
8304
8345
  // src/run-list.ts
8305
8346
  import { existsSync as existsSync28, readFileSync as readFileSync15 } from "node:fs";
8306
- import path36 from "node:path";
8347
+ import path37 from "node:path";
8307
8348
 
8308
8349
  // src/stale-reconcile.ts
8309
- import path35 from "node:path";
8350
+ import path36 from "node:path";
8310
8351
 
8311
8352
  // src/finalize.ts
8312
8353
  import path30 from "node:path";
@@ -8941,6 +8982,193 @@ function reconcileWorkerMetadataCli() {
8941
8982
  );
8942
8983
  }
8943
8984
 
8985
+ // src/local-pr-attention-reconcile.ts
8986
+ import { execFileSync as execFileSync2 } from "node:child_process";
8987
+ import path35 from "node:path";
8988
+ function normalizePrUrl3(url) {
8989
+ const m = url.trim().match(/github\.com\/([^/]+\/[^/]+)\/(?:pull|pulls)\/(\d+)/i);
8990
+ if (!m) return null;
8991
+ return `https://github.com/${m[1]}/pull/${m[2]}`;
8992
+ }
8993
+ function prNumberFromUrl(url) {
8994
+ const m = normalizePrUrl3(url)?.match(/\/pull\/(\d+)$/);
8995
+ if (!m) return null;
8996
+ const n = Number.parseInt(m[1], 10);
8997
+ return Number.isFinite(n) ? n : null;
8998
+ }
8999
+ function extractText(value) {
9000
+ if (value == null) return "";
9001
+ if (typeof value === "string") return value;
9002
+ try {
9003
+ return JSON.stringify(value);
9004
+ } catch {
9005
+ return "";
9006
+ }
9007
+ }
9008
+ function extractPrNumbersFromText(text) {
9009
+ const out = /* @__PURE__ */ new Set();
9010
+ for (const match of text.matchAll(/github\.com\/[^/\s)>"']+\/[^/\s)>"']+\/(?:pull|pulls)\/(\d+)/gi)) {
9011
+ const n = Number.parseInt(match[1], 10);
9012
+ if (Number.isFinite(n)) out.add(n);
9013
+ }
9014
+ for (const match of text.matchAll(/\bPR\s*#?\s*(\d{2,})\b/gi)) {
9015
+ const n = Number.parseInt(match[1], 10);
9016
+ if (Number.isFinite(n)) out.add(n);
9017
+ }
9018
+ for (const match of text.matchAll(/\bpr[-_]?(\d{2,})\b/gi)) {
9019
+ const n = Number.parseInt(match[1], 10);
9020
+ if (Number.isFinite(n)) out.add(n);
9021
+ }
9022
+ return [...out];
9023
+ }
9024
+ function extractTargetPrReconciliation(value) {
9025
+ let record3 = null;
9026
+ if (typeof value === "string") record3 = extractEmbeddedWorkerFinalResultRecord(value);
9027
+ else if (value && typeof value === "object" && !Array.isArray(value)) record3 = value;
9028
+ if (!record3) return [];
9029
+ const raw = record3.targetPrReconciliation ?? record3.target_pr_reconciliation;
9030
+ if (!Array.isArray(raw)) return [];
9031
+ const out = [];
9032
+ for (const item of raw) {
9033
+ if (!item || typeof item !== "object" || Array.isArray(item)) continue;
9034
+ const row = item;
9035
+ const prUrl = normalizePrUrl3(String(row.prUrl ?? row.pr_url ?? ""));
9036
+ const outcome = String(row.outcome ?? "").trim();
9037
+ if (!prUrl || outcome !== "merged") continue;
9038
+ out.push({
9039
+ prUrl,
9040
+ mergeCommit: typeof row.mergeCommit === "string" ? row.mergeCommit : typeof row.merge_commit === "string" ? row.merge_commit : null,
9041
+ reason: typeof row.reason === "string" ? row.reason : null
9042
+ });
9043
+ }
9044
+ return out;
9045
+ }
9046
+ function defaultLookupPr(input) {
9047
+ try {
9048
+ const repo = execFileSync2("git", ["config", "--get", "remote.origin.url"], {
9049
+ cwd: input.repoDir,
9050
+ encoding: "utf8",
9051
+ stdio: ["ignore", "pipe", "ignore"]
9052
+ }).trim();
9053
+ const repoMatch = repo.match(/github\.com[:/]([^/]+\/[^/.]+)(?:\.git)?$/i);
9054
+ const repoSlug = repoMatch?.[1];
9055
+ if (!repoSlug) return null;
9056
+ const raw = execFileSync2(
9057
+ "gh",
9058
+ ["pr", "view", String(input.prNumber), "--repo", repoSlug, "--json", "state,mergedAt,mergeCommit,url"],
9059
+ { cwd: input.repoDir, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
9060
+ );
9061
+ const parsed = JSON.parse(raw);
9062
+ return {
9063
+ prUrl: normalizePrUrl3(parsed.url ?? `https://github.com/${repoSlug}/pull/${input.prNumber}`) ?? `https://github.com/${repoSlug}/pull/${input.prNumber}`,
9064
+ state: parsed.state ?? "",
9065
+ mergedAt: parsed.mergedAt ?? null,
9066
+ mergeCommit: parsed.mergeCommit?.oid ?? null
9067
+ };
9068
+ } catch {
9069
+ return null;
9070
+ }
9071
+ }
9072
+ function finalResultForWorker(worker) {
9073
+ const heartbeat = parseHeartbeat(worker.heartbeatPath);
9074
+ return worker.completionSnapshot?.finalResult ?? terminalFinalResultFromHeartbeat(heartbeat);
9075
+ }
9076
+ function collectMergedEvidenceByPr(run) {
9077
+ const byPr = /* @__PURE__ */ new Map();
9078
+ const runDir2 = runDirectory(run.id);
9079
+ for (const name of listRunWorkerNames(run)) {
9080
+ const worker = readJson(
9081
+ path35.join(runDir2, "workers", safeSlug(name), "worker.json"),
9082
+ void 0
9083
+ );
9084
+ if (!worker) continue;
9085
+ for (const evidence of extractTargetPrReconciliation(finalResultForWorker(worker))) {
9086
+ const n = prNumberFromUrl(evidence.prUrl);
9087
+ if (n != null && !byPr.has(n)) byPr.set(n, evidence);
9088
+ }
9089
+ }
9090
+ return byPr;
9091
+ }
9092
+ function candidatePrNumbers(worker, statusFinalResult) {
9093
+ const heartbeat = parseHeartbeat(worker.heartbeatPath);
9094
+ const text = [
9095
+ worker.name,
9096
+ worker.repairTargetPrUrl,
9097
+ worker.taskPrUrl,
9098
+ worker.branch,
9099
+ heartbeat.lastHeartbeatSummary,
9100
+ extractText(statusFinalResult)
9101
+ ].filter(Boolean).join("\n");
9102
+ return extractPrNumbersFromText(text);
9103
+ }
9104
+ function isLocalOnlyAttentionNoise(worker) {
9105
+ return worker.localOnly === true && !worker.taskId && !worker.agentOsId;
9106
+ }
9107
+ function reconcileLocalOnlyMergedPrAttention(options = {}) {
9108
+ const lookupPr = options.lookupPr ?? defaultLookupPr;
9109
+ const outcomes = [];
9110
+ const liveLookupCache = /* @__PURE__ */ new Map();
9111
+ for (const run of listRunRecords()) {
9112
+ const mergedByPr = collectMergedEvidenceByPr(run);
9113
+ const runDir2 = runDirectory(run.id);
9114
+ for (const name of listRunWorkerNames(run)) {
9115
+ const workerPath = path35.join(runDir2, "workers", safeSlug(name), "worker.json");
9116
+ const worker = readJson(workerPath, void 0);
9117
+ if (!worker || !isLocalOnlyAttentionNoise(worker)) continue;
9118
+ const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
9119
+ if (status.attention.state !== "needs_attention") continue;
9120
+ let merged = null;
9121
+ for (const prNumber of candidatePrNumbers(worker, status.finalResult)) {
9122
+ merged = mergedByPr.get(prNumber) ?? null;
9123
+ if (!merged) {
9124
+ const repoDir = run.repo || worker.worktreePath;
9125
+ const cacheKey = `${repoDir}#${prNumber}`;
9126
+ const live = liveLookupCache.has(cacheKey) ? liveLookupCache.get(cacheKey) ?? null : lookupPr({ repoDir, prNumber });
9127
+ if (!liveLookupCache.has(cacheKey)) liveLookupCache.set(cacheKey, live);
9128
+ if (live && (live.state === "MERGED" || live.mergedAt)) {
9129
+ merged = { prUrl: live.prUrl, mergeCommit: live.mergeCommit ?? null, reason: "GitHub reports PR merged" };
9130
+ }
9131
+ }
9132
+ if (merged) break;
9133
+ }
9134
+ if (!merged) {
9135
+ outcomes.push({ runId: run.id, worker: name, action: "skipped", reason: "no merged PR evidence" });
9136
+ continue;
9137
+ }
9138
+ const now = (/* @__PURE__ */ new Date()).toISOString();
9139
+ worker.status = "done";
9140
+ worker.completionSnapshot = {
9141
+ prUrl: merged.prUrl,
9142
+ summary: `Local-only worker superseded by merged PR ${merged.prUrl}`,
9143
+ finalResult: {
9144
+ summary: `Local-only repair/salvage worker superseded by merged PR ${merged.prUrl}`,
9145
+ targetPrReconciliation: [
9146
+ {
9147
+ prUrl: merged.prUrl,
9148
+ outcome: "merged",
9149
+ mergeCommit: merged.mergeCommit ?? null,
9150
+ reason: merged.reason ?? "PR already merged; local-only worker no longer requires attention"
9151
+ }
9152
+ ]
9153
+ }
9154
+ };
9155
+ worker.completionAckSource = "local-pr-merged-reconcile";
9156
+ worker.reconciledAt = now;
9157
+ worker.reconcileReason = "local-only needs_attention superseded by merged PR";
9158
+ saveWorker(run.id, worker);
9159
+ outcomes.push({
9160
+ runId: run.id,
9161
+ worker: name,
9162
+ action: "marked_done",
9163
+ reason: worker.reconcileReason,
9164
+ prUrl: merged.prUrl,
9165
+ mergeCommit: merged.mergeCommit ?? null
9166
+ });
9167
+ }
9168
+ }
9169
+ return { workers: outcomes };
9170
+ }
9171
+
8944
9172
  // src/stale-reconcile.ts
8945
9173
  var STALE_RECONCILE_HEARTBEAT_MS = 15 * 60 * 1e3;
8946
9174
  function staleReconcileDisabled() {
@@ -8949,13 +9177,14 @@ function staleReconcileDisabled() {
8949
9177
  function reconcileStaleWorkers() {
8950
9178
  const metadataReconcile = reconcileWorkerMetadata();
8951
9179
  if (staleReconcileDisabled()) {
8952
- return { workers: [], finalizedRuns: finalizeStaleRuns(), metadataReconcile };
9180
+ const localPrAttentionReconcile2 = reconcileLocalOnlyMergedPrAttention();
9181
+ return { workers: [], finalizedRuns: finalizeStaleRuns(), metadataReconcile, localPrAttentionReconcile: localPrAttentionReconcile2 };
8953
9182
  }
8954
9183
  const outcomes = [];
8955
9184
  const now = Date.now();
8956
9185
  for (const run of listRunRecords()) {
8957
9186
  for (const name of listRunWorkerNames(run)) {
8958
- const workerPath = path35.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
9187
+ const workerPath = path36.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
8959
9188
  const worker = readJson(workerPath, void 0);
8960
9189
  if (!worker || worker.status !== "running") {
8961
9190
  outcomes.push({
@@ -9027,7 +9256,8 @@ function reconcileStaleWorkers() {
9027
9256
  });
9028
9257
  }
9029
9258
  }
9030
- return { workers: outcomes, finalizedRuns: finalizeStaleRuns(), metadataReconcile };
9259
+ const localPrAttentionReconcile = reconcileLocalOnlyMergedPrAttention();
9260
+ return { workers: outcomes, finalizedRuns: finalizeStaleRuns(), metadataReconcile, localPrAttentionReconcile };
9031
9261
  }
9032
9262
  function reconcileRunsCli() {
9033
9263
  const result = reconcileStaleWorkers();
@@ -9038,6 +9268,10 @@ function reconcileRunsCli() {
9038
9268
  acc[row.action] = (acc[row.action] ?? 0) + 1;
9039
9269
  return acc;
9040
9270
  }, {});
9271
+ const localPrAttentionTotals = result.localPrAttentionReconcile.workers.reduce((acc, row) => {
9272
+ acc[row.action] = (acc[row.action] ?? 0) + 1;
9273
+ return acc;
9274
+ }, {});
9041
9275
  const runRetentionTotals = result.metadataReconcile.runMetadataRetention.runs.reduce((acc, row) => {
9042
9276
  acc[row.action] = (acc[row.action] ?? 0) + 1;
9043
9277
  return acc;
@@ -9055,10 +9289,15 @@ function reconcileRunsCli() {
9055
9289
  total: result.metadataReconcile.runMetadataRetention.runs.length
9056
9290
  }
9057
9291
  },
9292
+ localPrAttentionReconcile: {
9293
+ totals: localPrAttentionTotals,
9294
+ total: result.localPrAttentionReconcile.workers.length
9295
+ },
9058
9296
  finalizedRuns: result.finalizedRuns.length,
9059
9297
  details: {
9060
9298
  workers: result.workers,
9061
9299
  metadataReconcile: result.metadataReconcile.workers,
9300
+ localPrAttentionReconcile: result.localPrAttentionReconcile.workers,
9062
9301
  runMetadataRetention: result.metadataReconcile.runMetadataRetention.runs,
9063
9302
  finalizedRuns: result.finalizedRuns
9064
9303
  }
@@ -9079,7 +9318,7 @@ function heartbeatByteLength(heartbeatPath) {
9079
9318
  }
9080
9319
  }
9081
9320
  function workerEvidence(run, workerName) {
9082
- const workerPath = path36.join(runDirectory(run.id), "workers", safeSlug(workerName), "worker.json");
9321
+ const workerPath = path37.join(runDirectory(run.id), "workers", safeSlug(workerName), "worker.json");
9083
9322
  const worker = readJson(workerPath, void 0);
9084
9323
  if (!worker) {
9085
9324
  return {
@@ -9136,7 +9375,7 @@ function aggregateRunAttention(workers) {
9136
9375
  function countOpenWorkers(run) {
9137
9376
  let open = 0;
9138
9377
  for (const name of listRunWorkerNames(run)) {
9139
- const workerPath = path36.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
9378
+ const workerPath = path37.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
9140
9379
  const worker = readJson(workerPath, void 0);
9141
9380
  if (!worker) continue;
9142
9381
  const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
@@ -9212,7 +9451,7 @@ function createRun(args) {
9212
9451
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
9213
9452
  workers: {}
9214
9453
  };
9215
- writeJson(path37.join(dir, "run.json"), run);
9454
+ writeJson(path38.join(dir, "run.json"), run);
9216
9455
  console.log(JSON.stringify({ runId: id, runDir: dir, repo, base, baseCommit }, null, 2));
9217
9456
  }
9218
9457
  function listRuns() {
@@ -9223,8 +9462,32 @@ function failExists(message) {
9223
9462
  process.exit(1);
9224
9463
  }
9225
9464
 
9465
+ // src/run-resolve.ts
9466
+ function resolveHarnessRunByName(runName) {
9467
+ const name = runName.trim();
9468
+ if (!name) return null;
9469
+ const rows = buildRunListRows();
9470
+ return rows.find((row) => row.name === name && row.status !== "completed") ?? null;
9471
+ }
9472
+ function resolveHarnessRunCli(args) {
9473
+ const name = String(required(String(args.name || ""), "--name"));
9474
+ const hit = resolveHarnessRunByName(name);
9475
+ console.log(
9476
+ JSON.stringify(
9477
+ {
9478
+ runId: hit?.id ?? null,
9479
+ name,
9480
+ status: hit?.status ?? null,
9481
+ effectiveStatus: hit?.effectiveStatus ?? null
9482
+ },
9483
+ null,
9484
+ 2
9485
+ )
9486
+ );
9487
+ }
9488
+
9226
9489
  // src/sweep.ts
9227
- import path38 from "node:path";
9490
+ import path39 from "node:path";
9228
9491
  async function sweepRun(args) {
9229
9492
  const pipeline = args.pipeline === true || args.pipeline === "true";
9230
9493
  try {
@@ -9237,7 +9500,7 @@ async function sweepRun(args) {
9237
9500
  const releasedLocalOrphans = [];
9238
9501
  for (const name of Object.keys(run.workers || {})) {
9239
9502
  const worker = readJson(
9240
- path38.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
9503
+ path39.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
9241
9504
  void 0
9242
9505
  );
9243
9506
  if (!worker || !worker.dispatched || !worker.taskId) continue;
@@ -9286,17 +9549,17 @@ async function sweepRun(args) {
9286
9549
 
9287
9550
  // src/harness-storage-snapshot.ts
9288
9551
  import { existsSync as existsSync31, readdirSync as readdirSync9, statSync as statSync7 } from "node:fs";
9289
- import path40 from "node:path";
9552
+ import path41 from "node:path";
9290
9553
 
9291
9554
  // src/cleanup-dir-size.ts
9292
- import { execFileSync as execFileSync2 } from "node:child_process";
9555
+ import { execFileSync as execFileSync3 } from "node:child_process";
9293
9556
  import { existsSync as existsSync30, readdirSync as readdirSync8, statSync as statSync6 } from "node:fs";
9294
- import path39 from "node:path";
9557
+ import path40 from "node:path";
9295
9558
  var DEFAULT_DU_TIMEOUT_MS = 2500;
9296
9559
  function directorySizeBytesDu(root, timeoutMs = DEFAULT_DU_TIMEOUT_MS) {
9297
9560
  if (!existsSync30(root)) return 0;
9298
9561
  try {
9299
- const out = execFileSync2("du", ["-sb", root], {
9562
+ const out = execFileSync3("du", ["-sb", root], {
9300
9563
  encoding: "utf8",
9301
9564
  timeout: timeoutMs,
9302
9565
  stdio: ["ignore", "pipe", "ignore"]
@@ -9325,7 +9588,7 @@ function directorySizeBytes(root, maxEntries = 5e4) {
9325
9588
  }
9326
9589
  for (const name of entries) {
9327
9590
  if (seen++ > maxEntries) return null;
9328
- const full = path39.join(current, name);
9591
+ const full = path40.join(current, name);
9329
9592
  let st;
9330
9593
  try {
9331
9594
  st = statSync6(full);
@@ -9377,7 +9640,7 @@ function harnessStorageSnapshot(opts = {}) {
9377
9640
  for (const runEntry of entries) {
9378
9641
  if (!runEntry.isDirectory()) continue;
9379
9642
  runCount += 1;
9380
- const runPath = path40.join(worktreesDir, runEntry.name);
9643
+ const runPath = path41.join(worktreesDir, runEntry.name);
9381
9644
  try {
9382
9645
  const st = statSync7(runPath);
9383
9646
  oldestMs = oldestMs === null ? st.mtimeMs : Math.min(oldestMs, st.mtimeMs);
@@ -9412,10 +9675,10 @@ function harnessStorageSnapshot(opts = {}) {
9412
9675
  }
9413
9676
 
9414
9677
  // src/cleanup.ts
9415
- import path52 from "node:path";
9678
+ import path53 from "node:path";
9416
9679
 
9417
9680
  // src/cleanup-guards.ts
9418
- import path41 from "node:path";
9681
+ import path42 from "node:path";
9419
9682
 
9420
9683
  // src/cleanup-build-cache-paths.ts
9421
9684
  var HARNESS_BUILD_CACHE_RELATIVE_PATHS = [
@@ -9556,7 +9819,7 @@ function skipWorktreeRemoval(input) {
9556
9819
  function skipDependencyCacheRemoval(input) {
9557
9820
  const { indexed, nodeModulesAgeMs, ageMs, worktreePath, activeWorktreePaths, diskPressure } = input;
9558
9821
  if (!diskPressure && ageMs < nodeModulesAgeMs) return "below_age_threshold";
9559
- if (activeWorktreePaths.has(path41.resolve(worktreePath))) return "active_worker";
9822
+ if (activeWorktreePaths.has(path42.resolve(worktreePath))) return "active_worker";
9560
9823
  if (indexed && isWorkerProcessLive(indexed)) return "active_worker";
9561
9824
  if (indexed && indexedWorktreeHasMaterialChanges(indexed)) return "dirty_worktree";
9562
9825
  return null;
@@ -9583,11 +9846,11 @@ var LIVE_SKIP_REASONS = /* @__PURE__ */ new Set([
9583
9846
  function collectPreservedLivePaths(actions, skips) {
9584
9847
  const out = [];
9585
9848
  const seen = /* @__PURE__ */ new Set();
9586
- const push = (path71, reason, detail) => {
9587
- const key = `${path71}\0${reason}`;
9849
+ const push = (path73, reason, detail) => {
9850
+ const key = `${path73}\0${reason}`;
9588
9851
  if (seen.has(key) || out.length >= MAX_PRESERVED_LIVE_PATH_SAMPLES) return;
9589
9852
  seen.add(key);
9590
- out.push({ path: path71, reason, ...detail ? { detail } : {} });
9853
+ out.push({ path: path73, reason, ...detail ? { detail } : {} });
9591
9854
  };
9592
9855
  for (const skip2 of skips) {
9593
9856
  if (!LIVE_SKIP_REASONS.has(skip2.reason)) continue;
@@ -9603,11 +9866,11 @@ function collectPreservedLivePaths(actions, skips) {
9603
9866
 
9604
9867
  // src/cleanup-run-directory.ts
9605
9868
  import { existsSync as existsSync33, readdirSync as readdirSync11, statSync as statSync9 } from "node:fs";
9606
- import path43 from "node:path";
9869
+ import path44 from "node:path";
9607
9870
 
9608
9871
  // src/cleanup-active-worktrees.ts
9609
9872
  import { existsSync as existsSync32, readdirSync as readdirSync10, statSync as statSync8 } from "node:fs";
9610
- import path42 from "node:path";
9873
+ import path43 from "node:path";
9611
9874
  function workerHasRecentHarnessActivity(worker, now) {
9612
9875
  const paths = [worker.heartbeatPath, worker.stdoutPath, worker.stderrPath];
9613
9876
  for (const target of paths) {
@@ -9633,11 +9896,11 @@ function collectActiveWorktreeGuards(harnessRoots, now = Date.now()) {
9633
9896
  let runHasLive = false;
9634
9897
  for (const name of Object.keys(run.workers || {})) {
9635
9898
  const worker = readJson(
9636
- path42.join(runDirectoryAt(harnessRoot, run.id), "workers", safeSlug(name), "worker.json"),
9899
+ path43.join(runDirectoryAt(harnessRoot, run.id), "workers", safeSlug(name), "worker.json"),
9637
9900
  void 0
9638
9901
  );
9639
9902
  if (!worker?.worktreePath) continue;
9640
- const worktreePath = path42.resolve(worker.worktreePath);
9903
+ const worktreePath = path43.resolve(worker.worktreePath);
9641
9904
  if (!isActiveHarnessWorker2(worker, now)) continue;
9642
9905
  runHasLive = true;
9643
9906
  activeWorktreePaths.add(worktreePath);
@@ -9665,7 +9928,7 @@ function pathAgeMs(target, now) {
9665
9928
  }
9666
9929
  }
9667
9930
  function loadRunStatus(harnessRoot, runId) {
9668
- const runPath = path43.join(harnessRoot, "runs", runId, "run.json");
9931
+ const runPath = path44.join(harnessRoot, "runs", runId, "run.json");
9669
9932
  if (!existsSync33(runPath)) return null;
9670
9933
  return readJson(runPath, null);
9671
9934
  }
@@ -9701,7 +9964,7 @@ function scanStaleRunDirectoryCandidates(opts) {
9701
9964
  if (!runEntry.isDirectory()) continue;
9702
9965
  const runId = runEntry.name;
9703
9966
  if (opts.runIdFilter && runId !== opts.runIdFilter) continue;
9704
- const runPath = path43.join(opts.worktreesDir, runId);
9967
+ const runPath = path44.join(opts.worktreesDir, runId);
9705
9968
  if (!runDirectoryIsEmpty(runPath)) continue;
9706
9969
  candidates.push({
9707
9970
  kind: "remove_run_directory",
@@ -9755,20 +10018,20 @@ function pathHasForeignOwnedEntry(targetPath, maxEntries = 32) {
9755
10018
 
9756
10019
  // src/cleanup-privileged-remove.ts
9757
10020
  import { spawnSync as spawnSync5 } from "node:child_process";
9758
- import path45 from "node:path";
10021
+ import path46 from "node:path";
9759
10022
 
9760
10023
  // src/cleanup-harness-path-validate.ts
9761
- import path44 from "node:path";
10024
+ import path45 from "node:path";
9762
10025
  function isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, cacheDirName) {
9763
- const resolved = path44.resolve(targetPath);
9764
- const suffix = `${path44.sep}${cacheDirName}`;
10026
+ const resolved = path45.resolve(targetPath);
10027
+ const suffix = `${path45.sep}${cacheDirName}`;
9765
10028
  const cachePath = resolved.endsWith(suffix) ? resolved : null;
9766
10029
  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);
10030
+ const rel = path45.relative(worktreesDir, cachePath);
10031
+ if (rel.startsWith("..") || path45.isAbsolute(rel)) return "path_outside_harness";
10032
+ const parts = rel.split(path45.sep);
9770
10033
  if (parts.length < 3 || parts[parts.length - 1] !== cacheDirName) return "path_outside_harness";
9771
- if (!resolved.startsWith(path44.resolve(harnessRoot))) return "path_outside_harness";
10034
+ if (!resolved.startsWith(path45.resolve(harnessRoot))) return "path_outside_harness";
9772
10035
  return null;
9773
10036
  }
9774
10037
  function isHarnessNodeModulesPath(targetPath, harnessRoot, worktreesDir) {
@@ -9778,16 +10041,16 @@ function isHarnessNextCachePath(targetPath, harnessRoot, worktreesDir) {
9778
10041
  return isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, ".next");
9779
10042
  }
9780
10043
  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);
10044
+ const resolved = path45.resolve(targetPath);
10045
+ const relToWt = path45.relative(worktreesDir, resolved);
10046
+ if (relToWt.startsWith("..") || path45.isAbsolute(relToWt)) return "path_outside_harness";
10047
+ const parts = relToWt.split(path45.sep);
9785
10048
  if (parts.length < 3) return "path_outside_harness";
9786
- if (!resolved.startsWith(path44.resolve(harnessRoot))) return "path_outside_harness";
10049
+ if (!resolved.startsWith(path45.resolve(harnessRoot))) return "path_outside_harness";
9787
10050
  return null;
9788
10051
  }
9789
10052
  function isHarnessGeneratedCachePath(targetPath, harnessRoot, worktreesDir) {
9790
- const resolved = path44.resolve(targetPath);
10053
+ const resolved = path45.resolve(targetPath);
9791
10054
  return isHarnessNodeModulesPath(resolved, harnessRoot, worktreesDir) === null || isHarnessNextCachePath(resolved, harnessRoot, worktreesDir) === null || isHarnessBuildCachePath(resolved, harnessRoot, worktreesDir) === null;
9792
10055
  }
9793
10056
 
@@ -9821,12 +10084,12 @@ function tryPrivilegedReclaimHarnessCache(targetPath, harnessRoot, worktreesDir)
9821
10084
  "chown",
9822
10085
  "-R",
9823
10086
  `${effectiveUid}:${effectiveGid}`,
9824
- path45.resolve(targetPath)
10087
+ path46.resolve(targetPath)
9825
10088
  ]);
9826
10089
  if (chown.ok) {
9827
10090
  return { ok: true, method: "chown_then_rm" };
9828
10091
  }
9829
- const rm = runSudoNonInteractive(["rm", "-rf", path45.resolve(targetPath)]);
10092
+ const rm = runSudoNonInteractive(["rm", "-rf", path46.resolve(targetPath)]);
9830
10093
  if (rm.ok) {
9831
10094
  return { ok: true, method: "sudo_rm" };
9832
10095
  }
@@ -10028,7 +10291,7 @@ function removeWorktree(candidate, execute) {
10028
10291
 
10029
10292
  // src/cleanup-scan.ts
10030
10293
  import { existsSync as existsSync36, readdirSync as readdirSync13, statSync as statSync10 } from "node:fs";
10031
- import path46 from "node:path";
10294
+ import path47 from "node:path";
10032
10295
  function pathAgeMs2(target, now) {
10033
10296
  try {
10034
10297
  const mtime = statSync10(target).mtimeMs;
@@ -10038,16 +10301,16 @@ function pathAgeMs2(target, now) {
10038
10301
  }
10039
10302
  }
10040
10303
  function isPathInside(child, parent) {
10041
- const rel = path46.relative(parent, child);
10042
- return rel === "" || !rel.startsWith("..") && !path46.isAbsolute(rel);
10304
+ const rel = path47.relative(parent, child);
10305
+ return rel === "" || !rel.startsWith("..") && !path47.isAbsolute(rel);
10043
10306
  }
10044
10307
  function collectBuildCacheForWorktree(worktreePath, opts, seen, meta) {
10045
10308
  const out = [];
10046
10309
  for (const rel of HARNESS_BUILD_CACHE_RELATIVE_PATHS) {
10047
10310
  if (rel === ".next") continue;
10048
- const target = path46.join(worktreePath, rel);
10311
+ const target = path47.join(worktreePath, rel);
10049
10312
  if (!existsSync36(target)) continue;
10050
- const resolved = path46.resolve(target);
10313
+ const resolved = path47.resolve(target);
10051
10314
  if (seen.has(resolved)) continue;
10052
10315
  if (!isPathInside(resolved, opts.harnessRoot)) continue;
10053
10316
  seen.add(resolved);
@@ -10079,10 +10342,10 @@ function scanBuildCacheCandidates(opts) {
10079
10342
  if (!opts.includeOrphans || !existsSync36(opts.worktreesDir)) return candidates;
10080
10343
  for (const runEntry of readdirSync13(opts.worktreesDir, { withFileTypes: true })) {
10081
10344
  if (!runEntry.isDirectory()) continue;
10082
- const runPath = path46.join(opts.worktreesDir, runEntry.name);
10345
+ const runPath = path47.join(opts.worktreesDir, runEntry.name);
10083
10346
  for (const workerEntry of readdirSync13(runPath, { withFileTypes: true })) {
10084
10347
  if (!workerEntry.isDirectory()) continue;
10085
- const worktreePath = path46.join(runPath, workerEntry.name);
10348
+ const worktreePath = path47.join(runPath, workerEntry.name);
10086
10349
  candidates.push(
10087
10350
  ...collectBuildCacheForWorktree(worktreePath, opts, seen, {
10088
10351
  runId: runEntry.name,
@@ -10120,12 +10383,12 @@ function scanWorktreeCandidates(opts) {
10120
10383
  if (!orphanEnabled || !existsSync36(opts.worktreesDir)) return candidates;
10121
10384
  const indexedPaths = /* @__PURE__ */ new Set();
10122
10385
  for (const entry of opts.index.values()) {
10123
- indexedPaths.add(path46.resolve(entry.worktreePath));
10386
+ indexedPaths.add(path47.resolve(entry.worktreePath));
10124
10387
  }
10125
10388
  for (const runEntry of readdirSync13(opts.worktreesDir, { withFileTypes: true })) {
10126
10389
  if (!runEntry.isDirectory()) continue;
10127
10390
  if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
10128
- const runPath = path46.join(opts.worktreesDir, runEntry.name);
10391
+ const runPath = path47.join(opts.worktreesDir, runEntry.name);
10129
10392
  let workerEntries;
10130
10393
  try {
10131
10394
  workerEntries = readdirSync13(runPath, { withFileTypes: true });
@@ -10134,7 +10397,7 @@ function scanWorktreeCandidates(opts) {
10134
10397
  }
10135
10398
  for (const workerEntry of workerEntries) {
10136
10399
  if (!workerEntry.isDirectory()) continue;
10137
- const worktreePath = path46.resolve(path46.join(runPath, workerEntry.name));
10400
+ const worktreePath = path47.resolve(path47.join(runPath, workerEntry.name));
10138
10401
  if (seen.has(worktreePath)) continue;
10139
10402
  if (indexedPaths.has(worktreePath)) continue;
10140
10403
  if (!isPathInside(worktreePath, opts.harnessRoot)) continue;
@@ -10154,7 +10417,7 @@ function scanWorktreeCandidates(opts) {
10154
10417
 
10155
10418
  // src/cleanup-dependency-scan.ts
10156
10419
  import { existsSync as existsSync37, readdirSync as readdirSync14, statSync as statSync11 } from "node:fs";
10157
- import path47 from "node:path";
10420
+ import path48 from "node:path";
10158
10421
  var DEPENDENCY_CACHE_DIRS = [
10159
10422
  { dirName: "node_modules", kind: "remove_node_modules" },
10160
10423
  { dirName: ".next", kind: "remove_next_cache" }
@@ -10168,12 +10431,12 @@ function pathAgeMs3(target, now) {
10168
10431
  }
10169
10432
  }
10170
10433
  function isPathInside2(child, parent) {
10171
- const rel = path47.relative(parent, child);
10172
- return rel === "" || !rel.startsWith("..") && !path47.isAbsolute(rel);
10434
+ const rel = path48.relative(parent, child);
10435
+ return rel === "" || !rel.startsWith("..") && !path48.isAbsolute(rel);
10173
10436
  }
10174
10437
  function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
10175
10438
  if (!existsSync37(targetPath)) return;
10176
- const resolved = path47.resolve(targetPath);
10439
+ const resolved = path48.resolve(targetPath);
10177
10440
  if (seen.has(resolved)) return;
10178
10441
  if (!isPathInside2(resolved, opts.harnessRoot)) return;
10179
10442
  seen.add(resolved);
@@ -10190,7 +10453,7 @@ function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
10190
10453
  }
10191
10454
  function scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, meta) {
10192
10455
  for (const entry of DEPENDENCY_CACHE_DIRS) {
10193
- pushCandidate2(candidates, seen, opts, path47.join(worktreePath, entry.dirName), entry.kind, meta);
10456
+ pushCandidate2(candidates, seen, opts, path48.join(worktreePath, entry.dirName), entry.kind, meta);
10194
10457
  }
10195
10458
  }
10196
10459
  function scanDependencyCacheCandidates(opts) {
@@ -10208,7 +10471,7 @@ function scanDependencyCacheCandidates(opts) {
10208
10471
  for (const runEntry of readdirSync14(opts.worktreesDir, { withFileTypes: true })) {
10209
10472
  if (!runEntry.isDirectory()) continue;
10210
10473
  if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
10211
- const runPath = path47.join(opts.worktreesDir, runEntry.name);
10474
+ const runPath = path48.join(opts.worktreesDir, runEntry.name);
10212
10475
  let workerEntries;
10213
10476
  try {
10214
10477
  workerEntries = readdirSync14(runPath, { withFileTypes: true });
@@ -10217,7 +10480,7 @@ function scanDependencyCacheCandidates(opts) {
10217
10480
  }
10218
10481
  for (const workerEntry of workerEntries) {
10219
10482
  if (!workerEntry.isDirectory()) continue;
10220
- const worktreePath = path47.join(runPath, workerEntry.name);
10483
+ const worktreePath = path48.join(runPath, workerEntry.name);
10221
10484
  scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, {
10222
10485
  runId: runEntry.name,
10223
10486
  worker: workerEntry.name
@@ -10229,7 +10492,7 @@ function scanDependencyCacheCandidates(opts) {
10229
10492
 
10230
10493
  // src/cleanup-duplicate-worktrees.ts
10231
10494
  import { existsSync as existsSync38, statSync as statSync12 } from "node:fs";
10232
- import path48 from "node:path";
10495
+ import path49 from "node:path";
10233
10496
  function pathAgeMs4(target, now) {
10234
10497
  try {
10235
10498
  const mtime = statSync12(target).mtimeMs;
@@ -10259,8 +10522,8 @@ function parseWorktreePorcelain(output) {
10259
10522
  return records;
10260
10523
  }
10261
10524
  function isUnderWorktreesDir(worktreePath, worktreesDir) {
10262
- const rel = path48.relative(path48.resolve(worktreesDir), path48.resolve(worktreePath));
10263
- return rel !== "" && !rel.startsWith("..") && !path48.isAbsolute(rel);
10525
+ const rel = path49.relative(path49.resolve(worktreesDir), path49.resolve(worktreePath));
10526
+ return rel !== "" && !rel.startsWith("..") && !path49.isAbsolute(rel);
10264
10527
  }
10265
10528
  function isCleanWorktree(worktreePath, repoRoot) {
10266
10529
  try {
@@ -10276,11 +10539,11 @@ function scanDuplicateWorktreeCandidates(opts) {
10276
10539
  if (!opts.includeOrphans || !existsSync38(opts.worktreesDir)) return [];
10277
10540
  const repos = /* @__PURE__ */ new Set();
10278
10541
  for (const entry of opts.index.values()) {
10279
- if (entry.run.repo) repos.add(path48.resolve(entry.run.repo));
10542
+ if (entry.run.repo) repos.add(path49.resolve(entry.run.repo));
10280
10543
  }
10281
10544
  const indexedPaths = /* @__PURE__ */ new Set();
10282
10545
  for (const entry of opts.index.values()) {
10283
- indexedPaths.add(path48.resolve(entry.worktreePath));
10546
+ indexedPaths.add(path49.resolve(entry.worktreePath));
10284
10547
  }
10285
10548
  const candidates = [];
10286
10549
  const seen = /* @__PURE__ */ new Set();
@@ -10293,15 +10556,15 @@ function scanDuplicateWorktreeCandidates(opts) {
10293
10556
  }
10294
10557
  const worktrees = parseWorktreePorcelain(porcelain);
10295
10558
  for (const wt of worktrees) {
10296
- const resolved = path48.resolve(wt.path);
10297
- if (resolved === path48.resolve(repoRoot)) continue;
10559
+ const resolved = path49.resolve(wt.path);
10560
+ if (resolved === path49.resolve(repoRoot)) continue;
10298
10561
  if (!isUnderWorktreesDir(resolved, opts.worktreesDir)) continue;
10299
10562
  if (indexedPaths.has(resolved)) continue;
10300
10563
  if (seen.has(resolved)) continue;
10301
10564
  if (!existsSync38(resolved)) continue;
10302
10565
  if (!isCleanWorktree(resolved, repoRoot)) continue;
10303
- const rel = path48.relative(opts.worktreesDir, resolved);
10304
- const parts = rel.split(path48.sep);
10566
+ const rel = path49.relative(opts.worktreesDir, resolved);
10567
+ const parts = rel.split(path49.sep);
10305
10568
  const runId = parts[0];
10306
10569
  const worker = parts[1] ?? "unknown";
10307
10570
  seen.add(resolved);
@@ -10320,12 +10583,12 @@ function scanDuplicateWorktreeCandidates(opts) {
10320
10583
  }
10321
10584
 
10322
10585
  // src/cleanup-worktree-index.ts
10323
- import path49 from "node:path";
10586
+ import path50 from "node:path";
10324
10587
  function buildWorktreeIndexAt(harnessRoot) {
10325
10588
  const index = /* @__PURE__ */ new Map();
10326
10589
  for (const run of listRunRecordsForHarnessRoot(harnessRoot)) {
10327
10590
  for (const name of Object.keys(run.workers || {})) {
10328
- const workerPath = path49.join(
10591
+ const workerPath = path50.join(
10329
10592
  runDirectoryAt(harnessRoot, run.id),
10330
10593
  "workers",
10331
10594
  safeSlug(name),
@@ -10333,9 +10596,9 @@ function buildWorktreeIndexAt(harnessRoot) {
10333
10596
  );
10334
10597
  const worker = readJson(workerPath, void 0);
10335
10598
  if (!worker?.worktreePath) continue;
10336
- index.set(path49.resolve(worker.worktreePath), {
10599
+ index.set(path50.resolve(worker.worktreePath), {
10337
10600
  harnessRoot,
10338
- worktreePath: path49.resolve(worker.worktreePath),
10601
+ worktreePath: path50.resolve(worker.worktreePath),
10339
10602
  runId: run.id,
10340
10603
  workerName: name,
10341
10604
  run,
@@ -10405,14 +10668,14 @@ function resolvePipelineHarnessRetention(runId) {
10405
10668
 
10406
10669
  // src/cleanup-orphan-safety.ts
10407
10670
  import { existsSync as existsSync39, statSync as statSync13 } from "node:fs";
10408
- import path50 from "node:path";
10671
+ import path51 from "node:path";
10409
10672
  var DEFAULT_HEARTBEAT_FRESH_MS = 30 * 60 * 1e3;
10410
10673
  function assessOrphanWorktreeSafety(input) {
10411
10674
  const now = input.now ?? Date.now();
10412
10675
  const heartbeatFreshMs = input.heartbeatFreshMs ?? DEFAULT_HEARTBEAT_FRESH_MS;
10413
10676
  if (!existsSync39(input.worktreePath)) return null;
10414
10677
  if (input.runId && input.workerName) {
10415
- const heartbeatPath = path50.join(
10678
+ const heartbeatPath = path51.join(
10416
10679
  input.harnessRoot,
10417
10680
  "runs",
10418
10681
  input.runId,
@@ -10426,7 +10689,7 @@ function assessOrphanWorktreeSafety(input) {
10426
10689
  } catch {
10427
10690
  }
10428
10691
  }
10429
- const gitDir = path50.join(input.worktreePath, ".git");
10692
+ const gitDir = path51.join(input.worktreePath, ".git");
10430
10693
  if (!existsSync39(gitDir)) return null;
10431
10694
  const porcelain = gitCapture(input.worktreePath, ["status", "--porcelain"]);
10432
10695
  if (porcelain.status !== 0) return "pr_or_unmerged_commits";
@@ -10458,10 +10721,10 @@ function assessOrphanWorktreeSafety(input) {
10458
10721
  // src/cleanup-harness-roots.ts
10459
10722
  import { existsSync as existsSync40 } from "node:fs";
10460
10723
  import { homedir as homedir13 } from "node:os";
10461
- import path51 from "node:path";
10724
+ import path52 from "node:path";
10462
10725
  var WELL_KNOWN_HARNESS_SCAN_ROOTS = [
10463
10726
  "/var/tmp/kynver-harness",
10464
- path51.join(homedir13(), ".openclaw", "harness")
10727
+ path52.join(homedir13(), ".openclaw", "harness")
10465
10728
  ];
10466
10729
  function addRoot(seen, roots, candidate) {
10467
10730
  if (!candidate?.trim()) return;
@@ -10483,7 +10746,7 @@ function resolveHarnessScanRoots(options = {}) {
10483
10746
  for (const candidate of extra ?? []) addRoot(seen, roots, candidate);
10484
10747
  if (shouldScanWellKnownRoots(options)) {
10485
10748
  for (const candidate of WELL_KNOWN_HARNESS_SCAN_ROOTS) {
10486
- const resolved = path51.resolve(candidate);
10749
+ const resolved = path52.resolve(candidate);
10487
10750
  if (!seen.has(resolved) && existsSync40(resolved)) addRoot(seen, roots, resolved);
10488
10751
  }
10489
10752
  }
@@ -10638,9 +10901,9 @@ function mergeWorktreeIndexes(scanRoots) {
10638
10901
  }
10639
10902
  function worktreePathForCandidate(candidate, worktreesDir) {
10640
10903
  if (candidate.runId && candidate.worker) {
10641
- return path52.join(worktreesDir, candidate.runId, candidate.worker);
10904
+ return path53.join(worktreesDir, candidate.runId, candidate.worker);
10642
10905
  }
10643
- return path52.resolve(candidate.path, "..");
10906
+ return path53.resolve(candidate.path, "..");
10644
10907
  }
10645
10908
  function runHarnessCleanup(options = {}) {
10646
10909
  let retention = resolveHarnessRetention(options);
@@ -10665,7 +10928,7 @@ function runHarnessCleanup(options = {}) {
10665
10928
  for (const harnessRoot of paths.scanRoots) {
10666
10929
  if (atSweepCap()) break;
10667
10930
  emitCleanupProgress("root", harnessRoot);
10668
- const worktreesDir = path52.join(harnessRoot, "worktrees");
10931
+ const worktreesDir = path53.join(harnessRoot, "worktrees");
10669
10932
  const scanOpts = {
10670
10933
  harnessRoot,
10671
10934
  worktreesDir,
@@ -10678,7 +10941,7 @@ function runHarnessCleanup(options = {}) {
10678
10941
  };
10679
10942
  for (const raw of scanDependencyCacheCandidates(scanOpts)) {
10680
10943
  if (atSweepCap()) break;
10681
- const resolved = path52.resolve(raw.path);
10944
+ const resolved = path53.resolve(raw.path);
10682
10945
  if (processedPaths.has(resolved)) continue;
10683
10946
  processedPaths.add(resolved);
10684
10947
  const candidate = { ...raw, path: resolved };
@@ -10689,7 +10952,7 @@ function runHarnessCleanup(options = {}) {
10689
10952
  continue;
10690
10953
  }
10691
10954
  const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
10692
- const indexed = index.get(path52.resolve(worktreePath)) ?? null;
10955
+ const indexed = index.get(path53.resolve(worktreePath)) ?? null;
10693
10956
  const guardReason = skipDependencyCacheRemoval({
10694
10957
  indexed,
10695
10958
  includeOrphans: true,
@@ -10713,7 +10976,7 @@ function runHarnessCleanup(options = {}) {
10713
10976
  }
10714
10977
  for (const raw of scanBuildCacheCandidates(scanOpts)) {
10715
10978
  if (atSweepCap()) break;
10716
- const resolved = path52.resolve(raw.path);
10979
+ const resolved = path53.resolve(raw.path);
10717
10980
  if (processedPaths.has(resolved)) continue;
10718
10981
  processedPaths.add(resolved);
10719
10982
  const candidate = { ...raw, path: resolved };
@@ -10724,7 +10987,7 @@ function runHarnessCleanup(options = {}) {
10724
10987
  continue;
10725
10988
  }
10726
10989
  const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
10727
- const indexed = index.get(path52.resolve(worktreePath)) ?? null;
10990
+ const indexed = index.get(path53.resolve(worktreePath)) ?? null;
10728
10991
  const guardReason = skipBuildCacheRemoval({
10729
10992
  indexed,
10730
10993
  includeOrphans: true,
@@ -10754,11 +11017,11 @@ function runHarnessCleanup(options = {}) {
10754
11017
  const worktreeSeen = /* @__PURE__ */ new Set();
10755
11018
  for (const raw of worktreeCandidates) {
10756
11019
  if (atSweepCap()) break;
10757
- const resolved = path52.resolve(raw.path);
11020
+ const resolved = path53.resolve(raw.path);
10758
11021
  if (worktreeSeen.has(resolved)) continue;
10759
11022
  worktreeSeen.add(resolved);
10760
11023
  const candidate = { ...raw, path: resolved };
10761
- const indexed = index.get(path52.resolve(candidate.path)) ?? null;
11024
+ const indexed = index.get(path53.resolve(candidate.path)) ?? null;
10762
11025
  const orphanSafety = indexed ? null : assessOrphanWorktreeSafety({
10763
11026
  worktreePath: candidate.path,
10764
11027
  harnessRoot,
@@ -10768,7 +11031,7 @@ function runHarnessCleanup(options = {}) {
10768
11031
  });
10769
11032
  const guardSkip = skipWorktreeRemoval({
10770
11033
  indexed,
10771
- worktreePath: path52.resolve(candidate.path),
11034
+ worktreePath: path53.resolve(candidate.path),
10772
11035
  includeOrphans: retention.includeOrphans,
10773
11036
  worktreesAgeMs: retention.worktreesAgeMs,
10774
11037
  terminalWorktreesAgeMs: retention.terminalWorktreesAgeMs,
@@ -10800,11 +11063,11 @@ function runHarnessCleanup(options = {}) {
10800
11063
  now: paths.now
10801
11064
  })) {
10802
11065
  if (atSweepCap()) break;
10803
- const resolved = path52.resolve(raw.path);
11066
+ const resolved = path53.resolve(raw.path);
10804
11067
  if (processedPaths.has(resolved)) continue;
10805
11068
  processedPaths.add(resolved);
10806
11069
  const candidate = { ...raw, path: resolved };
10807
- const runId = candidate.runId ?? path52.basename(resolved);
11070
+ const runId = candidate.runId ?? path53.basename(resolved);
10808
11071
  const dirSkip = skipRunDirectoryRemoval({
10809
11072
  harnessRoot,
10810
11073
  runId,
@@ -10943,7 +11206,7 @@ import { fileURLToPath as fileURLToPath5 } from "node:url";
10943
11206
 
10944
11207
  // src/discard-disposable.ts
10945
11208
  import { existsSync as existsSync41, rmSync as rmSync4 } from "node:fs";
10946
- import path53 from "node:path";
11209
+ import path54 from "node:path";
10947
11210
  function normalizeRelativePath2(value) {
10948
11211
  const normalized = value.replace(/\\/g, "/").replace(/^\.\//, "").trim();
10949
11212
  if (!normalized || normalized.startsWith("/") || normalized.includes("..")) {
@@ -10965,12 +11228,12 @@ function discardDisposableArtifacts(args) {
10965
11228
  if (paths.length === 0) {
10966
11229
  return { ok: false, removed: [], reason: "requires at least one --path" };
10967
11230
  }
10968
- const worktreeRoot = path53.resolve(worker.worktreePath);
11231
+ const worktreeRoot = path54.resolve(worker.worktreePath);
10969
11232
  const removed = [];
10970
11233
  for (const raw of paths) {
10971
11234
  const rel = normalizeRelativePath2(raw);
10972
- const abs = path53.resolve(worktreeRoot, rel);
10973
- if (!abs.startsWith(worktreeRoot + path53.sep) && abs !== worktreeRoot) {
11235
+ const abs = path54.resolve(worktreeRoot, rel);
11236
+ if (!abs.startsWith(worktreeRoot + path54.sep) && abs !== worktreeRoot) {
10974
11237
  return { ok: false, removed, reason: `path escapes worktree: ${raw}` };
10975
11238
  }
10976
11239
  if (!existsSync41(abs)) {
@@ -11037,7 +11300,7 @@ function validateDaemonInstallIdentity(config = loadUserConfig(), env = process.
11037
11300
  // src/cron/cron-env.ts
11038
11301
  import { existsSync as existsSync42 } from "node:fs";
11039
11302
  import { homedir as homedir14 } from "node:os";
11040
- import path54 from "node:path";
11303
+ import path55 from "node:path";
11041
11304
  function envFlag3(name, defaultValue) {
11042
11305
  const raw = process.env[name]?.trim().toLowerCase();
11043
11306
  if (!raw) return defaultValue;
@@ -11053,7 +11316,7 @@ function envInt(name, fallback, min = 1) {
11053
11316
  function defaultKynverCronStorePath() {
11054
11317
  const explicit = process.env.KYNVER_CRON_STORE_PATH?.trim() || process.env.OPENCLAW_CRON_STORE_PATH?.trim();
11055
11318
  if (explicit) return explicit;
11056
- return path54.join(homedir14(), ".kynver", "agent-os-cron.json");
11319
+ return path55.join(homedir14(), ".kynver", "agent-os-cron.json");
11057
11320
  }
11058
11321
  function defaultKynverCronStatePath(storePath = defaultKynverCronStorePath()) {
11059
11322
  const explicit = process.env.KYNVER_CRON_TICK_STATE_PATH?.trim();
@@ -11295,7 +11558,7 @@ async function loadCronJobs(storePath = defaultKynverCronStorePath()) {
11295
11558
  // src/cron/cron-tick-state.ts
11296
11559
  import { randomBytes } from "node:crypto";
11297
11560
  import { promises as fs4 } from "node:fs";
11298
- import path55 from "node:path";
11561
+ import path56 from "node:path";
11299
11562
  var EMPTY = { version: 1, jobs: {} };
11300
11563
  async function readFileIfExists2(filePath) {
11301
11564
  try {
@@ -11322,7 +11585,7 @@ async function loadCronTickState(statePath) {
11322
11585
  return parseCronTickState(raw);
11323
11586
  }
11324
11587
  async function writeStateAtomic(statePath, state) {
11325
- await fs4.mkdir(path55.dirname(statePath), { recursive: true });
11588
+ await fs4.mkdir(path56.dirname(statePath), { recursive: true });
11326
11589
  const suffix = randomBytes(6).toString("hex");
11327
11590
  const tmp = `${statePath}.tmp-${process.pid}-${Date.now()}-${suffix}`;
11328
11591
  await fs4.writeFile(tmp, `${JSON.stringify(state, null, 2)}
@@ -11499,7 +11762,7 @@ async function runKynverCronTick(opts = {}) {
11499
11762
  }
11500
11763
 
11501
11764
  // src/pipeline-tick.ts
11502
- import path57 from "node:path";
11765
+ import path58 from "node:path";
11503
11766
 
11504
11767
  // src/pipeline-dispatch.ts
11505
11768
  var RESERVED_REVIEW_STARTS = 1;
@@ -11630,7 +11893,7 @@ function resolvePipelineMaxStarts(resourceGate, operatorTick) {
11630
11893
  }
11631
11894
 
11632
11895
  // src/plan-progress-daemon-sync.ts
11633
- import path56 from "node:path";
11896
+ import path57 from "node:path";
11634
11897
 
11635
11898
  // src/plan-progress-sync.ts
11636
11899
  async function syncPlanProgress(args) {
@@ -11654,7 +11917,7 @@ async function syncActiveWorkerPlanProgress(runId, args) {
11654
11917
  const outcomes = [];
11655
11918
  for (const name of Object.keys(run.workers || {})) {
11656
11919
  const worker = readJson(
11657
- path56.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
11920
+ path57.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
11658
11921
  void 0
11659
11922
  );
11660
11923
  if (!worker?.dispatched || !worker.taskId) continue;
@@ -11716,7 +11979,7 @@ async function completeFinishedWorkers(runId, args) {
11716
11979
  const outcomes = [];
11717
11980
  for (const name of Object.keys(run.workers || {})) {
11718
11981
  const worker = readJson(
11719
- path57.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
11982
+ path58.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
11720
11983
  void 0
11721
11984
  );
11722
11985
  if (!worker?.taskId || worker.localOnly) continue;
@@ -11863,6 +12126,15 @@ async function runPipelineTick(args) {
11863
12126
  var DEFAULT_INTERVAL_MS = 6e4;
11864
12127
  var IDLE_INTERVAL_MS = 5 * 6e4;
11865
12128
  var MAX_IDLE_STREAK = 10;
12129
+ var SLEEP_POLL_MS = 250;
12130
+ async function awaitDaemonBackoff(ms, isStopping) {
12131
+ let remaining = ms;
12132
+ while (remaining > 0 && !isStopping()) {
12133
+ const step = Math.min(SLEEP_POLL_MS, remaining);
12134
+ await sleepMsAsync(step);
12135
+ remaining -= step;
12136
+ }
12137
+ }
11866
12138
  async function runDaemon(args) {
11867
12139
  const runId = String(required(String(args.run || ""), "--run"));
11868
12140
  const agentOsId = String(required(String(args.agentOsId || loadUserConfig().agentOsId || ""), "--agent-os-id"));
@@ -11920,17 +12192,17 @@ async function runDaemon(args) {
11920
12192
  idleStreak = 0;
11921
12193
  }
11922
12194
  const backoff = idleStreak >= MAX_IDLE_STREAK ? IDLE_INTERVAL_MS : intervalMs;
11923
- await sleepMs(backoff);
12195
+ await awaitDaemonBackoff(backoff, () => stopping);
11924
12196
  } catch (error) {
11925
12197
  console.error(JSON.stringify({ event: "daemon_tick_error", error: error.message }));
11926
- await sleepMs(intervalMs);
12198
+ await awaitDaemonBackoff(intervalMs, () => stopping);
11927
12199
  }
11928
12200
  }
11929
12201
  console.error(JSON.stringify({ event: "daemon_stop", runId, agentOsId }));
11930
12202
  }
11931
12203
 
11932
12204
  // src/plan-progress.ts
11933
- import path61 from "node:path";
12205
+ import path62 from "node:path";
11934
12206
 
11935
12207
  // src/bounded-build/constants.ts
11936
12208
  var DEFAULT_BUILD_MEM_BUDGET_BYTES = 1536 * 1024 * 1024;
@@ -12073,16 +12345,16 @@ import {
12073
12345
  unlinkSync as unlinkSync4,
12074
12346
  writeFileSync as writeFileSync5
12075
12347
  } from "node:fs";
12076
- import path59 from "node:path";
12348
+ import path60 from "node:path";
12077
12349
 
12078
12350
  // src/heavy-verification/paths.ts
12079
12351
  import { mkdirSync as mkdirSync7 } from "node:fs";
12080
- import path58 from "node:path";
12352
+ import path59 from "node:path";
12081
12353
  function resolveHeavyVerificationRoot() {
12082
- return path58.join(resolveKynverStateRoot(), "heavy-verification");
12354
+ return path59.join(resolveKynverStateRoot(), "heavy-verification");
12083
12355
  }
12084
12356
  function heavyVerificationSlotsDir() {
12085
- return path58.join(resolveHeavyVerificationRoot(), "slots");
12357
+ return path59.join(resolveHeavyVerificationRoot(), "slots");
12086
12358
  }
12087
12359
  function ensureHeavyVerificationDirs() {
12088
12360
  const dir = heavyVerificationSlotsDir();
@@ -12111,7 +12383,7 @@ function indexedSlotId(index) {
12111
12383
  return `slot-${index}`;
12112
12384
  }
12113
12385
  function slotFilePath(slotId, slotsDir = heavyVerificationSlotsDir()) {
12114
- return path59.join(slotsDir, `${slotId}.json`);
12386
+ return path60.join(slotsDir, `${slotId}.json`);
12115
12387
  }
12116
12388
  function readSlotRecord(filePath) {
12117
12389
  if (!existsSync44(filePath)) return null;
@@ -12150,7 +12422,7 @@ function reclaimStaleHeavyVerificationSlots(opts = {}) {
12150
12422
  let reclaimed = 0;
12151
12423
  for (const name of readdirSync15(slotsDir)) {
12152
12424
  if (!name.endsWith(".json")) continue;
12153
- const filePath = path59.join(slotsDir, name);
12425
+ const filePath = path60.join(slotsDir, name);
12154
12426
  const before = existsSync44(filePath);
12155
12427
  reclaimStaleSlot(filePath, staleMs);
12156
12428
  if (before && !existsSync44(filePath)) reclaimed += 1;
@@ -12164,7 +12436,7 @@ function listActiveHeavyVerificationSlots(opts = {}) {
12164
12436
  const active = [];
12165
12437
  for (const name of readdirSync15(slotsDir)) {
12166
12438
  if (!name.endsWith(".json")) continue;
12167
- const record3 = readSlotRecord(path59.join(slotsDir, name));
12439
+ const record3 = readSlotRecord(path60.join(slotsDir, name));
12168
12440
  if (record3 && !slotIsStale(record3, staleMs)) active.push(record3);
12169
12441
  }
12170
12442
  return active;
@@ -12284,11 +12556,11 @@ function waitForHeavyVerificationSlot(command, timeoutMs, pollMs = 2e3, opts = {
12284
12556
  }
12285
12557
 
12286
12558
  // src/harness-worktree-build-guard.ts
12287
- import path60 from "node:path";
12559
+ import path61 from "node:path";
12288
12560
  function isPathUnderHarnessWorktree(cwd) {
12289
12561
  const worktreesDir = harnessWorktreesDir(resolveHarnessRoot());
12290
- const rel = path60.relative(worktreesDir, path60.resolve(cwd));
12291
- return rel.length > 0 && !rel.startsWith("..") && !path60.isAbsolute(rel);
12562
+ const rel = path61.relative(worktreesDir, path61.resolve(cwd));
12563
+ return rel.length > 0 && !rel.startsWith("..") && !path61.isAbsolute(rel);
12292
12564
  }
12293
12565
  function assessHarnessWorktreeBuildGuard(cwd) {
12294
12566
  if (!isPathUnderHarnessWorktree(cwd)) return { ok: true };
@@ -12500,7 +12772,7 @@ async function emitPlanProgress(args) {
12500
12772
  }
12501
12773
  function verifyPlanLocal(args) {
12502
12774
  const worktree = required(args.worktree ? String(args.worktree) : void 0, "worktree");
12503
- const cwd = path61.resolve(worktree);
12775
+ const cwd = path62.resolve(worktree);
12504
12776
  const summary = runHarnessVerifyCommands(cwd);
12505
12777
  const emitJson = args.json === true || args.json === "true";
12506
12778
  const payload = { passed: summary.passed, worktree: cwd, steps: summary.steps };
@@ -12549,9 +12821,9 @@ async function verifyPlan(args) {
12549
12821
  }
12550
12822
 
12551
12823
  // src/harness-verify-cli.ts
12552
- import path62 from "node:path";
12824
+ import path63 from "node:path";
12553
12825
  function runHarnessVerifyCli(args) {
12554
- const cwd = path62.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
12826
+ const cwd = path63.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
12555
12827
  const emitJson = args.json === true || args.json === "true" || args.emitJson === true || args.emitJson === "true";
12556
12828
  const commands = [];
12557
12829
  const rawCmd = args.command;
@@ -12988,7 +13260,7 @@ ${text.slice(0, 800)}`,
12988
13260
  }
12989
13261
 
12990
13262
  // src/monitor/monitor.service.ts
12991
- import path64 from "node:path";
13263
+ import path65 from "node:path";
12992
13264
 
12993
13265
  // src/monitor/monitor.classify.ts
12994
13266
  function classifyWorkerHealth(input) {
@@ -13041,10 +13313,10 @@ function classifyWorkerHealth(input) {
13041
13313
 
13042
13314
  // src/monitor/monitor.store.ts
13043
13315
  import { existsSync as existsSync45, mkdirSync as mkdirSync9, readdirSync as readdirSync16, unlinkSync as unlinkSync5 } from "node:fs";
13044
- import path63 from "node:path";
13316
+ import path64 from "node:path";
13045
13317
  function monitorsDir() {
13046
13318
  const { harnessRoot } = getHarnessPaths();
13047
- const dir = path63.join(harnessRoot, "monitors");
13319
+ const dir = path64.join(harnessRoot, "monitors");
13048
13320
  mkdirSync9(dir, { recursive: true });
13049
13321
  return dir;
13050
13322
  }
@@ -13052,7 +13324,7 @@ function monitorIdFor(runId, workerName) {
13052
13324
  return workerName ? `${safeSlug(runId)}--${safeSlug(workerName)}` : safeSlug(runId);
13053
13325
  }
13054
13326
  function monitorPath(monitorId) {
13055
- return path63.join(monitorsDir(), `${monitorId}.json`);
13327
+ return path64.join(monitorsDir(), `${monitorId}.json`);
13056
13328
  }
13057
13329
  function loadMonitorSession(monitorId) {
13058
13330
  return readJson(monitorPath(monitorId), void 0);
@@ -13073,7 +13345,7 @@ function listMonitorSessions() {
13073
13345
  for (const name of readdirSync16(dir)) {
13074
13346
  if (!name.endsWith(".json")) continue;
13075
13347
  const session = readJson(
13076
- path63.join(dir, name),
13348
+ path64.join(dir, name),
13077
13349
  void 0
13078
13350
  );
13079
13351
  if (!session?.monitorId) continue;
@@ -13164,7 +13436,7 @@ async function fetchTaskLeasesForWorkers(input) {
13164
13436
  // src/monitor/monitor.service.ts
13165
13437
  function workerRecord2(runId, name) {
13166
13438
  return readJson(
13167
- path64.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
13439
+ path65.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
13168
13440
  void 0
13169
13441
  );
13170
13442
  }
@@ -13371,17 +13643,17 @@ async function runMonitorLoop(args) {
13371
13643
  // src/monitor/monitor-spawn.ts
13372
13644
  import { spawn as spawn6 } from "node:child_process";
13373
13645
  import { closeSync as closeSync8, existsSync as existsSync46, openSync as openSync8 } from "node:fs";
13374
- import path65 from "node:path";
13646
+ import path66 from "node:path";
13375
13647
  import { fileURLToPath as fileURLToPath4 } from "node:url";
13376
13648
  function resolveDefaultCliPath2() {
13377
- return path65.join(fileURLToPath4(new URL(".", import.meta.url)), "cli.js");
13649
+ return path66.join(fileURLToPath4(new URL(".", import.meta.url)), "cli.js");
13378
13650
  }
13379
13651
  function spawnMonitorSidecar(opts) {
13380
13652
  const cliPath = opts.cliPath ?? resolveDefaultCliPath2();
13381
13653
  if (!existsSync46(cliPath)) return void 0;
13382
13654
  const monitorId = monitorIdFor(opts.runId, opts.workerName);
13383
13655
  const { harnessRoot } = getHarnessPaths();
13384
- const logPath = path65.join(harnessRoot, "monitors", `${monitorId}.log`);
13656
+ const logPath = path66.join(harnessRoot, "monitors", `${monitorId}.log`);
13385
13657
  let logFd;
13386
13658
  try {
13387
13659
  logFd = openSync8(logPath, "a");
@@ -13501,7 +13773,7 @@ async function monitorTickCli(args) {
13501
13773
  }
13502
13774
 
13503
13775
  // src/post-restart-unblock.ts
13504
- import path66 from "node:path";
13776
+ import path67 from "node:path";
13505
13777
  function skip(runId, worker, taskId, agentOsId, leaseOwner, reason) {
13506
13778
  return { runId, worker, taskId, agentOsId, leaseOwner, action: "skipped", reason };
13507
13779
  }
@@ -13514,7 +13786,7 @@ async function postRestartUnblock(args) {
13514
13786
  const errors = [];
13515
13787
  for (const run of listRunRecords()) {
13516
13788
  for (const name of Object.keys(run.workers ?? {})) {
13517
- const workerPath = path66.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
13789
+ const workerPath = path67.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
13518
13790
  const worker = readJson(workerPath, void 0);
13519
13791
  if (!worker) {
13520
13792
  skipped.push(skip(run.id, name, "", "", "", "worker.json missing"));
@@ -13626,9 +13898,9 @@ async function postRestartUnblockCli(args) {
13626
13898
  }
13627
13899
 
13628
13900
  // src/default-repo-cli.ts
13629
- import path67 from "node:path";
13901
+ import path68 from "node:path";
13630
13902
  import { homedir as homedir15 } from "node:os";
13631
- var CONFIG_FILE2 = path67.join(homedir15(), ".kynver", "config.json");
13903
+ var CONFIG_FILE2 = path68.join(homedir15(), ".kynver", "config.json");
13632
13904
  function ensureDefaultRepo(opts) {
13633
13905
  const existing = loadUserConfig();
13634
13906
  const resolved = resolveDefaultRepo({ ...opts, config: existing });
@@ -13709,12 +13981,12 @@ function summarizeResolvedDefaultRepo(resolved) {
13709
13981
  }
13710
13982
 
13711
13983
  // src/doctor/runtime-takeover.ts
13712
- import path69 from "node:path";
13984
+ import path70 from "node:path";
13713
13985
 
13714
13986
  // src/doctor/runtime-takeover.probes.ts
13715
13987
  import { accessSync, constants, existsSync as existsSync47, readFileSync as readFileSync19 } from "node:fs";
13716
13988
  import { homedir as homedir16 } from "node:os";
13717
- import path68 from "node:path";
13989
+ import path69 from "node:path";
13718
13990
  import { spawnSync as spawnSync10 } from "node:child_process";
13719
13991
  function captureCommand(bin, args) {
13720
13992
  try {
@@ -13756,10 +14028,10 @@ var defaultRuntimeTakeoverProbes = {
13756
14028
  commandOnPath: (bin) => captureCommand(process.platform === "win32" ? "where" : "which", [bin]),
13757
14029
  kynverVersion: (bin) => captureCommand(bin, ["--version"]),
13758
14030
  loadConfig: () => loadUserConfig(),
13759
- configFilePath: () => path68.join(homedir16(), ".kynver", "config.json"),
13760
- credentialsFilePath: () => path68.join(homedir16(), ".kynver", "credentials"),
14031
+ configFilePath: () => path69.join(homedir16(), ".kynver", "config.json"),
14032
+ credentialsFilePath: () => path69.join(homedir16(), ".kynver", "credentials"),
13761
14033
  readCredentials: () => {
13762
- const credPath = path68.join(homedir16(), ".kynver", "credentials");
14034
+ const credPath = path69.join(homedir16(), ".kynver", "credentials");
13763
14035
  if (!existsSync47(credPath)) {
13764
14036
  return { hasApiKey: false };
13765
14037
  }
@@ -13794,7 +14066,7 @@ var defaultRuntimeTakeoverProbes = {
13794
14066
  })()
13795
14067
  }),
13796
14068
  harnessRoot: () => resolveHarnessRoot(),
13797
- legacyOpenclawHarnessRoot: () => path68.join(homedir16(), ".openclaw", "harness"),
14069
+ legacyOpenclawHarnessRoot: () => path69.join(homedir16(), ".openclaw", "harness"),
13798
14070
  pathExists: (target) => existsSync47(target),
13799
14071
  pathWritable: (target) => isWritable(target)
13800
14072
  };
@@ -14201,8 +14473,8 @@ function assessVercelDeployEvidence(probes) {
14201
14473
  }
14202
14474
  function assessHarnessDirs(probes) {
14203
14475
  const harnessRoot = probes.harnessRoot();
14204
- const runsDir = path69.join(harnessRoot, "runs");
14205
- const worktreesDir = path69.join(harnessRoot, "worktrees");
14476
+ const runsDir = path70.join(harnessRoot, "runs");
14477
+ const worktreesDir = path70.join(harnessRoot, "worktrees");
14206
14478
  const displayHarnessRoot = redactHomePath(harnessRoot);
14207
14479
  const displayRunsDir = redactHomePath(runsDir);
14208
14480
  const displayWorktreesDir = redactHomePath(worktreesDir);
@@ -14466,9 +14738,9 @@ function applySchedulerCutoverAttestation(config) {
14466
14738
  }
14467
14739
 
14468
14740
  // src/scheduler-cutover-cli.ts
14469
- import path70 from "node:path";
14741
+ import path71 from "node:path";
14470
14742
  import { homedir as homedir17 } from "node:os";
14471
- var CONFIG_FILE3 = path70.join(homedir17(), ".kynver", "config.json");
14743
+ var CONFIG_FILE3 = path71.join(homedir17(), ".kynver", "config.json");
14472
14744
  function runSchedulerCutoverCheckCli(json = false) {
14473
14745
  const config = loadUserConfig();
14474
14746
  const report = assessSchedulerCutover(config);
@@ -14605,6 +14877,157 @@ async function runCronTickCli(args) {
14605
14877
  );
14606
14878
  }
14607
14879
 
14880
+ // src/lane/landing-maintainer-tick.ts
14881
+ import os9 from "node:os";
14882
+
14883
+ // src/lane/lane-spec.ts
14884
+ var LANDING_MAINTAINER_LANE_SPEC = {
14885
+ slug: "landing-maintainer",
14886
+ originCron: "maintain-8-blocker-and-pr-landing-workers",
14887
+ defaultRepo: "Totalsolutionsync/Kynver",
14888
+ landScript: "scripts/agent-os-land-pr.mjs",
14889
+ landScriptArgs: ["--skip-not-ready"]
14890
+ };
14891
+
14892
+ // src/lane/landing-maintainer-local.ts
14893
+ import { spawnSync as spawnSync11 } from "node:child_process";
14894
+ import path72 from "node:path";
14895
+ function runLandingWrapper(prNumber, repoRoot, execute) {
14896
+ const script = path72.join(repoRoot, LANDING_MAINTAINER_LANE_SPEC.landScript);
14897
+ const args = [script, String(prNumber), ...LANDING_MAINTAINER_LANE_SPEC.landScriptArgs];
14898
+ if (!execute) {
14899
+ return {
14900
+ action: { kind: "land_pr", prNumber, reason: "dry-run" },
14901
+ executed: false,
14902
+ exitCode: 0,
14903
+ stdout: `dry-run: node ${args.join(" ")}`,
14904
+ stderr: ""
14905
+ };
14906
+ }
14907
+ const result = spawnSync11("node", args, {
14908
+ cwd: repoRoot,
14909
+ encoding: "utf8",
14910
+ timeout: 10 * 60 * 1e3
14911
+ });
14912
+ return {
14913
+ action: { kind: "land_pr", prNumber, reason: "landing wrapper invoked" },
14914
+ executed: true,
14915
+ exitCode: result.status,
14916
+ stdout: result.stdout ?? "",
14917
+ stderr: result.stderr ?? ""
14918
+ };
14919
+ }
14920
+ function resolveLandingMaintainerRepoRoot(args) {
14921
+ const explicit = args.repoPath ? String(args.repoPath).trim() : "";
14922
+ if (explicit) return path72.resolve(explicit);
14923
+ const resolved = resolveDefaultRepo();
14924
+ return resolved?.repo ?? process.cwd();
14925
+ }
14926
+
14927
+ // src/lane/landing-maintainer-tick.ts
14928
+ async function runLandingMaintainerLaneTick(args) {
14929
+ const agentOsId = String(required(String(args.agentOsId || ""), "--agent-os-id"));
14930
+ const repoSlug = String(args.repo || LANDING_MAINTAINER_LANE_SPEC.defaultRepo).trim();
14931
+ const fleet = args.fleet === true || args.fleet === "true";
14932
+ const execute = args.execute === true || args.execute === "true";
14933
+ const runId = args.run ? String(args.run) : void 0;
14934
+ const resourceGate = observeRunnerResourceGate({
14935
+ runId: runId ?? "fleet-lane-tick"
14936
+ });
14937
+ const boxCapacity = {
14938
+ ...buildBoxResourceSnapshotFromGate(resourceGate, {
14939
+ harnessRunId: runId,
14940
+ boxKind: resolveBoxKindFromConfig(loadUserConfig()),
14941
+ hostLabel: os9.hostname()
14942
+ }),
14943
+ providerHealthy: resourceGate.ok,
14944
+ authorizedForRepair: resourceGate.ok,
14945
+ authorizedForLanding: resourceGate.ok,
14946
+ systemHealthBlockers: resourceGate.ok ? [] : [resourceGate.reason ?? "resource_gate_blocked"],
14947
+ actionableWorkers: resourceGate.activeWorkers
14948
+ };
14949
+ const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
14950
+ const secret = await resolveCallbackSecretWithMint(
14951
+ args.secret ? String(args.secret) : void 0,
14952
+ agentOsId,
14953
+ { baseUrl: base }
14954
+ );
14955
+ const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/fleet-landing-maintainer/tick`;
14956
+ const res = await postJson(url, secret, {
14957
+ repo: repoSlug,
14958
+ fleet,
14959
+ execute,
14960
+ runId,
14961
+ boxCapacity
14962
+ });
14963
+ const coordinator = res.response;
14964
+ const localOutcomes = [];
14965
+ const repoRoot = resolveLandingMaintainerRepoRoot(args);
14966
+ const actions = Array.isArray(coordinator?.localActions) ? coordinator.localActions : [];
14967
+ const holderBoxId = boxCapacity.boxId;
14968
+ for (const action of actions) {
14969
+ if (action.kind === "land_pr" && typeof action.prNumber === "number") {
14970
+ const outcome = runLandingWrapper(action.prNumber, repoRoot, execute);
14971
+ localOutcomes.push({
14972
+ action: outcome.action,
14973
+ executed: outcome.executed,
14974
+ exitCode: outcome.exitCode
14975
+ });
14976
+ if (execute && outcome.executed) {
14977
+ const merged = outcome.exitCode === 0;
14978
+ const prUrl = typeof action.prUrl === "string" && action.prUrl.trim() ? action.prUrl.trim() : `https://github.com/${repoSlug}/pull/${action.prNumber}`;
14979
+ try {
14980
+ await postJson(
14981
+ `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/fleet-landing-maintainer/outcome`,
14982
+ secret,
14983
+ { repo: repoSlug, prUrl, holderBoxId, merged }
14984
+ );
14985
+ } catch {
14986
+ }
14987
+ }
14988
+ continue;
14989
+ }
14990
+ localOutcomes.push({
14991
+ action,
14992
+ executed: false,
14993
+ exitCode: null
14994
+ });
14995
+ }
14996
+ return {
14997
+ repo: repoSlug,
14998
+ fleet,
14999
+ execute,
15000
+ coordinator,
15001
+ localOutcomes
15002
+ };
15003
+ }
15004
+
15005
+ // src/lane/lane-tick-cli.ts
15006
+ async function runLaneTickCli(args, laneFromArgv) {
15007
+ const lane = String(laneFromArgv ?? args.lane ?? "").trim();
15008
+ if (lane !== "landing-maintainer") {
15009
+ console.error(`unknown lane: ${lane || "(none)"}`);
15010
+ process.exit(1);
15011
+ }
15012
+ const result = await runLandingMaintainerLaneTick(args);
15013
+ if (args.json === true || args.json === "true") {
15014
+ console.log(JSON.stringify(result, null, 2));
15015
+ return;
15016
+ }
15017
+ console.log(
15018
+ [
15019
+ `fleet landing-maintainer tick`,
15020
+ `repo=${result.repo}`,
15021
+ `fleet=${result.fleet}`,
15022
+ `execute=${result.execute}`,
15023
+ `localOutcomes=${result.localOutcomes.length}`
15024
+ ].join(" ")
15025
+ );
15026
+ for (const row of result.localOutcomes) {
15027
+ console.log(` ${row.action.kind} pr=${row.action.prNumber ?? "-"} executed=${row.executed}`);
15028
+ }
15029
+ }
15030
+
14608
15031
  // src/cli.ts
14609
15032
  function isHelpFlag(arg) {
14610
15033
  return arg === "help" || arg === "--help" || arg === "-h";
@@ -14625,6 +15048,7 @@ function usage(code = 0) {
14625
15048
  " kynver daemon --run RUN_ID --agent-os-id AOS_ID [--execute] [--interval-ms MS]",
14626
15049
  " kynver run create [--repo /path/repo] [--name name] [--base origin/main]",
14627
15050
  " kynver run list",
15051
+ " kynver run resolve --name RUN_NAME",
14628
15052
  " kynver run status --run RUN_ID [--json] [--compact]",
14629
15053
  " 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
15054
  " kynver run sweep --run RUN_ID --agent-os-id AOS_ID [--base-url URL] [--secret SECRET] [--grace-ms MS]",
@@ -14660,6 +15084,7 @@ function usage(code = 0) {
14660
15084
  " kynver scheduler attest-cutover [--json]",
14661
15085
  " kynver cron status [--json]",
14662
15086
  " kynver cron tick [--agent-os-id AOS_ID] [--json]",
15087
+ " kynver lane tick landing-maintainer [--fleet] [--repo OWNER/NAME] [--agent-os-id AOS_ID] [--execute] [--json]",
14663
15088
  " kynver board contract [--agent-os-id ID] [--base-url URL] [--since ISO] [--limit N]"
14664
15089
  ].join("\n")
14665
15090
  );
@@ -14671,7 +15096,7 @@ async function main(argv = process.argv.slice(2)) {
14671
15096
  const scope = argv.shift();
14672
15097
  let action;
14673
15098
  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") {
15099
+ 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
15100
  action = argv.shift();
14676
15101
  rest = argv;
14677
15102
  } else {
@@ -14728,11 +15153,16 @@ async function main(argv = process.argv.slice(2)) {
14728
15153
  if (scope === "cron" && action === "tick") {
14729
15154
  return void await runCronTickCli(args);
14730
15155
  }
15156
+ if (scope === "lane" && action === "tick") {
15157
+ const laneName = rest.shift();
15158
+ return void await runLaneTickCli(parseArgs(rest), laneName);
15159
+ }
14731
15160
  if (scope === "board" && action === "contract") {
14732
15161
  return void await runCommandCenterContractCli(args);
14733
15162
  }
14734
15163
  if (scope === "run" && action === "create") return createRun(args);
14735
15164
  if (scope === "run" && action === "list") return listRuns();
15165
+ if (scope === "run" && action === "resolve") return resolveHarnessRunCli(args);
14736
15166
  if (scope === "run" && action === "status") return runStatus(args);
14737
15167
  if (scope === "run" && action === "dispatch") return void await dispatchRun(args);
14738
15168
  if (scope === "run" && action === "sweep") return void await sweepRun(args);
@@ -15226,6 +15656,7 @@ export {
15226
15656
  hasNestedRunsSegment,
15227
15657
  hashPlanBody,
15228
15658
  hermesCodexProvider,
15659
+ isActiveHarnessWorker,
15229
15660
  isClaudeFamilyProvider,
15230
15661
  isDashboardVercelUrl,
15231
15662
  isEngagementRequiredSkip,
@@ -15289,6 +15720,7 @@ export {
15289
15720
  readMemAvailableBytes,
15290
15721
  readProductionDbKeysFromEnvFile,
15291
15722
  reclaimStaleHeavyVerificationSlots,
15723
+ reconcileLocalOnlyMergedPrAttention,
15292
15724
  reconcileRunsCli,
15293
15725
  reconcileStaleWorkers,
15294
15726
  reconcileWorkerMetadata,
@@ -15307,6 +15739,8 @@ export {
15307
15739
  resolveConfiguredWorkerProvider,
15308
15740
  resolveDefaultRepo,
15309
15741
  resolveHarnessRoot,
15742
+ resolveHarnessRunByName,
15743
+ resolveHarnessRunCli,
15310
15744
  resolveHeavyVerificationMaxConcurrent,
15311
15745
  resolveOpenAiCodexRetryBudget,
15312
15746
  resolveOrchestrationPolicyMode,