@kynver-app/runtime 0.1.120 → 0.1.123

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -523,23 +523,23 @@ function isWslHost() {
523
523
  function observeWslHostDisk(options = {}) {
524
524
  const wsl = options.forceWsl === void 0 ? isWslHost() : options.forceWsl;
525
525
  if (!wsl) return null;
526
- const path77 = options.wslHostMount?.trim() || process.env.KYNVER_WSL_HOST_MOUNT?.trim() || DEFAULT_WSL_HOST_MOUNT;
526
+ const path79 = options.wslHostMount?.trim() || process.env.KYNVER_WSL_HOST_MOUNT?.trim() || DEFAULT_WSL_HOST_MOUNT;
527
527
  const warnBelowBytes = options.wslHostFreeWarnBytes ?? DEFAULT_WSL_HOST_WARN_FREE_BYTES;
528
528
  const criticalBelowBytes = options.wslHostFreeCriticalBytes ?? DEFAULT_WSL_HOST_CRITICAL_FREE_BYTES;
529
529
  const statfs = options.statfs ?? statfsSync;
530
530
  let stats;
531
531
  try {
532
- stats = statfs(path77);
532
+ stats = statfs(path79);
533
533
  } catch (error) {
534
534
  return {
535
535
  ok: false,
536
- path: path77,
536
+ path: path79,
537
537
  freeBytes: 0,
538
538
  totalBytes: 0,
539
539
  usedPercent: 100,
540
540
  warnBelowBytes,
541
541
  criticalBelowBytes,
542
- reason: `Windows host disk probe failed at ${path77}: ${error.message}`,
542
+ reason: `Windows host disk probe failed at ${path79}: ${error.message}`,
543
543
  probeError: error.message
544
544
  };
545
545
  }
@@ -553,11 +553,11 @@ function observeWslHostDisk(options = {}) {
553
553
  let reason = null;
554
554
  if (!ok) {
555
555
  const tag = criticalFree ? "critical" : "warning";
556
- reason = `Windows host disk ${path77} at ${tag}: ${freeGiB} GiB free (<${(criticalFree ? criticalBelowBytes : warnBelowBytes) / 1024 / 1024 / 1024} GiB); WSL VHDX cannot grow safely. ${summarizeWslRecoverySteps()}`;
556
+ reason = `Windows host disk ${path79} at ${tag}: ${freeGiB} GiB free (<${(criticalFree ? criticalBelowBytes : warnBelowBytes) / 1024 / 1024 / 1024} GiB); WSL VHDX cannot grow safely. ${summarizeWslRecoverySteps()}`;
557
557
  }
558
558
  return {
559
559
  ok,
560
- path: path77,
560
+ path: path79,
561
561
  freeBytes,
562
562
  totalBytes,
563
563
  usedPercent,
@@ -583,12 +583,12 @@ var init_wsl_host = __esm({
583
583
  // src/disk-gate.ts
584
584
  import { statfsSync as statfsSync2 } from "node:fs";
585
585
  function observeRunnerDiskGate(input = {}) {
586
- const path77 = input.diskPath?.trim() || "/";
586
+ const path79 = input.diskPath?.trim() || "/";
587
587
  const warnBelowBytes = input.diskFreeWarnBytes ?? DEFAULT_WARN_FREE_BYTES;
588
588
  const criticalBelowBytes = input.diskFreeCriticalBytes ?? DEFAULT_CRITICAL_FREE_BYTES;
589
589
  const maxUsedPercent = input.diskMaxUsedPercent ?? DEFAULT_MAX_USED_PERCENT;
590
590
  const hardMaxUsedPercent = input.diskHardMaxUsedPercent ?? DEFAULT_HARD_MAX_USED_PERCENT;
591
- const stats = statfsSync2(path77);
591
+ const stats = statfsSync2(path79);
592
592
  const freeBytes = Number(stats.bavail) * Number(stats.bsize);
593
593
  const totalBytes = Number(stats.blocks) * Number(stats.bsize);
594
594
  const usedPercent = totalBytes > 0 ? (totalBytes - freeBytes) / totalBytes * 100 : 100;
@@ -611,7 +611,7 @@ function observeRunnerDiskGate(input = {}) {
611
611
  }
612
612
  return {
613
613
  ok,
614
- path: path77,
614
+ path: path79,
615
615
  freeBytes,
616
616
  totalBytes,
617
617
  usedPercent,
@@ -2616,23 +2616,29 @@ function resolveConfiguredCallbackSecret(argsSecret, agentOsId) {
2616
2616
  }
2617
2617
  return void 0;
2618
2618
  }
2619
- async function resolveCallbackSecretWithMint(argsSecret, agentOsId, opts) {
2619
+ async function tryResolveCallbackSecretWithMint(argsSecret, agentOsId, opts) {
2620
2620
  const configured = resolveConfiguredCallbackSecret(argsSecret, agentOsId);
2621
- if (configured) return configured;
2621
+ if (configured) return { ok: true, secret: configured };
2622
2622
  const apiKey = loadApiKey();
2623
2623
  const baseUrl = resolveConfiguredBaseUrl(opts?.baseUrl);
2624
2624
  if (apiKey && agentOsId && baseUrl) {
2625
2625
  try {
2626
2626
  const token = await fetchRunnerCredential(agentOsId, { baseUrl, apiKey });
2627
2627
  saveRunnerToken(agentOsId, token);
2628
- return token;
2628
+ return { ok: true, secret: token };
2629
2629
  } catch (error) {
2630
- failConfig(`runner credential mint failed: ${error.message}`);
2630
+ return { ok: false, reason: `runner credential mint failed: ${error.message}` };
2631
2631
  }
2632
2632
  }
2633
- failConfig(
2634
- "requires --secret, KYNVER_RUNNER_TOKEN, a scoped runner token (`kynver runner credential`), ~/.kynver/credentials runnerToken, KYNVER_API_KEY with an API base URL to mint one, or (legacy) KYNVER_RUNTIME_SECRET / KYNVER_CRON_SECRET / OPENCLAW_CRON_SECRET"
2635
- );
2633
+ return {
2634
+ ok: false,
2635
+ reason: "no runner credential: requires --secret, KYNVER_RUNNER_TOKEN, a scoped runner token (`kynver runner credential`), ~/.kynver/credentials runnerToken, or KYNVER_API_KEY + API base URL to mint one"
2636
+ };
2637
+ }
2638
+ async function resolveCallbackSecretWithMint(argsSecret, agentOsId, opts) {
2639
+ const result = await tryResolveCallbackSecretWithMint(argsSecret, agentOsId, opts);
2640
+ if (result.ok) return result.secret;
2641
+ failConfig(result.reason);
2636
2642
  }
2637
2643
  async function refreshRunnerToken(agentOsId, opts) {
2638
2644
  const apiKey = loadApiKey();
@@ -2752,7 +2758,10 @@ async function runSetup(args) {
2752
2758
  ...existing,
2753
2759
  ...inferSetupFields(existing, args),
2754
2760
  ...workerConfig,
2755
- workerProvider: typeof args.provider === "string" ? args.provider : existing.workerProvider || "cursor"
2761
+ workerProvider: typeof args.provider === "string" ? args.provider : existing.workerProvider || "cursor",
2762
+ // M5.4 chat-turn delegation: `--chat-oauth` persists consent to use the
2763
+ // Claude Code CLI's local OAuth token for delegated chat turns.
2764
+ ...args.chatOauth === true ? { chatUseClaudeOauth: true } : {}
2756
2765
  });
2757
2766
  saveUserConfig(config);
2758
2767
  const boxIdentity = resolveBoxIdentity(process.env, config);
@@ -2820,7 +2829,7 @@ var init_config = __esm({
2820
2829
 
2821
2830
  // src/cli.ts
2822
2831
  init_config();
2823
- import { mkdirSync as mkdirSync13, realpathSync } from "node:fs";
2832
+ import { mkdirSync as mkdirSync15, realpathSync } from "node:fs";
2824
2833
  import { fileURLToPath as fileURLToPath5 } from "node:url";
2825
2834
 
2826
2835
  // src/bootstrap.ts
@@ -3508,7 +3517,7 @@ async function runKynverCronTick(opts = {}) {
3508
3517
  init_util();
3509
3518
 
3510
3519
  // src/pipeline-tick.ts
3511
- import path55 from "node:path";
3520
+ import path57 from "node:path";
3512
3521
 
3513
3522
  // src/active-harness-workers.ts
3514
3523
  init_run_store();
@@ -6176,8 +6185,8 @@ function dirtyPathsCoveredByDisposableRemoval(changedFiles, removed) {
6176
6185
  if (removed.length === 0) return false;
6177
6186
  const removedSet = new Set(removed.map((p) => normalizeRelativePath(p)));
6178
6187
  return material.every((line) => {
6179
- const path77 = normalizeRelativePath(pathFromGitStatusLine(line));
6180
- return removedSet.has(path77);
6188
+ const path79 = normalizeRelativePath(pathFromGitStatusLine(line));
6189
+ return removedSet.has(path79);
6181
6190
  });
6182
6191
  }
6183
6192
 
@@ -7013,6 +7022,9 @@ function buildBoard(run, workers, compact) {
7013
7022
  }
7014
7023
  return board;
7015
7024
  }
7025
+ function isTerminalDoneBoardWorker(worker) {
7026
+ return worker.status === "done" && worker.attention === "done";
7027
+ }
7016
7028
  function buildRunBoard(runId) {
7017
7029
  const run = loadRun(runId);
7018
7030
  const names = Object.keys(run.workers || {});
@@ -7033,7 +7045,7 @@ function buildRunBoard(runId) {
7033
7045
  function buildCompactRunBoard(runId) {
7034
7046
  const run = loadRun(runId);
7035
7047
  const names = Object.keys(run.workers || {});
7036
- const workers = names.map((name) => {
7048
+ const allWorkers = names.map((name) => {
7037
7049
  const worker = readJson(
7038
7050
  path23.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
7039
7051
  void 0
@@ -7044,7 +7056,17 @@ function buildCompactRunBoard(runId) {
7044
7056
  if (isMetadataTerminalDone(worker)) return buildCompactDoneEntry(name, worker);
7045
7057
  return buildWorkerBoardEntry({ run, workerName: name, worker });
7046
7058
  });
7059
+ const workers = allWorkers.filter((worker) => !isTerminalDoneBoardWorker(worker));
7047
7060
  const board = buildBoard(run, workers, true);
7061
+ board.status = deriveRunStatus(run.status, allWorkers);
7062
+ board.summary = {
7063
+ ...board.summary ?? {},
7064
+ totalWorkerCount: allWorkers.length,
7065
+ shownWorkerCount: workers.length,
7066
+ omittedTerminalDoneWorkerCount: allWorkers.length - workers.length,
7067
+ allStatusCounts: countBy(allWorkers, "status"),
7068
+ allAttentionCounts: countBy(allWorkers, "attention")
7069
+ };
7048
7070
  writeJson(path23.join(runDirectory(run.id), "last-board-compact.json"), board);
7049
7071
  return board;
7050
7072
  }
@@ -9251,7 +9273,7 @@ init_util();
9251
9273
  // src/worker-metadata-reconcile.ts
9252
9274
  init_heartbeat();
9253
9275
  init_stream();
9254
- import { existsSync as existsSync25, lstatSync, readdirSync as readdirSync7, readlinkSync, renameSync as renameSync3, rmSync } from "node:fs";
9276
+ import { existsSync as existsSync25, lstatSync, readdirSync as readdirSync7, readlinkSync, renameSync as renameSync4, rmSync } from "node:fs";
9255
9277
  import path33 from "node:path";
9256
9278
 
9257
9279
  // src/worker-metadata-paths.ts
@@ -9300,7 +9322,7 @@ function resolveWorkerJsonPath(input) {
9300
9322
  }
9301
9323
 
9302
9324
  // src/run-metadata-retention.ts
9303
- import { existsSync as existsSync24, readdirSync as readdirSync6, statSync as statSync5 } from "node:fs";
9325
+ import { existsSync as existsSync24, mkdirSync as mkdirSync7, readdirSync as readdirSync6, renameSync as renameSync3, statSync as statSync5 } from "node:fs";
9304
9326
  import path32 from "node:path";
9305
9327
 
9306
9328
  // src/default-repo.ts
@@ -9362,6 +9384,7 @@ init_paths();
9362
9384
  init_run_store();
9363
9385
  init_util();
9364
9386
  var RUN_METADATA_ACTIVE_SIGNAL_MS = 15 * 60 * 1e3;
9387
+ var TERMINAL_WORKER_ARCHIVE_AGE_MS = 60 * 60 * 1e3;
9365
9388
  function isHarnessRunMetadataPath(targetPath, harnessRoot) {
9366
9389
  const resolved = path32.resolve(targetPath);
9367
9390
  const runsDir = path32.resolve(harnessRunsDir(harnessRoot));
@@ -9385,6 +9408,14 @@ function listWorkerNamesOnDisk(runDir2) {
9385
9408
  return [];
9386
9409
  }
9387
9410
  }
9411
+ function workerArchiveDir(runDir2) {
9412
+ return path32.join(runDir2, "archived-workers");
9413
+ }
9414
+ function uniqueArchivePath(runDir2, workerName) {
9415
+ const base = path32.join(workerArchiveDir(runDir2), safeSlug(workerName));
9416
+ if (!existsSync24(base)) return base;
9417
+ return `${base}-${Date.now()}`;
9418
+ }
9388
9419
  function pathRecentlyTouched(target, now, windowMs) {
9389
9420
  if (!existsSync24(target)) return false;
9390
9421
  try {
@@ -9411,6 +9442,41 @@ function workerDirHasActiveRetentionSignals(workerDir, now = Date.now(), windowM
9411
9442
  if (pathRecentlyTouched(artifacts.heartbeatPath, now, windowMs)) return true;
9412
9443
  return false;
9413
9444
  }
9445
+ function workerTerminalArchiveAgeMs(workerDir, worker, now) {
9446
+ const completionReportedAt = typeof worker.completionReportedAt === "string" ? Date.parse(worker.completionReportedAt) : NaN;
9447
+ if (Number.isFinite(completionReportedAt)) return now - completionReportedAt;
9448
+ const completionSnapshotAt = (() => {
9449
+ const raw = worker.completionResponse;
9450
+ if (!raw || typeof raw !== "object") return NaN;
9451
+ const task = raw.task;
9452
+ if (!task || typeof task !== "object") return NaN;
9453
+ const updatedAt = task.updatedAt;
9454
+ return typeof updatedAt === "string" ? Date.parse(updatedAt) : NaN;
9455
+ })();
9456
+ if (Number.isFinite(completionSnapshotAt)) return now - completionSnapshotAt;
9457
+ try {
9458
+ return now - statSync5(path32.join(workerDir, "worker.json")).mtimeMs;
9459
+ } catch {
9460
+ return 0;
9461
+ }
9462
+ }
9463
+ function isArchiveEligibleTerminalWorker(workerDir, worker, now, archiveAgeMs) {
9464
+ if (workerDirHasActiveRetentionSignals(workerDir, now)) {
9465
+ return { eligible: false, reason: "worker has active/recent retention signals" };
9466
+ }
9467
+ const status = typeof worker.status === "string" ? worker.status : "";
9468
+ const completionBlocker = typeof worker.completionBlocker === "string" && worker.completionBlocker.trim().length > 0;
9469
+ if (completionBlocker) return { eligible: false, reason: "worker has completion blocker" };
9470
+ const completionAcknowledged = typeof worker.completionReportedAt === "string" || worker.completionOutcome === "acknowledged";
9471
+ if (status !== "done" && !completionAcknowledged) {
9472
+ return { eligible: false, reason: `worker status is ${status || "unknown"}` };
9473
+ }
9474
+ const age = workerTerminalArchiveAgeMs(workerDir, worker, now);
9475
+ if (!Number.isFinite(age) || age < archiveAgeMs) {
9476
+ return { eligible: false, reason: "terminal worker is still within archive grace window" };
9477
+ }
9478
+ return { eligible: true, reason: "terminal worker metadata archived" };
9479
+ }
9414
9480
  function runDirHasActiveRetentionSignals(runDir2, now = Date.now(), windowMs = RUN_METADATA_ACTIVE_SIGNAL_MS) {
9415
9481
  for (const name of listWorkerNamesOnDisk(runDir2)) {
9416
9482
  if (workerDirHasActiveRetentionSignals(path32.join(runDir2, "workers", name), now, windowMs)) {
@@ -9488,6 +9554,59 @@ function repairMissingRunMetadata(harnessRootInput) {
9488
9554
  }
9489
9555
  return { runs: outcomes };
9490
9556
  }
9557
+ function archiveTerminalWorkerMetadata(harnessRootInput, options = {}) {
9558
+ const harnessRoot = normalizeHarnessRoot(harnessRootInput ?? resolveHarnessRoot());
9559
+ const runsDir = harnessRunsDir(harnessRoot);
9560
+ const now = options.now ?? Date.now();
9561
+ const archiveAgeMs = options.archiveAgeMs ?? TERMINAL_WORKER_ARCHIVE_AGE_MS;
9562
+ const outcomes = [];
9563
+ repairMissingRunMetadata(harnessRoot);
9564
+ for (const runId of listRunDirIds(runsDir)) {
9565
+ const runDir2 = path32.join(runsDir, runId);
9566
+ const runJsonPath = path32.join(runDir2, "run.json");
9567
+ const run = readJson(runJsonPath, void 0);
9568
+ if (!run?.id) continue;
9569
+ const names = /* @__PURE__ */ new Set([...Object.keys(run.workers || {}), ...listWorkerNamesOnDisk(runDir2)]);
9570
+ let changed = false;
9571
+ for (const name of names) {
9572
+ const workerDir = path32.join(runDir2, "workers", safeSlug(name));
9573
+ const workerJsonPath = path32.join(workerDir, "worker.json");
9574
+ const worker = readJson(workerJsonPath, void 0);
9575
+ if (!worker) {
9576
+ const archivedWorkerDir = path32.join(workerArchiveDir(runDir2), safeSlug(name));
9577
+ if (existsSync24(archivedWorkerDir) && (run.workers[safeSlug(name)] || run.workers[name])) {
9578
+ delete run.workers[safeSlug(name)];
9579
+ delete run.workers[name];
9580
+ changed = true;
9581
+ outcomes.push({
9582
+ runId: run.id,
9583
+ worker: name,
9584
+ action: "archived",
9585
+ reason: "removed stale live index entry for archived worker metadata",
9586
+ archivePath: archivedWorkerDir
9587
+ });
9588
+ continue;
9589
+ }
9590
+ outcomes.push({ runId: run.id, worker: name, action: "skipped", reason: "worker.json missing" });
9591
+ continue;
9592
+ }
9593
+ const eligibility = isArchiveEligibleTerminalWorker(workerDir, worker, now, archiveAgeMs);
9594
+ if (!eligibility.eligible) {
9595
+ outcomes.push({ runId: run.id, worker: name, action: "skipped", reason: eligibility.reason });
9596
+ continue;
9597
+ }
9598
+ const archivePath = uniqueArchivePath(runDir2, name);
9599
+ mkdirSync7(path32.dirname(archivePath), { recursive: true });
9600
+ renameSync3(workerDir, archivePath);
9601
+ delete run.workers[safeSlug(name)];
9602
+ delete run.workers[name];
9603
+ changed = true;
9604
+ outcomes.push({ runId: run.id, worker: name, action: "archived", reason: eligibility.reason, archivePath });
9605
+ }
9606
+ if (changed) saveRun(run);
9607
+ }
9608
+ return { workers: outcomes };
9609
+ }
9491
9610
  function collectFilesystemLiveRunKeys(harnessRoot, now = Date.now()) {
9492
9611
  const keys = /* @__PURE__ */ new Set();
9493
9612
  const runsDir = harnessRunsDir(harnessRoot);
@@ -9517,9 +9636,9 @@ function materializeSymlinkedRunDir(harnessRoot, runId) {
9517
9636
  return null;
9518
9637
  }
9519
9638
  const staging = `${canonical}.materialize-${Date.now()}`;
9520
- renameSync3(linkedTarget, staging);
9639
+ renameSync4(linkedTarget, staging);
9521
9640
  rmSync(canonical);
9522
- renameSync3(staging, canonical);
9641
+ renameSync4(staging, canonical);
9523
9642
  return linkedTarget;
9524
9643
  }
9525
9644
  function relocateArtifacts(input) {
@@ -9528,7 +9647,7 @@ function relocateArtifacts(input) {
9528
9647
  const from = path33.join(input.fromDir, fileName);
9529
9648
  const to = path33.join(input.toDir, fileName);
9530
9649
  if (!existsSync25(from) || existsSync25(to)) continue;
9531
- renameSync3(from, to);
9650
+ renameSync4(from, to);
9532
9651
  moved.push(fileName);
9533
9652
  }
9534
9653
  return moved;
@@ -9986,7 +10105,14 @@ function reconcileStaleWorkers() {
9986
10105
  const metadataReconcile = reconcileWorkerMetadata();
9987
10106
  if (staleReconcileDisabled()) {
9988
10107
  const localPrAttentionReconcile2 = reconcileLocalOnlyMergedPrAttention();
9989
- return { workers: [], finalizedRuns: finalizeStaleRuns(), metadataReconcile, localPrAttentionReconcile: localPrAttentionReconcile2 };
10108
+ const terminalWorkerArchive2 = archiveTerminalWorkerMetadata();
10109
+ return {
10110
+ workers: [],
10111
+ finalizedRuns: finalizeStaleRuns(),
10112
+ metadataReconcile,
10113
+ localPrAttentionReconcile: localPrAttentionReconcile2,
10114
+ terminalWorkerArchive: terminalWorkerArchive2
10115
+ };
9990
10116
  }
9991
10117
  const outcomes = [];
9992
10118
  const now = Date.now();
@@ -10065,7 +10191,8 @@ function reconcileStaleWorkers() {
10065
10191
  }
10066
10192
  }
10067
10193
  const localPrAttentionReconcile = reconcileLocalOnlyMergedPrAttention();
10068
- return { workers: outcomes, finalizedRuns: finalizeStaleRuns(), metadataReconcile, localPrAttentionReconcile };
10194
+ const terminalWorkerArchive = archiveTerminalWorkerMetadata();
10195
+ return { workers: outcomes, finalizedRuns: finalizeStaleRuns(), metadataReconcile, localPrAttentionReconcile, terminalWorkerArchive };
10069
10196
  }
10070
10197
  function reconcileRunsCli() {
10071
10198
  const result = reconcileStaleWorkers();
@@ -10084,6 +10211,10 @@ function reconcileRunsCli() {
10084
10211
  acc[row.action] = (acc[row.action] ?? 0) + 1;
10085
10212
  return acc;
10086
10213
  }, {});
10214
+ const archiveTotals = result.terminalWorkerArchive.workers.reduce((acc, row) => {
10215
+ acc[row.action] = (acc[row.action] ?? 0) + 1;
10216
+ return acc;
10217
+ }, {});
10087
10218
  console.log(
10088
10219
  JSON.stringify(
10089
10220
  {
@@ -10101,11 +10232,16 @@ function reconcileRunsCli() {
10101
10232
  totals: localPrAttentionTotals,
10102
10233
  total: result.localPrAttentionReconcile.workers.length
10103
10234
  },
10235
+ terminalWorkerArchive: {
10236
+ totals: archiveTotals,
10237
+ total: result.terminalWorkerArchive.workers.length
10238
+ },
10104
10239
  finalizedRuns: result.finalizedRuns.length,
10105
10240
  details: {
10106
10241
  workers: result.workers,
10107
10242
  metadataReconcile: result.metadataReconcile.workers,
10108
10243
  localPrAttentionReconcile: result.localPrAttentionReconcile.workers,
10244
+ terminalWorkerArchive: result.terminalWorkerArchive.workers,
10109
10245
  runMetadataRetention: result.metadataReconcile.runMetadataRetention.runs,
10110
10246
  finalizedRuns: result.finalizedRuns
10111
10247
  }
@@ -10312,11 +10448,11 @@ init_box_identity();
10312
10448
 
10313
10449
  // src/cleanup.ts
10314
10450
  init_paths();
10315
- import path51 from "node:path";
10451
+ import path53 from "node:path";
10316
10452
 
10317
10453
  // src/cleanup-guards.ts
10318
10454
  init_landing_gate();
10319
- import path38 from "node:path";
10455
+ import path39 from "node:path";
10320
10456
 
10321
10457
  // src/cleanup-index-status.ts
10322
10458
  init_git();
@@ -10459,6 +10595,44 @@ init_status();
10459
10595
  // src/cleanup-run-liveness.ts
10460
10596
  init_status();
10461
10597
 
10598
+ // src/cleanup-worker-harness-live.ts
10599
+ init_heartbeat();
10600
+ init_util();
10601
+ function heartbeatContentIsFresh(worker, now = Date.now(), windowMs = RUN_METADATA_ACTIVE_SIGNAL_MS) {
10602
+ const heartbeat = parseHeartbeat(worker.heartbeatPath);
10603
+ if (!heartbeat.lastHeartbeatAt) return false;
10604
+ const age = now - Date.parse(heartbeat.lastHeartbeatAt);
10605
+ return Number.isFinite(age) && age >= 0 && age < windowMs;
10606
+ }
10607
+ function isHarnessWorkerHarnessLive(worker, now = Date.now(), windowMs = RUN_METADATA_ACTIVE_SIGNAL_MS) {
10608
+ if (isPidAlive(worker.pid)) return true;
10609
+ return heartbeatContentIsFresh(worker, now, windowMs);
10610
+ }
10611
+
10612
+ // src/cleanup-run-liveness.ts
10613
+ function deriveRunTerminal(indexed, ctx) {
10614
+ if (ctx) return ctx.runTerminalCache.derive(indexed.run);
10615
+ return deriveTerminalRunStatus(indexed.run);
10616
+ }
10617
+ function isWorkerProcessLive(indexed, now = Date.now()) {
10618
+ return isHarnessWorkerHarnessLive(indexed.worker, now);
10619
+ }
10620
+ function isRunStaleActive(indexed, ctx) {
10621
+ if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) return false;
10622
+ return deriveRunTerminal(indexed, ctx) !== null;
10623
+ }
10624
+ function runBlocksWorktreeRemoval(indexed, ctx, now = Date.now()) {
10625
+ if (isWorkerProcessLive(indexed, now)) return true;
10626
+ if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) return false;
10627
+ if (isRunStaleActive(indexed, ctx)) return false;
10628
+ const status = indexedWorktreeStatus(indexed);
10629
+ if (!isFinishedWorkerStatus(status)) {
10630
+ if (!isWorkerProcessLive(indexed, now)) return false;
10631
+ return true;
10632
+ }
10633
+ return false;
10634
+ }
10635
+
10462
10636
  // src/cleanup-completion-blocker.ts
10463
10637
  init_landing_gate();
10464
10638
  init_status();
@@ -10480,51 +10654,80 @@ function completionBlockerBlocksWorktreeRemoval(indexed, status) {
10480
10654
  return false;
10481
10655
  }
10482
10656
 
10483
- // src/cleanup-run-liveness.ts
10657
+ // src/cleanup-salvage-evidence.ts
10658
+ init_git();
10484
10659
  init_util();
10485
- var TERMINAL_WORKER_JSON_STATUSES = /* @__PURE__ */ new Set([
10486
- "done",
10487
- "exited",
10488
- "blocked",
10489
- "failed",
10490
- "abandoned"
10491
- ]);
10492
- function deriveRunTerminal(indexed, ctx) {
10493
- if (ctx) return ctx.runTerminalCache.derive(indexed.run);
10494
- return deriveTerminalRunStatus(indexed.run);
10495
- }
10496
- function isWorkerProcessLive(indexed) {
10497
- if (isPidAlive(indexed.worker.pid)) return true;
10498
- if (typeof indexed.worker.completionReportedAt === "string" && indexed.worker.completionReportedAt.trim().length > 0) {
10499
- return false;
10500
- }
10501
- const workerStatus2 = indexed.worker.status;
10502
- if (workerStatus2 && TERMINAL_WORKER_JSON_STATUSES.has(workerStatus2)) return false;
10503
- if (!indexed.worker.pid) {
10504
- if (workerStatus2 !== "running") return false;
10505
- return indexedWorktreeStatus(indexed).alive;
10506
- }
10507
- return false;
10660
+ import { existsSync as existsSync26, mkdirSync as mkdirSync8, writeFileSync as writeFileSync6 } from "node:fs";
10661
+ import path38 from "node:path";
10662
+ function salvageEvidenceDir(harnessRoot, runId, workerName) {
10663
+ return path38.join(harnessRoot, "salvage", safeSlug(runId), safeSlug(workerName));
10508
10664
  }
10509
- function isRunStaleActive(indexed, ctx) {
10510
- if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) return false;
10511
- return deriveRunTerminal(indexed, ctx) !== null;
10665
+ function hasSalvageEvidence(input) {
10666
+ return existsSync26(path38.join(salvageEvidenceDir(input.harnessRoot, input.runId, input.workerName), "evidence.json"));
10512
10667
  }
10513
- function runBlocksWorktreeRemoval(indexed, ctx) {
10514
- if (isWorkerProcessLive(indexed)) return true;
10515
- const workerStatus2 = indexed.worker.status;
10516
- if (workerStatus2 && TERMINAL_WORKER_JSON_STATUSES.has(workerStatus2) && !indexed.worker.completionBlocker) {
10517
- return false;
10518
- }
10519
- const status = indexedWorktreeStatus(indexed);
10520
- if (completionBlockerBlocksWorktreeRemoval(indexed, status)) return true;
10521
- if (isFinishedWorkerStatus(status)) return false;
10522
- if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) return false;
10523
- if (isRunStaleActive(indexed, ctx)) return false;
10524
- return deriveRunTerminal(indexed, ctx) === null;
10668
+ function writeWorktreeSalvageEvidence(input) {
10669
+ const dir = salvageEvidenceDir(input.harnessRoot, input.indexed.runId, input.indexed.workerName);
10670
+ mkdirSync8(dir, { recursive: true });
10671
+ const patchPath = path38.join(dir, "salvage.patch");
10672
+ let wrotePatch = false;
10673
+ if (existsSync26(input.indexed.worktreePath)) {
10674
+ const diff = gitCapture(input.indexed.worktreePath, ["diff", "HEAD"]);
10675
+ const staged = gitCapture(input.indexed.worktreePath, ["diff", "--cached"]);
10676
+ const patch = [diff.stdout, staged.stdout].filter((chunk) => chunk.trim()).join("\n");
10677
+ if (patch.trim()) {
10678
+ writeFileSync6(patchPath, patch.endsWith("\n") ? patch : `${patch}
10679
+ `);
10680
+ wrotePatch = true;
10681
+ }
10682
+ }
10683
+ const record = {
10684
+ capturedAt: new Date(input.now ?? Date.now()).toISOString(),
10685
+ skipReason: input.skipReason,
10686
+ runId: input.indexed.runId,
10687
+ workerName: input.indexed.workerName,
10688
+ worktreePath: input.indexed.worktreePath,
10689
+ taskId: input.indexed.worker.taskId,
10690
+ agentOsId: input.indexed.worker.agentOsId,
10691
+ branch: input.indexed.worker.branch,
10692
+ completionBlocker: input.indexed.worker.completionBlocker,
10693
+ finalResult: input.status.finalResult,
10694
+ changedFiles: input.status.changedFiles,
10695
+ gitAncestry: input.status.gitAncestry,
10696
+ prUrl: input.status.prUrl ?? null,
10697
+ ...wrotePatch ? { patchPath } : {}
10698
+ };
10699
+ writeFileSync6(path38.join(dir, "evidence.json"), `${JSON.stringify(record, null, 2)}
10700
+ `);
10701
+ return record;
10525
10702
  }
10526
10703
 
10527
10704
  // src/cleanup-guards.ts
10705
+ var SALVAGE_ELIGIBLE_SKIP_REASONS = /* @__PURE__ */ new Set([
10706
+ "completion_blocked",
10707
+ "dirty_worktree",
10708
+ "pr_or_unmerged_commits",
10709
+ "landing_blocked"
10710
+ ]);
10711
+ function maybeSalvageBlockedWorktree(input) {
10712
+ if (!SALVAGE_ELIGIBLE_SKIP_REASONS.has(input.skipReason)) return false;
10713
+ if (isWorkerProcessLive(input.indexed, input.now)) return false;
10714
+ if (hasSalvageEvidence({
10715
+ harnessRoot: input.harnessRoot,
10716
+ runId: input.indexed.runId,
10717
+ workerName: input.indexed.workerName
10718
+ })) {
10719
+ return true;
10720
+ }
10721
+ if (!input.writeSalvageEvidence) return false;
10722
+ writeWorktreeSalvageEvidence({
10723
+ harnessRoot: input.harnessRoot,
10724
+ indexed: input.indexed,
10725
+ skipReason: input.skipReason,
10726
+ status: input.status,
10727
+ now: input.now
10728
+ });
10729
+ return true;
10730
+ }
10528
10731
  function effectiveWorktreeAgeMs(input) {
10529
10732
  const { indexed, includeOrphans, worktreesAgeMs, terminalWorktreesAgeMs } = input;
10530
10733
  if (!indexed) return includeOrphans ? terminalWorktreesAgeMs : worktreesAgeMs;
@@ -10539,8 +10742,18 @@ function effectiveWorktreeAgeMs(input) {
10539
10742
  }
10540
10743
  return worktreesAgeMs;
10541
10744
  }
10745
+ function resolveHarnessRoot2(input, indexed) {
10746
+ if (input.harnessRoot?.trim()) return path39.resolve(input.harnessRoot);
10747
+ const workerDir = indexed.worker.workerDir;
10748
+ if (!workerDir) return null;
10749
+ const runsMarker = `${path39.sep}runs${path39.sep}`;
10750
+ const idx = workerDir.indexOf(runsMarker);
10751
+ if (idx < 0) return null;
10752
+ return workerDir.slice(0, idx + runsMarker.length - 1);
10753
+ }
10542
10754
  function skipWorktreeRemoval(input) {
10543
10755
  const { indexed, includeOrphans, worktreesAgeMs, ageMs, orphanSafety, worktreeRemovalGuard } = input;
10756
+ const now = input.now ?? Date.now();
10544
10757
  if (!indexed) {
10545
10758
  if (!includeOrphans) return "orphan_without_flag";
10546
10759
  return orphanSafety ?? null;
@@ -10548,25 +10761,55 @@ function skipWorktreeRemoval(input) {
10548
10761
  const ageThresholdMs = effectiveWorktreeAgeMs(input);
10549
10762
  if (worktreesAgeMs <= 0 && !includeOrphans && ageThresholdMs <= 0) return "worktrees_disabled";
10550
10763
  if (ageThresholdMs > 0 && ageMs < ageThresholdMs) return "below_age_threshold";
10551
- if (isWorkerProcessLive(indexed)) return "active_worker";
10764
+ if (isWorkerProcessLive(indexed, now)) return "active_worker";
10765
+ const status = resolveWorktreeGuardStatus(indexed, input.liveness);
10766
+ const salvageHarnessRoot = resolveHarnessRoot2(input, indexed);
10767
+ const salvageOrBlock = (skipReason) => {
10768
+ if (salvageHarnessRoot && maybeSalvageBlockedWorktree({
10769
+ indexed,
10770
+ harnessRoot: salvageHarnessRoot,
10771
+ skipReason,
10772
+ status,
10773
+ now,
10774
+ writeSalvageEvidence: input.writeSalvageEvidence === true
10775
+ })) {
10776
+ return null;
10777
+ }
10778
+ return skipReason;
10779
+ };
10780
+ if (completionBlockerBlocksWorktreeRemoval(indexed, status)) {
10781
+ const blocked = salvageOrBlock("completion_blocked");
10782
+ if (blocked) return blocked;
10783
+ }
10552
10784
  if (indexedWorktreeHasMaterialChanges(indexed, input.liveness?.gitStatusCache)) {
10553
- return "dirty_worktree";
10785
+ const blocked = salvageOrBlock("dirty_worktree");
10786
+ if (blocked) return blocked;
10554
10787
  }
10555
10788
  const ahead = input.liveness?.gitRevCache?.countAheadOfMain(input.worktreePath);
10556
- if (ahead !== null && ahead !== void 0 && ahead > 0) return "pr_or_unmerged_commits";
10557
- const status = resolveWorktreeGuardStatus(indexed, input.liveness);
10558
- if (completionBlockerBlocksWorktreeRemoval(indexed, status)) return "completion_blocked";
10559
- if (runBlocksWorktreeRemoval(indexed, input.liveness)) return "run_still_active";
10789
+ if (ahead !== null && ahead !== void 0 && ahead > 0) {
10790
+ const blocked = salvageOrBlock("pr_or_unmerged_commits");
10791
+ if (blocked) return blocked;
10792
+ }
10793
+ if (runBlocksWorktreeRemoval(indexed, input.liveness, now)) return "run_still_active";
10560
10794
  if (!isFinishedWorkerStatus(status)) return "run_still_active";
10561
- if (isPrOrUnmergedWork(status)) return "pr_or_unmerged_commits";
10562
- if (materialWorktreeChanges2(status.changedFiles).length > 0) return "dirty_worktree";
10795
+ if (isPrOrUnmergedWork(status)) {
10796
+ const blocked = salvageOrBlock("pr_or_unmerged_commits");
10797
+ if (blocked) return blocked;
10798
+ }
10799
+ if (materialWorktreeChanges2(status.changedFiles).length > 0) {
10800
+ const blocked = salvageOrBlock("dirty_worktree");
10801
+ if (blocked) return blocked;
10802
+ }
10563
10803
  const landing = assessWorkerLanding({
10564
10804
  finalResult: status.finalResult,
10565
10805
  changedFiles: status.changedFiles,
10566
10806
  gitAncestry: status.gitAncestry,
10567
10807
  prUrl: prUrlFromFinalResult(status.finalResult)
10568
10808
  });
10569
- if (landing.blocked) return "landing_blocked";
10809
+ if (landing.blocked) {
10810
+ const blocked = salvageOrBlock("landing_blocked");
10811
+ if (blocked) return blocked;
10812
+ }
10570
10813
  if (worktreeRemovalGuard && input.worktreePath) {
10571
10814
  const overlay = worktreeRemovalGuard({
10572
10815
  worktreePath: input.worktreePath,
@@ -10583,7 +10826,7 @@ function skipWorktreeRemoval(input) {
10583
10826
  function skipDependencyCacheRemoval(input) {
10584
10827
  const { indexed, nodeModulesAgeMs, ageMs, worktreePath, activeWorktreePaths, diskPressure } = input;
10585
10828
  if (!diskPressure && ageMs < nodeModulesAgeMs) return "below_age_threshold";
10586
- if (activeWorktreePaths.has(path38.resolve(worktreePath))) return "active_worker";
10829
+ if (activeWorktreePaths.has(path39.resolve(worktreePath))) return "active_worker";
10587
10830
  if (indexed && isWorkerProcessLive(indexed)) return "active_worker";
10588
10831
  if (indexed && indexedWorktreeHasMaterialChanges(indexed, input.gitStatusCache)) {
10589
10832
  return "dirty_worktree";
@@ -10612,11 +10855,11 @@ var LIVE_SKIP_REASONS = /* @__PURE__ */ new Set([
10612
10855
  function collectPreservedLivePaths(actions, skips) {
10613
10856
  const out = [];
10614
10857
  const seen = /* @__PURE__ */ new Set();
10615
- const push = (path77, reason, detail) => {
10616
- const key = `${path77}\0${reason}`;
10858
+ const push = (path79, reason, detail) => {
10859
+ const key = `${path79}\0${reason}`;
10617
10860
  if (seen.has(key) || out.length >= MAX_PRESERVED_LIVE_PATH_SAMPLES) return;
10618
10861
  seen.add(key);
10619
- out.push({ path: path77, reason, ...detail ? { detail } : {} });
10862
+ out.push({ path: path79, reason, ...detail ? { detail } : {} });
10620
10863
  };
10621
10864
  for (const skip2 of skips) {
10622
10865
  if (!LIVE_SKIP_REASONS.has(skip2.reason)) continue;
@@ -10631,31 +10874,16 @@ function collectPreservedLivePaths(actions, skips) {
10631
10874
  }
10632
10875
 
10633
10876
  // src/cleanup-run-directory.ts
10634
- import { existsSync as existsSync27, readdirSync as readdirSync9, statSync as statSync7 } from "node:fs";
10635
- import path40 from "node:path";
10877
+ import { existsSync as existsSync27, readdirSync as readdirSync8, statSync as statSync6 } from "node:fs";
10878
+ import path41 from "node:path";
10636
10879
 
10637
10880
  // src/cleanup-active-worktrees.ts
10638
10881
  init_run_store();
10639
10882
  init_paths();
10640
- import { existsSync as existsSync26, readdirSync as readdirSync8, statSync as statSync6 } from "node:fs";
10641
- import path39 from "node:path";
10883
+ import path40 from "node:path";
10642
10884
  init_util();
10643
- function workerHasRecentHarnessActivity(worker, now) {
10644
- const paths = [worker.heartbeatPath, worker.stdoutPath, worker.stderrPath];
10645
- for (const target of paths) {
10646
- if (!existsSync26(target)) continue;
10647
- try {
10648
- const age = now - statSync6(target).mtimeMs;
10649
- if (Number.isFinite(age) && age >= 0 && age < RUN_METADATA_ACTIVE_SIGNAL_MS) return true;
10650
- } catch {
10651
- }
10652
- }
10653
- return false;
10654
- }
10655
10885
  function isActiveHarnessWorker2(worker, now) {
10656
- if (isPidAlive(worker.pid)) return true;
10657
- if (worker.status === "running" && workerHasRecentHarnessActivity(worker, now)) return true;
10658
- return false;
10886
+ return isHarnessWorkerHarnessLive(worker, now);
10659
10887
  }
10660
10888
  function collectActiveWorktreeGuards(harnessRoots, now = Date.now()) {
10661
10889
  const activeWorktreePaths = /* @__PURE__ */ new Set();
@@ -10665,11 +10893,11 @@ function collectActiveWorktreeGuards(harnessRoots, now = Date.now()) {
10665
10893
  let runHasLive = false;
10666
10894
  for (const name of Object.keys(run.workers || {})) {
10667
10895
  const worker = readJson(
10668
- path39.join(runDirectoryAt(harnessRoot, run.id), "workers", safeSlug(name), "worker.json"),
10896
+ path40.join(runDirectoryAt(harnessRoot, run.id), "workers", safeSlug(name), "worker.json"),
10669
10897
  void 0
10670
10898
  );
10671
10899
  if (!worker?.worktreePath) continue;
10672
- const worktreePath = path39.resolve(worker.worktreePath);
10900
+ const worktreePath = path40.resolve(worker.worktreePath);
10673
10901
  if (!isActiveHarnessWorker2(worker, now)) continue;
10674
10902
  runHasLive = true;
10675
10903
  activeWorktreePaths.add(worktreePath);
@@ -10691,20 +10919,20 @@ function isWorktreeOnLiveRun(worktreePath, harnessRoot, runId, liveRunKeys) {
10691
10919
  init_util();
10692
10920
  function pathAgeMs(target, now) {
10693
10921
  try {
10694
- const mtime = statSync7(target).mtimeMs;
10922
+ const mtime = statSync6(target).mtimeMs;
10695
10923
  return Math.max(0, now - mtime);
10696
10924
  } catch {
10697
10925
  return 0;
10698
10926
  }
10699
10927
  }
10700
10928
  function loadRunStatus(harnessRoot, runId) {
10701
- const runPath = path40.join(harnessRoot, "runs", runId, "run.json");
10929
+ const runPath = path41.join(harnessRoot, "runs", runId, "run.json");
10702
10930
  if (!existsSync27(runPath)) return null;
10703
10931
  return readJson(runPath, null);
10704
10932
  }
10705
10933
  function runDirectoryIsEmpty(runPath) {
10706
10934
  try {
10707
- const entries = readdirSync9(runPath);
10935
+ const entries = readdirSync8(runPath);
10708
10936
  return entries.length === 0;
10709
10937
  } catch {
10710
10938
  return false;
@@ -10728,7 +10956,7 @@ function scanStaleRunDirectoryCandidates(opts) {
10728
10956
  const candidates = [];
10729
10957
  let entries;
10730
10958
  try {
10731
- entries = readdirSync9(opts.worktreesDir, { withFileTypes: true });
10959
+ entries = readdirSync8(opts.worktreesDir, { withFileTypes: true });
10732
10960
  } catch {
10733
10961
  return [];
10734
10962
  }
@@ -10736,7 +10964,7 @@ function scanStaleRunDirectoryCandidates(opts) {
10736
10964
  if (!runEntry.isDirectory()) continue;
10737
10965
  const runId = runEntry.name;
10738
10966
  if (opts.runIdFilter && runId !== opts.runIdFilter) continue;
10739
- const runPath = path40.join(opts.worktreesDir, runId);
10967
+ const runPath = path41.join(opts.worktreesDir, runId);
10740
10968
  if (!runDirectoryIsEmpty(runPath)) continue;
10741
10969
  candidates.push({
10742
10970
  kind: "remove_run_directory",
@@ -10756,8 +10984,8 @@ import { existsSync as existsSync30, rmSync as rmSync3 } from "node:fs";
10756
10984
 
10757
10985
  // src/cleanup-dir-size.ts
10758
10986
  import { execFileSync as execFileSync2 } from "node:child_process";
10759
- import { existsSync as existsSync28, readdirSync as readdirSync10, statSync as statSync8 } from "node:fs";
10760
- import path41 from "node:path";
10987
+ import { existsSync as existsSync28, readdirSync as readdirSync9, statSync as statSync7 } from "node:fs";
10988
+ import path42 from "node:path";
10761
10989
  var DEFAULT_DU_TIMEOUT_MS = 2500;
10762
10990
  function directorySizeBytesDu(root, timeoutMs = DEFAULT_DU_TIMEOUT_MS) {
10763
10991
  if (!existsSync28(root)) return 0;
@@ -10785,16 +11013,16 @@ function directorySizeBytes(root, maxEntries = 5e4) {
10785
11013
  const current = stack.pop();
10786
11014
  let entries;
10787
11015
  try {
10788
- entries = readdirSync10(current);
11016
+ entries = readdirSync9(current);
10789
11017
  } catch {
10790
11018
  continue;
10791
11019
  }
10792
11020
  for (const name of entries) {
10793
11021
  if (seen++ > maxEntries) return null;
10794
- const full = path41.join(current, name);
11022
+ const full = path42.join(current, name);
10795
11023
  let st;
10796
11024
  try {
10797
- st = statSync8(full);
11025
+ st = statSync7(full);
10798
11026
  } catch {
10799
11027
  continue;
10800
11028
  }
@@ -10810,7 +11038,7 @@ import { existsSync as existsSync29, rmSync as rmSync2 } from "node:fs";
10810
11038
  init_paths();
10811
11039
 
10812
11040
  // src/cleanup-path-ownership.ts
10813
- import { lstatSync as lstatSync2, readdirSync as readdirSync11 } from "node:fs";
11041
+ import { lstatSync as lstatSync2, readdirSync as readdirSync10 } from "node:fs";
10814
11042
  function readPathOwnership(targetPath) {
10815
11043
  try {
10816
11044
  const st = lstatSync2(targetPath);
@@ -10827,7 +11055,7 @@ function pathHasForeignOwnedEntry(targetPath, maxEntries = 32) {
10827
11055
  if (!root) return false;
10828
11056
  if (root.foreign) return true;
10829
11057
  try {
10830
- const names = readdirSync11(targetPath);
11058
+ const names = readdirSync10(targetPath);
10831
11059
  let checked = 0;
10832
11060
  for (const name of names) {
10833
11061
  if (checked >= maxEntries) break;
@@ -10843,20 +11071,20 @@ function pathHasForeignOwnedEntry(targetPath, maxEntries = 32) {
10843
11071
 
10844
11072
  // src/cleanup-privileged-remove.ts
10845
11073
  import { spawnSync as spawnSync6 } from "node:child_process";
10846
- import path43 from "node:path";
11074
+ import path44 from "node:path";
10847
11075
 
10848
11076
  // src/cleanup-harness-path-validate.ts
10849
- import path42 from "node:path";
11077
+ import path43 from "node:path";
10850
11078
  function isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, cacheDirName) {
10851
- const resolved = path42.resolve(targetPath);
10852
- const suffix = `${path42.sep}${cacheDirName}`;
11079
+ const resolved = path43.resolve(targetPath);
11080
+ const suffix = `${path43.sep}${cacheDirName}`;
10853
11081
  const cachePath = resolved.endsWith(suffix) ? resolved : null;
10854
11082
  if (!cachePath) return "path_outside_harness";
10855
- const rel = path42.relative(worktreesDir, cachePath);
10856
- if (rel.startsWith("..") || path42.isAbsolute(rel)) return "path_outside_harness";
10857
- const parts = rel.split(path42.sep);
11083
+ const rel = path43.relative(worktreesDir, cachePath);
11084
+ if (rel.startsWith("..") || path43.isAbsolute(rel)) return "path_outside_harness";
11085
+ const parts = rel.split(path43.sep);
10858
11086
  if (parts.length < 3 || parts[parts.length - 1] !== cacheDirName) return "path_outside_harness";
10859
- if (!resolved.startsWith(path42.resolve(harnessRoot))) return "path_outside_harness";
11087
+ if (!resolved.startsWith(path43.resolve(harnessRoot))) return "path_outside_harness";
10860
11088
  return null;
10861
11089
  }
10862
11090
  function isHarnessNodeModulesPath(targetPath, harnessRoot, worktreesDir) {
@@ -10866,16 +11094,16 @@ function isHarnessNextCachePath(targetPath, harnessRoot, worktreesDir) {
10866
11094
  return isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, ".next");
10867
11095
  }
10868
11096
  function isHarnessBuildCachePath(targetPath, harnessRoot, worktreesDir) {
10869
- const resolved = path42.resolve(targetPath);
10870
- const relToWt = path42.relative(worktreesDir, resolved);
10871
- if (relToWt.startsWith("..") || path42.isAbsolute(relToWt)) return "path_outside_harness";
10872
- const parts = relToWt.split(path42.sep);
11097
+ const resolved = path43.resolve(targetPath);
11098
+ const relToWt = path43.relative(worktreesDir, resolved);
11099
+ if (relToWt.startsWith("..") || path43.isAbsolute(relToWt)) return "path_outside_harness";
11100
+ const parts = relToWt.split(path43.sep);
10873
11101
  if (parts.length < 3) return "path_outside_harness";
10874
- if (!resolved.startsWith(path42.resolve(harnessRoot))) return "path_outside_harness";
11102
+ if (!resolved.startsWith(path43.resolve(harnessRoot))) return "path_outside_harness";
10875
11103
  return null;
10876
11104
  }
10877
11105
  function isHarnessGeneratedCachePath(targetPath, harnessRoot, worktreesDir) {
10878
- const resolved = path42.resolve(targetPath);
11106
+ const resolved = path43.resolve(targetPath);
10879
11107
  return isHarnessNodeModulesPath(resolved, harnessRoot, worktreesDir) === null || isHarnessNextCachePath(resolved, harnessRoot, worktreesDir) === null || isHarnessBuildCachePath(resolved, harnessRoot, worktreesDir) === null;
10880
11108
  }
10881
11109
 
@@ -10909,12 +11137,12 @@ function tryPrivilegedReclaimHarnessCache(targetPath, harnessRoot, worktreesDir)
10909
11137
  "chown",
10910
11138
  "-R",
10911
11139
  `${effectiveUid}:${effectiveGid}`,
10912
- path43.resolve(targetPath)
11140
+ path44.resolve(targetPath)
10913
11141
  ]);
10914
11142
  if (chown.ok) {
10915
11143
  return { ok: true, method: "chown_then_rm" };
10916
11144
  }
10917
- const rm = runSudoNonInteractive(["rm", "-rf", path43.resolve(targetPath)]);
11145
+ const rm = runSudoNonInteractive(["rm", "-rf", path44.resolve(targetPath)]);
10918
11146
  if (rm.ok) {
10919
11147
  return { ok: true, method: "sudo_rm" };
10920
11148
  }
@@ -11115,27 +11343,27 @@ function removeWorktree(candidate, execute) {
11115
11343
  }
11116
11344
 
11117
11345
  // src/cleanup-scan.ts
11118
- import { existsSync as existsSync31, readdirSync as readdirSync12, statSync as statSync9 } from "node:fs";
11119
- import path44 from "node:path";
11346
+ import { existsSync as existsSync31, readdirSync as readdirSync11, statSync as statSync8 } from "node:fs";
11347
+ import path45 from "node:path";
11120
11348
  function pathAgeMs2(target, now) {
11121
11349
  try {
11122
- const mtime = statSync9(target).mtimeMs;
11350
+ const mtime = statSync8(target).mtimeMs;
11123
11351
  return Math.max(0, now - mtime);
11124
11352
  } catch {
11125
11353
  return 0;
11126
11354
  }
11127
11355
  }
11128
11356
  function isPathInside(child, parent) {
11129
- const rel = path44.relative(parent, child);
11130
- return rel === "" || !rel.startsWith("..") && !path44.isAbsolute(rel);
11357
+ const rel = path45.relative(parent, child);
11358
+ return rel === "" || !rel.startsWith("..") && !path45.isAbsolute(rel);
11131
11359
  }
11132
11360
  function collectBuildCacheForWorktree(worktreePath, opts, seen, meta) {
11133
11361
  const out = [];
11134
11362
  for (const rel of HARNESS_BUILD_CACHE_RELATIVE_PATHS) {
11135
11363
  if (rel === ".next") continue;
11136
- const target = path44.join(worktreePath, rel);
11364
+ const target = path45.join(worktreePath, rel);
11137
11365
  if (!existsSync31(target)) continue;
11138
- const resolved = path44.resolve(target);
11366
+ const resolved = path45.resolve(target);
11139
11367
  if (seen.has(resolved)) continue;
11140
11368
  if (!isPathInside(resolved, opts.harnessRoot)) continue;
11141
11369
  seen.add(resolved);
@@ -11165,12 +11393,12 @@ function scanBuildCacheCandidates(opts) {
11165
11393
  );
11166
11394
  }
11167
11395
  if (!opts.includeOrphans || !existsSync31(opts.worktreesDir)) return candidates;
11168
- for (const runEntry of readdirSync12(opts.worktreesDir, { withFileTypes: true })) {
11396
+ for (const runEntry of readdirSync11(opts.worktreesDir, { withFileTypes: true })) {
11169
11397
  if (!runEntry.isDirectory()) continue;
11170
- const runPath = path44.join(opts.worktreesDir, runEntry.name);
11171
- for (const workerEntry of readdirSync12(runPath, { withFileTypes: true })) {
11398
+ const runPath = path45.join(opts.worktreesDir, runEntry.name);
11399
+ for (const workerEntry of readdirSync11(runPath, { withFileTypes: true })) {
11172
11400
  if (!workerEntry.isDirectory()) continue;
11173
- const worktreePath = path44.join(runPath, workerEntry.name);
11401
+ const worktreePath = path45.join(runPath, workerEntry.name);
11174
11402
  candidates.push(
11175
11403
  ...collectBuildCacheForWorktree(worktreePath, opts, seen, {
11176
11404
  runId: runEntry.name,
@@ -11208,21 +11436,21 @@ function scanWorktreeCandidates(opts) {
11208
11436
  if (!orphanEnabled || !existsSync31(opts.worktreesDir)) return candidates;
11209
11437
  const indexedPaths = /* @__PURE__ */ new Set();
11210
11438
  for (const entry of opts.index.values()) {
11211
- indexedPaths.add(path44.resolve(entry.worktreePath));
11439
+ indexedPaths.add(path45.resolve(entry.worktreePath));
11212
11440
  }
11213
- for (const runEntry of readdirSync12(opts.worktreesDir, { withFileTypes: true })) {
11441
+ for (const runEntry of readdirSync11(opts.worktreesDir, { withFileTypes: true })) {
11214
11442
  if (!runEntry.isDirectory()) continue;
11215
11443
  if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
11216
- const runPath = path44.join(opts.worktreesDir, runEntry.name);
11444
+ const runPath = path45.join(opts.worktreesDir, runEntry.name);
11217
11445
  let workerEntries;
11218
11446
  try {
11219
- workerEntries = readdirSync12(runPath, { withFileTypes: true });
11447
+ workerEntries = readdirSync11(runPath, { withFileTypes: true });
11220
11448
  } catch {
11221
11449
  continue;
11222
11450
  }
11223
11451
  for (const workerEntry of workerEntries) {
11224
11452
  if (!workerEntry.isDirectory()) continue;
11225
- const worktreePath = path44.resolve(path44.join(runPath, workerEntry.name));
11453
+ const worktreePath = path45.resolve(path45.join(runPath, workerEntry.name));
11226
11454
  if (seen.has(worktreePath)) continue;
11227
11455
  if (indexedPaths.has(worktreePath)) continue;
11228
11456
  if (!isPathInside(worktreePath, opts.harnessRoot)) continue;
@@ -11241,27 +11469,27 @@ function scanWorktreeCandidates(opts) {
11241
11469
  }
11242
11470
 
11243
11471
  // src/cleanup-dependency-scan.ts
11244
- import { existsSync as existsSync32, readdirSync as readdirSync13, statSync as statSync10 } from "node:fs";
11245
- import path45 from "node:path";
11472
+ import { existsSync as existsSync32, readdirSync as readdirSync12, statSync as statSync9 } from "node:fs";
11473
+ import path46 from "node:path";
11246
11474
  var DEPENDENCY_CACHE_DIRS = [
11247
11475
  { dirName: "node_modules", kind: "remove_node_modules" },
11248
11476
  { dirName: ".next", kind: "remove_next_cache" }
11249
11477
  ];
11250
11478
  function pathAgeMs3(target, now) {
11251
11479
  try {
11252
- const mtime = statSync10(target).mtimeMs;
11480
+ const mtime = statSync9(target).mtimeMs;
11253
11481
  return Math.max(0, now - mtime);
11254
11482
  } catch {
11255
11483
  return 0;
11256
11484
  }
11257
11485
  }
11258
11486
  function isPathInside2(child, parent) {
11259
- const rel = path45.relative(parent, child);
11260
- return rel === "" || !rel.startsWith("..") && !path45.isAbsolute(rel);
11487
+ const rel = path46.relative(parent, child);
11488
+ return rel === "" || !rel.startsWith("..") && !path46.isAbsolute(rel);
11261
11489
  }
11262
11490
  function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
11263
11491
  if (!existsSync32(targetPath)) return;
11264
- const resolved = path45.resolve(targetPath);
11492
+ const resolved = path46.resolve(targetPath);
11265
11493
  if (seen.has(resolved)) return;
11266
11494
  if (!isPathInside2(resolved, opts.harnessRoot)) return;
11267
11495
  seen.add(resolved);
@@ -11278,7 +11506,7 @@ function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
11278
11506
  }
11279
11507
  function scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, meta) {
11280
11508
  for (const entry of DEPENDENCY_CACHE_DIRS) {
11281
- pushCandidate2(candidates, seen, opts, path45.join(worktreePath, entry.dirName), entry.kind, meta);
11509
+ pushCandidate2(candidates, seen, opts, path46.join(worktreePath, entry.dirName), entry.kind, meta);
11282
11510
  }
11283
11511
  }
11284
11512
  function scanDependencyCacheCandidates(opts) {
@@ -11293,19 +11521,19 @@ function scanDependencyCacheCandidates(opts) {
11293
11521
  });
11294
11522
  }
11295
11523
  if (!opts.includeOrphans || !existsSync32(opts.worktreesDir)) return candidates;
11296
- for (const runEntry of readdirSync13(opts.worktreesDir, { withFileTypes: true })) {
11524
+ for (const runEntry of readdirSync12(opts.worktreesDir, { withFileTypes: true })) {
11297
11525
  if (!runEntry.isDirectory()) continue;
11298
11526
  if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
11299
- const runPath = path45.join(opts.worktreesDir, runEntry.name);
11527
+ const runPath = path46.join(opts.worktreesDir, runEntry.name);
11300
11528
  let workerEntries;
11301
11529
  try {
11302
- workerEntries = readdirSync13(runPath, { withFileTypes: true });
11530
+ workerEntries = readdirSync12(runPath, { withFileTypes: true });
11303
11531
  } catch {
11304
11532
  continue;
11305
11533
  }
11306
11534
  for (const workerEntry of workerEntries) {
11307
11535
  if (!workerEntry.isDirectory()) continue;
11308
- const worktreePath = path45.join(runPath, workerEntry.name);
11536
+ const worktreePath = path46.join(runPath, workerEntry.name);
11309
11537
  scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, {
11310
11538
  runId: runEntry.name,
11311
11539
  worker: workerEntry.name
@@ -11317,11 +11545,11 @@ function scanDependencyCacheCandidates(opts) {
11317
11545
 
11318
11546
  // src/cleanup-duplicate-worktrees.ts
11319
11547
  init_git();
11320
- import { existsSync as existsSync33, statSync as statSync11 } from "node:fs";
11321
- import path46 from "node:path";
11548
+ import { existsSync as existsSync33, statSync as statSync10 } from "node:fs";
11549
+ import path47 from "node:path";
11322
11550
  function pathAgeMs4(target, now) {
11323
11551
  try {
11324
- const mtime = statSync11(target).mtimeMs;
11552
+ const mtime = statSync10(target).mtimeMs;
11325
11553
  return Math.max(0, now - mtime);
11326
11554
  } catch {
11327
11555
  return 0;
@@ -11348,32 +11576,24 @@ function parseWorktreePorcelain(output) {
11348
11576
  return records;
11349
11577
  }
11350
11578
  function isUnderWorktreesDir(worktreePath, worktreesDir) {
11351
- const rel = path46.relative(path46.resolve(worktreesDir), path46.resolve(worktreePath));
11352
- return rel !== "" && !rel.startsWith("..") && !path46.isAbsolute(rel);
11353
- }
11354
- function isCleanWorktree(worktreePath, repoRoot) {
11355
- try {
11356
- const porcelain = git(repoRoot, ["-C", worktreePath, "status", "--porcelain"], {
11357
- allowFailure: true
11358
- });
11359
- return !String(porcelain || "").trim();
11360
- } catch {
11361
- return false;
11362
- }
11579
+ const rel = path47.relative(path47.resolve(worktreesDir), path47.resolve(worktreePath));
11580
+ return rel !== "" && !rel.startsWith("..") && !path47.isAbsolute(rel);
11363
11581
  }
11582
+ var MAX_DUPLICATE_WORKTREE_CANDIDATES_PER_REPO = 200;
11364
11583
  function scanDuplicateWorktreeCandidates(opts) {
11365
11584
  if (!opts.includeOrphans || !existsSync33(opts.worktreesDir)) return [];
11366
11585
  const repos = /* @__PURE__ */ new Set();
11367
11586
  for (const entry of opts.index.values()) {
11368
- if (entry.run.repo) repos.add(path46.resolve(entry.run.repo));
11587
+ if (entry.run.repo) repos.add(path47.resolve(entry.run.repo));
11369
11588
  }
11370
11589
  const indexedPaths = /* @__PURE__ */ new Set();
11371
11590
  for (const entry of opts.index.values()) {
11372
- indexedPaths.add(path46.resolve(entry.worktreePath));
11591
+ indexedPaths.add(path47.resolve(entry.worktreePath));
11373
11592
  }
11374
11593
  const candidates = [];
11375
11594
  const seen = /* @__PURE__ */ new Set();
11376
11595
  for (const repoRoot of repos) {
11596
+ let repoCandidateCount = 0;
11377
11597
  let porcelain;
11378
11598
  try {
11379
11599
  porcelain = git(repoRoot, ["worktree", "list", "--porcelain"], { allowFailure: true });
@@ -11382,18 +11602,19 @@ function scanDuplicateWorktreeCandidates(opts) {
11382
11602
  }
11383
11603
  const worktrees = parseWorktreePorcelain(porcelain);
11384
11604
  for (const wt of worktrees) {
11385
- const resolved = path46.resolve(wt.path);
11386
- if (resolved === path46.resolve(repoRoot)) continue;
11605
+ if (repoCandidateCount >= MAX_DUPLICATE_WORKTREE_CANDIDATES_PER_REPO) break;
11606
+ const resolved = path47.resolve(wt.path);
11607
+ if (resolved === path47.resolve(repoRoot)) continue;
11387
11608
  if (!isUnderWorktreesDir(resolved, opts.worktreesDir)) continue;
11388
11609
  if (indexedPaths.has(resolved)) continue;
11389
11610
  if (seen.has(resolved)) continue;
11390
11611
  if (!existsSync33(resolved)) continue;
11391
- if (!isCleanWorktree(resolved, repoRoot)) continue;
11392
- const rel = path46.relative(opts.worktreesDir, resolved);
11393
- const parts = rel.split(path46.sep);
11612
+ const rel = path47.relative(opts.worktreesDir, resolved);
11613
+ const parts = rel.split(path47.sep);
11394
11614
  const runId = parts[0];
11395
11615
  const worker = parts[1] ?? "unknown";
11396
11616
  seen.add(resolved);
11617
+ repoCandidateCount += 1;
11397
11618
  candidates.push({
11398
11619
  kind: "remove_worktree",
11399
11620
  path: resolved,
@@ -11411,12 +11632,22 @@ function scanDuplicateWorktreeCandidates(opts) {
11411
11632
  // src/cleanup-worktree-index.ts
11412
11633
  init_run_store();
11413
11634
  init_util();
11414
- import path47 from "node:path";
11635
+ import path48 from "node:path";
11636
+ function filterWorktreeIndexForRoot(index, harnessRoot) {
11637
+ const resolvedRoot = path48.resolve(harnessRoot);
11638
+ const scoped = /* @__PURE__ */ new Map();
11639
+ for (const [key, entry] of index) {
11640
+ const entryRoot = entry.harnessRoot ? path48.resolve(entry.harnessRoot) : null;
11641
+ if (entryRoot && entryRoot !== resolvedRoot) continue;
11642
+ scoped.set(key, entry);
11643
+ }
11644
+ return scoped;
11645
+ }
11415
11646
  function buildWorktreeIndexAt(harnessRoot) {
11416
11647
  const index = /* @__PURE__ */ new Map();
11417
11648
  for (const run of listRunRecordsForHarnessRoot(harnessRoot)) {
11418
11649
  for (const name of Object.keys(run.workers || {})) {
11419
- const workerPath = path47.join(
11650
+ const workerPath = path48.join(
11420
11651
  runDirectoryAt(harnessRoot, run.id),
11421
11652
  "workers",
11422
11653
  safeSlug(name),
@@ -11424,9 +11655,9 @@ function buildWorktreeIndexAt(harnessRoot) {
11424
11655
  );
11425
11656
  const worker = readJson(workerPath, void 0);
11426
11657
  if (!worker?.worktreePath) continue;
11427
- index.set(path47.resolve(worker.worktreePath), {
11658
+ index.set(path48.resolve(worker.worktreePath), {
11428
11659
  harnessRoot,
11429
- worktreePath: path47.resolve(worker.worktreePath),
11660
+ worktreePath: path48.resolve(worker.worktreePath),
11430
11661
  runId: run.id,
11431
11662
  workerName: name,
11432
11663
  run,
@@ -11452,6 +11683,7 @@ function resolveHarnessRetention(options = {}) {
11452
11683
  const execute = options.execute === true || options.execute !== false && envFlag3("KYNVER_CLEANUP_EXECUTE");
11453
11684
  const finalizeStaleRuns2 = options.finalizeStaleRuns !== false && !envFlag3("KYNVER_CLEANUP_SKIP_FINALIZE");
11454
11685
  const nodeModulesAgeMs = options.nodeModulesAgeMs ?? envMs("KYNVER_CLEANUP_NODE_MODULES_AGE_MS", DEFAULT_NODE_MODULES_AGE_MS);
11686
+ const scanDependencyCaches = options.scanDependencyCaches ?? (options.nodeModulesAgeMs !== void 0 || process.env.KYNVER_CLEANUP_NODE_MODULES_AGE_MS != null);
11455
11687
  const worktreesAgeMs = options.worktreesAgeMs ?? envMs("KYNVER_CLEANUP_WORKTREES_AGE_MS", 0);
11456
11688
  const terminalWorktreesAgeMs = options.terminalWorktreesAgeMs ?? envMs("KYNVER_CLEANUP_TERMINAL_WORKTREES_AGE_MS", DEFAULT_TERMINAL_WORKTREES_AGE_MS);
11457
11689
  const runDirectoriesAgeMs = options.runDirectoriesAgeMs ?? envMs("KYNVER_CLEANUP_RUN_DIRECTORIES_AGE_MS", DEFAULT_RUN_DIRECTORIES_AGE_MS);
@@ -11467,6 +11699,7 @@ function resolveHarnessRetention(options = {}) {
11467
11699
  return {
11468
11700
  execute,
11469
11701
  finalizeStaleRuns: finalizeStaleRuns2,
11702
+ scanDependencyCaches,
11470
11703
  nodeModulesAgeMs,
11471
11704
  worktreesAgeMs: worktreesAgeMs > 0 ? worktreesAgeMs : 0,
11472
11705
  terminalWorktreesAgeMs: terminalWorktreesAgeMs >= 0 ? terminalWorktreesAgeMs : 0,
@@ -11490,35 +11723,38 @@ function resolvePipelineHarnessRetention(runId) {
11490
11723
  runDirectoriesAgeMs: scopeAll ? DEFAULT_RUN_DIRECTORIES_AGE_MS : void 0,
11491
11724
  includeOrphans: scopeAll ? true : void 0,
11492
11725
  finalizeStaleRuns: true,
11493
- accountBytes: true
11726
+ accountBytes: true,
11727
+ scanDependencyCaches: true
11494
11728
  });
11495
11729
  }
11496
11730
 
11497
11731
  // src/cleanup-orphan-safety.ts
11498
11732
  init_git();
11499
- import { existsSync as existsSync34, statSync as statSync12 } from "node:fs";
11500
- import path48 from "node:path";
11733
+ import { existsSync as existsSync34 } from "node:fs";
11734
+ import path49 from "node:path";
11735
+ init_util();
11501
11736
  var DEFAULT_HEARTBEAT_FRESH_MS = 30 * 60 * 1e3;
11502
11737
  function assessOrphanWorktreeSafety(input) {
11503
11738
  const now = input.now ?? Date.now();
11504
11739
  const heartbeatFreshMs = input.heartbeatFreshMs ?? DEFAULT_HEARTBEAT_FRESH_MS;
11505
11740
  if (!existsSync34(input.worktreePath)) return null;
11506
11741
  if (input.runId && input.workerName) {
11507
- const heartbeatPath = path48.join(
11742
+ const workerDir = path49.join(
11508
11743
  input.harnessRoot,
11509
11744
  "runs",
11510
11745
  input.runId,
11511
11746
  "workers",
11512
- input.workerName,
11513
- "heartbeat.jsonl"
11747
+ input.workerName
11514
11748
  );
11515
- try {
11516
- const mtime = statSync12(heartbeatPath).mtimeMs;
11517
- if (now - mtime < heartbeatFreshMs) return "active_worker";
11518
- } catch {
11749
+ const worker = readJson(
11750
+ path49.join(workerDir, "worker.json"),
11751
+ void 0
11752
+ );
11753
+ if (worker && heartbeatContentIsFresh(worker, now, heartbeatFreshMs)) {
11754
+ return "active_worker";
11519
11755
  }
11520
11756
  }
11521
- const gitDir = path48.join(input.worktreePath, ".git");
11757
+ const gitDir = path49.join(input.worktreePath, ".git");
11522
11758
  if (!existsSync34(gitDir)) return null;
11523
11759
  const porcelain = gitCapture(input.worktreePath, ["status", "--porcelain"]);
11524
11760
  if (porcelain.status !== 0) return "pr_or_unmerged_commits";
@@ -11549,8 +11785,8 @@ function assessOrphanWorktreeSafety(input) {
11549
11785
 
11550
11786
  // src/harness-storage-snapshot.ts
11551
11787
  init_paths();
11552
- import { existsSync as existsSync35, readdirSync as readdirSync14, statSync as statSync13 } from "node:fs";
11553
- import path49 from "node:path";
11788
+ import { existsSync as existsSync35, readdirSync as readdirSync13, statSync as statSync11 } from "node:fs";
11789
+ import path50 from "node:path";
11554
11790
  function harnessStorageSnapshot(opts = {}) {
11555
11791
  const harnessRoot = normalizeHarnessRoot(opts.harnessRoot ?? resolveHarnessRoot());
11556
11792
  const worktreesDir = harnessWorktreesDir(harnessRoot);
@@ -11573,7 +11809,7 @@ function harnessStorageSnapshot(opts = {}) {
11573
11809
  let oldestMs = null;
11574
11810
  let entries;
11575
11811
  try {
11576
- entries = readdirSync14(worktreesDir, { withFileTypes: true });
11812
+ entries = readdirSync13(worktreesDir, { withFileTypes: true });
11577
11813
  } catch {
11578
11814
  return {
11579
11815
  harnessRoot,
@@ -11588,14 +11824,14 @@ function harnessStorageSnapshot(opts = {}) {
11588
11824
  for (const runEntry of entries) {
11589
11825
  if (!runEntry.isDirectory()) continue;
11590
11826
  runCount += 1;
11591
- const runPath = path49.join(worktreesDir, runEntry.name);
11827
+ const runPath = path50.join(worktreesDir, runEntry.name);
11592
11828
  try {
11593
- const st = statSync13(runPath);
11829
+ const st = statSync11(runPath);
11594
11830
  oldestMs = oldestMs === null ? st.mtimeMs : Math.min(oldestMs, st.mtimeMs);
11595
11831
  } catch {
11596
11832
  }
11597
11833
  try {
11598
- for (const workerEntry of readdirSync14(runPath, { withFileTypes: true })) {
11834
+ for (const workerEntry of readdirSync13(runPath, { withFileTypes: true })) {
11599
11835
  if (workerEntry.isDirectory()) workerCount += 1;
11600
11836
  }
11601
11837
  } catch {
@@ -11626,10 +11862,10 @@ function harnessStorageSnapshot(opts = {}) {
11626
11862
  init_paths();
11627
11863
  import { existsSync as existsSync36 } from "node:fs";
11628
11864
  import { homedir as homedir13 } from "node:os";
11629
- import path50 from "node:path";
11865
+ import path51 from "node:path";
11630
11866
  var WELL_KNOWN_HARNESS_SCAN_ROOTS = [
11631
11867
  "/var/tmp/kynver-harness",
11632
- path50.join(homedir13(), ".openclaw", "harness")
11868
+ path51.join(homedir13(), ".openclaw", "harness")
11633
11869
  ];
11634
11870
  function addRoot(seen, roots, candidate) {
11635
11871
  if (!candidate?.trim()) return;
@@ -11651,7 +11887,7 @@ function resolveHarnessScanRoots(options = {}) {
11651
11887
  for (const candidate of extra ?? []) addRoot(seen, roots, candidate);
11652
11888
  if (shouldScanWellKnownRoots(options)) {
11653
11889
  for (const candidate of WELL_KNOWN_HARNESS_SCAN_ROOTS) {
11654
- const resolved = path50.resolve(candidate);
11890
+ const resolved = path51.resolve(candidate);
11655
11891
  if (!seen.has(resolved) && existsSync36(resolved)) addRoot(seen, roots, resolved);
11656
11892
  }
11657
11893
  }
@@ -11730,15 +11966,52 @@ var CleanupGitRevCache = class {
11730
11966
  }
11731
11967
  };
11732
11968
 
11969
+ // src/cleanup-git-probe.ts
11970
+ import { spawnSync as spawnSync7 } from "node:child_process";
11971
+ import { existsSync as existsSync37 } from "node:fs";
11972
+ import path52 from "node:path";
11973
+ var CLEANUP_GIT_PROBE_TIMEOUT_MS = 5e3;
11974
+ function cleanupGitCapture(cwd, args) {
11975
+ if (!existsSync37(path52.join(cwd, ".git"))) {
11976
+ return { status: null, stdout: "", stderr: "", error: "not_a_git_repo" };
11977
+ }
11978
+ try {
11979
+ const res = spawnSync7("git", args, {
11980
+ cwd,
11981
+ encoding: "utf8",
11982
+ timeout: CLEANUP_GIT_PROBE_TIMEOUT_MS
11983
+ });
11984
+ const timedOut = res.error?.message?.includes("ETIMEDOUT") === true;
11985
+ return {
11986
+ status: timedOut ? null : res.status,
11987
+ stdout: res.stdout || "",
11988
+ stderr: res.stderr || "",
11989
+ error: timedOut ? "git_timeout" : res.error ? res.error.message : null
11990
+ };
11991
+ } catch (error) {
11992
+ return {
11993
+ status: null,
11994
+ stdout: "",
11995
+ stderr: "",
11996
+ error: error.message
11997
+ };
11998
+ }
11999
+ }
12000
+ function gitStatusShortForCleanup(worktreePath) {
12001
+ const captured = cleanupGitCapture(worktreePath, ["status", "--short"]);
12002
+ if (captured.error === "not_a_git_repo") return [];
12003
+ if (captured.error === "git_timeout" || captured.status !== 0) return null;
12004
+ return captured.stdout.split("\n").map((line) => line.trim()).filter(Boolean);
12005
+ }
12006
+
11733
12007
  // src/cleanup-git-status-cache.ts
11734
- init_git();
11735
12008
  var CleanupGitStatusCache = class {
11736
12009
  cache = /* @__PURE__ */ new Map();
11737
12010
  porcelain(worktreePath) {
11738
12011
  const resolved = worktreePath;
11739
12012
  const cached = this.cache.get(resolved);
11740
12013
  if (cached !== void 0) return cached;
11741
- const lines = gitStatusShort(resolved);
12014
+ const lines = gitStatusShortForCleanup(resolved) ?? [];
11742
12015
  this.cache.set(resolved, lines);
11743
12016
  return lines;
11744
12017
  }
@@ -11843,9 +12116,9 @@ function mergeWorktreeIndexes(scanRoots) {
11843
12116
  }
11844
12117
  function worktreePathForCandidate(candidate, worktreesDir) {
11845
12118
  if (candidate.runId && candidate.worker) {
11846
- return path51.join(worktreesDir, candidate.runId, candidate.worker);
12119
+ return path53.join(worktreesDir, candidate.runId, candidate.worker);
11847
12120
  }
11848
- return path51.resolve(candidate.path, "..");
12121
+ return path53.resolve(candidate.path, "..");
11849
12122
  }
11850
12123
  function runHarnessCleanup(options = {}) {
11851
12124
  let retention = resolveHarnessRetention(options);
@@ -11874,7 +12147,8 @@ function runHarnessCleanup(options = {}) {
11874
12147
  for (const harnessRoot of paths.scanRoots) {
11875
12148
  if (atSweepCap()) break;
11876
12149
  emitCleanupProgress("root", harnessRoot);
11877
- const worktreesDir = path51.join(harnessRoot, "worktrees");
12150
+ const worktreesDir = path53.join(harnessRoot, "worktrees");
12151
+ const rootIndex = filterWorktreeIndexForRoot(index, harnessRoot);
11878
12152
  const scanOpts = {
11879
12153
  harnessRoot,
11880
12154
  worktreesDir,
@@ -11882,11 +12156,14 @@ function runHarnessCleanup(options = {}) {
11882
12156
  worktreesAgeMs: retention.worktreesAgeMs,
11883
12157
  includeOrphans: retention.includeOrphans,
11884
12158
  runIdFilter: retention.runIdFilter,
11885
- index,
12159
+ index: rootIndex,
11886
12160
  now: paths.now
11887
12161
  };
11888
- const dependencyCandidates = scanDependencyCacheCandidates(scanOpts);
11889
- emitCleanupProgress("dependency", `${dependencyCandidates.length} cache candidate(s) at ${harnessRoot}`);
12162
+ const dependencyCandidates = retention.scanDependencyCaches ? scanDependencyCacheCandidates(scanOpts) : [];
12163
+ emitCleanupProgress(
12164
+ "dependency",
12165
+ retention.scanDependencyCaches ? `${dependencyCandidates.length} cache candidate(s) at ${harnessRoot}` : "skipped (worktree-only sweep)"
12166
+ );
11890
12167
  let dependencyProcessed = 0;
11891
12168
  for (const raw of dependencyCandidates) {
11892
12169
  if (atSweepCap()) break;
@@ -11894,7 +12171,7 @@ function runHarnessCleanup(options = {}) {
11894
12171
  if (dependencyProcessed % 50 === 0) {
11895
12172
  emitCleanupProgress("dependency", `${dependencyProcessed}/${dependencyCandidates.length} evaluated`);
11896
12173
  }
11897
- const resolved = path51.resolve(raw.path);
12174
+ const resolved = path53.resolve(raw.path);
11898
12175
  if (processedPaths.has(resolved)) continue;
11899
12176
  processedPaths.add(resolved);
11900
12177
  const candidate = { ...raw, path: resolved };
@@ -11905,7 +12182,7 @@ function runHarnessCleanup(options = {}) {
11905
12182
  continue;
11906
12183
  }
11907
12184
  const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
11908
- const indexed = index.get(path51.resolve(worktreePath)) ?? null;
12185
+ const indexed = rootIndex.get(path53.resolve(worktreePath)) ?? null;
11909
12186
  const guardReason = skipDependencyCacheRemoval({
11910
12187
  indexed,
11911
12188
  includeOrphans: true,
@@ -11928,9 +12205,9 @@ function runHarnessCleanup(options = {}) {
11928
12205
  )
11929
12206
  );
11930
12207
  }
11931
- for (const raw of scanBuildCacheCandidates(scanOpts)) {
12208
+ if (retention.scanDependencyCaches) for (const raw of scanBuildCacheCandidates(scanOpts)) {
11932
12209
  if (atSweepCap()) break;
11933
- const resolved = path51.resolve(raw.path);
12210
+ const resolved = path53.resolve(raw.path);
11934
12211
  if (processedPaths.has(resolved)) continue;
11935
12212
  processedPaths.add(resolved);
11936
12213
  const candidate = { ...raw, path: resolved };
@@ -11941,7 +12218,7 @@ function runHarnessCleanup(options = {}) {
11941
12218
  continue;
11942
12219
  }
11943
12220
  const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
11944
- const indexed = index.get(path51.resolve(worktreePath)) ?? null;
12221
+ const indexed = rootIndex.get(path53.resolve(worktreePath)) ?? null;
11945
12222
  const guardReason = skipBuildCacheRemoval({
11946
12223
  indexed,
11947
12224
  includeOrphans: true,
@@ -11977,11 +12254,11 @@ function runHarnessCleanup(options = {}) {
11977
12254
  if (worktreeProcessed % 50 === 0) {
11978
12255
  emitCleanupProgress("worktrees", `${worktreeProcessed}/${worktreeCandidates.length} evaluated`);
11979
12256
  }
11980
- const resolved = path51.resolve(raw.path);
12257
+ const resolved = path53.resolve(raw.path);
11981
12258
  if (worktreeSeen.has(resolved)) continue;
11982
12259
  worktreeSeen.add(resolved);
11983
12260
  const candidate = { ...raw, path: resolved };
11984
- const indexed = index.get(path51.resolve(candidate.path)) ?? null;
12261
+ const indexed = rootIndex.get(path53.resolve(candidate.path)) ?? null;
11985
12262
  const orphanSafety = indexed ? null : assessOrphanWorktreeSafety({
11986
12263
  worktreePath: candidate.path,
11987
12264
  harnessRoot,
@@ -11991,14 +12268,17 @@ function runHarnessCleanup(options = {}) {
11991
12268
  });
11992
12269
  const guardSkip = skipWorktreeRemoval({
11993
12270
  indexed,
11994
- worktreePath: path51.resolve(candidate.path),
12271
+ worktreePath: path53.resolve(candidate.path),
11995
12272
  includeOrphans: retention.includeOrphans,
11996
12273
  worktreesAgeMs: retention.worktreesAgeMs,
11997
12274
  terminalWorktreesAgeMs: retention.terminalWorktreesAgeMs,
11998
12275
  ageMs: candidate.ageMs,
11999
12276
  orphanSafety,
12000
12277
  worktreeRemovalGuard: options.worktreeRemovalGuard,
12001
- liveness
12278
+ liveness,
12279
+ now: paths.now,
12280
+ harnessRoot,
12281
+ writeSalvageEvidence: retention.execute
12002
12282
  });
12003
12283
  if (guardSkip) {
12004
12284
  const { reason: guardReason, detail: guardDetail } = normalizeGuardSkip(guardSkip);
@@ -12023,11 +12303,11 @@ function runHarnessCleanup(options = {}) {
12023
12303
  now: paths.now
12024
12304
  })) {
12025
12305
  if (atSweepCap()) break;
12026
- const resolved = path51.resolve(raw.path);
12306
+ const resolved = path53.resolve(raw.path);
12027
12307
  if (processedPaths.has(resolved)) continue;
12028
12308
  processedPaths.add(resolved);
12029
12309
  const candidate = { ...raw, path: resolved };
12030
- const runId = candidate.runId ?? path51.basename(resolved);
12310
+ const runId = candidate.runId ?? path53.basename(resolved);
12031
12311
  const dirSkip = skipRunDirectoryRemoval({
12032
12312
  harnessRoot,
12033
12313
  runId,
@@ -12163,12 +12443,12 @@ function isPipelineCleanupEnabled() {
12163
12443
  // src/installed-package-versions.ts
12164
12444
  import { readFile } from "node:fs/promises";
12165
12445
  import { homedir as homedir14 } from "node:os";
12166
- import path53 from "node:path";
12446
+ import path55 from "node:path";
12167
12447
 
12168
12448
  // src/memory-cost-package-version-guard.ts
12169
12449
  init_default_repo_discovery();
12170
- import { existsSync as existsSync37, readFileSync as readFileSync13 } from "node:fs";
12171
- import path52 from "node:path";
12450
+ import { existsSync as existsSync38, readFileSync as readFileSync13 } from "node:fs";
12451
+ import path54 from "node:path";
12172
12452
  var MEMORY_COST_PACKAGE_MIN_VERSIONS = {
12173
12453
  "@kynver-app/runtime": "0.1.83",
12174
12454
  "@kynver-app/openclaw-agent-os": "0.1.43",
@@ -12230,8 +12510,8 @@ function resolveRepoRoot(cwd, explicitRepoRoot) {
12230
12510
  (value) => Boolean(value?.trim())
12231
12511
  );
12232
12512
  for (const candidate of candidates) {
12233
- const resolved = path52.resolve(candidate);
12234
- if (existsSync37(path52.join(resolved, "packages/kynver-runtime/package.json")) && existsSync37(path52.join(resolved, "package.json"))) {
12513
+ const resolved = path54.resolve(candidate);
12514
+ if (existsSync38(path54.join(resolved, "packages/kynver-runtime/package.json")) && existsSync38(path54.join(resolved, "package.json"))) {
12235
12515
  return resolved;
12236
12516
  }
12237
12517
  }
@@ -12244,7 +12524,7 @@ function probeRepoPackageVersions(input = {}) {
12244
12524
  if (!repoRoot) return {};
12245
12525
  const out = {};
12246
12526
  for (const packageName of MEMORY_COST_MANAGED_PACKAGES) {
12247
- const packageJsonPath = path52.join(repoRoot, REPO_PACKAGE_JSON_RELATIVE[packageName]);
12527
+ const packageJsonPath = path54.join(repoRoot, REPO_PACKAGE_JSON_RELATIVE[packageName]);
12248
12528
  const version = readPackageJsonVersion(packageJsonPath);
12249
12529
  if (!version) continue;
12250
12530
  out[packageName] = { version, source: "repo", path: packageJsonPath };
@@ -12364,12 +12644,12 @@ function unique(values) {
12364
12644
  }
12365
12645
  function moduleRoots() {
12366
12646
  const home = homedir14();
12367
- const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ?? path53.join(home, ".openclaw", "npm");
12368
- const npmGlobalRoot = trim(process.env.KYNVER_NPM_GLOBAL_ROOT) ?? trim(process.env.KYNVER_NPM_GLOBAL_MODULES_ROOT) ?? (trim(process.env.NPM_CONFIG_PREFIX) ? path53.join(trim(process.env.NPM_CONFIG_PREFIX), "lib", "node_modules") : path53.join(home, ".npm-global", "lib", "node_modules"));
12647
+ const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ?? path55.join(home, ".openclaw", "npm");
12648
+ const npmGlobalRoot = trim(process.env.KYNVER_NPM_GLOBAL_ROOT) ?? trim(process.env.KYNVER_NPM_GLOBAL_MODULES_ROOT) ?? (trim(process.env.NPM_CONFIG_PREFIX) ? path55.join(trim(process.env.NPM_CONFIG_PREFIX), "lib", "node_modules") : path55.join(home, ".npm-global", "lib", "node_modules"));
12369
12649
  return unique([
12370
- path53.join(openClawPrefix, "lib", "node_modules"),
12371
- path53.join(openClawPrefix, "node_modules"),
12372
- npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot : path53.join(npmGlobalRoot, "lib", "node_modules")
12650
+ path55.join(openClawPrefix, "lib", "node_modules"),
12651
+ path55.join(openClawPrefix, "node_modules"),
12652
+ npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot : path55.join(npmGlobalRoot, "lib", "node_modules")
12373
12653
  ]);
12374
12654
  }
12375
12655
  async function readVersion(packageJsonPath) {
@@ -12385,7 +12665,7 @@ function installedPackageJsonCandidates(packageName) {
12385
12665
  const seen = /* @__PURE__ */ new Set();
12386
12666
  const out = [];
12387
12667
  for (const root of roots) {
12388
- const candidate = path53.join(root, packageName, "package.json");
12668
+ const candidate = path55.join(root, packageName, "package.json");
12389
12669
  if (seen.has(candidate)) continue;
12390
12670
  seen.add(candidate);
12391
12671
  out.push(candidate);
@@ -12411,12 +12691,12 @@ async function collectInstalledPackageVersions(observedAt = (/* @__PURE__ */ new
12411
12691
  }
12412
12692
 
12413
12693
  // src/provider-evidence/exec.ts
12414
- import { spawnSync as spawnSync7 } from "node:child_process";
12694
+ import { spawnSync as spawnSync8 } from "node:child_process";
12415
12695
  var DEFAULT_CLI_TIMEOUT_MS = 1e4;
12416
12696
  var MAX_CLI_BUFFER_BYTES = 4 * 1024 * 1024;
12417
12697
  var defaultCliRunner = (cmd, args, opts) => {
12418
12698
  try {
12419
- const result = spawnSync7(cmd, args, {
12699
+ const result = spawnSync8(cmd, args, {
12420
12700
  encoding: "utf8",
12421
12701
  stdio: ["ignore", "pipe", "pipe"],
12422
12702
  timeout: opts?.timeoutMs ?? DEFAULT_CLI_TIMEOUT_MS,
@@ -12716,10 +12996,10 @@ function collectProviderEvidence(wanted, opts = {}) {
12716
12996
  // src/provider-evidence/wanted-store.ts
12717
12997
  init_run_store();
12718
12998
  init_util();
12719
- import path54 from "node:path";
12999
+ import path56 from "node:path";
12720
13000
  var WANTED_FILE = "provider-evidence-wanted.json";
12721
13001
  function wantedFilePath(runId) {
12722
- return path54.join(runDirectory(runId), WANTED_FILE);
13002
+ return path56.join(runDirectory(runId), WANTED_FILE);
12723
13003
  }
12724
13004
  function parseWantedItems(value) {
12725
13005
  if (!Array.isArray(value)) return [];
@@ -12758,7 +13038,7 @@ async function completeFinishedWorkers(runId, args) {
12758
13038
  const outcomes = [];
12759
13039
  for (const name of Object.keys(run.workers || {})) {
12760
13040
  const worker = readJson(
12761
- path55.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
13041
+ path57.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
12762
13042
  void 0
12763
13043
  );
12764
13044
  if (!worker?.taskId || worker.localOnly) continue;
@@ -12933,7 +13213,7 @@ import os11 from "node:os";
12933
13213
  // src/chat/anthropic-credentials.ts
12934
13214
  import { readFileSync as readFileSync14 } from "node:fs";
12935
13215
  import { homedir as homedir15, platform } from "node:os";
12936
- import path56 from "node:path";
13216
+ import path58 from "node:path";
12937
13217
  import { execFileSync as execFileSync3 } from "node:child_process";
12938
13218
  function parseClaudeCredentials(raw, now = Date.now()) {
12939
13219
  try {
@@ -12961,17 +13241,18 @@ function readClaudeCliToken() {
12961
13241
  }
12962
13242
  }
12963
13243
  try {
12964
- const raw = readFileSync14(path56.join(homedir15(), ".claude", ".credentials.json"), "utf8");
13244
+ const raw = readFileSync14(path58.join(homedir15(), ".claude", ".credentials.json"), "utf8");
12965
13245
  return parseClaudeCredentials(raw);
12966
13246
  } catch {
12967
13247
  return null;
12968
13248
  }
12969
13249
  }
12970
- function resolveLocalAnthropicCredentials(env = process.env) {
13250
+ function resolveLocalAnthropicCredentials(env = process.env, opts = {}) {
12971
13251
  const apiKey = env.ANTHROPIC_API_KEY?.trim();
12972
13252
  if (apiKey) return { kind: "api_key", key: apiKey };
12973
- const optIn = env.KYNVER_CHAT_USE_CLAUDE_OAUTH;
12974
- if (optIn === "1" || optIn === "true" || optIn === "yes") {
13253
+ const envOptIn = env.KYNVER_CHAT_USE_CLAUDE_OAUTH;
13254
+ const optedIn = opts.oauthOptIn === true || envOptIn === "1" || envOptIn === "true" || envOptIn === "yes";
13255
+ if (optedIn) {
12975
13256
  const token = readClaudeCliToken();
12976
13257
  if (token) return { kind: "oauth", token };
12977
13258
  }
@@ -13157,20 +13438,43 @@ var CLAIM_WAIT_MS = 25e3;
13157
13438
  var CLAIM_FETCH_TIMEOUT_MS = 32e3;
13158
13439
  var ERROR_BACKOFF_MS = 5e3;
13159
13440
  var AUTH_BACKOFF_MS = 6e4;
13441
+ var BRIDGE_REDISCOVER_MS = 5 * 6e4;
13442
+ var DISCOVERY_TIMEOUT_MS = 5e3;
13160
13443
  var DEFAULT_CHAT_MODEL = "claude-sonnet-4-6";
13161
- function resolveChatBridgeTarget(env = process.env) {
13162
- const bridgeUrl = env.KYNVER_RUNTIME_CHAT_BRIDGE_URL?.trim();
13163
- if (!bridgeUrl) return null;
13444
+ async function discoverBridgeUrl(apiBaseUrl, apiKey, env) {
13445
+ const override = env.KYNVER_RUNTIME_CHAT_BRIDGE_URL?.trim();
13446
+ if (override) return override;
13447
+ if (!apiBaseUrl) return null;
13448
+ const controller = new AbortController();
13449
+ const timer = setTimeout(() => controller.abort(), DISCOVERY_TIMEOUT_MS);
13450
+ try {
13451
+ const res = await fetch(`${apiBaseUrl.replace(/\/$/, "")}/api/runtime/chat-bridge`, {
13452
+ headers: { Authorization: `Bearer ${apiKey}` },
13453
+ signal: controller.signal
13454
+ });
13455
+ if (!res.ok) return null;
13456
+ const body = await res.json().catch(() => null);
13457
+ return typeof body?.bridgeUrl === "string" && body.bridgeUrl.trim() ? body.bridgeUrl.trim() : null;
13458
+ } catch {
13459
+ return null;
13460
+ } finally {
13461
+ clearTimeout(timer);
13462
+ }
13463
+ }
13464
+ async function resolveChatBridgeTarget(env = process.env) {
13164
13465
  const config = loadUserConfig();
13165
13466
  const agentOsId = config.agentOsId?.trim();
13166
13467
  const apiKey = loadApiKey();
13167
13468
  if (!agentOsId || !apiKey) return null;
13469
+ const bridgeUrl = await discoverBridgeUrl(config.apiBaseUrl, apiKey, env);
13470
+ if (!bridgeUrl) return null;
13168
13471
  return {
13169
13472
  bridgeUrl: bridgeUrl.replace(/\/$/, ""),
13170
13473
  agentOsId,
13171
13474
  apiKey,
13172
13475
  boxId: os11.hostname(),
13173
- model: env.KYNVER_CHAT_MODEL?.trim() || config.defaultModel?.trim() || DEFAULT_CHAT_MODEL
13476
+ model: env.KYNVER_CHAT_MODEL?.trim() || config.defaultModel?.trim() || DEFAULT_CHAT_MODEL,
13477
+ useClaudeOauth: config.chatUseClaudeOauth === true
13174
13478
  };
13175
13479
  }
13176
13480
  async function claimOnce(target, shouldStop) {
@@ -13231,7 +13535,7 @@ async function executeChatTurn(target, turn) {
13231
13535
  });
13232
13536
  };
13233
13537
  const batcher = new DeltaBatcher(sendBatch);
13234
- const creds = resolveLocalAnthropicCredentials();
13538
+ const creds = resolveLocalAnthropicCredentials(process.env, { oauthOptIn: target.useClaudeOauth });
13235
13539
  if (!creds) {
13236
13540
  await pushEvents(target, turn.turnId, [
13237
13541
  { seq: 0, kind: "error", error: "no_local_credentials" }
@@ -13276,15 +13580,31 @@ async function sleep2(ms, shouldStop) {
13276
13580
  }
13277
13581
  }
13278
13582
  async function runChatClaimLoop(opts) {
13279
- const target = resolveChatBridgeTarget();
13280
- if (!target) return;
13583
+ let target = await resolveChatBridgeTarget();
13584
+ if (!target) {
13585
+ console.error(
13586
+ JSON.stringify({
13587
+ event: "chat_claim_loop_waiting",
13588
+ reason: !loadApiKey() || !loadUserConfig().agentOsId?.trim() ? "machine_not_linked" : "bridge_not_discovered (rollout off, discovery route missing/404, or API unreachable)",
13589
+ rediscoverMs: BRIDGE_REDISCOVER_MS
13590
+ })
13591
+ );
13592
+ }
13593
+ while (!target && !opts.shouldStop()) {
13594
+ if (!loadApiKey() || !loadUserConfig().agentOsId?.trim()) return;
13595
+ await sleep2(BRIDGE_REDISCOVER_MS, opts.shouldStop);
13596
+ if (opts.shouldStop()) return;
13597
+ target = await resolveChatBridgeTarget();
13598
+ }
13599
+ if (!target || opts.shouldStop()) return;
13281
13600
  console.error(
13282
13601
  JSON.stringify({
13283
13602
  event: "chat_claim_loop_start",
13284
13603
  bridgeUrl: target.bridgeUrl,
13285
13604
  agentOsId: target.agentOsId,
13286
13605
  boxId: target.boxId,
13287
- model: target.model
13606
+ model: target.model,
13607
+ useClaudeOauth: target.useClaudeOauth
13288
13608
  })
13289
13609
  );
13290
13610
  while (!opts.shouldStop()) {
@@ -13369,10 +13689,34 @@ async function runDaemon(args) {
13369
13689
  })
13370
13690
  );
13371
13691
  });
13692
+ let credentialMissingLogged = false;
13372
13693
  while (!stopping) {
13373
13694
  try {
13374
13695
  writeDaemonHeartbeat({ agentOsId, runId });
13375
- if (cronEnv.tickEnabled) {
13696
+ const credential = await tryResolveCallbackSecretWithMint(
13697
+ args.secret ? String(args.secret) : void 0,
13698
+ agentOsId
13699
+ );
13700
+ if (!credential.ok) {
13701
+ if (!credentialMissingLogged) {
13702
+ credentialMissingLogged = true;
13703
+ console.error(
13704
+ JSON.stringify({
13705
+ event: "daemon_runner_credential_missing",
13706
+ agentOsId,
13707
+ reason: credential.reason,
13708
+ remedy: `run \`kynver runner credential --agent-os-id ${agentOsId}\` (or \`kynver bootstrap\`); ticks resume automatically once the credential exists`
13709
+ })
13710
+ );
13711
+ }
13712
+ await awaitDaemonBackoff(intervalMs, () => stopping);
13713
+ continue;
13714
+ }
13715
+ if (credentialMissingLogged) {
13716
+ credentialMissingLogged = false;
13717
+ console.error(JSON.stringify({ event: "daemon_runner_credential_recovered", agentOsId }));
13718
+ }
13719
+ if (cronEnv.tickEnabled) {
13376
13720
  const cronTick = await runKynverCronTick({
13377
13721
  env: cronEnv,
13378
13722
  agentOsIdFilter: agentOsId
@@ -13399,21 +13743,152 @@ async function runDaemon(args) {
13399
13743
  console.error(JSON.stringify({ event: "daemon_stop", runId, agentOsId }));
13400
13744
  }
13401
13745
 
13746
+ // src/daemon-keeper.ts
13747
+ init_config();
13748
+ import { spawn as spawn6 } from "node:child_process";
13749
+ init_util();
13750
+ var DEFAULT_STALL_MS = 15 * 6e4;
13751
+ var STARTUP_GRACE_MS = 2 * 6e4;
13752
+ var KILL_GRACE_MS = 1e4;
13753
+ var BACKOFF_BASE_MS = 5e3;
13754
+ var BACKOFF_CAP_MS = 5 * 6e4;
13755
+ var HEALTHY_RESET_MS = 30 * 6e4;
13756
+ var POLL_MS = 5e3;
13757
+ function resolveKeeperStallMs(flag, env = process.env) {
13758
+ const fromFlag = typeof flag === "string" ? Number.parseInt(flag, 10) : NaN;
13759
+ if (Number.isFinite(fromFlag) && fromFlag > 0) return fromFlag;
13760
+ const fromEnv = Number.parseInt(env.KYNVER_DAEMON_STALL_MS ?? "", 10);
13761
+ if (Number.isFinite(fromEnv) && fromEnv > 0) return fromEnv;
13762
+ return DEFAULT_STALL_MS;
13763
+ }
13764
+ function shouldRunDaemonKeeper(args, env = process.env) {
13765
+ if (args.keeperChild === true || args.keeperChild === "true") return false;
13766
+ if (args.noSupervise === true || args.noSupervise === "true") return false;
13767
+ if (args.supervised === "false") return false;
13768
+ const envFlag5 = (env.KYNVER_DAEMON_SUPERVISED ?? "").trim().toLowerCase();
13769
+ if (envFlag5 === "0" || envFlag5 === "false" || envFlag5 === "no" || envFlag5 === "off") {
13770
+ return false;
13771
+ }
13772
+ return true;
13773
+ }
13774
+ function nextKeeperBackoffMs(consecutiveFailures, base = BACKOFF_BASE_MS, cap = BACKOFF_CAP_MS) {
13775
+ const exp = Math.min(Math.max(consecutiveFailures, 1) - 1, 10);
13776
+ return Math.min(base * 2 ** exp, cap);
13777
+ }
13778
+ function keeperRunWasHealthy(startedAtMs, endedAtMs, healthyMs = HEALTHY_RESET_MS) {
13779
+ return endedAtMs - startedAtMs >= healthyMs;
13780
+ }
13781
+ function keeperLog(event, detail = {}) {
13782
+ console.error(JSON.stringify({ event: `daemon_keeper_${event}`, ...detail }));
13783
+ }
13784
+ function buildKeeperChildArgv(argv) {
13785
+ const out = [];
13786
+ for (let i = 0; i < argv.length; i += 1) {
13787
+ const arg = argv[i];
13788
+ if (arg === "--supervised" || arg === "--no-supervise" || arg === "--keeper-child") continue;
13789
+ if (arg === "--stall-ms") {
13790
+ if (i + 1 < argv.length && !argv[i + 1].startsWith("--")) i += 1;
13791
+ continue;
13792
+ }
13793
+ if (arg.startsWith("--stall-ms=") || arg.startsWith("--supervised=")) continue;
13794
+ out.push(arg);
13795
+ }
13796
+ out.push("--keeper-child");
13797
+ return out;
13798
+ }
13799
+ async function runDaemonKeeper(args, rawArgv = process.argv.slice(2)) {
13800
+ const agentOsId = String(
13801
+ required(String(args.agentOsId || loadUserConfig().agentOsId || ""), "--agent-os-id")
13802
+ );
13803
+ const stallMs = resolveKeeperStallMs(args.stallMs);
13804
+ const childArgv = buildKeeperChildArgv(rawArgv);
13805
+ const cliEntry = process.argv[1];
13806
+ let stopping = false;
13807
+ let child = null;
13808
+ let consecutiveFailures = 0;
13809
+ const stop = (signal) => {
13810
+ stopping = true;
13811
+ keeperLog("stop", { signal });
13812
+ if (child?.pid) child.kill(signal);
13813
+ };
13814
+ process.on("SIGINT", () => stop("SIGINT"));
13815
+ process.on("SIGTERM", () => stop("SIGTERM"));
13816
+ keeperLog("start", { agentOsId, stallMs, childArgv });
13817
+ while (!stopping) {
13818
+ const startedAt = Date.now();
13819
+ let exited = false;
13820
+ let exitCode = null;
13821
+ let exitSignal = null;
13822
+ child = spawn6(process.execPath, [cliEntry, ...childArgv], {
13823
+ stdio: "inherit",
13824
+ env: process.env
13825
+ });
13826
+ keeperLog("child_spawned", { pid: child.pid ?? null });
13827
+ child.on("exit", (code, signal) => {
13828
+ exited = true;
13829
+ exitCode = code;
13830
+ exitSignal = signal;
13831
+ });
13832
+ while (!exited && !stopping) {
13833
+ await sleepMsAsync(POLL_MS);
13834
+ if (exited || stopping) break;
13835
+ if (Date.now() - startedAt < STARTUP_GRACE_MS) continue;
13836
+ const beat = readDaemonHeartbeat(agentOsId);
13837
+ const ownBeat = beat && beat.pid === child.pid ? beat : null;
13838
+ if (ownBeat && isDaemonHeartbeatStale(ownBeat, stallMs)) {
13839
+ keeperLog("stall_detected", {
13840
+ pid: child.pid ?? null,
13841
+ lastBeatAt: ownBeat.observedAt,
13842
+ stallMs
13843
+ });
13844
+ child.kill("SIGTERM");
13845
+ await sleepMsAsync(KILL_GRACE_MS);
13846
+ if (!exited) child.kill("SIGKILL");
13847
+ break;
13848
+ }
13849
+ if (!ownBeat && Date.now() - startedAt > stallMs + STARTUP_GRACE_MS) {
13850
+ keeperLog("no_heartbeat_detected", { pid: child.pid ?? null, stallMs });
13851
+ child.kill("SIGTERM");
13852
+ await sleepMsAsync(KILL_GRACE_MS);
13853
+ if (!exited) child.kill("SIGKILL");
13854
+ break;
13855
+ }
13856
+ }
13857
+ while (!exited && !stopping) {
13858
+ await sleepMsAsync(POLL_MS);
13859
+ }
13860
+ if (stopping) break;
13861
+ const endedAt = Date.now();
13862
+ if (keeperRunWasHealthy(startedAt, endedAt)) consecutiveFailures = 0;
13863
+ consecutiveFailures += 1;
13864
+ const backoff = nextKeeperBackoffMs(consecutiveFailures);
13865
+ keeperLog("child_exited", {
13866
+ code: exitCode,
13867
+ signal: exitSignal,
13868
+ uptimeMs: endedAt - startedAt,
13869
+ consecutiveFailures,
13870
+ respawnInMs: backoff
13871
+ });
13872
+ await sleepMsAsync(backoff);
13873
+ }
13874
+ keeperLog("stopped", { agentOsId });
13875
+ }
13876
+
13402
13877
  // src/worktree.ts
13403
13878
  init_git();
13404
13879
  init_run_store();
13405
- import { existsSync as existsSync39, mkdirSync as mkdirSync7 } from "node:fs";
13406
- import path58 from "node:path";
13880
+ import { existsSync as existsSync40, mkdirSync as mkdirSync9 } from "node:fs";
13881
+ import path60 from "node:path";
13407
13882
 
13408
13883
  // src/run-list.ts
13409
13884
  init_run_store();
13410
13885
  init_run_worker_index();
13411
- import { existsSync as existsSync38, readFileSync as readFileSync15 } from "node:fs";
13412
- import path57 from "node:path";
13886
+ import { existsSync as existsSync39, readFileSync as readFileSync15 } from "node:fs";
13887
+ import path59 from "node:path";
13413
13888
  init_status();
13414
13889
  init_util();
13415
13890
  function heartbeatByteLength(heartbeatPath) {
13416
- if (!heartbeatPath || !existsSync38(heartbeatPath)) return 0;
13891
+ if (!heartbeatPath || !existsSync39(heartbeatPath)) return 0;
13417
13892
  try {
13418
13893
  return readFileSync15(heartbeatPath, "utf8").trim().length;
13419
13894
  } catch {
@@ -13421,7 +13896,7 @@ function heartbeatByteLength(heartbeatPath) {
13421
13896
  }
13422
13897
  }
13423
13898
  function workerEvidence(run, workerName) {
13424
- const workerPath = path57.join(runDirectory(run.id), "workers", safeSlug(workerName), "worker.json");
13899
+ const workerPath = path59.join(runDirectory(run.id), "workers", safeSlug(workerName), "worker.json");
13425
13900
  const worker = readJson(workerPath, void 0);
13426
13901
  if (!worker) {
13427
13902
  return {
@@ -13478,7 +13953,7 @@ function aggregateRunAttention(workers) {
13478
13953
  function countOpenWorkers(run) {
13479
13954
  let open = 0;
13480
13955
  for (const name of listRunWorkerNames(run)) {
13481
- const workerPath = path57.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
13956
+ const workerPath = path59.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
13482
13957
  const worker = readJson(workerPath, void 0);
13483
13958
  if (!worker) continue;
13484
13959
  const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
@@ -13541,8 +14016,8 @@ function createRun(args) {
13541
14016
  ensureGitRepo(repo);
13542
14017
  const id = args.id ? validateRunId(String(args.id)) : timestampSlug(String(args.name || "run"));
13543
14018
  const dir = runDirectory(id);
13544
- if (existsSync39(dir)) failExists(`run already exists: ${id}`);
13545
- mkdirSync7(dir, { recursive: true });
14019
+ if (existsSync40(dir)) failExists(`run already exists: ${id}`);
14020
+ mkdirSync9(dir, { recursive: true });
13546
14021
  const base = String(args.base || "origin/main");
13547
14022
  const baseCommit = git(repo, ["rev-parse", base]).trim();
13548
14023
  const run = {
@@ -13555,7 +14030,7 @@ function createRun(args) {
13555
14030
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
13556
14031
  workers: {}
13557
14032
  };
13558
- writeJson(path58.join(dir, "run.json"), run);
14033
+ writeJson(path60.join(dir, "run.json"), run);
13559
14034
  const info = { runId: id, runDir: dir, repo, base, baseCommit };
13560
14035
  console.log(JSON.stringify(info, null, 2));
13561
14036
  return info;
@@ -13587,6 +14062,11 @@ async function runStart(args) {
13587
14062
  console.error("No AgentOS workspace configured \u2014 run `kynver bootstrap` (or pass --agent-os-id).");
13588
14063
  process.exit(1);
13589
14064
  }
14065
+ if (args.chatOauth === true && config.chatUseClaudeOauth !== true) {
14066
+ saveUserConfig({ ...config, chatUseClaudeOauth: true });
14067
+ config = loadUserConfig();
14068
+ console.log(" Chat: Claude Code OAuth opt-in saved (delegated turns may use your local subscription).");
14069
+ }
13590
14070
  const repo = (typeof args.repo === "string" ? args.repo.trim() : "") || config.defaultRepo?.trim() || resolveDefaultRepo()?.repo || "";
13591
14071
  if (!repo) {
13592
14072
  console.error("No repo configured \u2014 pass --repo /path/to/repo or run `kynver setup --discover-repo`.");
@@ -13608,7 +14088,25 @@ async function runStart(args) {
13608
14088
  console.log(` run: ${runId}`);
13609
14089
  console.log(" Ctrl-C stops the agent. (Advanced control: `kynver daemon --help`.)");
13610
14090
  console.log("");
13611
- await runDaemon({ ...args, run: runId, agentOsId });
14091
+ const daemonArgs = { ...args, run: runId, agentOsId };
14092
+ if (shouldRunDaemonKeeper(daemonArgs)) {
14093
+ await runDaemonKeeper(daemonArgs, buildStartDaemonArgv(runId, agentOsId, args));
14094
+ return;
14095
+ }
14096
+ await runDaemon(daemonArgs);
14097
+ }
14098
+ function buildStartDaemonArgv(runId, agentOsId, args) {
14099
+ const argv = ["daemon", "--run", runId, "--agent-os-id", agentOsId];
14100
+ if (typeof args.intervalMs === "string" && args.intervalMs.trim()) {
14101
+ argv.push("--interval-ms", args.intervalMs.trim());
14102
+ }
14103
+ if (args.execute === false || args.execute === "false") {
14104
+ argv.push("--execute", "false");
14105
+ }
14106
+ if (typeof args.stallMs === "string" && args.stallMs.trim()) {
14107
+ argv.push("--stall-ms", args.stallMs.trim());
14108
+ }
14109
+ return argv;
13612
14110
  }
13613
14111
 
13614
14112
  // src/cli.ts
@@ -13617,8 +14115,8 @@ init_run_store();
13617
14115
  // src/discard-disposable.ts
13618
14116
  init_run_store();
13619
14117
  init_status();
13620
- import { existsSync as existsSync40, rmSync as rmSync4 } from "node:fs";
13621
- import path59 from "node:path";
14118
+ import { existsSync as existsSync41, rmSync as rmSync4 } from "node:fs";
14119
+ import path61 from "node:path";
13622
14120
  function normalizeRelativePath2(value) {
13623
14121
  const normalized = value.replace(/\\/g, "/").replace(/^\.\//, "").trim();
13624
14122
  if (!normalized || normalized.startsWith("/") || normalized.includes("..")) {
@@ -13640,15 +14138,15 @@ function discardDisposableArtifacts(args) {
13640
14138
  if (paths.length === 0) {
13641
14139
  return { ok: false, removed: [], reason: "requires at least one --path" };
13642
14140
  }
13643
- const worktreeRoot = path59.resolve(worker.worktreePath);
14141
+ const worktreeRoot = path61.resolve(worker.worktreePath);
13644
14142
  const removed = [];
13645
14143
  for (const raw of paths) {
13646
14144
  const rel = normalizeRelativePath2(raw);
13647
- const abs = path59.resolve(worktreeRoot, rel);
13648
- if (!abs.startsWith(worktreeRoot + path59.sep) && abs !== worktreeRoot) {
14145
+ const abs = path61.resolve(worktreeRoot, rel);
14146
+ if (!abs.startsWith(worktreeRoot + path61.sep) && abs !== worktreeRoot) {
13649
14147
  return { ok: false, removed, reason: `path escapes worktree: ${raw}` };
13650
14148
  }
13651
- if (!existsSync40(abs)) {
14149
+ if (!existsSync41(abs)) {
13652
14150
  return { ok: false, removed, reason: `path not found: ${raw}` };
13653
14151
  }
13654
14152
  rmSync4(abs, { recursive: true, force: true });
@@ -13670,140 +14168,9 @@ function discardDisposableCli(args) {
13670
14168
  if (!result.ok) process.exit(1);
13671
14169
  }
13672
14170
 
13673
- // src/daemon-keeper.ts
13674
- init_config();
13675
- import { spawn as spawn6 } from "node:child_process";
13676
- init_util();
13677
- var DEFAULT_STALL_MS = 15 * 6e4;
13678
- var STARTUP_GRACE_MS = 2 * 6e4;
13679
- var KILL_GRACE_MS = 1e4;
13680
- var BACKOFF_BASE_MS = 5e3;
13681
- var BACKOFF_CAP_MS = 5 * 6e4;
13682
- var HEALTHY_RESET_MS = 30 * 6e4;
13683
- var POLL_MS = 5e3;
13684
- function resolveKeeperStallMs(flag, env = process.env) {
13685
- const fromFlag = typeof flag === "string" ? Number.parseInt(flag, 10) : NaN;
13686
- if (Number.isFinite(fromFlag) && fromFlag > 0) return fromFlag;
13687
- const fromEnv = Number.parseInt(env.KYNVER_DAEMON_STALL_MS ?? "", 10);
13688
- if (Number.isFinite(fromEnv) && fromEnv > 0) return fromEnv;
13689
- return DEFAULT_STALL_MS;
13690
- }
13691
- function shouldRunDaemonKeeper(args, env = process.env) {
13692
- if (args.keeperChild === true || args.keeperChild === "true") return false;
13693
- if (args.noSupervise === true || args.noSupervise === "true") return false;
13694
- if (args.supervised === "false") return false;
13695
- const envFlag5 = (env.KYNVER_DAEMON_SUPERVISED ?? "").trim().toLowerCase();
13696
- if (envFlag5 === "0" || envFlag5 === "false" || envFlag5 === "no" || envFlag5 === "off") {
13697
- return false;
13698
- }
13699
- return true;
13700
- }
13701
- function nextKeeperBackoffMs(consecutiveFailures, base = BACKOFF_BASE_MS, cap = BACKOFF_CAP_MS) {
13702
- const exp = Math.min(Math.max(consecutiveFailures, 1) - 1, 10);
13703
- return Math.min(base * 2 ** exp, cap);
13704
- }
13705
- function keeperRunWasHealthy(startedAtMs, endedAtMs, healthyMs = HEALTHY_RESET_MS) {
13706
- return endedAtMs - startedAtMs >= healthyMs;
13707
- }
13708
- function keeperLog(event, detail = {}) {
13709
- console.error(JSON.stringify({ event: `daemon_keeper_${event}`, ...detail }));
13710
- }
13711
- function buildKeeperChildArgv(argv) {
13712
- const out = [];
13713
- for (let i = 0; i < argv.length; i += 1) {
13714
- const arg = argv[i];
13715
- if (arg === "--supervised" || arg === "--no-supervise" || arg === "--keeper-child") continue;
13716
- if (arg === "--stall-ms") {
13717
- if (i + 1 < argv.length && !argv[i + 1].startsWith("--")) i += 1;
13718
- continue;
13719
- }
13720
- if (arg.startsWith("--stall-ms=") || arg.startsWith("--supervised=")) continue;
13721
- out.push(arg);
13722
- }
13723
- out.push("--keeper-child");
13724
- return out;
13725
- }
13726
- async function runDaemonKeeper(args, rawArgv = process.argv.slice(2)) {
13727
- const agentOsId = String(
13728
- required(String(args.agentOsId || loadUserConfig().agentOsId || ""), "--agent-os-id")
13729
- );
13730
- const stallMs = resolveKeeperStallMs(args.stallMs);
13731
- const childArgv = buildKeeperChildArgv(rawArgv);
13732
- const cliEntry = process.argv[1];
13733
- let stopping = false;
13734
- let child = null;
13735
- let consecutiveFailures = 0;
13736
- const stop = (signal) => {
13737
- stopping = true;
13738
- keeperLog("stop", { signal });
13739
- if (child?.pid) child.kill(signal);
13740
- };
13741
- process.on("SIGINT", () => stop("SIGINT"));
13742
- process.on("SIGTERM", () => stop("SIGTERM"));
13743
- keeperLog("start", { agentOsId, stallMs, childArgv });
13744
- while (!stopping) {
13745
- const startedAt = Date.now();
13746
- let exited = false;
13747
- let exitCode = null;
13748
- let exitSignal = null;
13749
- child = spawn6(process.execPath, [cliEntry, ...childArgv], {
13750
- stdio: "inherit",
13751
- env: process.env
13752
- });
13753
- keeperLog("child_spawned", { pid: child.pid ?? null });
13754
- child.on("exit", (code, signal) => {
13755
- exited = true;
13756
- exitCode = code;
13757
- exitSignal = signal;
13758
- });
13759
- while (!exited && !stopping) {
13760
- await sleepMsAsync(POLL_MS);
13761
- if (exited || stopping) break;
13762
- if (Date.now() - startedAt < STARTUP_GRACE_MS) continue;
13763
- const beat = readDaemonHeartbeat(agentOsId);
13764
- const ownBeat = beat && beat.pid === child.pid ? beat : null;
13765
- if (ownBeat && isDaemonHeartbeatStale(ownBeat, stallMs)) {
13766
- keeperLog("stall_detected", {
13767
- pid: child.pid ?? null,
13768
- lastBeatAt: ownBeat.observedAt,
13769
- stallMs
13770
- });
13771
- child.kill("SIGTERM");
13772
- await sleepMsAsync(KILL_GRACE_MS);
13773
- if (!exited) child.kill("SIGKILL");
13774
- break;
13775
- }
13776
- if (!ownBeat && Date.now() - startedAt > stallMs + STARTUP_GRACE_MS) {
13777
- keeperLog("no_heartbeat_detected", { pid: child.pid ?? null, stallMs });
13778
- child.kill("SIGTERM");
13779
- await sleepMsAsync(KILL_GRACE_MS);
13780
- if (!exited) child.kill("SIGKILL");
13781
- break;
13782
- }
13783
- }
13784
- while (!exited && !stopping) {
13785
- await sleepMsAsync(POLL_MS);
13786
- }
13787
- if (stopping) break;
13788
- const endedAt = Date.now();
13789
- if (keeperRunWasHealthy(startedAt, endedAt)) consecutiveFailures = 0;
13790
- consecutiveFailures += 1;
13791
- const backoff = nextKeeperBackoffMs(consecutiveFailures);
13792
- keeperLog("child_exited", {
13793
- code: exitCode,
13794
- signal: exitSignal,
13795
- uptimeMs: endedAt - startedAt,
13796
- consecutiveFailures,
13797
- respawnInMs: backoff
13798
- });
13799
- await sleepMsAsync(backoff);
13800
- }
13801
- keeperLog("stopped", { agentOsId });
13802
- }
13803
-
13804
14171
  // src/plan-progress.ts
13805
14172
  init_config();
13806
- import path63 from "node:path";
14173
+ import path65 from "node:path";
13807
14174
 
13808
14175
  // src/bounded-build/constants.ts
13809
14176
  var DEFAULT_BUILD_MEM_BUDGET_BYTES = 1536 * 1024 * 1024;
@@ -13844,7 +14211,7 @@ function formatNodeOptionsFlag(mb = resolveNodeOldSpaceSizeMb()) {
13844
14211
  }
13845
14212
 
13846
14213
  // src/bounded-build/systemd-wrap.ts
13847
- import { spawnSync as spawnSync8 } from "node:child_process";
14214
+ import { spawnSync as spawnSync9 } from "node:child_process";
13848
14215
  var systemdAvailableCache;
13849
14216
  function isSystemdRunAvailable() {
13850
14217
  if (process.env.KYNVER_BUILD_SKIP_SYSTEMD === "1" || process.env.KYNVER_BUILD_SKIP_SYSTEMD === "true") {
@@ -13855,7 +14222,7 @@ function isSystemdRunAvailable() {
13855
14222
  systemdAvailableCache = false;
13856
14223
  return false;
13857
14224
  }
13858
- const res = spawnSync8("systemd-run", ["--version"], { encoding: "utf8", stdio: ["ignore", "ignore", "pipe"] });
14225
+ const res = spawnSync9("systemd-run", ["--version"], { encoding: "utf8", stdio: ["ignore", "ignore", "pipe"] });
13859
14226
  systemdAvailableCache = res.status === 0;
13860
14227
  return systemdAvailableCache;
13861
14228
  }
@@ -13880,7 +14247,7 @@ function buildSystemdRunArgv(opts) {
13880
14247
 
13881
14248
  // src/bounded-build/admission.ts
13882
14249
  init_config();
13883
- import { spawnSync as spawnSync9 } from "node:child_process";
14250
+ import { spawnSync as spawnSync10 } from "node:child_process";
13884
14251
  init_meminfo();
13885
14252
  function positiveInt4(value, fallback) {
13886
14253
  const n = Number(value);
@@ -13917,7 +14284,7 @@ function assessBuildAdmission(opts = {}) {
13917
14284
  }
13918
14285
  function sleepMs2(ms) {
13919
14286
  if (ms <= 0) return;
13920
- spawnSync9(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
14287
+ spawnSync10(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
13921
14288
  stdio: "ignore"
13922
14289
  });
13923
14290
  }
@@ -13938,34 +14305,34 @@ function waitForBuildAdmission(timeoutMs, pollMs = 2e3, opts = {}) {
13938
14305
  }
13939
14306
 
13940
14307
  // src/bounded-build/exec.ts
13941
- import { spawnSync as spawnSync11 } from "node:child_process";
14308
+ import { spawnSync as spawnSync12 } from "node:child_process";
13942
14309
 
13943
14310
  // src/heavy-verification/slot.ts
13944
14311
  init_util();
13945
14312
  import {
13946
14313
  closeSync as closeSync7,
13947
- existsSync as existsSync41,
13948
- mkdirSync as mkdirSync9,
14314
+ existsSync as existsSync42,
14315
+ mkdirSync as mkdirSync11,
13949
14316
  openSync as openSync7,
13950
- readdirSync as readdirSync15,
14317
+ readdirSync as readdirSync14,
13951
14318
  readFileSync as readFileSync16,
13952
14319
  unlinkSync as unlinkSync4,
13953
- writeFileSync as writeFileSync6
14320
+ writeFileSync as writeFileSync7
13954
14321
  } from "node:fs";
13955
- import path61 from "node:path";
14322
+ import path63 from "node:path";
13956
14323
 
13957
14324
  // src/heavy-verification/paths.ts
13958
- import { mkdirSync as mkdirSync8 } from "node:fs";
13959
- import path60 from "node:path";
14325
+ import { mkdirSync as mkdirSync10 } from "node:fs";
14326
+ import path62 from "node:path";
13960
14327
  function resolveHeavyVerificationRoot() {
13961
- return path60.join(resolveKynverStateRoot(), "heavy-verification");
14328
+ return path62.join(resolveKynverStateRoot(), "heavy-verification");
13962
14329
  }
13963
14330
  function heavyVerificationSlotsDir() {
13964
- return path60.join(resolveHeavyVerificationRoot(), "slots");
14331
+ return path62.join(resolveHeavyVerificationRoot(), "slots");
13965
14332
  }
13966
14333
  function ensureHeavyVerificationDirs() {
13967
14334
  const dir = heavyVerificationSlotsDir();
13968
- mkdirSync8(dir, { recursive: true });
14335
+ mkdirSync10(dir, { recursive: true });
13969
14336
  return dir;
13970
14337
  }
13971
14338
 
@@ -13990,10 +14357,10 @@ function indexedSlotId(index) {
13990
14357
  return `slot-${index}`;
13991
14358
  }
13992
14359
  function slotFilePath(slotId, slotsDir = heavyVerificationSlotsDir()) {
13993
- return path61.join(slotsDir, `${slotId}.json`);
14360
+ return path63.join(slotsDir, `${slotId}.json`);
13994
14361
  }
13995
14362
  function readSlotRecord(filePath) {
13996
- if (!existsSync41(filePath)) return null;
14363
+ if (!existsSync42(filePath)) return null;
13997
14364
  try {
13998
14365
  const parsed = JSON.parse(readFileSync16(filePath, "utf8"));
13999
14366
  if (typeof parsed.slotId === "string" && typeof parsed.pid === "number" && typeof parsed.acquiredAt === "string" && typeof parsed.command === "string") {
@@ -14020,19 +14387,19 @@ function reclaimStaleSlot(filePath, staleMs) {
14020
14387
  }
14021
14388
  }
14022
14389
  function ensureSlotsDir(slotsDir) {
14023
- mkdirSync9(slotsDir, { recursive: true });
14390
+ mkdirSync11(slotsDir, { recursive: true });
14024
14391
  return slotsDir;
14025
14392
  }
14026
14393
  function reclaimStaleHeavyVerificationSlots(opts = {}) {
14027
14394
  const slotsDir = ensureSlotsDir(opts.slotsDir ?? ensureHeavyVerificationDirs());
14028
14395
  const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
14029
14396
  let reclaimed = 0;
14030
- for (const name of readdirSync15(slotsDir)) {
14397
+ for (const name of readdirSync14(slotsDir)) {
14031
14398
  if (!name.endsWith(".json")) continue;
14032
- const filePath = path61.join(slotsDir, name);
14033
- const before = existsSync41(filePath);
14399
+ const filePath = path63.join(slotsDir, name);
14400
+ const before = existsSync42(filePath);
14034
14401
  reclaimStaleSlot(filePath, staleMs);
14035
- if (before && !existsSync41(filePath)) reclaimed += 1;
14402
+ if (before && !existsSync42(filePath)) reclaimed += 1;
14036
14403
  }
14037
14404
  return reclaimed;
14038
14405
  }
@@ -14041,9 +14408,9 @@ function listActiveHeavyVerificationSlots(opts = {}) {
14041
14408
  const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
14042
14409
  reclaimStaleHeavyVerificationSlots({ slotsDir, staleMs });
14043
14410
  const active = [];
14044
- for (const name of readdirSync15(slotsDir)) {
14411
+ for (const name of readdirSync14(slotsDir)) {
14045
14412
  if (!name.endsWith(".json")) continue;
14046
- const record = readSlotRecord(path61.join(slotsDir, name));
14413
+ const record = readSlotRecord(path63.join(slotsDir, name));
14047
14414
  if (record && !slotIsStale(record, staleMs)) active.push(record);
14048
14415
  }
14049
14416
  return active;
@@ -14085,7 +14452,7 @@ function tryAcquireHeavyVerificationSlot(command, opts = {}) {
14085
14452
  };
14086
14453
  try {
14087
14454
  const fd = openSync7(filePath, "wx");
14088
- writeFileSync6(fd, JSON.stringify(record, null, 2), "utf8");
14455
+ writeFileSync7(fd, JSON.stringify(record, null, 2), "utf8");
14089
14456
  closeSync7(fd);
14090
14457
  const activeSlots2 = countActiveHeavyVerificationSlots({ slotsDir, staleMs });
14091
14458
  return {
@@ -14145,10 +14512,10 @@ function assessHeavyVerificationGate(command, opts = {}) {
14145
14512
  }
14146
14513
 
14147
14514
  // src/heavy-verification/gate.ts
14148
- import { spawnSync as spawnSync10 } from "node:child_process";
14515
+ import { spawnSync as spawnSync11 } from "node:child_process";
14149
14516
  function sleepMs3(ms) {
14150
14517
  if (ms <= 0) return;
14151
- spawnSync10(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
14518
+ spawnSync11(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
14152
14519
  stdio: "ignore"
14153
14520
  });
14154
14521
  }
@@ -14164,11 +14531,11 @@ function waitForHeavyVerificationSlot(command, timeoutMs, pollMs = 2e3, opts = {
14164
14531
 
14165
14532
  // src/harness-worktree-build-guard.ts
14166
14533
  init_paths();
14167
- import path62 from "node:path";
14534
+ import path64 from "node:path";
14168
14535
  function isPathUnderHarnessWorktree(cwd) {
14169
14536
  const worktreesDir = harnessWorktreesDir(resolveHarnessRoot());
14170
- const rel = path62.relative(worktreesDir, path62.resolve(cwd));
14171
- return rel.length > 0 && !rel.startsWith("..") && !path62.isAbsolute(rel);
14537
+ const rel = path64.relative(worktreesDir, path64.resolve(cwd));
14538
+ return rel.length > 0 && !rel.startsWith("..") && !path64.isAbsolute(rel);
14172
14539
  }
14173
14540
  function assessHarnessWorktreeBuildGuard(cwd) {
14174
14541
  if (!isPathUnderHarnessWorktree(cwd)) return { ok: true };
@@ -14192,7 +14559,7 @@ function envArgv(env) {
14192
14559
  return out;
14193
14560
  }
14194
14561
  function runSpawn(argv, opts) {
14195
- const res = spawnSync11(argv[0], argv.slice(1), {
14562
+ const res = spawnSync12(argv[0], argv.slice(1), {
14196
14563
  cwd: opts.cwd,
14197
14564
  env: opts.env,
14198
14565
  encoding: "utf8",
@@ -14381,7 +14748,7 @@ async function emitPlanProgress(args) {
14381
14748
  }
14382
14749
  function verifyPlanLocal(args) {
14383
14750
  const worktree = required(args.worktree ? String(args.worktree) : void 0, "worktree");
14384
- const cwd = path63.resolve(worktree);
14751
+ const cwd = path65.resolve(worktree);
14385
14752
  const summary = runHarnessVerifyCommands(cwd);
14386
14753
  const emitJson = args.json === true || args.json === "true";
14387
14754
  const payload = { passed: summary.passed, worktree: cwd, steps: summary.steps };
@@ -14430,10 +14797,10 @@ async function verifyPlan(args) {
14430
14797
  }
14431
14798
 
14432
14799
  // src/harness-verify-cli.ts
14433
- import path64 from "node:path";
14800
+ import path66 from "node:path";
14434
14801
  init_util();
14435
14802
  function runHarnessVerifyCli(args) {
14436
- const cwd = path64.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
14803
+ const cwd = path66.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
14437
14804
  const emitJson = args.json === true || args.json === "true" || args.emitJson === true || args.emitJson === "true";
14438
14805
  const commands = [];
14439
14806
  const rawCmd = args.command;
@@ -14565,7 +14932,9 @@ function runCleanupCli(args) {
14565
14932
  const skipFinalize = args.skipFinalize === true || args.skipFinalize === "true";
14566
14933
  const accountBytes = args.accountBytes === true || args.accountBytes === "true";
14567
14934
  const compact = args.compact === true || args.compact === "true";
14568
- const nodeModulesAgeMs = args.nodeModulesAgeMs ? Number(args.nodeModulesAgeMs) : DEFAULT_NODE_MODULES_AGE_MS;
14935
+ const nodeModulesAgeMsRaw = args.nodeModulesAgeMs;
14936
+ const scanDependencyCaches = nodeModulesAgeMsRaw != null && nodeModulesAgeMsRaw !== "";
14937
+ const nodeModulesAgeMs = scanDependencyCaches ? Number(nodeModulesAgeMsRaw) : void 0;
14569
14938
  const worktreesAgeMs = args.worktreesAgeMs ? Number(args.worktreesAgeMs) : 0;
14570
14939
  const includeOrphans = args.includeOrphans === true || args.includeOrphans === "true";
14571
14940
  const harnessRoot = args.harnessRoot ? String(args.harnessRoot) : void 0;
@@ -14573,7 +14942,8 @@ function runCleanupCli(args) {
14573
14942
  execute,
14574
14943
  finalizeStaleRuns: !skipFinalize,
14575
14944
  accountBytes,
14576
- nodeModulesAgeMs: Number.isFinite(nodeModulesAgeMs) ? nodeModulesAgeMs : DEFAULT_NODE_MODULES_AGE_MS,
14945
+ scanDependencyCaches,
14946
+ nodeModulesAgeMs: nodeModulesAgeMs !== void 0 && Number.isFinite(nodeModulesAgeMs) ? nodeModulesAgeMs : scanDependencyCaches ? DEFAULT_NODE_MODULES_AGE_MS : void 0,
14577
14947
  worktreesAgeMs: Number.isFinite(worktreesAgeMs) ? worktreesAgeMs : 0,
14578
14948
  includeOrphans,
14579
14949
  harnessRoot
@@ -14643,7 +15013,7 @@ function formatMonitorTickNotice(tick) {
14643
15013
  }
14644
15014
 
14645
15015
  // src/monitor/monitor.service.ts
14646
- import path66 from "node:path";
15016
+ import path68 from "node:path";
14647
15017
  init_run_store();
14648
15018
  init_status();
14649
15019
  init_util();
@@ -14702,19 +15072,19 @@ function classifyWorkerHealth(input) {
14702
15072
  // src/monitor/monitor.store.ts
14703
15073
  init_paths();
14704
15074
  init_util();
14705
- import { existsSync as existsSync42, mkdirSync as mkdirSync10, readdirSync as readdirSync16, unlinkSync as unlinkSync5 } from "node:fs";
14706
- import path65 from "node:path";
15075
+ import { existsSync as existsSync43, mkdirSync as mkdirSync12, readdirSync as readdirSync15, unlinkSync as unlinkSync5 } from "node:fs";
15076
+ import path67 from "node:path";
14707
15077
  function monitorsDir() {
14708
15078
  const { harnessRoot } = getHarnessPaths();
14709
- const dir = path65.join(harnessRoot, "monitors");
14710
- mkdirSync10(dir, { recursive: true });
15079
+ const dir = path67.join(harnessRoot, "monitors");
15080
+ mkdirSync12(dir, { recursive: true });
14711
15081
  return dir;
14712
15082
  }
14713
15083
  function monitorIdFor(runId, workerName) {
14714
15084
  return workerName ? `${safeSlug(runId)}--${safeSlug(workerName)}` : safeSlug(runId);
14715
15085
  }
14716
15086
  function monitorPath(monitorId) {
14717
- return path65.join(monitorsDir(), `${monitorId}.json`);
15087
+ return path67.join(monitorsDir(), `${monitorId}.json`);
14718
15088
  }
14719
15089
  function loadMonitorSession(monitorId) {
14720
15090
  return readJson(monitorPath(monitorId), void 0);
@@ -14724,18 +15094,18 @@ function saveMonitorSession(session) {
14724
15094
  }
14725
15095
  function deleteMonitorSession(monitorId) {
14726
15096
  const file = monitorPath(monitorId);
14727
- if (!existsSync42(file)) return false;
15097
+ if (!existsSync43(file)) return false;
14728
15098
  unlinkSync5(file);
14729
15099
  return true;
14730
15100
  }
14731
15101
  function listMonitorSessions() {
14732
15102
  const dir = monitorsDir();
14733
- if (!existsSync42(dir)) return [];
15103
+ if (!existsSync43(dir)) return [];
14734
15104
  const entries = [];
14735
- for (const name of readdirSync16(dir)) {
15105
+ for (const name of readdirSync15(dir)) {
14736
15106
  if (!name.endsWith(".json")) continue;
14737
15107
  const session = readJson(
14738
- path65.join(dir, name),
15108
+ path67.join(dir, name),
14739
15109
  void 0
14740
15110
  );
14741
15111
  if (!session?.monitorId) continue;
@@ -14828,7 +15198,7 @@ async function fetchTaskLeasesForWorkers(input) {
14828
15198
  // src/monitor/monitor.service.ts
14829
15199
  function workerRecord2(runId, name) {
14830
15200
  return readJson(
14831
- path66.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
15201
+ path68.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
14832
15202
  void 0
14833
15203
  );
14834
15204
  }
@@ -15037,18 +15407,18 @@ async function runMonitorLoop(args) {
15037
15407
  init_util();
15038
15408
  init_paths();
15039
15409
  import { spawn as spawn7 } from "node:child_process";
15040
- import { closeSync as closeSync8, existsSync as existsSync43, openSync as openSync8 } from "node:fs";
15041
- import path67 from "node:path";
15410
+ import { closeSync as closeSync8, existsSync as existsSync44, openSync as openSync8 } from "node:fs";
15411
+ import path69 from "node:path";
15042
15412
  import { fileURLToPath as fileURLToPath3 } from "node:url";
15043
15413
  function resolveDefaultCliPath2() {
15044
- return path67.join(fileURLToPath3(new URL(".", import.meta.url)), "cli.js");
15414
+ return path69.join(fileURLToPath3(new URL(".", import.meta.url)), "cli.js");
15045
15415
  }
15046
15416
  function spawnMonitorSidecar(opts) {
15047
15417
  const cliPath = opts.cliPath ?? resolveDefaultCliPath2();
15048
- if (!existsSync43(cliPath)) return void 0;
15418
+ if (!existsSync44(cliPath)) return void 0;
15049
15419
  const monitorId = monitorIdFor(opts.runId, opts.workerName);
15050
15420
  const { harnessRoot } = getHarnessPaths();
15051
- const logPath = path67.join(harnessRoot, "monitors", `${monitorId}.log`);
15421
+ const logPath = path69.join(harnessRoot, "monitors", `${monitorId}.log`);
15052
15422
  let logFd;
15053
15423
  try {
15054
15424
  logFd = openSync8(logPath, "a");
@@ -15169,13 +15539,13 @@ async function monitorTickCli(args) {
15169
15539
  }
15170
15540
 
15171
15541
  // src/package-version.ts
15172
- import { existsSync as existsSync44, readFileSync as readFileSync18 } from "node:fs";
15542
+ import { existsSync as existsSync45, readFileSync as readFileSync18 } from "node:fs";
15173
15543
  import { dirname, join } from "node:path";
15174
15544
  import { fileURLToPath as fileURLToPath4 } from "node:url";
15175
15545
  function resolvePackageRoot(moduleUrl) {
15176
15546
  let dir = dirname(fileURLToPath4(moduleUrl));
15177
15547
  for (let depth = 0; depth < 6; depth += 1) {
15178
- if (existsSync44(join(dir, "package.json"))) return dir;
15548
+ if (existsSync45(join(dir, "package.json"))) return dir;
15179
15549
  const parent = dirname(dir);
15180
15550
  if (parent === dir) break;
15181
15551
  dir = parent;
@@ -15281,7 +15651,7 @@ init_run_store();
15281
15651
  init_status();
15282
15652
  init_util();
15283
15653
  init_config();
15284
- import path68 from "node:path";
15654
+ import path70 from "node:path";
15285
15655
  function skip(runId, worker, taskId, agentOsId, leaseOwner, reason) {
15286
15656
  return { runId, worker, taskId, agentOsId, leaseOwner, action: "skipped", reason };
15287
15657
  }
@@ -15294,7 +15664,7 @@ async function postRestartUnblock(args) {
15294
15664
  const errors = [];
15295
15665
  for (const run of listRunRecords()) {
15296
15666
  for (const name of Object.keys(run.workers ?? {})) {
15297
- const workerPath = path68.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
15667
+ const workerPath = path70.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
15298
15668
  const worker = readJson(workerPath, void 0);
15299
15669
  if (!worker) {
15300
15670
  skipped.push(skip(run.id, name, "", "", "", "worker.json missing"));
@@ -15408,9 +15778,9 @@ async function postRestartUnblockCli(args) {
15408
15778
  // src/default-repo-cli.ts
15409
15779
  init_path_values();
15410
15780
  init_config();
15411
- import path69 from "node:path";
15781
+ import path71 from "node:path";
15412
15782
  import { homedir as homedir16 } from "node:os";
15413
- var CONFIG_FILE2 = path69.join(homedir16(), ".kynver", "config.json");
15783
+ var CONFIG_FILE2 = path71.join(homedir16(), ".kynver", "config.json");
15414
15784
  function ensureDefaultRepo(opts) {
15415
15785
  const existing = loadUserConfig();
15416
15786
  const resolved = resolveDefaultRepo({ ...opts, config: existing });
@@ -15491,19 +15861,19 @@ function summarizeResolvedDefaultRepo(resolved) {
15491
15861
  }
15492
15862
 
15493
15863
  // src/doctor/runtime-takeover.ts
15494
- import path71 from "node:path";
15864
+ import path73 from "node:path";
15495
15865
  init_path_values();
15496
15866
 
15497
15867
  // src/doctor/runtime-takeover.probes.ts
15498
15868
  init_config();
15499
- import { accessSync, constants, existsSync as existsSync45, readFileSync as readFileSync19 } from "node:fs";
15869
+ import { accessSync, constants, existsSync as existsSync46, readFileSync as readFileSync19 } from "node:fs";
15500
15870
  import { homedir as homedir17 } from "node:os";
15501
- import path70 from "node:path";
15502
- import { spawnSync as spawnSync12 } from "node:child_process";
15871
+ import path72 from "node:path";
15872
+ import { spawnSync as spawnSync13 } from "node:child_process";
15503
15873
  init_paths();
15504
15874
  function captureCommand(bin, args) {
15505
15875
  try {
15506
- const res = spawnSync12(bin, args, { encoding: "utf8" });
15876
+ const res = spawnSync13(bin, args, { encoding: "utf8" });
15507
15877
  const stdout = (res.stdout || "").trim();
15508
15878
  const stderr = (res.stderr || "").trim();
15509
15879
  const ok = res.status === 0;
@@ -15528,7 +15898,7 @@ function tokenPrefix(token) {
15528
15898
  return trimmed.length <= 12 ? `${trimmed}\u2026` : `${trimmed.slice(0, 12)}\u2026`;
15529
15899
  }
15530
15900
  function isWritable(target) {
15531
- if (!existsSync45(target)) return false;
15901
+ if (!existsSync46(target)) return false;
15532
15902
  try {
15533
15903
  accessSync(target, constants.W_OK);
15534
15904
  return true;
@@ -15541,11 +15911,11 @@ var defaultRuntimeTakeoverProbes = {
15541
15911
  commandOnPath: (bin) => captureCommand(process.platform === "win32" ? "where" : "which", [bin]),
15542
15912
  kynverVersion: (bin) => captureCommand(bin, ["--version"]),
15543
15913
  loadConfig: () => loadUserConfig(),
15544
- configFilePath: () => path70.join(homedir17(), ".kynver", "config.json"),
15545
- credentialsFilePath: () => path70.join(homedir17(), ".kynver", "credentials"),
15914
+ configFilePath: () => path72.join(homedir17(), ".kynver", "config.json"),
15915
+ credentialsFilePath: () => path72.join(homedir17(), ".kynver", "credentials"),
15546
15916
  readCredentials: () => {
15547
- const credPath = path70.join(homedir17(), ".kynver", "credentials");
15548
- if (!existsSync45(credPath)) {
15917
+ const credPath = path72.join(homedir17(), ".kynver", "credentials");
15918
+ if (!existsSync46(credPath)) {
15549
15919
  return { hasApiKey: false };
15550
15920
  }
15551
15921
  try {
@@ -15579,8 +15949,8 @@ var defaultRuntimeTakeoverProbes = {
15579
15949
  })()
15580
15950
  }),
15581
15951
  harnessRoot: () => resolveHarnessRoot(),
15582
- legacyOpenclawHarnessRoot: () => path70.join(homedir17(), ".openclaw", "harness"),
15583
- pathExists: (target) => existsSync45(target),
15952
+ legacyOpenclawHarnessRoot: () => path72.join(homedir17(), ".openclaw", "harness"),
15953
+ pathExists: (target) => existsSync46(target),
15584
15954
  pathWritable: (target) => isWritable(target)
15585
15955
  };
15586
15956
 
@@ -15986,8 +16356,8 @@ function assessVercelDeployEvidence(probes) {
15986
16356
  }
15987
16357
  function assessHarnessDirs(probes) {
15988
16358
  const harnessRoot = probes.harnessRoot();
15989
- const runsDir = path71.join(harnessRoot, "runs");
15990
- const worktreesDir = path71.join(harnessRoot, "worktrees");
16359
+ const runsDir = path73.join(harnessRoot, "runs");
16360
+ const worktreesDir = path73.join(harnessRoot, "worktrees");
15991
16361
  const displayHarnessRoot = redactHomePath(harnessRoot);
15992
16362
  const displayRunsDir = redactHomePath(runsDir);
15993
16363
  const displayWorktreesDir = redactHomePath(worktreesDir);
@@ -16256,9 +16626,9 @@ function applySchedulerCutoverAttestation(config) {
16256
16626
 
16257
16627
  // src/scheduler-cutover-cli.ts
16258
16628
  init_config();
16259
- import path72 from "node:path";
16629
+ import path74 from "node:path";
16260
16630
  import { homedir as homedir18 } from "node:os";
16261
- var CONFIG_FILE3 = path72.join(homedir18(), ".kynver", "config.json");
16631
+ var CONFIG_FILE3 = path74.join(homedir18(), ".kynver", "config.json");
16262
16632
  function runSchedulerCutoverCheckCli(json = false) {
16263
16633
  const config = loadUserConfig();
16264
16634
  const report = assessSchedulerCutover(config);
@@ -16402,7 +16772,7 @@ init_config();
16402
16772
  init_config();
16403
16773
  init_path_values();
16404
16774
  init_util();
16405
- import { existsSync as existsSync49 } from "node:fs";
16775
+ import { existsSync as existsSync50 } from "node:fs";
16406
16776
 
16407
16777
  // src/cron/cron-id.ts
16408
16778
  import { createHash as createHash4 } from "node:crypto";
@@ -16421,13 +16791,13 @@ function deterministicCronProviderId(spec) {
16421
16791
 
16422
16792
  // src/cron/cron-install-plan.ts
16423
16793
  import { homedir as homedir20 } from "node:os";
16424
- import path74 from "node:path";
16794
+ import path76 from "node:path";
16425
16795
 
16426
16796
  // src/cron/cron-env-file.ts
16427
- import { existsSync as existsSync46, mkdirSync as mkdirSync11, readFileSync as readFileSync20, writeFileSync as writeFileSync7 } from "node:fs";
16797
+ import { existsSync as existsSync47, mkdirSync as mkdirSync13, readFileSync as readFileSync20, writeFileSync as writeFileSync8 } from "node:fs";
16428
16798
  import { homedir as homedir19 } from "node:os";
16429
- import path73 from "node:path";
16430
- var DEFAULT_KYNVER_ENV_FILE = path73.join(homedir19(), ".kynver", ".env");
16799
+ import path75 from "node:path";
16800
+ var DEFAULT_KYNVER_ENV_FILE = path75.join(homedir19(), ".kynver", ".env");
16431
16801
  function parseEnvFile(content) {
16432
16802
  const map = /* @__PURE__ */ new Map();
16433
16803
  for (const line of content.split(/\r?\n/)) {
@@ -16454,12 +16824,12 @@ function serializeEnvFile(values, header = "# Managed by kynver cron install \u2
16454
16824
  return lines.join("\n");
16455
16825
  }
16456
16826
  function readEnvFile(filePath = DEFAULT_KYNVER_ENV_FILE) {
16457
- if (!existsSync46(filePath)) return /* @__PURE__ */ new Map();
16827
+ if (!existsSync47(filePath)) return /* @__PURE__ */ new Map();
16458
16828
  return parseEnvFile(readFileSync20(filePath, "utf8"));
16459
16829
  }
16460
16830
  function mergeEnvFile(updates, options = {}) {
16461
16831
  const filePath = options.filePath ?? DEFAULT_KYNVER_ENV_FILE;
16462
- const existing = existsSync46(filePath) ? readFileSync20(filePath, "utf8") : "";
16832
+ const existing = existsSync47(filePath) ? readFileSync20(filePath, "utf8") : "";
16463
16833
  const map = parseEnvFile(existing);
16464
16834
  const keysWritten = [];
16465
16835
  const keysRemoved = [];
@@ -16484,8 +16854,8 @@ function mergeEnvFile(updates, options = {}) {
16484
16854
  }
16485
16855
  const nextContent = serializeEnvFile(map);
16486
16856
  if (changed) {
16487
- mkdirSync11(path73.dirname(filePath), { recursive: true });
16488
- writeFileSync7(filePath, nextContent, { mode: 384 });
16857
+ mkdirSync13(path75.dirname(filePath), { recursive: true });
16858
+ writeFileSync8(filePath, nextContent, { mode: 384 });
16489
16859
  }
16490
16860
  return { path: filePath, changed, keysWritten, keysRemoved };
16491
16861
  }
@@ -16502,7 +16872,7 @@ var VERCEL_KYNVER_CRON_CUTOVER_STEPS = [
16502
16872
  function buildCronInstallPlan(input) {
16503
16873
  const storePath = input.storePath?.trim() || defaultKynverCronStorePath();
16504
16874
  const envFilePath = input.envFilePath?.trim() || DEFAULT_KYNVER_ENV_FILE;
16505
- const configPath = path74.join(homedir20(), ".kynver", "config.json");
16875
+ const configPath = path76.join(homedir20(), ".kynver", "config.json");
16506
16876
  const callbackPath = `/api/agent-os/by-id/${input.agentOsId}/scheduler/fire`;
16507
16877
  const prerequisites = [];
16508
16878
  if (!input.apiBaseUrl?.trim()) prerequisites.push("apiBaseUrl \u2014 run `kynver setup --api-base-url \u2026`");
@@ -16654,13 +17024,13 @@ function resolveCronSecretForInstall(envFilePath = DEFAULT_KYNVER_ENV_FILE) {
16654
17024
  }
16655
17025
 
16656
17026
  // src/cron/cron-install-systemd.ts
16657
- import { existsSync as existsSync47, mkdirSync as mkdirSync12, writeFileSync as writeFileSync8 } from "node:fs";
17027
+ import { existsSync as existsSync48, mkdirSync as mkdirSync14, writeFileSync as writeFileSync9 } from "node:fs";
16658
17028
  import { homedir as homedir21 } from "node:os";
16659
- import path75 from "node:path";
16660
- import { spawnSync as spawnSync13 } from "node:child_process";
17029
+ import path77 from "node:path";
17030
+ import { spawnSync as spawnSync14 } from "node:child_process";
16661
17031
  var KYNVER_CRON_DAEMON_UNIT = "kynver-cron-daemon.service";
16662
17032
  function defaultSystemdUserUnitDir() {
16663
- return path75.join(homedir21(), ".config", "systemd", "user");
17033
+ return path77.join(homedir21(), ".config", "systemd", "user");
16664
17034
  }
16665
17035
  function renderKynverCronDaemonService(input) {
16666
17036
  const kynverBin = input.kynverBin?.trim() || "kynver";
@@ -16693,7 +17063,7 @@ function installSystemdUserDaemon(input, execute) {
16693
17063
  };
16694
17064
  }
16695
17065
  const unitDir = defaultSystemdUserUnitDir();
16696
- const unitPath = path75.join(unitDir, KYNVER_CRON_DAEMON_UNIT);
17066
+ const unitPath = path77.join(unitDir, KYNVER_CRON_DAEMON_UNIT);
16697
17067
  const content = renderKynverCronDaemonService(input);
16698
17068
  if (!execute) {
16699
17069
  return {
@@ -16705,10 +17075,10 @@ function installSystemdUserDaemon(input, execute) {
16705
17075
  note: "Dry-run \u2014 pass --execute to write and enable the user unit."
16706
17076
  };
16707
17077
  }
16708
- mkdirSync12(unitDir, { recursive: true });
16709
- const existed = existsSync47(unitPath);
16710
- writeFileSync8(unitPath, content, "utf8");
16711
- const reload = spawnSync13("systemctl", ["--user", "daemon-reload"], { encoding: "utf8" });
17078
+ mkdirSync14(unitDir, { recursive: true });
17079
+ const existed = existsSync48(unitPath);
17080
+ writeFileSync9(unitPath, content, "utf8");
17081
+ const reload = spawnSync14("systemctl", ["--user", "daemon-reload"], { encoding: "utf8" });
16712
17082
  if (reload.status !== 0) {
16713
17083
  return {
16714
17084
  supported: true,
@@ -16719,7 +17089,7 @@ function installSystemdUserDaemon(input, execute) {
16719
17089
  note: `Wrote ${unitPath} but systemctl --user daemon-reload failed: ${reload.stderr || reload.stdout}`
16720
17090
  };
16721
17091
  }
16722
- const enable = spawnSync13("systemctl", ["--user", "enable", "--now", KYNVER_CRON_DAEMON_UNIT], {
17092
+ const enable = spawnSync14("systemctl", ["--user", "enable", "--now", KYNVER_CRON_DAEMON_UNIT], {
16723
17093
  encoding: "utf8"
16724
17094
  });
16725
17095
  return {
@@ -16733,7 +17103,7 @@ function installSystemdUserDaemon(input, execute) {
16733
17103
  }
16734
17104
 
16735
17105
  // src/cron/cron-install-verify.ts
16736
- import { existsSync as existsSync48 } from "node:fs";
17106
+ import { existsSync as existsSync49 } from "node:fs";
16737
17107
  async function verifyCronInstall(input) {
16738
17108
  const envFilePath = input.envFilePath ?? DEFAULT_KYNVER_ENV_FILE;
16739
17109
  const checks = [];
@@ -16766,14 +17136,14 @@ async function verifyCronInstall(input) {
16766
17136
  });
16767
17137
  checks.push({
16768
17138
  id: "env_file",
16769
- ok: existsSync48(envFilePath) && Boolean(fileEnv.get("KYNVER_CRON_SECRET")),
16770
- summary: existsSync48(envFilePath) ? `~/.kynver/.env present (${fileEnv.size} keys)` : "~/.kynver/.env missing",
17139
+ ok: existsSync49(envFilePath) && Boolean(fileEnv.get("KYNVER_CRON_SECRET")),
17140
+ summary: existsSync49(envFilePath) ? `~/.kynver/.env present (${fileEnv.size} keys)` : "~/.kynver/.env missing",
16771
17141
  remediation: "Run `kynver cron install` to write ~/.kynver/.env."
16772
17142
  });
16773
17143
  checks.push({
16774
17144
  id: "cron_store",
16775
- ok: existsSync48(env.storePath),
16776
- summary: existsSync48(env.storePath) ? `cron store present (${env.storePath})` : `cron store missing (${env.storePath})`,
17145
+ ok: existsSync49(env.storePath),
17146
+ summary: existsSync49(env.storePath) ? `cron store present (${env.storePath})` : `cron store missing (${env.storePath})`,
16777
17147
  remediation: "Run `kynver cron install` to initialize the local store."
16778
17148
  });
16779
17149
  const jobs = await loadCronJobs(env.storePath).catch(() => []);
@@ -16807,7 +17177,7 @@ function resolveDaemonRunId(config, explicit) {
16807
17177
  if (explicit?.trim()) return explicit.trim();
16808
17178
  if (config.defaultDaemonRunId?.trim()) return config.defaultDaemonRunId.trim();
16809
17179
  const runsDir = getPaths().runsDir;
16810
- if (!existsSync49(runsDir)) return null;
17180
+ if (!existsSync50(runsDir)) return null;
16811
17181
  const runs = listRunRecords().sort(
16812
17182
  (a, b) => Date.parse(b.createdAt) - Date.parse(a.createdAt)
16813
17183
  );
@@ -16859,7 +17229,7 @@ async function runCronInstall(opts = {}) {
16859
17229
  process.env.KYNVER_CRON_STORE_PATH = plan.storePath;
16860
17230
  process.env.KYNVER_CRON_TICK_ENABLED = "1";
16861
17231
  const storeInit = await ensureCronStoreInitialized(plan.storePath);
16862
- result.storeInitialized = storeInit.created || existsSync49(plan.storePath);
17232
+ result.storeInitialized = storeInit.created || existsSync50(plan.storePath);
16863
17233
  const apiKey = loadApiKey();
16864
17234
  if (apiKey) {
16865
17235
  try {
@@ -17078,10 +17448,10 @@ var LANDING_MAINTAINER_LANE_SPEC = {
17078
17448
  };
17079
17449
 
17080
17450
  // src/lane/landing-maintainer-local.ts
17081
- import { spawnSync as spawnSync14 } from "node:child_process";
17082
- import path76 from "node:path";
17451
+ import { spawnSync as spawnSync15 } from "node:child_process";
17452
+ import path78 from "node:path";
17083
17453
  function runLandingWrapper(prNumber, repoRoot, execute) {
17084
- const script = path76.join(repoRoot, LANDING_MAINTAINER_LANE_SPEC.landScript);
17454
+ const script = path78.join(repoRoot, LANDING_MAINTAINER_LANE_SPEC.landScript);
17085
17455
  const args = [script, String(prNumber), ...LANDING_MAINTAINER_LANE_SPEC.landScriptArgs];
17086
17456
  if (!execute) {
17087
17457
  return {
@@ -17092,7 +17462,7 @@ function runLandingWrapper(prNumber, repoRoot, execute) {
17092
17462
  stderr: ""
17093
17463
  };
17094
17464
  }
17095
- const result = spawnSync14("node", args, {
17465
+ const result = spawnSync15("node", args, {
17096
17466
  cwd: repoRoot,
17097
17467
  encoding: "utf8",
17098
17468
  timeout: 10 * 60 * 1e3
@@ -17107,7 +17477,7 @@ function runLandingWrapper(prNumber, repoRoot, execute) {
17107
17477
  }
17108
17478
  function resolveLandingMaintainerRepoRoot(args) {
17109
17479
  const explicit = args.repoPath ? String(args.repoPath).trim() : "";
17110
- if (explicit) return path76.resolve(explicit);
17480
+ if (explicit) return path78.resolve(explicit);
17111
17481
  const resolved = resolveDefaultRepo();
17112
17482
  return resolved?.repo ?? process.cwd();
17113
17483
  }
@@ -17232,9 +17602,9 @@ function usage(code = 0) {
17232
17602
  "Usage:",
17233
17603
  " kynver login [--api-key KEY] [--api-base-url URL] (omit --api-key to authorize in the browser)",
17234
17604
  " kynver bootstrap [--api-base-url URL] [--api-key KEY] [--repo PATH] (login + setup + runner credential in one shot)",
17235
- " kynver start [--repo PATH] [--api-base-url URL] [--run RUN_ID] [--interval-ms MS] (bring your agent online: bootstrap if needed + run + daemon)",
17605
+ " kynver start [--repo PATH] [--api-base-url URL] [--run RUN_ID] [--interval-ms MS] [--chat-oauth] (bring your agent online: bootstrap if needed + run + daemon; --chat-oauth lets delegated chat turns use your local Claude subscription)",
17236
17606
  " kynver runner credential [--agent-os-id ID] [--base-url URL]",
17237
- " kynver setup [--api-base-url URL] [--agent-os-id ID] [--agent-os-slug SLUG] [--box-kind forge|ghost] [--repo PATH] [--discover-repo] [--max-workers N] [--provider claude|cursor]",
17607
+ " kynver setup [--api-base-url URL] [--agent-os-id ID] [--agent-os-slug SLUG] [--box-kind forge|ghost] [--repo PATH] [--discover-repo] [--max-workers N] [--provider claude|cursor] [--chat-oauth]",
17238
17608
  " kynver daemon --run RUN_ID --agent-os-id AOS_ID [--execute] [--interval-ms MS] [--stall-ms MS] [--no-supervise]",
17239
17609
  " kynver status --run RUN_ID [--blocked] [--running] [--task TASK_ID] [--worker WORKER] [--full] # top-level compact run status",
17240
17610
  " kynver run create [--repo /path/repo] [--name name] [--base origin/main]",
@@ -17298,8 +17668,8 @@ async function main(argv = process.argv.slice(2)) {
17298
17668
  if (action && isHelpFlag(action) || rest.some(isHelpFlag)) return usage(0);
17299
17669
  const args = parseArgs(rest);
17300
17670
  const { runsDir, worktreesDir } = getPaths();
17301
- mkdirSync13(runsDir, { recursive: true });
17302
- mkdirSync13(worktreesDir, { recursive: true });
17671
+ mkdirSync15(runsDir, { recursive: true });
17672
+ mkdirSync15(worktreesDir, { recursive: true });
17303
17673
  if (scope === "daemon") {
17304
17674
  assertNativeDaemonAllowed();
17305
17675
  }