@kynver-app/runtime 0.1.95 → 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
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { mkdirSync as mkdirSync8, realpathSync } from "node:fs";
4
+ import { mkdirSync as mkdirSync10, realpathSync } from "node:fs";
5
5
  import { fileURLToPath as fileURLToPath5 } from "node:url";
6
6
 
7
7
  // src/config.ts
8
- import { existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "node:fs";
8
+ import { existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as readFileSync8, writeFileSync as writeFileSync2 } from "node:fs";
9
9
  import { homedir as homedir4, totalmem } from "node:os";
10
10
  import path8 from "node:path";
11
11
 
@@ -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 {
@@ -485,23 +488,23 @@ function isWslHost() {
485
488
  function observeWslHostDisk(options = {}) {
486
489
  const wsl = options.forceWsl === void 0 ? isWslHost() : options.forceWsl;
487
490
  if (!wsl) return null;
488
- const path67 = 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;
489
492
  const warnBelowBytes = options.wslHostFreeWarnBytes ?? DEFAULT_WSL_HOST_WARN_FREE_BYTES;
490
493
  const criticalBelowBytes = options.wslHostFreeCriticalBytes ?? DEFAULT_WSL_HOST_CRITICAL_FREE_BYTES;
491
494
  const statfs = options.statfs ?? statfsSync;
492
495
  let stats;
493
496
  try {
494
- stats = statfs(path67);
497
+ stats = statfs(path71);
495
498
  } catch (error) {
496
499
  return {
497
500
  ok: false,
498
- path: path67,
501
+ path: path71,
499
502
  freeBytes: 0,
500
503
  totalBytes: 0,
501
504
  usedPercent: 100,
502
505
  warnBelowBytes,
503
506
  criticalBelowBytes,
504
- reason: `Windows host disk probe failed at ${path67}: ${error.message}`,
507
+ reason: `Windows host disk probe failed at ${path71}: ${error.message}`,
505
508
  probeError: error.message
506
509
  };
507
510
  }
@@ -515,11 +518,11 @@ function observeWslHostDisk(options = {}) {
515
518
  let reason = null;
516
519
  if (!ok) {
517
520
  const tag = criticalFree ? "critical" : "warning";
518
- reason = `Windows host disk ${path67} 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()}`;
519
522
  }
520
523
  return {
521
524
  ok,
522
- path: path67,
525
+ path: path71,
523
526
  freeBytes,
524
527
  totalBytes,
525
528
  usedPercent,
@@ -539,12 +542,12 @@ var DEFAULT_CRITICAL_FREE_BYTES = 15 * 1024 * 1024 * 1024;
539
542
  var DEFAULT_MAX_USED_PERCENT = 80;
540
543
  var DEFAULT_HARD_MAX_USED_PERCENT = 90;
541
544
  function observeRunnerDiskGate(input = {}) {
542
- const path67 = input.diskPath?.trim() || "/";
545
+ const path71 = input.diskPath?.trim() || "/";
543
546
  const warnBelowBytes = input.diskFreeWarnBytes ?? DEFAULT_WARN_FREE_BYTES;
544
547
  const criticalBelowBytes = input.diskFreeCriticalBytes ?? DEFAULT_CRITICAL_FREE_BYTES;
545
548
  const maxUsedPercent = input.diskMaxUsedPercent ?? DEFAULT_MAX_USED_PERCENT;
546
549
  const hardMaxUsedPercent = input.diskHardMaxUsedPercent ?? DEFAULT_HARD_MAX_USED_PERCENT;
547
- const stats = statfsSync2(path67);
550
+ const stats = statfsSync2(path71);
548
551
  const freeBytes = Number(stats.bavail) * Number(stats.bsize);
549
552
  const totalBytes = Number(stats.blocks) * Number(stats.bsize);
550
553
  const usedPercent = totalBytes > 0 ? (totalBytes - freeBytes) / totalBytes * 100 : 100;
@@ -567,7 +570,7 @@ function observeRunnerDiskGate(input = {}) {
567
570
  }
568
571
  return {
569
572
  ok,
570
- path: path67,
573
+ path: path71,
571
574
  freeBytes,
572
575
  totalBytes,
573
576
  usedPercent,
@@ -701,6 +704,9 @@ function listRunWorkerNames(run) {
701
704
  return [...names];
702
705
  }
703
706
 
707
+ // src/harness-worker-active.ts
708
+ import { readFileSync as readFileSync7 } from "node:fs";
709
+
704
710
  // src/heartbeat.ts
705
711
  import { existsSync as existsSync7, readFileSync as readFileSync5 } from "node:fs";
706
712
 
@@ -1781,6 +1787,9 @@ function computeAttention(input) {
1781
1787
  return { state: "blocked", reason: input.completionBlocker };
1782
1788
  }
1783
1789
  if (input.finalResult) {
1790
+ if (input.localOnly && hasMergedTargetPrReconciliation(input.finalResult)) {
1791
+ return { state: "done", reason: "local-only worker superseded by merged PR" };
1792
+ }
1784
1793
  const landingSnapshot = {
1785
1794
  finalResult: input.finalResult,
1786
1795
  changedFiles: input.changedFiles ?? [],
@@ -1846,9 +1855,24 @@ function computeAttention(input) {
1846
1855
  }
1847
1856
  return { state: "ok", reason: "recent activity" };
1848
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
+ }
1849
1870
  function resolveFinalResult(worker, parsedFinalResult, heartbeat) {
1850
- if (parsedFinalResult) return parsedFinalResult;
1851
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;
1852
1876
  if (ackSnapshot !== void 0 && ackSnapshot !== null) return ackSnapshot;
1853
1877
  return terminalFinalResultFromHeartbeat(heartbeat);
1854
1878
  }
@@ -1895,7 +1919,8 @@ function computeWorkerStatus(worker, options = {}) {
1895
1919
  gitAncestry,
1896
1920
  completionBlocker,
1897
1921
  landingContract,
1898
- prUrl: worker.repairTargetPrUrl ?? worker.taskPrUrl ?? null
1922
+ prUrl: worker.repairTargetPrUrl ?? worker.taskPrUrl ?? null,
1923
+ localOnly: worker.localOnly === true
1899
1924
  });
1900
1925
  const workerStatusLabel = completionBlocker || attention.state === "blocked" ? "blocked" : completionAcknowledged || attention.state === "done" ? "done" : finalResult ? "exited" : alive ? "running" : "exited";
1901
1926
  return {
@@ -1949,6 +1974,33 @@ function deriveRunStatus(fallback, workers) {
1949
1974
  return fallback;
1950
1975
  }
1951
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
+
1952
2004
  // src/resource-gate.ts
1953
2005
  var DEFAULT_PER_WORKER_MEM_BYTES = 500 * 1024 * 1024;
1954
2006
  var DEFAULT_MEM_RESERVE_BYTES = 4 * 1024 * 1024 * 1024;
@@ -1991,13 +2043,6 @@ function computeAutoMaxWorkers(totalMemBytes, opts = {}) {
1991
2043
  function readAvailableMemBytes() {
1992
2044
  return readMemAvailableBytes();
1993
2045
  }
1994
- function isActiveHarnessWorker(worker) {
1995
- if (typeof worker.completionBlocker === "string" && worker.completionBlocker.trim()) {
1996
- return false;
1997
- }
1998
- const status = computeWorkerStatus(worker);
1999
- return status.alive && !status.finalResult && status.attention.state !== "done";
2000
- }
2001
2046
  function countActiveWorkersForRun(run) {
2002
2047
  let active = 0;
2003
2048
  for (const name of listRunWorkerNames(run)) {
@@ -2146,7 +2191,7 @@ var CREDENTIALS_FILE = path8.join(CONFIG_DIR, "credentials");
2146
2191
  function loadUserConfig() {
2147
2192
  if (!existsSync9(CONFIG_FILE)) return {};
2148
2193
  try {
2149
- return JSON.parse(readFileSync7(CONFIG_FILE, "utf8"));
2194
+ return JSON.parse(readFileSync8(CONFIG_FILE, "utf8"));
2150
2195
  } catch {
2151
2196
  return {};
2152
2197
  }
@@ -2223,7 +2268,7 @@ function resolveSetupWorkerConfig(existing, args, totalMemBytes = totalmem()) {
2223
2268
  function loadCredentialsFile() {
2224
2269
  if (!existsSync9(CREDENTIALS_FILE)) return {};
2225
2270
  try {
2226
- return JSON.parse(readFileSync7(CREDENTIALS_FILE, "utf8"));
2271
+ return JSON.parse(readFileSync8(CREDENTIALS_FILE, "utf8"));
2227
2272
  } catch {
2228
2273
  return {};
2229
2274
  }
@@ -2493,15 +2538,30 @@ async function withTimeout(fn) {
2493
2538
  clearTimeout(timeout);
2494
2539
  }
2495
2540
  }
2541
+ function callbackFetchError(error) {
2542
+ return {
2543
+ ok: false,
2544
+ status: 0,
2545
+ response: {
2546
+ error: error instanceof Error ? error.message : String(error),
2547
+ timeoutMs: callbackTimeoutMs()
2548
+ }
2549
+ };
2550
+ }
2496
2551
  async function postJson(url, secret, body) {
2497
- const res = await withTimeout(
2498
- (signal) => fetch(url, {
2499
- method: "POST",
2500
- headers: buildHarnessCallbackHeaders(secret),
2501
- body: JSON.stringify(body),
2502
- signal
2503
- })
2504
- );
2552
+ let res;
2553
+ try {
2554
+ res = await withTimeout(
2555
+ (signal) => fetch(url, {
2556
+ method: "POST",
2557
+ headers: buildHarnessCallbackHeaders(secret),
2558
+ body: JSON.stringify(body),
2559
+ signal
2560
+ })
2561
+ );
2562
+ } catch (error) {
2563
+ return callbackFetchError(error);
2564
+ }
2505
2565
  let response = null;
2506
2566
  try {
2507
2567
  response = await res.json();
@@ -2519,13 +2579,18 @@ async function postJsonWithCredentialRefresh(url, secret, body, opts) {
2519
2579
  return { ...retry, refreshedAuth: true };
2520
2580
  }
2521
2581
  async function getJson(url, secret) {
2522
- const res = await withTimeout(
2523
- (signal) => fetch(url, {
2524
- method: "GET",
2525
- headers: buildHarnessCallbackHeaders(secret),
2526
- signal
2527
- })
2528
- );
2582
+ let res;
2583
+ try {
2584
+ res = await withTimeout(
2585
+ (signal) => fetch(url, {
2586
+ method: "GET",
2587
+ headers: buildHarnessCallbackHeaders(secret),
2588
+ signal
2589
+ })
2590
+ );
2591
+ } catch (error) {
2592
+ return callbackFetchError(error);
2593
+ }
2529
2594
  let response = null;
2530
2595
  try {
2531
2596
  response = await res.json();
@@ -2535,41 +2600,6 @@ async function getJson(url, secret) {
2535
2600
  return { ok: res.ok, status: res.status, response };
2536
2601
  }
2537
2602
 
2538
- // src/dispatch-lane-normalization.ts
2539
- function trimLower(value) {
2540
- return (value ?? "").trim().toLowerCase();
2541
- }
2542
- function roleLaneToDispatchLane(roleLane) {
2543
- switch (trimLower(roleLane)) {
2544
- case "implementer":
2545
- case "repair_implementer":
2546
- case "plan_author":
2547
- case "runtime_verifier":
2548
- return "implementation";
2549
- case "plan_reviewer":
2550
- case "report_reviewer":
2551
- case "deep_reviewer":
2552
- return "review";
2553
- default:
2554
- return null;
2555
- }
2556
- }
2557
- function normalizeDispatchNextLaneFilter(raw) {
2558
- const key = trimLower(raw);
2559
- if (!key) return void 0;
2560
- if (key === "implementation" || key === "review" || key === "landing" || key === "any") {
2561
- return key;
2562
- }
2563
- const mapped = roleLaneToDispatchLane(key);
2564
- if (mapped) return mapped;
2565
- if (key === "implement" || key === "repair" || key === "coding") return "implementation";
2566
- if (key === "land" || key === "merge") return "landing";
2567
- return void 0;
2568
- }
2569
- function resolveDispatchNextLaneFilter(raw) {
2570
- return normalizeDispatchNextLaneFilter(raw) ?? "any";
2571
- }
2572
-
2573
2603
  // src/worker-persona-catalog.ts
2574
2604
  var WORKER_PERSONA_CATALOG = [
2575
2605
  {
@@ -2672,6 +2702,41 @@ function workerPersonaReviewSlugs() {
2672
2702
  );
2673
2703
  }
2674
2704
 
2705
+ // src/dispatch-lane-normalization.ts
2706
+ function trimLower(value) {
2707
+ return (value ?? "").trim().toLowerCase();
2708
+ }
2709
+ function roleLaneToDispatchLane(roleLane) {
2710
+ switch (trimLower(roleLane)) {
2711
+ case "implementer":
2712
+ case "repair_implementer":
2713
+ case "plan_author":
2714
+ case "runtime_verifier":
2715
+ return "implementation";
2716
+ case "plan_reviewer":
2717
+ case "report_reviewer":
2718
+ case "deep_reviewer":
2719
+ return "review";
2720
+ default:
2721
+ return null;
2722
+ }
2723
+ }
2724
+ function normalizeDispatchNextLaneFilter(raw) {
2725
+ const key = trimLower(raw);
2726
+ if (!key) return void 0;
2727
+ if (key === "implementation" || key === "review" || key === "landing" || key === "any") {
2728
+ return key;
2729
+ }
2730
+ const mapped = roleLaneToDispatchLane(key);
2731
+ if (mapped) return mapped;
2732
+ if (key === "implement" || key === "repair" || key === "coding") return "implementation";
2733
+ if (key === "land" || key === "merge") return "landing";
2734
+ return void 0;
2735
+ }
2736
+ function resolveDispatchNextLaneFilter(raw) {
2737
+ return normalizeDispatchNextLaneFilter(raw) ?? "any";
2738
+ }
2739
+
2675
2740
  // src/model-routing-task-enrich.ts
2676
2741
  function taskString(task, key) {
2677
2742
  const v = task[key];
@@ -3129,7 +3194,7 @@ function probeCursorOAuthBinding(nowIso = (/* @__PURE__ */ new Date()).toISOStri
3129
3194
  }
3130
3195
 
3131
3196
  // src/orchestration-providers/hermes-cli-adapter.ts
3132
- import { existsSync as existsSync14, readFileSync as readFileSync8 } from "node:fs";
3197
+ import { existsSync as existsSync14, readFileSync as readFileSync9 } from "node:fs";
3133
3198
  import { homedir as homedir9 } from "node:os";
3134
3199
  import path13 from "node:path";
3135
3200
  var PROFILE_ENV_KEYS = [
@@ -3145,7 +3210,7 @@ function hermesProfileEnvPath() {
3145
3210
  }
3146
3211
  function profileEnvKeyPresence(envPath) {
3147
3212
  try {
3148
- const text = readFileSync8(envPath, "utf8");
3213
+ const text = readFileSync9(envPath, "utf8");
3149
3214
  const present = [];
3150
3215
  for (const key of PROFILE_ENV_KEYS) {
3151
3216
  const re = new RegExp(`^${key}=`, "m");
@@ -4106,7 +4171,7 @@ function buildPrompt(input) {
4106
4171
  "Completion handoff (required): before you stop, ensure the harness records a final result \u2014 summarize outcome in your last message and append a heartbeat line with phase `complete`. If you leave uncommitted changes or committed work without a PR, the orchestrator blocks completion until a GitHub PR exists (or you discard/commit cleanly). One-off helper scripts must be removed (`kynver worker discard-disposable --path <file>`) or committed before completion \u2014 maintenance/board-drain workers are not exempt. Exiting with only dirty files and no PR routes to salvage review, not production review.",
4107
4172
  "PR-ready handoff: for substantial implementation work, commit, push, and open a GitHub PR (draft OK) on your branch before finishing \u2014 or rely on the harness to run `gh pr create` at completion when `gh` is authenticated.",
4108
4173
  "Expert review / production-review workers (Dalton/Lorentz, plan-review-task, scheduledJob reviewer children): do NOT open new implementation PRs \u2014 review the parent task's existing PR and record reviewVerdict in finalResult; landing-contract targetPrReconciliation does not apply.",
4109
- "Worker resource guard: do not run full monorepo verification (`npm run typecheck`, `npm run build`, or equivalent) from this worker lane unless an operator explicitly requests it. Use targeted checks for touched paths and rely on CI/operator lanes for heavy gates.",
4174
+ "Worker resource guard: do not run full monorepo verification (`npm run typecheck`, `npm run build`, or equivalent) from this worker lane unless an operator explicitly requests it. Use targeted checks for touched paths and rely on CI/operator lanes for heavy gates. When heavy verification is required, route through `node scripts/verify-pr-local.mjs` or `kynver harness verify` \u2014 they acquire the global heavy-verification lease so parallel workers do not launch simultaneous typechecks.",
4110
4175
  "npm publish boundary: do not run `npm publish`, do not republish `@kynver-app/*` packages, and do not block on an operator to publish. When you need newer runtime code than npm, use this repo checkout (`npm run kynver:build`, `npm run kynver`) and record evidence: packages/kynver-runtime/package.json version + git ref in your completion report.",
4111
4176
  "If verification fails (including OOM), append a heartbeat line immediately with the last command, failure reason, dirty-file status, commit/PR handoff state, and next action so recovery does not require log spelunking.",
4112
4177
  "Landing-wrapper cleanup on a git ref: use `node scripts/agent-os-land-pr-cleanup-verify.mjs --ref origin/main` (JSON, exit 0). Do not use `git show <ref>:scripts/agent-os-land-pr.mjs | rg \u2026` \u2014 ripgrep exit 1 when markers are absent is reported as a failed command in Telegram/status tooling.",
@@ -4982,8 +5047,8 @@ function dirtyPathsCoveredByDisposableRemoval(changedFiles, removed) {
4982
5047
  if (removed.length === 0) return false;
4983
5048
  const removedSet = new Set(removed.map((p) => normalizeRelativePath(p)));
4984
5049
  return material.every((line) => {
4985
- const path67 = normalizeRelativePath(pathFromGitStatusLine(line));
4986
- return removedSet.has(path67);
5050
+ const path71 = normalizeRelativePath(pathFromGitStatusLine(line));
5051
+ return removedSet.has(path71);
4987
5052
  });
4988
5053
  }
4989
5054
 
@@ -6277,7 +6342,7 @@ function collectRunActiveHarnessWorkers(runId) {
6277
6342
  path22.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
6278
6343
  void 0
6279
6344
  );
6280
- if (!worker?.taskId || !isPidAlive(worker.pid)) continue;
6345
+ if (!worker?.taskId || !isActiveHarnessWorker(worker)) continue;
6281
6346
  out.push({
6282
6347
  runId: run.id,
6283
6348
  workerName: name,
@@ -6516,7 +6581,7 @@ function isTmpOnlyPath(filePath) {
6516
6581
  // src/plan-persist/outbox-store.ts
6517
6582
  import {
6518
6583
  existsSync as existsSync21,
6519
- readFileSync as readFileSync9,
6584
+ readFileSync as readFileSync10,
6520
6585
  renameSync,
6521
6586
  readdirSync as readdirSync5,
6522
6587
  writeFileSync as writeFileSync3,
@@ -6544,7 +6609,7 @@ function findOutboxByIdempotencyKey(key) {
6544
6609
  function readOutboxItem(jsonPath) {
6545
6610
  if (!existsSync21(jsonPath)) return null;
6546
6611
  try {
6547
- return JSON.parse(readFileSync9(jsonPath, "utf8"));
6612
+ return JSON.parse(readFileSync10(jsonPath, "utf8"));
6548
6613
  } catch {
6549
6614
  return null;
6550
6615
  }
@@ -6552,7 +6617,7 @@ function readOutboxItem(jsonPath) {
6552
6617
  function readOutboxBody(item) {
6553
6618
  const { outboxDir } = ensurePlanOutboxDirs();
6554
6619
  const bodyFile = path24.join(outboxDir, item.bodyPath);
6555
- return readFileSync9(bodyFile, "utf8");
6620
+ return readFileSync10(bodyFile, "utf8");
6556
6621
  }
6557
6622
  function writeOutboxItem(input, opts) {
6558
6623
  const { outboxDir } = ensurePlanOutboxDirs();
@@ -7138,7 +7203,7 @@ async function dispatchRun(args) {
7138
7203
  );
7139
7204
  }
7140
7205
  const attempt = Number(task.attempt) || 1;
7141
- if (attempt >= retryLimits.maxTaskAttempts) {
7206
+ if (attempt > retryLimits.maxTaskAttempts) {
7142
7207
  return abortClaimedSpawn(
7143
7208
  task,
7144
7209
  `task attempt ${attempt} exceeds KYNVER_MAX_TASK_ATTEMPTS (${retryLimits.maxTaskAttempts})`
@@ -7274,7 +7339,12 @@ async function dispatchRun(args) {
7274
7339
  const admissionExhaustion = readAdmissionExhaustion(result);
7275
7340
  const capacityIdle = admissionExhaustion?.capacityIdle === true || startedCount === 0 && Number(result.resourceGate?.slotsAvailable) > 0;
7276
7341
  if (capacityIdle && admissionExhaustion?.summary) {
7277
- 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
+ );
7278
7348
  }
7279
7349
  const summary = {
7280
7350
  runId: run.id,
@@ -7370,14 +7440,14 @@ async function sweepRun(args) {
7370
7440
 
7371
7441
  // src/worktree.ts
7372
7442
  import { existsSync as existsSync25, mkdirSync as mkdirSync6 } from "node:fs";
7373
- import path34 from "node:path";
7443
+ import path35 from "node:path";
7374
7444
 
7375
7445
  // src/run-list.ts
7376
- import { existsSync as existsSync24, readFileSync as readFileSync10 } from "node:fs";
7377
- import path33 from "node:path";
7446
+ import { existsSync as existsSync24, readFileSync as readFileSync11 } from "node:fs";
7447
+ import path34 from "node:path";
7378
7448
 
7379
7449
  // src/stale-reconcile.ts
7380
- import path32 from "node:path";
7450
+ import path33 from "node:path";
7381
7451
 
7382
7452
  // src/finalize.ts
7383
7453
  import path27 from "node:path";
@@ -7960,6 +8030,193 @@ function reconcileWorkerMetadata() {
7960
8030
  return { workers: outcomes, runMetadataRetention };
7961
8031
  }
7962
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
+
7963
8220
  // src/stale-reconcile.ts
7964
8221
  var STALE_RECONCILE_HEARTBEAT_MS = 15 * 60 * 1e3;
7965
8222
  function staleReconcileDisabled() {
@@ -7968,13 +8225,14 @@ function staleReconcileDisabled() {
7968
8225
  function reconcileStaleWorkers() {
7969
8226
  const metadataReconcile = reconcileWorkerMetadata();
7970
8227
  if (staleReconcileDisabled()) {
7971
- return { workers: [], finalizedRuns: finalizeStaleRuns(), metadataReconcile };
8228
+ const localPrAttentionReconcile2 = reconcileLocalOnlyMergedPrAttention();
8229
+ return { workers: [], finalizedRuns: finalizeStaleRuns(), metadataReconcile, localPrAttentionReconcile: localPrAttentionReconcile2 };
7972
8230
  }
7973
8231
  const outcomes = [];
7974
8232
  const now = Date.now();
7975
8233
  for (const run of listRunRecords()) {
7976
8234
  for (const name of listRunWorkerNames(run)) {
7977
- 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");
7978
8236
  const worker = readJson(workerPath, void 0);
7979
8237
  if (!worker || worker.status !== "running") {
7980
8238
  outcomes.push({
@@ -8046,7 +8304,8 @@ function reconcileStaleWorkers() {
8046
8304
  });
8047
8305
  }
8048
8306
  }
8049
- return { workers: outcomes, finalizedRuns: finalizeStaleRuns(), metadataReconcile };
8307
+ const localPrAttentionReconcile = reconcileLocalOnlyMergedPrAttention();
8308
+ return { workers: outcomes, finalizedRuns: finalizeStaleRuns(), metadataReconcile, localPrAttentionReconcile };
8050
8309
  }
8051
8310
  function reconcileRunsCli() {
8052
8311
  const result = reconcileStaleWorkers();
@@ -8057,6 +8316,10 @@ function reconcileRunsCli() {
8057
8316
  acc[row.action] = (acc[row.action] ?? 0) + 1;
8058
8317
  return acc;
8059
8318
  }, {});
8319
+ const localPrAttentionTotals = result.localPrAttentionReconcile.workers.reduce((acc, row) => {
8320
+ acc[row.action] = (acc[row.action] ?? 0) + 1;
8321
+ return acc;
8322
+ }, {});
8060
8323
  const runRetentionTotals = result.metadataReconcile.runMetadataRetention.runs.reduce((acc, row) => {
8061
8324
  acc[row.action] = (acc[row.action] ?? 0) + 1;
8062
8325
  return acc;
@@ -8074,10 +8337,15 @@ function reconcileRunsCli() {
8074
8337
  total: result.metadataReconcile.runMetadataRetention.runs.length
8075
8338
  }
8076
8339
  },
8340
+ localPrAttentionReconcile: {
8341
+ totals: localPrAttentionTotals,
8342
+ total: result.localPrAttentionReconcile.workers.length
8343
+ },
8077
8344
  finalizedRuns: result.finalizedRuns.length,
8078
8345
  details: {
8079
8346
  workers: result.workers,
8080
8347
  metadataReconcile: result.metadataReconcile.workers,
8348
+ localPrAttentionReconcile: result.localPrAttentionReconcile.workers,
8081
8349
  runMetadataRetention: result.metadataReconcile.runMetadataRetention.runs,
8082
8350
  finalizedRuns: result.finalizedRuns
8083
8351
  }
@@ -8092,13 +8360,13 @@ function reconcileRunsCli() {
8092
8360
  function heartbeatByteLength(heartbeatPath) {
8093
8361
  if (!heartbeatPath || !existsSync24(heartbeatPath)) return 0;
8094
8362
  try {
8095
- return readFileSync10(heartbeatPath, "utf8").trim().length;
8363
+ return readFileSync11(heartbeatPath, "utf8").trim().length;
8096
8364
  } catch {
8097
8365
  return 0;
8098
8366
  }
8099
8367
  }
8100
8368
  function workerEvidence(run, workerName) {
8101
- 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");
8102
8370
  const worker = readJson(workerPath, void 0);
8103
8371
  if (!worker) {
8104
8372
  return {
@@ -8155,7 +8423,7 @@ function aggregateRunAttention(workers) {
8155
8423
  function countOpenWorkers(run) {
8156
8424
  let open = 0;
8157
8425
  for (const name of listRunWorkerNames(run)) {
8158
- 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");
8159
8427
  const worker = readJson(workerPath, void 0);
8160
8428
  if (!worker) continue;
8161
8429
  const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
@@ -8231,7 +8499,7 @@ function createRun(args) {
8231
8499
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
8232
8500
  workers: {}
8233
8501
  };
8234
- writeJson(path34.join(dir, "run.json"), run);
8502
+ writeJson(path35.join(dir, "run.json"), run);
8235
8503
  console.log(JSON.stringify({ runId: id, runDir: dir, repo, base, baseCommit }, null, 2));
8236
8504
  }
8237
8505
  function listRuns() {
@@ -8244,7 +8512,7 @@ function failExists(message) {
8244
8512
 
8245
8513
  // src/discard-disposable.ts
8246
8514
  import { existsSync as existsSync26, rmSync as rmSync2 } from "node:fs";
8247
- import path35 from "node:path";
8515
+ import path36 from "node:path";
8248
8516
  function normalizeRelativePath2(value) {
8249
8517
  const normalized = value.replace(/\\/g, "/").replace(/^\.\//, "").trim();
8250
8518
  if (!normalized || normalized.startsWith("/") || normalized.includes("..")) {
@@ -8266,12 +8534,12 @@ function discardDisposableArtifacts(args) {
8266
8534
  if (paths.length === 0) {
8267
8535
  return { ok: false, removed: [], reason: "requires at least one --path" };
8268
8536
  }
8269
- const worktreeRoot = path35.resolve(worker.worktreePath);
8537
+ const worktreeRoot = path36.resolve(worker.worktreePath);
8270
8538
  const removed = [];
8271
8539
  for (const raw of paths) {
8272
8540
  const rel = normalizeRelativePath2(raw);
8273
- const abs = path35.resolve(worktreeRoot, rel);
8274
- if (!abs.startsWith(worktreeRoot + path35.sep) && abs !== worktreeRoot) {
8541
+ const abs = path36.resolve(worktreeRoot, rel);
8542
+ if (!abs.startsWith(worktreeRoot + path36.sep) && abs !== worktreeRoot) {
8275
8543
  return { ok: false, removed, reason: `path escapes worktree: ${raw}` };
8276
8544
  }
8277
8545
  if (!existsSync26(abs)) {
@@ -8338,7 +8606,7 @@ function validateDaemonInstallIdentity(config = loadUserConfig(), env = process.
8338
8606
  // src/cron/cron-env.ts
8339
8607
  import { existsSync as existsSync27 } from "node:fs";
8340
8608
  import { homedir as homedir11 } from "node:os";
8341
- import path36 from "node:path";
8609
+ import path37 from "node:path";
8342
8610
  function envFlag(name, defaultValue) {
8343
8611
  const raw = process.env[name]?.trim().toLowerCase();
8344
8612
  if (!raw) return defaultValue;
@@ -8354,7 +8622,7 @@ function envInt(name, fallback, min = 1) {
8354
8622
  function defaultKynverCronStorePath() {
8355
8623
  const explicit = process.env.KYNVER_CRON_STORE_PATH?.trim() || process.env.OPENCLAW_CRON_STORE_PATH?.trim();
8356
8624
  if (explicit) return explicit;
8357
- return path36.join(homedir11(), ".kynver", "agent-os-cron.json");
8625
+ return path37.join(homedir11(), ".kynver", "agent-os-cron.json");
8358
8626
  }
8359
8627
  function defaultKynverCronStatePath(storePath = defaultKynverCronStorePath()) {
8360
8628
  const explicit = process.env.KYNVER_CRON_TICK_STATE_PATH?.trim();
@@ -8427,12 +8695,12 @@ async function fireKynverCronJob(input) {
8427
8695
  }
8428
8696
 
8429
8697
  // src/cron/cron-lock.ts
8430
- import { closeSync as closeSync6, existsSync as existsSync28, openSync as openSync6, readFileSync as readFileSync11, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "node:fs";
8698
+ import { closeSync as closeSync6, existsSync as existsSync28, openSync as openSync6, readFileSync as readFileSync12, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "node:fs";
8431
8699
  var STALE_LOCK_MS = 10 * 6e4;
8432
8700
  function readLockInfo(lockPath) {
8433
8701
  if (!existsSync28(lockPath)) return null;
8434
8702
  try {
8435
- const parsed = JSON.parse(readFileSync11(lockPath, "utf8"));
8703
+ const parsed = JSON.parse(readFileSync12(lockPath, "utf8"));
8436
8704
  if (typeof parsed.pid === "number" && typeof parsed.at === "string") return parsed;
8437
8705
  } catch {
8438
8706
  return null;
@@ -8596,7 +8864,7 @@ async function loadCronJobs(storePath = defaultKynverCronStorePath()) {
8596
8864
  // src/cron/cron-tick-state.ts
8597
8865
  import { randomBytes } from "node:crypto";
8598
8866
  import { promises as fs2 } from "node:fs";
8599
- import path37 from "node:path";
8867
+ import path38 from "node:path";
8600
8868
  var EMPTY = { version: 1, jobs: {} };
8601
8869
  async function readFileIfExists2(filePath) {
8602
8870
  try {
@@ -8623,7 +8891,7 @@ async function loadCronTickState(statePath) {
8623
8891
  return parseCronTickState(raw);
8624
8892
  }
8625
8893
  async function writeStateAtomic(statePath, state) {
8626
- await fs2.mkdir(path37.dirname(statePath), { recursive: true });
8894
+ await fs2.mkdir(path38.dirname(statePath), { recursive: true });
8627
8895
  const suffix = randomBytes(6).toString("hex");
8628
8896
  const tmp = `${statePath}.tmp-${process.pid}-${Date.now()}-${suffix}`;
8629
8897
  await fs2.writeFile(tmp, `${JSON.stringify(state, null, 2)}
@@ -8800,7 +9068,7 @@ async function runKynverCronTick(opts = {}) {
8800
9068
  }
8801
9069
 
8802
9070
  // src/pipeline-tick.ts
8803
- import path55 from "node:path";
9071
+ import path56 from "node:path";
8804
9072
 
8805
9073
  // src/pipeline-dispatch.ts
8806
9074
  var RESERVED_REVIEW_STARTS = 1;
@@ -8956,7 +9224,7 @@ function buildBoxResourceSnapshotFromGate(gate, input = {}) {
8956
9224
  }
8957
9225
 
8958
9226
  // src/plan-progress-daemon-sync.ts
8959
- import path38 from "node:path";
9227
+ import path39 from "node:path";
8960
9228
 
8961
9229
  // src/plan-progress-sync.ts
8962
9230
  async function syncPlanProgress(args) {
@@ -8980,7 +9248,7 @@ async function syncActiveWorkerPlanProgress(runId, args) {
8980
9248
  const outcomes = [];
8981
9249
  for (const name of Object.keys(run.workers || {})) {
8982
9250
  const worker = readJson(
8983
- path38.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
9251
+ path39.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
8984
9252
  void 0
8985
9253
  );
8986
9254
  if (!worker?.dispatched || !worker.taskId) continue;
@@ -9037,10 +9305,10 @@ async function fetchWorkspaceRuntimePreferences(agentOsId, args) {
9037
9305
  }
9038
9306
 
9039
9307
  // src/cleanup.ts
9040
- import path52 from "node:path";
9308
+ import path53 from "node:path";
9041
9309
 
9042
9310
  // src/cleanup-guards.ts
9043
- import path39 from "node:path";
9311
+ import path40 from "node:path";
9044
9312
 
9045
9313
  // src/cleanup-build-cache-paths.ts
9046
9314
  var HARNESS_BUILD_CACHE_RELATIVE_PATHS = [
@@ -9181,7 +9449,7 @@ function skipWorktreeRemoval(input) {
9181
9449
  function skipDependencyCacheRemoval(input) {
9182
9450
  const { indexed, nodeModulesAgeMs, ageMs, worktreePath, activeWorktreePaths, diskPressure } = input;
9183
9451
  if (!diskPressure && ageMs < nodeModulesAgeMs) return "below_age_threshold";
9184
- if (activeWorktreePaths.has(path39.resolve(worktreePath))) return "active_worker";
9452
+ if (activeWorktreePaths.has(path40.resolve(worktreePath))) return "active_worker";
9185
9453
  if (indexed && isWorkerProcessLive(indexed)) return "active_worker";
9186
9454
  if (indexed && indexedWorktreeHasMaterialChanges(indexed)) return "dirty_worktree";
9187
9455
  return null;
@@ -9208,11 +9476,11 @@ var LIVE_SKIP_REASONS = /* @__PURE__ */ new Set([
9208
9476
  function collectPreservedLivePaths(actions, skips) {
9209
9477
  const out = [];
9210
9478
  const seen = /* @__PURE__ */ new Set();
9211
- const push = (path67, reason, detail) => {
9212
- const key = `${path67}\0${reason}`;
9479
+ const push = (path71, reason, detail) => {
9480
+ const key = `${path71}\0${reason}`;
9213
9481
  if (seen.has(key) || out.length >= MAX_PRESERVED_LIVE_PATH_SAMPLES) return;
9214
9482
  seen.add(key);
9215
- out.push({ path: path67, reason, ...detail ? { detail } : {} });
9483
+ out.push({ path: path71, reason, ...detail ? { detail } : {} });
9216
9484
  };
9217
9485
  for (const skip2 of skips) {
9218
9486
  if (!LIVE_SKIP_REASONS.has(skip2.reason)) continue;
@@ -9228,11 +9496,11 @@ function collectPreservedLivePaths(actions, skips) {
9228
9496
 
9229
9497
  // src/cleanup-run-directory.ts
9230
9498
  import { existsSync as existsSync30, readdirSync as readdirSync9, statSync as statSync7 } from "node:fs";
9231
- import path41 from "node:path";
9499
+ import path42 from "node:path";
9232
9500
 
9233
9501
  // src/cleanup-active-worktrees.ts
9234
9502
  import { existsSync as existsSync29, readdirSync as readdirSync8, statSync as statSync6 } from "node:fs";
9235
- import path40 from "node:path";
9503
+ import path41 from "node:path";
9236
9504
  function workerHasRecentHarnessActivity(worker, now) {
9237
9505
  const paths = [worker.heartbeatPath, worker.stdoutPath, worker.stderrPath];
9238
9506
  for (const target of paths) {
@@ -9258,11 +9526,11 @@ function collectActiveWorktreeGuards(harnessRoots, now = Date.now()) {
9258
9526
  let runHasLive = false;
9259
9527
  for (const name of Object.keys(run.workers || {})) {
9260
9528
  const worker = readJson(
9261
- path40.join(runDirectoryAt(harnessRoot, run.id), "workers", safeSlug(name), "worker.json"),
9529
+ path41.join(runDirectoryAt(harnessRoot, run.id), "workers", safeSlug(name), "worker.json"),
9262
9530
  void 0
9263
9531
  );
9264
9532
  if (!worker?.worktreePath) continue;
9265
- const worktreePath = path40.resolve(worker.worktreePath);
9533
+ const worktreePath = path41.resolve(worker.worktreePath);
9266
9534
  if (!isActiveHarnessWorker2(worker, now)) continue;
9267
9535
  runHasLive = true;
9268
9536
  activeWorktreePaths.add(worktreePath);
@@ -9290,7 +9558,7 @@ function pathAgeMs(target, now) {
9290
9558
  }
9291
9559
  }
9292
9560
  function loadRunStatus(harnessRoot, runId) {
9293
- const runPath = path41.join(harnessRoot, "runs", runId, "run.json");
9561
+ const runPath = path42.join(harnessRoot, "runs", runId, "run.json");
9294
9562
  if (!existsSync30(runPath)) return null;
9295
9563
  return readJson(runPath, null);
9296
9564
  }
@@ -9326,7 +9594,7 @@ function scanStaleRunDirectoryCandidates(opts) {
9326
9594
  if (!runEntry.isDirectory()) continue;
9327
9595
  const runId = runEntry.name;
9328
9596
  if (opts.runIdFilter && runId !== opts.runIdFilter) continue;
9329
- const runPath = path41.join(opts.worktreesDir, runId);
9597
+ const runPath = path42.join(opts.worktreesDir, runId);
9330
9598
  if (!runDirectoryIsEmpty(runPath)) continue;
9331
9599
  candidates.push({
9332
9600
  kind: "remove_run_directory",
@@ -9344,14 +9612,14 @@ function scanStaleRunDirectoryCandidates(opts) {
9344
9612
  import { existsSync as existsSync33, rmSync as rmSync4 } from "node:fs";
9345
9613
 
9346
9614
  // src/cleanup-dir-size.ts
9347
- import { execFileSync } from "node:child_process";
9615
+ import { execFileSync as execFileSync2 } from "node:child_process";
9348
9616
  import { existsSync as existsSync31, readdirSync as readdirSync10, statSync as statSync8 } from "node:fs";
9349
- import path42 from "node:path";
9617
+ import path43 from "node:path";
9350
9618
  var DEFAULT_DU_TIMEOUT_MS = 2500;
9351
9619
  function directorySizeBytesDu(root, timeoutMs = DEFAULT_DU_TIMEOUT_MS) {
9352
9620
  if (!existsSync31(root)) return 0;
9353
9621
  try {
9354
- const out = execFileSync("du", ["-sb", root], {
9622
+ const out = execFileSync2("du", ["-sb", root], {
9355
9623
  encoding: "utf8",
9356
9624
  timeout: timeoutMs,
9357
9625
  stdio: ["ignore", "pipe", "ignore"]
@@ -9380,7 +9648,7 @@ function directorySizeBytes(root, maxEntries = 5e4) {
9380
9648
  }
9381
9649
  for (const name of entries) {
9382
9650
  if (seen++ > maxEntries) return null;
9383
- const full = path42.join(current, name);
9651
+ const full = path43.join(current, name);
9384
9652
  let st;
9385
9653
  try {
9386
9654
  st = statSync8(full);
@@ -9431,20 +9699,20 @@ function pathHasForeignOwnedEntry(targetPath, maxEntries = 32) {
9431
9699
 
9432
9700
  // src/cleanup-privileged-remove.ts
9433
9701
  import { spawnSync as spawnSync4 } from "node:child_process";
9434
- import path44 from "node:path";
9702
+ import path45 from "node:path";
9435
9703
 
9436
9704
  // src/cleanup-harness-path-validate.ts
9437
- import path43 from "node:path";
9705
+ import path44 from "node:path";
9438
9706
  function isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, cacheDirName) {
9439
- const resolved = path43.resolve(targetPath);
9440
- const suffix = `${path43.sep}${cacheDirName}`;
9707
+ const resolved = path44.resolve(targetPath);
9708
+ const suffix = `${path44.sep}${cacheDirName}`;
9441
9709
  const cachePath = resolved.endsWith(suffix) ? resolved : null;
9442
9710
  if (!cachePath) return "path_outside_harness";
9443
- const rel = path43.relative(worktreesDir, cachePath);
9444
- if (rel.startsWith("..") || path43.isAbsolute(rel)) return "path_outside_harness";
9445
- 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);
9446
9714
  if (parts.length < 3 || parts[parts.length - 1] !== cacheDirName) return "path_outside_harness";
9447
- if (!resolved.startsWith(path43.resolve(harnessRoot))) return "path_outside_harness";
9715
+ if (!resolved.startsWith(path44.resolve(harnessRoot))) return "path_outside_harness";
9448
9716
  return null;
9449
9717
  }
9450
9718
  function isHarnessNodeModulesPath(targetPath, harnessRoot, worktreesDir) {
@@ -9454,16 +9722,16 @@ function isHarnessNextCachePath(targetPath, harnessRoot, worktreesDir) {
9454
9722
  return isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, ".next");
9455
9723
  }
9456
9724
  function isHarnessBuildCachePath(targetPath, harnessRoot, worktreesDir) {
9457
- const resolved = path43.resolve(targetPath);
9458
- const relToWt = path43.relative(worktreesDir, resolved);
9459
- if (relToWt.startsWith("..") || path43.isAbsolute(relToWt)) return "path_outside_harness";
9460
- 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);
9461
9729
  if (parts.length < 3) return "path_outside_harness";
9462
- if (!resolved.startsWith(path43.resolve(harnessRoot))) return "path_outside_harness";
9730
+ if (!resolved.startsWith(path44.resolve(harnessRoot))) return "path_outside_harness";
9463
9731
  return null;
9464
9732
  }
9465
9733
  function isHarnessGeneratedCachePath(targetPath, harnessRoot, worktreesDir) {
9466
- const resolved = path43.resolve(targetPath);
9734
+ const resolved = path44.resolve(targetPath);
9467
9735
  return isHarnessNodeModulesPath(resolved, harnessRoot, worktreesDir) === null || isHarnessNextCachePath(resolved, harnessRoot, worktreesDir) === null || isHarnessBuildCachePath(resolved, harnessRoot, worktreesDir) === null;
9468
9736
  }
9469
9737
 
@@ -9497,12 +9765,12 @@ function tryPrivilegedReclaimHarnessCache(targetPath, harnessRoot, worktreesDir)
9497
9765
  "chown",
9498
9766
  "-R",
9499
9767
  `${effectiveUid}:${effectiveGid}`,
9500
- path44.resolve(targetPath)
9768
+ path45.resolve(targetPath)
9501
9769
  ]);
9502
9770
  if (chown.ok) {
9503
9771
  return { ok: true, method: "chown_then_rm" };
9504
9772
  }
9505
- const rm = runSudoNonInteractive(["rm", "-rf", path44.resolve(targetPath)]);
9773
+ const rm = runSudoNonInteractive(["rm", "-rf", path45.resolve(targetPath)]);
9506
9774
  if (rm.ok) {
9507
9775
  return { ok: true, method: "sudo_rm" };
9508
9776
  }
@@ -9704,7 +9972,7 @@ function removeWorktree(candidate, execute) {
9704
9972
 
9705
9973
  // src/cleanup-scan.ts
9706
9974
  import { existsSync as existsSync34, readdirSync as readdirSync12, statSync as statSync9 } from "node:fs";
9707
- import path45 from "node:path";
9975
+ import path46 from "node:path";
9708
9976
  function pathAgeMs2(target, now) {
9709
9977
  try {
9710
9978
  const mtime = statSync9(target).mtimeMs;
@@ -9714,16 +9982,16 @@ function pathAgeMs2(target, now) {
9714
9982
  }
9715
9983
  }
9716
9984
  function isPathInside(child, parent) {
9717
- const rel = path45.relative(parent, child);
9718
- return rel === "" || !rel.startsWith("..") && !path45.isAbsolute(rel);
9985
+ const rel = path46.relative(parent, child);
9986
+ return rel === "" || !rel.startsWith("..") && !path46.isAbsolute(rel);
9719
9987
  }
9720
9988
  function collectBuildCacheForWorktree(worktreePath, opts, seen, meta) {
9721
9989
  const out = [];
9722
9990
  for (const rel of HARNESS_BUILD_CACHE_RELATIVE_PATHS) {
9723
9991
  if (rel === ".next") continue;
9724
- const target = path45.join(worktreePath, rel);
9992
+ const target = path46.join(worktreePath, rel);
9725
9993
  if (!existsSync34(target)) continue;
9726
- const resolved = path45.resolve(target);
9994
+ const resolved = path46.resolve(target);
9727
9995
  if (seen.has(resolved)) continue;
9728
9996
  if (!isPathInside(resolved, opts.harnessRoot)) continue;
9729
9997
  seen.add(resolved);
@@ -9755,10 +10023,10 @@ function scanBuildCacheCandidates(opts) {
9755
10023
  if (!opts.includeOrphans || !existsSync34(opts.worktreesDir)) return candidates;
9756
10024
  for (const runEntry of readdirSync12(opts.worktreesDir, { withFileTypes: true })) {
9757
10025
  if (!runEntry.isDirectory()) continue;
9758
- const runPath = path45.join(opts.worktreesDir, runEntry.name);
10026
+ const runPath = path46.join(opts.worktreesDir, runEntry.name);
9759
10027
  for (const workerEntry of readdirSync12(runPath, { withFileTypes: true })) {
9760
10028
  if (!workerEntry.isDirectory()) continue;
9761
- const worktreePath = path45.join(runPath, workerEntry.name);
10029
+ const worktreePath = path46.join(runPath, workerEntry.name);
9762
10030
  candidates.push(
9763
10031
  ...collectBuildCacheForWorktree(worktreePath, opts, seen, {
9764
10032
  runId: runEntry.name,
@@ -9796,12 +10064,12 @@ function scanWorktreeCandidates(opts) {
9796
10064
  if (!orphanEnabled || !existsSync34(opts.worktreesDir)) return candidates;
9797
10065
  const indexedPaths = /* @__PURE__ */ new Set();
9798
10066
  for (const entry of opts.index.values()) {
9799
- indexedPaths.add(path45.resolve(entry.worktreePath));
10067
+ indexedPaths.add(path46.resolve(entry.worktreePath));
9800
10068
  }
9801
10069
  for (const runEntry of readdirSync12(opts.worktreesDir, { withFileTypes: true })) {
9802
10070
  if (!runEntry.isDirectory()) continue;
9803
10071
  if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
9804
- const runPath = path45.join(opts.worktreesDir, runEntry.name);
10072
+ const runPath = path46.join(opts.worktreesDir, runEntry.name);
9805
10073
  let workerEntries;
9806
10074
  try {
9807
10075
  workerEntries = readdirSync12(runPath, { withFileTypes: true });
@@ -9810,7 +10078,7 @@ function scanWorktreeCandidates(opts) {
9810
10078
  }
9811
10079
  for (const workerEntry of workerEntries) {
9812
10080
  if (!workerEntry.isDirectory()) continue;
9813
- const worktreePath = path45.resolve(path45.join(runPath, workerEntry.name));
10081
+ const worktreePath = path46.resolve(path46.join(runPath, workerEntry.name));
9814
10082
  if (seen.has(worktreePath)) continue;
9815
10083
  if (indexedPaths.has(worktreePath)) continue;
9816
10084
  if (!isPathInside(worktreePath, opts.harnessRoot)) continue;
@@ -9830,7 +10098,7 @@ function scanWorktreeCandidates(opts) {
9830
10098
 
9831
10099
  // src/cleanup-dependency-scan.ts
9832
10100
  import { existsSync as existsSync35, readdirSync as readdirSync13, statSync as statSync10 } from "node:fs";
9833
- import path46 from "node:path";
10101
+ import path47 from "node:path";
9834
10102
  var DEPENDENCY_CACHE_DIRS = [
9835
10103
  { dirName: "node_modules", kind: "remove_node_modules" },
9836
10104
  { dirName: ".next", kind: "remove_next_cache" }
@@ -9844,12 +10112,12 @@ function pathAgeMs3(target, now) {
9844
10112
  }
9845
10113
  }
9846
10114
  function isPathInside2(child, parent) {
9847
- const rel = path46.relative(parent, child);
9848
- return rel === "" || !rel.startsWith("..") && !path46.isAbsolute(rel);
10115
+ const rel = path47.relative(parent, child);
10116
+ return rel === "" || !rel.startsWith("..") && !path47.isAbsolute(rel);
9849
10117
  }
9850
10118
  function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
9851
10119
  if (!existsSync35(targetPath)) return;
9852
- const resolved = path46.resolve(targetPath);
10120
+ const resolved = path47.resolve(targetPath);
9853
10121
  if (seen.has(resolved)) return;
9854
10122
  if (!isPathInside2(resolved, opts.harnessRoot)) return;
9855
10123
  seen.add(resolved);
@@ -9866,7 +10134,7 @@ function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
9866
10134
  }
9867
10135
  function scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, meta) {
9868
10136
  for (const entry of DEPENDENCY_CACHE_DIRS) {
9869
- 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);
9870
10138
  }
9871
10139
  }
9872
10140
  function scanDependencyCacheCandidates(opts) {
@@ -9884,7 +10152,7 @@ function scanDependencyCacheCandidates(opts) {
9884
10152
  for (const runEntry of readdirSync13(opts.worktreesDir, { withFileTypes: true })) {
9885
10153
  if (!runEntry.isDirectory()) continue;
9886
10154
  if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
9887
- const runPath = path46.join(opts.worktreesDir, runEntry.name);
10155
+ const runPath = path47.join(opts.worktreesDir, runEntry.name);
9888
10156
  let workerEntries;
9889
10157
  try {
9890
10158
  workerEntries = readdirSync13(runPath, { withFileTypes: true });
@@ -9893,7 +10161,7 @@ function scanDependencyCacheCandidates(opts) {
9893
10161
  }
9894
10162
  for (const workerEntry of workerEntries) {
9895
10163
  if (!workerEntry.isDirectory()) continue;
9896
- const worktreePath = path46.join(runPath, workerEntry.name);
10164
+ const worktreePath = path47.join(runPath, workerEntry.name);
9897
10165
  scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, {
9898
10166
  runId: runEntry.name,
9899
10167
  worker: workerEntry.name
@@ -9905,7 +10173,7 @@ function scanDependencyCacheCandidates(opts) {
9905
10173
 
9906
10174
  // src/cleanup-duplicate-worktrees.ts
9907
10175
  import { existsSync as existsSync36, statSync as statSync11 } from "node:fs";
9908
- import path47 from "node:path";
10176
+ import path48 from "node:path";
9909
10177
  function pathAgeMs4(target, now) {
9910
10178
  try {
9911
10179
  const mtime = statSync11(target).mtimeMs;
@@ -9935,8 +10203,8 @@ function parseWorktreePorcelain(output) {
9935
10203
  return records;
9936
10204
  }
9937
10205
  function isUnderWorktreesDir(worktreePath, worktreesDir) {
9938
- const rel = path47.relative(path47.resolve(worktreesDir), path47.resolve(worktreePath));
9939
- 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);
9940
10208
  }
9941
10209
  function isCleanWorktree(worktreePath, repoRoot) {
9942
10210
  try {
@@ -9952,11 +10220,11 @@ function scanDuplicateWorktreeCandidates(opts) {
9952
10220
  if (!opts.includeOrphans || !existsSync36(opts.worktreesDir)) return [];
9953
10221
  const repos = /* @__PURE__ */ new Set();
9954
10222
  for (const entry of opts.index.values()) {
9955
- if (entry.run.repo) repos.add(path47.resolve(entry.run.repo));
10223
+ if (entry.run.repo) repos.add(path48.resolve(entry.run.repo));
9956
10224
  }
9957
10225
  const indexedPaths = /* @__PURE__ */ new Set();
9958
10226
  for (const entry of opts.index.values()) {
9959
- indexedPaths.add(path47.resolve(entry.worktreePath));
10227
+ indexedPaths.add(path48.resolve(entry.worktreePath));
9960
10228
  }
9961
10229
  const candidates = [];
9962
10230
  const seen = /* @__PURE__ */ new Set();
@@ -9969,15 +10237,15 @@ function scanDuplicateWorktreeCandidates(opts) {
9969
10237
  }
9970
10238
  const worktrees = parseWorktreePorcelain(porcelain);
9971
10239
  for (const wt of worktrees) {
9972
- const resolved = path47.resolve(wt.path);
9973
- if (resolved === path47.resolve(repoRoot)) continue;
10240
+ const resolved = path48.resolve(wt.path);
10241
+ if (resolved === path48.resolve(repoRoot)) continue;
9974
10242
  if (!isUnderWorktreesDir(resolved, opts.worktreesDir)) continue;
9975
10243
  if (indexedPaths.has(resolved)) continue;
9976
10244
  if (seen.has(resolved)) continue;
9977
10245
  if (!existsSync36(resolved)) continue;
9978
10246
  if (!isCleanWorktree(resolved, repoRoot)) continue;
9979
- const rel = path47.relative(opts.worktreesDir, resolved);
9980
- const parts = rel.split(path47.sep);
10247
+ const rel = path48.relative(opts.worktreesDir, resolved);
10248
+ const parts = rel.split(path48.sep);
9981
10249
  const runId = parts[0];
9982
10250
  const worker = parts[1] ?? "unknown";
9983
10251
  seen.add(resolved);
@@ -9996,12 +10264,12 @@ function scanDuplicateWorktreeCandidates(opts) {
9996
10264
  }
9997
10265
 
9998
10266
  // src/cleanup-worktree-index.ts
9999
- import path48 from "node:path";
10267
+ import path49 from "node:path";
10000
10268
  function buildWorktreeIndexAt(harnessRoot) {
10001
10269
  const index = /* @__PURE__ */ new Map();
10002
10270
  for (const run of listRunRecordsForHarnessRoot(harnessRoot)) {
10003
10271
  for (const name of Object.keys(run.workers || {})) {
10004
- const workerPath = path48.join(
10272
+ const workerPath = path49.join(
10005
10273
  runDirectoryAt(harnessRoot, run.id),
10006
10274
  "workers",
10007
10275
  safeSlug(name),
@@ -10009,9 +10277,9 @@ function buildWorktreeIndexAt(harnessRoot) {
10009
10277
  );
10010
10278
  const worker = readJson(workerPath, void 0);
10011
10279
  if (!worker?.worktreePath) continue;
10012
- index.set(path48.resolve(worker.worktreePath), {
10280
+ index.set(path49.resolve(worker.worktreePath), {
10013
10281
  harnessRoot,
10014
- worktreePath: path48.resolve(worker.worktreePath),
10282
+ worktreePath: path49.resolve(worker.worktreePath),
10015
10283
  runId: run.id,
10016
10284
  workerName: name,
10017
10285
  run,
@@ -10081,14 +10349,14 @@ function resolvePipelineHarnessRetention(runId) {
10081
10349
 
10082
10350
  // src/cleanup-orphan-safety.ts
10083
10351
  import { existsSync as existsSync37, statSync as statSync12 } from "node:fs";
10084
- import path49 from "node:path";
10352
+ import path50 from "node:path";
10085
10353
  var DEFAULT_HEARTBEAT_FRESH_MS = 30 * 60 * 1e3;
10086
10354
  function assessOrphanWorktreeSafety(input) {
10087
10355
  const now = input.now ?? Date.now();
10088
10356
  const heartbeatFreshMs = input.heartbeatFreshMs ?? DEFAULT_HEARTBEAT_FRESH_MS;
10089
10357
  if (!existsSync37(input.worktreePath)) return null;
10090
10358
  if (input.runId && input.workerName) {
10091
- const heartbeatPath = path49.join(
10359
+ const heartbeatPath = path50.join(
10092
10360
  input.harnessRoot,
10093
10361
  "runs",
10094
10362
  input.runId,
@@ -10102,7 +10370,7 @@ function assessOrphanWorktreeSafety(input) {
10102
10370
  } catch {
10103
10371
  }
10104
10372
  }
10105
- const gitDir = path49.join(input.worktreePath, ".git");
10373
+ const gitDir = path50.join(input.worktreePath, ".git");
10106
10374
  if (!existsSync37(gitDir)) return null;
10107
10375
  const porcelain = gitCapture(input.worktreePath, ["status", "--porcelain"]);
10108
10376
  if (porcelain.status !== 0) return "pr_or_unmerged_commits";
@@ -10133,7 +10401,7 @@ function assessOrphanWorktreeSafety(input) {
10133
10401
 
10134
10402
  // src/harness-storage-snapshot.ts
10135
10403
  import { existsSync as existsSync38, readdirSync as readdirSync14, statSync as statSync13 } from "node:fs";
10136
- import path50 from "node:path";
10404
+ import path51 from "node:path";
10137
10405
  function harnessStorageSnapshot(opts = {}) {
10138
10406
  const harnessRoot = normalizeHarnessRoot(opts.harnessRoot ?? resolveHarnessRoot());
10139
10407
  const worktreesDir = harnessWorktreesDir(harnessRoot);
@@ -10171,7 +10439,7 @@ function harnessStorageSnapshot(opts = {}) {
10171
10439
  for (const runEntry of entries) {
10172
10440
  if (!runEntry.isDirectory()) continue;
10173
10441
  runCount += 1;
10174
- const runPath = path50.join(worktreesDir, runEntry.name);
10442
+ const runPath = path51.join(worktreesDir, runEntry.name);
10175
10443
  try {
10176
10444
  const st = statSync13(runPath);
10177
10445
  oldestMs = oldestMs === null ? st.mtimeMs : Math.min(oldestMs, st.mtimeMs);
@@ -10208,10 +10476,10 @@ function harnessStorageSnapshot(opts = {}) {
10208
10476
  // src/cleanup-harness-roots.ts
10209
10477
  import { existsSync as existsSync39 } from "node:fs";
10210
10478
  import { homedir as homedir12 } from "node:os";
10211
- import path51 from "node:path";
10479
+ import path52 from "node:path";
10212
10480
  var WELL_KNOWN_HARNESS_SCAN_ROOTS = [
10213
10481
  "/var/tmp/kynver-harness",
10214
- path51.join(homedir12(), ".openclaw", "harness")
10482
+ path52.join(homedir12(), ".openclaw", "harness")
10215
10483
  ];
10216
10484
  function addRoot(seen, roots, candidate) {
10217
10485
  if (!candidate?.trim()) return;
@@ -10233,7 +10501,7 @@ function resolveHarnessScanRoots(options = {}) {
10233
10501
  for (const candidate of extra ?? []) addRoot(seen, roots, candidate);
10234
10502
  if (shouldScanWellKnownRoots(options)) {
10235
10503
  for (const candidate of WELL_KNOWN_HARNESS_SCAN_ROOTS) {
10236
- const resolved = path51.resolve(candidate);
10504
+ const resolved = path52.resolve(candidate);
10237
10505
  if (!seen.has(resolved) && existsSync39(resolved)) addRoot(seen, roots, resolved);
10238
10506
  }
10239
10507
  }
@@ -10388,9 +10656,9 @@ function mergeWorktreeIndexes(scanRoots) {
10388
10656
  }
10389
10657
  function worktreePathForCandidate(candidate, worktreesDir) {
10390
10658
  if (candidate.runId && candidate.worker) {
10391
- return path52.join(worktreesDir, candidate.runId, candidate.worker);
10659
+ return path53.join(worktreesDir, candidate.runId, candidate.worker);
10392
10660
  }
10393
- return path52.resolve(candidate.path, "..");
10661
+ return path53.resolve(candidate.path, "..");
10394
10662
  }
10395
10663
  function runHarnessCleanup(options = {}) {
10396
10664
  let retention = resolveHarnessRetention(options);
@@ -10415,7 +10683,7 @@ function runHarnessCleanup(options = {}) {
10415
10683
  for (const harnessRoot of paths.scanRoots) {
10416
10684
  if (atSweepCap()) break;
10417
10685
  emitCleanupProgress("root", harnessRoot);
10418
- const worktreesDir = path52.join(harnessRoot, "worktrees");
10686
+ const worktreesDir = path53.join(harnessRoot, "worktrees");
10419
10687
  const scanOpts = {
10420
10688
  harnessRoot,
10421
10689
  worktreesDir,
@@ -10428,7 +10696,7 @@ function runHarnessCleanup(options = {}) {
10428
10696
  };
10429
10697
  for (const raw of scanDependencyCacheCandidates(scanOpts)) {
10430
10698
  if (atSweepCap()) break;
10431
- const resolved = path52.resolve(raw.path);
10699
+ const resolved = path53.resolve(raw.path);
10432
10700
  if (processedPaths.has(resolved)) continue;
10433
10701
  processedPaths.add(resolved);
10434
10702
  const candidate = { ...raw, path: resolved };
@@ -10439,7 +10707,7 @@ function runHarnessCleanup(options = {}) {
10439
10707
  continue;
10440
10708
  }
10441
10709
  const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
10442
- const indexed = index.get(path52.resolve(worktreePath)) ?? null;
10710
+ const indexed = index.get(path53.resolve(worktreePath)) ?? null;
10443
10711
  const guardReason = skipDependencyCacheRemoval({
10444
10712
  indexed,
10445
10713
  includeOrphans: true,
@@ -10463,7 +10731,7 @@ function runHarnessCleanup(options = {}) {
10463
10731
  }
10464
10732
  for (const raw of scanBuildCacheCandidates(scanOpts)) {
10465
10733
  if (atSweepCap()) break;
10466
- const resolved = path52.resolve(raw.path);
10734
+ const resolved = path53.resolve(raw.path);
10467
10735
  if (processedPaths.has(resolved)) continue;
10468
10736
  processedPaths.add(resolved);
10469
10737
  const candidate = { ...raw, path: resolved };
@@ -10474,7 +10742,7 @@ function runHarnessCleanup(options = {}) {
10474
10742
  continue;
10475
10743
  }
10476
10744
  const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
10477
- const indexed = index.get(path52.resolve(worktreePath)) ?? null;
10745
+ const indexed = index.get(path53.resolve(worktreePath)) ?? null;
10478
10746
  const guardReason = skipBuildCacheRemoval({
10479
10747
  indexed,
10480
10748
  includeOrphans: true,
@@ -10504,11 +10772,11 @@ function runHarnessCleanup(options = {}) {
10504
10772
  const worktreeSeen = /* @__PURE__ */ new Set();
10505
10773
  for (const raw of worktreeCandidates) {
10506
10774
  if (atSweepCap()) break;
10507
- const resolved = path52.resolve(raw.path);
10775
+ const resolved = path53.resolve(raw.path);
10508
10776
  if (worktreeSeen.has(resolved)) continue;
10509
10777
  worktreeSeen.add(resolved);
10510
10778
  const candidate = { ...raw, path: resolved };
10511
- const indexed = index.get(path52.resolve(candidate.path)) ?? null;
10779
+ const indexed = index.get(path53.resolve(candidate.path)) ?? null;
10512
10780
  const orphanSafety = indexed ? null : assessOrphanWorktreeSafety({
10513
10781
  worktreePath: candidate.path,
10514
10782
  harnessRoot,
@@ -10518,7 +10786,7 @@ function runHarnessCleanup(options = {}) {
10518
10786
  });
10519
10787
  const guardSkip = skipWorktreeRemoval({
10520
10788
  indexed,
10521
- worktreePath: path52.resolve(candidate.path),
10789
+ worktreePath: path53.resolve(candidate.path),
10522
10790
  includeOrphans: retention.includeOrphans,
10523
10791
  worktreesAgeMs: retention.worktreesAgeMs,
10524
10792
  terminalWorktreesAgeMs: retention.terminalWorktreesAgeMs,
@@ -10550,11 +10818,11 @@ function runHarnessCleanup(options = {}) {
10550
10818
  now: paths.now
10551
10819
  })) {
10552
10820
  if (atSweepCap()) break;
10553
- const resolved = path52.resolve(raw.path);
10821
+ const resolved = path53.resolve(raw.path);
10554
10822
  if (processedPaths.has(resolved)) continue;
10555
10823
  processedPaths.add(resolved);
10556
10824
  const candidate = { ...raw, path: resolved };
10557
- const runId = candidate.runId ?? path52.basename(resolved);
10825
+ const runId = candidate.runId ?? path53.basename(resolved);
10558
10826
  const dirSkip = skipRunDirectoryRemoval({
10559
10827
  harnessRoot,
10560
10828
  runId,
@@ -10690,11 +10958,11 @@ function isPipelineCleanupEnabled() {
10690
10958
  // src/installed-package-versions.ts
10691
10959
  import { readFile } from "node:fs/promises";
10692
10960
  import { homedir as homedir13 } from "node:os";
10693
- import path54 from "node:path";
10961
+ import path55 from "node:path";
10694
10962
 
10695
10963
  // src/memory-cost-package-version-guard.ts
10696
- import { existsSync as existsSync40, readFileSync as readFileSync12 } from "node:fs";
10697
- import path53 from "node:path";
10964
+ import { existsSync as existsSync40, readFileSync as readFileSync13 } from "node:fs";
10965
+ import path54 from "node:path";
10698
10966
  var MEMORY_COST_PACKAGE_MIN_VERSIONS = {
10699
10967
  "@kynver-app/runtime": "0.1.83",
10700
10968
  "@kynver-app/openclaw-agent-os": "0.1.43",
@@ -10745,7 +11013,7 @@ function maxSemver(versions) {
10745
11013
  }
10746
11014
  function readPackageJsonVersion(packageJsonPath) {
10747
11015
  try {
10748
- const parsed = JSON.parse(readFileSync12(packageJsonPath, "utf8"));
11016
+ const parsed = JSON.parse(readFileSync13(packageJsonPath, "utf8"));
10749
11017
  return typeof parsed.version === "string" && parsed.version.trim() ? parsed.version.trim() : null;
10750
11018
  } catch {
10751
11019
  return null;
@@ -10756,8 +11024,8 @@ function resolveRepoRoot(cwd, explicitRepoRoot) {
10756
11024
  (value) => Boolean(value?.trim())
10757
11025
  );
10758
11026
  for (const candidate of candidates) {
10759
- const resolved = path53.resolve(candidate);
10760
- 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"))) {
10761
11029
  return resolved;
10762
11030
  }
10763
11031
  }
@@ -10770,7 +11038,7 @@ function probeRepoPackageVersions(input = {}) {
10770
11038
  if (!repoRoot) return {};
10771
11039
  const out = {};
10772
11040
  for (const packageName of MEMORY_COST_MANAGED_PACKAGES) {
10773
- const packageJsonPath = path53.join(repoRoot, REPO_PACKAGE_JSON_RELATIVE[packageName]);
11041
+ const packageJsonPath = path54.join(repoRoot, REPO_PACKAGE_JSON_RELATIVE[packageName]);
10774
11042
  const version = readPackageJsonVersion(packageJsonPath);
10775
11043
  if (!version) continue;
10776
11044
  out[packageName] = { version, source: "repo", path: packageJsonPath };
@@ -10890,12 +11158,12 @@ function unique(values) {
10890
11158
  }
10891
11159
  function moduleRoots() {
10892
11160
  const home = homedir13();
10893
- const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ?? path54.join(home, ".openclaw", "npm");
10894
- 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"));
10895
11163
  return unique([
10896
- path54.join(openClawPrefix, "lib", "node_modules"),
10897
- path54.join(openClawPrefix, "node_modules"),
10898
- 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")
10899
11167
  ]);
10900
11168
  }
10901
11169
  async function readVersion(packageJsonPath) {
@@ -10911,7 +11179,7 @@ function installedPackageJsonCandidates(packageName) {
10911
11179
  const seen = /* @__PURE__ */ new Set();
10912
11180
  const out = [];
10913
11181
  for (const root of roots) {
10914
- const candidate = path54.join(root, packageName, "package.json");
11182
+ const candidate = path55.join(root, packageName, "package.json");
10915
11183
  if (seen.has(candidate)) continue;
10916
11184
  seen.add(candidate);
10917
11185
  out.push(candidate);
@@ -10942,7 +11210,7 @@ async function completeFinishedWorkers(runId, args) {
10942
11210
  const outcomes = [];
10943
11211
  for (const name of Object.keys(run.workers || {})) {
10944
11212
  const worker = readJson(
10945
- path55.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
11213
+ path56.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
10946
11214
  void 0
10947
11215
  );
10948
11216
  if (!worker?.taskId || worker.localOnly) continue;
@@ -11089,6 +11357,15 @@ async function runPipelineTick(args) {
11089
11357
  var DEFAULT_INTERVAL_MS = 6e4;
11090
11358
  var IDLE_INTERVAL_MS = 5 * 6e4;
11091
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
+ }
11092
11369
  async function runDaemon(args) {
11093
11370
  const runId = String(required(String(args.run || ""), "--run"));
11094
11371
  const agentOsId = String(required(String(args.agentOsId || loadUserConfig().agentOsId || ""), "--agent-os-id"));
@@ -11146,17 +11423,17 @@ async function runDaemon(args) {
11146
11423
  idleStreak = 0;
11147
11424
  }
11148
11425
  const backoff = idleStreak >= MAX_IDLE_STREAK ? IDLE_INTERVAL_MS : intervalMs;
11149
- sleepMs(backoff);
11426
+ await awaitDaemonBackoff(backoff, () => stopping);
11150
11427
  } catch (error) {
11151
11428
  console.error(JSON.stringify({ event: "daemon_tick_error", error: error.message }));
11152
- sleepMs(intervalMs);
11429
+ await awaitDaemonBackoff(intervalMs, () => stopping);
11153
11430
  }
11154
11431
  }
11155
11432
  console.error(JSON.stringify({ event: "daemon_stop", runId, agentOsId }));
11156
11433
  }
11157
11434
 
11158
11435
  // src/plan-progress.ts
11159
- import path57 from "node:path";
11436
+ import path60 from "node:path";
11160
11437
 
11161
11438
  // src/bounded-build/constants.ts
11162
11439
  var DEFAULT_BUILD_MEM_BUDGET_BYTES = 1536 * 1024 * 1024;
@@ -11286,14 +11563,235 @@ function waitForBuildAdmission(timeoutMs, pollMs = 2e3, opts = {}) {
11286
11563
  }
11287
11564
 
11288
11565
  // src/bounded-build/exec.ts
11566
+ import { spawnSync as spawnSync8 } from "node:child_process";
11567
+
11568
+ // src/heavy-verification/slot.ts
11569
+ import {
11570
+ closeSync as closeSync7,
11571
+ existsSync as existsSync41,
11572
+ mkdirSync as mkdirSync8,
11573
+ openSync as openSync7,
11574
+ readdirSync as readdirSync15,
11575
+ readFileSync as readFileSync14,
11576
+ unlinkSync as unlinkSync4,
11577
+ writeFileSync as writeFileSync5
11578
+ } from "node:fs";
11579
+ import path58 from "node:path";
11580
+
11581
+ // src/heavy-verification/paths.ts
11582
+ import { mkdirSync as mkdirSync7 } from "node:fs";
11583
+ import path57 from "node:path";
11584
+ function resolveHeavyVerificationRoot() {
11585
+ return path57.join(resolveKynverStateRoot(), "heavy-verification");
11586
+ }
11587
+ function heavyVerificationSlotsDir() {
11588
+ return path57.join(resolveHeavyVerificationRoot(), "slots");
11589
+ }
11590
+ function ensureHeavyVerificationDirs() {
11591
+ const dir = heavyVerificationSlotsDir();
11592
+ mkdirSync7(dir, { recursive: true });
11593
+ return dir;
11594
+ }
11595
+
11596
+ // src/heavy-verification/slot.ts
11597
+ var DEFAULT_HEAVY_VERIFICATION_STALE_MS = 2 * 60 * 6e4;
11598
+ var DEFAULT_HEAVY_VERIFICATION_MAX_CONCURRENT = 1;
11599
+ function positiveInt5(value, fallback) {
11600
+ const n = Number(value);
11601
+ if (!Number.isFinite(n) || n <= 0) return fallback;
11602
+ return Math.floor(n);
11603
+ }
11604
+ function isHeavyVerificationGateSkipped() {
11605
+ const v = process.env.KYNVER_HEAVY_VERIFICATION_SKIP?.trim().toLowerCase();
11606
+ return v === "1" || v === "true" || v === "yes";
11607
+ }
11608
+ function resolveHeavyVerificationMaxConcurrent() {
11609
+ const env = process.env.KYNVER_HEAVY_VERIFICATION_MAX_CONCURRENT;
11610
+ if (env) return positiveInt5(env, DEFAULT_HEAVY_VERIFICATION_MAX_CONCURRENT);
11611
+ return DEFAULT_HEAVY_VERIFICATION_MAX_CONCURRENT;
11612
+ }
11613
+ function indexedSlotId(index) {
11614
+ return `slot-${index}`;
11615
+ }
11616
+ function slotFilePath(slotId, slotsDir = heavyVerificationSlotsDir()) {
11617
+ return path58.join(slotsDir, `${slotId}.json`);
11618
+ }
11619
+ function readSlotRecord(filePath) {
11620
+ if (!existsSync41(filePath)) return null;
11621
+ try {
11622
+ const parsed = JSON.parse(readFileSync14(filePath, "utf8"));
11623
+ if (typeof parsed.slotId === "string" && typeof parsed.pid === "number" && typeof parsed.acquiredAt === "string" && typeof parsed.command === "string") {
11624
+ return parsed;
11625
+ }
11626
+ } catch {
11627
+ return null;
11628
+ }
11629
+ return null;
11630
+ }
11631
+ function slotIsStale(record, staleMs = DEFAULT_HEAVY_VERIFICATION_STALE_MS) {
11632
+ if (!record) return true;
11633
+ if (!isPidAlive(record.pid)) return true;
11634
+ const atMs = Date.parse(record.acquiredAt);
11635
+ if (Number.isNaN(atMs)) return true;
11636
+ return Date.now() - atMs > staleMs;
11637
+ }
11638
+ function reclaimStaleSlot(filePath, staleMs) {
11639
+ const record = readSlotRecord(filePath);
11640
+ if (!slotIsStale(record, staleMs)) return;
11641
+ try {
11642
+ unlinkSync4(filePath);
11643
+ } catch {
11644
+ }
11645
+ }
11646
+ function ensureSlotsDir(slotsDir) {
11647
+ mkdirSync8(slotsDir, { recursive: true });
11648
+ return slotsDir;
11649
+ }
11650
+ function reclaimStaleHeavyVerificationSlots(opts = {}) {
11651
+ const slotsDir = ensureSlotsDir(opts.slotsDir ?? ensureHeavyVerificationDirs());
11652
+ const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
11653
+ let reclaimed = 0;
11654
+ for (const name of readdirSync15(slotsDir)) {
11655
+ if (!name.endsWith(".json")) continue;
11656
+ const filePath = path58.join(slotsDir, name);
11657
+ const before = existsSync41(filePath);
11658
+ reclaimStaleSlot(filePath, staleMs);
11659
+ if (before && !existsSync41(filePath)) reclaimed += 1;
11660
+ }
11661
+ return reclaimed;
11662
+ }
11663
+ function listActiveHeavyVerificationSlots(opts = {}) {
11664
+ const slotsDir = opts.slotsDir ?? ensureHeavyVerificationDirs();
11665
+ const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
11666
+ reclaimStaleHeavyVerificationSlots({ slotsDir, staleMs });
11667
+ const active = [];
11668
+ for (const name of readdirSync15(slotsDir)) {
11669
+ if (!name.endsWith(".json")) continue;
11670
+ const record = readSlotRecord(path58.join(slotsDir, name));
11671
+ if (record && !slotIsStale(record, staleMs)) active.push(record);
11672
+ }
11673
+ return active;
11674
+ }
11675
+ function countActiveHeavyVerificationSlots(opts = {}) {
11676
+ return listActiveHeavyVerificationSlots(opts).length;
11677
+ }
11678
+ function tryAcquireHeavyVerificationSlot(command, opts = {}) {
11679
+ if (isHeavyVerificationGateSkipped()) {
11680
+ return {
11681
+ admitted: true,
11682
+ slotId: null,
11683
+ activeSlots: 0,
11684
+ maxSlots: resolveHeavyVerificationMaxConcurrent(),
11685
+ reason: null
11686
+ };
11687
+ }
11688
+ const slotsDir = opts.slotsDir ?? ensureHeavyVerificationDirs();
11689
+ const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
11690
+ const maxSlots = opts.maxSlots ?? resolveHeavyVerificationMaxConcurrent();
11691
+ reclaimStaleHeavyVerificationSlots({ slotsDir, staleMs });
11692
+ for (let index = 0; index < maxSlots; index += 1) {
11693
+ const slotId = indexedSlotId(index);
11694
+ const filePath = slotFilePath(slotId, slotsDir);
11695
+ const existing = readSlotRecord(filePath);
11696
+ if (existing && slotIsStale(existing, staleMs)) {
11697
+ try {
11698
+ unlinkSync4(filePath);
11699
+ } catch {
11700
+ }
11701
+ } else if (existing && !slotIsStale(existing, staleMs)) {
11702
+ continue;
11703
+ }
11704
+ const record = {
11705
+ slotId,
11706
+ pid: process.pid,
11707
+ acquiredAt: (/* @__PURE__ */ new Date()).toISOString(),
11708
+ command
11709
+ };
11710
+ try {
11711
+ const fd = openSync7(filePath, "wx");
11712
+ writeFileSync5(fd, JSON.stringify(record, null, 2), "utf8");
11713
+ closeSync7(fd);
11714
+ const activeSlots2 = countActiveHeavyVerificationSlots({ slotsDir, staleMs });
11715
+ return {
11716
+ admitted: true,
11717
+ slotId,
11718
+ activeSlots: activeSlots2,
11719
+ maxSlots,
11720
+ reason: null
11721
+ };
11722
+ } catch (err) {
11723
+ if (err.code === "EEXIST") {
11724
+ continue;
11725
+ }
11726
+ throw err;
11727
+ }
11728
+ }
11729
+ const activeSlots = countActiveHeavyVerificationSlots({ slotsDir, staleMs });
11730
+ return {
11731
+ admitted: false,
11732
+ slotId: null,
11733
+ activeSlots,
11734
+ maxSlots,
11735
+ reason: `heavy verification at capacity (${activeSlots}/${maxSlots} slots)`
11736
+ };
11737
+ }
11738
+ function releaseHeavyVerificationSlot(slotId, opts = {}) {
11739
+ if (!slotId) return;
11740
+ const filePath = slotFilePath(slotId, opts.slotsDir ?? heavyVerificationSlotsDir());
11741
+ try {
11742
+ unlinkSync4(filePath);
11743
+ } catch {
11744
+ }
11745
+ }
11746
+ function assessHeavyVerificationGate(command, opts = {}) {
11747
+ if (isHeavyVerificationGateSkipped()) {
11748
+ return {
11749
+ admitted: true,
11750
+ slotId: null,
11751
+ activeSlots: 0,
11752
+ maxSlots: resolveHeavyVerificationMaxConcurrent(),
11753
+ reason: null
11754
+ };
11755
+ }
11756
+ const slotsDir = opts.slotsDir ?? ensureHeavyVerificationDirs();
11757
+ const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
11758
+ const maxSlots = opts.maxSlots ?? resolveHeavyVerificationMaxConcurrent();
11759
+ reclaimStaleHeavyVerificationSlots({ slotsDir, staleMs });
11760
+ const activeSlots = countActiveHeavyVerificationSlots({ slotsDir, staleMs });
11761
+ const admitted = activeSlots < maxSlots;
11762
+ return {
11763
+ admitted,
11764
+ slotId: null,
11765
+ activeSlots,
11766
+ maxSlots,
11767
+ reason: admitted ? null : `heavy verification at capacity (${activeSlots}/${maxSlots} slots); waiting for ${command}`
11768
+ };
11769
+ }
11770
+
11771
+ // src/heavy-verification/gate.ts
11289
11772
  import { spawnSync as spawnSync7 } from "node:child_process";
11773
+ function sleepMs3(ms) {
11774
+ if (ms <= 0) return;
11775
+ spawnSync7(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
11776
+ stdio: "ignore"
11777
+ });
11778
+ }
11779
+ function waitForHeavyVerificationSlot(command, timeoutMs, pollMs = 2e3, opts = {}) {
11780
+ const deadline = Date.now() + Math.max(0, timeoutMs);
11781
+ let verdict = tryAcquireHeavyVerificationSlot(command, opts);
11782
+ while (!verdict.admitted && Date.now() < deadline) {
11783
+ sleepMs3(Math.min(pollMs, deadline - Date.now()));
11784
+ verdict = tryAcquireHeavyVerificationSlot(command, opts);
11785
+ }
11786
+ return verdict;
11787
+ }
11290
11788
 
11291
11789
  // src/harness-worktree-build-guard.ts
11292
- import path56 from "node:path";
11790
+ import path59 from "node:path";
11293
11791
  function isPathUnderHarnessWorktree(cwd) {
11294
11792
  const worktreesDir = harnessWorktreesDir(resolveHarnessRoot());
11295
- const rel = path56.relative(worktreesDir, path56.resolve(cwd));
11296
- return rel.length > 0 && !rel.startsWith("..") && !path56.isAbsolute(rel);
11793
+ const rel = path59.relative(worktreesDir, path59.resolve(cwd));
11794
+ return rel.length > 0 && !rel.startsWith("..") && !path59.isAbsolute(rel);
11297
11795
  }
11298
11796
  function assessHarnessWorktreeBuildGuard(cwd) {
11299
11797
  if (!isPathUnderHarnessWorktree(cwd)) return { ok: true };
@@ -11317,7 +11815,7 @@ function envArgv(env) {
11317
11815
  return out;
11318
11816
  }
11319
11817
  function runSpawn(argv, opts) {
11320
- const res = spawnSync7(argv[0], argv.slice(1), {
11818
+ const res = spawnSync8(argv[0], argv.slice(1), {
11321
11819
  cwd: opts.cwd,
11322
11820
  env: opts.env,
11323
11821
  encoding: "utf8",
@@ -11333,8 +11831,25 @@ function runSpawn(argv, opts) {
11333
11831
  }
11334
11832
  function runBoundedBuildCheck(input) {
11335
11833
  const waitMs = input.waitForAdmissionMs ?? 6e5;
11834
+ const verificationGate = waitMs > 0 ? waitForHeavyVerificationSlot(input.command, waitMs) : tryAcquireOrAssessVerificationGate(input.command);
11835
+ if (!verificationGate.admitted) {
11836
+ return {
11837
+ ok: false,
11838
+ exitCode: 1,
11839
+ stdout: "",
11840
+ stderr: verificationGate.reason ?? "heavy verification gate denied",
11841
+ admitted: false,
11842
+ wrappedWithSystemd: false,
11843
+ nodeOptionsFlag: formatNodeOptionsFlag(),
11844
+ admission: assessBuildAdmission(),
11845
+ verificationGate,
11846
+ command: input.command
11847
+ };
11848
+ }
11849
+ const slotId = verificationGate.slotId;
11336
11850
  const admission = waitMs > 0 ? waitForBuildAdmission(waitMs) : assessBuildAdmission();
11337
11851
  if (!admission.admitted) {
11852
+ releaseHeavyVerificationSlot(slotId);
11338
11853
  return {
11339
11854
  ok: false,
11340
11855
  exitCode: 1,
@@ -11344,11 +11859,13 @@ function runBoundedBuildCheck(input) {
11344
11859
  wrappedWithSystemd: false,
11345
11860
  nodeOptionsFlag: formatNodeOptionsFlag(),
11346
11861
  admission,
11862
+ verificationGate,
11347
11863
  command: input.command
11348
11864
  };
11349
11865
  }
11350
11866
  const worktreeGuard = assessHarnessWorktreeBuildGuard(input.cwd);
11351
11867
  if (!worktreeGuard.ok) {
11868
+ releaseHeavyVerificationSlot(slotId);
11352
11869
  return {
11353
11870
  ok: false,
11354
11871
  exitCode: 1,
@@ -11358,6 +11875,7 @@ function runBoundedBuildCheck(input) {
11358
11875
  wrappedWithSystemd: false,
11359
11876
  nodeOptionsFlag: formatNodeOptionsFlag(),
11360
11877
  admission,
11878
+ verificationGate,
11361
11879
  command: input.command
11362
11880
  };
11363
11881
  }
@@ -11390,12 +11908,19 @@ function runBoundedBuildCheck(input) {
11390
11908
  wrappedWithSystemd: useSystemd,
11391
11909
  nodeOptionsFlag,
11392
11910
  admission,
11911
+ verificationGate,
11393
11912
  command: input.command
11394
11913
  };
11395
11914
  } finally {
11396
11915
  registerBuildEnd();
11916
+ releaseHeavyVerificationSlot(slotId);
11397
11917
  }
11398
11918
  }
11919
+ function tryAcquireOrAssessVerificationGate(command) {
11920
+ const acquired = waitForHeavyVerificationSlot(command, 0);
11921
+ if (acquired.admitted) return acquired;
11922
+ return { ...assessHeavyVerificationGate(command), slotId: null };
11923
+ }
11399
11924
 
11400
11925
  // src/harness-verify.ts
11401
11926
  var DEFAULT_HARNESS_VERIFY_COMMANDS = ["npm run typecheck", "npm run test"];
@@ -11478,7 +12003,7 @@ async function emitPlanProgress(args) {
11478
12003
  }
11479
12004
  function verifyPlanLocal(args) {
11480
12005
  const worktree = required(args.worktree ? String(args.worktree) : void 0, "worktree");
11481
- const cwd = path57.resolve(worktree);
12006
+ const cwd = path60.resolve(worktree);
11482
12007
  const summary = runHarnessVerifyCommands(cwd);
11483
12008
  const emitJson = args.json === true || args.json === "true";
11484
12009
  const payload = { passed: summary.passed, worktree: cwd, steps: summary.steps };
@@ -11527,9 +12052,9 @@ async function verifyPlan(args) {
11527
12052
  }
11528
12053
 
11529
12054
  // src/harness-verify-cli.ts
11530
- import path58 from "node:path";
12055
+ import path61 from "node:path";
11531
12056
  function runHarnessVerifyCli(args) {
11532
- const cwd = path58.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
12057
+ const cwd = path61.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
11533
12058
  const emitJson = args.json === true || args.json === "true" || args.emitJson === true || args.emitJson === "true";
11534
12059
  const commands = [];
11535
12060
  const rawCmd = args.command;
@@ -11557,6 +12082,7 @@ function runHarnessVerifyCli(args) {
11557
12082
  wrappedWithSystemd: s.result.wrappedWithSystemd,
11558
12083
  nodeOptionsFlag: s.result.nodeOptionsFlag,
11559
12084
  admission: s.result.admission,
12085
+ verificationGate: s.result.verificationGate,
11560
12086
  stderr: s.result.stderr.slice(0, 4e3)
11561
12087
  }))
11562
12088
  };
@@ -11573,7 +12099,7 @@ function runHarnessVerifyCli(args) {
11573
12099
  }
11574
12100
 
11575
12101
  // src/plan-persist-cli.ts
11576
- import { readFileSync as readFileSync13 } from "node:fs";
12102
+ import { readFileSync as readFileSync15 } from "node:fs";
11577
12103
  var OPERATIONS = ["create", "add_version", "update_metadata"];
11578
12104
  var FAILURE_KINDS = [
11579
12105
  "approval_guard",
@@ -11585,7 +12111,7 @@ var FAILURE_KINDS = [
11585
12111
  function readBodyArg(args) {
11586
12112
  const bodyFile = args.bodyFile ? String(args.bodyFile) : void 0;
11587
12113
  if (bodyFile) {
11588
- return { body: readFileSync13(bodyFile, "utf8"), bodyPathHint: bodyFile };
12114
+ return { body: readFileSync15(bodyFile, "utf8"), bodyPathHint: bodyFile };
11589
12115
  }
11590
12116
  const inline = args.body ? String(args.body) : void 0;
11591
12117
  if (inline) return { body: inline };
@@ -11736,7 +12262,7 @@ function formatMonitorTickNotice(tick) {
11736
12262
  }
11737
12263
 
11738
12264
  // src/monitor/monitor.service.ts
11739
- import path60 from "node:path";
12265
+ import path63 from "node:path";
11740
12266
 
11741
12267
  // src/monitor/monitor.classify.ts
11742
12268
  function classifyWorkerHealth(input) {
@@ -11788,19 +12314,19 @@ function classifyWorkerHealth(input) {
11788
12314
  }
11789
12315
 
11790
12316
  // src/monitor/monitor.store.ts
11791
- import { existsSync as existsSync41, mkdirSync as mkdirSync7, readdirSync as readdirSync15, unlinkSync as unlinkSync4 } from "node:fs";
11792
- import path59 from "node:path";
12317
+ import { existsSync as existsSync42, mkdirSync as mkdirSync9, readdirSync as readdirSync16, unlinkSync as unlinkSync5 } from "node:fs";
12318
+ import path62 from "node:path";
11793
12319
  function monitorsDir() {
11794
12320
  const { harnessRoot } = getHarnessPaths();
11795
- const dir = path59.join(harnessRoot, "monitors");
11796
- mkdirSync7(dir, { recursive: true });
12321
+ const dir = path62.join(harnessRoot, "monitors");
12322
+ mkdirSync9(dir, { recursive: true });
11797
12323
  return dir;
11798
12324
  }
11799
12325
  function monitorIdFor(runId, workerName) {
11800
12326
  return workerName ? `${safeSlug(runId)}--${safeSlug(workerName)}` : safeSlug(runId);
11801
12327
  }
11802
12328
  function monitorPath(monitorId) {
11803
- return path59.join(monitorsDir(), `${monitorId}.json`);
12329
+ return path62.join(monitorsDir(), `${monitorId}.json`);
11804
12330
  }
11805
12331
  function loadMonitorSession(monitorId) {
11806
12332
  return readJson(monitorPath(monitorId), void 0);
@@ -11810,18 +12336,18 @@ function saveMonitorSession(session) {
11810
12336
  }
11811
12337
  function deleteMonitorSession(monitorId) {
11812
12338
  const file = monitorPath(monitorId);
11813
- if (!existsSync41(file)) return false;
11814
- unlinkSync4(file);
12339
+ if (!existsSync42(file)) return false;
12340
+ unlinkSync5(file);
11815
12341
  return true;
11816
12342
  }
11817
12343
  function listMonitorSessions() {
11818
12344
  const dir = monitorsDir();
11819
- if (!existsSync41(dir)) return [];
12345
+ if (!existsSync42(dir)) return [];
11820
12346
  const entries = [];
11821
- for (const name of readdirSync15(dir)) {
12347
+ for (const name of readdirSync16(dir)) {
11822
12348
  if (!name.endsWith(".json")) continue;
11823
12349
  const session = readJson(
11824
- path59.join(dir, name),
12350
+ path62.join(dir, name),
11825
12351
  void 0
11826
12352
  );
11827
12353
  if (!session?.monitorId) continue;
@@ -11912,7 +12438,7 @@ async function fetchTaskLeasesForWorkers(input) {
11912
12438
  // src/monitor/monitor.service.ts
11913
12439
  function workerRecord2(runId, name) {
11914
12440
  return readJson(
11915
- path60.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
12441
+ path63.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
11916
12442
  void 0
11917
12443
  );
11918
12444
  }
@@ -12118,21 +12644,21 @@ async function runMonitorLoop(args) {
12118
12644
 
12119
12645
  // src/monitor/monitor-spawn.ts
12120
12646
  import { spawn as spawn6 } from "node:child_process";
12121
- import { closeSync as closeSync7, existsSync as existsSync42, openSync as openSync7 } from "node:fs";
12122
- import path61 from "node:path";
12647
+ import { closeSync as closeSync8, existsSync as existsSync43, openSync as openSync8 } from "node:fs";
12648
+ import path64 from "node:path";
12123
12649
  import { fileURLToPath as fileURLToPath3 } from "node:url";
12124
12650
  function resolveDefaultCliPath2() {
12125
- return path61.join(fileURLToPath3(new URL(".", import.meta.url)), "cli.js");
12651
+ return path64.join(fileURLToPath3(new URL(".", import.meta.url)), "cli.js");
12126
12652
  }
12127
12653
  function spawnMonitorSidecar(opts) {
12128
12654
  const cliPath = opts.cliPath ?? resolveDefaultCliPath2();
12129
- if (!existsSync42(cliPath)) return void 0;
12655
+ if (!existsSync43(cliPath)) return void 0;
12130
12656
  const monitorId = monitorIdFor(opts.runId, opts.workerName);
12131
12657
  const { harnessRoot } = getHarnessPaths();
12132
- const logPath = path61.join(harnessRoot, "monitors", `${monitorId}.log`);
12658
+ const logPath = path64.join(harnessRoot, "monitors", `${monitorId}.log`);
12133
12659
  let logFd;
12134
12660
  try {
12135
- logFd = openSync7(logPath, "a");
12661
+ logFd = openSync8(logPath, "a");
12136
12662
  } catch {
12137
12663
  logFd = void 0;
12138
12664
  }
@@ -12172,7 +12698,7 @@ function spawnMonitorSidecar(opts) {
12172
12698
  env: process.env
12173
12699
  })
12174
12700
  );
12175
- if (logFd !== void 0) closeSync7(logFd);
12701
+ if (logFd !== void 0) closeSync8(logFd);
12176
12702
  child.unref();
12177
12703
  const session = {
12178
12704
  monitorId,
@@ -12189,7 +12715,7 @@ function spawnMonitorSidecar(opts) {
12189
12715
  } catch {
12190
12716
  if (logFd !== void 0) {
12191
12717
  try {
12192
- closeSync7(logFd);
12718
+ closeSync8(logFd);
12193
12719
  } catch {
12194
12720
  }
12195
12721
  }
@@ -12249,13 +12775,13 @@ async function monitorTickCli(args) {
12249
12775
  }
12250
12776
 
12251
12777
  // src/package-version.ts
12252
- import { existsSync as existsSync43, readFileSync as readFileSync14 } from "node:fs";
12778
+ import { existsSync as existsSync44, readFileSync as readFileSync16 } from "node:fs";
12253
12779
  import { dirname, join } from "node:path";
12254
12780
  import { fileURLToPath as fileURLToPath4 } from "node:url";
12255
12781
  function resolvePackageRoot(moduleUrl) {
12256
12782
  let dir = dirname(fileURLToPath4(moduleUrl));
12257
12783
  for (let depth = 0; depth < 6; depth += 1) {
12258
- if (existsSync43(join(dir, "package.json"))) return dir;
12784
+ if (existsSync44(join(dir, "package.json"))) return dir;
12259
12785
  const parent = dirname(dir);
12260
12786
  if (parent === dir) break;
12261
12787
  dir = parent;
@@ -12264,7 +12790,7 @@ function resolvePackageRoot(moduleUrl) {
12264
12790
  }
12265
12791
  function readOwnPackageVersion(moduleUrl = import.meta.url) {
12266
12792
  const pkgPath = join(resolvePackageRoot(moduleUrl), "package.json");
12267
- const pkg = JSON.parse(readFileSync14(pkgPath, "utf8"));
12793
+ const pkg = JSON.parse(readFileSync16(pkgPath, "utf8"));
12268
12794
  if (typeof pkg.version !== "string" || !pkg.version.trim()) {
12269
12795
  throw new Error(`Missing package.json version at ${pkgPath}`);
12270
12796
  }
@@ -12330,8 +12856,32 @@ function shouldEnforceMemoryCostPackageGuardCli(scope, action) {
12330
12856
  return false;
12331
12857
  }
12332
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
+
12333
12883
  // src/post-restart-unblock.ts
12334
- import path62 from "node:path";
12884
+ import path65 from "node:path";
12335
12885
  function skip(runId, worker, taskId, agentOsId, leaseOwner, reason) {
12336
12886
  return { runId, worker, taskId, agentOsId, leaseOwner, action: "skipped", reason };
12337
12887
  }
@@ -12344,7 +12894,7 @@ async function postRestartUnblock(args) {
12344
12894
  const errors = [];
12345
12895
  for (const run of listRunRecords()) {
12346
12896
  for (const name of Object.keys(run.workers ?? {})) {
12347
- const workerPath = path62.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
12897
+ const workerPath = path65.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
12348
12898
  const worker = readJson(workerPath, void 0);
12349
12899
  if (!worker) {
12350
12900
  skipped.push(skip(run.id, name, "", "", "", "worker.json missing"));
@@ -12456,9 +13006,9 @@ async function postRestartUnblockCli(args) {
12456
13006
  }
12457
13007
 
12458
13008
  // src/default-repo-cli.ts
12459
- import path63 from "node:path";
13009
+ import path66 from "node:path";
12460
13010
  import { homedir as homedir14 } from "node:os";
12461
- var CONFIG_FILE2 = path63.join(homedir14(), ".kynver", "config.json");
13011
+ var CONFIG_FILE2 = path66.join(homedir14(), ".kynver", "config.json");
12462
13012
  function ensureDefaultRepo(opts) {
12463
13013
  const existing = loadUserConfig();
12464
13014
  const resolved = resolveDefaultRepo({ ...opts, config: existing });
@@ -12539,16 +13089,16 @@ function summarizeResolvedDefaultRepo(resolved) {
12539
13089
  }
12540
13090
 
12541
13091
  // src/doctor/runtime-takeover.ts
12542
- import path65 from "node:path";
13092
+ import path68 from "node:path";
12543
13093
 
12544
13094
  // src/doctor/runtime-takeover.probes.ts
12545
- import { accessSync, constants, existsSync as existsSync44, readFileSync as readFileSync15 } from "node:fs";
13095
+ import { accessSync, constants, existsSync as existsSync45, readFileSync as readFileSync17 } from "node:fs";
12546
13096
  import { homedir as homedir15 } from "node:os";
12547
- import path64 from "node:path";
12548
- import { spawnSync as spawnSync8 } from "node:child_process";
13097
+ import path67 from "node:path";
13098
+ import { spawnSync as spawnSync9 } from "node:child_process";
12549
13099
  function captureCommand(bin, args) {
12550
13100
  try {
12551
- const res = spawnSync8(bin, args, { encoding: "utf8" });
13101
+ const res = spawnSync9(bin, args, { encoding: "utf8" });
12552
13102
  const stdout = (res.stdout || "").trim();
12553
13103
  const stderr = (res.stderr || "").trim();
12554
13104
  const ok = res.status === 0;
@@ -12573,7 +13123,7 @@ function tokenPrefix(token) {
12573
13123
  return trimmed.length <= 12 ? `${trimmed}\u2026` : `${trimmed.slice(0, 12)}\u2026`;
12574
13124
  }
12575
13125
  function isWritable(target) {
12576
- if (!existsSync44(target)) return false;
13126
+ if (!existsSync45(target)) return false;
12577
13127
  try {
12578
13128
  accessSync(target, constants.W_OK);
12579
13129
  return true;
@@ -12586,15 +13136,15 @@ var defaultRuntimeTakeoverProbes = {
12586
13136
  commandOnPath: (bin) => captureCommand(process.platform === "win32" ? "where" : "which", [bin]),
12587
13137
  kynverVersion: (bin) => captureCommand(bin, ["--version"]),
12588
13138
  loadConfig: () => loadUserConfig(),
12589
- configFilePath: () => path64.join(homedir15(), ".kynver", "config.json"),
12590
- credentialsFilePath: () => path64.join(homedir15(), ".kynver", "credentials"),
13139
+ configFilePath: () => path67.join(homedir15(), ".kynver", "config.json"),
13140
+ credentialsFilePath: () => path67.join(homedir15(), ".kynver", "credentials"),
12591
13141
  readCredentials: () => {
12592
- const credPath = path64.join(homedir15(), ".kynver", "credentials");
12593
- if (!existsSync44(credPath)) {
13142
+ const credPath = path67.join(homedir15(), ".kynver", "credentials");
13143
+ if (!existsSync45(credPath)) {
12594
13144
  return { hasApiKey: false };
12595
13145
  }
12596
13146
  try {
12597
- const parsed = JSON.parse(readFileSync15(credPath, "utf8"));
13147
+ const parsed = JSON.parse(readFileSync17(credPath, "utf8"));
12598
13148
  return {
12599
13149
  hasApiKey: Boolean(parsed.apiKey?.trim()),
12600
13150
  runnerTokenPrefix: tokenPrefix(parsed.runnerToken),
@@ -12624,8 +13174,8 @@ var defaultRuntimeTakeoverProbes = {
12624
13174
  })()
12625
13175
  }),
12626
13176
  harnessRoot: () => resolveHarnessRoot(),
12627
- legacyOpenclawHarnessRoot: () => path64.join(homedir15(), ".openclaw", "harness"),
12628
- pathExists: (target) => existsSync44(target),
13177
+ legacyOpenclawHarnessRoot: () => path67.join(homedir15(), ".openclaw", "harness"),
13178
+ pathExists: (target) => existsSync45(target),
12629
13179
  pathWritable: (target) => isWritable(target)
12630
13180
  };
12631
13181
 
@@ -13031,8 +13581,8 @@ function assessVercelDeployEvidence(probes) {
13031
13581
  }
13032
13582
  function assessHarnessDirs(probes) {
13033
13583
  const harnessRoot = probes.harnessRoot();
13034
- const runsDir = path65.join(harnessRoot, "runs");
13035
- const worktreesDir = path65.join(harnessRoot, "worktrees");
13584
+ const runsDir = path68.join(harnessRoot, "runs");
13585
+ const worktreesDir = path68.join(harnessRoot, "worktrees");
13036
13586
  const displayHarnessRoot = redactHomePath(harnessRoot);
13037
13587
  const displayRunsDir = redactHomePath(runsDir);
13038
13588
  const displayWorktreesDir = redactHomePath(worktreesDir);
@@ -13296,9 +13846,9 @@ function applySchedulerCutoverAttestation(config) {
13296
13846
  }
13297
13847
 
13298
13848
  // src/scheduler-cutover-cli.ts
13299
- import path66 from "node:path";
13849
+ import path69 from "node:path";
13300
13850
  import { homedir as homedir16 } from "node:os";
13301
- var CONFIG_FILE3 = path66.join(homedir16(), ".kynver", "config.json");
13851
+ var CONFIG_FILE3 = path69.join(homedir16(), ".kynver", "config.json");
13302
13852
  function runSchedulerCutoverCheckCli(json = false) {
13303
13853
  const config = loadUserConfig();
13304
13854
  const report = assessSchedulerCutover(config);
@@ -13435,6 +13985,157 @@ async function runCronTickCli(args) {
13435
13985
  );
13436
13986
  }
13437
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
+
13438
14139
  // src/cli.ts
13439
14140
  function isHelpFlag(arg) {
13440
14141
  return arg === "help" || arg === "--help" || arg === "-h";
@@ -13455,6 +14156,7 @@ function usage(code = 0) {
13455
14156
  " kynver daemon --run RUN_ID --agent-os-id AOS_ID [--execute] [--interval-ms MS]",
13456
14157
  " kynver run create [--repo /path/repo] [--name name] [--base origin/main]",
13457
14158
  " kynver run list",
14159
+ " kynver run resolve --name RUN_NAME",
13458
14160
  " kynver run status --run RUN_ID [--json] [--compact]",
13459
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]",
13460
14162
  " kynver run sweep --run RUN_ID --agent-os-id AOS_ID [--base-url URL] [--secret SECRET] [--grace-ms MS]",
@@ -13490,6 +14192,7 @@ function usage(code = 0) {
13490
14192
  " kynver scheduler attest-cutover [--json]",
13491
14193
  " kynver cron status [--json]",
13492
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]",
13493
14196
  " kynver board contract [--agent-os-id ID] [--base-url URL] [--since ISO] [--limit N]"
13494
14197
  ].join("\n")
13495
14198
  );
@@ -13501,7 +14204,7 @@ async function main(argv = process.argv.slice(2)) {
13501
14204
  const scope = argv.shift();
13502
14205
  let action;
13503
14206
  let rest;
13504
- 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") {
13505
14208
  action = argv.shift();
13506
14209
  rest = argv;
13507
14210
  } else {
@@ -13510,8 +14213,8 @@ async function main(argv = process.argv.slice(2)) {
13510
14213
  if (action && isHelpFlag(action) || rest.some(isHelpFlag)) return usage(0);
13511
14214
  const args = parseArgs(rest);
13512
14215
  const { runsDir, worktreesDir } = getPaths();
13513
- mkdirSync8(runsDir, { recursive: true });
13514
- mkdirSync8(worktreesDir, { recursive: true });
14216
+ mkdirSync10(runsDir, { recursive: true });
14217
+ mkdirSync10(worktreesDir, { recursive: true });
13515
14218
  if (shouldEnforceMemoryCostPackageGuardCli(scope, action)) {
13516
14219
  let repoRoot;
13517
14220
  const runId = args.run ? String(args.run).trim() : "";
@@ -13558,11 +14261,16 @@ async function main(argv = process.argv.slice(2)) {
13558
14261
  if (scope === "cron" && action === "tick") {
13559
14262
  return void await runCronTickCli(args);
13560
14263
  }
14264
+ if (scope === "lane" && action === "tick") {
14265
+ const laneName = rest.shift();
14266
+ return void await runLaneTickCli(parseArgs(rest), laneName);
14267
+ }
13561
14268
  if (scope === "board" && action === "contract") {
13562
14269
  return void await runCommandCenterContractCli(args);
13563
14270
  }
13564
14271
  if (scope === "run" && action === "create") return createRun(args);
13565
14272
  if (scope === "run" && action === "list") return listRuns();
14273
+ if (scope === "run" && action === "resolve") return resolveHarnessRunCli(args);
13566
14274
  if (scope === "run" && action === "status") return runStatus(args);
13567
14275
  if (scope === "run" && action === "dispatch") return void await dispatchRun(args);
13568
14276
  if (scope === "run" && action === "sweep") return void await sweepRun(args);