@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/cli.js CHANGED
@@ -90,6 +90,9 @@ function readMaybeFile(file) {
90
90
  function sleepMs(ms) {
91
91
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
92
92
  }
93
+ function sleepMsAsync(ms) {
94
+ return new Promise((resolve) => setTimeout(resolve, ms));
95
+ }
93
96
  function isPidAlive(pid) {
94
97
  if (!pid) return false;
95
98
  try {
@@ -461,7 +464,6 @@ function readMemAvailableBytes(meminfoText) {
461
464
 
462
465
  // src/resource-gate.ts
463
466
  import path7 from "node:path";
464
- import { readFileSync as readFileSync7 } from "node:fs";
465
467
 
466
468
  // src/disk-gate.ts
467
469
  import { statfsSync as statfsSync2 } from "node:fs";
@@ -486,23 +488,23 @@ function isWslHost() {
486
488
  function observeWslHostDisk(options = {}) {
487
489
  const wsl = options.forceWsl === void 0 ? isWslHost() : options.forceWsl;
488
490
  if (!wsl) return null;
489
- const path69 = options.wslHostMount?.trim() || process.env.KYNVER_WSL_HOST_MOUNT?.trim() || DEFAULT_WSL_HOST_MOUNT;
491
+ const path71 = options.wslHostMount?.trim() || process.env.KYNVER_WSL_HOST_MOUNT?.trim() || DEFAULT_WSL_HOST_MOUNT;
490
492
  const warnBelowBytes = options.wslHostFreeWarnBytes ?? DEFAULT_WSL_HOST_WARN_FREE_BYTES;
491
493
  const criticalBelowBytes = options.wslHostFreeCriticalBytes ?? DEFAULT_WSL_HOST_CRITICAL_FREE_BYTES;
492
494
  const statfs = options.statfs ?? statfsSync;
493
495
  let stats;
494
496
  try {
495
- stats = statfs(path69);
497
+ stats = statfs(path71);
496
498
  } catch (error) {
497
499
  return {
498
500
  ok: false,
499
- path: path69,
501
+ path: path71,
500
502
  freeBytes: 0,
501
503
  totalBytes: 0,
502
504
  usedPercent: 100,
503
505
  warnBelowBytes,
504
506
  criticalBelowBytes,
505
- reason: `Windows host disk probe failed at ${path69}: ${error.message}`,
507
+ reason: `Windows host disk probe failed at ${path71}: ${error.message}`,
506
508
  probeError: error.message
507
509
  };
508
510
  }
@@ -516,11 +518,11 @@ function observeWslHostDisk(options = {}) {
516
518
  let reason = null;
517
519
  if (!ok) {
518
520
  const tag = criticalFree ? "critical" : "warning";
519
- reason = `Windows host disk ${path69} at ${tag}: ${freeGiB} GiB free (<${(criticalFree ? criticalBelowBytes : warnBelowBytes) / 1024 / 1024 / 1024} GiB); WSL VHDX cannot grow safely. ${summarizeWslRecoverySteps()}`;
521
+ reason = `Windows host disk ${path71} at ${tag}: ${freeGiB} GiB free (<${(criticalFree ? criticalBelowBytes : warnBelowBytes) / 1024 / 1024 / 1024} GiB); WSL VHDX cannot grow safely. ${summarizeWslRecoverySteps()}`;
520
522
  }
521
523
  return {
522
524
  ok,
523
- path: path69,
525
+ path: path71,
524
526
  freeBytes,
525
527
  totalBytes,
526
528
  usedPercent,
@@ -540,12 +542,12 @@ var DEFAULT_CRITICAL_FREE_BYTES = 15 * 1024 * 1024 * 1024;
540
542
  var DEFAULT_MAX_USED_PERCENT = 80;
541
543
  var DEFAULT_HARD_MAX_USED_PERCENT = 90;
542
544
  function observeRunnerDiskGate(input = {}) {
543
- const path69 = input.diskPath?.trim() || "/";
545
+ const path71 = input.diskPath?.trim() || "/";
544
546
  const warnBelowBytes = input.diskFreeWarnBytes ?? DEFAULT_WARN_FREE_BYTES;
545
547
  const criticalBelowBytes = input.diskFreeCriticalBytes ?? DEFAULT_CRITICAL_FREE_BYTES;
546
548
  const maxUsedPercent = input.diskMaxUsedPercent ?? DEFAULT_MAX_USED_PERCENT;
547
549
  const hardMaxUsedPercent = input.diskHardMaxUsedPercent ?? DEFAULT_HARD_MAX_USED_PERCENT;
548
- const stats = statfsSync2(path69);
550
+ const stats = statfsSync2(path71);
549
551
  const freeBytes = Number(stats.bavail) * Number(stats.bsize);
550
552
  const totalBytes = Number(stats.blocks) * Number(stats.bsize);
551
553
  const usedPercent = totalBytes > 0 ? (totalBytes - freeBytes) / totalBytes * 100 : 100;
@@ -568,7 +570,7 @@ function observeRunnerDiskGate(input = {}) {
568
570
  }
569
571
  return {
570
572
  ok,
571
- path: path69,
573
+ path: path71,
572
574
  freeBytes,
573
575
  totalBytes,
574
576
  usedPercent,
@@ -702,6 +704,9 @@ function listRunWorkerNames(run) {
702
704
  return [...names];
703
705
  }
704
706
 
707
+ // src/harness-worker-active.ts
708
+ import { readFileSync as readFileSync7 } from "node:fs";
709
+
705
710
  // src/heartbeat.ts
706
711
  import { existsSync as existsSync7, readFileSync as readFileSync5 } from "node:fs";
707
712
 
@@ -1782,6 +1787,9 @@ function computeAttention(input) {
1782
1787
  return { state: "blocked", reason: input.completionBlocker };
1783
1788
  }
1784
1789
  if (input.finalResult) {
1790
+ if (input.localOnly && hasMergedTargetPrReconciliation(input.finalResult)) {
1791
+ return { state: "done", reason: "local-only worker superseded by merged PR" };
1792
+ }
1785
1793
  const landingSnapshot = {
1786
1794
  finalResult: input.finalResult,
1787
1795
  changedFiles: input.changedFiles ?? [],
@@ -1847,9 +1855,24 @@ function computeAttention(input) {
1847
1855
  }
1848
1856
  return { state: "ok", reason: "recent activity" };
1849
1857
  }
1858
+ function hasMergedTargetPrReconciliation(value) {
1859
+ let record = null;
1860
+ if (typeof value === "string") record = extractEmbeddedWorkerFinalResultRecord(value);
1861
+ else if (value && typeof value === "object" && !Array.isArray(value)) record = value;
1862
+ if (!record) return false;
1863
+ const raw = record.targetPrReconciliation ?? record.target_pr_reconciliation;
1864
+ if (!Array.isArray(raw)) return false;
1865
+ return raw.some((item) => {
1866
+ if (!item || typeof item !== "object" || Array.isArray(item)) return false;
1867
+ return String(item.outcome ?? "").trim() === "merged";
1868
+ });
1869
+ }
1850
1870
  function resolveFinalResult(worker, parsedFinalResult, heartbeat) {
1851
- if (parsedFinalResult) return parsedFinalResult;
1852
1871
  const ackSnapshot = worker.completionSnapshot?.finalResult;
1872
+ if (worker.completionAckSource === "local-pr-merged-reconcile" && ackSnapshot !== void 0 && ackSnapshot !== null) {
1873
+ return ackSnapshot;
1874
+ }
1875
+ if (parsedFinalResult) return parsedFinalResult;
1853
1876
  if (ackSnapshot !== void 0 && ackSnapshot !== null) return ackSnapshot;
1854
1877
  return terminalFinalResultFromHeartbeat(heartbeat);
1855
1878
  }
@@ -1896,7 +1919,8 @@ function computeWorkerStatus(worker, options = {}) {
1896
1919
  gitAncestry,
1897
1920
  completionBlocker,
1898
1921
  landingContract,
1899
- prUrl: worker.repairTargetPrUrl ?? worker.taskPrUrl ?? null
1922
+ prUrl: worker.repairTargetPrUrl ?? worker.taskPrUrl ?? null,
1923
+ localOnly: worker.localOnly === true
1900
1924
  });
1901
1925
  const workerStatusLabel = completionBlocker || attention.state === "blocked" ? "blocked" : completionAcknowledged || attention.state === "done" ? "done" : finalResult ? "exited" : alive ? "running" : "exited";
1902
1926
  return {
@@ -1950,6 +1974,33 @@ function deriveRunStatus(fallback, workers) {
1950
1974
  return fallback;
1951
1975
  }
1952
1976
 
1977
+ // src/harness-worker-active.ts
1978
+ function pidCommandLine(pid) {
1979
+ if (!pid || process.platform !== "linux") return null;
1980
+ try {
1981
+ return readFileSync7(`/proc/${pid}/cmdline`, "utf8").replace(/\0/g, " ");
1982
+ } catch {
1983
+ return null;
1984
+ }
1985
+ }
1986
+ function workerProcessMatchesRecord(worker) {
1987
+ if (!worker.pid || process.platform !== "linux") return true;
1988
+ const cmdline = pidCommandLine(worker.pid);
1989
+ if (!cmdline) return false;
1990
+ const probes = [worker.worktreePath, worker.workerDir, worker.heartbeatPath].filter(
1991
+ (value) => typeof value === "string" && value.trim().length > 0
1992
+ );
1993
+ return probes.some((probe) => cmdline.includes(probe));
1994
+ }
1995
+ function isActiveHarnessWorker(worker) {
1996
+ if (typeof worker.completionBlocker === "string" && worker.completionBlocker.trim()) {
1997
+ return false;
1998
+ }
1999
+ const status = computeWorkerStatus(worker);
2000
+ if (status.alive && !workerProcessMatchesRecord(worker)) return false;
2001
+ return status.alive && !status.finalResult && status.attention.state !== "done";
2002
+ }
2003
+
1953
2004
  // src/resource-gate.ts
1954
2005
  var DEFAULT_PER_WORKER_MEM_BYTES = 500 * 1024 * 1024;
1955
2006
  var DEFAULT_MEM_RESERVE_BYTES = 4 * 1024 * 1024 * 1024;
@@ -1992,31 +2043,6 @@ function computeAutoMaxWorkers(totalMemBytes, opts = {}) {
1992
2043
  function readAvailableMemBytes() {
1993
2044
  return readMemAvailableBytes();
1994
2045
  }
1995
- function pidCommandLine(pid) {
1996
- if (!pid || process.platform !== "linux") return null;
1997
- try {
1998
- return readFileSync7(`/proc/${pid}/cmdline`, "utf8").replace(/\0/g, " ");
1999
- } catch {
2000
- return null;
2001
- }
2002
- }
2003
- function workerProcessMatchesRecord(worker) {
2004
- if (!worker.pid || process.platform !== "linux") return true;
2005
- const cmdline = pidCommandLine(worker.pid);
2006
- if (!cmdline) return false;
2007
- const probes = [worker.worktreePath, worker.workerDir, worker.heartbeatPath].filter(
2008
- (value) => typeof value === "string" && value.trim().length > 0
2009
- );
2010
- return probes.some((probe) => cmdline.includes(probe));
2011
- }
2012
- function isActiveHarnessWorker(worker) {
2013
- if (typeof worker.completionBlocker === "string" && worker.completionBlocker.trim()) {
2014
- return false;
2015
- }
2016
- const status = computeWorkerStatus(worker);
2017
- if (status.alive && !workerProcessMatchesRecord(worker)) return false;
2018
- return status.alive && !status.finalResult && status.attention.state !== "done";
2019
- }
2020
2046
  function countActiveWorkersForRun(run) {
2021
2047
  let active = 0;
2022
2048
  for (const name of listRunWorkerNames(run)) {
@@ -5021,8 +5047,8 @@ function dirtyPathsCoveredByDisposableRemoval(changedFiles, removed) {
5021
5047
  if (removed.length === 0) return false;
5022
5048
  const removedSet = new Set(removed.map((p) => normalizeRelativePath(p)));
5023
5049
  return material.every((line) => {
5024
- const path69 = normalizeRelativePath(pathFromGitStatusLine(line));
5025
- return removedSet.has(path69);
5050
+ const path71 = normalizeRelativePath(pathFromGitStatusLine(line));
5051
+ return removedSet.has(path71);
5026
5052
  });
5027
5053
  }
5028
5054
 
@@ -6316,7 +6342,7 @@ function collectRunActiveHarnessWorkers(runId) {
6316
6342
  path22.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
6317
6343
  void 0
6318
6344
  );
6319
- if (!worker?.taskId || !workerProcessMatchesRecord(worker)) continue;
6345
+ if (!worker?.taskId || !isActiveHarnessWorker(worker)) continue;
6320
6346
  out.push({
6321
6347
  runId: run.id,
6322
6348
  workerName: name,
@@ -7177,7 +7203,7 @@ async function dispatchRun(args) {
7177
7203
  );
7178
7204
  }
7179
7205
  const attempt = Number(task.attempt) || 1;
7180
- if (attempt >= retryLimits.maxTaskAttempts) {
7206
+ if (attempt > retryLimits.maxTaskAttempts) {
7181
7207
  return abortClaimedSpawn(
7182
7208
  task,
7183
7209
  `task attempt ${attempt} exceeds KYNVER_MAX_TASK_ATTEMPTS (${retryLimits.maxTaskAttempts})`
@@ -7313,7 +7339,12 @@ async function dispatchRun(args) {
7313
7339
  const admissionExhaustion = readAdmissionExhaustion(result);
7314
7340
  const capacityIdle = admissionExhaustion?.capacityIdle === true || startedCount === 0 && Number(result.resourceGate?.slotsAvailable) > 0;
7315
7341
  if (capacityIdle && admissionExhaustion?.summary) {
7316
- console.error(`[dispatch] ${admissionExhaustion.summary}`);
7342
+ const retryCeiling = admissionExhaustion.skipReasonCounts?.retry_ceiling_exceeded ?? 0;
7343
+ const recovery = admissionExhaustion.overAttemptIdleRecovery;
7344
+ const recoveryNote = recovery?.attempted === true ? `; over_attempt_recovery minted=${recovery.minted ?? 0} started=${recovery.started ?? 0}` : retryCeiling > 0 ? "; over_attempt_recovery not attempted" : "";
7345
+ console.error(
7346
+ `[dispatch] ${admissionExhaustion.summary}${retryCeiling > 0 ? `; retry_ceiling_exceeded=${retryCeiling}` : ""}${recoveryNote}`
7347
+ );
7317
7348
  }
7318
7349
  const summary = {
7319
7350
  runId: run.id,
@@ -7409,14 +7440,14 @@ async function sweepRun(args) {
7409
7440
 
7410
7441
  // src/worktree.ts
7411
7442
  import { existsSync as existsSync25, mkdirSync as mkdirSync6 } from "node:fs";
7412
- import path34 from "node:path";
7443
+ import path35 from "node:path";
7413
7444
 
7414
7445
  // src/run-list.ts
7415
7446
  import { existsSync as existsSync24, readFileSync as readFileSync11 } from "node:fs";
7416
- import path33 from "node:path";
7447
+ import path34 from "node:path";
7417
7448
 
7418
7449
  // src/stale-reconcile.ts
7419
- import path32 from "node:path";
7450
+ import path33 from "node:path";
7420
7451
 
7421
7452
  // src/finalize.ts
7422
7453
  import path27 from "node:path";
@@ -7999,6 +8030,193 @@ function reconcileWorkerMetadata() {
7999
8030
  return { workers: outcomes, runMetadataRetention };
8000
8031
  }
8001
8032
 
8033
+ // src/local-pr-attention-reconcile.ts
8034
+ import { execFileSync } from "node:child_process";
8035
+ import path32 from "node:path";
8036
+ function normalizePrUrl3(url) {
8037
+ const m = url.trim().match(/github\.com\/([^/]+\/[^/]+)\/(?:pull|pulls)\/(\d+)/i);
8038
+ if (!m) return null;
8039
+ return `https://github.com/${m[1]}/pull/${m[2]}`;
8040
+ }
8041
+ function prNumberFromUrl(url) {
8042
+ const m = normalizePrUrl3(url)?.match(/\/pull\/(\d+)$/);
8043
+ if (!m) return null;
8044
+ const n = Number.parseInt(m[1], 10);
8045
+ return Number.isFinite(n) ? n : null;
8046
+ }
8047
+ function extractText(value) {
8048
+ if (value == null) return "";
8049
+ if (typeof value === "string") return value;
8050
+ try {
8051
+ return JSON.stringify(value);
8052
+ } catch {
8053
+ return "";
8054
+ }
8055
+ }
8056
+ function extractPrNumbersFromText(text) {
8057
+ const out = /* @__PURE__ */ new Set();
8058
+ for (const match of text.matchAll(/github\.com\/[^/\s)>"']+\/[^/\s)>"']+\/(?:pull|pulls)\/(\d+)/gi)) {
8059
+ const n = Number.parseInt(match[1], 10);
8060
+ if (Number.isFinite(n)) out.add(n);
8061
+ }
8062
+ for (const match of text.matchAll(/\bPR\s*#?\s*(\d{2,})\b/gi)) {
8063
+ const n = Number.parseInt(match[1], 10);
8064
+ if (Number.isFinite(n)) out.add(n);
8065
+ }
8066
+ for (const match of text.matchAll(/\bpr[-_]?(\d{2,})\b/gi)) {
8067
+ const n = Number.parseInt(match[1], 10);
8068
+ if (Number.isFinite(n)) out.add(n);
8069
+ }
8070
+ return [...out];
8071
+ }
8072
+ function extractTargetPrReconciliation(value) {
8073
+ let record = null;
8074
+ if (typeof value === "string") record = extractEmbeddedWorkerFinalResultRecord(value);
8075
+ else if (value && typeof value === "object" && !Array.isArray(value)) record = value;
8076
+ if (!record) return [];
8077
+ const raw = record.targetPrReconciliation ?? record.target_pr_reconciliation;
8078
+ if (!Array.isArray(raw)) return [];
8079
+ const out = [];
8080
+ for (const item of raw) {
8081
+ if (!item || typeof item !== "object" || Array.isArray(item)) continue;
8082
+ const row = item;
8083
+ const prUrl = normalizePrUrl3(String(row.prUrl ?? row.pr_url ?? ""));
8084
+ const outcome = String(row.outcome ?? "").trim();
8085
+ if (!prUrl || outcome !== "merged") continue;
8086
+ out.push({
8087
+ prUrl,
8088
+ mergeCommit: typeof row.mergeCommit === "string" ? row.mergeCommit : typeof row.merge_commit === "string" ? row.merge_commit : null,
8089
+ reason: typeof row.reason === "string" ? row.reason : null
8090
+ });
8091
+ }
8092
+ return out;
8093
+ }
8094
+ function defaultLookupPr(input) {
8095
+ try {
8096
+ const repo = execFileSync("git", ["config", "--get", "remote.origin.url"], {
8097
+ cwd: input.repoDir,
8098
+ encoding: "utf8",
8099
+ stdio: ["ignore", "pipe", "ignore"]
8100
+ }).trim();
8101
+ const repoMatch = repo.match(/github\.com[:/]([^/]+\/[^/.]+)(?:\.git)?$/i);
8102
+ const repoSlug = repoMatch?.[1];
8103
+ if (!repoSlug) return null;
8104
+ const raw = execFileSync(
8105
+ "gh",
8106
+ ["pr", "view", String(input.prNumber), "--repo", repoSlug, "--json", "state,mergedAt,mergeCommit,url"],
8107
+ { cwd: input.repoDir, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
8108
+ );
8109
+ const parsed = JSON.parse(raw);
8110
+ return {
8111
+ prUrl: normalizePrUrl3(parsed.url ?? `https://github.com/${repoSlug}/pull/${input.prNumber}`) ?? `https://github.com/${repoSlug}/pull/${input.prNumber}`,
8112
+ state: parsed.state ?? "",
8113
+ mergedAt: parsed.mergedAt ?? null,
8114
+ mergeCommit: parsed.mergeCommit?.oid ?? null
8115
+ };
8116
+ } catch {
8117
+ return null;
8118
+ }
8119
+ }
8120
+ function finalResultForWorker(worker) {
8121
+ const heartbeat = parseHeartbeat(worker.heartbeatPath);
8122
+ return worker.completionSnapshot?.finalResult ?? terminalFinalResultFromHeartbeat(heartbeat);
8123
+ }
8124
+ function collectMergedEvidenceByPr(run) {
8125
+ const byPr = /* @__PURE__ */ new Map();
8126
+ const runDir2 = runDirectory(run.id);
8127
+ for (const name of listRunWorkerNames(run)) {
8128
+ const worker = readJson(
8129
+ path32.join(runDir2, "workers", safeSlug(name), "worker.json"),
8130
+ void 0
8131
+ );
8132
+ if (!worker) continue;
8133
+ for (const evidence of extractTargetPrReconciliation(finalResultForWorker(worker))) {
8134
+ const n = prNumberFromUrl(evidence.prUrl);
8135
+ if (n != null && !byPr.has(n)) byPr.set(n, evidence);
8136
+ }
8137
+ }
8138
+ return byPr;
8139
+ }
8140
+ function candidatePrNumbers(worker, statusFinalResult) {
8141
+ const heartbeat = parseHeartbeat(worker.heartbeatPath);
8142
+ const text = [
8143
+ worker.name,
8144
+ worker.repairTargetPrUrl,
8145
+ worker.taskPrUrl,
8146
+ worker.branch,
8147
+ heartbeat.lastHeartbeatSummary,
8148
+ extractText(statusFinalResult)
8149
+ ].filter(Boolean).join("\n");
8150
+ return extractPrNumbersFromText(text);
8151
+ }
8152
+ function isLocalOnlyAttentionNoise(worker) {
8153
+ return worker.localOnly === true && !worker.taskId && !worker.agentOsId;
8154
+ }
8155
+ function reconcileLocalOnlyMergedPrAttention(options = {}) {
8156
+ const lookupPr = options.lookupPr ?? defaultLookupPr;
8157
+ const outcomes = [];
8158
+ const liveLookupCache = /* @__PURE__ */ new Map();
8159
+ for (const run of listRunRecords()) {
8160
+ const mergedByPr = collectMergedEvidenceByPr(run);
8161
+ const runDir2 = runDirectory(run.id);
8162
+ for (const name of listRunWorkerNames(run)) {
8163
+ const workerPath = path32.join(runDir2, "workers", safeSlug(name), "worker.json");
8164
+ const worker = readJson(workerPath, void 0);
8165
+ if (!worker || !isLocalOnlyAttentionNoise(worker)) continue;
8166
+ const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
8167
+ if (status.attention.state !== "needs_attention") continue;
8168
+ let merged = null;
8169
+ for (const prNumber of candidatePrNumbers(worker, status.finalResult)) {
8170
+ merged = mergedByPr.get(prNumber) ?? null;
8171
+ if (!merged) {
8172
+ const repoDir = run.repo || worker.worktreePath;
8173
+ const cacheKey = `${repoDir}#${prNumber}`;
8174
+ const live = liveLookupCache.has(cacheKey) ? liveLookupCache.get(cacheKey) ?? null : lookupPr({ repoDir, prNumber });
8175
+ if (!liveLookupCache.has(cacheKey)) liveLookupCache.set(cacheKey, live);
8176
+ if (live && (live.state === "MERGED" || live.mergedAt)) {
8177
+ merged = { prUrl: live.prUrl, mergeCommit: live.mergeCommit ?? null, reason: "GitHub reports PR merged" };
8178
+ }
8179
+ }
8180
+ if (merged) break;
8181
+ }
8182
+ if (!merged) {
8183
+ outcomes.push({ runId: run.id, worker: name, action: "skipped", reason: "no merged PR evidence" });
8184
+ continue;
8185
+ }
8186
+ const now = (/* @__PURE__ */ new Date()).toISOString();
8187
+ worker.status = "done";
8188
+ worker.completionSnapshot = {
8189
+ prUrl: merged.prUrl,
8190
+ summary: `Local-only worker superseded by merged PR ${merged.prUrl}`,
8191
+ finalResult: {
8192
+ summary: `Local-only repair/salvage worker superseded by merged PR ${merged.prUrl}`,
8193
+ targetPrReconciliation: [
8194
+ {
8195
+ prUrl: merged.prUrl,
8196
+ outcome: "merged",
8197
+ mergeCommit: merged.mergeCommit ?? null,
8198
+ reason: merged.reason ?? "PR already merged; local-only worker no longer requires attention"
8199
+ }
8200
+ ]
8201
+ }
8202
+ };
8203
+ worker.completionAckSource = "local-pr-merged-reconcile";
8204
+ worker.reconciledAt = now;
8205
+ worker.reconcileReason = "local-only needs_attention superseded by merged PR";
8206
+ saveWorker(run.id, worker);
8207
+ outcomes.push({
8208
+ runId: run.id,
8209
+ worker: name,
8210
+ action: "marked_done",
8211
+ reason: worker.reconcileReason,
8212
+ prUrl: merged.prUrl,
8213
+ mergeCommit: merged.mergeCommit ?? null
8214
+ });
8215
+ }
8216
+ }
8217
+ return { workers: outcomes };
8218
+ }
8219
+
8002
8220
  // src/stale-reconcile.ts
8003
8221
  var STALE_RECONCILE_HEARTBEAT_MS = 15 * 60 * 1e3;
8004
8222
  function staleReconcileDisabled() {
@@ -8007,13 +8225,14 @@ function staleReconcileDisabled() {
8007
8225
  function reconcileStaleWorkers() {
8008
8226
  const metadataReconcile = reconcileWorkerMetadata();
8009
8227
  if (staleReconcileDisabled()) {
8010
- return { workers: [], finalizedRuns: finalizeStaleRuns(), metadataReconcile };
8228
+ const localPrAttentionReconcile2 = reconcileLocalOnlyMergedPrAttention();
8229
+ return { workers: [], finalizedRuns: finalizeStaleRuns(), metadataReconcile, localPrAttentionReconcile: localPrAttentionReconcile2 };
8011
8230
  }
8012
8231
  const outcomes = [];
8013
8232
  const now = Date.now();
8014
8233
  for (const run of listRunRecords()) {
8015
8234
  for (const name of listRunWorkerNames(run)) {
8016
- const workerPath = path32.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
8235
+ const workerPath = path33.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
8017
8236
  const worker = readJson(workerPath, void 0);
8018
8237
  if (!worker || worker.status !== "running") {
8019
8238
  outcomes.push({
@@ -8085,7 +8304,8 @@ function reconcileStaleWorkers() {
8085
8304
  });
8086
8305
  }
8087
8306
  }
8088
- return { workers: outcomes, finalizedRuns: finalizeStaleRuns(), metadataReconcile };
8307
+ const localPrAttentionReconcile = reconcileLocalOnlyMergedPrAttention();
8308
+ return { workers: outcomes, finalizedRuns: finalizeStaleRuns(), metadataReconcile, localPrAttentionReconcile };
8089
8309
  }
8090
8310
  function reconcileRunsCli() {
8091
8311
  const result = reconcileStaleWorkers();
@@ -8096,6 +8316,10 @@ function reconcileRunsCli() {
8096
8316
  acc[row.action] = (acc[row.action] ?? 0) + 1;
8097
8317
  return acc;
8098
8318
  }, {});
8319
+ const localPrAttentionTotals = result.localPrAttentionReconcile.workers.reduce((acc, row) => {
8320
+ acc[row.action] = (acc[row.action] ?? 0) + 1;
8321
+ return acc;
8322
+ }, {});
8099
8323
  const runRetentionTotals = result.metadataReconcile.runMetadataRetention.runs.reduce((acc, row) => {
8100
8324
  acc[row.action] = (acc[row.action] ?? 0) + 1;
8101
8325
  return acc;
@@ -8113,10 +8337,15 @@ function reconcileRunsCli() {
8113
8337
  total: result.metadataReconcile.runMetadataRetention.runs.length
8114
8338
  }
8115
8339
  },
8340
+ localPrAttentionReconcile: {
8341
+ totals: localPrAttentionTotals,
8342
+ total: result.localPrAttentionReconcile.workers.length
8343
+ },
8116
8344
  finalizedRuns: result.finalizedRuns.length,
8117
8345
  details: {
8118
8346
  workers: result.workers,
8119
8347
  metadataReconcile: result.metadataReconcile.workers,
8348
+ localPrAttentionReconcile: result.localPrAttentionReconcile.workers,
8120
8349
  runMetadataRetention: result.metadataReconcile.runMetadataRetention.runs,
8121
8350
  finalizedRuns: result.finalizedRuns
8122
8351
  }
@@ -8137,7 +8366,7 @@ function heartbeatByteLength(heartbeatPath) {
8137
8366
  }
8138
8367
  }
8139
8368
  function workerEvidence(run, workerName) {
8140
- const workerPath = path33.join(runDirectory(run.id), "workers", safeSlug(workerName), "worker.json");
8369
+ const workerPath = path34.join(runDirectory(run.id), "workers", safeSlug(workerName), "worker.json");
8141
8370
  const worker = readJson(workerPath, void 0);
8142
8371
  if (!worker) {
8143
8372
  return {
@@ -8194,7 +8423,7 @@ function aggregateRunAttention(workers) {
8194
8423
  function countOpenWorkers(run) {
8195
8424
  let open = 0;
8196
8425
  for (const name of listRunWorkerNames(run)) {
8197
- const workerPath = path33.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
8426
+ const workerPath = path34.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
8198
8427
  const worker = readJson(workerPath, void 0);
8199
8428
  if (!worker) continue;
8200
8429
  const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
@@ -8270,7 +8499,7 @@ function createRun(args) {
8270
8499
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
8271
8500
  workers: {}
8272
8501
  };
8273
- writeJson(path34.join(dir, "run.json"), run);
8502
+ writeJson(path35.join(dir, "run.json"), run);
8274
8503
  console.log(JSON.stringify({ runId: id, runDir: dir, repo, base, baseCommit }, null, 2));
8275
8504
  }
8276
8505
  function listRuns() {
@@ -8283,7 +8512,7 @@ function failExists(message) {
8283
8512
 
8284
8513
  // src/discard-disposable.ts
8285
8514
  import { existsSync as existsSync26, rmSync as rmSync2 } from "node:fs";
8286
- import path35 from "node:path";
8515
+ import path36 from "node:path";
8287
8516
  function normalizeRelativePath2(value) {
8288
8517
  const normalized = value.replace(/\\/g, "/").replace(/^\.\//, "").trim();
8289
8518
  if (!normalized || normalized.startsWith("/") || normalized.includes("..")) {
@@ -8305,12 +8534,12 @@ function discardDisposableArtifacts(args) {
8305
8534
  if (paths.length === 0) {
8306
8535
  return { ok: false, removed: [], reason: "requires at least one --path" };
8307
8536
  }
8308
- const worktreeRoot = path35.resolve(worker.worktreePath);
8537
+ const worktreeRoot = path36.resolve(worker.worktreePath);
8309
8538
  const removed = [];
8310
8539
  for (const raw of paths) {
8311
8540
  const rel = normalizeRelativePath2(raw);
8312
- const abs = path35.resolve(worktreeRoot, rel);
8313
- if (!abs.startsWith(worktreeRoot + path35.sep) && abs !== worktreeRoot) {
8541
+ const abs = path36.resolve(worktreeRoot, rel);
8542
+ if (!abs.startsWith(worktreeRoot + path36.sep) && abs !== worktreeRoot) {
8314
8543
  return { ok: false, removed, reason: `path escapes worktree: ${raw}` };
8315
8544
  }
8316
8545
  if (!existsSync26(abs)) {
@@ -8377,7 +8606,7 @@ function validateDaemonInstallIdentity(config = loadUserConfig(), env = process.
8377
8606
  // src/cron/cron-env.ts
8378
8607
  import { existsSync as existsSync27 } from "node:fs";
8379
8608
  import { homedir as homedir11 } from "node:os";
8380
- import path36 from "node:path";
8609
+ import path37 from "node:path";
8381
8610
  function envFlag(name, defaultValue) {
8382
8611
  const raw = process.env[name]?.trim().toLowerCase();
8383
8612
  if (!raw) return defaultValue;
@@ -8393,7 +8622,7 @@ function envInt(name, fallback, min = 1) {
8393
8622
  function defaultKynverCronStorePath() {
8394
8623
  const explicit = process.env.KYNVER_CRON_STORE_PATH?.trim() || process.env.OPENCLAW_CRON_STORE_PATH?.trim();
8395
8624
  if (explicit) return explicit;
8396
- return path36.join(homedir11(), ".kynver", "agent-os-cron.json");
8625
+ return path37.join(homedir11(), ".kynver", "agent-os-cron.json");
8397
8626
  }
8398
8627
  function defaultKynverCronStatePath(storePath = defaultKynverCronStorePath()) {
8399
8628
  const explicit = process.env.KYNVER_CRON_TICK_STATE_PATH?.trim();
@@ -8635,7 +8864,7 @@ async function loadCronJobs(storePath = defaultKynverCronStorePath()) {
8635
8864
  // src/cron/cron-tick-state.ts
8636
8865
  import { randomBytes } from "node:crypto";
8637
8866
  import { promises as fs2 } from "node:fs";
8638
- import path37 from "node:path";
8867
+ import path38 from "node:path";
8639
8868
  var EMPTY = { version: 1, jobs: {} };
8640
8869
  async function readFileIfExists2(filePath) {
8641
8870
  try {
@@ -8662,7 +8891,7 @@ async function loadCronTickState(statePath) {
8662
8891
  return parseCronTickState(raw);
8663
8892
  }
8664
8893
  async function writeStateAtomic(statePath, state) {
8665
- await fs2.mkdir(path37.dirname(statePath), { recursive: true });
8894
+ await fs2.mkdir(path38.dirname(statePath), { recursive: true });
8666
8895
  const suffix = randomBytes(6).toString("hex");
8667
8896
  const tmp = `${statePath}.tmp-${process.pid}-${Date.now()}-${suffix}`;
8668
8897
  await fs2.writeFile(tmp, `${JSON.stringify(state, null, 2)}
@@ -8839,7 +9068,7 @@ async function runKynverCronTick(opts = {}) {
8839
9068
  }
8840
9069
 
8841
9070
  // src/pipeline-tick.ts
8842
- import path55 from "node:path";
9071
+ import path56 from "node:path";
8843
9072
 
8844
9073
  // src/pipeline-dispatch.ts
8845
9074
  var RESERVED_REVIEW_STARTS = 1;
@@ -8995,7 +9224,7 @@ function buildBoxResourceSnapshotFromGate(gate, input = {}) {
8995
9224
  }
8996
9225
 
8997
9226
  // src/plan-progress-daemon-sync.ts
8998
- import path38 from "node:path";
9227
+ import path39 from "node:path";
8999
9228
 
9000
9229
  // src/plan-progress-sync.ts
9001
9230
  async function syncPlanProgress(args) {
@@ -9019,7 +9248,7 @@ async function syncActiveWorkerPlanProgress(runId, args) {
9019
9248
  const outcomes = [];
9020
9249
  for (const name of Object.keys(run.workers || {})) {
9021
9250
  const worker = readJson(
9022
- path38.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
9251
+ path39.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
9023
9252
  void 0
9024
9253
  );
9025
9254
  if (!worker?.dispatched || !worker.taskId) continue;
@@ -9076,10 +9305,10 @@ async function fetchWorkspaceRuntimePreferences(agentOsId, args) {
9076
9305
  }
9077
9306
 
9078
9307
  // src/cleanup.ts
9079
- import path52 from "node:path";
9308
+ import path53 from "node:path";
9080
9309
 
9081
9310
  // src/cleanup-guards.ts
9082
- import path39 from "node:path";
9311
+ import path40 from "node:path";
9083
9312
 
9084
9313
  // src/cleanup-build-cache-paths.ts
9085
9314
  var HARNESS_BUILD_CACHE_RELATIVE_PATHS = [
@@ -9220,7 +9449,7 @@ function skipWorktreeRemoval(input) {
9220
9449
  function skipDependencyCacheRemoval(input) {
9221
9450
  const { indexed, nodeModulesAgeMs, ageMs, worktreePath, activeWorktreePaths, diskPressure } = input;
9222
9451
  if (!diskPressure && ageMs < nodeModulesAgeMs) return "below_age_threshold";
9223
- if (activeWorktreePaths.has(path39.resolve(worktreePath))) return "active_worker";
9452
+ if (activeWorktreePaths.has(path40.resolve(worktreePath))) return "active_worker";
9224
9453
  if (indexed && isWorkerProcessLive(indexed)) return "active_worker";
9225
9454
  if (indexed && indexedWorktreeHasMaterialChanges(indexed)) return "dirty_worktree";
9226
9455
  return null;
@@ -9247,11 +9476,11 @@ var LIVE_SKIP_REASONS = /* @__PURE__ */ new Set([
9247
9476
  function collectPreservedLivePaths(actions, skips) {
9248
9477
  const out = [];
9249
9478
  const seen = /* @__PURE__ */ new Set();
9250
- const push = (path69, reason, detail) => {
9251
- const key = `${path69}\0${reason}`;
9479
+ const push = (path71, reason, detail) => {
9480
+ const key = `${path71}\0${reason}`;
9252
9481
  if (seen.has(key) || out.length >= MAX_PRESERVED_LIVE_PATH_SAMPLES) return;
9253
9482
  seen.add(key);
9254
- out.push({ path: path69, reason, ...detail ? { detail } : {} });
9483
+ out.push({ path: path71, reason, ...detail ? { detail } : {} });
9255
9484
  };
9256
9485
  for (const skip2 of skips) {
9257
9486
  if (!LIVE_SKIP_REASONS.has(skip2.reason)) continue;
@@ -9267,11 +9496,11 @@ function collectPreservedLivePaths(actions, skips) {
9267
9496
 
9268
9497
  // src/cleanup-run-directory.ts
9269
9498
  import { existsSync as existsSync30, readdirSync as readdirSync9, statSync as statSync7 } from "node:fs";
9270
- import path41 from "node:path";
9499
+ import path42 from "node:path";
9271
9500
 
9272
9501
  // src/cleanup-active-worktrees.ts
9273
9502
  import { existsSync as existsSync29, readdirSync as readdirSync8, statSync as statSync6 } from "node:fs";
9274
- import path40 from "node:path";
9503
+ import path41 from "node:path";
9275
9504
  function workerHasRecentHarnessActivity(worker, now) {
9276
9505
  const paths = [worker.heartbeatPath, worker.stdoutPath, worker.stderrPath];
9277
9506
  for (const target of paths) {
@@ -9297,11 +9526,11 @@ function collectActiveWorktreeGuards(harnessRoots, now = Date.now()) {
9297
9526
  let runHasLive = false;
9298
9527
  for (const name of Object.keys(run.workers || {})) {
9299
9528
  const worker = readJson(
9300
- path40.join(runDirectoryAt(harnessRoot, run.id), "workers", safeSlug(name), "worker.json"),
9529
+ path41.join(runDirectoryAt(harnessRoot, run.id), "workers", safeSlug(name), "worker.json"),
9301
9530
  void 0
9302
9531
  );
9303
9532
  if (!worker?.worktreePath) continue;
9304
- const worktreePath = path40.resolve(worker.worktreePath);
9533
+ const worktreePath = path41.resolve(worker.worktreePath);
9305
9534
  if (!isActiveHarnessWorker2(worker, now)) continue;
9306
9535
  runHasLive = true;
9307
9536
  activeWorktreePaths.add(worktreePath);
@@ -9329,7 +9558,7 @@ function pathAgeMs(target, now) {
9329
9558
  }
9330
9559
  }
9331
9560
  function loadRunStatus(harnessRoot, runId) {
9332
- const runPath = path41.join(harnessRoot, "runs", runId, "run.json");
9561
+ const runPath = path42.join(harnessRoot, "runs", runId, "run.json");
9333
9562
  if (!existsSync30(runPath)) return null;
9334
9563
  return readJson(runPath, null);
9335
9564
  }
@@ -9365,7 +9594,7 @@ function scanStaleRunDirectoryCandidates(opts) {
9365
9594
  if (!runEntry.isDirectory()) continue;
9366
9595
  const runId = runEntry.name;
9367
9596
  if (opts.runIdFilter && runId !== opts.runIdFilter) continue;
9368
- const runPath = path41.join(opts.worktreesDir, runId);
9597
+ const runPath = path42.join(opts.worktreesDir, runId);
9369
9598
  if (!runDirectoryIsEmpty(runPath)) continue;
9370
9599
  candidates.push({
9371
9600
  kind: "remove_run_directory",
@@ -9383,14 +9612,14 @@ function scanStaleRunDirectoryCandidates(opts) {
9383
9612
  import { existsSync as existsSync33, rmSync as rmSync4 } from "node:fs";
9384
9613
 
9385
9614
  // src/cleanup-dir-size.ts
9386
- import { execFileSync } from "node:child_process";
9615
+ import { execFileSync as execFileSync2 } from "node:child_process";
9387
9616
  import { existsSync as existsSync31, readdirSync as readdirSync10, statSync as statSync8 } from "node:fs";
9388
- import path42 from "node:path";
9617
+ import path43 from "node:path";
9389
9618
  var DEFAULT_DU_TIMEOUT_MS = 2500;
9390
9619
  function directorySizeBytesDu(root, timeoutMs = DEFAULT_DU_TIMEOUT_MS) {
9391
9620
  if (!existsSync31(root)) return 0;
9392
9621
  try {
9393
- const out = execFileSync("du", ["-sb", root], {
9622
+ const out = execFileSync2("du", ["-sb", root], {
9394
9623
  encoding: "utf8",
9395
9624
  timeout: timeoutMs,
9396
9625
  stdio: ["ignore", "pipe", "ignore"]
@@ -9419,7 +9648,7 @@ function directorySizeBytes(root, maxEntries = 5e4) {
9419
9648
  }
9420
9649
  for (const name of entries) {
9421
9650
  if (seen++ > maxEntries) return null;
9422
- const full = path42.join(current, name);
9651
+ const full = path43.join(current, name);
9423
9652
  let st;
9424
9653
  try {
9425
9654
  st = statSync8(full);
@@ -9470,20 +9699,20 @@ function pathHasForeignOwnedEntry(targetPath, maxEntries = 32) {
9470
9699
 
9471
9700
  // src/cleanup-privileged-remove.ts
9472
9701
  import { spawnSync as spawnSync4 } from "node:child_process";
9473
- import path44 from "node:path";
9702
+ import path45 from "node:path";
9474
9703
 
9475
9704
  // src/cleanup-harness-path-validate.ts
9476
- import path43 from "node:path";
9705
+ import path44 from "node:path";
9477
9706
  function isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, cacheDirName) {
9478
- const resolved = path43.resolve(targetPath);
9479
- const suffix = `${path43.sep}${cacheDirName}`;
9707
+ const resolved = path44.resolve(targetPath);
9708
+ const suffix = `${path44.sep}${cacheDirName}`;
9480
9709
  const cachePath = resolved.endsWith(suffix) ? resolved : null;
9481
9710
  if (!cachePath) return "path_outside_harness";
9482
- const rel = path43.relative(worktreesDir, cachePath);
9483
- if (rel.startsWith("..") || path43.isAbsolute(rel)) return "path_outside_harness";
9484
- const parts = rel.split(path43.sep);
9711
+ const rel = path44.relative(worktreesDir, cachePath);
9712
+ if (rel.startsWith("..") || path44.isAbsolute(rel)) return "path_outside_harness";
9713
+ const parts = rel.split(path44.sep);
9485
9714
  if (parts.length < 3 || parts[parts.length - 1] !== cacheDirName) return "path_outside_harness";
9486
- if (!resolved.startsWith(path43.resolve(harnessRoot))) return "path_outside_harness";
9715
+ if (!resolved.startsWith(path44.resolve(harnessRoot))) return "path_outside_harness";
9487
9716
  return null;
9488
9717
  }
9489
9718
  function isHarnessNodeModulesPath(targetPath, harnessRoot, worktreesDir) {
@@ -9493,16 +9722,16 @@ function isHarnessNextCachePath(targetPath, harnessRoot, worktreesDir) {
9493
9722
  return isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, ".next");
9494
9723
  }
9495
9724
  function isHarnessBuildCachePath(targetPath, harnessRoot, worktreesDir) {
9496
- const resolved = path43.resolve(targetPath);
9497
- const relToWt = path43.relative(worktreesDir, resolved);
9498
- if (relToWt.startsWith("..") || path43.isAbsolute(relToWt)) return "path_outside_harness";
9499
- const parts = relToWt.split(path43.sep);
9725
+ const resolved = path44.resolve(targetPath);
9726
+ const relToWt = path44.relative(worktreesDir, resolved);
9727
+ if (relToWt.startsWith("..") || path44.isAbsolute(relToWt)) return "path_outside_harness";
9728
+ const parts = relToWt.split(path44.sep);
9500
9729
  if (parts.length < 3) return "path_outside_harness";
9501
- if (!resolved.startsWith(path43.resolve(harnessRoot))) return "path_outside_harness";
9730
+ if (!resolved.startsWith(path44.resolve(harnessRoot))) return "path_outside_harness";
9502
9731
  return null;
9503
9732
  }
9504
9733
  function isHarnessGeneratedCachePath(targetPath, harnessRoot, worktreesDir) {
9505
- const resolved = path43.resolve(targetPath);
9734
+ const resolved = path44.resolve(targetPath);
9506
9735
  return isHarnessNodeModulesPath(resolved, harnessRoot, worktreesDir) === null || isHarnessNextCachePath(resolved, harnessRoot, worktreesDir) === null || isHarnessBuildCachePath(resolved, harnessRoot, worktreesDir) === null;
9507
9736
  }
9508
9737
 
@@ -9536,12 +9765,12 @@ function tryPrivilegedReclaimHarnessCache(targetPath, harnessRoot, worktreesDir)
9536
9765
  "chown",
9537
9766
  "-R",
9538
9767
  `${effectiveUid}:${effectiveGid}`,
9539
- path44.resolve(targetPath)
9768
+ path45.resolve(targetPath)
9540
9769
  ]);
9541
9770
  if (chown.ok) {
9542
9771
  return { ok: true, method: "chown_then_rm" };
9543
9772
  }
9544
- const rm = runSudoNonInteractive(["rm", "-rf", path44.resolve(targetPath)]);
9773
+ const rm = runSudoNonInteractive(["rm", "-rf", path45.resolve(targetPath)]);
9545
9774
  if (rm.ok) {
9546
9775
  return { ok: true, method: "sudo_rm" };
9547
9776
  }
@@ -9743,7 +9972,7 @@ function removeWorktree(candidate, execute) {
9743
9972
 
9744
9973
  // src/cleanup-scan.ts
9745
9974
  import { existsSync as existsSync34, readdirSync as readdirSync12, statSync as statSync9 } from "node:fs";
9746
- import path45 from "node:path";
9975
+ import path46 from "node:path";
9747
9976
  function pathAgeMs2(target, now) {
9748
9977
  try {
9749
9978
  const mtime = statSync9(target).mtimeMs;
@@ -9753,16 +9982,16 @@ function pathAgeMs2(target, now) {
9753
9982
  }
9754
9983
  }
9755
9984
  function isPathInside(child, parent) {
9756
- const rel = path45.relative(parent, child);
9757
- return rel === "" || !rel.startsWith("..") && !path45.isAbsolute(rel);
9985
+ const rel = path46.relative(parent, child);
9986
+ return rel === "" || !rel.startsWith("..") && !path46.isAbsolute(rel);
9758
9987
  }
9759
9988
  function collectBuildCacheForWorktree(worktreePath, opts, seen, meta) {
9760
9989
  const out = [];
9761
9990
  for (const rel of HARNESS_BUILD_CACHE_RELATIVE_PATHS) {
9762
9991
  if (rel === ".next") continue;
9763
- const target = path45.join(worktreePath, rel);
9992
+ const target = path46.join(worktreePath, rel);
9764
9993
  if (!existsSync34(target)) continue;
9765
- const resolved = path45.resolve(target);
9994
+ const resolved = path46.resolve(target);
9766
9995
  if (seen.has(resolved)) continue;
9767
9996
  if (!isPathInside(resolved, opts.harnessRoot)) continue;
9768
9997
  seen.add(resolved);
@@ -9794,10 +10023,10 @@ function scanBuildCacheCandidates(opts) {
9794
10023
  if (!opts.includeOrphans || !existsSync34(opts.worktreesDir)) return candidates;
9795
10024
  for (const runEntry of readdirSync12(opts.worktreesDir, { withFileTypes: true })) {
9796
10025
  if (!runEntry.isDirectory()) continue;
9797
- const runPath = path45.join(opts.worktreesDir, runEntry.name);
10026
+ const runPath = path46.join(opts.worktreesDir, runEntry.name);
9798
10027
  for (const workerEntry of readdirSync12(runPath, { withFileTypes: true })) {
9799
10028
  if (!workerEntry.isDirectory()) continue;
9800
- const worktreePath = path45.join(runPath, workerEntry.name);
10029
+ const worktreePath = path46.join(runPath, workerEntry.name);
9801
10030
  candidates.push(
9802
10031
  ...collectBuildCacheForWorktree(worktreePath, opts, seen, {
9803
10032
  runId: runEntry.name,
@@ -9835,12 +10064,12 @@ function scanWorktreeCandidates(opts) {
9835
10064
  if (!orphanEnabled || !existsSync34(opts.worktreesDir)) return candidates;
9836
10065
  const indexedPaths = /* @__PURE__ */ new Set();
9837
10066
  for (const entry of opts.index.values()) {
9838
- indexedPaths.add(path45.resolve(entry.worktreePath));
10067
+ indexedPaths.add(path46.resolve(entry.worktreePath));
9839
10068
  }
9840
10069
  for (const runEntry of readdirSync12(opts.worktreesDir, { withFileTypes: true })) {
9841
10070
  if (!runEntry.isDirectory()) continue;
9842
10071
  if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
9843
- const runPath = path45.join(opts.worktreesDir, runEntry.name);
10072
+ const runPath = path46.join(opts.worktreesDir, runEntry.name);
9844
10073
  let workerEntries;
9845
10074
  try {
9846
10075
  workerEntries = readdirSync12(runPath, { withFileTypes: true });
@@ -9849,7 +10078,7 @@ function scanWorktreeCandidates(opts) {
9849
10078
  }
9850
10079
  for (const workerEntry of workerEntries) {
9851
10080
  if (!workerEntry.isDirectory()) continue;
9852
- const worktreePath = path45.resolve(path45.join(runPath, workerEntry.name));
10081
+ const worktreePath = path46.resolve(path46.join(runPath, workerEntry.name));
9853
10082
  if (seen.has(worktreePath)) continue;
9854
10083
  if (indexedPaths.has(worktreePath)) continue;
9855
10084
  if (!isPathInside(worktreePath, opts.harnessRoot)) continue;
@@ -9869,7 +10098,7 @@ function scanWorktreeCandidates(opts) {
9869
10098
 
9870
10099
  // src/cleanup-dependency-scan.ts
9871
10100
  import { existsSync as existsSync35, readdirSync as readdirSync13, statSync as statSync10 } from "node:fs";
9872
- import path46 from "node:path";
10101
+ import path47 from "node:path";
9873
10102
  var DEPENDENCY_CACHE_DIRS = [
9874
10103
  { dirName: "node_modules", kind: "remove_node_modules" },
9875
10104
  { dirName: ".next", kind: "remove_next_cache" }
@@ -9883,12 +10112,12 @@ function pathAgeMs3(target, now) {
9883
10112
  }
9884
10113
  }
9885
10114
  function isPathInside2(child, parent) {
9886
- const rel = path46.relative(parent, child);
9887
- return rel === "" || !rel.startsWith("..") && !path46.isAbsolute(rel);
10115
+ const rel = path47.relative(parent, child);
10116
+ return rel === "" || !rel.startsWith("..") && !path47.isAbsolute(rel);
9888
10117
  }
9889
10118
  function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
9890
10119
  if (!existsSync35(targetPath)) return;
9891
- const resolved = path46.resolve(targetPath);
10120
+ const resolved = path47.resolve(targetPath);
9892
10121
  if (seen.has(resolved)) return;
9893
10122
  if (!isPathInside2(resolved, opts.harnessRoot)) return;
9894
10123
  seen.add(resolved);
@@ -9905,7 +10134,7 @@ function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
9905
10134
  }
9906
10135
  function scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, meta) {
9907
10136
  for (const entry of DEPENDENCY_CACHE_DIRS) {
9908
- pushCandidate2(candidates, seen, opts, path46.join(worktreePath, entry.dirName), entry.kind, meta);
10137
+ pushCandidate2(candidates, seen, opts, path47.join(worktreePath, entry.dirName), entry.kind, meta);
9909
10138
  }
9910
10139
  }
9911
10140
  function scanDependencyCacheCandidates(opts) {
@@ -9923,7 +10152,7 @@ function scanDependencyCacheCandidates(opts) {
9923
10152
  for (const runEntry of readdirSync13(opts.worktreesDir, { withFileTypes: true })) {
9924
10153
  if (!runEntry.isDirectory()) continue;
9925
10154
  if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
9926
- const runPath = path46.join(opts.worktreesDir, runEntry.name);
10155
+ const runPath = path47.join(opts.worktreesDir, runEntry.name);
9927
10156
  let workerEntries;
9928
10157
  try {
9929
10158
  workerEntries = readdirSync13(runPath, { withFileTypes: true });
@@ -9932,7 +10161,7 @@ function scanDependencyCacheCandidates(opts) {
9932
10161
  }
9933
10162
  for (const workerEntry of workerEntries) {
9934
10163
  if (!workerEntry.isDirectory()) continue;
9935
- const worktreePath = path46.join(runPath, workerEntry.name);
10164
+ const worktreePath = path47.join(runPath, workerEntry.name);
9936
10165
  scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, {
9937
10166
  runId: runEntry.name,
9938
10167
  worker: workerEntry.name
@@ -9944,7 +10173,7 @@ function scanDependencyCacheCandidates(opts) {
9944
10173
 
9945
10174
  // src/cleanup-duplicate-worktrees.ts
9946
10175
  import { existsSync as existsSync36, statSync as statSync11 } from "node:fs";
9947
- import path47 from "node:path";
10176
+ import path48 from "node:path";
9948
10177
  function pathAgeMs4(target, now) {
9949
10178
  try {
9950
10179
  const mtime = statSync11(target).mtimeMs;
@@ -9974,8 +10203,8 @@ function parseWorktreePorcelain(output) {
9974
10203
  return records;
9975
10204
  }
9976
10205
  function isUnderWorktreesDir(worktreePath, worktreesDir) {
9977
- const rel = path47.relative(path47.resolve(worktreesDir), path47.resolve(worktreePath));
9978
- return rel !== "" && !rel.startsWith("..") && !path47.isAbsolute(rel);
10206
+ const rel = path48.relative(path48.resolve(worktreesDir), path48.resolve(worktreePath));
10207
+ return rel !== "" && !rel.startsWith("..") && !path48.isAbsolute(rel);
9979
10208
  }
9980
10209
  function isCleanWorktree(worktreePath, repoRoot) {
9981
10210
  try {
@@ -9991,11 +10220,11 @@ function scanDuplicateWorktreeCandidates(opts) {
9991
10220
  if (!opts.includeOrphans || !existsSync36(opts.worktreesDir)) return [];
9992
10221
  const repos = /* @__PURE__ */ new Set();
9993
10222
  for (const entry of opts.index.values()) {
9994
- if (entry.run.repo) repos.add(path47.resolve(entry.run.repo));
10223
+ if (entry.run.repo) repos.add(path48.resolve(entry.run.repo));
9995
10224
  }
9996
10225
  const indexedPaths = /* @__PURE__ */ new Set();
9997
10226
  for (const entry of opts.index.values()) {
9998
- indexedPaths.add(path47.resolve(entry.worktreePath));
10227
+ indexedPaths.add(path48.resolve(entry.worktreePath));
9999
10228
  }
10000
10229
  const candidates = [];
10001
10230
  const seen = /* @__PURE__ */ new Set();
@@ -10008,15 +10237,15 @@ function scanDuplicateWorktreeCandidates(opts) {
10008
10237
  }
10009
10238
  const worktrees = parseWorktreePorcelain(porcelain);
10010
10239
  for (const wt of worktrees) {
10011
- const resolved = path47.resolve(wt.path);
10012
- if (resolved === path47.resolve(repoRoot)) continue;
10240
+ const resolved = path48.resolve(wt.path);
10241
+ if (resolved === path48.resolve(repoRoot)) continue;
10013
10242
  if (!isUnderWorktreesDir(resolved, opts.worktreesDir)) continue;
10014
10243
  if (indexedPaths.has(resolved)) continue;
10015
10244
  if (seen.has(resolved)) continue;
10016
10245
  if (!existsSync36(resolved)) continue;
10017
10246
  if (!isCleanWorktree(resolved, repoRoot)) continue;
10018
- const rel = path47.relative(opts.worktreesDir, resolved);
10019
- const parts = rel.split(path47.sep);
10247
+ const rel = path48.relative(opts.worktreesDir, resolved);
10248
+ const parts = rel.split(path48.sep);
10020
10249
  const runId = parts[0];
10021
10250
  const worker = parts[1] ?? "unknown";
10022
10251
  seen.add(resolved);
@@ -10035,12 +10264,12 @@ function scanDuplicateWorktreeCandidates(opts) {
10035
10264
  }
10036
10265
 
10037
10266
  // src/cleanup-worktree-index.ts
10038
- import path48 from "node:path";
10267
+ import path49 from "node:path";
10039
10268
  function buildWorktreeIndexAt(harnessRoot) {
10040
10269
  const index = /* @__PURE__ */ new Map();
10041
10270
  for (const run of listRunRecordsForHarnessRoot(harnessRoot)) {
10042
10271
  for (const name of Object.keys(run.workers || {})) {
10043
- const workerPath = path48.join(
10272
+ const workerPath = path49.join(
10044
10273
  runDirectoryAt(harnessRoot, run.id),
10045
10274
  "workers",
10046
10275
  safeSlug(name),
@@ -10048,9 +10277,9 @@ function buildWorktreeIndexAt(harnessRoot) {
10048
10277
  );
10049
10278
  const worker = readJson(workerPath, void 0);
10050
10279
  if (!worker?.worktreePath) continue;
10051
- index.set(path48.resolve(worker.worktreePath), {
10280
+ index.set(path49.resolve(worker.worktreePath), {
10052
10281
  harnessRoot,
10053
- worktreePath: path48.resolve(worker.worktreePath),
10282
+ worktreePath: path49.resolve(worker.worktreePath),
10054
10283
  runId: run.id,
10055
10284
  workerName: name,
10056
10285
  run,
@@ -10120,14 +10349,14 @@ function resolvePipelineHarnessRetention(runId) {
10120
10349
 
10121
10350
  // src/cleanup-orphan-safety.ts
10122
10351
  import { existsSync as existsSync37, statSync as statSync12 } from "node:fs";
10123
- import path49 from "node:path";
10352
+ import path50 from "node:path";
10124
10353
  var DEFAULT_HEARTBEAT_FRESH_MS = 30 * 60 * 1e3;
10125
10354
  function assessOrphanWorktreeSafety(input) {
10126
10355
  const now = input.now ?? Date.now();
10127
10356
  const heartbeatFreshMs = input.heartbeatFreshMs ?? DEFAULT_HEARTBEAT_FRESH_MS;
10128
10357
  if (!existsSync37(input.worktreePath)) return null;
10129
10358
  if (input.runId && input.workerName) {
10130
- const heartbeatPath = path49.join(
10359
+ const heartbeatPath = path50.join(
10131
10360
  input.harnessRoot,
10132
10361
  "runs",
10133
10362
  input.runId,
@@ -10141,7 +10370,7 @@ function assessOrphanWorktreeSafety(input) {
10141
10370
  } catch {
10142
10371
  }
10143
10372
  }
10144
- const gitDir = path49.join(input.worktreePath, ".git");
10373
+ const gitDir = path50.join(input.worktreePath, ".git");
10145
10374
  if (!existsSync37(gitDir)) return null;
10146
10375
  const porcelain = gitCapture(input.worktreePath, ["status", "--porcelain"]);
10147
10376
  if (porcelain.status !== 0) return "pr_or_unmerged_commits";
@@ -10172,7 +10401,7 @@ function assessOrphanWorktreeSafety(input) {
10172
10401
 
10173
10402
  // src/harness-storage-snapshot.ts
10174
10403
  import { existsSync as existsSync38, readdirSync as readdirSync14, statSync as statSync13 } from "node:fs";
10175
- import path50 from "node:path";
10404
+ import path51 from "node:path";
10176
10405
  function harnessStorageSnapshot(opts = {}) {
10177
10406
  const harnessRoot = normalizeHarnessRoot(opts.harnessRoot ?? resolveHarnessRoot());
10178
10407
  const worktreesDir = harnessWorktreesDir(harnessRoot);
@@ -10210,7 +10439,7 @@ function harnessStorageSnapshot(opts = {}) {
10210
10439
  for (const runEntry of entries) {
10211
10440
  if (!runEntry.isDirectory()) continue;
10212
10441
  runCount += 1;
10213
- const runPath = path50.join(worktreesDir, runEntry.name);
10442
+ const runPath = path51.join(worktreesDir, runEntry.name);
10214
10443
  try {
10215
10444
  const st = statSync13(runPath);
10216
10445
  oldestMs = oldestMs === null ? st.mtimeMs : Math.min(oldestMs, st.mtimeMs);
@@ -10247,10 +10476,10 @@ function harnessStorageSnapshot(opts = {}) {
10247
10476
  // src/cleanup-harness-roots.ts
10248
10477
  import { existsSync as existsSync39 } from "node:fs";
10249
10478
  import { homedir as homedir12 } from "node:os";
10250
- import path51 from "node:path";
10479
+ import path52 from "node:path";
10251
10480
  var WELL_KNOWN_HARNESS_SCAN_ROOTS = [
10252
10481
  "/var/tmp/kynver-harness",
10253
- path51.join(homedir12(), ".openclaw", "harness")
10482
+ path52.join(homedir12(), ".openclaw", "harness")
10254
10483
  ];
10255
10484
  function addRoot(seen, roots, candidate) {
10256
10485
  if (!candidate?.trim()) return;
@@ -10272,7 +10501,7 @@ function resolveHarnessScanRoots(options = {}) {
10272
10501
  for (const candidate of extra ?? []) addRoot(seen, roots, candidate);
10273
10502
  if (shouldScanWellKnownRoots(options)) {
10274
10503
  for (const candidate of WELL_KNOWN_HARNESS_SCAN_ROOTS) {
10275
- const resolved = path51.resolve(candidate);
10504
+ const resolved = path52.resolve(candidate);
10276
10505
  if (!seen.has(resolved) && existsSync39(resolved)) addRoot(seen, roots, resolved);
10277
10506
  }
10278
10507
  }
@@ -10427,9 +10656,9 @@ function mergeWorktreeIndexes(scanRoots) {
10427
10656
  }
10428
10657
  function worktreePathForCandidate(candidate, worktreesDir) {
10429
10658
  if (candidate.runId && candidate.worker) {
10430
- return path52.join(worktreesDir, candidate.runId, candidate.worker);
10659
+ return path53.join(worktreesDir, candidate.runId, candidate.worker);
10431
10660
  }
10432
- return path52.resolve(candidate.path, "..");
10661
+ return path53.resolve(candidate.path, "..");
10433
10662
  }
10434
10663
  function runHarnessCleanup(options = {}) {
10435
10664
  let retention = resolveHarnessRetention(options);
@@ -10454,7 +10683,7 @@ function runHarnessCleanup(options = {}) {
10454
10683
  for (const harnessRoot of paths.scanRoots) {
10455
10684
  if (atSweepCap()) break;
10456
10685
  emitCleanupProgress("root", harnessRoot);
10457
- const worktreesDir = path52.join(harnessRoot, "worktrees");
10686
+ const worktreesDir = path53.join(harnessRoot, "worktrees");
10458
10687
  const scanOpts = {
10459
10688
  harnessRoot,
10460
10689
  worktreesDir,
@@ -10467,7 +10696,7 @@ function runHarnessCleanup(options = {}) {
10467
10696
  };
10468
10697
  for (const raw of scanDependencyCacheCandidates(scanOpts)) {
10469
10698
  if (atSweepCap()) break;
10470
- const resolved = path52.resolve(raw.path);
10699
+ const resolved = path53.resolve(raw.path);
10471
10700
  if (processedPaths.has(resolved)) continue;
10472
10701
  processedPaths.add(resolved);
10473
10702
  const candidate = { ...raw, path: resolved };
@@ -10478,7 +10707,7 @@ function runHarnessCleanup(options = {}) {
10478
10707
  continue;
10479
10708
  }
10480
10709
  const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
10481
- const indexed = index.get(path52.resolve(worktreePath)) ?? null;
10710
+ const indexed = index.get(path53.resolve(worktreePath)) ?? null;
10482
10711
  const guardReason = skipDependencyCacheRemoval({
10483
10712
  indexed,
10484
10713
  includeOrphans: true,
@@ -10502,7 +10731,7 @@ function runHarnessCleanup(options = {}) {
10502
10731
  }
10503
10732
  for (const raw of scanBuildCacheCandidates(scanOpts)) {
10504
10733
  if (atSweepCap()) break;
10505
- const resolved = path52.resolve(raw.path);
10734
+ const resolved = path53.resolve(raw.path);
10506
10735
  if (processedPaths.has(resolved)) continue;
10507
10736
  processedPaths.add(resolved);
10508
10737
  const candidate = { ...raw, path: resolved };
@@ -10513,7 +10742,7 @@ function runHarnessCleanup(options = {}) {
10513
10742
  continue;
10514
10743
  }
10515
10744
  const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
10516
- const indexed = index.get(path52.resolve(worktreePath)) ?? null;
10745
+ const indexed = index.get(path53.resolve(worktreePath)) ?? null;
10517
10746
  const guardReason = skipBuildCacheRemoval({
10518
10747
  indexed,
10519
10748
  includeOrphans: true,
@@ -10543,11 +10772,11 @@ function runHarnessCleanup(options = {}) {
10543
10772
  const worktreeSeen = /* @__PURE__ */ new Set();
10544
10773
  for (const raw of worktreeCandidates) {
10545
10774
  if (atSweepCap()) break;
10546
- const resolved = path52.resolve(raw.path);
10775
+ const resolved = path53.resolve(raw.path);
10547
10776
  if (worktreeSeen.has(resolved)) continue;
10548
10777
  worktreeSeen.add(resolved);
10549
10778
  const candidate = { ...raw, path: resolved };
10550
- const indexed = index.get(path52.resolve(candidate.path)) ?? null;
10779
+ const indexed = index.get(path53.resolve(candidate.path)) ?? null;
10551
10780
  const orphanSafety = indexed ? null : assessOrphanWorktreeSafety({
10552
10781
  worktreePath: candidate.path,
10553
10782
  harnessRoot,
@@ -10557,7 +10786,7 @@ function runHarnessCleanup(options = {}) {
10557
10786
  });
10558
10787
  const guardSkip = skipWorktreeRemoval({
10559
10788
  indexed,
10560
- worktreePath: path52.resolve(candidate.path),
10789
+ worktreePath: path53.resolve(candidate.path),
10561
10790
  includeOrphans: retention.includeOrphans,
10562
10791
  worktreesAgeMs: retention.worktreesAgeMs,
10563
10792
  terminalWorktreesAgeMs: retention.terminalWorktreesAgeMs,
@@ -10589,11 +10818,11 @@ function runHarnessCleanup(options = {}) {
10589
10818
  now: paths.now
10590
10819
  })) {
10591
10820
  if (atSweepCap()) break;
10592
- const resolved = path52.resolve(raw.path);
10821
+ const resolved = path53.resolve(raw.path);
10593
10822
  if (processedPaths.has(resolved)) continue;
10594
10823
  processedPaths.add(resolved);
10595
10824
  const candidate = { ...raw, path: resolved };
10596
- const runId = candidate.runId ?? path52.basename(resolved);
10825
+ const runId = candidate.runId ?? path53.basename(resolved);
10597
10826
  const dirSkip = skipRunDirectoryRemoval({
10598
10827
  harnessRoot,
10599
10828
  runId,
@@ -10729,11 +10958,11 @@ function isPipelineCleanupEnabled() {
10729
10958
  // src/installed-package-versions.ts
10730
10959
  import { readFile } from "node:fs/promises";
10731
10960
  import { homedir as homedir13 } from "node:os";
10732
- import path54 from "node:path";
10961
+ import path55 from "node:path";
10733
10962
 
10734
10963
  // src/memory-cost-package-version-guard.ts
10735
10964
  import { existsSync as existsSync40, readFileSync as readFileSync13 } from "node:fs";
10736
- import path53 from "node:path";
10965
+ import path54 from "node:path";
10737
10966
  var MEMORY_COST_PACKAGE_MIN_VERSIONS = {
10738
10967
  "@kynver-app/runtime": "0.1.83",
10739
10968
  "@kynver-app/openclaw-agent-os": "0.1.43",
@@ -10795,8 +11024,8 @@ function resolveRepoRoot(cwd, explicitRepoRoot) {
10795
11024
  (value) => Boolean(value?.trim())
10796
11025
  );
10797
11026
  for (const candidate of candidates) {
10798
- const resolved = path53.resolve(candidate);
10799
- if (existsSync40(path53.join(resolved, "packages/kynver-runtime/package.json")) && existsSync40(path53.join(resolved, "package.json"))) {
11027
+ const resolved = path54.resolve(candidate);
11028
+ if (existsSync40(path54.join(resolved, "packages/kynver-runtime/package.json")) && existsSync40(path54.join(resolved, "package.json"))) {
10800
11029
  return resolved;
10801
11030
  }
10802
11031
  }
@@ -10809,7 +11038,7 @@ function probeRepoPackageVersions(input = {}) {
10809
11038
  if (!repoRoot) return {};
10810
11039
  const out = {};
10811
11040
  for (const packageName of MEMORY_COST_MANAGED_PACKAGES) {
10812
- const packageJsonPath = path53.join(repoRoot, REPO_PACKAGE_JSON_RELATIVE[packageName]);
11041
+ const packageJsonPath = path54.join(repoRoot, REPO_PACKAGE_JSON_RELATIVE[packageName]);
10813
11042
  const version = readPackageJsonVersion(packageJsonPath);
10814
11043
  if (!version) continue;
10815
11044
  out[packageName] = { version, source: "repo", path: packageJsonPath };
@@ -10929,12 +11158,12 @@ function unique(values) {
10929
11158
  }
10930
11159
  function moduleRoots() {
10931
11160
  const home = homedir13();
10932
- const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ?? path54.join(home, ".openclaw", "npm");
10933
- const npmGlobalRoot = trim(process.env.KYNVER_NPM_GLOBAL_ROOT) ?? trim(process.env.KYNVER_NPM_GLOBAL_MODULES_ROOT) ?? (trim(process.env.NPM_CONFIG_PREFIX) ? path54.join(trim(process.env.NPM_CONFIG_PREFIX), "lib", "node_modules") : path54.join(home, ".npm-global", "lib", "node_modules"));
11161
+ const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ?? path55.join(home, ".openclaw", "npm");
11162
+ const npmGlobalRoot = trim(process.env.KYNVER_NPM_GLOBAL_ROOT) ?? trim(process.env.KYNVER_NPM_GLOBAL_MODULES_ROOT) ?? (trim(process.env.NPM_CONFIG_PREFIX) ? path55.join(trim(process.env.NPM_CONFIG_PREFIX), "lib", "node_modules") : path55.join(home, ".npm-global", "lib", "node_modules"));
10934
11163
  return unique([
10935
- path54.join(openClawPrefix, "lib", "node_modules"),
10936
- path54.join(openClawPrefix, "node_modules"),
10937
- npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot : path54.join(npmGlobalRoot, "lib", "node_modules")
11164
+ path55.join(openClawPrefix, "lib", "node_modules"),
11165
+ path55.join(openClawPrefix, "node_modules"),
11166
+ npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot : path55.join(npmGlobalRoot, "lib", "node_modules")
10938
11167
  ]);
10939
11168
  }
10940
11169
  async function readVersion(packageJsonPath) {
@@ -10950,7 +11179,7 @@ function installedPackageJsonCandidates(packageName) {
10950
11179
  const seen = /* @__PURE__ */ new Set();
10951
11180
  const out = [];
10952
11181
  for (const root of roots) {
10953
- const candidate = path54.join(root, packageName, "package.json");
11182
+ const candidate = path55.join(root, packageName, "package.json");
10954
11183
  if (seen.has(candidate)) continue;
10955
11184
  seen.add(candidate);
10956
11185
  out.push(candidate);
@@ -10981,7 +11210,7 @@ async function completeFinishedWorkers(runId, args) {
10981
11210
  const outcomes = [];
10982
11211
  for (const name of Object.keys(run.workers || {})) {
10983
11212
  const worker = readJson(
10984
- path55.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
11213
+ path56.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
10985
11214
  void 0
10986
11215
  );
10987
11216
  if (!worker?.taskId || worker.localOnly) continue;
@@ -11128,6 +11357,15 @@ async function runPipelineTick(args) {
11128
11357
  var DEFAULT_INTERVAL_MS = 6e4;
11129
11358
  var IDLE_INTERVAL_MS = 5 * 6e4;
11130
11359
  var MAX_IDLE_STREAK = 10;
11360
+ var SLEEP_POLL_MS = 250;
11361
+ async function awaitDaemonBackoff(ms, isStopping) {
11362
+ let remaining = ms;
11363
+ while (remaining > 0 && !isStopping()) {
11364
+ const step = Math.min(SLEEP_POLL_MS, remaining);
11365
+ await sleepMsAsync(step);
11366
+ remaining -= step;
11367
+ }
11368
+ }
11131
11369
  async function runDaemon(args) {
11132
11370
  const runId = String(required(String(args.run || ""), "--run"));
11133
11371
  const agentOsId = String(required(String(args.agentOsId || loadUserConfig().agentOsId || ""), "--agent-os-id"));
@@ -11185,17 +11423,17 @@ async function runDaemon(args) {
11185
11423
  idleStreak = 0;
11186
11424
  }
11187
11425
  const backoff = idleStreak >= MAX_IDLE_STREAK ? IDLE_INTERVAL_MS : intervalMs;
11188
- await sleepMs(backoff);
11426
+ await awaitDaemonBackoff(backoff, () => stopping);
11189
11427
  } catch (error) {
11190
11428
  console.error(JSON.stringify({ event: "daemon_tick_error", error: error.message }));
11191
- await sleepMs(intervalMs);
11429
+ await awaitDaemonBackoff(intervalMs, () => stopping);
11192
11430
  }
11193
11431
  }
11194
11432
  console.error(JSON.stringify({ event: "daemon_stop", runId, agentOsId }));
11195
11433
  }
11196
11434
 
11197
11435
  // src/plan-progress.ts
11198
- import path59 from "node:path";
11436
+ import path60 from "node:path";
11199
11437
 
11200
11438
  // src/bounded-build/constants.ts
11201
11439
  var DEFAULT_BUILD_MEM_BUDGET_BYTES = 1536 * 1024 * 1024;
@@ -11338,16 +11576,16 @@ import {
11338
11576
  unlinkSync as unlinkSync4,
11339
11577
  writeFileSync as writeFileSync5
11340
11578
  } from "node:fs";
11341
- import path57 from "node:path";
11579
+ import path58 from "node:path";
11342
11580
 
11343
11581
  // src/heavy-verification/paths.ts
11344
11582
  import { mkdirSync as mkdirSync7 } from "node:fs";
11345
- import path56 from "node:path";
11583
+ import path57 from "node:path";
11346
11584
  function resolveHeavyVerificationRoot() {
11347
- return path56.join(resolveKynverStateRoot(), "heavy-verification");
11585
+ return path57.join(resolveKynverStateRoot(), "heavy-verification");
11348
11586
  }
11349
11587
  function heavyVerificationSlotsDir() {
11350
- return path56.join(resolveHeavyVerificationRoot(), "slots");
11588
+ return path57.join(resolveHeavyVerificationRoot(), "slots");
11351
11589
  }
11352
11590
  function ensureHeavyVerificationDirs() {
11353
11591
  const dir = heavyVerificationSlotsDir();
@@ -11376,7 +11614,7 @@ function indexedSlotId(index) {
11376
11614
  return `slot-${index}`;
11377
11615
  }
11378
11616
  function slotFilePath(slotId, slotsDir = heavyVerificationSlotsDir()) {
11379
- return path57.join(slotsDir, `${slotId}.json`);
11617
+ return path58.join(slotsDir, `${slotId}.json`);
11380
11618
  }
11381
11619
  function readSlotRecord(filePath) {
11382
11620
  if (!existsSync41(filePath)) return null;
@@ -11415,7 +11653,7 @@ function reclaimStaleHeavyVerificationSlots(opts = {}) {
11415
11653
  let reclaimed = 0;
11416
11654
  for (const name of readdirSync15(slotsDir)) {
11417
11655
  if (!name.endsWith(".json")) continue;
11418
- const filePath = path57.join(slotsDir, name);
11656
+ const filePath = path58.join(slotsDir, name);
11419
11657
  const before = existsSync41(filePath);
11420
11658
  reclaimStaleSlot(filePath, staleMs);
11421
11659
  if (before && !existsSync41(filePath)) reclaimed += 1;
@@ -11429,7 +11667,7 @@ function listActiveHeavyVerificationSlots(opts = {}) {
11429
11667
  const active = [];
11430
11668
  for (const name of readdirSync15(slotsDir)) {
11431
11669
  if (!name.endsWith(".json")) continue;
11432
- const record = readSlotRecord(path57.join(slotsDir, name));
11670
+ const record = readSlotRecord(path58.join(slotsDir, name));
11433
11671
  if (record && !slotIsStale(record, staleMs)) active.push(record);
11434
11672
  }
11435
11673
  return active;
@@ -11549,11 +11787,11 @@ function waitForHeavyVerificationSlot(command, timeoutMs, pollMs = 2e3, opts = {
11549
11787
  }
11550
11788
 
11551
11789
  // src/harness-worktree-build-guard.ts
11552
- import path58 from "node:path";
11790
+ import path59 from "node:path";
11553
11791
  function isPathUnderHarnessWorktree(cwd) {
11554
11792
  const worktreesDir = harnessWorktreesDir(resolveHarnessRoot());
11555
- const rel = path58.relative(worktreesDir, path58.resolve(cwd));
11556
- return rel.length > 0 && !rel.startsWith("..") && !path58.isAbsolute(rel);
11793
+ const rel = path59.relative(worktreesDir, path59.resolve(cwd));
11794
+ return rel.length > 0 && !rel.startsWith("..") && !path59.isAbsolute(rel);
11557
11795
  }
11558
11796
  function assessHarnessWorktreeBuildGuard(cwd) {
11559
11797
  if (!isPathUnderHarnessWorktree(cwd)) return { ok: true };
@@ -11765,7 +12003,7 @@ async function emitPlanProgress(args) {
11765
12003
  }
11766
12004
  function verifyPlanLocal(args) {
11767
12005
  const worktree = required(args.worktree ? String(args.worktree) : void 0, "worktree");
11768
- const cwd = path59.resolve(worktree);
12006
+ const cwd = path60.resolve(worktree);
11769
12007
  const summary = runHarnessVerifyCommands(cwd);
11770
12008
  const emitJson = args.json === true || args.json === "true";
11771
12009
  const payload = { passed: summary.passed, worktree: cwd, steps: summary.steps };
@@ -11814,9 +12052,9 @@ async function verifyPlan(args) {
11814
12052
  }
11815
12053
 
11816
12054
  // src/harness-verify-cli.ts
11817
- import path60 from "node:path";
12055
+ import path61 from "node:path";
11818
12056
  function runHarnessVerifyCli(args) {
11819
- const cwd = path60.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
12057
+ const cwd = path61.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
11820
12058
  const emitJson = args.json === true || args.json === "true" || args.emitJson === true || args.emitJson === "true";
11821
12059
  const commands = [];
11822
12060
  const rawCmd = args.command;
@@ -12024,7 +12262,7 @@ function formatMonitorTickNotice(tick) {
12024
12262
  }
12025
12263
 
12026
12264
  // src/monitor/monitor.service.ts
12027
- import path62 from "node:path";
12265
+ import path63 from "node:path";
12028
12266
 
12029
12267
  // src/monitor/monitor.classify.ts
12030
12268
  function classifyWorkerHealth(input) {
@@ -12077,10 +12315,10 @@ function classifyWorkerHealth(input) {
12077
12315
 
12078
12316
  // src/monitor/monitor.store.ts
12079
12317
  import { existsSync as existsSync42, mkdirSync as mkdirSync9, readdirSync as readdirSync16, unlinkSync as unlinkSync5 } from "node:fs";
12080
- import path61 from "node:path";
12318
+ import path62 from "node:path";
12081
12319
  function monitorsDir() {
12082
12320
  const { harnessRoot } = getHarnessPaths();
12083
- const dir = path61.join(harnessRoot, "monitors");
12321
+ const dir = path62.join(harnessRoot, "monitors");
12084
12322
  mkdirSync9(dir, { recursive: true });
12085
12323
  return dir;
12086
12324
  }
@@ -12088,7 +12326,7 @@ function monitorIdFor(runId, workerName) {
12088
12326
  return workerName ? `${safeSlug(runId)}--${safeSlug(workerName)}` : safeSlug(runId);
12089
12327
  }
12090
12328
  function monitorPath(monitorId) {
12091
- return path61.join(monitorsDir(), `${monitorId}.json`);
12329
+ return path62.join(monitorsDir(), `${monitorId}.json`);
12092
12330
  }
12093
12331
  function loadMonitorSession(monitorId) {
12094
12332
  return readJson(monitorPath(monitorId), void 0);
@@ -12109,7 +12347,7 @@ function listMonitorSessions() {
12109
12347
  for (const name of readdirSync16(dir)) {
12110
12348
  if (!name.endsWith(".json")) continue;
12111
12349
  const session = readJson(
12112
- path61.join(dir, name),
12350
+ path62.join(dir, name),
12113
12351
  void 0
12114
12352
  );
12115
12353
  if (!session?.monitorId) continue;
@@ -12200,7 +12438,7 @@ async function fetchTaskLeasesForWorkers(input) {
12200
12438
  // src/monitor/monitor.service.ts
12201
12439
  function workerRecord2(runId, name) {
12202
12440
  return readJson(
12203
- path62.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
12441
+ path63.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
12204
12442
  void 0
12205
12443
  );
12206
12444
  }
@@ -12407,17 +12645,17 @@ async function runMonitorLoop(args) {
12407
12645
  // src/monitor/monitor-spawn.ts
12408
12646
  import { spawn as spawn6 } from "node:child_process";
12409
12647
  import { closeSync as closeSync8, existsSync as existsSync43, openSync as openSync8 } from "node:fs";
12410
- import path63 from "node:path";
12648
+ import path64 from "node:path";
12411
12649
  import { fileURLToPath as fileURLToPath3 } from "node:url";
12412
12650
  function resolveDefaultCliPath2() {
12413
- return path63.join(fileURLToPath3(new URL(".", import.meta.url)), "cli.js");
12651
+ return path64.join(fileURLToPath3(new URL(".", import.meta.url)), "cli.js");
12414
12652
  }
12415
12653
  function spawnMonitorSidecar(opts) {
12416
12654
  const cliPath = opts.cliPath ?? resolveDefaultCliPath2();
12417
12655
  if (!existsSync43(cliPath)) return void 0;
12418
12656
  const monitorId = monitorIdFor(opts.runId, opts.workerName);
12419
12657
  const { harnessRoot } = getHarnessPaths();
12420
- const logPath = path63.join(harnessRoot, "monitors", `${monitorId}.log`);
12658
+ const logPath = path64.join(harnessRoot, "monitors", `${monitorId}.log`);
12421
12659
  let logFd;
12422
12660
  try {
12423
12661
  logFd = openSync8(logPath, "a");
@@ -12618,8 +12856,32 @@ function shouldEnforceMemoryCostPackageGuardCli(scope, action) {
12618
12856
  return false;
12619
12857
  }
12620
12858
 
12859
+ // src/run-resolve.ts
12860
+ function resolveHarnessRunByName(runName) {
12861
+ const name = runName.trim();
12862
+ if (!name) return null;
12863
+ const rows = buildRunListRows();
12864
+ return rows.find((row) => row.name === name && row.status !== "completed") ?? null;
12865
+ }
12866
+ function resolveHarnessRunCli(args) {
12867
+ const name = String(required(String(args.name || ""), "--name"));
12868
+ const hit = resolveHarnessRunByName(name);
12869
+ console.log(
12870
+ JSON.stringify(
12871
+ {
12872
+ runId: hit?.id ?? null,
12873
+ name,
12874
+ status: hit?.status ?? null,
12875
+ effectiveStatus: hit?.effectiveStatus ?? null
12876
+ },
12877
+ null,
12878
+ 2
12879
+ )
12880
+ );
12881
+ }
12882
+
12621
12883
  // src/post-restart-unblock.ts
12622
- import path64 from "node:path";
12884
+ import path65 from "node:path";
12623
12885
  function skip(runId, worker, taskId, agentOsId, leaseOwner, reason) {
12624
12886
  return { runId, worker, taskId, agentOsId, leaseOwner, action: "skipped", reason };
12625
12887
  }
@@ -12632,7 +12894,7 @@ async function postRestartUnblock(args) {
12632
12894
  const errors = [];
12633
12895
  for (const run of listRunRecords()) {
12634
12896
  for (const name of Object.keys(run.workers ?? {})) {
12635
- const workerPath = path64.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
12897
+ const workerPath = path65.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
12636
12898
  const worker = readJson(workerPath, void 0);
12637
12899
  if (!worker) {
12638
12900
  skipped.push(skip(run.id, name, "", "", "", "worker.json missing"));
@@ -12744,9 +13006,9 @@ async function postRestartUnblockCli(args) {
12744
13006
  }
12745
13007
 
12746
13008
  // src/default-repo-cli.ts
12747
- import path65 from "node:path";
13009
+ import path66 from "node:path";
12748
13010
  import { homedir as homedir14 } from "node:os";
12749
- var CONFIG_FILE2 = path65.join(homedir14(), ".kynver", "config.json");
13011
+ var CONFIG_FILE2 = path66.join(homedir14(), ".kynver", "config.json");
12750
13012
  function ensureDefaultRepo(opts) {
12751
13013
  const existing = loadUserConfig();
12752
13014
  const resolved = resolveDefaultRepo({ ...opts, config: existing });
@@ -12827,12 +13089,12 @@ function summarizeResolvedDefaultRepo(resolved) {
12827
13089
  }
12828
13090
 
12829
13091
  // src/doctor/runtime-takeover.ts
12830
- import path67 from "node:path";
13092
+ import path68 from "node:path";
12831
13093
 
12832
13094
  // src/doctor/runtime-takeover.probes.ts
12833
13095
  import { accessSync, constants, existsSync as existsSync45, readFileSync as readFileSync17 } from "node:fs";
12834
13096
  import { homedir as homedir15 } from "node:os";
12835
- import path66 from "node:path";
13097
+ import path67 from "node:path";
12836
13098
  import { spawnSync as spawnSync9 } from "node:child_process";
12837
13099
  function captureCommand(bin, args) {
12838
13100
  try {
@@ -12874,10 +13136,10 @@ var defaultRuntimeTakeoverProbes = {
12874
13136
  commandOnPath: (bin) => captureCommand(process.platform === "win32" ? "where" : "which", [bin]),
12875
13137
  kynverVersion: (bin) => captureCommand(bin, ["--version"]),
12876
13138
  loadConfig: () => loadUserConfig(),
12877
- configFilePath: () => path66.join(homedir15(), ".kynver", "config.json"),
12878
- credentialsFilePath: () => path66.join(homedir15(), ".kynver", "credentials"),
13139
+ configFilePath: () => path67.join(homedir15(), ".kynver", "config.json"),
13140
+ credentialsFilePath: () => path67.join(homedir15(), ".kynver", "credentials"),
12879
13141
  readCredentials: () => {
12880
- const credPath = path66.join(homedir15(), ".kynver", "credentials");
13142
+ const credPath = path67.join(homedir15(), ".kynver", "credentials");
12881
13143
  if (!existsSync45(credPath)) {
12882
13144
  return { hasApiKey: false };
12883
13145
  }
@@ -12912,7 +13174,7 @@ var defaultRuntimeTakeoverProbes = {
12912
13174
  })()
12913
13175
  }),
12914
13176
  harnessRoot: () => resolveHarnessRoot(),
12915
- legacyOpenclawHarnessRoot: () => path66.join(homedir15(), ".openclaw", "harness"),
13177
+ legacyOpenclawHarnessRoot: () => path67.join(homedir15(), ".openclaw", "harness"),
12916
13178
  pathExists: (target) => existsSync45(target),
12917
13179
  pathWritable: (target) => isWritable(target)
12918
13180
  };
@@ -13319,8 +13581,8 @@ function assessVercelDeployEvidence(probes) {
13319
13581
  }
13320
13582
  function assessHarnessDirs(probes) {
13321
13583
  const harnessRoot = probes.harnessRoot();
13322
- const runsDir = path67.join(harnessRoot, "runs");
13323
- const worktreesDir = path67.join(harnessRoot, "worktrees");
13584
+ const runsDir = path68.join(harnessRoot, "runs");
13585
+ const worktreesDir = path68.join(harnessRoot, "worktrees");
13324
13586
  const displayHarnessRoot = redactHomePath(harnessRoot);
13325
13587
  const displayRunsDir = redactHomePath(runsDir);
13326
13588
  const displayWorktreesDir = redactHomePath(worktreesDir);
@@ -13584,9 +13846,9 @@ function applySchedulerCutoverAttestation(config) {
13584
13846
  }
13585
13847
 
13586
13848
  // src/scheduler-cutover-cli.ts
13587
- import path68 from "node:path";
13849
+ import path69 from "node:path";
13588
13850
  import { homedir as homedir16 } from "node:os";
13589
- var CONFIG_FILE3 = path68.join(homedir16(), ".kynver", "config.json");
13851
+ var CONFIG_FILE3 = path69.join(homedir16(), ".kynver", "config.json");
13590
13852
  function runSchedulerCutoverCheckCli(json = false) {
13591
13853
  const config = loadUserConfig();
13592
13854
  const report = assessSchedulerCutover(config);
@@ -13723,6 +13985,157 @@ async function runCronTickCli(args) {
13723
13985
  );
13724
13986
  }
13725
13987
 
13988
+ // src/lane/landing-maintainer-tick.ts
13989
+ import os9 from "node:os";
13990
+
13991
+ // src/lane/lane-spec.ts
13992
+ var LANDING_MAINTAINER_LANE_SPEC = {
13993
+ slug: "landing-maintainer",
13994
+ originCron: "maintain-8-blocker-and-pr-landing-workers",
13995
+ defaultRepo: "Totalsolutionsync/Kynver",
13996
+ landScript: "scripts/agent-os-land-pr.mjs",
13997
+ landScriptArgs: ["--skip-not-ready"]
13998
+ };
13999
+
14000
+ // src/lane/landing-maintainer-local.ts
14001
+ import { spawnSync as spawnSync10 } from "node:child_process";
14002
+ import path70 from "node:path";
14003
+ function runLandingWrapper(prNumber, repoRoot, execute) {
14004
+ const script = path70.join(repoRoot, LANDING_MAINTAINER_LANE_SPEC.landScript);
14005
+ const args = [script, String(prNumber), ...LANDING_MAINTAINER_LANE_SPEC.landScriptArgs];
14006
+ if (!execute) {
14007
+ return {
14008
+ action: { kind: "land_pr", prNumber, reason: "dry-run" },
14009
+ executed: false,
14010
+ exitCode: 0,
14011
+ stdout: `dry-run: node ${args.join(" ")}`,
14012
+ stderr: ""
14013
+ };
14014
+ }
14015
+ const result = spawnSync10("node", args, {
14016
+ cwd: repoRoot,
14017
+ encoding: "utf8",
14018
+ timeout: 10 * 60 * 1e3
14019
+ });
14020
+ return {
14021
+ action: { kind: "land_pr", prNumber, reason: "landing wrapper invoked" },
14022
+ executed: true,
14023
+ exitCode: result.status,
14024
+ stdout: result.stdout ?? "",
14025
+ stderr: result.stderr ?? ""
14026
+ };
14027
+ }
14028
+ function resolveLandingMaintainerRepoRoot(args) {
14029
+ const explicit = args.repoPath ? String(args.repoPath).trim() : "";
14030
+ if (explicit) return path70.resolve(explicit);
14031
+ const resolved = resolveDefaultRepo();
14032
+ return resolved?.repo ?? process.cwd();
14033
+ }
14034
+
14035
+ // src/lane/landing-maintainer-tick.ts
14036
+ async function runLandingMaintainerLaneTick(args) {
14037
+ const agentOsId = String(required(String(args.agentOsId || ""), "--agent-os-id"));
14038
+ const repoSlug = String(args.repo || LANDING_MAINTAINER_LANE_SPEC.defaultRepo).trim();
14039
+ const fleet = args.fleet === true || args.fleet === "true";
14040
+ const execute = args.execute !== false && args.execute !== "false";
14041
+ const runId = args.run ? String(args.run) : void 0;
14042
+ const resourceGate = observeRunnerResourceGate({
14043
+ runId: runId ?? "fleet-lane-tick"
14044
+ });
14045
+ const boxCapacity = {
14046
+ ...buildBoxResourceSnapshotFromGate(resourceGate, {
14047
+ harnessRunId: runId,
14048
+ boxKind: resolveBoxKindFromConfig(loadUserConfig()),
14049
+ hostLabel: os9.hostname()
14050
+ }),
14051
+ providerHealthy: resourceGate.ok,
14052
+ authorizedForRepair: resourceGate.ok,
14053
+ authorizedForLanding: resourceGate.ok,
14054
+ systemHealthBlockers: resourceGate.ok ? [] : [resourceGate.reason ?? "resource_gate_blocked"],
14055
+ actionableWorkers: resourceGate.activeWorkers
14056
+ };
14057
+ const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
14058
+ const secret = await resolveCallbackSecretWithMint(
14059
+ args.secret ? String(args.secret) : void 0,
14060
+ agentOsId,
14061
+ { baseUrl: base }
14062
+ );
14063
+ const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/fleet-landing-maintainer/tick`;
14064
+ const res = await postJson(url, secret, {
14065
+ repo: repoSlug,
14066
+ fleet,
14067
+ execute,
14068
+ runId,
14069
+ boxCapacity
14070
+ });
14071
+ const coordinator = res.response;
14072
+ const localOutcomes = [];
14073
+ const repoRoot = resolveLandingMaintainerRepoRoot(args);
14074
+ const actions = Array.isArray(coordinator?.localActions) ? coordinator.localActions : [];
14075
+ const holderBoxId = boxCapacity.boxId;
14076
+ for (const action of actions) {
14077
+ if (action.kind === "land_pr" && typeof action.prNumber === "number") {
14078
+ const outcome = runLandingWrapper(action.prNumber, repoRoot, execute);
14079
+ localOutcomes.push({
14080
+ action: outcome.action,
14081
+ executed: outcome.executed,
14082
+ exitCode: outcome.exitCode
14083
+ });
14084
+ if (execute && outcome.executed) {
14085
+ const merged = outcome.exitCode === 0;
14086
+ const prUrl = typeof action.prUrl === "string" && action.prUrl.trim() ? action.prUrl.trim() : `https://github.com/${repoSlug}/pull/${action.prNumber}`;
14087
+ try {
14088
+ await postJson(
14089
+ `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/fleet-landing-maintainer/outcome`,
14090
+ secret,
14091
+ { repo: repoSlug, prUrl, holderBoxId, merged }
14092
+ );
14093
+ } catch {
14094
+ }
14095
+ }
14096
+ continue;
14097
+ }
14098
+ localOutcomes.push({
14099
+ action,
14100
+ executed: false,
14101
+ exitCode: null
14102
+ });
14103
+ }
14104
+ return {
14105
+ repo: repoSlug,
14106
+ fleet,
14107
+ execute,
14108
+ coordinator,
14109
+ localOutcomes
14110
+ };
14111
+ }
14112
+
14113
+ // src/lane/lane-tick-cli.ts
14114
+ async function runLaneTickCli(args, laneFromArgv) {
14115
+ const lane = String(laneFromArgv ?? args.lane ?? "").trim();
14116
+ if (lane !== "landing-maintainer") {
14117
+ console.error(`unknown lane: ${lane || "(none)"}`);
14118
+ process.exit(1);
14119
+ }
14120
+ const result = await runLandingMaintainerLaneTick(args);
14121
+ if (args.json === true || args.json === "true") {
14122
+ console.log(JSON.stringify(result, null, 2));
14123
+ return;
14124
+ }
14125
+ console.log(
14126
+ [
14127
+ `fleet landing-maintainer tick`,
14128
+ `repo=${result.repo}`,
14129
+ `fleet=${result.fleet}`,
14130
+ `execute=${result.execute}`,
14131
+ `localOutcomes=${result.localOutcomes.length}`
14132
+ ].join(" ")
14133
+ );
14134
+ for (const row of result.localOutcomes) {
14135
+ console.log(` ${row.action.kind} pr=${row.action.prNumber ?? "-"} executed=${row.executed}`);
14136
+ }
14137
+ }
14138
+
13726
14139
  // src/cli.ts
13727
14140
  function isHelpFlag(arg) {
13728
14141
  return arg === "help" || arg === "--help" || arg === "-h";
@@ -13743,6 +14156,7 @@ function usage(code = 0) {
13743
14156
  " kynver daemon --run RUN_ID --agent-os-id AOS_ID [--execute] [--interval-ms MS]",
13744
14157
  " kynver run create [--repo /path/repo] [--name name] [--base origin/main]",
13745
14158
  " kynver run list",
14159
+ " kynver run resolve --name RUN_NAME",
13746
14160
  " kynver run status --run RUN_ID [--json] [--compact]",
13747
14161
  " 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]",
13748
14162
  " kynver run sweep --run RUN_ID --agent-os-id AOS_ID [--base-url URL] [--secret SECRET] [--grace-ms MS]",
@@ -13778,6 +14192,7 @@ function usage(code = 0) {
13778
14192
  " kynver scheduler attest-cutover [--json]",
13779
14193
  " kynver cron status [--json]",
13780
14194
  " kynver cron tick [--agent-os-id AOS_ID] [--json]",
14195
+ " kynver lane tick landing-maintainer [--fleet] [--repo OWNER/NAME] [--agent-os-id AOS_ID] [--execute] [--json]",
13781
14196
  " kynver board contract [--agent-os-id ID] [--base-url URL] [--since ISO] [--limit N]"
13782
14197
  ].join("\n")
13783
14198
  );
@@ -13789,7 +14204,7 @@ async function main(argv = process.argv.slice(2)) {
13789
14204
  const scope = argv.shift();
13790
14205
  let action;
13791
14206
  let rest;
13792
- if (scope === "run" || scope === "worker" || scope === "plan" || scope === "runner" || scope === "harness" || scope === "monitor" || scope === "doctor" || scope === "config" || scope === "scheduler" || scope === "cron" || scope === "board") {
14207
+ 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") {
13793
14208
  action = argv.shift();
13794
14209
  rest = argv;
13795
14210
  } else {
@@ -13846,11 +14261,16 @@ async function main(argv = process.argv.slice(2)) {
13846
14261
  if (scope === "cron" && action === "tick") {
13847
14262
  return void await runCronTickCli(args);
13848
14263
  }
14264
+ if (scope === "lane" && action === "tick") {
14265
+ const laneName = rest.shift();
14266
+ return void await runLaneTickCli(parseArgs(rest), laneName);
14267
+ }
13849
14268
  if (scope === "board" && action === "contract") {
13850
14269
  return void await runCommandCenterContractCli(args);
13851
14270
  }
13852
14271
  if (scope === "run" && action === "create") return createRun(args);
13853
14272
  if (scope === "run" && action === "list") return listRuns();
14273
+ if (scope === "run" && action === "resolve") return resolveHarnessRunCli(args);
13854
14274
  if (scope === "run" && action === "status") return runStatus(args);
13855
14275
  if (scope === "run" && action === "dispatch") return void await dispatchRun(args);
13856
14276
  if (scope === "run" && action === "sweep") return void await sweepRun(args);