@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/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,
@@ -6940,6 +6966,11 @@ function readAdmissionExhaustion(result) {
6940
6966
  if (!raw || typeof raw !== "object") return null;
6941
6967
  return raw;
6942
6968
  }
6969
+ function readDispatchSkipDrain(result) {
6970
+ const raw = result.dispatchSkipDrain;
6971
+ if (!raw || typeof raw !== "object") return null;
6972
+ return raw;
6973
+ }
6943
6974
  function readHarnessWorkerContext(decision) {
6944
6975
  const raw = decision.harnessWorkerContext;
6945
6976
  if (!raw || typeof raw !== "object") return null;
@@ -7103,6 +7134,7 @@ async function dispatchRun(args) {
7103
7134
  const result = first.result;
7104
7135
  if (dryRun) {
7105
7136
  const admissionExhaustion2 = readAdmissionExhaustion(result);
7137
+ const dispatchSkipDrain2 = readDispatchSkipDrain(result);
7106
7138
  const summary2 = {
7107
7139
  runId: run.id,
7108
7140
  agentOsId,
@@ -7121,7 +7153,8 @@ async function dispatchRun(args) {
7121
7153
  pagesScanned: result.pagesScanned ?? null,
7122
7154
  candidatesExhausted: result.candidatesExhausted ?? null,
7123
7155
  capacityIdle: admissionExhaustion2?.capacityIdle === true,
7124
- admissionExhaustion: admissionExhaustion2
7156
+ admissionExhaustion: admissionExhaustion2,
7157
+ dispatchSkipDrain: dispatchSkipDrain2
7125
7158
  };
7126
7159
  if (pipeline) return { ok: true, ...summary2 };
7127
7160
  console.log(JSON.stringify(summary2, null, 2));
@@ -7177,7 +7210,7 @@ async function dispatchRun(args) {
7177
7210
  );
7178
7211
  }
7179
7212
  const attempt = Number(task.attempt) || 1;
7180
- if (attempt >= retryLimits.maxTaskAttempts) {
7213
+ if (attempt > retryLimits.maxTaskAttempts) {
7181
7214
  return abortClaimedSpawn(
7182
7215
  task,
7183
7216
  `task attempt ${attempt} exceeds KYNVER_MAX_TASK_ATTEMPTS (${retryLimits.maxTaskAttempts})`
@@ -7311,9 +7344,16 @@ async function dispatchRun(args) {
7311
7344
  }
7312
7345
  const startedCount = outcomes.filter((o) => o.started).length;
7313
7346
  const admissionExhaustion = readAdmissionExhaustion(result);
7314
- const capacityIdle = admissionExhaustion?.capacityIdle === true || startedCount === 0 && Number(result.resourceGate?.slotsAvailable) > 0;
7347
+ const dispatchSkipDrain = readDispatchSkipDrain(result);
7348
+ const capacityIdle = startedCount === 0 && (admissionExhaustion?.capacityIdle === true || Number(result.resourceGate?.slotsAvailable) > 0);
7315
7349
  if (capacityIdle && admissionExhaustion?.summary) {
7316
- console.error(`[dispatch] ${admissionExhaustion.summary}`);
7350
+ const retryCeiling = admissionExhaustion.skipReasonCounts?.retry_ceiling_exceeded ?? 0;
7351
+ const recovery = result.overAttemptIdleRecovery ?? admissionExhaustion.overAttemptIdleRecovery;
7352
+ const recoveryNote = recovery?.attempted === true ? `; over_attempt_recovery minted=${recovery.minted ?? 0} started=${recovery.started ?? 0}` : retryCeiling > 0 ? "; over_attempt_recovery not attempted" : "";
7353
+ const drainNote = dispatchSkipDrain ? `; dispatch_skip_drain scanned=${dispatchSkipDrain.scanned ?? 0} advanced=${dispatchSkipDrain.advanced ?? 0}` : "";
7354
+ console.error(
7355
+ `[dispatch] ${admissionExhaustion.summary}${retryCeiling > 0 ? `; retry_ceiling_exceeded=${retryCeiling}` : ""}${recoveryNote}${drainNote}`
7356
+ );
7317
7357
  }
7318
7358
  const summary = {
7319
7359
  runId: run.id,
@@ -7323,6 +7363,7 @@ async function dispatchRun(args) {
7323
7363
  startedCount,
7324
7364
  capacityIdle,
7325
7365
  admissionExhaustion,
7366
+ dispatchSkipDrain,
7326
7367
  outcomes,
7327
7368
  skipped: skipped.map((d) => ({
7328
7369
  taskId: d.task.id,
@@ -7409,14 +7450,14 @@ async function sweepRun(args) {
7409
7450
 
7410
7451
  // src/worktree.ts
7411
7452
  import { existsSync as existsSync25, mkdirSync as mkdirSync6 } from "node:fs";
7412
- import path34 from "node:path";
7453
+ import path35 from "node:path";
7413
7454
 
7414
7455
  // src/run-list.ts
7415
7456
  import { existsSync as existsSync24, readFileSync as readFileSync11 } from "node:fs";
7416
- import path33 from "node:path";
7457
+ import path34 from "node:path";
7417
7458
 
7418
7459
  // src/stale-reconcile.ts
7419
- import path32 from "node:path";
7460
+ import path33 from "node:path";
7420
7461
 
7421
7462
  // src/finalize.ts
7422
7463
  import path27 from "node:path";
@@ -7999,6 +8040,193 @@ function reconcileWorkerMetadata() {
7999
8040
  return { workers: outcomes, runMetadataRetention };
8000
8041
  }
8001
8042
 
8043
+ // src/local-pr-attention-reconcile.ts
8044
+ import { execFileSync } from "node:child_process";
8045
+ import path32 from "node:path";
8046
+ function normalizePrUrl3(url) {
8047
+ const m = url.trim().match(/github\.com\/([^/]+\/[^/]+)\/(?:pull|pulls)\/(\d+)/i);
8048
+ if (!m) return null;
8049
+ return `https://github.com/${m[1]}/pull/${m[2]}`;
8050
+ }
8051
+ function prNumberFromUrl(url) {
8052
+ const m = normalizePrUrl3(url)?.match(/\/pull\/(\d+)$/);
8053
+ if (!m) return null;
8054
+ const n = Number.parseInt(m[1], 10);
8055
+ return Number.isFinite(n) ? n : null;
8056
+ }
8057
+ function extractText(value) {
8058
+ if (value == null) return "";
8059
+ if (typeof value === "string") return value;
8060
+ try {
8061
+ return JSON.stringify(value);
8062
+ } catch {
8063
+ return "";
8064
+ }
8065
+ }
8066
+ function extractPrNumbersFromText(text) {
8067
+ const out = /* @__PURE__ */ new Set();
8068
+ for (const match of text.matchAll(/github\.com\/[^/\s)>"']+\/[^/\s)>"']+\/(?:pull|pulls)\/(\d+)/gi)) {
8069
+ const n = Number.parseInt(match[1], 10);
8070
+ if (Number.isFinite(n)) out.add(n);
8071
+ }
8072
+ for (const match of text.matchAll(/\bPR\s*#?\s*(\d{2,})\b/gi)) {
8073
+ const n = Number.parseInt(match[1], 10);
8074
+ if (Number.isFinite(n)) out.add(n);
8075
+ }
8076
+ for (const match of text.matchAll(/\bpr[-_]?(\d{2,})\b/gi)) {
8077
+ const n = Number.parseInt(match[1], 10);
8078
+ if (Number.isFinite(n)) out.add(n);
8079
+ }
8080
+ return [...out];
8081
+ }
8082
+ function extractTargetPrReconciliation(value) {
8083
+ let record = null;
8084
+ if (typeof value === "string") record = extractEmbeddedWorkerFinalResultRecord(value);
8085
+ else if (value && typeof value === "object" && !Array.isArray(value)) record = value;
8086
+ if (!record) return [];
8087
+ const raw = record.targetPrReconciliation ?? record.target_pr_reconciliation;
8088
+ if (!Array.isArray(raw)) return [];
8089
+ const out = [];
8090
+ for (const item of raw) {
8091
+ if (!item || typeof item !== "object" || Array.isArray(item)) continue;
8092
+ const row = item;
8093
+ const prUrl = normalizePrUrl3(String(row.prUrl ?? row.pr_url ?? ""));
8094
+ const outcome = String(row.outcome ?? "").trim();
8095
+ if (!prUrl || outcome !== "merged") continue;
8096
+ out.push({
8097
+ prUrl,
8098
+ mergeCommit: typeof row.mergeCommit === "string" ? row.mergeCommit : typeof row.merge_commit === "string" ? row.merge_commit : null,
8099
+ reason: typeof row.reason === "string" ? row.reason : null
8100
+ });
8101
+ }
8102
+ return out;
8103
+ }
8104
+ function defaultLookupPr(input) {
8105
+ try {
8106
+ const repo = execFileSync("git", ["config", "--get", "remote.origin.url"], {
8107
+ cwd: input.repoDir,
8108
+ encoding: "utf8",
8109
+ stdio: ["ignore", "pipe", "ignore"]
8110
+ }).trim();
8111
+ const repoMatch = repo.match(/github\.com[:/]([^/]+\/[^/.]+)(?:\.git)?$/i);
8112
+ const repoSlug = repoMatch?.[1];
8113
+ if (!repoSlug) return null;
8114
+ const raw = execFileSync(
8115
+ "gh",
8116
+ ["pr", "view", String(input.prNumber), "--repo", repoSlug, "--json", "state,mergedAt,mergeCommit,url"],
8117
+ { cwd: input.repoDir, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
8118
+ );
8119
+ const parsed = JSON.parse(raw);
8120
+ return {
8121
+ prUrl: normalizePrUrl3(parsed.url ?? `https://github.com/${repoSlug}/pull/${input.prNumber}`) ?? `https://github.com/${repoSlug}/pull/${input.prNumber}`,
8122
+ state: parsed.state ?? "",
8123
+ mergedAt: parsed.mergedAt ?? null,
8124
+ mergeCommit: parsed.mergeCommit?.oid ?? null
8125
+ };
8126
+ } catch {
8127
+ return null;
8128
+ }
8129
+ }
8130
+ function finalResultForWorker(worker) {
8131
+ const heartbeat = parseHeartbeat(worker.heartbeatPath);
8132
+ return worker.completionSnapshot?.finalResult ?? terminalFinalResultFromHeartbeat(heartbeat);
8133
+ }
8134
+ function collectMergedEvidenceByPr(run) {
8135
+ const byPr = /* @__PURE__ */ new Map();
8136
+ const runDir2 = runDirectory(run.id);
8137
+ for (const name of listRunWorkerNames(run)) {
8138
+ const worker = readJson(
8139
+ path32.join(runDir2, "workers", safeSlug(name), "worker.json"),
8140
+ void 0
8141
+ );
8142
+ if (!worker) continue;
8143
+ for (const evidence of extractTargetPrReconciliation(finalResultForWorker(worker))) {
8144
+ const n = prNumberFromUrl(evidence.prUrl);
8145
+ if (n != null && !byPr.has(n)) byPr.set(n, evidence);
8146
+ }
8147
+ }
8148
+ return byPr;
8149
+ }
8150
+ function candidatePrNumbers(worker, statusFinalResult) {
8151
+ const heartbeat = parseHeartbeat(worker.heartbeatPath);
8152
+ const text = [
8153
+ worker.name,
8154
+ worker.repairTargetPrUrl,
8155
+ worker.taskPrUrl,
8156
+ worker.branch,
8157
+ heartbeat.lastHeartbeatSummary,
8158
+ extractText(statusFinalResult)
8159
+ ].filter(Boolean).join("\n");
8160
+ return extractPrNumbersFromText(text);
8161
+ }
8162
+ function isLocalOnlyAttentionNoise(worker) {
8163
+ return worker.localOnly === true && !worker.taskId && !worker.agentOsId;
8164
+ }
8165
+ function reconcileLocalOnlyMergedPrAttention(options = {}) {
8166
+ const lookupPr = options.lookupPr ?? defaultLookupPr;
8167
+ const outcomes = [];
8168
+ const liveLookupCache = /* @__PURE__ */ new Map();
8169
+ for (const run of listRunRecords()) {
8170
+ const mergedByPr = collectMergedEvidenceByPr(run);
8171
+ const runDir2 = runDirectory(run.id);
8172
+ for (const name of listRunWorkerNames(run)) {
8173
+ const workerPath = path32.join(runDir2, "workers", safeSlug(name), "worker.json");
8174
+ const worker = readJson(workerPath, void 0);
8175
+ if (!worker || !isLocalOnlyAttentionNoise(worker)) continue;
8176
+ const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
8177
+ if (status.attention.state !== "needs_attention") continue;
8178
+ let merged = null;
8179
+ for (const prNumber of candidatePrNumbers(worker, status.finalResult)) {
8180
+ merged = mergedByPr.get(prNumber) ?? null;
8181
+ if (!merged) {
8182
+ const repoDir = run.repo || worker.worktreePath;
8183
+ const cacheKey = `${repoDir}#${prNumber}`;
8184
+ const live = liveLookupCache.has(cacheKey) ? liveLookupCache.get(cacheKey) ?? null : lookupPr({ repoDir, prNumber });
8185
+ if (!liveLookupCache.has(cacheKey)) liveLookupCache.set(cacheKey, live);
8186
+ if (live && (live.state === "MERGED" || live.mergedAt)) {
8187
+ merged = { prUrl: live.prUrl, mergeCommit: live.mergeCommit ?? null, reason: "GitHub reports PR merged" };
8188
+ }
8189
+ }
8190
+ if (merged) break;
8191
+ }
8192
+ if (!merged) {
8193
+ outcomes.push({ runId: run.id, worker: name, action: "skipped", reason: "no merged PR evidence" });
8194
+ continue;
8195
+ }
8196
+ const now = (/* @__PURE__ */ new Date()).toISOString();
8197
+ worker.status = "done";
8198
+ worker.completionSnapshot = {
8199
+ prUrl: merged.prUrl,
8200
+ summary: `Local-only worker superseded by merged PR ${merged.prUrl}`,
8201
+ finalResult: {
8202
+ summary: `Local-only repair/salvage worker superseded by merged PR ${merged.prUrl}`,
8203
+ targetPrReconciliation: [
8204
+ {
8205
+ prUrl: merged.prUrl,
8206
+ outcome: "merged",
8207
+ mergeCommit: merged.mergeCommit ?? null,
8208
+ reason: merged.reason ?? "PR already merged; local-only worker no longer requires attention"
8209
+ }
8210
+ ]
8211
+ }
8212
+ };
8213
+ worker.completionAckSource = "local-pr-merged-reconcile";
8214
+ worker.reconciledAt = now;
8215
+ worker.reconcileReason = "local-only needs_attention superseded by merged PR";
8216
+ saveWorker(run.id, worker);
8217
+ outcomes.push({
8218
+ runId: run.id,
8219
+ worker: name,
8220
+ action: "marked_done",
8221
+ reason: worker.reconcileReason,
8222
+ prUrl: merged.prUrl,
8223
+ mergeCommit: merged.mergeCommit ?? null
8224
+ });
8225
+ }
8226
+ }
8227
+ return { workers: outcomes };
8228
+ }
8229
+
8002
8230
  // src/stale-reconcile.ts
8003
8231
  var STALE_RECONCILE_HEARTBEAT_MS = 15 * 60 * 1e3;
8004
8232
  function staleReconcileDisabled() {
@@ -8007,13 +8235,14 @@ function staleReconcileDisabled() {
8007
8235
  function reconcileStaleWorkers() {
8008
8236
  const metadataReconcile = reconcileWorkerMetadata();
8009
8237
  if (staleReconcileDisabled()) {
8010
- return { workers: [], finalizedRuns: finalizeStaleRuns(), metadataReconcile };
8238
+ const localPrAttentionReconcile2 = reconcileLocalOnlyMergedPrAttention();
8239
+ return { workers: [], finalizedRuns: finalizeStaleRuns(), metadataReconcile, localPrAttentionReconcile: localPrAttentionReconcile2 };
8011
8240
  }
8012
8241
  const outcomes = [];
8013
8242
  const now = Date.now();
8014
8243
  for (const run of listRunRecords()) {
8015
8244
  for (const name of listRunWorkerNames(run)) {
8016
- const workerPath = path32.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
8245
+ const workerPath = path33.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
8017
8246
  const worker = readJson(workerPath, void 0);
8018
8247
  if (!worker || worker.status !== "running") {
8019
8248
  outcomes.push({
@@ -8085,7 +8314,8 @@ function reconcileStaleWorkers() {
8085
8314
  });
8086
8315
  }
8087
8316
  }
8088
- return { workers: outcomes, finalizedRuns: finalizeStaleRuns(), metadataReconcile };
8317
+ const localPrAttentionReconcile = reconcileLocalOnlyMergedPrAttention();
8318
+ return { workers: outcomes, finalizedRuns: finalizeStaleRuns(), metadataReconcile, localPrAttentionReconcile };
8089
8319
  }
8090
8320
  function reconcileRunsCli() {
8091
8321
  const result = reconcileStaleWorkers();
@@ -8096,6 +8326,10 @@ function reconcileRunsCli() {
8096
8326
  acc[row.action] = (acc[row.action] ?? 0) + 1;
8097
8327
  return acc;
8098
8328
  }, {});
8329
+ const localPrAttentionTotals = result.localPrAttentionReconcile.workers.reduce((acc, row) => {
8330
+ acc[row.action] = (acc[row.action] ?? 0) + 1;
8331
+ return acc;
8332
+ }, {});
8099
8333
  const runRetentionTotals = result.metadataReconcile.runMetadataRetention.runs.reduce((acc, row) => {
8100
8334
  acc[row.action] = (acc[row.action] ?? 0) + 1;
8101
8335
  return acc;
@@ -8113,10 +8347,15 @@ function reconcileRunsCli() {
8113
8347
  total: result.metadataReconcile.runMetadataRetention.runs.length
8114
8348
  }
8115
8349
  },
8350
+ localPrAttentionReconcile: {
8351
+ totals: localPrAttentionTotals,
8352
+ total: result.localPrAttentionReconcile.workers.length
8353
+ },
8116
8354
  finalizedRuns: result.finalizedRuns.length,
8117
8355
  details: {
8118
8356
  workers: result.workers,
8119
8357
  metadataReconcile: result.metadataReconcile.workers,
8358
+ localPrAttentionReconcile: result.localPrAttentionReconcile.workers,
8120
8359
  runMetadataRetention: result.metadataReconcile.runMetadataRetention.runs,
8121
8360
  finalizedRuns: result.finalizedRuns
8122
8361
  }
@@ -8137,7 +8376,7 @@ function heartbeatByteLength(heartbeatPath) {
8137
8376
  }
8138
8377
  }
8139
8378
  function workerEvidence(run, workerName) {
8140
- const workerPath = path33.join(runDirectory(run.id), "workers", safeSlug(workerName), "worker.json");
8379
+ const workerPath = path34.join(runDirectory(run.id), "workers", safeSlug(workerName), "worker.json");
8141
8380
  const worker = readJson(workerPath, void 0);
8142
8381
  if (!worker) {
8143
8382
  return {
@@ -8194,7 +8433,7 @@ function aggregateRunAttention(workers) {
8194
8433
  function countOpenWorkers(run) {
8195
8434
  let open = 0;
8196
8435
  for (const name of listRunWorkerNames(run)) {
8197
- const workerPath = path33.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
8436
+ const workerPath = path34.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
8198
8437
  const worker = readJson(workerPath, void 0);
8199
8438
  if (!worker) continue;
8200
8439
  const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
@@ -8270,7 +8509,7 @@ function createRun(args) {
8270
8509
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
8271
8510
  workers: {}
8272
8511
  };
8273
- writeJson(path34.join(dir, "run.json"), run);
8512
+ writeJson(path35.join(dir, "run.json"), run);
8274
8513
  console.log(JSON.stringify({ runId: id, runDir: dir, repo, base, baseCommit }, null, 2));
8275
8514
  }
8276
8515
  function listRuns() {
@@ -8283,7 +8522,7 @@ function failExists(message) {
8283
8522
 
8284
8523
  // src/discard-disposable.ts
8285
8524
  import { existsSync as existsSync26, rmSync as rmSync2 } from "node:fs";
8286
- import path35 from "node:path";
8525
+ import path36 from "node:path";
8287
8526
  function normalizeRelativePath2(value) {
8288
8527
  const normalized = value.replace(/\\/g, "/").replace(/^\.\//, "").trim();
8289
8528
  if (!normalized || normalized.startsWith("/") || normalized.includes("..")) {
@@ -8305,12 +8544,12 @@ function discardDisposableArtifacts(args) {
8305
8544
  if (paths.length === 0) {
8306
8545
  return { ok: false, removed: [], reason: "requires at least one --path" };
8307
8546
  }
8308
- const worktreeRoot = path35.resolve(worker.worktreePath);
8547
+ const worktreeRoot = path36.resolve(worker.worktreePath);
8309
8548
  const removed = [];
8310
8549
  for (const raw of paths) {
8311
8550
  const rel = normalizeRelativePath2(raw);
8312
- const abs = path35.resolve(worktreeRoot, rel);
8313
- if (!abs.startsWith(worktreeRoot + path35.sep) && abs !== worktreeRoot) {
8551
+ const abs = path36.resolve(worktreeRoot, rel);
8552
+ if (!abs.startsWith(worktreeRoot + path36.sep) && abs !== worktreeRoot) {
8314
8553
  return { ok: false, removed, reason: `path escapes worktree: ${raw}` };
8315
8554
  }
8316
8555
  if (!existsSync26(abs)) {
@@ -8377,7 +8616,7 @@ function validateDaemonInstallIdentity(config = loadUserConfig(), env = process.
8377
8616
  // src/cron/cron-env.ts
8378
8617
  import { existsSync as existsSync27 } from "node:fs";
8379
8618
  import { homedir as homedir11 } from "node:os";
8380
- import path36 from "node:path";
8619
+ import path37 from "node:path";
8381
8620
  function envFlag(name, defaultValue) {
8382
8621
  const raw = process.env[name]?.trim().toLowerCase();
8383
8622
  if (!raw) return defaultValue;
@@ -8393,7 +8632,7 @@ function envInt(name, fallback, min = 1) {
8393
8632
  function defaultKynverCronStorePath() {
8394
8633
  const explicit = process.env.KYNVER_CRON_STORE_PATH?.trim() || process.env.OPENCLAW_CRON_STORE_PATH?.trim();
8395
8634
  if (explicit) return explicit;
8396
- return path36.join(homedir11(), ".kynver", "agent-os-cron.json");
8635
+ return path37.join(homedir11(), ".kynver", "agent-os-cron.json");
8397
8636
  }
8398
8637
  function defaultKynverCronStatePath(storePath = defaultKynverCronStorePath()) {
8399
8638
  const explicit = process.env.KYNVER_CRON_TICK_STATE_PATH?.trim();
@@ -8635,7 +8874,7 @@ async function loadCronJobs(storePath = defaultKynverCronStorePath()) {
8635
8874
  // src/cron/cron-tick-state.ts
8636
8875
  import { randomBytes } from "node:crypto";
8637
8876
  import { promises as fs2 } from "node:fs";
8638
- import path37 from "node:path";
8877
+ import path38 from "node:path";
8639
8878
  var EMPTY = { version: 1, jobs: {} };
8640
8879
  async function readFileIfExists2(filePath) {
8641
8880
  try {
@@ -8662,7 +8901,7 @@ async function loadCronTickState(statePath) {
8662
8901
  return parseCronTickState(raw);
8663
8902
  }
8664
8903
  async function writeStateAtomic(statePath, state) {
8665
- await fs2.mkdir(path37.dirname(statePath), { recursive: true });
8904
+ await fs2.mkdir(path38.dirname(statePath), { recursive: true });
8666
8905
  const suffix = randomBytes(6).toString("hex");
8667
8906
  const tmp = `${statePath}.tmp-${process.pid}-${Date.now()}-${suffix}`;
8668
8907
  await fs2.writeFile(tmp, `${JSON.stringify(state, null, 2)}
@@ -8839,7 +9078,7 @@ async function runKynverCronTick(opts = {}) {
8839
9078
  }
8840
9079
 
8841
9080
  // src/pipeline-tick.ts
8842
- import path55 from "node:path";
9081
+ import path56 from "node:path";
8843
9082
 
8844
9083
  // src/pipeline-dispatch.ts
8845
9084
  var RESERVED_REVIEW_STARTS = 1;
@@ -8995,7 +9234,7 @@ function buildBoxResourceSnapshotFromGate(gate, input = {}) {
8995
9234
  }
8996
9235
 
8997
9236
  // src/plan-progress-daemon-sync.ts
8998
- import path38 from "node:path";
9237
+ import path39 from "node:path";
8999
9238
 
9000
9239
  // src/plan-progress-sync.ts
9001
9240
  async function syncPlanProgress(args) {
@@ -9019,7 +9258,7 @@ async function syncActiveWorkerPlanProgress(runId, args) {
9019
9258
  const outcomes = [];
9020
9259
  for (const name of Object.keys(run.workers || {})) {
9021
9260
  const worker = readJson(
9022
- path38.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
9261
+ path39.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
9023
9262
  void 0
9024
9263
  );
9025
9264
  if (!worker?.dispatched || !worker.taskId) continue;
@@ -9076,10 +9315,10 @@ async function fetchWorkspaceRuntimePreferences(agentOsId, args) {
9076
9315
  }
9077
9316
 
9078
9317
  // src/cleanup.ts
9079
- import path52 from "node:path";
9318
+ import path53 from "node:path";
9080
9319
 
9081
9320
  // src/cleanup-guards.ts
9082
- import path39 from "node:path";
9321
+ import path40 from "node:path";
9083
9322
 
9084
9323
  // src/cleanup-build-cache-paths.ts
9085
9324
  var HARNESS_BUILD_CACHE_RELATIVE_PATHS = [
@@ -9220,7 +9459,7 @@ function skipWorktreeRemoval(input) {
9220
9459
  function skipDependencyCacheRemoval(input) {
9221
9460
  const { indexed, nodeModulesAgeMs, ageMs, worktreePath, activeWorktreePaths, diskPressure } = input;
9222
9461
  if (!diskPressure && ageMs < nodeModulesAgeMs) return "below_age_threshold";
9223
- if (activeWorktreePaths.has(path39.resolve(worktreePath))) return "active_worker";
9462
+ if (activeWorktreePaths.has(path40.resolve(worktreePath))) return "active_worker";
9224
9463
  if (indexed && isWorkerProcessLive(indexed)) return "active_worker";
9225
9464
  if (indexed && indexedWorktreeHasMaterialChanges(indexed)) return "dirty_worktree";
9226
9465
  return null;
@@ -9247,11 +9486,11 @@ var LIVE_SKIP_REASONS = /* @__PURE__ */ new Set([
9247
9486
  function collectPreservedLivePaths(actions, skips) {
9248
9487
  const out = [];
9249
9488
  const seen = /* @__PURE__ */ new Set();
9250
- const push = (path69, reason, detail) => {
9251
- const key = `${path69}\0${reason}`;
9489
+ const push = (path71, reason, detail) => {
9490
+ const key = `${path71}\0${reason}`;
9252
9491
  if (seen.has(key) || out.length >= MAX_PRESERVED_LIVE_PATH_SAMPLES) return;
9253
9492
  seen.add(key);
9254
- out.push({ path: path69, reason, ...detail ? { detail } : {} });
9493
+ out.push({ path: path71, reason, ...detail ? { detail } : {} });
9255
9494
  };
9256
9495
  for (const skip2 of skips) {
9257
9496
  if (!LIVE_SKIP_REASONS.has(skip2.reason)) continue;
@@ -9267,11 +9506,11 @@ function collectPreservedLivePaths(actions, skips) {
9267
9506
 
9268
9507
  // src/cleanup-run-directory.ts
9269
9508
  import { existsSync as existsSync30, readdirSync as readdirSync9, statSync as statSync7 } from "node:fs";
9270
- import path41 from "node:path";
9509
+ import path42 from "node:path";
9271
9510
 
9272
9511
  // src/cleanup-active-worktrees.ts
9273
9512
  import { existsSync as existsSync29, readdirSync as readdirSync8, statSync as statSync6 } from "node:fs";
9274
- import path40 from "node:path";
9513
+ import path41 from "node:path";
9275
9514
  function workerHasRecentHarnessActivity(worker, now) {
9276
9515
  const paths = [worker.heartbeatPath, worker.stdoutPath, worker.stderrPath];
9277
9516
  for (const target of paths) {
@@ -9297,11 +9536,11 @@ function collectActiveWorktreeGuards(harnessRoots, now = Date.now()) {
9297
9536
  let runHasLive = false;
9298
9537
  for (const name of Object.keys(run.workers || {})) {
9299
9538
  const worker = readJson(
9300
- path40.join(runDirectoryAt(harnessRoot, run.id), "workers", safeSlug(name), "worker.json"),
9539
+ path41.join(runDirectoryAt(harnessRoot, run.id), "workers", safeSlug(name), "worker.json"),
9301
9540
  void 0
9302
9541
  );
9303
9542
  if (!worker?.worktreePath) continue;
9304
- const worktreePath = path40.resolve(worker.worktreePath);
9543
+ const worktreePath = path41.resolve(worker.worktreePath);
9305
9544
  if (!isActiveHarnessWorker2(worker, now)) continue;
9306
9545
  runHasLive = true;
9307
9546
  activeWorktreePaths.add(worktreePath);
@@ -9329,7 +9568,7 @@ function pathAgeMs(target, now) {
9329
9568
  }
9330
9569
  }
9331
9570
  function loadRunStatus(harnessRoot, runId) {
9332
- const runPath = path41.join(harnessRoot, "runs", runId, "run.json");
9571
+ const runPath = path42.join(harnessRoot, "runs", runId, "run.json");
9333
9572
  if (!existsSync30(runPath)) return null;
9334
9573
  return readJson(runPath, null);
9335
9574
  }
@@ -9365,7 +9604,7 @@ function scanStaleRunDirectoryCandidates(opts) {
9365
9604
  if (!runEntry.isDirectory()) continue;
9366
9605
  const runId = runEntry.name;
9367
9606
  if (opts.runIdFilter && runId !== opts.runIdFilter) continue;
9368
- const runPath = path41.join(opts.worktreesDir, runId);
9607
+ const runPath = path42.join(opts.worktreesDir, runId);
9369
9608
  if (!runDirectoryIsEmpty(runPath)) continue;
9370
9609
  candidates.push({
9371
9610
  kind: "remove_run_directory",
@@ -9383,14 +9622,14 @@ function scanStaleRunDirectoryCandidates(opts) {
9383
9622
  import { existsSync as existsSync33, rmSync as rmSync4 } from "node:fs";
9384
9623
 
9385
9624
  // src/cleanup-dir-size.ts
9386
- import { execFileSync } from "node:child_process";
9625
+ import { execFileSync as execFileSync2 } from "node:child_process";
9387
9626
  import { existsSync as existsSync31, readdirSync as readdirSync10, statSync as statSync8 } from "node:fs";
9388
- import path42 from "node:path";
9627
+ import path43 from "node:path";
9389
9628
  var DEFAULT_DU_TIMEOUT_MS = 2500;
9390
9629
  function directorySizeBytesDu(root, timeoutMs = DEFAULT_DU_TIMEOUT_MS) {
9391
9630
  if (!existsSync31(root)) return 0;
9392
9631
  try {
9393
- const out = execFileSync("du", ["-sb", root], {
9632
+ const out = execFileSync2("du", ["-sb", root], {
9394
9633
  encoding: "utf8",
9395
9634
  timeout: timeoutMs,
9396
9635
  stdio: ["ignore", "pipe", "ignore"]
@@ -9419,7 +9658,7 @@ function directorySizeBytes(root, maxEntries = 5e4) {
9419
9658
  }
9420
9659
  for (const name of entries) {
9421
9660
  if (seen++ > maxEntries) return null;
9422
- const full = path42.join(current, name);
9661
+ const full = path43.join(current, name);
9423
9662
  let st;
9424
9663
  try {
9425
9664
  st = statSync8(full);
@@ -9470,20 +9709,20 @@ function pathHasForeignOwnedEntry(targetPath, maxEntries = 32) {
9470
9709
 
9471
9710
  // src/cleanup-privileged-remove.ts
9472
9711
  import { spawnSync as spawnSync4 } from "node:child_process";
9473
- import path44 from "node:path";
9712
+ import path45 from "node:path";
9474
9713
 
9475
9714
  // src/cleanup-harness-path-validate.ts
9476
- import path43 from "node:path";
9715
+ import path44 from "node:path";
9477
9716
  function isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, cacheDirName) {
9478
- const resolved = path43.resolve(targetPath);
9479
- const suffix = `${path43.sep}${cacheDirName}`;
9717
+ const resolved = path44.resolve(targetPath);
9718
+ const suffix = `${path44.sep}${cacheDirName}`;
9480
9719
  const cachePath = resolved.endsWith(suffix) ? resolved : null;
9481
9720
  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);
9721
+ const rel = path44.relative(worktreesDir, cachePath);
9722
+ if (rel.startsWith("..") || path44.isAbsolute(rel)) return "path_outside_harness";
9723
+ const parts = rel.split(path44.sep);
9485
9724
  if (parts.length < 3 || parts[parts.length - 1] !== cacheDirName) return "path_outside_harness";
9486
- if (!resolved.startsWith(path43.resolve(harnessRoot))) return "path_outside_harness";
9725
+ if (!resolved.startsWith(path44.resolve(harnessRoot))) return "path_outside_harness";
9487
9726
  return null;
9488
9727
  }
9489
9728
  function isHarnessNodeModulesPath(targetPath, harnessRoot, worktreesDir) {
@@ -9493,16 +9732,16 @@ function isHarnessNextCachePath(targetPath, harnessRoot, worktreesDir) {
9493
9732
  return isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, ".next");
9494
9733
  }
9495
9734
  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);
9735
+ const resolved = path44.resolve(targetPath);
9736
+ const relToWt = path44.relative(worktreesDir, resolved);
9737
+ if (relToWt.startsWith("..") || path44.isAbsolute(relToWt)) return "path_outside_harness";
9738
+ const parts = relToWt.split(path44.sep);
9500
9739
  if (parts.length < 3) return "path_outside_harness";
9501
- if (!resolved.startsWith(path43.resolve(harnessRoot))) return "path_outside_harness";
9740
+ if (!resolved.startsWith(path44.resolve(harnessRoot))) return "path_outside_harness";
9502
9741
  return null;
9503
9742
  }
9504
9743
  function isHarnessGeneratedCachePath(targetPath, harnessRoot, worktreesDir) {
9505
- const resolved = path43.resolve(targetPath);
9744
+ const resolved = path44.resolve(targetPath);
9506
9745
  return isHarnessNodeModulesPath(resolved, harnessRoot, worktreesDir) === null || isHarnessNextCachePath(resolved, harnessRoot, worktreesDir) === null || isHarnessBuildCachePath(resolved, harnessRoot, worktreesDir) === null;
9507
9746
  }
9508
9747
 
@@ -9536,12 +9775,12 @@ function tryPrivilegedReclaimHarnessCache(targetPath, harnessRoot, worktreesDir)
9536
9775
  "chown",
9537
9776
  "-R",
9538
9777
  `${effectiveUid}:${effectiveGid}`,
9539
- path44.resolve(targetPath)
9778
+ path45.resolve(targetPath)
9540
9779
  ]);
9541
9780
  if (chown.ok) {
9542
9781
  return { ok: true, method: "chown_then_rm" };
9543
9782
  }
9544
- const rm = runSudoNonInteractive(["rm", "-rf", path44.resolve(targetPath)]);
9783
+ const rm = runSudoNonInteractive(["rm", "-rf", path45.resolve(targetPath)]);
9545
9784
  if (rm.ok) {
9546
9785
  return { ok: true, method: "sudo_rm" };
9547
9786
  }
@@ -9743,7 +9982,7 @@ function removeWorktree(candidate, execute) {
9743
9982
 
9744
9983
  // src/cleanup-scan.ts
9745
9984
  import { existsSync as existsSync34, readdirSync as readdirSync12, statSync as statSync9 } from "node:fs";
9746
- import path45 from "node:path";
9985
+ import path46 from "node:path";
9747
9986
  function pathAgeMs2(target, now) {
9748
9987
  try {
9749
9988
  const mtime = statSync9(target).mtimeMs;
@@ -9753,16 +9992,16 @@ function pathAgeMs2(target, now) {
9753
9992
  }
9754
9993
  }
9755
9994
  function isPathInside(child, parent) {
9756
- const rel = path45.relative(parent, child);
9757
- return rel === "" || !rel.startsWith("..") && !path45.isAbsolute(rel);
9995
+ const rel = path46.relative(parent, child);
9996
+ return rel === "" || !rel.startsWith("..") && !path46.isAbsolute(rel);
9758
9997
  }
9759
9998
  function collectBuildCacheForWorktree(worktreePath, opts, seen, meta) {
9760
9999
  const out = [];
9761
10000
  for (const rel of HARNESS_BUILD_CACHE_RELATIVE_PATHS) {
9762
10001
  if (rel === ".next") continue;
9763
- const target = path45.join(worktreePath, rel);
10002
+ const target = path46.join(worktreePath, rel);
9764
10003
  if (!existsSync34(target)) continue;
9765
- const resolved = path45.resolve(target);
10004
+ const resolved = path46.resolve(target);
9766
10005
  if (seen.has(resolved)) continue;
9767
10006
  if (!isPathInside(resolved, opts.harnessRoot)) continue;
9768
10007
  seen.add(resolved);
@@ -9794,10 +10033,10 @@ function scanBuildCacheCandidates(opts) {
9794
10033
  if (!opts.includeOrphans || !existsSync34(opts.worktreesDir)) return candidates;
9795
10034
  for (const runEntry of readdirSync12(opts.worktreesDir, { withFileTypes: true })) {
9796
10035
  if (!runEntry.isDirectory()) continue;
9797
- const runPath = path45.join(opts.worktreesDir, runEntry.name);
10036
+ const runPath = path46.join(opts.worktreesDir, runEntry.name);
9798
10037
  for (const workerEntry of readdirSync12(runPath, { withFileTypes: true })) {
9799
10038
  if (!workerEntry.isDirectory()) continue;
9800
- const worktreePath = path45.join(runPath, workerEntry.name);
10039
+ const worktreePath = path46.join(runPath, workerEntry.name);
9801
10040
  candidates.push(
9802
10041
  ...collectBuildCacheForWorktree(worktreePath, opts, seen, {
9803
10042
  runId: runEntry.name,
@@ -9835,12 +10074,12 @@ function scanWorktreeCandidates(opts) {
9835
10074
  if (!orphanEnabled || !existsSync34(opts.worktreesDir)) return candidates;
9836
10075
  const indexedPaths = /* @__PURE__ */ new Set();
9837
10076
  for (const entry of opts.index.values()) {
9838
- indexedPaths.add(path45.resolve(entry.worktreePath));
10077
+ indexedPaths.add(path46.resolve(entry.worktreePath));
9839
10078
  }
9840
10079
  for (const runEntry of readdirSync12(opts.worktreesDir, { withFileTypes: true })) {
9841
10080
  if (!runEntry.isDirectory()) continue;
9842
10081
  if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
9843
- const runPath = path45.join(opts.worktreesDir, runEntry.name);
10082
+ const runPath = path46.join(opts.worktreesDir, runEntry.name);
9844
10083
  let workerEntries;
9845
10084
  try {
9846
10085
  workerEntries = readdirSync12(runPath, { withFileTypes: true });
@@ -9849,7 +10088,7 @@ function scanWorktreeCandidates(opts) {
9849
10088
  }
9850
10089
  for (const workerEntry of workerEntries) {
9851
10090
  if (!workerEntry.isDirectory()) continue;
9852
- const worktreePath = path45.resolve(path45.join(runPath, workerEntry.name));
10091
+ const worktreePath = path46.resolve(path46.join(runPath, workerEntry.name));
9853
10092
  if (seen.has(worktreePath)) continue;
9854
10093
  if (indexedPaths.has(worktreePath)) continue;
9855
10094
  if (!isPathInside(worktreePath, opts.harnessRoot)) continue;
@@ -9869,7 +10108,7 @@ function scanWorktreeCandidates(opts) {
9869
10108
 
9870
10109
  // src/cleanup-dependency-scan.ts
9871
10110
  import { existsSync as existsSync35, readdirSync as readdirSync13, statSync as statSync10 } from "node:fs";
9872
- import path46 from "node:path";
10111
+ import path47 from "node:path";
9873
10112
  var DEPENDENCY_CACHE_DIRS = [
9874
10113
  { dirName: "node_modules", kind: "remove_node_modules" },
9875
10114
  { dirName: ".next", kind: "remove_next_cache" }
@@ -9883,12 +10122,12 @@ function pathAgeMs3(target, now) {
9883
10122
  }
9884
10123
  }
9885
10124
  function isPathInside2(child, parent) {
9886
- const rel = path46.relative(parent, child);
9887
- return rel === "" || !rel.startsWith("..") && !path46.isAbsolute(rel);
10125
+ const rel = path47.relative(parent, child);
10126
+ return rel === "" || !rel.startsWith("..") && !path47.isAbsolute(rel);
9888
10127
  }
9889
10128
  function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
9890
10129
  if (!existsSync35(targetPath)) return;
9891
- const resolved = path46.resolve(targetPath);
10130
+ const resolved = path47.resolve(targetPath);
9892
10131
  if (seen.has(resolved)) return;
9893
10132
  if (!isPathInside2(resolved, opts.harnessRoot)) return;
9894
10133
  seen.add(resolved);
@@ -9905,7 +10144,7 @@ function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
9905
10144
  }
9906
10145
  function scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, meta) {
9907
10146
  for (const entry of DEPENDENCY_CACHE_DIRS) {
9908
- pushCandidate2(candidates, seen, opts, path46.join(worktreePath, entry.dirName), entry.kind, meta);
10147
+ pushCandidate2(candidates, seen, opts, path47.join(worktreePath, entry.dirName), entry.kind, meta);
9909
10148
  }
9910
10149
  }
9911
10150
  function scanDependencyCacheCandidates(opts) {
@@ -9923,7 +10162,7 @@ function scanDependencyCacheCandidates(opts) {
9923
10162
  for (const runEntry of readdirSync13(opts.worktreesDir, { withFileTypes: true })) {
9924
10163
  if (!runEntry.isDirectory()) continue;
9925
10164
  if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
9926
- const runPath = path46.join(opts.worktreesDir, runEntry.name);
10165
+ const runPath = path47.join(opts.worktreesDir, runEntry.name);
9927
10166
  let workerEntries;
9928
10167
  try {
9929
10168
  workerEntries = readdirSync13(runPath, { withFileTypes: true });
@@ -9932,7 +10171,7 @@ function scanDependencyCacheCandidates(opts) {
9932
10171
  }
9933
10172
  for (const workerEntry of workerEntries) {
9934
10173
  if (!workerEntry.isDirectory()) continue;
9935
- const worktreePath = path46.join(runPath, workerEntry.name);
10174
+ const worktreePath = path47.join(runPath, workerEntry.name);
9936
10175
  scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, {
9937
10176
  runId: runEntry.name,
9938
10177
  worker: workerEntry.name
@@ -9944,7 +10183,7 @@ function scanDependencyCacheCandidates(opts) {
9944
10183
 
9945
10184
  // src/cleanup-duplicate-worktrees.ts
9946
10185
  import { existsSync as existsSync36, statSync as statSync11 } from "node:fs";
9947
- import path47 from "node:path";
10186
+ import path48 from "node:path";
9948
10187
  function pathAgeMs4(target, now) {
9949
10188
  try {
9950
10189
  const mtime = statSync11(target).mtimeMs;
@@ -9974,8 +10213,8 @@ function parseWorktreePorcelain(output) {
9974
10213
  return records;
9975
10214
  }
9976
10215
  function isUnderWorktreesDir(worktreePath, worktreesDir) {
9977
- const rel = path47.relative(path47.resolve(worktreesDir), path47.resolve(worktreePath));
9978
- return rel !== "" && !rel.startsWith("..") && !path47.isAbsolute(rel);
10216
+ const rel = path48.relative(path48.resolve(worktreesDir), path48.resolve(worktreePath));
10217
+ return rel !== "" && !rel.startsWith("..") && !path48.isAbsolute(rel);
9979
10218
  }
9980
10219
  function isCleanWorktree(worktreePath, repoRoot) {
9981
10220
  try {
@@ -9991,11 +10230,11 @@ function scanDuplicateWorktreeCandidates(opts) {
9991
10230
  if (!opts.includeOrphans || !existsSync36(opts.worktreesDir)) return [];
9992
10231
  const repos = /* @__PURE__ */ new Set();
9993
10232
  for (const entry of opts.index.values()) {
9994
- if (entry.run.repo) repos.add(path47.resolve(entry.run.repo));
10233
+ if (entry.run.repo) repos.add(path48.resolve(entry.run.repo));
9995
10234
  }
9996
10235
  const indexedPaths = /* @__PURE__ */ new Set();
9997
10236
  for (const entry of opts.index.values()) {
9998
- indexedPaths.add(path47.resolve(entry.worktreePath));
10237
+ indexedPaths.add(path48.resolve(entry.worktreePath));
9999
10238
  }
10000
10239
  const candidates = [];
10001
10240
  const seen = /* @__PURE__ */ new Set();
@@ -10008,15 +10247,15 @@ function scanDuplicateWorktreeCandidates(opts) {
10008
10247
  }
10009
10248
  const worktrees = parseWorktreePorcelain(porcelain);
10010
10249
  for (const wt of worktrees) {
10011
- const resolved = path47.resolve(wt.path);
10012
- if (resolved === path47.resolve(repoRoot)) continue;
10250
+ const resolved = path48.resolve(wt.path);
10251
+ if (resolved === path48.resolve(repoRoot)) continue;
10013
10252
  if (!isUnderWorktreesDir(resolved, opts.worktreesDir)) continue;
10014
10253
  if (indexedPaths.has(resolved)) continue;
10015
10254
  if (seen.has(resolved)) continue;
10016
10255
  if (!existsSync36(resolved)) continue;
10017
10256
  if (!isCleanWorktree(resolved, repoRoot)) continue;
10018
- const rel = path47.relative(opts.worktreesDir, resolved);
10019
- const parts = rel.split(path47.sep);
10257
+ const rel = path48.relative(opts.worktreesDir, resolved);
10258
+ const parts = rel.split(path48.sep);
10020
10259
  const runId = parts[0];
10021
10260
  const worker = parts[1] ?? "unknown";
10022
10261
  seen.add(resolved);
@@ -10035,12 +10274,12 @@ function scanDuplicateWorktreeCandidates(opts) {
10035
10274
  }
10036
10275
 
10037
10276
  // src/cleanup-worktree-index.ts
10038
- import path48 from "node:path";
10277
+ import path49 from "node:path";
10039
10278
  function buildWorktreeIndexAt(harnessRoot) {
10040
10279
  const index = /* @__PURE__ */ new Map();
10041
10280
  for (const run of listRunRecordsForHarnessRoot(harnessRoot)) {
10042
10281
  for (const name of Object.keys(run.workers || {})) {
10043
- const workerPath = path48.join(
10282
+ const workerPath = path49.join(
10044
10283
  runDirectoryAt(harnessRoot, run.id),
10045
10284
  "workers",
10046
10285
  safeSlug(name),
@@ -10048,9 +10287,9 @@ function buildWorktreeIndexAt(harnessRoot) {
10048
10287
  );
10049
10288
  const worker = readJson(workerPath, void 0);
10050
10289
  if (!worker?.worktreePath) continue;
10051
- index.set(path48.resolve(worker.worktreePath), {
10290
+ index.set(path49.resolve(worker.worktreePath), {
10052
10291
  harnessRoot,
10053
- worktreePath: path48.resolve(worker.worktreePath),
10292
+ worktreePath: path49.resolve(worker.worktreePath),
10054
10293
  runId: run.id,
10055
10294
  workerName: name,
10056
10295
  run,
@@ -10120,14 +10359,14 @@ function resolvePipelineHarnessRetention(runId) {
10120
10359
 
10121
10360
  // src/cleanup-orphan-safety.ts
10122
10361
  import { existsSync as existsSync37, statSync as statSync12 } from "node:fs";
10123
- import path49 from "node:path";
10362
+ import path50 from "node:path";
10124
10363
  var DEFAULT_HEARTBEAT_FRESH_MS = 30 * 60 * 1e3;
10125
10364
  function assessOrphanWorktreeSafety(input) {
10126
10365
  const now = input.now ?? Date.now();
10127
10366
  const heartbeatFreshMs = input.heartbeatFreshMs ?? DEFAULT_HEARTBEAT_FRESH_MS;
10128
10367
  if (!existsSync37(input.worktreePath)) return null;
10129
10368
  if (input.runId && input.workerName) {
10130
- const heartbeatPath = path49.join(
10369
+ const heartbeatPath = path50.join(
10131
10370
  input.harnessRoot,
10132
10371
  "runs",
10133
10372
  input.runId,
@@ -10141,7 +10380,7 @@ function assessOrphanWorktreeSafety(input) {
10141
10380
  } catch {
10142
10381
  }
10143
10382
  }
10144
- const gitDir = path49.join(input.worktreePath, ".git");
10383
+ const gitDir = path50.join(input.worktreePath, ".git");
10145
10384
  if (!existsSync37(gitDir)) return null;
10146
10385
  const porcelain = gitCapture(input.worktreePath, ["status", "--porcelain"]);
10147
10386
  if (porcelain.status !== 0) return "pr_or_unmerged_commits";
@@ -10172,7 +10411,7 @@ function assessOrphanWorktreeSafety(input) {
10172
10411
 
10173
10412
  // src/harness-storage-snapshot.ts
10174
10413
  import { existsSync as existsSync38, readdirSync as readdirSync14, statSync as statSync13 } from "node:fs";
10175
- import path50 from "node:path";
10414
+ import path51 from "node:path";
10176
10415
  function harnessStorageSnapshot(opts = {}) {
10177
10416
  const harnessRoot = normalizeHarnessRoot(opts.harnessRoot ?? resolveHarnessRoot());
10178
10417
  const worktreesDir = harnessWorktreesDir(harnessRoot);
@@ -10210,7 +10449,7 @@ function harnessStorageSnapshot(opts = {}) {
10210
10449
  for (const runEntry of entries) {
10211
10450
  if (!runEntry.isDirectory()) continue;
10212
10451
  runCount += 1;
10213
- const runPath = path50.join(worktreesDir, runEntry.name);
10452
+ const runPath = path51.join(worktreesDir, runEntry.name);
10214
10453
  try {
10215
10454
  const st = statSync13(runPath);
10216
10455
  oldestMs = oldestMs === null ? st.mtimeMs : Math.min(oldestMs, st.mtimeMs);
@@ -10247,10 +10486,10 @@ function harnessStorageSnapshot(opts = {}) {
10247
10486
  // src/cleanup-harness-roots.ts
10248
10487
  import { existsSync as existsSync39 } from "node:fs";
10249
10488
  import { homedir as homedir12 } from "node:os";
10250
- import path51 from "node:path";
10489
+ import path52 from "node:path";
10251
10490
  var WELL_KNOWN_HARNESS_SCAN_ROOTS = [
10252
10491
  "/var/tmp/kynver-harness",
10253
- path51.join(homedir12(), ".openclaw", "harness")
10492
+ path52.join(homedir12(), ".openclaw", "harness")
10254
10493
  ];
10255
10494
  function addRoot(seen, roots, candidate) {
10256
10495
  if (!candidate?.trim()) return;
@@ -10272,7 +10511,7 @@ function resolveHarnessScanRoots(options = {}) {
10272
10511
  for (const candidate of extra ?? []) addRoot(seen, roots, candidate);
10273
10512
  if (shouldScanWellKnownRoots(options)) {
10274
10513
  for (const candidate of WELL_KNOWN_HARNESS_SCAN_ROOTS) {
10275
- const resolved = path51.resolve(candidate);
10514
+ const resolved = path52.resolve(candidate);
10276
10515
  if (!seen.has(resolved) && existsSync39(resolved)) addRoot(seen, roots, resolved);
10277
10516
  }
10278
10517
  }
@@ -10427,9 +10666,9 @@ function mergeWorktreeIndexes(scanRoots) {
10427
10666
  }
10428
10667
  function worktreePathForCandidate(candidate, worktreesDir) {
10429
10668
  if (candidate.runId && candidate.worker) {
10430
- return path52.join(worktreesDir, candidate.runId, candidate.worker);
10669
+ return path53.join(worktreesDir, candidate.runId, candidate.worker);
10431
10670
  }
10432
- return path52.resolve(candidate.path, "..");
10671
+ return path53.resolve(candidate.path, "..");
10433
10672
  }
10434
10673
  function runHarnessCleanup(options = {}) {
10435
10674
  let retention = resolveHarnessRetention(options);
@@ -10454,7 +10693,7 @@ function runHarnessCleanup(options = {}) {
10454
10693
  for (const harnessRoot of paths.scanRoots) {
10455
10694
  if (atSweepCap()) break;
10456
10695
  emitCleanupProgress("root", harnessRoot);
10457
- const worktreesDir = path52.join(harnessRoot, "worktrees");
10696
+ const worktreesDir = path53.join(harnessRoot, "worktrees");
10458
10697
  const scanOpts = {
10459
10698
  harnessRoot,
10460
10699
  worktreesDir,
@@ -10467,7 +10706,7 @@ function runHarnessCleanup(options = {}) {
10467
10706
  };
10468
10707
  for (const raw of scanDependencyCacheCandidates(scanOpts)) {
10469
10708
  if (atSweepCap()) break;
10470
- const resolved = path52.resolve(raw.path);
10709
+ const resolved = path53.resolve(raw.path);
10471
10710
  if (processedPaths.has(resolved)) continue;
10472
10711
  processedPaths.add(resolved);
10473
10712
  const candidate = { ...raw, path: resolved };
@@ -10478,7 +10717,7 @@ function runHarnessCleanup(options = {}) {
10478
10717
  continue;
10479
10718
  }
10480
10719
  const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
10481
- const indexed = index.get(path52.resolve(worktreePath)) ?? null;
10720
+ const indexed = index.get(path53.resolve(worktreePath)) ?? null;
10482
10721
  const guardReason = skipDependencyCacheRemoval({
10483
10722
  indexed,
10484
10723
  includeOrphans: true,
@@ -10502,7 +10741,7 @@ function runHarnessCleanup(options = {}) {
10502
10741
  }
10503
10742
  for (const raw of scanBuildCacheCandidates(scanOpts)) {
10504
10743
  if (atSweepCap()) break;
10505
- const resolved = path52.resolve(raw.path);
10744
+ const resolved = path53.resolve(raw.path);
10506
10745
  if (processedPaths.has(resolved)) continue;
10507
10746
  processedPaths.add(resolved);
10508
10747
  const candidate = { ...raw, path: resolved };
@@ -10513,7 +10752,7 @@ function runHarnessCleanup(options = {}) {
10513
10752
  continue;
10514
10753
  }
10515
10754
  const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
10516
- const indexed = index.get(path52.resolve(worktreePath)) ?? null;
10755
+ const indexed = index.get(path53.resolve(worktreePath)) ?? null;
10517
10756
  const guardReason = skipBuildCacheRemoval({
10518
10757
  indexed,
10519
10758
  includeOrphans: true,
@@ -10543,11 +10782,11 @@ function runHarnessCleanup(options = {}) {
10543
10782
  const worktreeSeen = /* @__PURE__ */ new Set();
10544
10783
  for (const raw of worktreeCandidates) {
10545
10784
  if (atSweepCap()) break;
10546
- const resolved = path52.resolve(raw.path);
10785
+ const resolved = path53.resolve(raw.path);
10547
10786
  if (worktreeSeen.has(resolved)) continue;
10548
10787
  worktreeSeen.add(resolved);
10549
10788
  const candidate = { ...raw, path: resolved };
10550
- const indexed = index.get(path52.resolve(candidate.path)) ?? null;
10789
+ const indexed = index.get(path53.resolve(candidate.path)) ?? null;
10551
10790
  const orphanSafety = indexed ? null : assessOrphanWorktreeSafety({
10552
10791
  worktreePath: candidate.path,
10553
10792
  harnessRoot,
@@ -10557,7 +10796,7 @@ function runHarnessCleanup(options = {}) {
10557
10796
  });
10558
10797
  const guardSkip = skipWorktreeRemoval({
10559
10798
  indexed,
10560
- worktreePath: path52.resolve(candidate.path),
10799
+ worktreePath: path53.resolve(candidate.path),
10561
10800
  includeOrphans: retention.includeOrphans,
10562
10801
  worktreesAgeMs: retention.worktreesAgeMs,
10563
10802
  terminalWorktreesAgeMs: retention.terminalWorktreesAgeMs,
@@ -10589,11 +10828,11 @@ function runHarnessCleanup(options = {}) {
10589
10828
  now: paths.now
10590
10829
  })) {
10591
10830
  if (atSweepCap()) break;
10592
- const resolved = path52.resolve(raw.path);
10831
+ const resolved = path53.resolve(raw.path);
10593
10832
  if (processedPaths.has(resolved)) continue;
10594
10833
  processedPaths.add(resolved);
10595
10834
  const candidate = { ...raw, path: resolved };
10596
- const runId = candidate.runId ?? path52.basename(resolved);
10835
+ const runId = candidate.runId ?? path53.basename(resolved);
10597
10836
  const dirSkip = skipRunDirectoryRemoval({
10598
10837
  harnessRoot,
10599
10838
  runId,
@@ -10729,11 +10968,11 @@ function isPipelineCleanupEnabled() {
10729
10968
  // src/installed-package-versions.ts
10730
10969
  import { readFile } from "node:fs/promises";
10731
10970
  import { homedir as homedir13 } from "node:os";
10732
- import path54 from "node:path";
10971
+ import path55 from "node:path";
10733
10972
 
10734
10973
  // src/memory-cost-package-version-guard.ts
10735
10974
  import { existsSync as existsSync40, readFileSync as readFileSync13 } from "node:fs";
10736
- import path53 from "node:path";
10975
+ import path54 from "node:path";
10737
10976
  var MEMORY_COST_PACKAGE_MIN_VERSIONS = {
10738
10977
  "@kynver-app/runtime": "0.1.83",
10739
10978
  "@kynver-app/openclaw-agent-os": "0.1.43",
@@ -10795,8 +11034,8 @@ function resolveRepoRoot(cwd, explicitRepoRoot) {
10795
11034
  (value) => Boolean(value?.trim())
10796
11035
  );
10797
11036
  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"))) {
11037
+ const resolved = path54.resolve(candidate);
11038
+ if (existsSync40(path54.join(resolved, "packages/kynver-runtime/package.json")) && existsSync40(path54.join(resolved, "package.json"))) {
10800
11039
  return resolved;
10801
11040
  }
10802
11041
  }
@@ -10809,7 +11048,7 @@ function probeRepoPackageVersions(input = {}) {
10809
11048
  if (!repoRoot) return {};
10810
11049
  const out = {};
10811
11050
  for (const packageName of MEMORY_COST_MANAGED_PACKAGES) {
10812
- const packageJsonPath = path53.join(repoRoot, REPO_PACKAGE_JSON_RELATIVE[packageName]);
11051
+ const packageJsonPath = path54.join(repoRoot, REPO_PACKAGE_JSON_RELATIVE[packageName]);
10813
11052
  const version = readPackageJsonVersion(packageJsonPath);
10814
11053
  if (!version) continue;
10815
11054
  out[packageName] = { version, source: "repo", path: packageJsonPath };
@@ -10929,12 +11168,12 @@ function unique(values) {
10929
11168
  }
10930
11169
  function moduleRoots() {
10931
11170
  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"));
11171
+ const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ?? path55.join(home, ".openclaw", "npm");
11172
+ 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
11173
  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")
11174
+ path55.join(openClawPrefix, "lib", "node_modules"),
11175
+ path55.join(openClawPrefix, "node_modules"),
11176
+ npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot : path55.join(npmGlobalRoot, "lib", "node_modules")
10938
11177
  ]);
10939
11178
  }
10940
11179
  async function readVersion(packageJsonPath) {
@@ -10950,7 +11189,7 @@ function installedPackageJsonCandidates(packageName) {
10950
11189
  const seen = /* @__PURE__ */ new Set();
10951
11190
  const out = [];
10952
11191
  for (const root of roots) {
10953
- const candidate = path54.join(root, packageName, "package.json");
11192
+ const candidate = path55.join(root, packageName, "package.json");
10954
11193
  if (seen.has(candidate)) continue;
10955
11194
  seen.add(candidate);
10956
11195
  out.push(candidate);
@@ -10981,7 +11220,7 @@ async function completeFinishedWorkers(runId, args) {
10981
11220
  const outcomes = [];
10982
11221
  for (const name of Object.keys(run.workers || {})) {
10983
11222
  const worker = readJson(
10984
- path55.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
11223
+ path56.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
10985
11224
  void 0
10986
11225
  );
10987
11226
  if (!worker?.taskId || worker.localOnly) continue;
@@ -11128,6 +11367,15 @@ async function runPipelineTick(args) {
11128
11367
  var DEFAULT_INTERVAL_MS = 6e4;
11129
11368
  var IDLE_INTERVAL_MS = 5 * 6e4;
11130
11369
  var MAX_IDLE_STREAK = 10;
11370
+ var SLEEP_POLL_MS = 250;
11371
+ async function awaitDaemonBackoff(ms, isStopping) {
11372
+ let remaining = ms;
11373
+ while (remaining > 0 && !isStopping()) {
11374
+ const step = Math.min(SLEEP_POLL_MS, remaining);
11375
+ await sleepMsAsync(step);
11376
+ remaining -= step;
11377
+ }
11378
+ }
11131
11379
  async function runDaemon(args) {
11132
11380
  const runId = String(required(String(args.run || ""), "--run"));
11133
11381
  const agentOsId = String(required(String(args.agentOsId || loadUserConfig().agentOsId || ""), "--agent-os-id"));
@@ -11185,17 +11433,17 @@ async function runDaemon(args) {
11185
11433
  idleStreak = 0;
11186
11434
  }
11187
11435
  const backoff = idleStreak >= MAX_IDLE_STREAK ? IDLE_INTERVAL_MS : intervalMs;
11188
- await sleepMs(backoff);
11436
+ await awaitDaemonBackoff(backoff, () => stopping);
11189
11437
  } catch (error) {
11190
11438
  console.error(JSON.stringify({ event: "daemon_tick_error", error: error.message }));
11191
- await sleepMs(intervalMs);
11439
+ await awaitDaemonBackoff(intervalMs, () => stopping);
11192
11440
  }
11193
11441
  }
11194
11442
  console.error(JSON.stringify({ event: "daemon_stop", runId, agentOsId }));
11195
11443
  }
11196
11444
 
11197
11445
  // src/plan-progress.ts
11198
- import path59 from "node:path";
11446
+ import path60 from "node:path";
11199
11447
 
11200
11448
  // src/bounded-build/constants.ts
11201
11449
  var DEFAULT_BUILD_MEM_BUDGET_BYTES = 1536 * 1024 * 1024;
@@ -11338,16 +11586,16 @@ import {
11338
11586
  unlinkSync as unlinkSync4,
11339
11587
  writeFileSync as writeFileSync5
11340
11588
  } from "node:fs";
11341
- import path57 from "node:path";
11589
+ import path58 from "node:path";
11342
11590
 
11343
11591
  // src/heavy-verification/paths.ts
11344
11592
  import { mkdirSync as mkdirSync7 } from "node:fs";
11345
- import path56 from "node:path";
11593
+ import path57 from "node:path";
11346
11594
  function resolveHeavyVerificationRoot() {
11347
- return path56.join(resolveKynverStateRoot(), "heavy-verification");
11595
+ return path57.join(resolveKynverStateRoot(), "heavy-verification");
11348
11596
  }
11349
11597
  function heavyVerificationSlotsDir() {
11350
- return path56.join(resolveHeavyVerificationRoot(), "slots");
11598
+ return path57.join(resolveHeavyVerificationRoot(), "slots");
11351
11599
  }
11352
11600
  function ensureHeavyVerificationDirs() {
11353
11601
  const dir = heavyVerificationSlotsDir();
@@ -11376,7 +11624,7 @@ function indexedSlotId(index) {
11376
11624
  return `slot-${index}`;
11377
11625
  }
11378
11626
  function slotFilePath(slotId, slotsDir = heavyVerificationSlotsDir()) {
11379
- return path57.join(slotsDir, `${slotId}.json`);
11627
+ return path58.join(slotsDir, `${slotId}.json`);
11380
11628
  }
11381
11629
  function readSlotRecord(filePath) {
11382
11630
  if (!existsSync41(filePath)) return null;
@@ -11415,7 +11663,7 @@ function reclaimStaleHeavyVerificationSlots(opts = {}) {
11415
11663
  let reclaimed = 0;
11416
11664
  for (const name of readdirSync15(slotsDir)) {
11417
11665
  if (!name.endsWith(".json")) continue;
11418
- const filePath = path57.join(slotsDir, name);
11666
+ const filePath = path58.join(slotsDir, name);
11419
11667
  const before = existsSync41(filePath);
11420
11668
  reclaimStaleSlot(filePath, staleMs);
11421
11669
  if (before && !existsSync41(filePath)) reclaimed += 1;
@@ -11429,7 +11677,7 @@ function listActiveHeavyVerificationSlots(opts = {}) {
11429
11677
  const active = [];
11430
11678
  for (const name of readdirSync15(slotsDir)) {
11431
11679
  if (!name.endsWith(".json")) continue;
11432
- const record = readSlotRecord(path57.join(slotsDir, name));
11680
+ const record = readSlotRecord(path58.join(slotsDir, name));
11433
11681
  if (record && !slotIsStale(record, staleMs)) active.push(record);
11434
11682
  }
11435
11683
  return active;
@@ -11549,11 +11797,11 @@ function waitForHeavyVerificationSlot(command, timeoutMs, pollMs = 2e3, opts = {
11549
11797
  }
11550
11798
 
11551
11799
  // src/harness-worktree-build-guard.ts
11552
- import path58 from "node:path";
11800
+ import path59 from "node:path";
11553
11801
  function isPathUnderHarnessWorktree(cwd) {
11554
11802
  const worktreesDir = harnessWorktreesDir(resolveHarnessRoot());
11555
- const rel = path58.relative(worktreesDir, path58.resolve(cwd));
11556
- return rel.length > 0 && !rel.startsWith("..") && !path58.isAbsolute(rel);
11803
+ const rel = path59.relative(worktreesDir, path59.resolve(cwd));
11804
+ return rel.length > 0 && !rel.startsWith("..") && !path59.isAbsolute(rel);
11557
11805
  }
11558
11806
  function assessHarnessWorktreeBuildGuard(cwd) {
11559
11807
  if (!isPathUnderHarnessWorktree(cwd)) return { ok: true };
@@ -11765,7 +12013,7 @@ async function emitPlanProgress(args) {
11765
12013
  }
11766
12014
  function verifyPlanLocal(args) {
11767
12015
  const worktree = required(args.worktree ? String(args.worktree) : void 0, "worktree");
11768
- const cwd = path59.resolve(worktree);
12016
+ const cwd = path60.resolve(worktree);
11769
12017
  const summary = runHarnessVerifyCommands(cwd);
11770
12018
  const emitJson = args.json === true || args.json === "true";
11771
12019
  const payload = { passed: summary.passed, worktree: cwd, steps: summary.steps };
@@ -11814,9 +12062,9 @@ async function verifyPlan(args) {
11814
12062
  }
11815
12063
 
11816
12064
  // src/harness-verify-cli.ts
11817
- import path60 from "node:path";
12065
+ import path61 from "node:path";
11818
12066
  function runHarnessVerifyCli(args) {
11819
- const cwd = path60.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
12067
+ const cwd = path61.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
11820
12068
  const emitJson = args.json === true || args.json === "true" || args.emitJson === true || args.emitJson === "true";
11821
12069
  const commands = [];
11822
12070
  const rawCmd = args.command;
@@ -12024,7 +12272,7 @@ function formatMonitorTickNotice(tick) {
12024
12272
  }
12025
12273
 
12026
12274
  // src/monitor/monitor.service.ts
12027
- import path62 from "node:path";
12275
+ import path63 from "node:path";
12028
12276
 
12029
12277
  // src/monitor/monitor.classify.ts
12030
12278
  function classifyWorkerHealth(input) {
@@ -12077,10 +12325,10 @@ function classifyWorkerHealth(input) {
12077
12325
 
12078
12326
  // src/monitor/monitor.store.ts
12079
12327
  import { existsSync as existsSync42, mkdirSync as mkdirSync9, readdirSync as readdirSync16, unlinkSync as unlinkSync5 } from "node:fs";
12080
- import path61 from "node:path";
12328
+ import path62 from "node:path";
12081
12329
  function monitorsDir() {
12082
12330
  const { harnessRoot } = getHarnessPaths();
12083
- const dir = path61.join(harnessRoot, "monitors");
12331
+ const dir = path62.join(harnessRoot, "monitors");
12084
12332
  mkdirSync9(dir, { recursive: true });
12085
12333
  return dir;
12086
12334
  }
@@ -12088,7 +12336,7 @@ function monitorIdFor(runId, workerName) {
12088
12336
  return workerName ? `${safeSlug(runId)}--${safeSlug(workerName)}` : safeSlug(runId);
12089
12337
  }
12090
12338
  function monitorPath(monitorId) {
12091
- return path61.join(monitorsDir(), `${monitorId}.json`);
12339
+ return path62.join(monitorsDir(), `${monitorId}.json`);
12092
12340
  }
12093
12341
  function loadMonitorSession(monitorId) {
12094
12342
  return readJson(monitorPath(monitorId), void 0);
@@ -12109,7 +12357,7 @@ function listMonitorSessions() {
12109
12357
  for (const name of readdirSync16(dir)) {
12110
12358
  if (!name.endsWith(".json")) continue;
12111
12359
  const session = readJson(
12112
- path61.join(dir, name),
12360
+ path62.join(dir, name),
12113
12361
  void 0
12114
12362
  );
12115
12363
  if (!session?.monitorId) continue;
@@ -12200,7 +12448,7 @@ async function fetchTaskLeasesForWorkers(input) {
12200
12448
  // src/monitor/monitor.service.ts
12201
12449
  function workerRecord2(runId, name) {
12202
12450
  return readJson(
12203
- path62.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
12451
+ path63.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
12204
12452
  void 0
12205
12453
  );
12206
12454
  }
@@ -12407,17 +12655,17 @@ async function runMonitorLoop(args) {
12407
12655
  // src/monitor/monitor-spawn.ts
12408
12656
  import { spawn as spawn6 } from "node:child_process";
12409
12657
  import { closeSync as closeSync8, existsSync as existsSync43, openSync as openSync8 } from "node:fs";
12410
- import path63 from "node:path";
12658
+ import path64 from "node:path";
12411
12659
  import { fileURLToPath as fileURLToPath3 } from "node:url";
12412
12660
  function resolveDefaultCliPath2() {
12413
- return path63.join(fileURLToPath3(new URL(".", import.meta.url)), "cli.js");
12661
+ return path64.join(fileURLToPath3(new URL(".", import.meta.url)), "cli.js");
12414
12662
  }
12415
12663
  function spawnMonitorSidecar(opts) {
12416
12664
  const cliPath = opts.cliPath ?? resolveDefaultCliPath2();
12417
12665
  if (!existsSync43(cliPath)) return void 0;
12418
12666
  const monitorId = monitorIdFor(opts.runId, opts.workerName);
12419
12667
  const { harnessRoot } = getHarnessPaths();
12420
- const logPath = path63.join(harnessRoot, "monitors", `${monitorId}.log`);
12668
+ const logPath = path64.join(harnessRoot, "monitors", `${monitorId}.log`);
12421
12669
  let logFd;
12422
12670
  try {
12423
12671
  logFd = openSync8(logPath, "a");
@@ -12618,8 +12866,32 @@ function shouldEnforceMemoryCostPackageGuardCli(scope, action) {
12618
12866
  return false;
12619
12867
  }
12620
12868
 
12869
+ // src/run-resolve.ts
12870
+ function resolveHarnessRunByName(runName) {
12871
+ const name = runName.trim();
12872
+ if (!name) return null;
12873
+ const rows = buildRunListRows();
12874
+ return rows.find((row) => row.name === name && row.status !== "completed") ?? null;
12875
+ }
12876
+ function resolveHarnessRunCli(args) {
12877
+ const name = String(required(String(args.name || ""), "--name"));
12878
+ const hit = resolveHarnessRunByName(name);
12879
+ console.log(
12880
+ JSON.stringify(
12881
+ {
12882
+ runId: hit?.id ?? null,
12883
+ name,
12884
+ status: hit?.status ?? null,
12885
+ effectiveStatus: hit?.effectiveStatus ?? null
12886
+ },
12887
+ null,
12888
+ 2
12889
+ )
12890
+ );
12891
+ }
12892
+
12621
12893
  // src/post-restart-unblock.ts
12622
- import path64 from "node:path";
12894
+ import path65 from "node:path";
12623
12895
  function skip(runId, worker, taskId, agentOsId, leaseOwner, reason) {
12624
12896
  return { runId, worker, taskId, agentOsId, leaseOwner, action: "skipped", reason };
12625
12897
  }
@@ -12632,7 +12904,7 @@ async function postRestartUnblock(args) {
12632
12904
  const errors = [];
12633
12905
  for (const run of listRunRecords()) {
12634
12906
  for (const name of Object.keys(run.workers ?? {})) {
12635
- const workerPath = path64.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
12907
+ const workerPath = path65.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
12636
12908
  const worker = readJson(workerPath, void 0);
12637
12909
  if (!worker) {
12638
12910
  skipped.push(skip(run.id, name, "", "", "", "worker.json missing"));
@@ -12744,9 +13016,9 @@ async function postRestartUnblockCli(args) {
12744
13016
  }
12745
13017
 
12746
13018
  // src/default-repo-cli.ts
12747
- import path65 from "node:path";
13019
+ import path66 from "node:path";
12748
13020
  import { homedir as homedir14 } from "node:os";
12749
- var CONFIG_FILE2 = path65.join(homedir14(), ".kynver", "config.json");
13021
+ var CONFIG_FILE2 = path66.join(homedir14(), ".kynver", "config.json");
12750
13022
  function ensureDefaultRepo(opts) {
12751
13023
  const existing = loadUserConfig();
12752
13024
  const resolved = resolveDefaultRepo({ ...opts, config: existing });
@@ -12827,12 +13099,12 @@ function summarizeResolvedDefaultRepo(resolved) {
12827
13099
  }
12828
13100
 
12829
13101
  // src/doctor/runtime-takeover.ts
12830
- import path67 from "node:path";
13102
+ import path68 from "node:path";
12831
13103
 
12832
13104
  // src/doctor/runtime-takeover.probes.ts
12833
13105
  import { accessSync, constants, existsSync as existsSync45, readFileSync as readFileSync17 } from "node:fs";
12834
13106
  import { homedir as homedir15 } from "node:os";
12835
- import path66 from "node:path";
13107
+ import path67 from "node:path";
12836
13108
  import { spawnSync as spawnSync9 } from "node:child_process";
12837
13109
  function captureCommand(bin, args) {
12838
13110
  try {
@@ -12874,10 +13146,10 @@ var defaultRuntimeTakeoverProbes = {
12874
13146
  commandOnPath: (bin) => captureCommand(process.platform === "win32" ? "where" : "which", [bin]),
12875
13147
  kynverVersion: (bin) => captureCommand(bin, ["--version"]),
12876
13148
  loadConfig: () => loadUserConfig(),
12877
- configFilePath: () => path66.join(homedir15(), ".kynver", "config.json"),
12878
- credentialsFilePath: () => path66.join(homedir15(), ".kynver", "credentials"),
13149
+ configFilePath: () => path67.join(homedir15(), ".kynver", "config.json"),
13150
+ credentialsFilePath: () => path67.join(homedir15(), ".kynver", "credentials"),
12879
13151
  readCredentials: () => {
12880
- const credPath = path66.join(homedir15(), ".kynver", "credentials");
13152
+ const credPath = path67.join(homedir15(), ".kynver", "credentials");
12881
13153
  if (!existsSync45(credPath)) {
12882
13154
  return { hasApiKey: false };
12883
13155
  }
@@ -12912,7 +13184,7 @@ var defaultRuntimeTakeoverProbes = {
12912
13184
  })()
12913
13185
  }),
12914
13186
  harnessRoot: () => resolveHarnessRoot(),
12915
- legacyOpenclawHarnessRoot: () => path66.join(homedir15(), ".openclaw", "harness"),
13187
+ legacyOpenclawHarnessRoot: () => path67.join(homedir15(), ".openclaw", "harness"),
12916
13188
  pathExists: (target) => existsSync45(target),
12917
13189
  pathWritable: (target) => isWritable(target)
12918
13190
  };
@@ -13319,8 +13591,8 @@ function assessVercelDeployEvidence(probes) {
13319
13591
  }
13320
13592
  function assessHarnessDirs(probes) {
13321
13593
  const harnessRoot = probes.harnessRoot();
13322
- const runsDir = path67.join(harnessRoot, "runs");
13323
- const worktreesDir = path67.join(harnessRoot, "worktrees");
13594
+ const runsDir = path68.join(harnessRoot, "runs");
13595
+ const worktreesDir = path68.join(harnessRoot, "worktrees");
13324
13596
  const displayHarnessRoot = redactHomePath(harnessRoot);
13325
13597
  const displayRunsDir = redactHomePath(runsDir);
13326
13598
  const displayWorktreesDir = redactHomePath(worktreesDir);
@@ -13584,9 +13856,9 @@ function applySchedulerCutoverAttestation(config) {
13584
13856
  }
13585
13857
 
13586
13858
  // src/scheduler-cutover-cli.ts
13587
- import path68 from "node:path";
13859
+ import path69 from "node:path";
13588
13860
  import { homedir as homedir16 } from "node:os";
13589
- var CONFIG_FILE3 = path68.join(homedir16(), ".kynver", "config.json");
13861
+ var CONFIG_FILE3 = path69.join(homedir16(), ".kynver", "config.json");
13590
13862
  function runSchedulerCutoverCheckCli(json = false) {
13591
13863
  const config = loadUserConfig();
13592
13864
  const report = assessSchedulerCutover(config);
@@ -13723,6 +13995,157 @@ async function runCronTickCli(args) {
13723
13995
  );
13724
13996
  }
13725
13997
 
13998
+ // src/lane/landing-maintainer-tick.ts
13999
+ import os9 from "node:os";
14000
+
14001
+ // src/lane/lane-spec.ts
14002
+ var LANDING_MAINTAINER_LANE_SPEC = {
14003
+ slug: "landing-maintainer",
14004
+ originCron: "maintain-8-blocker-and-pr-landing-workers",
14005
+ defaultRepo: "Totalsolutionsync/Kynver",
14006
+ landScript: "scripts/agent-os-land-pr.mjs",
14007
+ landScriptArgs: ["--skip-not-ready"]
14008
+ };
14009
+
14010
+ // src/lane/landing-maintainer-local.ts
14011
+ import { spawnSync as spawnSync10 } from "node:child_process";
14012
+ import path70 from "node:path";
14013
+ function runLandingWrapper(prNumber, repoRoot, execute) {
14014
+ const script = path70.join(repoRoot, LANDING_MAINTAINER_LANE_SPEC.landScript);
14015
+ const args = [script, String(prNumber), ...LANDING_MAINTAINER_LANE_SPEC.landScriptArgs];
14016
+ if (!execute) {
14017
+ return {
14018
+ action: { kind: "land_pr", prNumber, reason: "dry-run" },
14019
+ executed: false,
14020
+ exitCode: 0,
14021
+ stdout: `dry-run: node ${args.join(" ")}`,
14022
+ stderr: ""
14023
+ };
14024
+ }
14025
+ const result = spawnSync10("node", args, {
14026
+ cwd: repoRoot,
14027
+ encoding: "utf8",
14028
+ timeout: 10 * 60 * 1e3
14029
+ });
14030
+ return {
14031
+ action: { kind: "land_pr", prNumber, reason: "landing wrapper invoked" },
14032
+ executed: true,
14033
+ exitCode: result.status,
14034
+ stdout: result.stdout ?? "",
14035
+ stderr: result.stderr ?? ""
14036
+ };
14037
+ }
14038
+ function resolveLandingMaintainerRepoRoot(args) {
14039
+ const explicit = args.repoPath ? String(args.repoPath).trim() : "";
14040
+ if (explicit) return path70.resolve(explicit);
14041
+ const resolved = resolveDefaultRepo();
14042
+ return resolved?.repo ?? process.cwd();
14043
+ }
14044
+
14045
+ // src/lane/landing-maintainer-tick.ts
14046
+ async function runLandingMaintainerLaneTick(args) {
14047
+ const agentOsId = String(required(String(args.agentOsId || ""), "--agent-os-id"));
14048
+ const repoSlug = String(args.repo || LANDING_MAINTAINER_LANE_SPEC.defaultRepo).trim();
14049
+ const fleet = args.fleet === true || args.fleet === "true";
14050
+ const execute = args.execute === true || args.execute === "true";
14051
+ const runId = args.run ? String(args.run) : void 0;
14052
+ const resourceGate = observeRunnerResourceGate({
14053
+ runId: runId ?? "fleet-lane-tick"
14054
+ });
14055
+ const boxCapacity = {
14056
+ ...buildBoxResourceSnapshotFromGate(resourceGate, {
14057
+ harnessRunId: runId,
14058
+ boxKind: resolveBoxKindFromConfig(loadUserConfig()),
14059
+ hostLabel: os9.hostname()
14060
+ }),
14061
+ providerHealthy: resourceGate.ok,
14062
+ authorizedForRepair: resourceGate.ok,
14063
+ authorizedForLanding: resourceGate.ok,
14064
+ systemHealthBlockers: resourceGate.ok ? [] : [resourceGate.reason ?? "resource_gate_blocked"],
14065
+ actionableWorkers: resourceGate.activeWorkers
14066
+ };
14067
+ const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
14068
+ const secret = await resolveCallbackSecretWithMint(
14069
+ args.secret ? String(args.secret) : void 0,
14070
+ agentOsId,
14071
+ { baseUrl: base }
14072
+ );
14073
+ const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/fleet-landing-maintainer/tick`;
14074
+ const res = await postJson(url, secret, {
14075
+ repo: repoSlug,
14076
+ fleet,
14077
+ execute,
14078
+ runId,
14079
+ boxCapacity
14080
+ });
14081
+ const coordinator = res.response;
14082
+ const localOutcomes = [];
14083
+ const repoRoot = resolveLandingMaintainerRepoRoot(args);
14084
+ const actions = Array.isArray(coordinator?.localActions) ? coordinator.localActions : [];
14085
+ const holderBoxId = boxCapacity.boxId;
14086
+ for (const action of actions) {
14087
+ if (action.kind === "land_pr" && typeof action.prNumber === "number") {
14088
+ const outcome = runLandingWrapper(action.prNumber, repoRoot, execute);
14089
+ localOutcomes.push({
14090
+ action: outcome.action,
14091
+ executed: outcome.executed,
14092
+ exitCode: outcome.exitCode
14093
+ });
14094
+ if (execute && outcome.executed) {
14095
+ const merged = outcome.exitCode === 0;
14096
+ const prUrl = typeof action.prUrl === "string" && action.prUrl.trim() ? action.prUrl.trim() : `https://github.com/${repoSlug}/pull/${action.prNumber}`;
14097
+ try {
14098
+ await postJson(
14099
+ `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/fleet-landing-maintainer/outcome`,
14100
+ secret,
14101
+ { repo: repoSlug, prUrl, holderBoxId, merged }
14102
+ );
14103
+ } catch {
14104
+ }
14105
+ }
14106
+ continue;
14107
+ }
14108
+ localOutcomes.push({
14109
+ action,
14110
+ executed: false,
14111
+ exitCode: null
14112
+ });
14113
+ }
14114
+ return {
14115
+ repo: repoSlug,
14116
+ fleet,
14117
+ execute,
14118
+ coordinator,
14119
+ localOutcomes
14120
+ };
14121
+ }
14122
+
14123
+ // src/lane/lane-tick-cli.ts
14124
+ async function runLaneTickCli(args, laneFromArgv) {
14125
+ const lane = String(laneFromArgv ?? args.lane ?? "").trim();
14126
+ if (lane !== "landing-maintainer") {
14127
+ console.error(`unknown lane: ${lane || "(none)"}`);
14128
+ process.exit(1);
14129
+ }
14130
+ const result = await runLandingMaintainerLaneTick(args);
14131
+ if (args.json === true || args.json === "true") {
14132
+ console.log(JSON.stringify(result, null, 2));
14133
+ return;
14134
+ }
14135
+ console.log(
14136
+ [
14137
+ `fleet landing-maintainer tick`,
14138
+ `repo=${result.repo}`,
14139
+ `fleet=${result.fleet}`,
14140
+ `execute=${result.execute}`,
14141
+ `localOutcomes=${result.localOutcomes.length}`
14142
+ ].join(" ")
14143
+ );
14144
+ for (const row of result.localOutcomes) {
14145
+ console.log(` ${row.action.kind} pr=${row.action.prNumber ?? "-"} executed=${row.executed}`);
14146
+ }
14147
+ }
14148
+
13726
14149
  // src/cli.ts
13727
14150
  function isHelpFlag(arg) {
13728
14151
  return arg === "help" || arg === "--help" || arg === "-h";
@@ -13743,6 +14166,7 @@ function usage(code = 0) {
13743
14166
  " kynver daemon --run RUN_ID --agent-os-id AOS_ID [--execute] [--interval-ms MS]",
13744
14167
  " kynver run create [--repo /path/repo] [--name name] [--base origin/main]",
13745
14168
  " kynver run list",
14169
+ " kynver run resolve --name RUN_NAME",
13746
14170
  " kynver run status --run RUN_ID [--json] [--compact]",
13747
14171
  " 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
14172
  " kynver run sweep --run RUN_ID --agent-os-id AOS_ID [--base-url URL] [--secret SECRET] [--grace-ms MS]",
@@ -13778,6 +14202,7 @@ function usage(code = 0) {
13778
14202
  " kynver scheduler attest-cutover [--json]",
13779
14203
  " kynver cron status [--json]",
13780
14204
  " kynver cron tick [--agent-os-id AOS_ID] [--json]",
14205
+ " kynver lane tick landing-maintainer [--fleet] [--repo OWNER/NAME] [--agent-os-id AOS_ID] [--execute] [--json]",
13781
14206
  " kynver board contract [--agent-os-id ID] [--base-url URL] [--since ISO] [--limit N]"
13782
14207
  ].join("\n")
13783
14208
  );
@@ -13789,7 +14214,7 @@ async function main(argv = process.argv.slice(2)) {
13789
14214
  const scope = argv.shift();
13790
14215
  let action;
13791
14216
  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") {
14217
+ 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
14218
  action = argv.shift();
13794
14219
  rest = argv;
13795
14220
  } else {
@@ -13846,11 +14271,16 @@ async function main(argv = process.argv.slice(2)) {
13846
14271
  if (scope === "cron" && action === "tick") {
13847
14272
  return void await runCronTickCli(args);
13848
14273
  }
14274
+ if (scope === "lane" && action === "tick") {
14275
+ const laneName = rest.shift();
14276
+ return void await runLaneTickCli(parseArgs(rest), laneName);
14277
+ }
13849
14278
  if (scope === "board" && action === "contract") {
13850
14279
  return void await runCommandCenterContractCli(args);
13851
14280
  }
13852
14281
  if (scope === "run" && action === "create") return createRun(args);
13853
14282
  if (scope === "run" && action === "list") return listRuns();
14283
+ if (scope === "run" && action === "resolve") return resolveHarnessRunCli(args);
13854
14284
  if (scope === "run" && action === "status") return runStatus(args);
13855
14285
  if (scope === "run" && action === "dispatch") return void await dispatchRun(args);
13856
14286
  if (scope === "run" && action === "sweep") return void await sweepRun(args);