@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/index.js CHANGED
@@ -119,6 +119,9 @@ function readMaybeFile(file) {
119
119
  function sleepMs(ms) {
120
120
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
121
121
  }
122
+ function sleepMsAsync(ms) {
123
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
124
+ }
122
125
  function isPidAlive(pid) {
123
126
  if (!pid) return false;
124
127
  try {
@@ -725,7 +728,7 @@ function shouldEnforceMemoryCostPackageGuardCli(scope, action) {
725
728
  }
726
729
 
727
730
  // src/config.ts
728
- import { existsSync as existsSync11, mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync2 } from "node:fs";
731
+ import { existsSync as existsSync11, mkdirSync as mkdirSync2, readFileSync as readFileSync10, writeFileSync as writeFileSync2 } from "node:fs";
729
732
  import { homedir as homedir5, totalmem } from "node:os";
730
733
  import path10 from "node:path";
731
734
 
@@ -827,23 +830,23 @@ function isWslHost() {
827
830
  function observeWslHostDisk(options = {}) {
828
831
  const wsl = options.forceWsl === void 0 ? isWslHost() : options.forceWsl;
829
832
  if (!wsl) return null;
830
- const path69 = options.wslHostMount?.trim() || process.env.KYNVER_WSL_HOST_MOUNT?.trim() || DEFAULT_WSL_HOST_MOUNT;
833
+ const path73 = options.wslHostMount?.trim() || process.env.KYNVER_WSL_HOST_MOUNT?.trim() || DEFAULT_WSL_HOST_MOUNT;
831
834
  const warnBelowBytes = options.wslHostFreeWarnBytes ?? DEFAULT_WSL_HOST_WARN_FREE_BYTES;
832
835
  const criticalBelowBytes = options.wslHostFreeCriticalBytes ?? DEFAULT_WSL_HOST_CRITICAL_FREE_BYTES;
833
836
  const statfs = options.statfs ?? statfsSync;
834
837
  let stats;
835
838
  try {
836
- stats = statfs(path69);
839
+ stats = statfs(path73);
837
840
  } catch (error) {
838
841
  return {
839
842
  ok: false,
840
- path: path69,
843
+ path: path73,
841
844
  freeBytes: 0,
842
845
  totalBytes: 0,
843
846
  usedPercent: 100,
844
847
  warnBelowBytes,
845
848
  criticalBelowBytes,
846
- reason: `Windows host disk probe failed at ${path69}: ${error.message}`,
849
+ reason: `Windows host disk probe failed at ${path73}: ${error.message}`,
847
850
  probeError: error.message
848
851
  };
849
852
  }
@@ -857,11 +860,11 @@ function observeWslHostDisk(options = {}) {
857
860
  let reason = null;
858
861
  if (!ok) {
859
862
  const tag = criticalFree ? "critical" : "warning";
860
- reason = `Windows host disk ${path69} at ${tag}: ${freeGiB} GiB free (<${(criticalFree ? criticalBelowBytes : warnBelowBytes) / 1024 / 1024 / 1024} GiB); WSL VHDX cannot grow safely. ${summarizeWslRecoverySteps()}`;
863
+ reason = `Windows host disk ${path73} at ${tag}: ${freeGiB} GiB free (<${(criticalFree ? criticalBelowBytes : warnBelowBytes) / 1024 / 1024 / 1024} GiB); WSL VHDX cannot grow safely. ${summarizeWslRecoverySteps()}`;
861
864
  }
862
865
  return {
863
866
  ok,
864
- path: path69,
867
+ path: path73,
865
868
  freeBytes,
866
869
  totalBytes,
867
870
  usedPercent,
@@ -881,12 +884,12 @@ var DEFAULT_CRITICAL_FREE_BYTES = 15 * 1024 * 1024 * 1024;
881
884
  var DEFAULT_MAX_USED_PERCENT = 80;
882
885
  var DEFAULT_HARD_MAX_USED_PERCENT = 90;
883
886
  function observeRunnerDiskGate(input = {}) {
884
- const path69 = input.diskPath?.trim() || "/";
887
+ const path73 = input.diskPath?.trim() || "/";
885
888
  const warnBelowBytes = input.diskFreeWarnBytes ?? DEFAULT_WARN_FREE_BYTES;
886
889
  const criticalBelowBytes = input.diskFreeCriticalBytes ?? DEFAULT_CRITICAL_FREE_BYTES;
887
890
  const maxUsedPercent = input.diskMaxUsedPercent ?? DEFAULT_MAX_USED_PERCENT;
888
891
  const hardMaxUsedPercent = input.diskHardMaxUsedPercent ?? DEFAULT_HARD_MAX_USED_PERCENT;
889
- const stats = statfsSync2(path69);
892
+ const stats = statfsSync2(path73);
890
893
  const freeBytes = Number(stats.bavail) * Number(stats.bsize);
891
894
  const totalBytes = Number(stats.blocks) * Number(stats.bsize);
892
895
  const usedPercent = totalBytes > 0 ? (totalBytes - freeBytes) / totalBytes * 100 : 100;
@@ -909,7 +912,7 @@ function observeRunnerDiskGate(input = {}) {
909
912
  }
910
913
  return {
911
914
  ok,
912
- path: path69,
915
+ path: path73,
913
916
  freeBytes,
914
917
  totalBytes,
915
918
  usedPercent,
@@ -1043,6 +1046,9 @@ function listRunWorkerNames(run) {
1043
1046
  return [...names];
1044
1047
  }
1045
1048
 
1049
+ // src/harness-worker-active.ts
1050
+ import { readFileSync as readFileSync9 } from "node:fs";
1051
+
1046
1052
  // src/heartbeat.ts
1047
1053
  import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:fs";
1048
1054
 
@@ -2126,6 +2132,9 @@ function computeAttention(input) {
2126
2132
  return { state: "blocked", reason: input.completionBlocker };
2127
2133
  }
2128
2134
  if (input.finalResult) {
2135
+ if (input.localOnly && hasMergedTargetPrReconciliation(input.finalResult)) {
2136
+ return { state: "done", reason: "local-only worker superseded by merged PR" };
2137
+ }
2129
2138
  const landingSnapshot = {
2130
2139
  finalResult: input.finalResult,
2131
2140
  changedFiles: input.changedFiles ?? [],
@@ -2191,9 +2200,24 @@ function computeAttention(input) {
2191
2200
  }
2192
2201
  return { state: "ok", reason: "recent activity" };
2193
2202
  }
2203
+ function hasMergedTargetPrReconciliation(value) {
2204
+ let record3 = null;
2205
+ if (typeof value === "string") record3 = extractEmbeddedWorkerFinalResultRecord(value);
2206
+ else if (value && typeof value === "object" && !Array.isArray(value)) record3 = value;
2207
+ if (!record3) return false;
2208
+ const raw = record3.targetPrReconciliation ?? record3.target_pr_reconciliation;
2209
+ if (!Array.isArray(raw)) return false;
2210
+ return raw.some((item) => {
2211
+ if (!item || typeof item !== "object" || Array.isArray(item)) return false;
2212
+ return String(item.outcome ?? "").trim() === "merged";
2213
+ });
2214
+ }
2194
2215
  function resolveFinalResult(worker, parsedFinalResult, heartbeat) {
2195
- if (parsedFinalResult) return parsedFinalResult;
2196
2216
  const ackSnapshot = worker.completionSnapshot?.finalResult;
2217
+ if (worker.completionAckSource === "local-pr-merged-reconcile" && ackSnapshot !== void 0 && ackSnapshot !== null) {
2218
+ return ackSnapshot;
2219
+ }
2220
+ if (parsedFinalResult) return parsedFinalResult;
2197
2221
  if (ackSnapshot !== void 0 && ackSnapshot !== null) return ackSnapshot;
2198
2222
  return terminalFinalResultFromHeartbeat(heartbeat);
2199
2223
  }
@@ -2240,7 +2264,8 @@ function computeWorkerStatus(worker, options = {}) {
2240
2264
  gitAncestry,
2241
2265
  completionBlocker,
2242
2266
  landingContract,
2243
- prUrl: worker.repairTargetPrUrl ?? worker.taskPrUrl ?? null
2267
+ prUrl: worker.repairTargetPrUrl ?? worker.taskPrUrl ?? null,
2268
+ localOnly: worker.localOnly === true
2244
2269
  });
2245
2270
  const workerStatusLabel = completionBlocker || attention.state === "blocked" ? "blocked" : completionAcknowledged || attention.state === "done" ? "done" : finalResult ? "exited" : alive ? "running" : "exited";
2246
2271
  return {
@@ -2294,6 +2319,33 @@ function deriveRunStatus(fallback, workers) {
2294
2319
  return fallback;
2295
2320
  }
2296
2321
 
2322
+ // src/harness-worker-active.ts
2323
+ function pidCommandLine(pid) {
2324
+ if (!pid || process.platform !== "linux") return null;
2325
+ try {
2326
+ return readFileSync9(`/proc/${pid}/cmdline`, "utf8").replace(/\0/g, " ");
2327
+ } catch {
2328
+ return null;
2329
+ }
2330
+ }
2331
+ function workerProcessMatchesRecord(worker) {
2332
+ if (!worker.pid || process.platform !== "linux") return true;
2333
+ const cmdline = pidCommandLine(worker.pid);
2334
+ if (!cmdline) return false;
2335
+ const probes = [worker.worktreePath, worker.workerDir, worker.heartbeatPath].filter(
2336
+ (value) => typeof value === "string" && value.trim().length > 0
2337
+ );
2338
+ return probes.some((probe) => cmdline.includes(probe));
2339
+ }
2340
+ function isActiveHarnessWorker(worker) {
2341
+ if (typeof worker.completionBlocker === "string" && worker.completionBlocker.trim()) {
2342
+ return false;
2343
+ }
2344
+ const status = computeWorkerStatus(worker);
2345
+ if (status.alive && !workerProcessMatchesRecord(worker)) return false;
2346
+ return status.alive && !status.finalResult && status.attention.state !== "done";
2347
+ }
2348
+
2297
2349
  // src/resource-gate.ts
2298
2350
  var DEFAULT_PER_WORKER_MEM_BYTES = 500 * 1024 * 1024;
2299
2351
  var DEFAULT_MEM_RESERVE_BYTES = 4 * 1024 * 1024 * 1024;
@@ -2336,13 +2388,6 @@ function computeAutoMaxWorkers(totalMemBytes, opts = {}) {
2336
2388
  function readAvailableMemBytes() {
2337
2389
  return readMemAvailableBytes();
2338
2390
  }
2339
- function isActiveHarnessWorker(worker) {
2340
- if (typeof worker.completionBlocker === "string" && worker.completionBlocker.trim()) {
2341
- return false;
2342
- }
2343
- const status = computeWorkerStatus(worker);
2344
- return status.alive && !status.finalResult && status.attention.state !== "done";
2345
- }
2346
2391
  function countActiveWorkersForRun(run) {
2347
2392
  let active = 0;
2348
2393
  for (const name of listRunWorkerNames(run)) {
@@ -2491,7 +2536,7 @@ var CREDENTIALS_FILE = path10.join(CONFIG_DIR, "credentials");
2491
2536
  function loadUserConfig() {
2492
2537
  if (!existsSync11(CONFIG_FILE)) return {};
2493
2538
  try {
2494
- return JSON.parse(readFileSync9(CONFIG_FILE, "utf8"));
2539
+ return JSON.parse(readFileSync10(CONFIG_FILE, "utf8"));
2495
2540
  } catch {
2496
2541
  return {};
2497
2542
  }
@@ -2568,7 +2613,7 @@ function resolveSetupWorkerConfig(existing, args, totalMemBytes = totalmem()) {
2568
2613
  function loadCredentialsFile() {
2569
2614
  if (!existsSync11(CREDENTIALS_FILE)) return {};
2570
2615
  try {
2571
- return JSON.parse(readFileSync9(CREDENTIALS_FILE, "utf8"));
2616
+ return JSON.parse(readFileSync10(CREDENTIALS_FILE, "utf8"));
2572
2617
  } catch {
2573
2618
  return {};
2574
2619
  }
@@ -2845,15 +2890,30 @@ async function withTimeout(fn) {
2845
2890
  clearTimeout(timeout);
2846
2891
  }
2847
2892
  }
2893
+ function callbackFetchError(error) {
2894
+ return {
2895
+ ok: false,
2896
+ status: 0,
2897
+ response: {
2898
+ error: error instanceof Error ? error.message : String(error),
2899
+ timeoutMs: callbackTimeoutMs()
2900
+ }
2901
+ };
2902
+ }
2848
2903
  async function postJson(url, secret, body) {
2849
- const res = await withTimeout(
2850
- (signal) => fetch(url, {
2851
- method: "POST",
2852
- headers: buildHarnessCallbackHeaders(secret),
2853
- body: JSON.stringify(body),
2854
- signal
2855
- })
2856
- );
2904
+ let res;
2905
+ try {
2906
+ res = await withTimeout(
2907
+ (signal) => fetch(url, {
2908
+ method: "POST",
2909
+ headers: buildHarnessCallbackHeaders(secret),
2910
+ body: JSON.stringify(body),
2911
+ signal
2912
+ })
2913
+ );
2914
+ } catch (error) {
2915
+ return callbackFetchError(error);
2916
+ }
2857
2917
  let response = null;
2858
2918
  try {
2859
2919
  response = await res.json();
@@ -2871,13 +2931,18 @@ async function postJsonWithCredentialRefresh(url, secret, body, opts) {
2871
2931
  return { ...retry, refreshedAuth: true };
2872
2932
  }
2873
2933
  async function getJson(url, secret) {
2874
- const res = await withTimeout(
2875
- (signal) => fetch(url, {
2876
- method: "GET",
2877
- headers: buildHarnessCallbackHeaders(secret),
2878
- signal
2879
- })
2880
- );
2934
+ let res;
2935
+ try {
2936
+ res = await withTimeout(
2937
+ (signal) => fetch(url, {
2938
+ method: "GET",
2939
+ headers: buildHarnessCallbackHeaders(secret),
2940
+ signal
2941
+ })
2942
+ );
2943
+ } catch (error) {
2944
+ return callbackFetchError(error);
2945
+ }
2881
2946
  let response = null;
2882
2947
  try {
2883
2948
  response = await res.json();
@@ -2887,41 +2952,6 @@ async function getJson(url, secret) {
2887
2952
  return { ok: res.ok, status: res.status, response };
2888
2953
  }
2889
2954
 
2890
- // src/dispatch-lane-normalization.ts
2891
- function trimLower(value) {
2892
- return (value ?? "").trim().toLowerCase();
2893
- }
2894
- function roleLaneToDispatchLane(roleLane) {
2895
- switch (trimLower(roleLane)) {
2896
- case "implementer":
2897
- case "repair_implementer":
2898
- case "plan_author":
2899
- case "runtime_verifier":
2900
- return "implementation";
2901
- case "plan_reviewer":
2902
- case "report_reviewer":
2903
- case "deep_reviewer":
2904
- return "review";
2905
- default:
2906
- return null;
2907
- }
2908
- }
2909
- function normalizeDispatchNextLaneFilter(raw) {
2910
- const key = trimLower(raw);
2911
- if (!key) return void 0;
2912
- if (key === "implementation" || key === "review" || key === "landing" || key === "any") {
2913
- return key;
2914
- }
2915
- const mapped = roleLaneToDispatchLane(key);
2916
- if (mapped) return mapped;
2917
- if (key === "implement" || key === "repair" || key === "coding") return "implementation";
2918
- if (key === "land" || key === "merge") return "landing";
2919
- return void 0;
2920
- }
2921
- function resolveDispatchNextLaneFilter(raw) {
2922
- return normalizeDispatchNextLaneFilter(raw) ?? "any";
2923
- }
2924
-
2925
2955
  // src/worker-persona-catalog.ts
2926
2956
  var WORKER_PERSONA_CATALOG = [
2927
2957
  {
@@ -3036,6 +3066,41 @@ function workerPersonaLandingSlugs() {
3036
3066
  );
3037
3067
  }
3038
3068
 
3069
+ // src/dispatch-lane-normalization.ts
3070
+ function trimLower(value) {
3071
+ return (value ?? "").trim().toLowerCase();
3072
+ }
3073
+ function roleLaneToDispatchLane(roleLane) {
3074
+ switch (trimLower(roleLane)) {
3075
+ case "implementer":
3076
+ case "repair_implementer":
3077
+ case "plan_author":
3078
+ case "runtime_verifier":
3079
+ return "implementation";
3080
+ case "plan_reviewer":
3081
+ case "report_reviewer":
3082
+ case "deep_reviewer":
3083
+ return "review";
3084
+ default:
3085
+ return null;
3086
+ }
3087
+ }
3088
+ function normalizeDispatchNextLaneFilter(raw) {
3089
+ const key = trimLower(raw);
3090
+ if (!key) return void 0;
3091
+ if (key === "implementation" || key === "review" || key === "landing" || key === "any") {
3092
+ return key;
3093
+ }
3094
+ const mapped = roleLaneToDispatchLane(key);
3095
+ if (mapped) return mapped;
3096
+ if (key === "implement" || key === "repair" || key === "coding") return "implementation";
3097
+ if (key === "land" || key === "merge") return "landing";
3098
+ return void 0;
3099
+ }
3100
+ function resolveDispatchNextLaneFilter(raw) {
3101
+ return normalizeDispatchNextLaneFilter(raw) ?? "any";
3102
+ }
3103
+
3039
3104
  // src/model-routing-task-enrich.ts
3040
3105
  function taskString(task, key) {
3041
3106
  const v = task[key];
@@ -3503,7 +3568,7 @@ function probeCursorOAuthBinding(nowIso = (/* @__PURE__ */ new Date()).toISOStri
3503
3568
  }
3504
3569
 
3505
3570
  // src/orchestration-providers/hermes-cli-adapter.ts
3506
- import { existsSync as existsSync16, readFileSync as readFileSync10 } from "node:fs";
3571
+ import { existsSync as existsSync16, readFileSync as readFileSync11 } from "node:fs";
3507
3572
  import { homedir as homedir10 } from "node:os";
3508
3573
  import path15 from "node:path";
3509
3574
  var PROFILE_ENV_KEYS = [
@@ -3519,7 +3584,7 @@ function hermesProfileEnvPath() {
3519
3584
  }
3520
3585
  function profileEnvKeyPresence(envPath) {
3521
3586
  try {
3522
- const text = readFileSync10(envPath, "utf8");
3587
+ const text = readFileSync11(envPath, "utf8");
3523
3588
  const present = [];
3524
3589
  for (const key of PROFILE_ENV_KEYS) {
3525
3590
  const re = new RegExp(`^${key}=`, "m");
@@ -4493,7 +4558,7 @@ function buildPrompt(input) {
4493
4558
  "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.",
4494
4559
  "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.",
4495
4560
  "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.",
4496
- "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.",
4561
+ "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.",
4497
4562
  "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.",
4498
4563
  "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.",
4499
4564
  "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.",
@@ -5369,8 +5434,8 @@ function dirtyPathsCoveredByDisposableRemoval(changedFiles, removed) {
5369
5434
  if (removed.length === 0) return false;
5370
5435
  const removedSet = new Set(removed.map((p) => normalizeRelativePath(p)));
5371
5436
  return material.every((line) => {
5372
- const path69 = normalizeRelativePath(pathFromGitStatusLine(line));
5373
- return removedSet.has(path69);
5437
+ const path73 = normalizeRelativePath(pathFromGitStatusLine(line));
5438
+ return removedSet.has(path73);
5374
5439
  });
5375
5440
  }
5376
5441
 
@@ -6678,7 +6743,7 @@ function collectRunActiveHarnessWorkers(runId) {
6678
6743
  path24.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
6679
6744
  void 0
6680
6745
  );
6681
- if (!worker?.taskId || !isPidAlive(worker.pid)) continue;
6746
+ if (!worker?.taskId || !isActiveHarnessWorker(worker)) continue;
6682
6747
  out.push({
6683
6748
  runId: run.id,
6684
6749
  workerName: name,
@@ -6917,7 +6982,7 @@ function isTmpOnlyPath(filePath) {
6917
6982
  // src/plan-persist/outbox-store.ts
6918
6983
  import {
6919
6984
  existsSync as existsSync23,
6920
- readFileSync as readFileSync11,
6985
+ readFileSync as readFileSync12,
6921
6986
  renameSync,
6922
6987
  readdirSync as readdirSync5,
6923
6988
  writeFileSync as writeFileSync3,
@@ -6945,7 +7010,7 @@ function findOutboxByIdempotencyKey(key) {
6945
7010
  function readOutboxItem(jsonPath) {
6946
7011
  if (!existsSync23(jsonPath)) return null;
6947
7012
  try {
6948
- return JSON.parse(readFileSync11(jsonPath, "utf8"));
7013
+ return JSON.parse(readFileSync12(jsonPath, "utf8"));
6949
7014
  } catch {
6950
7015
  return null;
6951
7016
  }
@@ -6953,7 +7018,7 @@ function readOutboxItem(jsonPath) {
6953
7018
  function readOutboxBody(item) {
6954
7019
  const { outboxDir } = ensurePlanOutboxDirs();
6955
7020
  const bodyFile = path26.join(outboxDir, item.bodyPath);
6956
- return readFileSync11(bodyFile, "utf8");
7021
+ return readFileSync12(bodyFile, "utf8");
6957
7022
  }
6958
7023
  function writeOutboxItem(input, opts) {
6959
7024
  const { outboxDir } = ensurePlanOutboxDirs();
@@ -7539,7 +7604,7 @@ async function dispatchRun(args) {
7539
7604
  );
7540
7605
  }
7541
7606
  const attempt = Number(task.attempt) || 1;
7542
- if (attempt >= retryLimits.maxTaskAttempts) {
7607
+ if (attempt > retryLimits.maxTaskAttempts) {
7543
7608
  return abortClaimedSpawn(
7544
7609
  task,
7545
7610
  `task attempt ${attempt} exceeds KYNVER_MAX_TASK_ATTEMPTS (${retryLimits.maxTaskAttempts})`
@@ -7675,7 +7740,12 @@ async function dispatchRun(args) {
7675
7740
  const admissionExhaustion = readAdmissionExhaustion(result);
7676
7741
  const capacityIdle = admissionExhaustion?.capacityIdle === true || startedCount === 0 && Number(result.resourceGate?.slotsAvailable) > 0;
7677
7742
  if (capacityIdle && admissionExhaustion?.summary) {
7678
- console.error(`[dispatch] ${admissionExhaustion.summary}`);
7743
+ const retryCeiling = admissionExhaustion.skipReasonCounts?.retry_ceiling_exceeded ?? 0;
7744
+ const recovery = admissionExhaustion.overAttemptIdleRecovery;
7745
+ const recoveryNote = recovery?.attempted === true ? `; over_attempt_recovery minted=${recovery.minted ?? 0} started=${recovery.started ?? 0}` : retryCeiling > 0 ? "; over_attempt_recovery not attempted" : "";
7746
+ console.error(
7747
+ `[dispatch] ${admissionExhaustion.summary}${retryCeiling > 0 ? `; retry_ceiling_exceeded=${retryCeiling}` : ""}${recoveryNote}`
7748
+ );
7679
7749
  }
7680
7750
  const summary = {
7681
7751
  runId: run.id,
@@ -8260,14 +8330,14 @@ function applyProductionDatabaseToProcess(options = {}) {
8260
8330
 
8261
8331
  // src/worktree.ts
8262
8332
  import { existsSync as existsSync29, mkdirSync as mkdirSync6 } from "node:fs";
8263
- import path37 from "node:path";
8333
+ import path38 from "node:path";
8264
8334
 
8265
8335
  // src/run-list.ts
8266
- import { existsSync as existsSync28, readFileSync as readFileSync14 } from "node:fs";
8267
- import path36 from "node:path";
8336
+ import { existsSync as existsSync28, readFileSync as readFileSync15 } from "node:fs";
8337
+ import path37 from "node:path";
8268
8338
 
8269
8339
  // src/stale-reconcile.ts
8270
- import path35 from "node:path";
8340
+ import path36 from "node:path";
8271
8341
 
8272
8342
  // src/finalize.ts
8273
8343
  import path30 from "node:path";
@@ -8902,6 +8972,193 @@ function reconcileWorkerMetadataCli() {
8902
8972
  );
8903
8973
  }
8904
8974
 
8975
+ // src/local-pr-attention-reconcile.ts
8976
+ import { execFileSync as execFileSync2 } from "node:child_process";
8977
+ import path35 from "node:path";
8978
+ function normalizePrUrl3(url) {
8979
+ const m = url.trim().match(/github\.com\/([^/]+\/[^/]+)\/(?:pull|pulls)\/(\d+)/i);
8980
+ if (!m) return null;
8981
+ return `https://github.com/${m[1]}/pull/${m[2]}`;
8982
+ }
8983
+ function prNumberFromUrl(url) {
8984
+ const m = normalizePrUrl3(url)?.match(/\/pull\/(\d+)$/);
8985
+ if (!m) return null;
8986
+ const n = Number.parseInt(m[1], 10);
8987
+ return Number.isFinite(n) ? n : null;
8988
+ }
8989
+ function extractText(value) {
8990
+ if (value == null) return "";
8991
+ if (typeof value === "string") return value;
8992
+ try {
8993
+ return JSON.stringify(value);
8994
+ } catch {
8995
+ return "";
8996
+ }
8997
+ }
8998
+ function extractPrNumbersFromText(text) {
8999
+ const out = /* @__PURE__ */ new Set();
9000
+ for (const match of text.matchAll(/github\.com\/[^/\s)>"']+\/[^/\s)>"']+\/(?:pull|pulls)\/(\d+)/gi)) {
9001
+ const n = Number.parseInt(match[1], 10);
9002
+ if (Number.isFinite(n)) out.add(n);
9003
+ }
9004
+ for (const match of text.matchAll(/\bPR\s*#?\s*(\d{2,})\b/gi)) {
9005
+ const n = Number.parseInt(match[1], 10);
9006
+ if (Number.isFinite(n)) out.add(n);
9007
+ }
9008
+ for (const match of text.matchAll(/\bpr[-_]?(\d{2,})\b/gi)) {
9009
+ const n = Number.parseInt(match[1], 10);
9010
+ if (Number.isFinite(n)) out.add(n);
9011
+ }
9012
+ return [...out];
9013
+ }
9014
+ function extractTargetPrReconciliation(value) {
9015
+ let record3 = null;
9016
+ if (typeof value === "string") record3 = extractEmbeddedWorkerFinalResultRecord(value);
9017
+ else if (value && typeof value === "object" && !Array.isArray(value)) record3 = value;
9018
+ if (!record3) return [];
9019
+ const raw = record3.targetPrReconciliation ?? record3.target_pr_reconciliation;
9020
+ if (!Array.isArray(raw)) return [];
9021
+ const out = [];
9022
+ for (const item of raw) {
9023
+ if (!item || typeof item !== "object" || Array.isArray(item)) continue;
9024
+ const row = item;
9025
+ const prUrl = normalizePrUrl3(String(row.prUrl ?? row.pr_url ?? ""));
9026
+ const outcome = String(row.outcome ?? "").trim();
9027
+ if (!prUrl || outcome !== "merged") continue;
9028
+ out.push({
9029
+ prUrl,
9030
+ mergeCommit: typeof row.mergeCommit === "string" ? row.mergeCommit : typeof row.merge_commit === "string" ? row.merge_commit : null,
9031
+ reason: typeof row.reason === "string" ? row.reason : null
9032
+ });
9033
+ }
9034
+ return out;
9035
+ }
9036
+ function defaultLookupPr(input) {
9037
+ try {
9038
+ const repo = execFileSync2("git", ["config", "--get", "remote.origin.url"], {
9039
+ cwd: input.repoDir,
9040
+ encoding: "utf8",
9041
+ stdio: ["ignore", "pipe", "ignore"]
9042
+ }).trim();
9043
+ const repoMatch = repo.match(/github\.com[:/]([^/]+\/[^/.]+)(?:\.git)?$/i);
9044
+ const repoSlug = repoMatch?.[1];
9045
+ if (!repoSlug) return null;
9046
+ const raw = execFileSync2(
9047
+ "gh",
9048
+ ["pr", "view", String(input.prNumber), "--repo", repoSlug, "--json", "state,mergedAt,mergeCommit,url"],
9049
+ { cwd: input.repoDir, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
9050
+ );
9051
+ const parsed = JSON.parse(raw);
9052
+ return {
9053
+ prUrl: normalizePrUrl3(parsed.url ?? `https://github.com/${repoSlug}/pull/${input.prNumber}`) ?? `https://github.com/${repoSlug}/pull/${input.prNumber}`,
9054
+ state: parsed.state ?? "",
9055
+ mergedAt: parsed.mergedAt ?? null,
9056
+ mergeCommit: parsed.mergeCommit?.oid ?? null
9057
+ };
9058
+ } catch {
9059
+ return null;
9060
+ }
9061
+ }
9062
+ function finalResultForWorker(worker) {
9063
+ const heartbeat = parseHeartbeat(worker.heartbeatPath);
9064
+ return worker.completionSnapshot?.finalResult ?? terminalFinalResultFromHeartbeat(heartbeat);
9065
+ }
9066
+ function collectMergedEvidenceByPr(run) {
9067
+ const byPr = /* @__PURE__ */ new Map();
9068
+ const runDir2 = runDirectory(run.id);
9069
+ for (const name of listRunWorkerNames(run)) {
9070
+ const worker = readJson(
9071
+ path35.join(runDir2, "workers", safeSlug(name), "worker.json"),
9072
+ void 0
9073
+ );
9074
+ if (!worker) continue;
9075
+ for (const evidence of extractTargetPrReconciliation(finalResultForWorker(worker))) {
9076
+ const n = prNumberFromUrl(evidence.prUrl);
9077
+ if (n != null && !byPr.has(n)) byPr.set(n, evidence);
9078
+ }
9079
+ }
9080
+ return byPr;
9081
+ }
9082
+ function candidatePrNumbers(worker, statusFinalResult) {
9083
+ const heartbeat = parseHeartbeat(worker.heartbeatPath);
9084
+ const text = [
9085
+ worker.name,
9086
+ worker.repairTargetPrUrl,
9087
+ worker.taskPrUrl,
9088
+ worker.branch,
9089
+ heartbeat.lastHeartbeatSummary,
9090
+ extractText(statusFinalResult)
9091
+ ].filter(Boolean).join("\n");
9092
+ return extractPrNumbersFromText(text);
9093
+ }
9094
+ function isLocalOnlyAttentionNoise(worker) {
9095
+ return worker.localOnly === true && !worker.taskId && !worker.agentOsId;
9096
+ }
9097
+ function reconcileLocalOnlyMergedPrAttention(options = {}) {
9098
+ const lookupPr = options.lookupPr ?? defaultLookupPr;
9099
+ const outcomes = [];
9100
+ const liveLookupCache = /* @__PURE__ */ new Map();
9101
+ for (const run of listRunRecords()) {
9102
+ const mergedByPr = collectMergedEvidenceByPr(run);
9103
+ const runDir2 = runDirectory(run.id);
9104
+ for (const name of listRunWorkerNames(run)) {
9105
+ const workerPath = path35.join(runDir2, "workers", safeSlug(name), "worker.json");
9106
+ const worker = readJson(workerPath, void 0);
9107
+ if (!worker || !isLocalOnlyAttentionNoise(worker)) continue;
9108
+ const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
9109
+ if (status.attention.state !== "needs_attention") continue;
9110
+ let merged = null;
9111
+ for (const prNumber of candidatePrNumbers(worker, status.finalResult)) {
9112
+ merged = mergedByPr.get(prNumber) ?? null;
9113
+ if (!merged) {
9114
+ const repoDir = run.repo || worker.worktreePath;
9115
+ const cacheKey = `${repoDir}#${prNumber}`;
9116
+ const live = liveLookupCache.has(cacheKey) ? liveLookupCache.get(cacheKey) ?? null : lookupPr({ repoDir, prNumber });
9117
+ if (!liveLookupCache.has(cacheKey)) liveLookupCache.set(cacheKey, live);
9118
+ if (live && (live.state === "MERGED" || live.mergedAt)) {
9119
+ merged = { prUrl: live.prUrl, mergeCommit: live.mergeCommit ?? null, reason: "GitHub reports PR merged" };
9120
+ }
9121
+ }
9122
+ if (merged) break;
9123
+ }
9124
+ if (!merged) {
9125
+ outcomes.push({ runId: run.id, worker: name, action: "skipped", reason: "no merged PR evidence" });
9126
+ continue;
9127
+ }
9128
+ const now = (/* @__PURE__ */ new Date()).toISOString();
9129
+ worker.status = "done";
9130
+ worker.completionSnapshot = {
9131
+ prUrl: merged.prUrl,
9132
+ summary: `Local-only worker superseded by merged PR ${merged.prUrl}`,
9133
+ finalResult: {
9134
+ summary: `Local-only repair/salvage worker superseded by merged PR ${merged.prUrl}`,
9135
+ targetPrReconciliation: [
9136
+ {
9137
+ prUrl: merged.prUrl,
9138
+ outcome: "merged",
9139
+ mergeCommit: merged.mergeCommit ?? null,
9140
+ reason: merged.reason ?? "PR already merged; local-only worker no longer requires attention"
9141
+ }
9142
+ ]
9143
+ }
9144
+ };
9145
+ worker.completionAckSource = "local-pr-merged-reconcile";
9146
+ worker.reconciledAt = now;
9147
+ worker.reconcileReason = "local-only needs_attention superseded by merged PR";
9148
+ saveWorker(run.id, worker);
9149
+ outcomes.push({
9150
+ runId: run.id,
9151
+ worker: name,
9152
+ action: "marked_done",
9153
+ reason: worker.reconcileReason,
9154
+ prUrl: merged.prUrl,
9155
+ mergeCommit: merged.mergeCommit ?? null
9156
+ });
9157
+ }
9158
+ }
9159
+ return { workers: outcomes };
9160
+ }
9161
+
8905
9162
  // src/stale-reconcile.ts
8906
9163
  var STALE_RECONCILE_HEARTBEAT_MS = 15 * 60 * 1e3;
8907
9164
  function staleReconcileDisabled() {
@@ -8910,13 +9167,14 @@ function staleReconcileDisabled() {
8910
9167
  function reconcileStaleWorkers() {
8911
9168
  const metadataReconcile = reconcileWorkerMetadata();
8912
9169
  if (staleReconcileDisabled()) {
8913
- return { workers: [], finalizedRuns: finalizeStaleRuns(), metadataReconcile };
9170
+ const localPrAttentionReconcile2 = reconcileLocalOnlyMergedPrAttention();
9171
+ return { workers: [], finalizedRuns: finalizeStaleRuns(), metadataReconcile, localPrAttentionReconcile: localPrAttentionReconcile2 };
8914
9172
  }
8915
9173
  const outcomes = [];
8916
9174
  const now = Date.now();
8917
9175
  for (const run of listRunRecords()) {
8918
9176
  for (const name of listRunWorkerNames(run)) {
8919
- const workerPath = path35.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
9177
+ const workerPath = path36.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
8920
9178
  const worker = readJson(workerPath, void 0);
8921
9179
  if (!worker || worker.status !== "running") {
8922
9180
  outcomes.push({
@@ -8988,7 +9246,8 @@ function reconcileStaleWorkers() {
8988
9246
  });
8989
9247
  }
8990
9248
  }
8991
- return { workers: outcomes, finalizedRuns: finalizeStaleRuns(), metadataReconcile };
9249
+ const localPrAttentionReconcile = reconcileLocalOnlyMergedPrAttention();
9250
+ return { workers: outcomes, finalizedRuns: finalizeStaleRuns(), metadataReconcile, localPrAttentionReconcile };
8992
9251
  }
8993
9252
  function reconcileRunsCli() {
8994
9253
  const result = reconcileStaleWorkers();
@@ -8999,6 +9258,10 @@ function reconcileRunsCli() {
8999
9258
  acc[row.action] = (acc[row.action] ?? 0) + 1;
9000
9259
  return acc;
9001
9260
  }, {});
9261
+ const localPrAttentionTotals = result.localPrAttentionReconcile.workers.reduce((acc, row) => {
9262
+ acc[row.action] = (acc[row.action] ?? 0) + 1;
9263
+ return acc;
9264
+ }, {});
9002
9265
  const runRetentionTotals = result.metadataReconcile.runMetadataRetention.runs.reduce((acc, row) => {
9003
9266
  acc[row.action] = (acc[row.action] ?? 0) + 1;
9004
9267
  return acc;
@@ -9016,10 +9279,15 @@ function reconcileRunsCli() {
9016
9279
  total: result.metadataReconcile.runMetadataRetention.runs.length
9017
9280
  }
9018
9281
  },
9282
+ localPrAttentionReconcile: {
9283
+ totals: localPrAttentionTotals,
9284
+ total: result.localPrAttentionReconcile.workers.length
9285
+ },
9019
9286
  finalizedRuns: result.finalizedRuns.length,
9020
9287
  details: {
9021
9288
  workers: result.workers,
9022
9289
  metadataReconcile: result.metadataReconcile.workers,
9290
+ localPrAttentionReconcile: result.localPrAttentionReconcile.workers,
9023
9291
  runMetadataRetention: result.metadataReconcile.runMetadataRetention.runs,
9024
9292
  finalizedRuns: result.finalizedRuns
9025
9293
  }
@@ -9034,13 +9302,13 @@ function reconcileRunsCli() {
9034
9302
  function heartbeatByteLength(heartbeatPath) {
9035
9303
  if (!heartbeatPath || !existsSync28(heartbeatPath)) return 0;
9036
9304
  try {
9037
- return readFileSync14(heartbeatPath, "utf8").trim().length;
9305
+ return readFileSync15(heartbeatPath, "utf8").trim().length;
9038
9306
  } catch {
9039
9307
  return 0;
9040
9308
  }
9041
9309
  }
9042
9310
  function workerEvidence(run, workerName) {
9043
- const workerPath = path36.join(runDirectory(run.id), "workers", safeSlug(workerName), "worker.json");
9311
+ const workerPath = path37.join(runDirectory(run.id), "workers", safeSlug(workerName), "worker.json");
9044
9312
  const worker = readJson(workerPath, void 0);
9045
9313
  if (!worker) {
9046
9314
  return {
@@ -9097,7 +9365,7 @@ function aggregateRunAttention(workers) {
9097
9365
  function countOpenWorkers(run) {
9098
9366
  let open = 0;
9099
9367
  for (const name of listRunWorkerNames(run)) {
9100
- const workerPath = path36.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
9368
+ const workerPath = path37.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
9101
9369
  const worker = readJson(workerPath, void 0);
9102
9370
  if (!worker) continue;
9103
9371
  const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
@@ -9173,7 +9441,7 @@ function createRun(args) {
9173
9441
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
9174
9442
  workers: {}
9175
9443
  };
9176
- writeJson(path37.join(dir, "run.json"), run);
9444
+ writeJson(path38.join(dir, "run.json"), run);
9177
9445
  console.log(JSON.stringify({ runId: id, runDir: dir, repo, base, baseCommit }, null, 2));
9178
9446
  }
9179
9447
  function listRuns() {
@@ -9184,8 +9452,32 @@ function failExists(message) {
9184
9452
  process.exit(1);
9185
9453
  }
9186
9454
 
9455
+ // src/run-resolve.ts
9456
+ function resolveHarnessRunByName(runName) {
9457
+ const name = runName.trim();
9458
+ if (!name) return null;
9459
+ const rows = buildRunListRows();
9460
+ return rows.find((row) => row.name === name && row.status !== "completed") ?? null;
9461
+ }
9462
+ function resolveHarnessRunCli(args) {
9463
+ const name = String(required(String(args.name || ""), "--name"));
9464
+ const hit = resolveHarnessRunByName(name);
9465
+ console.log(
9466
+ JSON.stringify(
9467
+ {
9468
+ runId: hit?.id ?? null,
9469
+ name,
9470
+ status: hit?.status ?? null,
9471
+ effectiveStatus: hit?.effectiveStatus ?? null
9472
+ },
9473
+ null,
9474
+ 2
9475
+ )
9476
+ );
9477
+ }
9478
+
9187
9479
  // src/sweep.ts
9188
- import path38 from "node:path";
9480
+ import path39 from "node:path";
9189
9481
  async function sweepRun(args) {
9190
9482
  const pipeline = args.pipeline === true || args.pipeline === "true";
9191
9483
  try {
@@ -9198,7 +9490,7 @@ async function sweepRun(args) {
9198
9490
  const releasedLocalOrphans = [];
9199
9491
  for (const name of Object.keys(run.workers || {})) {
9200
9492
  const worker = readJson(
9201
- path38.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
9493
+ path39.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
9202
9494
  void 0
9203
9495
  );
9204
9496
  if (!worker || !worker.dispatched || !worker.taskId) continue;
@@ -9247,17 +9539,17 @@ async function sweepRun(args) {
9247
9539
 
9248
9540
  // src/harness-storage-snapshot.ts
9249
9541
  import { existsSync as existsSync31, readdirSync as readdirSync9, statSync as statSync7 } from "node:fs";
9250
- import path40 from "node:path";
9542
+ import path41 from "node:path";
9251
9543
 
9252
9544
  // src/cleanup-dir-size.ts
9253
- import { execFileSync as execFileSync2 } from "node:child_process";
9545
+ import { execFileSync as execFileSync3 } from "node:child_process";
9254
9546
  import { existsSync as existsSync30, readdirSync as readdirSync8, statSync as statSync6 } from "node:fs";
9255
- import path39 from "node:path";
9547
+ import path40 from "node:path";
9256
9548
  var DEFAULT_DU_TIMEOUT_MS = 2500;
9257
9549
  function directorySizeBytesDu(root, timeoutMs = DEFAULT_DU_TIMEOUT_MS) {
9258
9550
  if (!existsSync30(root)) return 0;
9259
9551
  try {
9260
- const out = execFileSync2("du", ["-sb", root], {
9552
+ const out = execFileSync3("du", ["-sb", root], {
9261
9553
  encoding: "utf8",
9262
9554
  timeout: timeoutMs,
9263
9555
  stdio: ["ignore", "pipe", "ignore"]
@@ -9286,7 +9578,7 @@ function directorySizeBytes(root, maxEntries = 5e4) {
9286
9578
  }
9287
9579
  for (const name of entries) {
9288
9580
  if (seen++ > maxEntries) return null;
9289
- const full = path39.join(current, name);
9581
+ const full = path40.join(current, name);
9290
9582
  let st;
9291
9583
  try {
9292
9584
  st = statSync6(full);
@@ -9338,7 +9630,7 @@ function harnessStorageSnapshot(opts = {}) {
9338
9630
  for (const runEntry of entries) {
9339
9631
  if (!runEntry.isDirectory()) continue;
9340
9632
  runCount += 1;
9341
- const runPath = path40.join(worktreesDir, runEntry.name);
9633
+ const runPath = path41.join(worktreesDir, runEntry.name);
9342
9634
  try {
9343
9635
  const st = statSync7(runPath);
9344
9636
  oldestMs = oldestMs === null ? st.mtimeMs : Math.min(oldestMs, st.mtimeMs);
@@ -9373,10 +9665,10 @@ function harnessStorageSnapshot(opts = {}) {
9373
9665
  }
9374
9666
 
9375
9667
  // src/cleanup.ts
9376
- import path52 from "node:path";
9668
+ import path53 from "node:path";
9377
9669
 
9378
9670
  // src/cleanup-guards.ts
9379
- import path41 from "node:path";
9671
+ import path42 from "node:path";
9380
9672
 
9381
9673
  // src/cleanup-build-cache-paths.ts
9382
9674
  var HARNESS_BUILD_CACHE_RELATIVE_PATHS = [
@@ -9517,7 +9809,7 @@ function skipWorktreeRemoval(input) {
9517
9809
  function skipDependencyCacheRemoval(input) {
9518
9810
  const { indexed, nodeModulesAgeMs, ageMs, worktreePath, activeWorktreePaths, diskPressure } = input;
9519
9811
  if (!diskPressure && ageMs < nodeModulesAgeMs) return "below_age_threshold";
9520
- if (activeWorktreePaths.has(path41.resolve(worktreePath))) return "active_worker";
9812
+ if (activeWorktreePaths.has(path42.resolve(worktreePath))) return "active_worker";
9521
9813
  if (indexed && isWorkerProcessLive(indexed)) return "active_worker";
9522
9814
  if (indexed && indexedWorktreeHasMaterialChanges(indexed)) return "dirty_worktree";
9523
9815
  return null;
@@ -9544,11 +9836,11 @@ var LIVE_SKIP_REASONS = /* @__PURE__ */ new Set([
9544
9836
  function collectPreservedLivePaths(actions, skips) {
9545
9837
  const out = [];
9546
9838
  const seen = /* @__PURE__ */ new Set();
9547
- const push = (path69, reason, detail) => {
9548
- const key = `${path69}\0${reason}`;
9839
+ const push = (path73, reason, detail) => {
9840
+ const key = `${path73}\0${reason}`;
9549
9841
  if (seen.has(key) || out.length >= MAX_PRESERVED_LIVE_PATH_SAMPLES) return;
9550
9842
  seen.add(key);
9551
- out.push({ path: path69, reason, ...detail ? { detail } : {} });
9843
+ out.push({ path: path73, reason, ...detail ? { detail } : {} });
9552
9844
  };
9553
9845
  for (const skip2 of skips) {
9554
9846
  if (!LIVE_SKIP_REASONS.has(skip2.reason)) continue;
@@ -9564,11 +9856,11 @@ function collectPreservedLivePaths(actions, skips) {
9564
9856
 
9565
9857
  // src/cleanup-run-directory.ts
9566
9858
  import { existsSync as existsSync33, readdirSync as readdirSync11, statSync as statSync9 } from "node:fs";
9567
- import path43 from "node:path";
9859
+ import path44 from "node:path";
9568
9860
 
9569
9861
  // src/cleanup-active-worktrees.ts
9570
9862
  import { existsSync as existsSync32, readdirSync as readdirSync10, statSync as statSync8 } from "node:fs";
9571
- import path42 from "node:path";
9863
+ import path43 from "node:path";
9572
9864
  function workerHasRecentHarnessActivity(worker, now) {
9573
9865
  const paths = [worker.heartbeatPath, worker.stdoutPath, worker.stderrPath];
9574
9866
  for (const target of paths) {
@@ -9594,11 +9886,11 @@ function collectActiveWorktreeGuards(harnessRoots, now = Date.now()) {
9594
9886
  let runHasLive = false;
9595
9887
  for (const name of Object.keys(run.workers || {})) {
9596
9888
  const worker = readJson(
9597
- path42.join(runDirectoryAt(harnessRoot, run.id), "workers", safeSlug(name), "worker.json"),
9889
+ path43.join(runDirectoryAt(harnessRoot, run.id), "workers", safeSlug(name), "worker.json"),
9598
9890
  void 0
9599
9891
  );
9600
9892
  if (!worker?.worktreePath) continue;
9601
- const worktreePath = path42.resolve(worker.worktreePath);
9893
+ const worktreePath = path43.resolve(worker.worktreePath);
9602
9894
  if (!isActiveHarnessWorker2(worker, now)) continue;
9603
9895
  runHasLive = true;
9604
9896
  activeWorktreePaths.add(worktreePath);
@@ -9626,7 +9918,7 @@ function pathAgeMs(target, now) {
9626
9918
  }
9627
9919
  }
9628
9920
  function loadRunStatus(harnessRoot, runId) {
9629
- const runPath = path43.join(harnessRoot, "runs", runId, "run.json");
9921
+ const runPath = path44.join(harnessRoot, "runs", runId, "run.json");
9630
9922
  if (!existsSync33(runPath)) return null;
9631
9923
  return readJson(runPath, null);
9632
9924
  }
@@ -9662,7 +9954,7 @@ function scanStaleRunDirectoryCandidates(opts) {
9662
9954
  if (!runEntry.isDirectory()) continue;
9663
9955
  const runId = runEntry.name;
9664
9956
  if (opts.runIdFilter && runId !== opts.runIdFilter) continue;
9665
- const runPath = path43.join(opts.worktreesDir, runId);
9957
+ const runPath = path44.join(opts.worktreesDir, runId);
9666
9958
  if (!runDirectoryIsEmpty(runPath)) continue;
9667
9959
  candidates.push({
9668
9960
  kind: "remove_run_directory",
@@ -9716,20 +10008,20 @@ function pathHasForeignOwnedEntry(targetPath, maxEntries = 32) {
9716
10008
 
9717
10009
  // src/cleanup-privileged-remove.ts
9718
10010
  import { spawnSync as spawnSync5 } from "node:child_process";
9719
- import path45 from "node:path";
10011
+ import path46 from "node:path";
9720
10012
 
9721
10013
  // src/cleanup-harness-path-validate.ts
9722
- import path44 from "node:path";
10014
+ import path45 from "node:path";
9723
10015
  function isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, cacheDirName) {
9724
- const resolved = path44.resolve(targetPath);
9725
- const suffix = `${path44.sep}${cacheDirName}`;
10016
+ const resolved = path45.resolve(targetPath);
10017
+ const suffix = `${path45.sep}${cacheDirName}`;
9726
10018
  const cachePath = resolved.endsWith(suffix) ? resolved : null;
9727
10019
  if (!cachePath) return "path_outside_harness";
9728
- const rel = path44.relative(worktreesDir, cachePath);
9729
- if (rel.startsWith("..") || path44.isAbsolute(rel)) return "path_outside_harness";
9730
- const parts = rel.split(path44.sep);
10020
+ const rel = path45.relative(worktreesDir, cachePath);
10021
+ if (rel.startsWith("..") || path45.isAbsolute(rel)) return "path_outside_harness";
10022
+ const parts = rel.split(path45.sep);
9731
10023
  if (parts.length < 3 || parts[parts.length - 1] !== cacheDirName) return "path_outside_harness";
9732
- if (!resolved.startsWith(path44.resolve(harnessRoot))) return "path_outside_harness";
10024
+ if (!resolved.startsWith(path45.resolve(harnessRoot))) return "path_outside_harness";
9733
10025
  return null;
9734
10026
  }
9735
10027
  function isHarnessNodeModulesPath(targetPath, harnessRoot, worktreesDir) {
@@ -9739,16 +10031,16 @@ function isHarnessNextCachePath(targetPath, harnessRoot, worktreesDir) {
9739
10031
  return isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, ".next");
9740
10032
  }
9741
10033
  function isHarnessBuildCachePath(targetPath, harnessRoot, worktreesDir) {
9742
- const resolved = path44.resolve(targetPath);
9743
- const relToWt = path44.relative(worktreesDir, resolved);
9744
- if (relToWt.startsWith("..") || path44.isAbsolute(relToWt)) return "path_outside_harness";
9745
- const parts = relToWt.split(path44.sep);
10034
+ const resolved = path45.resolve(targetPath);
10035
+ const relToWt = path45.relative(worktreesDir, resolved);
10036
+ if (relToWt.startsWith("..") || path45.isAbsolute(relToWt)) return "path_outside_harness";
10037
+ const parts = relToWt.split(path45.sep);
9746
10038
  if (parts.length < 3) return "path_outside_harness";
9747
- if (!resolved.startsWith(path44.resolve(harnessRoot))) return "path_outside_harness";
10039
+ if (!resolved.startsWith(path45.resolve(harnessRoot))) return "path_outside_harness";
9748
10040
  return null;
9749
10041
  }
9750
10042
  function isHarnessGeneratedCachePath(targetPath, harnessRoot, worktreesDir) {
9751
- const resolved = path44.resolve(targetPath);
10043
+ const resolved = path45.resolve(targetPath);
9752
10044
  return isHarnessNodeModulesPath(resolved, harnessRoot, worktreesDir) === null || isHarnessNextCachePath(resolved, harnessRoot, worktreesDir) === null || isHarnessBuildCachePath(resolved, harnessRoot, worktreesDir) === null;
9753
10045
  }
9754
10046
 
@@ -9782,12 +10074,12 @@ function tryPrivilegedReclaimHarnessCache(targetPath, harnessRoot, worktreesDir)
9782
10074
  "chown",
9783
10075
  "-R",
9784
10076
  `${effectiveUid}:${effectiveGid}`,
9785
- path45.resolve(targetPath)
10077
+ path46.resolve(targetPath)
9786
10078
  ]);
9787
10079
  if (chown.ok) {
9788
10080
  return { ok: true, method: "chown_then_rm" };
9789
10081
  }
9790
- const rm = runSudoNonInteractive(["rm", "-rf", path45.resolve(targetPath)]);
10082
+ const rm = runSudoNonInteractive(["rm", "-rf", path46.resolve(targetPath)]);
9791
10083
  if (rm.ok) {
9792
10084
  return { ok: true, method: "sudo_rm" };
9793
10085
  }
@@ -9989,7 +10281,7 @@ function removeWorktree(candidate, execute) {
9989
10281
 
9990
10282
  // src/cleanup-scan.ts
9991
10283
  import { existsSync as existsSync36, readdirSync as readdirSync13, statSync as statSync10 } from "node:fs";
9992
- import path46 from "node:path";
10284
+ import path47 from "node:path";
9993
10285
  function pathAgeMs2(target, now) {
9994
10286
  try {
9995
10287
  const mtime = statSync10(target).mtimeMs;
@@ -9999,16 +10291,16 @@ function pathAgeMs2(target, now) {
9999
10291
  }
10000
10292
  }
10001
10293
  function isPathInside(child, parent) {
10002
- const rel = path46.relative(parent, child);
10003
- return rel === "" || !rel.startsWith("..") && !path46.isAbsolute(rel);
10294
+ const rel = path47.relative(parent, child);
10295
+ return rel === "" || !rel.startsWith("..") && !path47.isAbsolute(rel);
10004
10296
  }
10005
10297
  function collectBuildCacheForWorktree(worktreePath, opts, seen, meta) {
10006
10298
  const out = [];
10007
10299
  for (const rel of HARNESS_BUILD_CACHE_RELATIVE_PATHS) {
10008
10300
  if (rel === ".next") continue;
10009
- const target = path46.join(worktreePath, rel);
10301
+ const target = path47.join(worktreePath, rel);
10010
10302
  if (!existsSync36(target)) continue;
10011
- const resolved = path46.resolve(target);
10303
+ const resolved = path47.resolve(target);
10012
10304
  if (seen.has(resolved)) continue;
10013
10305
  if (!isPathInside(resolved, opts.harnessRoot)) continue;
10014
10306
  seen.add(resolved);
@@ -10040,10 +10332,10 @@ function scanBuildCacheCandidates(opts) {
10040
10332
  if (!opts.includeOrphans || !existsSync36(opts.worktreesDir)) return candidates;
10041
10333
  for (const runEntry of readdirSync13(opts.worktreesDir, { withFileTypes: true })) {
10042
10334
  if (!runEntry.isDirectory()) continue;
10043
- const runPath = path46.join(opts.worktreesDir, runEntry.name);
10335
+ const runPath = path47.join(opts.worktreesDir, runEntry.name);
10044
10336
  for (const workerEntry of readdirSync13(runPath, { withFileTypes: true })) {
10045
10337
  if (!workerEntry.isDirectory()) continue;
10046
- const worktreePath = path46.join(runPath, workerEntry.name);
10338
+ const worktreePath = path47.join(runPath, workerEntry.name);
10047
10339
  candidates.push(
10048
10340
  ...collectBuildCacheForWorktree(worktreePath, opts, seen, {
10049
10341
  runId: runEntry.name,
@@ -10081,12 +10373,12 @@ function scanWorktreeCandidates(opts) {
10081
10373
  if (!orphanEnabled || !existsSync36(opts.worktreesDir)) return candidates;
10082
10374
  const indexedPaths = /* @__PURE__ */ new Set();
10083
10375
  for (const entry of opts.index.values()) {
10084
- indexedPaths.add(path46.resolve(entry.worktreePath));
10376
+ indexedPaths.add(path47.resolve(entry.worktreePath));
10085
10377
  }
10086
10378
  for (const runEntry of readdirSync13(opts.worktreesDir, { withFileTypes: true })) {
10087
10379
  if (!runEntry.isDirectory()) continue;
10088
10380
  if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
10089
- const runPath = path46.join(opts.worktreesDir, runEntry.name);
10381
+ const runPath = path47.join(opts.worktreesDir, runEntry.name);
10090
10382
  let workerEntries;
10091
10383
  try {
10092
10384
  workerEntries = readdirSync13(runPath, { withFileTypes: true });
@@ -10095,7 +10387,7 @@ function scanWorktreeCandidates(opts) {
10095
10387
  }
10096
10388
  for (const workerEntry of workerEntries) {
10097
10389
  if (!workerEntry.isDirectory()) continue;
10098
- const worktreePath = path46.resolve(path46.join(runPath, workerEntry.name));
10390
+ const worktreePath = path47.resolve(path47.join(runPath, workerEntry.name));
10099
10391
  if (seen.has(worktreePath)) continue;
10100
10392
  if (indexedPaths.has(worktreePath)) continue;
10101
10393
  if (!isPathInside(worktreePath, opts.harnessRoot)) continue;
@@ -10115,7 +10407,7 @@ function scanWorktreeCandidates(opts) {
10115
10407
 
10116
10408
  // src/cleanup-dependency-scan.ts
10117
10409
  import { existsSync as existsSync37, readdirSync as readdirSync14, statSync as statSync11 } from "node:fs";
10118
- import path47 from "node:path";
10410
+ import path48 from "node:path";
10119
10411
  var DEPENDENCY_CACHE_DIRS = [
10120
10412
  { dirName: "node_modules", kind: "remove_node_modules" },
10121
10413
  { dirName: ".next", kind: "remove_next_cache" }
@@ -10129,12 +10421,12 @@ function pathAgeMs3(target, now) {
10129
10421
  }
10130
10422
  }
10131
10423
  function isPathInside2(child, parent) {
10132
- const rel = path47.relative(parent, child);
10133
- return rel === "" || !rel.startsWith("..") && !path47.isAbsolute(rel);
10424
+ const rel = path48.relative(parent, child);
10425
+ return rel === "" || !rel.startsWith("..") && !path48.isAbsolute(rel);
10134
10426
  }
10135
10427
  function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
10136
10428
  if (!existsSync37(targetPath)) return;
10137
- const resolved = path47.resolve(targetPath);
10429
+ const resolved = path48.resolve(targetPath);
10138
10430
  if (seen.has(resolved)) return;
10139
10431
  if (!isPathInside2(resolved, opts.harnessRoot)) return;
10140
10432
  seen.add(resolved);
@@ -10151,7 +10443,7 @@ function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
10151
10443
  }
10152
10444
  function scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, meta) {
10153
10445
  for (const entry of DEPENDENCY_CACHE_DIRS) {
10154
- pushCandidate2(candidates, seen, opts, path47.join(worktreePath, entry.dirName), entry.kind, meta);
10446
+ pushCandidate2(candidates, seen, opts, path48.join(worktreePath, entry.dirName), entry.kind, meta);
10155
10447
  }
10156
10448
  }
10157
10449
  function scanDependencyCacheCandidates(opts) {
@@ -10169,7 +10461,7 @@ function scanDependencyCacheCandidates(opts) {
10169
10461
  for (const runEntry of readdirSync14(opts.worktreesDir, { withFileTypes: true })) {
10170
10462
  if (!runEntry.isDirectory()) continue;
10171
10463
  if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
10172
- const runPath = path47.join(opts.worktreesDir, runEntry.name);
10464
+ const runPath = path48.join(opts.worktreesDir, runEntry.name);
10173
10465
  let workerEntries;
10174
10466
  try {
10175
10467
  workerEntries = readdirSync14(runPath, { withFileTypes: true });
@@ -10178,7 +10470,7 @@ function scanDependencyCacheCandidates(opts) {
10178
10470
  }
10179
10471
  for (const workerEntry of workerEntries) {
10180
10472
  if (!workerEntry.isDirectory()) continue;
10181
- const worktreePath = path47.join(runPath, workerEntry.name);
10473
+ const worktreePath = path48.join(runPath, workerEntry.name);
10182
10474
  scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, {
10183
10475
  runId: runEntry.name,
10184
10476
  worker: workerEntry.name
@@ -10190,7 +10482,7 @@ function scanDependencyCacheCandidates(opts) {
10190
10482
 
10191
10483
  // src/cleanup-duplicate-worktrees.ts
10192
10484
  import { existsSync as existsSync38, statSync as statSync12 } from "node:fs";
10193
- import path48 from "node:path";
10485
+ import path49 from "node:path";
10194
10486
  function pathAgeMs4(target, now) {
10195
10487
  try {
10196
10488
  const mtime = statSync12(target).mtimeMs;
@@ -10220,8 +10512,8 @@ function parseWorktreePorcelain(output) {
10220
10512
  return records;
10221
10513
  }
10222
10514
  function isUnderWorktreesDir(worktreePath, worktreesDir) {
10223
- const rel = path48.relative(path48.resolve(worktreesDir), path48.resolve(worktreePath));
10224
- return rel !== "" && !rel.startsWith("..") && !path48.isAbsolute(rel);
10515
+ const rel = path49.relative(path49.resolve(worktreesDir), path49.resolve(worktreePath));
10516
+ return rel !== "" && !rel.startsWith("..") && !path49.isAbsolute(rel);
10225
10517
  }
10226
10518
  function isCleanWorktree(worktreePath, repoRoot) {
10227
10519
  try {
@@ -10237,11 +10529,11 @@ function scanDuplicateWorktreeCandidates(opts) {
10237
10529
  if (!opts.includeOrphans || !existsSync38(opts.worktreesDir)) return [];
10238
10530
  const repos = /* @__PURE__ */ new Set();
10239
10531
  for (const entry of opts.index.values()) {
10240
- if (entry.run.repo) repos.add(path48.resolve(entry.run.repo));
10532
+ if (entry.run.repo) repos.add(path49.resolve(entry.run.repo));
10241
10533
  }
10242
10534
  const indexedPaths = /* @__PURE__ */ new Set();
10243
10535
  for (const entry of opts.index.values()) {
10244
- indexedPaths.add(path48.resolve(entry.worktreePath));
10536
+ indexedPaths.add(path49.resolve(entry.worktreePath));
10245
10537
  }
10246
10538
  const candidates = [];
10247
10539
  const seen = /* @__PURE__ */ new Set();
@@ -10254,15 +10546,15 @@ function scanDuplicateWorktreeCandidates(opts) {
10254
10546
  }
10255
10547
  const worktrees = parseWorktreePorcelain(porcelain);
10256
10548
  for (const wt of worktrees) {
10257
- const resolved = path48.resolve(wt.path);
10258
- if (resolved === path48.resolve(repoRoot)) continue;
10549
+ const resolved = path49.resolve(wt.path);
10550
+ if (resolved === path49.resolve(repoRoot)) continue;
10259
10551
  if (!isUnderWorktreesDir(resolved, opts.worktreesDir)) continue;
10260
10552
  if (indexedPaths.has(resolved)) continue;
10261
10553
  if (seen.has(resolved)) continue;
10262
10554
  if (!existsSync38(resolved)) continue;
10263
10555
  if (!isCleanWorktree(resolved, repoRoot)) continue;
10264
- const rel = path48.relative(opts.worktreesDir, resolved);
10265
- const parts = rel.split(path48.sep);
10556
+ const rel = path49.relative(opts.worktreesDir, resolved);
10557
+ const parts = rel.split(path49.sep);
10266
10558
  const runId = parts[0];
10267
10559
  const worker = parts[1] ?? "unknown";
10268
10560
  seen.add(resolved);
@@ -10281,12 +10573,12 @@ function scanDuplicateWorktreeCandidates(opts) {
10281
10573
  }
10282
10574
 
10283
10575
  // src/cleanup-worktree-index.ts
10284
- import path49 from "node:path";
10576
+ import path50 from "node:path";
10285
10577
  function buildWorktreeIndexAt(harnessRoot) {
10286
10578
  const index = /* @__PURE__ */ new Map();
10287
10579
  for (const run of listRunRecordsForHarnessRoot(harnessRoot)) {
10288
10580
  for (const name of Object.keys(run.workers || {})) {
10289
- const workerPath = path49.join(
10581
+ const workerPath = path50.join(
10290
10582
  runDirectoryAt(harnessRoot, run.id),
10291
10583
  "workers",
10292
10584
  safeSlug(name),
@@ -10294,9 +10586,9 @@ function buildWorktreeIndexAt(harnessRoot) {
10294
10586
  );
10295
10587
  const worker = readJson(workerPath, void 0);
10296
10588
  if (!worker?.worktreePath) continue;
10297
- index.set(path49.resolve(worker.worktreePath), {
10589
+ index.set(path50.resolve(worker.worktreePath), {
10298
10590
  harnessRoot,
10299
- worktreePath: path49.resolve(worker.worktreePath),
10591
+ worktreePath: path50.resolve(worker.worktreePath),
10300
10592
  runId: run.id,
10301
10593
  workerName: name,
10302
10594
  run,
@@ -10366,14 +10658,14 @@ function resolvePipelineHarnessRetention(runId) {
10366
10658
 
10367
10659
  // src/cleanup-orphan-safety.ts
10368
10660
  import { existsSync as existsSync39, statSync as statSync13 } from "node:fs";
10369
- import path50 from "node:path";
10661
+ import path51 from "node:path";
10370
10662
  var DEFAULT_HEARTBEAT_FRESH_MS = 30 * 60 * 1e3;
10371
10663
  function assessOrphanWorktreeSafety(input) {
10372
10664
  const now = input.now ?? Date.now();
10373
10665
  const heartbeatFreshMs = input.heartbeatFreshMs ?? DEFAULT_HEARTBEAT_FRESH_MS;
10374
10666
  if (!existsSync39(input.worktreePath)) return null;
10375
10667
  if (input.runId && input.workerName) {
10376
- const heartbeatPath = path50.join(
10668
+ const heartbeatPath = path51.join(
10377
10669
  input.harnessRoot,
10378
10670
  "runs",
10379
10671
  input.runId,
@@ -10387,7 +10679,7 @@ function assessOrphanWorktreeSafety(input) {
10387
10679
  } catch {
10388
10680
  }
10389
10681
  }
10390
- const gitDir = path50.join(input.worktreePath, ".git");
10682
+ const gitDir = path51.join(input.worktreePath, ".git");
10391
10683
  if (!existsSync39(gitDir)) return null;
10392
10684
  const porcelain = gitCapture(input.worktreePath, ["status", "--porcelain"]);
10393
10685
  if (porcelain.status !== 0) return "pr_or_unmerged_commits";
@@ -10419,10 +10711,10 @@ function assessOrphanWorktreeSafety(input) {
10419
10711
  // src/cleanup-harness-roots.ts
10420
10712
  import { existsSync as existsSync40 } from "node:fs";
10421
10713
  import { homedir as homedir13 } from "node:os";
10422
- import path51 from "node:path";
10714
+ import path52 from "node:path";
10423
10715
  var WELL_KNOWN_HARNESS_SCAN_ROOTS = [
10424
10716
  "/var/tmp/kynver-harness",
10425
- path51.join(homedir13(), ".openclaw", "harness")
10717
+ path52.join(homedir13(), ".openclaw", "harness")
10426
10718
  ];
10427
10719
  function addRoot(seen, roots, candidate) {
10428
10720
  if (!candidate?.trim()) return;
@@ -10444,7 +10736,7 @@ function resolveHarnessScanRoots(options = {}) {
10444
10736
  for (const candidate of extra ?? []) addRoot(seen, roots, candidate);
10445
10737
  if (shouldScanWellKnownRoots(options)) {
10446
10738
  for (const candidate of WELL_KNOWN_HARNESS_SCAN_ROOTS) {
10447
- const resolved = path51.resolve(candidate);
10739
+ const resolved = path52.resolve(candidate);
10448
10740
  if (!seen.has(resolved) && existsSync40(resolved)) addRoot(seen, roots, resolved);
10449
10741
  }
10450
10742
  }
@@ -10599,9 +10891,9 @@ function mergeWorktreeIndexes(scanRoots) {
10599
10891
  }
10600
10892
  function worktreePathForCandidate(candidate, worktreesDir) {
10601
10893
  if (candidate.runId && candidate.worker) {
10602
- return path52.join(worktreesDir, candidate.runId, candidate.worker);
10894
+ return path53.join(worktreesDir, candidate.runId, candidate.worker);
10603
10895
  }
10604
- return path52.resolve(candidate.path, "..");
10896
+ return path53.resolve(candidate.path, "..");
10605
10897
  }
10606
10898
  function runHarnessCleanup(options = {}) {
10607
10899
  let retention = resolveHarnessRetention(options);
@@ -10626,7 +10918,7 @@ function runHarnessCleanup(options = {}) {
10626
10918
  for (const harnessRoot of paths.scanRoots) {
10627
10919
  if (atSweepCap()) break;
10628
10920
  emitCleanupProgress("root", harnessRoot);
10629
- const worktreesDir = path52.join(harnessRoot, "worktrees");
10921
+ const worktreesDir = path53.join(harnessRoot, "worktrees");
10630
10922
  const scanOpts = {
10631
10923
  harnessRoot,
10632
10924
  worktreesDir,
@@ -10639,7 +10931,7 @@ function runHarnessCleanup(options = {}) {
10639
10931
  };
10640
10932
  for (const raw of scanDependencyCacheCandidates(scanOpts)) {
10641
10933
  if (atSweepCap()) break;
10642
- const resolved = path52.resolve(raw.path);
10934
+ const resolved = path53.resolve(raw.path);
10643
10935
  if (processedPaths.has(resolved)) continue;
10644
10936
  processedPaths.add(resolved);
10645
10937
  const candidate = { ...raw, path: resolved };
@@ -10650,7 +10942,7 @@ function runHarnessCleanup(options = {}) {
10650
10942
  continue;
10651
10943
  }
10652
10944
  const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
10653
- const indexed = index.get(path52.resolve(worktreePath)) ?? null;
10945
+ const indexed = index.get(path53.resolve(worktreePath)) ?? null;
10654
10946
  const guardReason = skipDependencyCacheRemoval({
10655
10947
  indexed,
10656
10948
  includeOrphans: true,
@@ -10674,7 +10966,7 @@ function runHarnessCleanup(options = {}) {
10674
10966
  }
10675
10967
  for (const raw of scanBuildCacheCandidates(scanOpts)) {
10676
10968
  if (atSweepCap()) break;
10677
- const resolved = path52.resolve(raw.path);
10969
+ const resolved = path53.resolve(raw.path);
10678
10970
  if (processedPaths.has(resolved)) continue;
10679
10971
  processedPaths.add(resolved);
10680
10972
  const candidate = { ...raw, path: resolved };
@@ -10685,7 +10977,7 @@ function runHarnessCleanup(options = {}) {
10685
10977
  continue;
10686
10978
  }
10687
10979
  const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
10688
- const indexed = index.get(path52.resolve(worktreePath)) ?? null;
10980
+ const indexed = index.get(path53.resolve(worktreePath)) ?? null;
10689
10981
  const guardReason = skipBuildCacheRemoval({
10690
10982
  indexed,
10691
10983
  includeOrphans: true,
@@ -10715,11 +11007,11 @@ function runHarnessCleanup(options = {}) {
10715
11007
  const worktreeSeen = /* @__PURE__ */ new Set();
10716
11008
  for (const raw of worktreeCandidates) {
10717
11009
  if (atSweepCap()) break;
10718
- const resolved = path52.resolve(raw.path);
11010
+ const resolved = path53.resolve(raw.path);
10719
11011
  if (worktreeSeen.has(resolved)) continue;
10720
11012
  worktreeSeen.add(resolved);
10721
11013
  const candidate = { ...raw, path: resolved };
10722
- const indexed = index.get(path52.resolve(candidate.path)) ?? null;
11014
+ const indexed = index.get(path53.resolve(candidate.path)) ?? null;
10723
11015
  const orphanSafety = indexed ? null : assessOrphanWorktreeSafety({
10724
11016
  worktreePath: candidate.path,
10725
11017
  harnessRoot,
@@ -10729,7 +11021,7 @@ function runHarnessCleanup(options = {}) {
10729
11021
  });
10730
11022
  const guardSkip = skipWorktreeRemoval({
10731
11023
  indexed,
10732
- worktreePath: path52.resolve(candidate.path),
11024
+ worktreePath: path53.resolve(candidate.path),
10733
11025
  includeOrphans: retention.includeOrphans,
10734
11026
  worktreesAgeMs: retention.worktreesAgeMs,
10735
11027
  terminalWorktreesAgeMs: retention.terminalWorktreesAgeMs,
@@ -10761,11 +11053,11 @@ function runHarnessCleanup(options = {}) {
10761
11053
  now: paths.now
10762
11054
  })) {
10763
11055
  if (atSweepCap()) break;
10764
- const resolved = path52.resolve(raw.path);
11056
+ const resolved = path53.resolve(raw.path);
10765
11057
  if (processedPaths.has(resolved)) continue;
10766
11058
  processedPaths.add(resolved);
10767
11059
  const candidate = { ...raw, path: resolved };
10768
- const runId = candidate.runId ?? path52.basename(resolved);
11060
+ const runId = candidate.runId ?? path53.basename(resolved);
10769
11061
  const dirSkip = skipRunDirectoryRemoval({
10770
11062
  harnessRoot,
10771
11063
  runId,
@@ -10899,12 +11191,12 @@ function isPipelineCleanupEnabled() {
10899
11191
  }
10900
11192
 
10901
11193
  // src/cli.ts
10902
- import { mkdirSync as mkdirSync8, realpathSync } from "node:fs";
11194
+ import { mkdirSync as mkdirSync10, realpathSync } from "node:fs";
10903
11195
  import { fileURLToPath as fileURLToPath5 } from "node:url";
10904
11196
 
10905
11197
  // src/discard-disposable.ts
10906
11198
  import { existsSync as existsSync41, rmSync as rmSync4 } from "node:fs";
10907
- import path53 from "node:path";
11199
+ import path54 from "node:path";
10908
11200
  function normalizeRelativePath2(value) {
10909
11201
  const normalized = value.replace(/\\/g, "/").replace(/^\.\//, "").trim();
10910
11202
  if (!normalized || normalized.startsWith("/") || normalized.includes("..")) {
@@ -10926,12 +11218,12 @@ function discardDisposableArtifacts(args) {
10926
11218
  if (paths.length === 0) {
10927
11219
  return { ok: false, removed: [], reason: "requires at least one --path" };
10928
11220
  }
10929
- const worktreeRoot = path53.resolve(worker.worktreePath);
11221
+ const worktreeRoot = path54.resolve(worker.worktreePath);
10930
11222
  const removed = [];
10931
11223
  for (const raw of paths) {
10932
11224
  const rel = normalizeRelativePath2(raw);
10933
- const abs = path53.resolve(worktreeRoot, rel);
10934
- if (!abs.startsWith(worktreeRoot + path53.sep) && abs !== worktreeRoot) {
11225
+ const abs = path54.resolve(worktreeRoot, rel);
11226
+ if (!abs.startsWith(worktreeRoot + path54.sep) && abs !== worktreeRoot) {
10935
11227
  return { ok: false, removed, reason: `path escapes worktree: ${raw}` };
10936
11228
  }
10937
11229
  if (!existsSync41(abs)) {
@@ -10998,7 +11290,7 @@ function validateDaemonInstallIdentity(config = loadUserConfig(), env = process.
10998
11290
  // src/cron/cron-env.ts
10999
11291
  import { existsSync as existsSync42 } from "node:fs";
11000
11292
  import { homedir as homedir14 } from "node:os";
11001
- import path54 from "node:path";
11293
+ import path55 from "node:path";
11002
11294
  function envFlag3(name, defaultValue) {
11003
11295
  const raw = process.env[name]?.trim().toLowerCase();
11004
11296
  if (!raw) return defaultValue;
@@ -11014,7 +11306,7 @@ function envInt(name, fallback, min = 1) {
11014
11306
  function defaultKynverCronStorePath() {
11015
11307
  const explicit = process.env.KYNVER_CRON_STORE_PATH?.trim() || process.env.OPENCLAW_CRON_STORE_PATH?.trim();
11016
11308
  if (explicit) return explicit;
11017
- return path54.join(homedir14(), ".kynver", "agent-os-cron.json");
11309
+ return path55.join(homedir14(), ".kynver", "agent-os-cron.json");
11018
11310
  }
11019
11311
  function defaultKynverCronStatePath(storePath = defaultKynverCronStorePath()) {
11020
11312
  const explicit = process.env.KYNVER_CRON_TICK_STATE_PATH?.trim();
@@ -11087,12 +11379,12 @@ async function fireKynverCronJob(input) {
11087
11379
  }
11088
11380
 
11089
11381
  // src/cron/cron-lock.ts
11090
- import { closeSync as closeSync6, existsSync as existsSync43, openSync as openSync6, readFileSync as readFileSync15, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "node:fs";
11382
+ import { closeSync as closeSync6, existsSync as existsSync43, openSync as openSync6, readFileSync as readFileSync16, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "node:fs";
11091
11383
  var STALE_LOCK_MS = 10 * 6e4;
11092
11384
  function readLockInfo(lockPath) {
11093
11385
  if (!existsSync43(lockPath)) return null;
11094
11386
  try {
11095
- const parsed = JSON.parse(readFileSync15(lockPath, "utf8"));
11387
+ const parsed = JSON.parse(readFileSync16(lockPath, "utf8"));
11096
11388
  if (typeof parsed.pid === "number" && typeof parsed.at === "string") return parsed;
11097
11389
  } catch {
11098
11390
  return null;
@@ -11256,7 +11548,7 @@ async function loadCronJobs(storePath = defaultKynverCronStorePath()) {
11256
11548
  // src/cron/cron-tick-state.ts
11257
11549
  import { randomBytes } from "node:crypto";
11258
11550
  import { promises as fs4 } from "node:fs";
11259
- import path55 from "node:path";
11551
+ import path56 from "node:path";
11260
11552
  var EMPTY = { version: 1, jobs: {} };
11261
11553
  async function readFileIfExists2(filePath) {
11262
11554
  try {
@@ -11283,7 +11575,7 @@ async function loadCronTickState(statePath) {
11283
11575
  return parseCronTickState(raw);
11284
11576
  }
11285
11577
  async function writeStateAtomic(statePath, state) {
11286
- await fs4.mkdir(path55.dirname(statePath), { recursive: true });
11578
+ await fs4.mkdir(path56.dirname(statePath), { recursive: true });
11287
11579
  const suffix = randomBytes(6).toString("hex");
11288
11580
  const tmp = `${statePath}.tmp-${process.pid}-${Date.now()}-${suffix}`;
11289
11581
  await fs4.writeFile(tmp, `${JSON.stringify(state, null, 2)}
@@ -11460,7 +11752,7 @@ async function runKynverCronTick(opts = {}) {
11460
11752
  }
11461
11753
 
11462
11754
  // src/pipeline-tick.ts
11463
- import path57 from "node:path";
11755
+ import path58 from "node:path";
11464
11756
 
11465
11757
  // src/pipeline-dispatch.ts
11466
11758
  var RESERVED_REVIEW_STARTS = 1;
@@ -11591,7 +11883,7 @@ function resolvePipelineMaxStarts(resourceGate, operatorTick) {
11591
11883
  }
11592
11884
 
11593
11885
  // src/plan-progress-daemon-sync.ts
11594
- import path56 from "node:path";
11886
+ import path57 from "node:path";
11595
11887
 
11596
11888
  // src/plan-progress-sync.ts
11597
11889
  async function syncPlanProgress(args) {
@@ -11615,7 +11907,7 @@ async function syncActiveWorkerPlanProgress(runId, args) {
11615
11907
  const outcomes = [];
11616
11908
  for (const name of Object.keys(run.workers || {})) {
11617
11909
  const worker = readJson(
11618
- path56.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
11910
+ path57.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
11619
11911
  void 0
11620
11912
  );
11621
11913
  if (!worker?.dispatched || !worker.taskId) continue;
@@ -11677,7 +11969,7 @@ async function completeFinishedWorkers(runId, args) {
11677
11969
  const outcomes = [];
11678
11970
  for (const name of Object.keys(run.workers || {})) {
11679
11971
  const worker = readJson(
11680
- path57.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
11972
+ path58.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
11681
11973
  void 0
11682
11974
  );
11683
11975
  if (!worker?.taskId || worker.localOnly) continue;
@@ -11824,6 +12116,15 @@ async function runPipelineTick(args) {
11824
12116
  var DEFAULT_INTERVAL_MS = 6e4;
11825
12117
  var IDLE_INTERVAL_MS = 5 * 6e4;
11826
12118
  var MAX_IDLE_STREAK = 10;
12119
+ var SLEEP_POLL_MS = 250;
12120
+ async function awaitDaemonBackoff(ms, isStopping) {
12121
+ let remaining = ms;
12122
+ while (remaining > 0 && !isStopping()) {
12123
+ const step = Math.min(SLEEP_POLL_MS, remaining);
12124
+ await sleepMsAsync(step);
12125
+ remaining -= step;
12126
+ }
12127
+ }
11827
12128
  async function runDaemon(args) {
11828
12129
  const runId = String(required(String(args.run || ""), "--run"));
11829
12130
  const agentOsId = String(required(String(args.agentOsId || loadUserConfig().agentOsId || ""), "--agent-os-id"));
@@ -11881,17 +12182,17 @@ async function runDaemon(args) {
11881
12182
  idleStreak = 0;
11882
12183
  }
11883
12184
  const backoff = idleStreak >= MAX_IDLE_STREAK ? IDLE_INTERVAL_MS : intervalMs;
11884
- sleepMs(backoff);
12185
+ await awaitDaemonBackoff(backoff, () => stopping);
11885
12186
  } catch (error) {
11886
12187
  console.error(JSON.stringify({ event: "daemon_tick_error", error: error.message }));
11887
- sleepMs(intervalMs);
12188
+ await awaitDaemonBackoff(intervalMs, () => stopping);
11888
12189
  }
11889
12190
  }
11890
12191
  console.error(JSON.stringify({ event: "daemon_stop", runId, agentOsId }));
11891
12192
  }
11892
12193
 
11893
12194
  // src/plan-progress.ts
11894
- import path59 from "node:path";
12195
+ import path62 from "node:path";
11895
12196
 
11896
12197
  // src/bounded-build/constants.ts
11897
12198
  var DEFAULT_BUILD_MEM_BUDGET_BYTES = 1536 * 1024 * 1024;
@@ -12021,14 +12322,235 @@ function waitForBuildAdmission(timeoutMs, pollMs = 2e3, opts = {}) {
12021
12322
  }
12022
12323
 
12023
12324
  // src/bounded-build/exec.ts
12325
+ import { spawnSync as spawnSync9 } from "node:child_process";
12326
+
12327
+ // src/heavy-verification/slot.ts
12328
+ import {
12329
+ closeSync as closeSync7,
12330
+ existsSync as existsSync44,
12331
+ mkdirSync as mkdirSync8,
12332
+ openSync as openSync7,
12333
+ readdirSync as readdirSync15,
12334
+ readFileSync as readFileSync17,
12335
+ unlinkSync as unlinkSync4,
12336
+ writeFileSync as writeFileSync5
12337
+ } from "node:fs";
12338
+ import path60 from "node:path";
12339
+
12340
+ // src/heavy-verification/paths.ts
12341
+ import { mkdirSync as mkdirSync7 } from "node:fs";
12342
+ import path59 from "node:path";
12343
+ function resolveHeavyVerificationRoot() {
12344
+ return path59.join(resolveKynverStateRoot(), "heavy-verification");
12345
+ }
12346
+ function heavyVerificationSlotsDir() {
12347
+ return path59.join(resolveHeavyVerificationRoot(), "slots");
12348
+ }
12349
+ function ensureHeavyVerificationDirs() {
12350
+ const dir = heavyVerificationSlotsDir();
12351
+ mkdirSync7(dir, { recursive: true });
12352
+ return dir;
12353
+ }
12354
+
12355
+ // src/heavy-verification/slot.ts
12356
+ var DEFAULT_HEAVY_VERIFICATION_STALE_MS = 2 * 60 * 6e4;
12357
+ var DEFAULT_HEAVY_VERIFICATION_MAX_CONCURRENT = 1;
12358
+ function positiveInt5(value, fallback) {
12359
+ const n = Number(value);
12360
+ if (!Number.isFinite(n) || n <= 0) return fallback;
12361
+ return Math.floor(n);
12362
+ }
12363
+ function isHeavyVerificationGateSkipped() {
12364
+ const v = process.env.KYNVER_HEAVY_VERIFICATION_SKIP?.trim().toLowerCase();
12365
+ return v === "1" || v === "true" || v === "yes";
12366
+ }
12367
+ function resolveHeavyVerificationMaxConcurrent() {
12368
+ const env = process.env.KYNVER_HEAVY_VERIFICATION_MAX_CONCURRENT;
12369
+ if (env) return positiveInt5(env, DEFAULT_HEAVY_VERIFICATION_MAX_CONCURRENT);
12370
+ return DEFAULT_HEAVY_VERIFICATION_MAX_CONCURRENT;
12371
+ }
12372
+ function indexedSlotId(index) {
12373
+ return `slot-${index}`;
12374
+ }
12375
+ function slotFilePath(slotId, slotsDir = heavyVerificationSlotsDir()) {
12376
+ return path60.join(slotsDir, `${slotId}.json`);
12377
+ }
12378
+ function readSlotRecord(filePath) {
12379
+ if (!existsSync44(filePath)) return null;
12380
+ try {
12381
+ const parsed = JSON.parse(readFileSync17(filePath, "utf8"));
12382
+ if (typeof parsed.slotId === "string" && typeof parsed.pid === "number" && typeof parsed.acquiredAt === "string" && typeof parsed.command === "string") {
12383
+ return parsed;
12384
+ }
12385
+ } catch {
12386
+ return null;
12387
+ }
12388
+ return null;
12389
+ }
12390
+ function slotIsStale(record3, staleMs = DEFAULT_HEAVY_VERIFICATION_STALE_MS) {
12391
+ if (!record3) return true;
12392
+ if (!isPidAlive(record3.pid)) return true;
12393
+ const atMs = Date.parse(record3.acquiredAt);
12394
+ if (Number.isNaN(atMs)) return true;
12395
+ return Date.now() - atMs > staleMs;
12396
+ }
12397
+ function reclaimStaleSlot(filePath, staleMs) {
12398
+ const record3 = readSlotRecord(filePath);
12399
+ if (!slotIsStale(record3, staleMs)) return;
12400
+ try {
12401
+ unlinkSync4(filePath);
12402
+ } catch {
12403
+ }
12404
+ }
12405
+ function ensureSlotsDir(slotsDir) {
12406
+ mkdirSync8(slotsDir, { recursive: true });
12407
+ return slotsDir;
12408
+ }
12409
+ function reclaimStaleHeavyVerificationSlots(opts = {}) {
12410
+ const slotsDir = ensureSlotsDir(opts.slotsDir ?? ensureHeavyVerificationDirs());
12411
+ const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
12412
+ let reclaimed = 0;
12413
+ for (const name of readdirSync15(slotsDir)) {
12414
+ if (!name.endsWith(".json")) continue;
12415
+ const filePath = path60.join(slotsDir, name);
12416
+ const before = existsSync44(filePath);
12417
+ reclaimStaleSlot(filePath, staleMs);
12418
+ if (before && !existsSync44(filePath)) reclaimed += 1;
12419
+ }
12420
+ return reclaimed;
12421
+ }
12422
+ function listActiveHeavyVerificationSlots(opts = {}) {
12423
+ const slotsDir = opts.slotsDir ?? ensureHeavyVerificationDirs();
12424
+ const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
12425
+ reclaimStaleHeavyVerificationSlots({ slotsDir, staleMs });
12426
+ const active = [];
12427
+ for (const name of readdirSync15(slotsDir)) {
12428
+ if (!name.endsWith(".json")) continue;
12429
+ const record3 = readSlotRecord(path60.join(slotsDir, name));
12430
+ if (record3 && !slotIsStale(record3, staleMs)) active.push(record3);
12431
+ }
12432
+ return active;
12433
+ }
12434
+ function countActiveHeavyVerificationSlots(opts = {}) {
12435
+ return listActiveHeavyVerificationSlots(opts).length;
12436
+ }
12437
+ function tryAcquireHeavyVerificationSlot(command, opts = {}) {
12438
+ if (isHeavyVerificationGateSkipped()) {
12439
+ return {
12440
+ admitted: true,
12441
+ slotId: null,
12442
+ activeSlots: 0,
12443
+ maxSlots: resolveHeavyVerificationMaxConcurrent(),
12444
+ reason: null
12445
+ };
12446
+ }
12447
+ const slotsDir = opts.slotsDir ?? ensureHeavyVerificationDirs();
12448
+ const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
12449
+ const maxSlots = opts.maxSlots ?? resolveHeavyVerificationMaxConcurrent();
12450
+ reclaimStaleHeavyVerificationSlots({ slotsDir, staleMs });
12451
+ for (let index = 0; index < maxSlots; index += 1) {
12452
+ const slotId = indexedSlotId(index);
12453
+ const filePath = slotFilePath(slotId, slotsDir);
12454
+ const existing = readSlotRecord(filePath);
12455
+ if (existing && slotIsStale(existing, staleMs)) {
12456
+ try {
12457
+ unlinkSync4(filePath);
12458
+ } catch {
12459
+ }
12460
+ } else if (existing && !slotIsStale(existing, staleMs)) {
12461
+ continue;
12462
+ }
12463
+ const record3 = {
12464
+ slotId,
12465
+ pid: process.pid,
12466
+ acquiredAt: (/* @__PURE__ */ new Date()).toISOString(),
12467
+ command
12468
+ };
12469
+ try {
12470
+ const fd = openSync7(filePath, "wx");
12471
+ writeFileSync5(fd, JSON.stringify(record3, null, 2), "utf8");
12472
+ closeSync7(fd);
12473
+ const activeSlots2 = countActiveHeavyVerificationSlots({ slotsDir, staleMs });
12474
+ return {
12475
+ admitted: true,
12476
+ slotId,
12477
+ activeSlots: activeSlots2,
12478
+ maxSlots,
12479
+ reason: null
12480
+ };
12481
+ } catch (err) {
12482
+ if (err.code === "EEXIST") {
12483
+ continue;
12484
+ }
12485
+ throw err;
12486
+ }
12487
+ }
12488
+ const activeSlots = countActiveHeavyVerificationSlots({ slotsDir, staleMs });
12489
+ return {
12490
+ admitted: false,
12491
+ slotId: null,
12492
+ activeSlots,
12493
+ maxSlots,
12494
+ reason: `heavy verification at capacity (${activeSlots}/${maxSlots} slots)`
12495
+ };
12496
+ }
12497
+ function releaseHeavyVerificationSlot(slotId, opts = {}) {
12498
+ if (!slotId) return;
12499
+ const filePath = slotFilePath(slotId, opts.slotsDir ?? heavyVerificationSlotsDir());
12500
+ try {
12501
+ unlinkSync4(filePath);
12502
+ } catch {
12503
+ }
12504
+ }
12505
+ function assessHeavyVerificationGate(command, opts = {}) {
12506
+ if (isHeavyVerificationGateSkipped()) {
12507
+ return {
12508
+ admitted: true,
12509
+ slotId: null,
12510
+ activeSlots: 0,
12511
+ maxSlots: resolveHeavyVerificationMaxConcurrent(),
12512
+ reason: null
12513
+ };
12514
+ }
12515
+ const slotsDir = opts.slotsDir ?? ensureHeavyVerificationDirs();
12516
+ const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
12517
+ const maxSlots = opts.maxSlots ?? resolveHeavyVerificationMaxConcurrent();
12518
+ reclaimStaleHeavyVerificationSlots({ slotsDir, staleMs });
12519
+ const activeSlots = countActiveHeavyVerificationSlots({ slotsDir, staleMs });
12520
+ const admitted = activeSlots < maxSlots;
12521
+ return {
12522
+ admitted,
12523
+ slotId: null,
12524
+ activeSlots,
12525
+ maxSlots,
12526
+ reason: admitted ? null : `heavy verification at capacity (${activeSlots}/${maxSlots} slots); waiting for ${command}`
12527
+ };
12528
+ }
12529
+
12530
+ // src/heavy-verification/gate.ts
12024
12531
  import { spawnSync as spawnSync8 } from "node:child_process";
12532
+ function sleepMs3(ms) {
12533
+ if (ms <= 0) return;
12534
+ spawnSync8(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
12535
+ stdio: "ignore"
12536
+ });
12537
+ }
12538
+ function waitForHeavyVerificationSlot(command, timeoutMs, pollMs = 2e3, opts = {}) {
12539
+ const deadline = Date.now() + Math.max(0, timeoutMs);
12540
+ let verdict = tryAcquireHeavyVerificationSlot(command, opts);
12541
+ while (!verdict.admitted && Date.now() < deadline) {
12542
+ sleepMs3(Math.min(pollMs, deadline - Date.now()));
12543
+ verdict = tryAcquireHeavyVerificationSlot(command, opts);
12544
+ }
12545
+ return verdict;
12546
+ }
12025
12547
 
12026
12548
  // src/harness-worktree-build-guard.ts
12027
- import path58 from "node:path";
12549
+ import path61 from "node:path";
12028
12550
  function isPathUnderHarnessWorktree(cwd) {
12029
12551
  const worktreesDir = harnessWorktreesDir(resolveHarnessRoot());
12030
- const rel = path58.relative(worktreesDir, path58.resolve(cwd));
12031
- return rel.length > 0 && !rel.startsWith("..") && !path58.isAbsolute(rel);
12552
+ const rel = path61.relative(worktreesDir, path61.resolve(cwd));
12553
+ return rel.length > 0 && !rel.startsWith("..") && !path61.isAbsolute(rel);
12032
12554
  }
12033
12555
  function assessHarnessWorktreeBuildGuard(cwd) {
12034
12556
  if (!isPathUnderHarnessWorktree(cwd)) return { ok: true };
@@ -12052,7 +12574,7 @@ function envArgv(env) {
12052
12574
  return out;
12053
12575
  }
12054
12576
  function runSpawn(argv, opts) {
12055
- const res = spawnSync8(argv[0], argv.slice(1), {
12577
+ const res = spawnSync9(argv[0], argv.slice(1), {
12056
12578
  cwd: opts.cwd,
12057
12579
  env: opts.env,
12058
12580
  encoding: "utf8",
@@ -12068,8 +12590,25 @@ function runSpawn(argv, opts) {
12068
12590
  }
12069
12591
  function runBoundedBuildCheck(input) {
12070
12592
  const waitMs = input.waitForAdmissionMs ?? 6e5;
12593
+ const verificationGate = waitMs > 0 ? waitForHeavyVerificationSlot(input.command, waitMs) : tryAcquireOrAssessVerificationGate(input.command);
12594
+ if (!verificationGate.admitted) {
12595
+ return {
12596
+ ok: false,
12597
+ exitCode: 1,
12598
+ stdout: "",
12599
+ stderr: verificationGate.reason ?? "heavy verification gate denied",
12600
+ admitted: false,
12601
+ wrappedWithSystemd: false,
12602
+ nodeOptionsFlag: formatNodeOptionsFlag(),
12603
+ admission: assessBuildAdmission(),
12604
+ verificationGate,
12605
+ command: input.command
12606
+ };
12607
+ }
12608
+ const slotId = verificationGate.slotId;
12071
12609
  const admission = waitMs > 0 ? waitForBuildAdmission(waitMs) : assessBuildAdmission();
12072
12610
  if (!admission.admitted) {
12611
+ releaseHeavyVerificationSlot(slotId);
12073
12612
  return {
12074
12613
  ok: false,
12075
12614
  exitCode: 1,
@@ -12079,11 +12618,13 @@ function runBoundedBuildCheck(input) {
12079
12618
  wrappedWithSystemd: false,
12080
12619
  nodeOptionsFlag: formatNodeOptionsFlag(),
12081
12620
  admission,
12621
+ verificationGate,
12082
12622
  command: input.command
12083
12623
  };
12084
12624
  }
12085
12625
  const worktreeGuard = assessHarnessWorktreeBuildGuard(input.cwd);
12086
12626
  if (!worktreeGuard.ok) {
12627
+ releaseHeavyVerificationSlot(slotId);
12087
12628
  return {
12088
12629
  ok: false,
12089
12630
  exitCode: 1,
@@ -12093,6 +12634,7 @@ function runBoundedBuildCheck(input) {
12093
12634
  wrappedWithSystemd: false,
12094
12635
  nodeOptionsFlag: formatNodeOptionsFlag(),
12095
12636
  admission,
12637
+ verificationGate,
12096
12638
  command: input.command
12097
12639
  };
12098
12640
  }
@@ -12125,12 +12667,19 @@ function runBoundedBuildCheck(input) {
12125
12667
  wrappedWithSystemd: useSystemd,
12126
12668
  nodeOptionsFlag,
12127
12669
  admission,
12670
+ verificationGate,
12128
12671
  command: input.command
12129
12672
  };
12130
12673
  } finally {
12131
12674
  registerBuildEnd();
12675
+ releaseHeavyVerificationSlot(slotId);
12132
12676
  }
12133
12677
  }
12678
+ function tryAcquireOrAssessVerificationGate(command) {
12679
+ const acquired = waitForHeavyVerificationSlot(command, 0);
12680
+ if (acquired.admitted) return acquired;
12681
+ return { ...assessHeavyVerificationGate(command), slotId: null };
12682
+ }
12134
12683
 
12135
12684
  // src/harness-verify.ts
12136
12685
  var DEFAULT_HARNESS_VERIFY_COMMANDS = ["npm run typecheck", "npm run test"];
@@ -12213,7 +12762,7 @@ async function emitPlanProgress(args) {
12213
12762
  }
12214
12763
  function verifyPlanLocal(args) {
12215
12764
  const worktree = required(args.worktree ? String(args.worktree) : void 0, "worktree");
12216
- const cwd = path59.resolve(worktree);
12765
+ const cwd = path62.resolve(worktree);
12217
12766
  const summary = runHarnessVerifyCommands(cwd);
12218
12767
  const emitJson = args.json === true || args.json === "true";
12219
12768
  const payload = { passed: summary.passed, worktree: cwd, steps: summary.steps };
@@ -12262,9 +12811,9 @@ async function verifyPlan(args) {
12262
12811
  }
12263
12812
 
12264
12813
  // src/harness-verify-cli.ts
12265
- import path60 from "node:path";
12814
+ import path63 from "node:path";
12266
12815
  function runHarnessVerifyCli(args) {
12267
- const cwd = path60.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
12816
+ const cwd = path63.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
12268
12817
  const emitJson = args.json === true || args.json === "true" || args.emitJson === true || args.emitJson === "true";
12269
12818
  const commands = [];
12270
12819
  const rawCmd = args.command;
@@ -12292,6 +12841,7 @@ function runHarnessVerifyCli(args) {
12292
12841
  wrappedWithSystemd: s.result.wrappedWithSystemd,
12293
12842
  nodeOptionsFlag: s.result.nodeOptionsFlag,
12294
12843
  admission: s.result.admission,
12844
+ verificationGate: s.result.verificationGate,
12295
12845
  stderr: s.result.stderr.slice(0, 4e3)
12296
12846
  }))
12297
12847
  };
@@ -12308,7 +12858,7 @@ function runHarnessVerifyCli(args) {
12308
12858
  }
12309
12859
 
12310
12860
  // src/plan-persist-cli.ts
12311
- import { readFileSync as readFileSync16 } from "node:fs";
12861
+ import { readFileSync as readFileSync18 } from "node:fs";
12312
12862
  var OPERATIONS = ["create", "add_version", "update_metadata"];
12313
12863
  var FAILURE_KINDS = [
12314
12864
  "approval_guard",
@@ -12320,7 +12870,7 @@ var FAILURE_KINDS = [
12320
12870
  function readBodyArg(args) {
12321
12871
  const bodyFile = args.bodyFile ? String(args.bodyFile) : void 0;
12322
12872
  if (bodyFile) {
12323
- return { body: readFileSync16(bodyFile, "utf8"), bodyPathHint: bodyFile };
12873
+ return { body: readFileSync18(bodyFile, "utf8"), bodyPathHint: bodyFile };
12324
12874
  }
12325
12875
  const inline = args.body ? String(args.body) : void 0;
12326
12876
  if (inline) return { body: inline };
@@ -12700,7 +13250,7 @@ ${text.slice(0, 800)}`,
12700
13250
  }
12701
13251
 
12702
13252
  // src/monitor/monitor.service.ts
12703
- import path62 from "node:path";
13253
+ import path65 from "node:path";
12704
13254
 
12705
13255
  // src/monitor/monitor.classify.ts
12706
13256
  function classifyWorkerHealth(input) {
@@ -12752,19 +13302,19 @@ function classifyWorkerHealth(input) {
12752
13302
  }
12753
13303
 
12754
13304
  // src/monitor/monitor.store.ts
12755
- import { existsSync as existsSync44, mkdirSync as mkdirSync7, readdirSync as readdirSync15, unlinkSync as unlinkSync4 } from "node:fs";
12756
- import path61 from "node:path";
13305
+ import { existsSync as existsSync45, mkdirSync as mkdirSync9, readdirSync as readdirSync16, unlinkSync as unlinkSync5 } from "node:fs";
13306
+ import path64 from "node:path";
12757
13307
  function monitorsDir() {
12758
13308
  const { harnessRoot } = getHarnessPaths();
12759
- const dir = path61.join(harnessRoot, "monitors");
12760
- mkdirSync7(dir, { recursive: true });
13309
+ const dir = path64.join(harnessRoot, "monitors");
13310
+ mkdirSync9(dir, { recursive: true });
12761
13311
  return dir;
12762
13312
  }
12763
13313
  function monitorIdFor(runId, workerName) {
12764
13314
  return workerName ? `${safeSlug(runId)}--${safeSlug(workerName)}` : safeSlug(runId);
12765
13315
  }
12766
13316
  function monitorPath(monitorId) {
12767
- return path61.join(monitorsDir(), `${monitorId}.json`);
13317
+ return path64.join(monitorsDir(), `${monitorId}.json`);
12768
13318
  }
12769
13319
  function loadMonitorSession(monitorId) {
12770
13320
  return readJson(monitorPath(monitorId), void 0);
@@ -12774,18 +13324,18 @@ function saveMonitorSession(session) {
12774
13324
  }
12775
13325
  function deleteMonitorSession(monitorId) {
12776
13326
  const file = monitorPath(monitorId);
12777
- if (!existsSync44(file)) return false;
12778
- unlinkSync4(file);
13327
+ if (!existsSync45(file)) return false;
13328
+ unlinkSync5(file);
12779
13329
  return true;
12780
13330
  }
12781
13331
  function listMonitorSessions() {
12782
13332
  const dir = monitorsDir();
12783
- if (!existsSync44(dir)) return [];
13333
+ if (!existsSync45(dir)) return [];
12784
13334
  const entries = [];
12785
- for (const name of readdirSync15(dir)) {
13335
+ for (const name of readdirSync16(dir)) {
12786
13336
  if (!name.endsWith(".json")) continue;
12787
13337
  const session = readJson(
12788
- path61.join(dir, name),
13338
+ path64.join(dir, name),
12789
13339
  void 0
12790
13340
  );
12791
13341
  if (!session?.monitorId) continue;
@@ -12876,7 +13426,7 @@ async function fetchTaskLeasesForWorkers(input) {
12876
13426
  // src/monitor/monitor.service.ts
12877
13427
  function workerRecord2(runId, name) {
12878
13428
  return readJson(
12879
- path62.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
13429
+ path65.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
12880
13430
  void 0
12881
13431
  );
12882
13432
  }
@@ -13082,21 +13632,21 @@ async function runMonitorLoop(args) {
13082
13632
 
13083
13633
  // src/monitor/monitor-spawn.ts
13084
13634
  import { spawn as spawn6 } from "node:child_process";
13085
- import { closeSync as closeSync7, existsSync as existsSync45, openSync as openSync7 } from "node:fs";
13086
- import path63 from "node:path";
13635
+ import { closeSync as closeSync8, existsSync as existsSync46, openSync as openSync8 } from "node:fs";
13636
+ import path66 from "node:path";
13087
13637
  import { fileURLToPath as fileURLToPath4 } from "node:url";
13088
13638
  function resolveDefaultCliPath2() {
13089
- return path63.join(fileURLToPath4(new URL(".", import.meta.url)), "cli.js");
13639
+ return path66.join(fileURLToPath4(new URL(".", import.meta.url)), "cli.js");
13090
13640
  }
13091
13641
  function spawnMonitorSidecar(opts) {
13092
13642
  const cliPath = opts.cliPath ?? resolveDefaultCliPath2();
13093
- if (!existsSync45(cliPath)) return void 0;
13643
+ if (!existsSync46(cliPath)) return void 0;
13094
13644
  const monitorId = monitorIdFor(opts.runId, opts.workerName);
13095
13645
  const { harnessRoot } = getHarnessPaths();
13096
- const logPath = path63.join(harnessRoot, "monitors", `${monitorId}.log`);
13646
+ const logPath = path66.join(harnessRoot, "monitors", `${monitorId}.log`);
13097
13647
  let logFd;
13098
13648
  try {
13099
- logFd = openSync7(logPath, "a");
13649
+ logFd = openSync8(logPath, "a");
13100
13650
  } catch {
13101
13651
  logFd = void 0;
13102
13652
  }
@@ -13136,7 +13686,7 @@ function spawnMonitorSidecar(opts) {
13136
13686
  env: process.env
13137
13687
  })
13138
13688
  );
13139
- if (logFd !== void 0) closeSync7(logFd);
13689
+ if (logFd !== void 0) closeSync8(logFd);
13140
13690
  child.unref();
13141
13691
  const session = {
13142
13692
  monitorId,
@@ -13153,7 +13703,7 @@ function spawnMonitorSidecar(opts) {
13153
13703
  } catch {
13154
13704
  if (logFd !== void 0) {
13155
13705
  try {
13156
- closeSync7(logFd);
13706
+ closeSync8(logFd);
13157
13707
  } catch {
13158
13708
  }
13159
13709
  }
@@ -13213,7 +13763,7 @@ async function monitorTickCli(args) {
13213
13763
  }
13214
13764
 
13215
13765
  // src/post-restart-unblock.ts
13216
- import path64 from "node:path";
13766
+ import path67 from "node:path";
13217
13767
  function skip(runId, worker, taskId, agentOsId, leaseOwner, reason) {
13218
13768
  return { runId, worker, taskId, agentOsId, leaseOwner, action: "skipped", reason };
13219
13769
  }
@@ -13226,7 +13776,7 @@ async function postRestartUnblock(args) {
13226
13776
  const errors = [];
13227
13777
  for (const run of listRunRecords()) {
13228
13778
  for (const name of Object.keys(run.workers ?? {})) {
13229
- const workerPath = path64.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
13779
+ const workerPath = path67.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
13230
13780
  const worker = readJson(workerPath, void 0);
13231
13781
  if (!worker) {
13232
13782
  skipped.push(skip(run.id, name, "", "", "", "worker.json missing"));
@@ -13338,9 +13888,9 @@ async function postRestartUnblockCli(args) {
13338
13888
  }
13339
13889
 
13340
13890
  // src/default-repo-cli.ts
13341
- import path65 from "node:path";
13891
+ import path68 from "node:path";
13342
13892
  import { homedir as homedir15 } from "node:os";
13343
- var CONFIG_FILE2 = path65.join(homedir15(), ".kynver", "config.json");
13893
+ var CONFIG_FILE2 = path68.join(homedir15(), ".kynver", "config.json");
13344
13894
  function ensureDefaultRepo(opts) {
13345
13895
  const existing = loadUserConfig();
13346
13896
  const resolved = resolveDefaultRepo({ ...opts, config: existing });
@@ -13421,16 +13971,16 @@ function summarizeResolvedDefaultRepo(resolved) {
13421
13971
  }
13422
13972
 
13423
13973
  // src/doctor/runtime-takeover.ts
13424
- import path67 from "node:path";
13974
+ import path70 from "node:path";
13425
13975
 
13426
13976
  // src/doctor/runtime-takeover.probes.ts
13427
- import { accessSync, constants, existsSync as existsSync46, readFileSync as readFileSync17 } from "node:fs";
13977
+ import { accessSync, constants, existsSync as existsSync47, readFileSync as readFileSync19 } from "node:fs";
13428
13978
  import { homedir as homedir16 } from "node:os";
13429
- import path66 from "node:path";
13430
- import { spawnSync as spawnSync9 } from "node:child_process";
13979
+ import path69 from "node:path";
13980
+ import { spawnSync as spawnSync10 } from "node:child_process";
13431
13981
  function captureCommand(bin, args) {
13432
13982
  try {
13433
- const res = spawnSync9(bin, args, { encoding: "utf8" });
13983
+ const res = spawnSync10(bin, args, { encoding: "utf8" });
13434
13984
  const stdout = (res.stdout || "").trim();
13435
13985
  const stderr = (res.stderr || "").trim();
13436
13986
  const ok = res.status === 0;
@@ -13455,7 +14005,7 @@ function tokenPrefix(token) {
13455
14005
  return trimmed.length <= 12 ? `${trimmed}\u2026` : `${trimmed.slice(0, 12)}\u2026`;
13456
14006
  }
13457
14007
  function isWritable(target) {
13458
- if (!existsSync46(target)) return false;
14008
+ if (!existsSync47(target)) return false;
13459
14009
  try {
13460
14010
  accessSync(target, constants.W_OK);
13461
14011
  return true;
@@ -13468,15 +14018,15 @@ var defaultRuntimeTakeoverProbes = {
13468
14018
  commandOnPath: (bin) => captureCommand(process.platform === "win32" ? "where" : "which", [bin]),
13469
14019
  kynverVersion: (bin) => captureCommand(bin, ["--version"]),
13470
14020
  loadConfig: () => loadUserConfig(),
13471
- configFilePath: () => path66.join(homedir16(), ".kynver", "config.json"),
13472
- credentialsFilePath: () => path66.join(homedir16(), ".kynver", "credentials"),
14021
+ configFilePath: () => path69.join(homedir16(), ".kynver", "config.json"),
14022
+ credentialsFilePath: () => path69.join(homedir16(), ".kynver", "credentials"),
13473
14023
  readCredentials: () => {
13474
- const credPath = path66.join(homedir16(), ".kynver", "credentials");
13475
- if (!existsSync46(credPath)) {
14024
+ const credPath = path69.join(homedir16(), ".kynver", "credentials");
14025
+ if (!existsSync47(credPath)) {
13476
14026
  return { hasApiKey: false };
13477
14027
  }
13478
14028
  try {
13479
- const parsed = JSON.parse(readFileSync17(credPath, "utf8"));
14029
+ const parsed = JSON.parse(readFileSync19(credPath, "utf8"));
13480
14030
  return {
13481
14031
  hasApiKey: Boolean(parsed.apiKey?.trim()),
13482
14032
  runnerTokenPrefix: tokenPrefix(parsed.runnerToken),
@@ -13506,8 +14056,8 @@ var defaultRuntimeTakeoverProbes = {
13506
14056
  })()
13507
14057
  }),
13508
14058
  harnessRoot: () => resolveHarnessRoot(),
13509
- legacyOpenclawHarnessRoot: () => path66.join(homedir16(), ".openclaw", "harness"),
13510
- pathExists: (target) => existsSync46(target),
14059
+ legacyOpenclawHarnessRoot: () => path69.join(homedir16(), ".openclaw", "harness"),
14060
+ pathExists: (target) => existsSync47(target),
13511
14061
  pathWritable: (target) => isWritable(target)
13512
14062
  };
13513
14063
 
@@ -13913,8 +14463,8 @@ function assessVercelDeployEvidence(probes) {
13913
14463
  }
13914
14464
  function assessHarnessDirs(probes) {
13915
14465
  const harnessRoot = probes.harnessRoot();
13916
- const runsDir = path67.join(harnessRoot, "runs");
13917
- const worktreesDir = path67.join(harnessRoot, "worktrees");
14466
+ const runsDir = path70.join(harnessRoot, "runs");
14467
+ const worktreesDir = path70.join(harnessRoot, "worktrees");
13918
14468
  const displayHarnessRoot = redactHomePath(harnessRoot);
13919
14469
  const displayRunsDir = redactHomePath(runsDir);
13920
14470
  const displayWorktreesDir = redactHomePath(worktreesDir);
@@ -14178,9 +14728,9 @@ function applySchedulerCutoverAttestation(config) {
14178
14728
  }
14179
14729
 
14180
14730
  // src/scheduler-cutover-cli.ts
14181
- import path68 from "node:path";
14731
+ import path71 from "node:path";
14182
14732
  import { homedir as homedir17 } from "node:os";
14183
- var CONFIG_FILE3 = path68.join(homedir17(), ".kynver", "config.json");
14733
+ var CONFIG_FILE3 = path71.join(homedir17(), ".kynver", "config.json");
14184
14734
  function runSchedulerCutoverCheckCli(json = false) {
14185
14735
  const config = loadUserConfig();
14186
14736
  const report = assessSchedulerCutover(config);
@@ -14317,6 +14867,157 @@ async function runCronTickCli(args) {
14317
14867
  );
14318
14868
  }
14319
14869
 
14870
+ // src/lane/landing-maintainer-tick.ts
14871
+ import os9 from "node:os";
14872
+
14873
+ // src/lane/lane-spec.ts
14874
+ var LANDING_MAINTAINER_LANE_SPEC = {
14875
+ slug: "landing-maintainer",
14876
+ originCron: "maintain-8-blocker-and-pr-landing-workers",
14877
+ defaultRepo: "Totalsolutionsync/Kynver",
14878
+ landScript: "scripts/agent-os-land-pr.mjs",
14879
+ landScriptArgs: ["--skip-not-ready"]
14880
+ };
14881
+
14882
+ // src/lane/landing-maintainer-local.ts
14883
+ import { spawnSync as spawnSync11 } from "node:child_process";
14884
+ import path72 from "node:path";
14885
+ function runLandingWrapper(prNumber, repoRoot, execute) {
14886
+ const script = path72.join(repoRoot, LANDING_MAINTAINER_LANE_SPEC.landScript);
14887
+ const args = [script, String(prNumber), ...LANDING_MAINTAINER_LANE_SPEC.landScriptArgs];
14888
+ if (!execute) {
14889
+ return {
14890
+ action: { kind: "land_pr", prNumber, reason: "dry-run" },
14891
+ executed: false,
14892
+ exitCode: 0,
14893
+ stdout: `dry-run: node ${args.join(" ")}`,
14894
+ stderr: ""
14895
+ };
14896
+ }
14897
+ const result = spawnSync11("node", args, {
14898
+ cwd: repoRoot,
14899
+ encoding: "utf8",
14900
+ timeout: 10 * 60 * 1e3
14901
+ });
14902
+ return {
14903
+ action: { kind: "land_pr", prNumber, reason: "landing wrapper invoked" },
14904
+ executed: true,
14905
+ exitCode: result.status,
14906
+ stdout: result.stdout ?? "",
14907
+ stderr: result.stderr ?? ""
14908
+ };
14909
+ }
14910
+ function resolveLandingMaintainerRepoRoot(args) {
14911
+ const explicit = args.repoPath ? String(args.repoPath).trim() : "";
14912
+ if (explicit) return path72.resolve(explicit);
14913
+ const resolved = resolveDefaultRepo();
14914
+ return resolved?.repo ?? process.cwd();
14915
+ }
14916
+
14917
+ // src/lane/landing-maintainer-tick.ts
14918
+ async function runLandingMaintainerLaneTick(args) {
14919
+ const agentOsId = String(required(String(args.agentOsId || ""), "--agent-os-id"));
14920
+ const repoSlug = String(args.repo || LANDING_MAINTAINER_LANE_SPEC.defaultRepo).trim();
14921
+ const fleet = args.fleet === true || args.fleet === "true";
14922
+ const execute = args.execute !== false && args.execute !== "false";
14923
+ const runId = args.run ? String(args.run) : void 0;
14924
+ const resourceGate = observeRunnerResourceGate({
14925
+ runId: runId ?? "fleet-lane-tick"
14926
+ });
14927
+ const boxCapacity = {
14928
+ ...buildBoxResourceSnapshotFromGate(resourceGate, {
14929
+ harnessRunId: runId,
14930
+ boxKind: resolveBoxKindFromConfig(loadUserConfig()),
14931
+ hostLabel: os9.hostname()
14932
+ }),
14933
+ providerHealthy: resourceGate.ok,
14934
+ authorizedForRepair: resourceGate.ok,
14935
+ authorizedForLanding: resourceGate.ok,
14936
+ systemHealthBlockers: resourceGate.ok ? [] : [resourceGate.reason ?? "resource_gate_blocked"],
14937
+ actionableWorkers: resourceGate.activeWorkers
14938
+ };
14939
+ const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
14940
+ const secret = await resolveCallbackSecretWithMint(
14941
+ args.secret ? String(args.secret) : void 0,
14942
+ agentOsId,
14943
+ { baseUrl: base }
14944
+ );
14945
+ const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/fleet-landing-maintainer/tick`;
14946
+ const res = await postJson(url, secret, {
14947
+ repo: repoSlug,
14948
+ fleet,
14949
+ execute,
14950
+ runId,
14951
+ boxCapacity
14952
+ });
14953
+ const coordinator = res.response;
14954
+ const localOutcomes = [];
14955
+ const repoRoot = resolveLandingMaintainerRepoRoot(args);
14956
+ const actions = Array.isArray(coordinator?.localActions) ? coordinator.localActions : [];
14957
+ const holderBoxId = boxCapacity.boxId;
14958
+ for (const action of actions) {
14959
+ if (action.kind === "land_pr" && typeof action.prNumber === "number") {
14960
+ const outcome = runLandingWrapper(action.prNumber, repoRoot, execute);
14961
+ localOutcomes.push({
14962
+ action: outcome.action,
14963
+ executed: outcome.executed,
14964
+ exitCode: outcome.exitCode
14965
+ });
14966
+ if (execute && outcome.executed) {
14967
+ const merged = outcome.exitCode === 0;
14968
+ const prUrl = typeof action.prUrl === "string" && action.prUrl.trim() ? action.prUrl.trim() : `https://github.com/${repoSlug}/pull/${action.prNumber}`;
14969
+ try {
14970
+ await postJson(
14971
+ `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/fleet-landing-maintainer/outcome`,
14972
+ secret,
14973
+ { repo: repoSlug, prUrl, holderBoxId, merged }
14974
+ );
14975
+ } catch {
14976
+ }
14977
+ }
14978
+ continue;
14979
+ }
14980
+ localOutcomes.push({
14981
+ action,
14982
+ executed: false,
14983
+ exitCode: null
14984
+ });
14985
+ }
14986
+ return {
14987
+ repo: repoSlug,
14988
+ fleet,
14989
+ execute,
14990
+ coordinator,
14991
+ localOutcomes
14992
+ };
14993
+ }
14994
+
14995
+ // src/lane/lane-tick-cli.ts
14996
+ async function runLaneTickCli(args, laneFromArgv) {
14997
+ const lane = String(laneFromArgv ?? args.lane ?? "").trim();
14998
+ if (lane !== "landing-maintainer") {
14999
+ console.error(`unknown lane: ${lane || "(none)"}`);
15000
+ process.exit(1);
15001
+ }
15002
+ const result = await runLandingMaintainerLaneTick(args);
15003
+ if (args.json === true || args.json === "true") {
15004
+ console.log(JSON.stringify(result, null, 2));
15005
+ return;
15006
+ }
15007
+ console.log(
15008
+ [
15009
+ `fleet landing-maintainer tick`,
15010
+ `repo=${result.repo}`,
15011
+ `fleet=${result.fleet}`,
15012
+ `execute=${result.execute}`,
15013
+ `localOutcomes=${result.localOutcomes.length}`
15014
+ ].join(" ")
15015
+ );
15016
+ for (const row of result.localOutcomes) {
15017
+ console.log(` ${row.action.kind} pr=${row.action.prNumber ?? "-"} executed=${row.executed}`);
15018
+ }
15019
+ }
15020
+
14320
15021
  // src/cli.ts
14321
15022
  function isHelpFlag(arg) {
14322
15023
  return arg === "help" || arg === "--help" || arg === "-h";
@@ -14337,6 +15038,7 @@ function usage(code = 0) {
14337
15038
  " kynver daemon --run RUN_ID --agent-os-id AOS_ID [--execute] [--interval-ms MS]",
14338
15039
  " kynver run create [--repo /path/repo] [--name name] [--base origin/main]",
14339
15040
  " kynver run list",
15041
+ " kynver run resolve --name RUN_NAME",
14340
15042
  " kynver run status --run RUN_ID [--json] [--compact]",
14341
15043
  " kynver run dispatch --run RUN_ID --agent-os-id AOS_ID [--base-url URL] [--secret SECRET] [--execute] [--lane any|implementation|review|landing] [--target-task-id TASK_ID] [--executor harness] [--max-starts 1] [--lease-ms MS] [--owned path[,path]] [--model claude-opus-4-8] [--disk-path /] [--reconcile-stale-blockers]",
14342
15044
  " kynver run sweep --run RUN_ID --agent-os-id AOS_ID [--base-url URL] [--secret SECRET] [--grace-ms MS]",
@@ -14372,6 +15074,7 @@ function usage(code = 0) {
14372
15074
  " kynver scheduler attest-cutover [--json]",
14373
15075
  " kynver cron status [--json]",
14374
15076
  " kynver cron tick [--agent-os-id AOS_ID] [--json]",
15077
+ " kynver lane tick landing-maintainer [--fleet] [--repo OWNER/NAME] [--agent-os-id AOS_ID] [--execute] [--json]",
14375
15078
  " kynver board contract [--agent-os-id ID] [--base-url URL] [--since ISO] [--limit N]"
14376
15079
  ].join("\n")
14377
15080
  );
@@ -14383,7 +15086,7 @@ async function main(argv = process.argv.slice(2)) {
14383
15086
  const scope = argv.shift();
14384
15087
  let action;
14385
15088
  let rest;
14386
- if (scope === "run" || scope === "worker" || scope === "plan" || scope === "runner" || scope === "harness" || scope === "monitor" || scope === "doctor" || scope === "config" || scope === "scheduler" || scope === "cron" || scope === "board") {
15089
+ if (scope === "run" || scope === "worker" || scope === "plan" || scope === "runner" || scope === "harness" || scope === "monitor" || scope === "doctor" || scope === "config" || scope === "scheduler" || scope === "cron" || scope === "lane" || scope === "board") {
14387
15090
  action = argv.shift();
14388
15091
  rest = argv;
14389
15092
  } else {
@@ -14392,8 +15095,8 @@ async function main(argv = process.argv.slice(2)) {
14392
15095
  if (action && isHelpFlag(action) || rest.some(isHelpFlag)) return usage(0);
14393
15096
  const args = parseArgs(rest);
14394
15097
  const { runsDir, worktreesDir } = getPaths();
14395
- mkdirSync8(runsDir, { recursive: true });
14396
- mkdirSync8(worktreesDir, { recursive: true });
15098
+ mkdirSync10(runsDir, { recursive: true });
15099
+ mkdirSync10(worktreesDir, { recursive: true });
14397
15100
  if (shouldEnforceMemoryCostPackageGuardCli(scope, action)) {
14398
15101
  let repoRoot;
14399
15102
  const runId = args.run ? String(args.run).trim() : "";
@@ -14440,11 +15143,16 @@ async function main(argv = process.argv.slice(2)) {
14440
15143
  if (scope === "cron" && action === "tick") {
14441
15144
  return void await runCronTickCli(args);
14442
15145
  }
15146
+ if (scope === "lane" && action === "tick") {
15147
+ const laneName = rest.shift();
15148
+ return void await runLaneTickCli(parseArgs(rest), laneName);
15149
+ }
14443
15150
  if (scope === "board" && action === "contract") {
14444
15151
  return void await runCommandCenterContractCli(args);
14445
15152
  }
14446
15153
  if (scope === "run" && action === "create") return createRun(args);
14447
15154
  if (scope === "run" && action === "list") return listRuns();
15155
+ if (scope === "run" && action === "resolve") return resolveHarnessRunCli(args);
14448
15156
  if (scope === "run" && action === "status") return runStatus(args);
14449
15157
  if (scope === "run" && action === "dispatch") return void await dispatchRun(args);
14450
15158
  if (scope === "run" && action === "sweep") return void await sweepRun(args);
@@ -14828,6 +15536,7 @@ export {
14828
15536
  CODEX_DEFAULT_MODEL,
14829
15537
  DEFAULT_DISPATCH_LEASE_MS,
14830
15538
  DEFAULT_HARNESS_VERIFY_COMMANDS,
15539
+ DEFAULT_HEAVY_VERIFICATION_MAX_CONCURRENT,
14831
15540
  DEFAULT_MAX_ACTIONS_PER_SWEEP,
14832
15541
  DEFAULT_NODE_MODULES_AGE_MS,
14833
15542
  DEFAULT_RUN_DIRECTORIES_AGE_MS,
@@ -14856,6 +15565,7 @@ export {
14856
15565
  assessAutoCompleteEligibility,
14857
15566
  assessBuildAdmission,
14858
15567
  assessExitedWorkerSalvage,
15568
+ assessHeavyVerificationGate,
14859
15569
  assessOrphanWorktreeSafety,
14860
15570
  assessPrHandoffRequirement,
14861
15571
  assessWorkerLanding,
@@ -14889,6 +15599,7 @@ export {
14889
15599
  completeWorker,
14890
15600
  computeAttention,
14891
15601
  computeWorkerStatus,
15602
+ countActiveHeavyVerificationSlots,
14892
15603
  createRun,
14893
15604
  defaultBoxId,
14894
15605
  deriveRunStatus,
@@ -14935,6 +15646,7 @@ export {
14935
15646
  hasNestedRunsSegment,
14936
15647
  hashPlanBody,
14937
15648
  hermesCodexProvider,
15649
+ isActiveHarnessWorker,
14938
15650
  isClaudeFamilyProvider,
14939
15651
  isDashboardVercelUrl,
14940
15652
  isEngagementRequiredSkip,
@@ -14942,6 +15654,7 @@ export {
14942
15654
  isForbiddenWorkerEnvKey,
14943
15655
  isGeneratedHarnessPath,
14944
15656
  isHarnessRunMetadataPath,
15657
+ isHeavyVerificationGateSkipped,
14945
15658
  isInspectableVercelTarget,
14946
15659
  isKnownWorkerPersonaSlug,
14947
15660
  isKynverMonorepoRoot,
@@ -14956,6 +15669,7 @@ export {
14956
15669
  isWslHost,
14957
15670
  joinHarnessNotice,
14958
15671
  landingContractAttentionReason,
15672
+ listActiveHeavyVerificationSlots,
14959
15673
  listForbiddenWorkerEnvKeys,
14960
15674
  listMonitors,
14961
15675
  listOrchestrationProviderCapabilities,
@@ -14995,12 +15709,15 @@ export {
14995
15709
  providerCapableForRisk,
14996
15710
  readMemAvailableBytes,
14997
15711
  readProductionDbKeysFromEnvFile,
15712
+ reclaimStaleHeavyVerificationSlots,
15713
+ reconcileLocalOnlyMergedPrAttention,
14998
15714
  reconcileRunsCli,
14999
15715
  reconcileStaleWorkers,
15000
15716
  reconcileWorkerMetadata,
15001
15717
  reconcileWorkerMetadataCli,
15002
15718
  redactHarness,
15003
15719
  redactProviderErrorText,
15720
+ releaseHeavyVerificationSlot,
15004
15721
  remediateDefaultRepo,
15005
15722
  repairMissingRunMetadata,
15006
15723
  repairNestedRunsPath,
@@ -15012,6 +15729,9 @@ export {
15012
15729
  resolveConfiguredWorkerProvider,
15013
15730
  resolveDefaultRepo,
15014
15731
  resolveHarnessRoot,
15732
+ resolveHarnessRunByName,
15733
+ resolveHarnessRunCli,
15734
+ resolveHeavyVerificationMaxConcurrent,
15015
15735
  resolveOpenAiCodexRetryBudget,
15016
15736
  resolveOrchestrationPolicyMode,
15017
15737
  resolveOrchestrationRouting,
@@ -15049,12 +15769,14 @@ export {
15049
15769
  tailWorker,
15050
15770
  taskAllowsClaudeWorker,
15051
15771
  terminalFinalResultFromHeartbeat,
15772
+ tryAcquireHeavyVerificationSlot,
15052
15773
  usage,
15053
15774
  validateOwnedPaths,
15054
15775
  validateRepo,
15055
15776
  validateRunId,
15056
15777
  validateTailLines,
15057
15778
  validateWorkerName,
15779
+ waitForHeavyVerificationSlot,
15058
15780
  workerDirHasActiveRetentionSignals,
15059
15781
  workerPersonaLandingSlugs,
15060
15782
  workerPersonaReviewSlugs,