@kynver-app/runtime 0.1.77 → 0.1.79

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
@@ -746,23 +746,23 @@ function isWslHost() {
746
746
  function observeWslHostDisk(options = {}) {
747
747
  const wsl = options.forceWsl === void 0 ? isWslHost() : options.forceWsl;
748
748
  if (!wsl) return null;
749
- const path59 = options.wslHostMount?.trim() || process.env.KYNVER_WSL_HOST_MOUNT?.trim() || DEFAULT_WSL_HOST_MOUNT;
749
+ const path62 = options.wslHostMount?.trim() || process.env.KYNVER_WSL_HOST_MOUNT?.trim() || DEFAULT_WSL_HOST_MOUNT;
750
750
  const warnBelowBytes = options.wslHostFreeWarnBytes ?? DEFAULT_WSL_HOST_WARN_FREE_BYTES;
751
751
  const criticalBelowBytes = options.wslHostFreeCriticalBytes ?? DEFAULT_WSL_HOST_CRITICAL_FREE_BYTES;
752
752
  const statfs = options.statfs ?? statfsSync;
753
753
  let stats;
754
754
  try {
755
- stats = statfs(path59);
755
+ stats = statfs(path62);
756
756
  } catch (error) {
757
757
  return {
758
758
  ok: false,
759
- path: path59,
759
+ path: path62,
760
760
  freeBytes: 0,
761
761
  totalBytes: 0,
762
762
  usedPercent: 100,
763
763
  warnBelowBytes,
764
764
  criticalBelowBytes,
765
- reason: `Windows host disk probe failed at ${path59}: ${error.message}`,
765
+ reason: `Windows host disk probe failed at ${path62}: ${error.message}`,
766
766
  probeError: error.message
767
767
  };
768
768
  }
@@ -776,11 +776,11 @@ function observeWslHostDisk(options = {}) {
776
776
  let reason = null;
777
777
  if (!ok) {
778
778
  const tag = criticalFree ? "critical" : "warning";
779
- reason = `Windows host disk ${path59} at ${tag}: ${freeGiB} GiB free (<${(criticalFree ? criticalBelowBytes : warnBelowBytes) / 1024 / 1024 / 1024} GiB); WSL VHDX cannot grow safely. ${summarizeWslRecoverySteps()}`;
779
+ reason = `Windows host disk ${path62} at ${tag}: ${freeGiB} GiB free (<${(criticalFree ? criticalBelowBytes : warnBelowBytes) / 1024 / 1024 / 1024} GiB); WSL VHDX cannot grow safely. ${summarizeWslRecoverySteps()}`;
780
780
  }
781
781
  return {
782
782
  ok,
783
- path: path59,
783
+ path: path62,
784
784
  freeBytes,
785
785
  totalBytes,
786
786
  usedPercent,
@@ -800,12 +800,12 @@ var DEFAULT_CRITICAL_FREE_BYTES = 15 * 1024 * 1024 * 1024;
800
800
  var DEFAULT_MAX_USED_PERCENT = 80;
801
801
  var DEFAULT_HARD_MAX_USED_PERCENT = 90;
802
802
  function observeRunnerDiskGate(input = {}) {
803
- const path59 = input.diskPath?.trim() || "/";
803
+ const path62 = input.diskPath?.trim() || "/";
804
804
  const warnBelowBytes = input.diskFreeWarnBytes ?? DEFAULT_WARN_FREE_BYTES;
805
805
  const criticalBelowBytes = input.diskFreeCriticalBytes ?? DEFAULT_CRITICAL_FREE_BYTES;
806
806
  const maxUsedPercent = input.diskMaxUsedPercent ?? DEFAULT_MAX_USED_PERCENT;
807
807
  const hardMaxUsedPercent = input.diskHardMaxUsedPercent ?? DEFAULT_HARD_MAX_USED_PERCENT;
808
- const stats = statfsSync2(path59);
808
+ const stats = statfsSync2(path62);
809
809
  const freeBytes = Number(stats.bavail) * Number(stats.bsize);
810
810
  const totalBytes = Number(stats.blocks) * Number(stats.bsize);
811
811
  const usedPercent = totalBytes > 0 ? (totalBytes - freeBytes) / totalBytes * 100 : 100;
@@ -828,7 +828,7 @@ function observeRunnerDiskGate(input = {}) {
828
828
  }
829
829
  return {
830
830
  ok,
831
- path: path59,
831
+ path: path62,
832
832
  freeBytes,
833
833
  totalBytes,
834
834
  usedPercent,
@@ -2580,7 +2580,9 @@ function resolveOrchestrationRouting(input) {
2580
2580
  const risk = classifyOrchestrationRisk(task);
2581
2581
  const inventory = resolveInventory({
2582
2582
  inventory: input.inventory,
2583
- codexBinding: input.codexBinding ?? resolveCodexOrchestrationAdapter()
2583
+ // When callers pass a pre-built inventory (tests, CC snapshots), do not probe live
2584
+ // Hermes/Codex bindings — that would overwrite mocked oauth_local with subscription_hermes.
2585
+ codexBinding: input.codexBinding ?? (input.inventory ? null : resolveCodexOrchestrationAdapter())
2584
2586
  });
2585
2587
  const explicit = input.preferLowCost === false ? null : input.explicitProvider?.trim().toLowerCase();
2586
2588
  const explicitModel = input.explicitModel?.trim() || void 0;
@@ -3394,6 +3396,7 @@ function buildPrompt(input) {
3394
3396
  "",
3395
3397
  ...input.personaMarkdown?.trim() ? [input.personaMarkdown.trim(), ""] : [],
3396
3398
  ...input.instructionPolicyMarkdown?.trim() ? ["Operating rules (Lane A \u2014 from AgentOS memory policy):", input.instructionPolicyMarkdown.trim(), ""] : [],
3399
+ ...input.memoryQualityMarkdown?.trim() ? [input.memoryQualityMarkdown.trim(), ""] : [],
3397
3400
  ...input.repairTargetPrUrl ? [
3398
3401
  ...repairTargetPromptLines({
3399
3402
  targetPrUrl: input.repairTargetPrUrl,
@@ -4188,8 +4191,8 @@ function dirtyPathsCoveredByDisposableRemoval(changedFiles, removed) {
4188
4191
  if (removed.length === 0) return false;
4189
4192
  const removedSet = new Set(removed.map((p) => normalizeRelativePath(p)));
4190
4193
  return material.every((line) => {
4191
- const path59 = normalizeRelativePath(pathFromGitStatusLine(line));
4192
- return removedSet.has(path59);
4194
+ const path62 = normalizeRelativePath(pathFromGitStatusLine(line));
4195
+ return removedSet.has(path62);
4193
4196
  });
4194
4197
  }
4195
4198
 
@@ -4611,7 +4614,9 @@ async function completeWorker(args) {
4611
4614
  }
4612
4615
  }
4613
4616
  function workerStatus(args) {
4614
- const worker = loadWorker(String(args.run), String(args.name));
4617
+ const runId = required(typeof args.run === "string" ? args.run : "", "--run");
4618
+ const name = required(typeof args.name === "string" ? args.name : "", "--name");
4619
+ const worker = loadWorker(runId, name);
4615
4620
  const run = loadRun(worker.runId);
4616
4621
  const status = computeWorkerStatus(worker, workerStatusOptions(run));
4617
4622
  writeJson(path17.join(worker.workerDir, "last-status.json"), status);
@@ -5046,6 +5051,7 @@ function spawnWorkerProcess(run, opts) {
5046
5051
  planId: opts.planId,
5047
5052
  taskId: opts.taskId,
5048
5053
  instructionPolicyMarkdown: opts.instructionPolicyMarkdown,
5054
+ memoryQualityMarkdown: opts.memoryQualityPromptMarkdown ?? opts.memoryQualityCapture?.promptMarkdown ?? null,
5049
5055
  personaMarkdown: opts.personaMarkdown,
5050
5056
  model: launchModel,
5051
5057
  repairTargetPrUrl: opts.repairTargetPrUrl,
@@ -5824,11 +5830,13 @@ function readHarnessWorkerContext(decision) {
5824
5830
  const personaEvidence = ctx.personaEvidence && typeof ctx.personaEvidence === "object" ? ctx.personaEvidence : null;
5825
5831
  const personaInjectionReady = ctx.personaInjectionReady === true;
5826
5832
  const memoryQualityCapture = ctx.memoryQualityCapture && typeof ctx.memoryQualityCapture === "object" ? ctx.memoryQualityCapture : null;
5833
+ const memoryQualityPromptMarkdown = typeof ctx.memoryQualityPromptMarkdown === "string" ? ctx.memoryQualityPromptMarkdown : typeof memoryQualityCapture?.promptMarkdown === "string" ? memoryQualityCapture.promptMarkdown : null;
5827
5834
  return {
5828
5835
  instructionPolicyMarkdown: markdown,
5829
5836
  instructionPolicyFingerprint: fingerprint,
5830
5837
  instructionPolicyEvidence: evidence,
5831
5838
  memoryQualityCapture,
5839
+ memoryQualityPromptMarkdown,
5832
5840
  personaMarkdown,
5833
5841
  personaSlug,
5834
5842
  personaEvidence,
@@ -6085,6 +6093,7 @@ async function dispatchRun(args) {
6085
6093
  instructionPolicyFingerprint: harnessContext?.instructionPolicyFingerprint ?? null,
6086
6094
  instructionPolicyEvidence: harnessContext?.instructionPolicyEvidence ?? null,
6087
6095
  memoryQualityCapture: harnessContext?.memoryQualityCapture ?? null,
6096
+ memoryQualityPromptMarkdown: harnessContext?.memoryQualityPromptMarkdown ?? null,
6088
6097
  personaMarkdown: harnessContext?.personaMarkdown ?? null,
6089
6098
  personaSlug: harnessContext?.personaSlug ?? expectedPersona,
6090
6099
  personaEvidence: harnessContext?.personaEvidence ?? null,
@@ -6268,15 +6277,15 @@ async function sweepRun(args) {
6268
6277
  }
6269
6278
 
6270
6279
  // src/worktree.ts
6271
- import { existsSync as existsSync23, mkdirSync as mkdirSync5 } from "node:fs";
6272
- import path32 from "node:path";
6280
+ import { existsSync as existsSync24, mkdirSync as mkdirSync5 } from "node:fs";
6281
+ import path33 from "node:path";
6273
6282
 
6274
6283
  // src/run-list.ts
6275
- import { existsSync as existsSync22, readFileSync as readFileSync10 } from "node:fs";
6276
- import path29 from "node:path";
6284
+ import { existsSync as existsSync23, readFileSync as readFileSync10 } from "node:fs";
6285
+ import path31 from "node:path";
6277
6286
 
6278
6287
  // src/stale-reconcile.ts
6279
- import path28 from "node:path";
6288
+ import path30 from "node:path";
6280
6289
 
6281
6290
  // src/finalize.ts
6282
6291
  import path25 from "node:path";
@@ -6337,8 +6346,8 @@ function finalizeStaleRuns() {
6337
6346
  }
6338
6347
 
6339
6348
  // src/worker-metadata-reconcile.ts
6340
- import { existsSync as existsSync21, lstatSync, readdirSync as readdirSync5, readlinkSync, renameSync as renameSync2, rmSync } from "node:fs";
6341
- import path27 from "node:path";
6349
+ import { existsSync as existsSync22, lstatSync, readdirSync as readdirSync6, readlinkSync, renameSync as renameSync2, rmSync } from "node:fs";
6350
+ import path29 from "node:path";
6342
6351
 
6343
6352
  // src/worker-metadata-paths.ts
6344
6353
  import path26 from "node:path";
@@ -6383,6 +6392,192 @@ function resolveWorkerJsonPath(input) {
6383
6392
  return canonical;
6384
6393
  }
6385
6394
 
6395
+ // src/run-metadata-retention.ts
6396
+ import { existsSync as existsSync21, readdirSync as readdirSync5, statSync as statSync4 } from "node:fs";
6397
+ import path28 from "node:path";
6398
+
6399
+ // src/default-repo.ts
6400
+ import path27 from "node:path";
6401
+ function expandConfiguredRepo(value) {
6402
+ return path27.resolve(resolveUserPath(value.trim()));
6403
+ }
6404
+ function fromConfigured(value, source, persistedInConfig) {
6405
+ const trimmed = value?.trim();
6406
+ if (!trimmed) return null;
6407
+ return {
6408
+ repo: expandConfiguredRepo(trimmed),
6409
+ source,
6410
+ persistedInConfig
6411
+ };
6412
+ }
6413
+ function resolveDefaultRepo(opts = {}) {
6414
+ const env = opts.env ?? process.env;
6415
+ const config = opts.config ?? loadUserConfig();
6416
+ const fromConfig = fromConfigured(config.defaultRepo, "config", true);
6417
+ if (fromConfig) return fromConfig;
6418
+ const fromDefaultEnv = fromConfigured(env.KYNVER_DEFAULT_REPO, "env_default_repo", false);
6419
+ if (fromDefaultEnv) return fromDefaultEnv;
6420
+ const fromHarnessEnv = fromConfigured(env.KYNVER_HARNESS_REPO, "env_harness_repo", false);
6421
+ if (fromHarnessEnv) return fromHarnessEnv;
6422
+ const discovered = discoverDefaultRepo({
6423
+ cwd: opts.cwd,
6424
+ runtimeModuleUrl: opts.runtimeModuleUrl
6425
+ });
6426
+ if (!discovered) return null;
6427
+ return {
6428
+ repo: discovered.repo,
6429
+ source: discovered.source,
6430
+ persistedInConfig: false
6431
+ };
6432
+ }
6433
+ function formatResolvedDefaultRepo(resolved) {
6434
+ return {
6435
+ defaultRepo: displayUserPath(resolved.repo),
6436
+ source: resolved.source,
6437
+ persistedInConfig: resolved.persistedInConfig
6438
+ };
6439
+ }
6440
+
6441
+ // src/run-metadata-retention.ts
6442
+ var RUN_METADATA_ACTIVE_SIGNAL_MS = 15 * 60 * 1e3;
6443
+ function isHarnessRunMetadataPath(targetPath, harnessRoot) {
6444
+ const resolved = path28.resolve(targetPath);
6445
+ const runsDir = path28.resolve(harnessRunsDir(harnessRoot));
6446
+ const rel = path28.relative(runsDir, resolved);
6447
+ return rel !== ".." && !rel.startsWith("..") && !path28.isAbsolute(rel);
6448
+ }
6449
+ function listRunDirIds(runsDir) {
6450
+ if (!existsSync21(runsDir)) return [];
6451
+ try {
6452
+ return readdirSync5(runsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name !== "runs").map((entry) => entry.name);
6453
+ } catch {
6454
+ return [];
6455
+ }
6456
+ }
6457
+ function listWorkerNamesOnDisk(runDir2) {
6458
+ const workersDir = path28.join(runDir2, "workers");
6459
+ if (!existsSync21(workersDir)) return [];
6460
+ try {
6461
+ return readdirSync5(workersDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
6462
+ } catch {
6463
+ return [];
6464
+ }
6465
+ }
6466
+ function pathRecentlyTouched(target, now, windowMs) {
6467
+ if (!existsSync21(target)) return false;
6468
+ try {
6469
+ const age = now - statSync4(target).mtimeMs;
6470
+ return Number.isFinite(age) && age >= 0 && age < windowMs;
6471
+ } catch {
6472
+ return false;
6473
+ }
6474
+ }
6475
+ function workerDirHasActiveRetentionSignals(workerDir, now = Date.now(), windowMs = RUN_METADATA_ACTIVE_SIGNAL_MS) {
6476
+ if (!existsSync21(workerDir)) return false;
6477
+ const artifacts = workerArtifactPaths(workerDir);
6478
+ const worker = readJson(
6479
+ artifacts.workerJsonPath,
6480
+ void 0
6481
+ );
6482
+ if (worker?.status === "running" && isPidAlive(worker.pid)) return true;
6483
+ const heartbeat = parseHeartbeat(artifacts.heartbeatPath);
6484
+ if (heartbeat.lastHeartbeatAt) {
6485
+ const age = now - Date.parse(heartbeat.lastHeartbeatAt);
6486
+ if (Number.isFinite(age) && age >= 0 && age < windowMs) return true;
6487
+ }
6488
+ if (pathRecentlyTouched(artifacts.stdoutPath, now, windowMs)) return true;
6489
+ if (pathRecentlyTouched(artifacts.heartbeatPath, now, windowMs)) return true;
6490
+ return false;
6491
+ }
6492
+ function runDirHasActiveRetentionSignals(runDir2, now = Date.now(), windowMs = RUN_METADATA_ACTIVE_SIGNAL_MS) {
6493
+ for (const name of listWorkerNamesOnDisk(runDir2)) {
6494
+ if (workerDirHasActiveRetentionSignals(path28.join(runDir2, "workers", name), now, windowMs)) {
6495
+ return true;
6496
+ }
6497
+ }
6498
+ return false;
6499
+ }
6500
+ function inferRepoFields() {
6501
+ const resolved = resolveDefaultRepo();
6502
+ return {
6503
+ repo: resolved?.repo ?? "",
6504
+ base: "origin/main",
6505
+ baseCommit: "unknown"
6506
+ };
6507
+ }
6508
+ function synthesizeRunFromDisk(harnessRoot, runId) {
6509
+ const runDir2 = path28.join(harnessRunsDir(harnessRoot), safeSlug(runId));
6510
+ if (!existsSync21(runDir2)) return null;
6511
+ const workerNames = listWorkerNamesOnDisk(runDir2);
6512
+ if (workerNames.length === 0) return null;
6513
+ let createdAt;
6514
+ try {
6515
+ createdAt = statSync4(runDir2).birthtime.toISOString();
6516
+ } catch {
6517
+ createdAt = (/* @__PURE__ */ new Date()).toISOString();
6518
+ }
6519
+ const workers = {};
6520
+ for (const name of workerNames) {
6521
+ const canonicalDir = canonicalWorkerDir(harnessRoot, runId, name);
6522
+ workers[name] = {
6523
+ workerDir: canonicalDir,
6524
+ statusPath: path28.join(canonicalDir, "worker.json")
6525
+ };
6526
+ }
6527
+ const hasActive = runDirHasActiveRetentionSignals(runDir2);
6528
+ return {
6529
+ id: runId,
6530
+ name: runId,
6531
+ ...inferRepoFields(),
6532
+ status: hasActive ? "running" : "needs_attention",
6533
+ createdAt,
6534
+ workers
6535
+ };
6536
+ }
6537
+ function repairMissingRunMetadata(harnessRootInput) {
6538
+ const harnessRoot = normalizeHarnessRoot(harnessRootInput ?? resolveHarnessRoot());
6539
+ const runsDir = harnessRunsDir(harnessRoot);
6540
+ const outcomes = [];
6541
+ for (const runId of listRunDirIds(runsDir)) {
6542
+ const runJsonPath = path28.join(runsDir, runId, "run.json");
6543
+ if (existsSync21(runJsonPath)) {
6544
+ outcomes.push({
6545
+ runId,
6546
+ action: "skipped",
6547
+ reason: "run.json present"
6548
+ });
6549
+ continue;
6550
+ }
6551
+ const synthesized = synthesizeRunFromDisk(harnessRoot, runId);
6552
+ if (!synthesized) {
6553
+ outcomes.push({
6554
+ runId,
6555
+ action: "skipped",
6556
+ reason: "no worker dirs on disk"
6557
+ });
6558
+ continue;
6559
+ }
6560
+ saveRun(synthesized);
6561
+ outcomes.push({
6562
+ runId,
6563
+ action: "repaired_run_json",
6564
+ reason: runDirHasActiveRetentionSignals(path28.join(runsDir, runId)) ? "synthesized run.json while worker artifacts still active" : "synthesized run.json from orphan worker metadata dirs"
6565
+ });
6566
+ }
6567
+ return { runs: outcomes };
6568
+ }
6569
+ function collectFilesystemLiveRunKeys(harnessRoot, now = Date.now()) {
6570
+ const keys = /* @__PURE__ */ new Set();
6571
+ const runsDir = harnessRunsDir(harnessRoot);
6572
+ for (const runId of listRunDirIds(runsDir)) {
6573
+ const runDir2 = path28.join(runsDir, runId);
6574
+ if (runDirHasActiveRetentionSignals(runDir2, now)) {
6575
+ keys.add(`${harnessRoot}\0${runId}`);
6576
+ }
6577
+ }
6578
+ return keys;
6579
+ }
6580
+
6386
6581
  // src/worker-metadata-reconcile.ts
6387
6582
  function materializeSymlinkedRunDir(harnessRoot, runId) {
6388
6583
  const canonical = canonicalRunDir(harnessRoot, runId);
@@ -6393,7 +6588,10 @@ function materializeSymlinkedRunDir(harnessRoot, runId) {
6393
6588
  return null;
6394
6589
  }
6395
6590
  if (!stat.isSymbolicLink()) return null;
6396
- const linkedTarget = path27.resolve(path27.dirname(canonical), readlinkSync(canonical));
6591
+ const linkedTarget = path29.resolve(path29.dirname(canonical), readlinkSync(canonical));
6592
+ if (runDirHasActiveRetentionSignals(linkedTarget) || runDirHasActiveRetentionSignals(canonical)) {
6593
+ return null;
6594
+ }
6397
6595
  const staging = `${canonical}.materialize-${Date.now()}`;
6398
6596
  renameSync2(linkedTarget, staging);
6399
6597
  rmSync(canonical);
@@ -6403,9 +6601,9 @@ function materializeSymlinkedRunDir(harnessRoot, runId) {
6403
6601
  function relocateArtifacts(input) {
6404
6602
  const moved = [];
6405
6603
  for (const fileName of workerArtifactFileNames()) {
6406
- const from = path27.join(input.fromDir, fileName);
6407
- const to = path27.join(input.toDir, fileName);
6408
- if (!existsSync21(from) || existsSync21(to)) continue;
6604
+ const from = path29.join(input.fromDir, fileName);
6605
+ const to = path29.join(input.toDir, fileName);
6606
+ if (!existsSync22(from) || existsSync22(to)) continue;
6409
6607
  renameSync2(from, to);
6410
6608
  moved.push(fileName);
6411
6609
  }
@@ -6444,8 +6642,8 @@ function deriveSynthesizedStatus(input) {
6444
6642
  function synthesizeWorkerFromArtifacts(input) {
6445
6643
  const artifacts = workerArtifactPaths(input.canonicalDir);
6446
6644
  const lastStatus = readJson(artifacts.lastStatusPath, void 0);
6447
- const stdoutExists = existsSync21(artifacts.stdoutPath);
6448
- const heartbeatExists = existsSync21(artifacts.heartbeatPath);
6645
+ const stdoutExists = existsSync22(artifacts.stdoutPath);
6646
+ const heartbeatExists = existsSync22(artifacts.heartbeatPath);
6449
6647
  const hasArtifacts = stdoutExists || heartbeatExists || lastStatus != null;
6450
6648
  if (!hasArtifacts) return null;
6451
6649
  const parsedStdout = stdoutExists ? parseHarnessStream(artifacts.stdoutPath) : { finalResult: null };
@@ -6461,7 +6659,7 @@ function synthesizeWorkerFromArtifacts(input) {
6461
6659
  runId: input.run.id,
6462
6660
  status,
6463
6661
  branch: typeof lastStatus?.branch === "string" ? lastStatus.branch : `agent/${input.run.id}/${input.workerName}`,
6464
- worktreePath: typeof lastStatus?.worktreePath === "string" ? lastStatus.worktreePath : path27.join(getPaths().worktreesDir, input.run.id, input.workerName),
6662
+ worktreePath: typeof lastStatus?.worktreePath === "string" ? lastStatus.worktreePath : path29.join(getPaths().worktreesDir, input.run.id, input.workerName),
6465
6663
  workerDir: input.canonicalDir,
6466
6664
  stdoutPath: artifacts.stdoutPath,
6467
6665
  stderrPath: artifacts.stderrPath,
@@ -6488,7 +6686,7 @@ function repairRunWorkerIndex(run, harnessRoot) {
6488
6686
  const nextWorkers = { ...run.workers || {} };
6489
6687
  for (const [name, ref] of Object.entries(nextWorkers)) {
6490
6688
  const canonicalDir = canonicalWorkerDir(harnessRoot, run.id, name);
6491
- const canonicalStatus = path27.join(canonicalDir, "worker.json");
6689
+ const canonicalStatus = path29.join(canonicalDir, "worker.json");
6492
6690
  const nested = hasNestedRunsSegment(ref.workerDir) || hasNestedRunsSegment(ref.statusPath) || ref.workerDir !== canonicalDir || ref.statusPath !== canonicalStatus;
6493
6691
  if (!nested) continue;
6494
6692
  nextWorkers[name] = { workerDir: canonicalDir, statusPath: canonicalStatus };
@@ -6521,7 +6719,7 @@ function reconcileOneWorker(input) {
6521
6719
  statusPath: input.statusPath
6522
6720
  });
6523
6721
  let worker = readJson(workerJsonPath, void 0);
6524
- if (!worker && existsSync21(canonicalArtifacts.workerJsonPath)) {
6722
+ if (!worker && existsSync22(canonicalArtifacts.workerJsonPath)) {
6525
6723
  worker = readJson(canonicalArtifacts.workerJsonPath, void 0);
6526
6724
  }
6527
6725
  if (!worker) {
@@ -6541,15 +6739,15 @@ function reconcileOneWorker(input) {
6541
6739
  });
6542
6740
  return outcomes;
6543
6741
  }
6544
- const dirExists = existsSync21(canonicalDir);
6545
- const hasAnyArtifact = workerArtifactFileNames().some((f) => existsSync21(path27.join(canonicalDir, f)));
6742
+ const dirExists = existsSync22(canonicalDir);
6743
+ const hasAnyArtifact = workerArtifactFileNames().some((f) => existsSync22(path29.join(canonicalDir, f)));
6546
6744
  if (dirExists && !hasAnyArtifact) {
6547
6745
  const abandoned = {
6548
6746
  name: input.workerName,
6549
6747
  runId: input.run.id,
6550
6748
  status: "exited",
6551
6749
  branch: `agent/${input.run.id}/${input.workerName}`,
6552
- worktreePath: path27.join(getPaths().worktreesDir, input.run.id, input.workerName),
6750
+ worktreePath: path29.join(getPaths().worktreesDir, input.run.id, input.workerName),
6553
6751
  workerDir: canonicalDir,
6554
6752
  stdoutPath: canonicalArtifacts.stdoutPath,
6555
6753
  stderrPath: canonicalArtifacts.stderrPath,
@@ -6596,11 +6794,11 @@ function reconcileOneWorker(input) {
6596
6794
  return outcomes;
6597
6795
  }
6598
6796
  function listOrphanWorkerNames(run, harnessRoot) {
6599
- const workersDir = path27.join(runDirectory(run.id), "workers");
6600
- if (!existsSync21(workersDir)) return [];
6797
+ const workersDir = path29.join(runDirectory(run.id), "workers");
6798
+ if (!existsSync22(workersDir)) return [];
6601
6799
  const indexed = new Set(Object.keys(run.workers || {}));
6602
6800
  const orphans = [];
6603
- for (const entry of readdirSync5(workersDir, { withFileTypes: true })) {
6801
+ for (const entry of readdirSync6(workersDir, { withFileTypes: true })) {
6604
6802
  if (!entry.isDirectory()) continue;
6605
6803
  if (indexed.has(entry.name)) continue;
6606
6804
  orphans.push(entry.name);
@@ -6609,6 +6807,7 @@ function listOrphanWorkerNames(run, harnessRoot) {
6609
6807
  }
6610
6808
  function reconcileWorkerMetadata() {
6611
6809
  const { harnessRoot } = getPaths();
6810
+ const runMetadataRetention = repairMissingRunMetadata(harnessRoot);
6612
6811
  const outcomes = [];
6613
6812
  for (const run of listRunRecords()) {
6614
6813
  const materializedFrom = materializeSymlinkedRunDir(harnessRoot, run.id);
@@ -6635,7 +6834,7 @@ function reconcileWorkerMetadata() {
6635
6834
  ...run.workers || {},
6636
6835
  [orphan]: {
6637
6836
  workerDir: canonicalWorkerDir(harnessRoot, run.id, orphan),
6638
- statusPath: path27.join(canonicalWorkerDir(harnessRoot, run.id, orphan), "worker.json")
6837
+ statusPath: path29.join(canonicalWorkerDir(harnessRoot, run.id, orphan), "worker.json")
6639
6838
  }
6640
6839
  };
6641
6840
  saveRun(run);
@@ -6658,7 +6857,7 @@ function reconcileWorkerMetadata() {
6658
6857
  );
6659
6858
  }
6660
6859
  }
6661
- return { workers: outcomes };
6860
+ return { workers: outcomes, runMetadataRetention };
6662
6861
  }
6663
6862
 
6664
6863
  // src/stale-reconcile.ts
@@ -6675,7 +6874,7 @@ function reconcileStaleWorkers() {
6675
6874
  const now = Date.now();
6676
6875
  for (const run of listRunRecords()) {
6677
6876
  for (const name of Object.keys(run.workers || {})) {
6678
- const workerPath = path28.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
6877
+ const workerPath = path30.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
6679
6878
  const worker = readJson(workerPath, void 0);
6680
6879
  if (!worker || worker.status !== "running") {
6681
6880
  outcomes.push({
@@ -6758,16 +6957,28 @@ function reconcileRunsCli() {
6758
6957
  acc[row.action] = (acc[row.action] ?? 0) + 1;
6759
6958
  return acc;
6760
6959
  }, {});
6960
+ const runRetentionTotals = result.metadataReconcile.runMetadataRetention.runs.reduce((acc, row) => {
6961
+ acc[row.action] = (acc[row.action] ?? 0) + 1;
6962
+ return acc;
6963
+ }, {});
6761
6964
  console.log(
6762
6965
  JSON.stringify(
6763
6966
  {
6764
6967
  ok: true,
6765
6968
  workers: { markedExited, killedStale, skipped, total: result.workers.length },
6766
- metadataReconcile: { totals: metadataTotals, total: result.metadataReconcile.workers.length },
6969
+ metadataReconcile: {
6970
+ totals: metadataTotals,
6971
+ total: result.metadataReconcile.workers.length,
6972
+ runMetadataRetention: {
6973
+ totals: runRetentionTotals,
6974
+ total: result.metadataReconcile.runMetadataRetention.runs.length
6975
+ }
6976
+ },
6767
6977
  finalizedRuns: result.finalizedRuns.length,
6768
6978
  details: {
6769
6979
  workers: result.workers,
6770
6980
  metadataReconcile: result.metadataReconcile.workers,
6981
+ runMetadataRetention: result.metadataReconcile.runMetadataRetention.runs,
6771
6982
  finalizedRuns: result.finalizedRuns
6772
6983
  }
6773
6984
  },
@@ -6779,7 +6990,7 @@ function reconcileRunsCli() {
6779
6990
 
6780
6991
  // src/run-list.ts
6781
6992
  function heartbeatByteLength(heartbeatPath) {
6782
- if (!heartbeatPath || !existsSync22(heartbeatPath)) return 0;
6993
+ if (!heartbeatPath || !existsSync23(heartbeatPath)) return 0;
6783
6994
  try {
6784
6995
  return readFileSync10(heartbeatPath, "utf8").trim().length;
6785
6996
  } catch {
@@ -6787,7 +6998,7 @@ function heartbeatByteLength(heartbeatPath) {
6787
6998
  }
6788
6999
  }
6789
7000
  function workerEvidence(run, workerName) {
6790
- const workerPath = path29.join(runDirectory(run.id), "workers", safeSlug(workerName), "worker.json");
7001
+ const workerPath = path31.join(runDirectory(run.id), "workers", safeSlug(workerName), "worker.json");
6791
7002
  const worker = readJson(workerPath, void 0);
6792
7003
  if (!worker) {
6793
7004
  return {
@@ -6844,7 +7055,7 @@ function aggregateRunAttention(workers) {
6844
7055
  function countOpenWorkers(run) {
6845
7056
  let open = 0;
6846
7057
  for (const name of Object.keys(run.workers || {})) {
6847
- const workerPath = path29.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
7058
+ const workerPath = path31.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
6848
7059
  const worker = readJson(workerPath, void 0);
6849
7060
  if (!worker) continue;
6850
7061
  const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
@@ -6892,50 +7103,8 @@ function listRunsCli() {
6892
7103
  console.log(JSON.stringify(buildRunListRows(), null, 2));
6893
7104
  }
6894
7105
 
6895
- // src/default-repo.ts
6896
- import path30 from "node:path";
6897
- function expandConfiguredRepo(value) {
6898
- return path30.resolve(resolveUserPath(value.trim()));
6899
- }
6900
- function fromConfigured(value, source, persistedInConfig) {
6901
- const trimmed = value?.trim();
6902
- if (!trimmed) return null;
6903
- return {
6904
- repo: expandConfiguredRepo(trimmed),
6905
- source,
6906
- persistedInConfig
6907
- };
6908
- }
6909
- function resolveDefaultRepo(opts = {}) {
6910
- const env = opts.env ?? process.env;
6911
- const config = opts.config ?? loadUserConfig();
6912
- const fromConfig = fromConfigured(config.defaultRepo, "config", true);
6913
- if (fromConfig) return fromConfig;
6914
- const fromDefaultEnv = fromConfigured(env.KYNVER_DEFAULT_REPO, "env_default_repo", false);
6915
- if (fromDefaultEnv) return fromDefaultEnv;
6916
- const fromHarnessEnv = fromConfigured(env.KYNVER_HARNESS_REPO, "env_harness_repo", false);
6917
- if (fromHarnessEnv) return fromHarnessEnv;
6918
- const discovered = discoverDefaultRepo({
6919
- cwd: opts.cwd,
6920
- runtimeModuleUrl: opts.runtimeModuleUrl
6921
- });
6922
- if (!discovered) return null;
6923
- return {
6924
- repo: discovered.repo,
6925
- source: discovered.source,
6926
- persistedInConfig: false
6927
- };
6928
- }
6929
- function formatResolvedDefaultRepo(resolved) {
6930
- return {
6931
- defaultRepo: displayUserPath(resolved.repo),
6932
- source: resolved.source,
6933
- persistedInConfig: resolved.persistedInConfig
6934
- };
6935
- }
6936
-
6937
7106
  // src/validate.ts
6938
- import path31 from "node:path";
7107
+ import path32 from "node:path";
6939
7108
  var RUN_ID_RE = /^[a-z0-9][a-z0-9._-]{0,127}$/i;
6940
7109
  function validateRunId(runId) {
6941
7110
  const trimmed = runId.trim();
@@ -6943,7 +7112,7 @@ function validateRunId(runId) {
6943
7112
  return trimmed;
6944
7113
  }
6945
7114
  function validateRepo(repo) {
6946
- const resolved = path31.resolve(repo);
7115
+ const resolved = path32.resolve(repo);
6947
7116
  if (resolved.includes("..")) throw new Error("repo path must not contain .. segments");
6948
7117
  return resolved;
6949
7118
  }
@@ -6962,7 +7131,7 @@ function createRun(args) {
6962
7131
  ensureGitRepo(repo);
6963
7132
  const id = args.id ? validateRunId(String(args.id)) : timestampSlug(String(args.name || "run"));
6964
7133
  const dir = runDirectory(id);
6965
- if (existsSync23(dir)) failExists(`run already exists: ${id}`);
7134
+ if (existsSync24(dir)) failExists(`run already exists: ${id}`);
6966
7135
  mkdirSync5(dir, { recursive: true });
6967
7136
  const base = String(args.base || "origin/main");
6968
7137
  const baseCommit = git(repo, ["rev-parse", base]).trim();
@@ -6976,7 +7145,7 @@ function createRun(args) {
6976
7145
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
6977
7146
  workers: {}
6978
7147
  };
6979
- writeJson(path32.join(dir, "run.json"), run);
7148
+ writeJson(path33.join(dir, "run.json"), run);
6980
7149
  console.log(JSON.stringify({ runId: id, runDir: dir, repo, base, baseCommit }, null, 2));
6981
7150
  }
6982
7151
  function listRuns() {
@@ -6988,8 +7157,8 @@ function failExists(message) {
6988
7157
  }
6989
7158
 
6990
7159
  // src/discard-disposable.ts
6991
- import { existsSync as existsSync24, rmSync as rmSync2 } from "node:fs";
6992
- import path33 from "node:path";
7160
+ import { existsSync as existsSync25, rmSync as rmSync2 } from "node:fs";
7161
+ import path34 from "node:path";
6993
7162
  function normalizeRelativePath2(value) {
6994
7163
  const normalized = value.replace(/\\/g, "/").replace(/^\.\//, "").trim();
6995
7164
  if (!normalized || normalized.startsWith("/") || normalized.includes("..")) {
@@ -7010,15 +7179,15 @@ function discardDisposableArtifacts(args) {
7010
7179
  if (paths.length === 0) {
7011
7180
  return { ok: false, removed: [], reason: "requires at least one --path" };
7012
7181
  }
7013
- const worktreeRoot = path33.resolve(worker.worktreePath);
7182
+ const worktreeRoot = path34.resolve(worker.worktreePath);
7014
7183
  const removed = [];
7015
7184
  for (const raw of paths) {
7016
7185
  const rel = normalizeRelativePath2(raw);
7017
- const abs = path33.resolve(worktreeRoot, rel);
7018
- if (!abs.startsWith(worktreeRoot + path33.sep) && abs !== worktreeRoot) {
7186
+ const abs = path34.resolve(worktreeRoot, rel);
7187
+ if (!abs.startsWith(worktreeRoot + path34.sep) && abs !== worktreeRoot) {
7019
7188
  return { ok: false, removed, reason: `path escapes worktree: ${raw}` };
7020
7189
  }
7021
- if (!existsSync24(abs)) {
7190
+ if (!existsSync25(abs)) {
7022
7191
  return { ok: false, removed, reason: `path not found: ${raw}` };
7023
7192
  }
7024
7193
  rmSync2(abs, { recursive: true, force: true });
@@ -7040,8 +7209,472 @@ function discardDisposableCli(args) {
7040
7209
  if (!result.ok) process.exit(1);
7041
7210
  }
7042
7211
 
7212
+ // src/cron/cron-env.ts
7213
+ import { existsSync as existsSync26 } from "node:fs";
7214
+ import { homedir as homedir11 } from "node:os";
7215
+ import path35 from "node:path";
7216
+ function envFlag(name, defaultValue) {
7217
+ const raw = process.env[name]?.trim().toLowerCase();
7218
+ if (!raw) return defaultValue;
7219
+ if (raw === "0" || raw === "false" || raw === "no" || raw === "off") return false;
7220
+ if (raw === "1" || raw === "true" || raw === "yes" || raw === "on") return true;
7221
+ return defaultValue;
7222
+ }
7223
+ function envInt(name, fallback, min = 1) {
7224
+ const n = Number(process.env[name]);
7225
+ if (!Number.isFinite(n) || n < min) return fallback;
7226
+ return Math.floor(n);
7227
+ }
7228
+ function defaultKynverCronStorePath() {
7229
+ const explicit = process.env.KYNVER_CRON_STORE_PATH?.trim() || process.env.OPENCLAW_CRON_STORE_PATH?.trim();
7230
+ if (explicit) return explicit;
7231
+ return path35.join(homedir11(), ".kynver", "agent-os-cron.json");
7232
+ }
7233
+ function defaultKynverCronStatePath(storePath = defaultKynverCronStorePath()) {
7234
+ const explicit = process.env.KYNVER_CRON_TICK_STATE_PATH?.trim();
7235
+ if (explicit) return explicit;
7236
+ return `${storePath.replace(/\.json$/i, "")}.tick-state.json`;
7237
+ }
7238
+ function resolveKynverCronFireBaseUrl() {
7239
+ const config = loadUserConfig();
7240
+ return process.env.KYNVER_API_URL?.trim() || config.apiBaseUrl?.trim() || process.env.KYNVER_CRON_FIRE_BASE_URL?.trim() || process.env.OPENCLAW_CRON_FIRE_BASE_URL?.trim() || null;
7241
+ }
7242
+ function resolveKynverCronSecret() {
7243
+ return process.env.KYNVER_CRON_SECRET?.trim() || process.env.OPENCLAW_CRON_SECRET?.trim() || process.env.KYNVER_RUNTIME_SECRET?.trim() || null;
7244
+ }
7245
+ function resolveKynverCronEnv() {
7246
+ const storePath = defaultKynverCronStorePath();
7247
+ const statePath = defaultKynverCronStatePath(storePath);
7248
+ const fireBaseUrl = resolveKynverCronFireBaseUrl();
7249
+ const secret = resolveKynverCronSecret();
7250
+ const credsReady = Boolean(fireBaseUrl && secret);
7251
+ const storeExists = existsSync26(storePath);
7252
+ const defaultEnabled = credsReady && (storeExists || envFlag("KYNVER_CRON_TICK_FORCE", false));
7253
+ return {
7254
+ storePath,
7255
+ statePath,
7256
+ lockPath: `${statePath}.lock`,
7257
+ fireBaseUrl,
7258
+ secret,
7259
+ tickEnabled: envFlag("KYNVER_CRON_TICK_ENABLED", defaultEnabled),
7260
+ tickIntervalMs: envInt("KYNVER_CRON_TICK_INTERVAL_MS", 6e4, 5e3),
7261
+ missedRunPolicy: process.env.KYNVER_CRON_MISSED_RUN_POLICY?.trim().toLowerCase() === "skip" ? "skip" : "catch_up",
7262
+ maxCatchUpPerTick: envInt("KYNVER_CRON_MAX_CATCH_UP_PER_TICK", 3, 0),
7263
+ maxRetries: envInt("KYNVER_CRON_MAX_RETRIES", 3, 0),
7264
+ retryBackoffMs: envInt("KYNVER_CRON_RETRY_BACKOFF_MS", 6e4, 1e3),
7265
+ inflightLeaseMs: envInt("KYNVER_CRON_INFLIGHT_LEASE_MS", 12e4, 5e3)
7266
+ };
7267
+ }
7268
+ function isKynverCronDaemonPrimary(env = resolveKynverCronEnv()) {
7269
+ return env.tickEnabled && Boolean(env.fireBaseUrl && env.secret);
7270
+ }
7271
+
7272
+ // src/cron/cron-fire.ts
7273
+ function trimTrailingSlash2(url) {
7274
+ return url.replace(/\/+$/, "");
7275
+ }
7276
+ async function fireKynverCronJob(input) {
7277
+ const doFetch = input.fetchFn ?? fetch;
7278
+ const callbackPath = input.entry.spec.callbackPath.startsWith("/") ? input.entry.spec.callbackPath : `/${input.entry.spec.callbackPath}`;
7279
+ const url = `${trimTrailingSlash2(input.baseUrl)}${callbackPath}`;
7280
+ const jobId = input.jobId ?? input.entry.spec.dedupeKey ?? null;
7281
+ const body = {
7282
+ source: "kynver-cron",
7283
+ jobId,
7284
+ agentOsId: input.entry.spec.target.agentOsId,
7285
+ kind: input.entry.spec.kind,
7286
+ target: input.entry.spec.target,
7287
+ ...input.entry.spec.payload !== void 0 && { payload: input.entry.spec.payload }
7288
+ };
7289
+ const res = await doFetch(url, {
7290
+ method: "POST",
7291
+ headers: buildHarnessCallbackHeaders(input.secret),
7292
+ body: JSON.stringify(body)
7293
+ });
7294
+ let parsed = null;
7295
+ try {
7296
+ parsed = await res.json();
7297
+ } catch {
7298
+ parsed = null;
7299
+ }
7300
+ return { ok: res.ok, status: res.status, body: parsed };
7301
+ }
7302
+
7303
+ // src/cron/cron-lock.ts
7304
+ import { closeSync as closeSync6, existsSync as existsSync27, openSync as openSync6, readFileSync as readFileSync11, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
7305
+ var STALE_LOCK_MS = 10 * 6e4;
7306
+ function readLockInfo(lockPath) {
7307
+ if (!existsSync27(lockPath)) return null;
7308
+ try {
7309
+ const parsed = JSON.parse(readFileSync11(lockPath, "utf8"));
7310
+ if (typeof parsed.pid === "number" && typeof parsed.at === "string") return parsed;
7311
+ } catch {
7312
+ return null;
7313
+ }
7314
+ return null;
7315
+ }
7316
+ function lockIsStale(lockPath) {
7317
+ const info = readLockInfo(lockPath);
7318
+ if (!info) return true;
7319
+ if (!isPidAlive(info.pid)) return true;
7320
+ const atMs = Date.parse(info.at);
7321
+ if (Number.isNaN(atMs)) return true;
7322
+ return Date.now() - atMs > STALE_LOCK_MS;
7323
+ }
7324
+ function tryAcquireCronTickLock(lockPath) {
7325
+ if (existsSync27(lockPath) && !lockIsStale(lockPath)) {
7326
+ const info = readLockInfo(lockPath);
7327
+ return {
7328
+ acquired: false,
7329
+ reason: info ? `held by pid ${info.pid}` : "held by another process"
7330
+ };
7331
+ }
7332
+ if (existsSync27(lockPath)) {
7333
+ try {
7334
+ unlinkSync2(lockPath);
7335
+ } catch {
7336
+ }
7337
+ }
7338
+ try {
7339
+ const fd = openSync6(lockPath, "wx");
7340
+ writeFileSync4(
7341
+ fd,
7342
+ JSON.stringify({ pid: process.pid, at: (/* @__PURE__ */ new Date()).toISOString() }),
7343
+ "utf8"
7344
+ );
7345
+ closeSync6(fd);
7346
+ return { acquired: true };
7347
+ } catch (err) {
7348
+ if (err.code === "EEXIST") {
7349
+ return { acquired: false, reason: "concurrent acquire" };
7350
+ }
7351
+ throw err;
7352
+ }
7353
+ }
7354
+ function releaseCronTickLock(lockPath) {
7355
+ try {
7356
+ unlinkSync2(lockPath);
7357
+ } catch {
7358
+ }
7359
+ }
7360
+
7361
+ // src/cron/cron-match.ts
7362
+ var CRON_RE = /^[\d*/,\-?LW#]+\s+[\d*/,\-?LW#]+\s+[\d*/,\-?LW#]+\s+[\d*/,\-?LW#]+\s+[\d*/,\-?LW#]+$/;
7363
+ function isCronExpression(value) {
7364
+ return CRON_RE.test(value.trim());
7365
+ }
7366
+ function parseList(field, min, max) {
7367
+ const out = /* @__PURE__ */ new Set();
7368
+ for (const part of field.split(",")) {
7369
+ const token = part.trim();
7370
+ if (!token) continue;
7371
+ if (token === "*") {
7372
+ for (let i = min; i <= max; i++) out.add(i);
7373
+ continue;
7374
+ }
7375
+ const stepMatch = /^(.+)\/(\d+)$/.exec(token);
7376
+ const base = stepMatch ? stepMatch[1] : token;
7377
+ const step = stepMatch ? Math.max(1, Number(stepMatch[2])) : 1;
7378
+ if (base === "*") {
7379
+ for (let i = min; i <= max; i += step) out.add(i);
7380
+ continue;
7381
+ }
7382
+ const rangeMatch = /^(\d+)-(\d+)$/.exec(base);
7383
+ if (rangeMatch) {
7384
+ const start = Math.max(min, Number(rangeMatch[1]));
7385
+ const end = Math.min(max, Number(rangeMatch[2]));
7386
+ for (let i = start; i <= end; i += step) out.add(i);
7387
+ continue;
7388
+ }
7389
+ const n = Number(base);
7390
+ if (Number.isInteger(n) && n >= min && n <= max) out.add(n);
7391
+ }
7392
+ return out;
7393
+ }
7394
+ function fieldMatches(field, value, min, max) {
7395
+ const trimmed = field.trim();
7396
+ if (trimmed === "*") return true;
7397
+ return parseList(trimmed, min, max).has(value);
7398
+ }
7399
+ function cronMatchesUtc(expr, at) {
7400
+ const parts = expr.trim().split(/\s+/);
7401
+ if (parts.length !== 5) return false;
7402
+ const [minF, hourF, domF, monF, dowF] = parts;
7403
+ return fieldMatches(minF, at.getUTCMinutes(), 0, 59) && fieldMatches(hourF, at.getUTCHours(), 0, 23) && fieldMatches(domF, at.getUTCDate(), 1, 31) && fieldMatches(monF, at.getUTCMonth() + 1, 1, 12) && fieldMatches(dowF, at.getUTCDay(), 0, 6);
7404
+ }
7405
+ var MAX_LOOKAHEAD_MINUTES = 366 * 24 * 60;
7406
+ function truncateToUtcMinute(at) {
7407
+ return new Date(
7408
+ Date.UTC(
7409
+ at.getUTCFullYear(),
7410
+ at.getUTCMonth(),
7411
+ at.getUTCDate(),
7412
+ at.getUTCHours(),
7413
+ at.getUTCMinutes(),
7414
+ 0,
7415
+ 0
7416
+ )
7417
+ );
7418
+ }
7419
+ function computeNextCronFireUtc(expr, after) {
7420
+ if (!isCronExpression(expr)) return null;
7421
+ let cursor = truncateToUtcMinute(after);
7422
+ cursor = new Date(cursor.getTime() + 6e4);
7423
+ for (let i = 0; i < MAX_LOOKAHEAD_MINUTES; i++) {
7424
+ if (cronMatchesUtc(expr, cursor)) return cursor;
7425
+ cursor = new Date(cursor.getTime() + 6e4);
7426
+ }
7427
+ return null;
7428
+ }
7429
+ function computeInitialNextFire(spec, now) {
7430
+ if (spec.scheduleKind === "runAt" && spec.runAt) {
7431
+ const ms = Date.parse(spec.runAt);
7432
+ return Number.isNaN(ms) ? null : new Date(ms).toISOString();
7433
+ }
7434
+ if (spec.scheduleKind === "cron" && spec.cron) {
7435
+ const next = computeNextCronFireUtc(spec.cron.trim(), now);
7436
+ return next ? next.toISOString() : null;
7437
+ }
7438
+ return null;
7439
+ }
7440
+ function advanceRecurringNextFire(spec, fromInclusive) {
7441
+ if (spec.scheduleKind !== "cron" || !spec.cron?.trim()) return null;
7442
+ const next = computeNextCronFireUtc(spec.cron.trim(), fromInclusive);
7443
+ return next ? next.toISOString() : null;
7444
+ }
7445
+
7446
+ // src/cron/cron-store.ts
7447
+ import { promises as fs } from "node:fs";
7448
+ async function readFileIfExists(filePath) {
7449
+ try {
7450
+ return await fs.readFile(filePath, "utf8");
7451
+ } catch (err) {
7452
+ if (err.code === "ENOENT") return null;
7453
+ throw err;
7454
+ }
7455
+ }
7456
+ function parseCronStore(raw) {
7457
+ if (!raw) return [];
7458
+ try {
7459
+ const parsed = JSON.parse(raw);
7460
+ return Array.isArray(parsed.entries) ? parsed.entries : [];
7461
+ } catch {
7462
+ return [];
7463
+ }
7464
+ }
7465
+ async function loadCronJobs(storePath = defaultKynverCronStorePath()) {
7466
+ const raw = await readFileIfExists(storePath);
7467
+ return parseCronStore(raw);
7468
+ }
7469
+
7470
+ // src/cron/cron-tick-state.ts
7471
+ import { randomBytes } from "node:crypto";
7472
+ import { promises as fs2 } from "node:fs";
7473
+ import path36 from "node:path";
7474
+ var EMPTY = { version: 1, jobs: {} };
7475
+ async function readFileIfExists2(filePath) {
7476
+ try {
7477
+ return await fs2.readFile(filePath, "utf8");
7478
+ } catch (err) {
7479
+ if (err.code === "ENOENT") return null;
7480
+ throw err;
7481
+ }
7482
+ }
7483
+ function parseCronTickState(raw) {
7484
+ if (!raw) return { ...EMPTY, jobs: { ...EMPTY.jobs } };
7485
+ try {
7486
+ const parsed = JSON.parse(raw);
7487
+ if (parsed?.version !== 1 || typeof parsed.jobs !== "object" || !parsed.jobs) {
7488
+ return { ...EMPTY, jobs: {} };
7489
+ }
7490
+ return parsed;
7491
+ } catch {
7492
+ return { ...EMPTY, jobs: {} };
7493
+ }
7494
+ }
7495
+ async function loadCronTickState(statePath) {
7496
+ const raw = await readFileIfExists2(statePath);
7497
+ return parseCronTickState(raw);
7498
+ }
7499
+ async function writeStateAtomic(statePath, state) {
7500
+ await fs2.mkdir(path36.dirname(statePath), { recursive: true });
7501
+ const suffix = randomBytes(6).toString("hex");
7502
+ const tmp = `${statePath}.tmp-${process.pid}-${Date.now()}-${suffix}`;
7503
+ await fs2.writeFile(tmp, `${JSON.stringify(state, null, 2)}
7504
+ `, "utf8");
7505
+ try {
7506
+ await fs2.rename(tmp, statePath);
7507
+ } catch (err) {
7508
+ const code = err.code;
7509
+ if (code !== "EPERM" && code !== "EEXIST" && code !== "EACCES") throw err;
7510
+ await fs2.unlink(tmp).catch(() => {
7511
+ });
7512
+ }
7513
+ }
7514
+ async function saveCronTickState(statePath, state) {
7515
+ await writeStateAtomic(statePath, state);
7516
+ }
7517
+ function getOrCreateJobState(state, providerScheduleId) {
7518
+ const existing = state.jobs[providerScheduleId];
7519
+ if (existing) return existing;
7520
+ const created = {
7521
+ providerScheduleId,
7522
+ nextFireAt: null,
7523
+ lastFiredAt: null,
7524
+ lastAttemptAt: null,
7525
+ consecutiveFailures: 0,
7526
+ completedAt: null,
7527
+ inflightUntil: null
7528
+ };
7529
+ state.jobs[providerScheduleId] = created;
7530
+ return created;
7531
+ }
7532
+
7533
+ // src/cron/cron-tick.ts
7534
+ function isInflight(job, nowMs) {
7535
+ if (!job.inflightUntil) return false;
7536
+ const until = Date.parse(job.inflightUntil);
7537
+ return !Number.isNaN(until) && until > nowMs;
7538
+ }
7539
+ function isCompleted(job) {
7540
+ return Boolean(job.completedAt);
7541
+ }
7542
+ function backoffReady(job, env, nowMs) {
7543
+ if (job.consecutiveFailures === 0) return true;
7544
+ if (job.consecutiveFailures > env.maxRetries) return false;
7545
+ if (!job.lastAttemptAt) return true;
7546
+ const last = Date.parse(job.lastAttemptAt);
7547
+ if (Number.isNaN(last)) return true;
7548
+ return nowMs - last >= env.retryBackoffMs;
7549
+ }
7550
+ function ensureNextFire(entry, job, now) {
7551
+ if (job.nextFireAt) return job.nextFireAt;
7552
+ const initial = computeInitialNextFire(entry.spec, now);
7553
+ job.nextFireAt = initial;
7554
+ return initial;
7555
+ }
7556
+ function isDue(entry, job, nowMs, env) {
7557
+ if (entry.paused || isCompleted(job) || isInflight(job, nowMs)) return false;
7558
+ if (!backoffReady(job, env, nowMs)) return false;
7559
+ const nextMs = job.nextFireAt ? Date.parse(job.nextFireAt) : NaN;
7560
+ if (Number.isNaN(nextMs)) return false;
7561
+ return nowMs >= nextMs;
7562
+ }
7563
+ function advanceRecurringBeforeFire(entry, job, now) {
7564
+ if (entry.spec.scheduleKind !== "cron") return;
7565
+ job.nextFireAt = advanceRecurringNextFire(entry.spec, now);
7566
+ }
7567
+ async function runKynverCronTick(opts = {}) {
7568
+ const env = opts.env ?? resolveKynverCronEnv();
7569
+ const now = opts.now ?? /* @__PURE__ */ new Date();
7570
+ const nowMs = now.getTime();
7571
+ if (!env.tickEnabled) {
7572
+ return { enabled: false, skipped: "tick_disabled", scanned: 0, due: 0, fired: 0, skippedJobs: 0, errors: 0 };
7573
+ }
7574
+ if (!env.fireBaseUrl || !env.secret) {
7575
+ return {
7576
+ enabled: true,
7577
+ skipped: "missing_fire_credentials",
7578
+ scanned: 0,
7579
+ due: 0,
7580
+ fired: 0,
7581
+ skippedJobs: 0,
7582
+ errors: 0
7583
+ };
7584
+ }
7585
+ const lock = tryAcquireCronTickLock(env.lockPath);
7586
+ if (!lock.acquired) {
7587
+ return {
7588
+ enabled: true,
7589
+ skipped: lock.reason ?? "lock_not_acquired",
7590
+ scanned: 0,
7591
+ due: 0,
7592
+ fired: 0,
7593
+ skippedJobs: 0,
7594
+ errors: 0,
7595
+ lockHeld: true
7596
+ };
7597
+ }
7598
+ try {
7599
+ const entries = await loadCronJobs(env.storePath);
7600
+ const filtered = opts.agentOsIdFilter ? entries.filter((e) => e.spec.target.agentOsId === opts.agentOsIdFilter) : entries;
7601
+ const state = await loadCronTickState(env.statePath);
7602
+ const dueEntries = [];
7603
+ for (const entry of filtered) {
7604
+ const job = getOrCreateJobState(state, entry.providerScheduleId);
7605
+ ensureNextFire(entry, job, now);
7606
+ if (isDue(entry, job, nowMs, env)) {
7607
+ dueEntries.push({ entry, job });
7608
+ }
7609
+ }
7610
+ dueEntries.sort((a, b) => {
7611
+ const aMs = Date.parse(a.job.nextFireAt ?? "") || 0;
7612
+ const bMs = Date.parse(b.job.nextFireAt ?? "") || 0;
7613
+ return aMs - bMs;
7614
+ });
7615
+ let fired = 0;
7616
+ let errors = 0;
7617
+ let skippedJobs = 0;
7618
+ let catchUpBudget = env.maxCatchUpPerTick;
7619
+ for (const { entry, job } of dueEntries) {
7620
+ if (env.missedRunPolicy === "skip" && entry.spec.scheduleKind === "cron") {
7621
+ const next = Date.parse(job.nextFireAt ?? "");
7622
+ if (!Number.isNaN(next) && next < nowMs - env.tickIntervalMs * 2) {
7623
+ job.nextFireAt = advanceRecurringNextFire(entry.spec, now);
7624
+ skippedJobs++;
7625
+ continue;
7626
+ }
7627
+ }
7628
+ if (catchUpBudget <= 0) {
7629
+ skippedJobs++;
7630
+ continue;
7631
+ }
7632
+ job.inflightUntil = new Date(nowMs + env.inflightLeaseMs).toISOString();
7633
+ job.lastAttemptAt = now.toISOString();
7634
+ advanceRecurringBeforeFire(entry, job, now);
7635
+ try {
7636
+ const result = await fireKynverCronJob({
7637
+ entry,
7638
+ baseUrl: env.fireBaseUrl,
7639
+ secret: env.secret,
7640
+ fetchFn: opts.fetchFn
7641
+ });
7642
+ job.inflightUntil = null;
7643
+ if (result.ok) {
7644
+ job.lastFiredAt = now.toISOString();
7645
+ job.consecutiveFailures = 0;
7646
+ if (entry.spec.scheduleKind === "runAt") {
7647
+ job.completedAt = now.toISOString();
7648
+ job.nextFireAt = null;
7649
+ }
7650
+ fired++;
7651
+ catchUpBudget--;
7652
+ } else {
7653
+ job.consecutiveFailures += 1;
7654
+ errors++;
7655
+ }
7656
+ } catch {
7657
+ job.inflightUntil = null;
7658
+ job.consecutiveFailures += 1;
7659
+ errors++;
7660
+ }
7661
+ }
7662
+ await saveCronTickState(env.statePath, state);
7663
+ return {
7664
+ enabled: true,
7665
+ scanned: filtered.length,
7666
+ due: dueEntries.length,
7667
+ fired,
7668
+ skippedJobs,
7669
+ errors
7670
+ };
7671
+ } finally {
7672
+ releaseCronTickLock(env.lockPath);
7673
+ }
7674
+ }
7675
+
7043
7676
  // src/pipeline-tick.ts
7044
- import path49 from "node:path";
7677
+ import path52 from "node:path";
7045
7678
 
7046
7679
  // src/pipeline-dispatch.ts
7047
7680
  var RESERVED_REVIEW_STARTS = 1;
@@ -7131,24 +7764,43 @@ function operatorDispatchFromTick(operatorTick) {
7131
7764
  const dispatch = body.response?.dispatch;
7132
7765
  return dispatch && typeof dispatch === "object" ? dispatch : null;
7133
7766
  }
7767
+ function nonNegativeInt(value) {
7768
+ if (typeof value !== "number" || !Number.isFinite(value)) return null;
7769
+ return Math.max(0, Math.floor(value));
7770
+ }
7134
7771
  function resolvePipelineMaxStarts(resourceGate, operatorTick) {
7135
7772
  const dispatch = operatorDispatchFromTick(operatorTick);
7136
- const advised = typeof dispatch?.recommendedMaxStarts === "number" ? Math.max(0, dispatch.recommendedMaxStarts) : null;
7773
+ const advised = nonNegativeInt(dispatch?.recommendedMaxStarts);
7774
+ const actionableReady = nonNegativeInt(dispatch?.actionableReady);
7775
+ const queuedTasks = nonNegativeInt(dispatch?.queuedTasks);
7776
+ const boardAdvancedThisTick = nonNegativeInt(dispatch?.boardAdvancedThisTick) ?? 0;
7777
+ const leaseReapedThisTick = nonNegativeInt(dispatch?.leaseReapedThisTick) ?? 0;
7778
+ const hygieneAdvanced = boardAdvancedThisTick + leaseReapedThisTick;
7779
+ const readyFloor = actionableReady ?? queuedTasks;
7137
7780
  let maxStarts = resourceGate.slotsAvailable;
7138
- if (advised !== null) {
7781
+ if (readyFloor !== null) {
7782
+ maxStarts = Math.min(maxStarts, readyFloor);
7783
+ } else if (advised !== null) {
7139
7784
  maxStarts = Math.min(maxStarts, advised);
7140
7785
  }
7141
- const underutilized = dispatch?.underutilized === true;
7142
- const boardAdvancedThisTick = typeof dispatch?.boardAdvancedThisTick === "number" ? dispatch.boardAdvancedThisTick : 0;
7143
- if (underutilized && resourceGate.slotsAvailable > 0 && maxStarts === 0) {
7144
- const ready = dispatch?.actionableReady ?? dispatch?.queuedTasks ?? (boardAdvancedThisTick > 0 ? boardAdvancedThisTick : 1);
7786
+ if (readyFloor === null && advised !== null) {
7787
+ maxStarts = Math.max(maxStarts, Math.min(resourceGate.slotsAvailable, advised));
7788
+ }
7789
+ const underutilized = dispatch?.underutilized === true || (readyFloor ?? 0) > 0 && resourceGate.slotsAvailable > 0 && resourceGate.maxConcurrentWorkers > 0 && resourceGate.activeWorkers < resourceGate.maxConcurrentWorkers;
7790
+ if (resourceGate.slotsAvailable > 0 && maxStarts === 0 && (underutilized || hygieneAdvanced > 0)) {
7791
+ const ready = readyFloor ?? (hygieneAdvanced > 0 ? hygieneAdvanced : 1);
7145
7792
  maxStarts = Math.min(resourceGate.slotsAvailable, Math.max(1, ready));
7146
7793
  }
7794
+ const nonDispatchableReady = queuedTasks !== null && actionableReady !== null ? Math.max(0, queuedTasks - actionableReady) : null;
7147
7795
  return {
7148
7796
  maxStarts: Math.max(0, maxStarts),
7149
7797
  underutilized,
7150
7798
  advisedStarts: advised,
7151
- boardAdvancedThisTick
7799
+ actionableReady,
7800
+ queuedTasks,
7801
+ nonDispatchableReady,
7802
+ boardAdvancedThisTick,
7803
+ leaseReapedThisTick
7152
7804
  };
7153
7805
  }
7154
7806
 
@@ -7192,7 +7844,7 @@ function buildBoxResourceSnapshotFromGate(gate, input = {}) {
7192
7844
  }
7193
7845
 
7194
7846
  // src/plan-progress-daemon-sync.ts
7195
- import path34 from "node:path";
7847
+ import path37 from "node:path";
7196
7848
 
7197
7849
  // src/plan-progress-sync.ts
7198
7850
  async function syncPlanProgress(args) {
@@ -7216,7 +7868,7 @@ async function syncActiveWorkerPlanProgress(runId, args) {
7216
7868
  const outcomes = [];
7217
7869
  for (const name of Object.keys(run.workers || {})) {
7218
7870
  const worker = readJson(
7219
- path34.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
7871
+ path37.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
7220
7872
  void 0
7221
7873
  );
7222
7874
  if (!worker?.dispatched || !worker.taskId) continue;
@@ -7265,10 +7917,10 @@ async function fetchWorkspaceRuntimePreferences(agentOsId, args) {
7265
7917
  }
7266
7918
 
7267
7919
  // src/cleanup.ts
7268
- import path47 from "node:path";
7920
+ import path50 from "node:path";
7269
7921
 
7270
7922
  // src/cleanup-guards.ts
7271
- import path35 from "node:path";
7923
+ import path38 from "node:path";
7272
7924
 
7273
7925
  // src/cleanup-run-liveness.ts
7274
7926
  function isWorkerProcessLive(indexed) {
@@ -7392,7 +8044,7 @@ function skipWorktreeRemoval(input) {
7392
8044
  function skipDependencyCacheRemoval(input) {
7393
8045
  const { indexed, nodeModulesAgeMs, ageMs, worktreePath, activeWorktreePaths, diskPressure } = input;
7394
8046
  if (!diskPressure && ageMs < nodeModulesAgeMs) return "below_age_threshold";
7395
- if (activeWorktreePaths.has(path35.resolve(worktreePath))) return "active_worker";
8047
+ if (activeWorktreePaths.has(path38.resolve(worktreePath))) return "active_worker";
7396
8048
  if (indexed && isWorkerProcessLive(indexed)) return "active_worker";
7397
8049
  if (indexed && hasUnrestorableWorktreeChanges(indexed.status)) return "dirty_worktree";
7398
8050
  return null;
@@ -7419,11 +8071,11 @@ var LIVE_SKIP_REASONS = /* @__PURE__ */ new Set([
7419
8071
  function collectPreservedLivePaths(actions, skips) {
7420
8072
  const out = [];
7421
8073
  const seen = /* @__PURE__ */ new Set();
7422
- const push = (path59, reason, detail) => {
7423
- const key = `${path59}\0${reason}`;
8074
+ const push = (path62, reason, detail) => {
8075
+ const key = `${path62}\0${reason}`;
7424
8076
  if (seen.has(key) || out.length >= MAX_PRESERVED_LIVE_PATH_SAMPLES) return;
7425
8077
  seen.add(key);
7426
- out.push({ path: path59, reason, ...detail ? { detail } : {} });
8078
+ out.push({ path: path62, reason, ...detail ? { detail } : {} });
7427
8079
  };
7428
8080
  for (const skip2 of skips) {
7429
8081
  if (!LIVE_SKIP_REASONS.has(skip2.reason)) continue;
@@ -7438,11 +8090,11 @@ function collectPreservedLivePaths(actions, skips) {
7438
8090
  }
7439
8091
 
7440
8092
  // src/cleanup-run-directory.ts
7441
- import { existsSync as existsSync25, readdirSync as readdirSync6, statSync as statSync4 } from "node:fs";
7442
- import path37 from "node:path";
8093
+ import { existsSync as existsSync28, readdirSync as readdirSync7, statSync as statSync5 } from "node:fs";
8094
+ import path40 from "node:path";
7443
8095
 
7444
8096
  // src/cleanup-active-worktrees.ts
7445
- import path36 from "node:path";
8097
+ import path39 from "node:path";
7446
8098
  function isActiveHarnessWorker2(worker, runBase, runBaseCommit) {
7447
8099
  const status = computeWorkerStatus(worker, { base: runBase, baseCommit: runBaseCommit });
7448
8100
  return status.alive && !status.finalResult && status.attention.state !== "done";
@@ -7455,17 +8107,20 @@ function collectActiveWorktreeGuards(harnessRoots) {
7455
8107
  let runHasLive = false;
7456
8108
  for (const name of Object.keys(run.workers || {})) {
7457
8109
  const worker = readJson(
7458
- path36.join(runDirectoryAt(harnessRoot, run.id), "workers", safeSlug(name), "worker.json"),
8110
+ path39.join(runDirectoryAt(harnessRoot, run.id), "workers", safeSlug(name), "worker.json"),
7459
8111
  void 0
7460
8112
  );
7461
8113
  if (!worker?.worktreePath) continue;
7462
- const worktreePath = path36.resolve(worker.worktreePath);
8114
+ const worktreePath = path39.resolve(worker.worktreePath);
7463
8115
  if (!isActiveHarnessWorker2(worker, run.base, run.baseCommit)) continue;
7464
8116
  runHasLive = true;
7465
8117
  activeWorktreePaths.add(worktreePath);
7466
8118
  }
7467
8119
  if (runHasLive) liveRunKeys.add(`${harnessRoot}\0${run.id}`);
7468
8120
  }
8121
+ for (const key of collectFilesystemLiveRunKeys(harnessRoot)) {
8122
+ liveRunKeys.add(key);
8123
+ }
7469
8124
  }
7470
8125
  return { activeWorktreePaths, liveRunKeys };
7471
8126
  }
@@ -7477,20 +8132,20 @@ function isWorktreeOnLiveRun(worktreePath, harnessRoot, runId, liveRunKeys) {
7477
8132
  // src/cleanup-run-directory.ts
7478
8133
  function pathAgeMs(target, now) {
7479
8134
  try {
7480
- const mtime = statSync4(target).mtimeMs;
8135
+ const mtime = statSync5(target).mtimeMs;
7481
8136
  return Math.max(0, now - mtime);
7482
8137
  } catch {
7483
8138
  return 0;
7484
8139
  }
7485
8140
  }
7486
8141
  function loadRunStatus(harnessRoot, runId) {
7487
- const runPath = path37.join(harnessRoot, "runs", runId, "run.json");
7488
- if (!existsSync25(runPath)) return null;
8142
+ const runPath = path40.join(harnessRoot, "runs", runId, "run.json");
8143
+ if (!existsSync28(runPath)) return null;
7489
8144
  return readJson(runPath, null);
7490
8145
  }
7491
8146
  function runDirectoryIsEmpty(runPath) {
7492
8147
  try {
7493
- const entries = readdirSync6(runPath);
8148
+ const entries = readdirSync7(runPath);
7494
8149
  return entries.length === 0;
7495
8150
  } catch {
7496
8151
  return false;
@@ -7508,11 +8163,11 @@ function skipRunDirectoryRemoval(input) {
7508
8163
  return null;
7509
8164
  }
7510
8165
  function scanStaleRunDirectoryCandidates(opts) {
7511
- if (!existsSync25(opts.worktreesDir)) return [];
8166
+ if (!existsSync28(opts.worktreesDir)) return [];
7512
8167
  const candidates = [];
7513
8168
  let entries;
7514
8169
  try {
7515
- entries = readdirSync6(opts.worktreesDir, { withFileTypes: true });
8170
+ entries = readdirSync7(opts.worktreesDir, { withFileTypes: true });
7516
8171
  } catch {
7517
8172
  return [];
7518
8173
  }
@@ -7520,7 +8175,7 @@ function scanStaleRunDirectoryCandidates(opts) {
7520
8175
  if (!runEntry.isDirectory()) continue;
7521
8176
  const runId = runEntry.name;
7522
8177
  if (opts.runIdFilter && runId !== opts.runIdFilter) continue;
7523
- const runPath = path37.join(opts.worktreesDir, runId);
8178
+ const runPath = path40.join(opts.worktreesDir, runId);
7524
8179
  if (!runDirectoryIsEmpty(runPath)) continue;
7525
8180
  candidates.push({
7526
8181
  kind: "remove_run_directory",
@@ -7535,14 +8190,14 @@ function scanStaleRunDirectoryCandidates(opts) {
7535
8190
  }
7536
8191
 
7537
8192
  // src/cleanup-execute.ts
7538
- import { existsSync as existsSync27, rmSync as rmSync3 } from "node:fs";
7539
- import path39 from "node:path";
8193
+ import { existsSync as existsSync30, rmSync as rmSync3 } from "node:fs";
8194
+ import path42 from "node:path";
7540
8195
 
7541
8196
  // src/cleanup-dir-size.ts
7542
- import { existsSync as existsSync26, readdirSync as readdirSync7, statSync as statSync5 } from "node:fs";
7543
- import path38 from "node:path";
8197
+ import { existsSync as existsSync29, readdirSync as readdirSync8, statSync as statSync6 } from "node:fs";
8198
+ import path41 from "node:path";
7544
8199
  function directorySizeBytes(root, maxEntries = 5e4) {
7545
- if (!existsSync26(root)) return 0;
8200
+ if (!existsSync29(root)) return 0;
7546
8201
  let total = 0;
7547
8202
  let seen = 0;
7548
8203
  const stack = [root];
@@ -7550,16 +8205,16 @@ function directorySizeBytes(root, maxEntries = 5e4) {
7550
8205
  const current = stack.pop();
7551
8206
  let entries;
7552
8207
  try {
7553
- entries = readdirSync7(current);
8208
+ entries = readdirSync8(current);
7554
8209
  } catch {
7555
8210
  continue;
7556
8211
  }
7557
8212
  for (const name of entries) {
7558
8213
  if (seen++ > maxEntries) return null;
7559
- const full = path38.join(current, name);
8214
+ const full = path41.join(current, name);
7560
8215
  let st;
7561
8216
  try {
7562
- st = statSync5(full);
8217
+ st = statSync6(full);
7563
8218
  } catch {
7564
8219
  continue;
7565
8220
  }
@@ -7571,8 +8226,20 @@ function directorySizeBytes(root, maxEntries = 5e4) {
7571
8226
  }
7572
8227
 
7573
8228
  // src/cleanup-execute.ts
8229
+ function skipRunMetadataRemoval(candidate) {
8230
+ const harnessRoot = candidate.harnessRoot;
8231
+ if (!harnessRoot || !isHarnessRunMetadataPath(candidate.path, harnessRoot)) return null;
8232
+ return {
8233
+ ...candidate,
8234
+ executed: false,
8235
+ skipped: true,
8236
+ skipReason: "run_metadata_protected"
8237
+ };
8238
+ }
7574
8239
  function removeDependencyCache(candidate, execute) {
7575
- if (!existsSync27(candidate.path)) {
8240
+ const metadataSkip = skipRunMetadataRemoval(candidate);
8241
+ if (metadataSkip) return metadataSkip;
8242
+ if (!existsSync30(candidate.path)) {
7576
8243
  return {
7577
8244
  ...candidate,
7578
8245
  executed: false,
@@ -7612,7 +8279,9 @@ function removeBuildCache(candidate, execute) {
7612
8279
  return removeDependencyCache(candidate, execute);
7613
8280
  }
7614
8281
  function removeRunDirectory(candidate, execute) {
7615
- if (!existsSync27(candidate.path)) {
8282
+ const metadataSkip = skipRunMetadataRemoval(candidate);
8283
+ if (metadataSkip) return metadataSkip;
8284
+ if (!existsSync30(candidate.path)) {
7616
8285
  return {
7617
8286
  ...candidate,
7618
8287
  executed: false,
@@ -7643,7 +8312,9 @@ function removeRunDirectory(candidate, execute) {
7643
8312
  }
7644
8313
  }
7645
8314
  function removeWorktree(candidate, execute) {
7646
- if (!existsSync27(candidate.path)) {
8315
+ const metadataSkip = skipRunMetadataRemoval(candidate);
8316
+ if (metadataSkip) return metadataSkip;
8317
+ if (!existsSync30(candidate.path)) {
7647
8318
  return {
7648
8319
  ...candidate,
7649
8320
  executed: false,
@@ -7660,7 +8331,7 @@ function removeWorktree(candidate, execute) {
7660
8331
  if (repo) {
7661
8332
  git(repo, ["worktree", "remove", "--force", candidate.path], { allowFailure: true });
7662
8333
  }
7663
- if (existsSync27(candidate.path)) {
8334
+ if (existsSync30(candidate.path)) {
7664
8335
  rmSync3(candidate.path, { recursive: true, force: true });
7665
8336
  }
7666
8337
  return {
@@ -7680,15 +8351,15 @@ function removeWorktree(candidate, execute) {
7680
8351
  }
7681
8352
  }
7682
8353
  function isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, cacheDirName) {
7683
- const resolved = path39.resolve(targetPath);
7684
- const suffix = `${path39.sep}${cacheDirName}`;
8354
+ const resolved = path42.resolve(targetPath);
8355
+ const suffix = `${path42.sep}${cacheDirName}`;
7685
8356
  const cachePath = resolved.endsWith(suffix) ? resolved : null;
7686
8357
  if (!cachePath) return "path_outside_harness";
7687
- const rel = path39.relative(worktreesDir, cachePath);
7688
- if (rel.startsWith("..") || path39.isAbsolute(rel)) return "path_outside_harness";
7689
- const parts = rel.split(path39.sep);
8358
+ const rel = path42.relative(worktreesDir, cachePath);
8359
+ if (rel.startsWith("..") || path42.isAbsolute(rel)) return "path_outside_harness";
8360
+ const parts = rel.split(path42.sep);
7690
8361
  if (parts.length < 3 || parts[parts.length - 1] !== cacheDirName) return "path_outside_harness";
7691
- if (!resolved.startsWith(path39.resolve(harnessRoot))) return "path_outside_harness";
8362
+ if (!resolved.startsWith(path42.resolve(harnessRoot))) return "path_outside_harness";
7692
8363
  return null;
7693
8364
  }
7694
8365
  function isHarnessNodeModulesPath(targetPath, harnessRoot, worktreesDir) {
@@ -7698,37 +8369,37 @@ function isHarnessNextCachePath(targetPath, harnessRoot, worktreesDir) {
7698
8369
  return isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, ".next");
7699
8370
  }
7700
8371
  function isHarnessBuildCachePath(targetPath, harnessRoot, worktreesDir) {
7701
- const resolved = path39.resolve(targetPath);
7702
- const relToWt = path39.relative(worktreesDir, resolved);
7703
- if (relToWt.startsWith("..") || path39.isAbsolute(relToWt)) return "path_outside_harness";
7704
- const parts = relToWt.split(path39.sep);
8372
+ const resolved = path42.resolve(targetPath);
8373
+ const relToWt = path42.relative(worktreesDir, resolved);
8374
+ if (relToWt.startsWith("..") || path42.isAbsolute(relToWt)) return "path_outside_harness";
8375
+ const parts = relToWt.split(path42.sep);
7705
8376
  if (parts.length < 3) return "path_outside_harness";
7706
- if (!resolved.startsWith(path39.resolve(harnessRoot))) return "path_outside_harness";
8377
+ if (!resolved.startsWith(path42.resolve(harnessRoot))) return "path_outside_harness";
7707
8378
  return null;
7708
8379
  }
7709
8380
 
7710
8381
  // src/cleanup-scan.ts
7711
- import { existsSync as existsSync28, readdirSync as readdirSync8, statSync as statSync6 } from "node:fs";
7712
- import path40 from "node:path";
8382
+ import { existsSync as existsSync31, readdirSync as readdirSync9, statSync as statSync7 } from "node:fs";
8383
+ import path43 from "node:path";
7713
8384
  function pathAgeMs2(target, now) {
7714
8385
  try {
7715
- const mtime = statSync6(target).mtimeMs;
8386
+ const mtime = statSync7(target).mtimeMs;
7716
8387
  return Math.max(0, now - mtime);
7717
8388
  } catch {
7718
8389
  return 0;
7719
8390
  }
7720
8391
  }
7721
8392
  function isPathInside(child, parent) {
7722
- const rel = path40.relative(parent, child);
7723
- return rel === "" || !rel.startsWith("..") && !path40.isAbsolute(rel);
8393
+ const rel = path43.relative(parent, child);
8394
+ return rel === "" || !rel.startsWith("..") && !path43.isAbsolute(rel);
7724
8395
  }
7725
8396
  function collectBuildCacheForWorktree(worktreePath, opts, seen, meta) {
7726
8397
  const out = [];
7727
8398
  for (const rel of HARNESS_BUILD_CACHE_RELATIVE_PATHS) {
7728
8399
  if (rel === ".next") continue;
7729
- const target = path40.join(worktreePath, rel);
7730
- if (!existsSync28(target)) continue;
7731
- const resolved = path40.resolve(target);
8400
+ const target = path43.join(worktreePath, rel);
8401
+ if (!existsSync31(target)) continue;
8402
+ const resolved = path43.resolve(target);
7732
8403
  if (seen.has(resolved)) continue;
7733
8404
  if (!isPathInside(resolved, opts.harnessRoot)) continue;
7734
8405
  seen.add(resolved);
@@ -7757,13 +8428,13 @@ function scanBuildCacheCandidates(opts) {
7757
8428
  })
7758
8429
  );
7759
8430
  }
7760
- if (!opts.includeOrphans || !existsSync28(opts.worktreesDir)) return candidates;
7761
- for (const runEntry of readdirSync8(opts.worktreesDir, { withFileTypes: true })) {
8431
+ if (!opts.includeOrphans || !existsSync31(opts.worktreesDir)) return candidates;
8432
+ for (const runEntry of readdirSync9(opts.worktreesDir, { withFileTypes: true })) {
7762
8433
  if (!runEntry.isDirectory()) continue;
7763
- const runPath = path40.join(opts.worktreesDir, runEntry.name);
7764
- for (const workerEntry of readdirSync8(runPath, { withFileTypes: true })) {
8434
+ const runPath = path43.join(opts.worktreesDir, runEntry.name);
8435
+ for (const workerEntry of readdirSync9(runPath, { withFileTypes: true })) {
7765
8436
  if (!workerEntry.isDirectory()) continue;
7766
- const worktreePath = path40.join(runPath, workerEntry.name);
8437
+ const worktreePath = path43.join(runPath, workerEntry.name);
7767
8438
  candidates.push(
7768
8439
  ...collectBuildCacheForWorktree(worktreePath, opts, seen, {
7769
8440
  runId: runEntry.name,
@@ -7784,7 +8455,7 @@ function scanWorktreeCandidates(opts) {
7784
8455
  for (const entry of opts.index.values()) {
7785
8456
  if (opts.runIdFilter && entry.runId !== opts.runIdFilter) continue;
7786
8457
  const resolved = entry.worktreePath;
7787
- if (!existsSync28(resolved)) continue;
8458
+ if (!existsSync31(resolved)) continue;
7788
8459
  if (seen.has(resolved)) continue;
7789
8460
  seen.add(resolved);
7790
8461
  candidates.push({
@@ -7798,24 +8469,24 @@ function scanWorktreeCandidates(opts) {
7798
8469
  });
7799
8470
  }
7800
8471
  }
7801
- if (!orphanEnabled || !existsSync28(opts.worktreesDir)) return candidates;
8472
+ if (!orphanEnabled || !existsSync31(opts.worktreesDir)) return candidates;
7802
8473
  const indexedPaths = /* @__PURE__ */ new Set();
7803
8474
  for (const entry of opts.index.values()) {
7804
- indexedPaths.add(path40.resolve(entry.worktreePath));
8475
+ indexedPaths.add(path43.resolve(entry.worktreePath));
7805
8476
  }
7806
- for (const runEntry of readdirSync8(opts.worktreesDir, { withFileTypes: true })) {
8477
+ for (const runEntry of readdirSync9(opts.worktreesDir, { withFileTypes: true })) {
7807
8478
  if (!runEntry.isDirectory()) continue;
7808
8479
  if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
7809
- const runPath = path40.join(opts.worktreesDir, runEntry.name);
8480
+ const runPath = path43.join(opts.worktreesDir, runEntry.name);
7810
8481
  let workerEntries;
7811
8482
  try {
7812
- workerEntries = readdirSync8(runPath, { withFileTypes: true });
8483
+ workerEntries = readdirSync9(runPath, { withFileTypes: true });
7813
8484
  } catch {
7814
8485
  continue;
7815
8486
  }
7816
8487
  for (const workerEntry of workerEntries) {
7817
8488
  if (!workerEntry.isDirectory()) continue;
7818
- const worktreePath = path40.resolve(path40.join(runPath, workerEntry.name));
8489
+ const worktreePath = path43.resolve(path43.join(runPath, workerEntry.name));
7819
8490
  if (seen.has(worktreePath)) continue;
7820
8491
  if (indexedPaths.has(worktreePath)) continue;
7821
8492
  if (!isPathInside(worktreePath, opts.harnessRoot)) continue;
@@ -7834,27 +8505,27 @@ function scanWorktreeCandidates(opts) {
7834
8505
  }
7835
8506
 
7836
8507
  // src/cleanup-dependency-scan.ts
7837
- import { existsSync as existsSync29, readdirSync as readdirSync9, statSync as statSync7 } from "node:fs";
7838
- import path41 from "node:path";
8508
+ import { existsSync as existsSync32, readdirSync as readdirSync10, statSync as statSync8 } from "node:fs";
8509
+ import path44 from "node:path";
7839
8510
  var DEPENDENCY_CACHE_DIRS = [
7840
8511
  { dirName: "node_modules", kind: "remove_node_modules" },
7841
8512
  { dirName: ".next", kind: "remove_next_cache" }
7842
8513
  ];
7843
8514
  function pathAgeMs3(target, now) {
7844
8515
  try {
7845
- const mtime = statSync7(target).mtimeMs;
8516
+ const mtime = statSync8(target).mtimeMs;
7846
8517
  return Math.max(0, now - mtime);
7847
8518
  } catch {
7848
8519
  return 0;
7849
8520
  }
7850
8521
  }
7851
8522
  function isPathInside2(child, parent) {
7852
- const rel = path41.relative(parent, child);
7853
- return rel === "" || !rel.startsWith("..") && !path41.isAbsolute(rel);
8523
+ const rel = path44.relative(parent, child);
8524
+ return rel === "" || !rel.startsWith("..") && !path44.isAbsolute(rel);
7854
8525
  }
7855
8526
  function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
7856
- if (!existsSync29(targetPath)) return;
7857
- const resolved = path41.resolve(targetPath);
8527
+ if (!existsSync32(targetPath)) return;
8528
+ const resolved = path44.resolve(targetPath);
7858
8529
  if (seen.has(resolved)) return;
7859
8530
  if (!isPathInside2(resolved, opts.harnessRoot)) return;
7860
8531
  seen.add(resolved);
@@ -7871,7 +8542,7 @@ function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
7871
8542
  }
7872
8543
  function scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, meta) {
7873
8544
  for (const entry of DEPENDENCY_CACHE_DIRS) {
7874
- pushCandidate2(candidates, seen, opts, path41.join(worktreePath, entry.dirName), entry.kind, meta);
8545
+ pushCandidate2(candidates, seen, opts, path44.join(worktreePath, entry.dirName), entry.kind, meta);
7875
8546
  }
7876
8547
  }
7877
8548
  function scanDependencyCacheCandidates(opts) {
@@ -7885,20 +8556,20 @@ function scanDependencyCacheCandidates(opts) {
7885
8556
  repo: entry.run.repo
7886
8557
  });
7887
8558
  }
7888
- if (!existsSync29(opts.worktreesDir)) return candidates;
7889
- for (const runEntry of readdirSync9(opts.worktreesDir, { withFileTypes: true })) {
8559
+ if (!existsSync32(opts.worktreesDir)) return candidates;
8560
+ for (const runEntry of readdirSync10(opts.worktreesDir, { withFileTypes: true })) {
7890
8561
  if (!runEntry.isDirectory()) continue;
7891
8562
  if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
7892
- const runPath = path41.join(opts.worktreesDir, runEntry.name);
8563
+ const runPath = path44.join(opts.worktreesDir, runEntry.name);
7893
8564
  let workerEntries;
7894
8565
  try {
7895
- workerEntries = readdirSync9(runPath, { withFileTypes: true });
8566
+ workerEntries = readdirSync10(runPath, { withFileTypes: true });
7896
8567
  } catch {
7897
8568
  continue;
7898
8569
  }
7899
8570
  for (const workerEntry of workerEntries) {
7900
8571
  if (!workerEntry.isDirectory()) continue;
7901
- const worktreePath = path41.join(runPath, workerEntry.name);
8572
+ const worktreePath = path44.join(runPath, workerEntry.name);
7902
8573
  scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, {
7903
8574
  runId: runEntry.name,
7904
8575
  worker: workerEntry.name
@@ -7909,11 +8580,11 @@ function scanDependencyCacheCandidates(opts) {
7909
8580
  }
7910
8581
 
7911
8582
  // src/cleanup-duplicate-worktrees.ts
7912
- import { existsSync as existsSync30, statSync as statSync8 } from "node:fs";
7913
- import path42 from "node:path";
8583
+ import { existsSync as existsSync33, statSync as statSync9 } from "node:fs";
8584
+ import path45 from "node:path";
7914
8585
  function pathAgeMs4(target, now) {
7915
8586
  try {
7916
- const mtime = statSync8(target).mtimeMs;
8587
+ const mtime = statSync9(target).mtimeMs;
7917
8588
  return Math.max(0, now - mtime);
7918
8589
  } catch {
7919
8590
  return 0;
@@ -7940,8 +8611,8 @@ function parseWorktreePorcelain(output) {
7940
8611
  return records;
7941
8612
  }
7942
8613
  function isUnderWorktreesDir(worktreePath, worktreesDir) {
7943
- const rel = path42.relative(path42.resolve(worktreesDir), path42.resolve(worktreePath));
7944
- return rel !== "" && !rel.startsWith("..") && !path42.isAbsolute(rel);
8614
+ const rel = path45.relative(path45.resolve(worktreesDir), path45.resolve(worktreePath));
8615
+ return rel !== "" && !rel.startsWith("..") && !path45.isAbsolute(rel);
7945
8616
  }
7946
8617
  function isCleanWorktree(worktreePath, repoRoot) {
7947
8618
  try {
@@ -7954,14 +8625,14 @@ function isCleanWorktree(worktreePath, repoRoot) {
7954
8625
  }
7955
8626
  }
7956
8627
  function scanDuplicateWorktreeCandidates(opts) {
7957
- if (!opts.includeOrphans || !existsSync30(opts.worktreesDir)) return [];
8628
+ if (!opts.includeOrphans || !existsSync33(opts.worktreesDir)) return [];
7958
8629
  const repos = /* @__PURE__ */ new Set();
7959
8630
  for (const entry of opts.index.values()) {
7960
- if (entry.run.repo) repos.add(path42.resolve(entry.run.repo));
8631
+ if (entry.run.repo) repos.add(path45.resolve(entry.run.repo));
7961
8632
  }
7962
8633
  const indexedPaths = /* @__PURE__ */ new Set();
7963
8634
  for (const entry of opts.index.values()) {
7964
- indexedPaths.add(path42.resolve(entry.worktreePath));
8635
+ indexedPaths.add(path45.resolve(entry.worktreePath));
7965
8636
  }
7966
8637
  const candidates = [];
7967
8638
  const seen = /* @__PURE__ */ new Set();
@@ -7974,15 +8645,15 @@ function scanDuplicateWorktreeCandidates(opts) {
7974
8645
  }
7975
8646
  const worktrees = parseWorktreePorcelain(porcelain);
7976
8647
  for (const wt of worktrees) {
7977
- const resolved = path42.resolve(wt.path);
7978
- if (resolved === path42.resolve(repoRoot)) continue;
8648
+ const resolved = path45.resolve(wt.path);
8649
+ if (resolved === path45.resolve(repoRoot)) continue;
7979
8650
  if (!isUnderWorktreesDir(resolved, opts.worktreesDir)) continue;
7980
8651
  if (indexedPaths.has(resolved)) continue;
7981
8652
  if (seen.has(resolved)) continue;
7982
- if (!existsSync30(resolved)) continue;
8653
+ if (!existsSync33(resolved)) continue;
7983
8654
  if (!isCleanWorktree(resolved, repoRoot)) continue;
7984
- const rel = path42.relative(opts.worktreesDir, resolved);
7985
- const parts = rel.split(path42.sep);
8655
+ const rel = path45.relative(opts.worktreesDir, resolved);
8656
+ const parts = rel.split(path45.sep);
7986
8657
  const runId = parts[0];
7987
8658
  const worker = parts[1] ?? "unknown";
7988
8659
  seen.add(resolved);
@@ -8001,12 +8672,12 @@ function scanDuplicateWorktreeCandidates(opts) {
8001
8672
  }
8002
8673
 
8003
8674
  // src/cleanup-worktree-index.ts
8004
- import path43 from "node:path";
8675
+ import path46 from "node:path";
8005
8676
  function buildWorktreeIndexAt(harnessRoot) {
8006
8677
  const index = /* @__PURE__ */ new Map();
8007
8678
  for (const run of listRunRecordsForHarnessRoot(harnessRoot)) {
8008
8679
  for (const name of Object.keys(run.workers || {})) {
8009
- const workerPath = path43.join(
8680
+ const workerPath = path46.join(
8010
8681
  runDirectoryAt(harnessRoot, run.id),
8011
8682
  "workers",
8012
8683
  safeSlug(name),
@@ -8015,9 +8686,9 @@ function buildWorktreeIndexAt(harnessRoot) {
8015
8686
  const worker = readJson(workerPath, void 0);
8016
8687
  if (!worker?.worktreePath) continue;
8017
8688
  const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
8018
- index.set(path43.resolve(worker.worktreePath), {
8689
+ index.set(path46.resolve(worker.worktreePath), {
8019
8690
  harnessRoot,
8020
- worktreePath: path43.resolve(worker.worktreePath),
8691
+ worktreePath: path46.resolve(worker.worktreePath),
8021
8692
  runId: run.id,
8022
8693
  workerName: name,
8023
8694
  run,
@@ -8030,7 +8701,7 @@ function buildWorktreeIndexAt(harnessRoot) {
8030
8701
  }
8031
8702
 
8032
8703
  // src/cleanup-retention-config.ts
8033
- function envFlag(name) {
8704
+ function envFlag2(name) {
8034
8705
  const v = process.env[name];
8035
8706
  return v === "1" || v === "true" || v === "yes";
8036
8707
  }
@@ -8041,17 +8712,17 @@ function envMs(name, fallback) {
8041
8712
  return Number.isFinite(n) && n >= 0 ? n : fallback;
8042
8713
  }
8043
8714
  function resolveHarnessRetention(options = {}) {
8044
- const execute = options.execute === true || options.execute !== false && envFlag("KYNVER_CLEANUP_EXECUTE");
8045
- const finalizeStaleRuns2 = options.finalizeStaleRuns !== false && !envFlag("KYNVER_CLEANUP_SKIP_FINALIZE");
8715
+ const execute = options.execute === true || options.execute !== false && envFlag2("KYNVER_CLEANUP_EXECUTE");
8716
+ const finalizeStaleRuns2 = options.finalizeStaleRuns !== false && !envFlag2("KYNVER_CLEANUP_SKIP_FINALIZE");
8046
8717
  const nodeModulesAgeMs = options.nodeModulesAgeMs ?? envMs("KYNVER_CLEANUP_NODE_MODULES_AGE_MS", DEFAULT_NODE_MODULES_AGE_MS);
8047
8718
  const worktreesAgeMs = options.worktreesAgeMs ?? envMs("KYNVER_CLEANUP_WORKTREES_AGE_MS", 0);
8048
8719
  const terminalWorktreesAgeMs = options.terminalWorktreesAgeMs ?? envMs("KYNVER_CLEANUP_TERMINAL_WORKTREES_AGE_MS", DEFAULT_TERMINAL_WORKTREES_AGE_MS);
8049
8720
  const runDirectoriesAgeMs = options.runDirectoriesAgeMs ?? envMs("KYNVER_CLEANUP_RUN_DIRECTORIES_AGE_MS", DEFAULT_RUN_DIRECTORIES_AGE_MS);
8050
8721
  const maxActionsPerSweep = options.maxActionsPerSweep ?? envMs("KYNVER_CLEANUP_MAX_ACTIONS_PER_SWEEP", DEFAULT_MAX_ACTIONS_PER_SWEEP);
8051
- const includeOrphans = options.includeOrphans === true || envFlag("KYNVER_CLEANUP_INCLUDE_ORPHANS");
8052
- const scopeAll = envFlag("KYNVER_CLEANUP_SCOPE_ALL") || process.env.KYNVER_CLEANUP_SCOPE === "all";
8722
+ const includeOrphans = options.includeOrphans === true || envFlag2("KYNVER_CLEANUP_INCLUDE_ORPHANS");
8723
+ const scopeAll = envFlag2("KYNVER_CLEANUP_SCOPE_ALL") || process.env.KYNVER_CLEANUP_SCOPE === "all";
8053
8724
  const runIdFilter = scopeAll ? options.runIdFilter : options.runIdFilter ?? (process.env.KYNVER_CLEANUP_RUN_ID || void 0);
8054
- const accountBytes = options.accountBytes !== false && !envFlag("KYNVER_CLEANUP_SKIP_BYTE_ACCOUNTING");
8725
+ const accountBytes = options.accountBytes !== false && !envFlag2("KYNVER_CLEANUP_SKIP_BYTE_ACCOUNTING");
8055
8726
  return {
8056
8727
  execute,
8057
8728
  finalizeStaleRuns: finalizeStaleRuns2,
@@ -8081,15 +8752,15 @@ function resolvePipelineHarnessRetention(runId) {
8081
8752
  }
8082
8753
 
8083
8754
  // src/cleanup-orphan-safety.ts
8084
- import { existsSync as existsSync31, statSync as statSync9 } from "node:fs";
8085
- import path44 from "node:path";
8755
+ import { existsSync as existsSync34, statSync as statSync10 } from "node:fs";
8756
+ import path47 from "node:path";
8086
8757
  var DEFAULT_HEARTBEAT_FRESH_MS = 30 * 60 * 1e3;
8087
8758
  function assessOrphanWorktreeSafety(input) {
8088
8759
  const now = input.now ?? Date.now();
8089
8760
  const heartbeatFreshMs = input.heartbeatFreshMs ?? DEFAULT_HEARTBEAT_FRESH_MS;
8090
- if (!existsSync31(input.worktreePath)) return null;
8761
+ if (!existsSync34(input.worktreePath)) return null;
8091
8762
  if (input.runId && input.workerName) {
8092
- const heartbeatPath = path44.join(
8763
+ const heartbeatPath = path47.join(
8093
8764
  input.harnessRoot,
8094
8765
  "runs",
8095
8766
  input.runId,
@@ -8098,13 +8769,13 @@ function assessOrphanWorktreeSafety(input) {
8098
8769
  "heartbeat.jsonl"
8099
8770
  );
8100
8771
  try {
8101
- const mtime = statSync9(heartbeatPath).mtimeMs;
8772
+ const mtime = statSync10(heartbeatPath).mtimeMs;
8102
8773
  if (now - mtime < heartbeatFreshMs) return "active_worker";
8103
8774
  } catch {
8104
8775
  }
8105
8776
  }
8106
- const gitDir = path44.join(input.worktreePath, ".git");
8107
- if (!existsSync31(gitDir)) return null;
8777
+ const gitDir = path47.join(input.worktreePath, ".git");
8778
+ if (!existsSync34(gitDir)) return null;
8108
8779
  const porcelain = gitCapture(input.worktreePath, ["status", "--porcelain"]);
8109
8780
  if (porcelain.status !== 0) return "pr_or_unmerged_commits";
8110
8781
  const dirtyLines = porcelain.stdout.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
@@ -8133,14 +8804,14 @@ function assessOrphanWorktreeSafety(input) {
8133
8804
  }
8134
8805
 
8135
8806
  // src/harness-storage-snapshot.ts
8136
- import { existsSync as existsSync32, readdirSync as readdirSync10, statSync as statSync10 } from "node:fs";
8137
- import path45 from "node:path";
8807
+ import { existsSync as existsSync35, readdirSync as readdirSync11, statSync as statSync11 } from "node:fs";
8808
+ import path48 from "node:path";
8138
8809
  function harnessStorageSnapshot(opts = {}) {
8139
8810
  const harnessRoot = normalizeHarnessRoot(opts.harnessRoot ?? resolveHarnessRoot());
8140
8811
  const worktreesDir = harnessWorktreesDir(harnessRoot);
8141
8812
  const now = opts.now ?? Date.now();
8142
8813
  const scannedAt = new Date(now).toISOString();
8143
- if (!existsSync32(worktreesDir)) {
8814
+ if (!existsSync35(worktreesDir)) {
8144
8815
  return {
8145
8816
  harnessRoot,
8146
8817
  worktreesDir,
@@ -8157,7 +8828,7 @@ function harnessStorageSnapshot(opts = {}) {
8157
8828
  let oldestMs = null;
8158
8829
  let entries;
8159
8830
  try {
8160
- entries = readdirSync10(worktreesDir, { withFileTypes: true });
8831
+ entries = readdirSync11(worktreesDir, { withFileTypes: true });
8161
8832
  } catch {
8162
8833
  return {
8163
8834
  harnessRoot,
@@ -8172,14 +8843,14 @@ function harnessStorageSnapshot(opts = {}) {
8172
8843
  for (const runEntry of entries) {
8173
8844
  if (!runEntry.isDirectory()) continue;
8174
8845
  runCount += 1;
8175
- const runPath = path45.join(worktreesDir, runEntry.name);
8846
+ const runPath = path48.join(worktreesDir, runEntry.name);
8176
8847
  try {
8177
- const st = statSync10(runPath);
8848
+ const st = statSync11(runPath);
8178
8849
  oldestMs = oldestMs === null ? st.mtimeMs : Math.min(oldestMs, st.mtimeMs);
8179
8850
  } catch {
8180
8851
  }
8181
8852
  try {
8182
- for (const workerEntry of readdirSync10(runPath, { withFileTypes: true })) {
8853
+ for (const workerEntry of readdirSync11(runPath, { withFileTypes: true })) {
8183
8854
  if (workerEntry.isDirectory()) workerCount += 1;
8184
8855
  }
8185
8856
  } catch {
@@ -8207,12 +8878,12 @@ function harnessStorageSnapshot(opts = {}) {
8207
8878
  }
8208
8879
 
8209
8880
  // src/cleanup-harness-roots.ts
8210
- import { existsSync as existsSync33 } from "node:fs";
8211
- import { homedir as homedir11 } from "node:os";
8212
- import path46 from "node:path";
8881
+ import { existsSync as existsSync36 } from "node:fs";
8882
+ import { homedir as homedir12 } from "node:os";
8883
+ import path49 from "node:path";
8213
8884
  var WELL_KNOWN_HARNESS_SCAN_ROOTS = [
8214
8885
  "/var/tmp/kynver-harness",
8215
- path46.join(homedir11(), ".openclaw", "harness")
8886
+ path49.join(homedir12(), ".openclaw", "harness")
8216
8887
  ];
8217
8888
  function addRoot(seen, roots, candidate) {
8218
8889
  if (!candidate?.trim()) return;
@@ -8234,15 +8905,15 @@ function resolveHarnessScanRoots(options = {}) {
8234
8905
  for (const candidate of extra ?? []) addRoot(seen, roots, candidate);
8235
8906
  if (shouldScanWellKnownRoots(options)) {
8236
8907
  for (const candidate of WELL_KNOWN_HARNESS_SCAN_ROOTS) {
8237
- const resolved = path46.resolve(candidate);
8238
- if (!seen.has(resolved) && existsSync33(resolved)) addRoot(seen, roots, resolved);
8908
+ const resolved = path49.resolve(candidate);
8909
+ if (!seen.has(resolved) && existsSync36(resolved)) addRoot(seen, roots, resolved);
8239
8910
  }
8240
8911
  }
8241
8912
  return roots;
8242
8913
  }
8243
8914
 
8244
8915
  // src/cleanup-disk-pressure.ts
8245
- function envFlag2(name) {
8916
+ function envFlag3(name) {
8246
8917
  const v = process.env[name];
8247
8918
  return v === "1" || v === "true" || v === "yes";
8248
8919
  }
@@ -8265,7 +8936,7 @@ function observeCleanupDiskPressure(input = {}) {
8265
8936
  }
8266
8937
  function applyDiskPressureToRetention(retention, pressure) {
8267
8938
  if (!pressure.pressured) return retention;
8268
- const executeOnPressure = retention.execute || envFlag2("KYNVER_CLEANUP_EXECUTE_ON_PRESSURE");
8939
+ const executeOnPressure = retention.execute || envFlag3("KYNVER_CLEANUP_EXECUTE_ON_PRESSURE");
8269
8940
  return {
8270
8941
  ...retention,
8271
8942
  execute: executeOnPressure,
@@ -8324,9 +8995,9 @@ function mergeWorktreeIndexes(scanRoots) {
8324
8995
  }
8325
8996
  function worktreePathForCandidate(candidate, worktreesDir) {
8326
8997
  if (candidate.runId && candidate.worker) {
8327
- return path47.join(worktreesDir, candidate.runId, candidate.worker);
8998
+ return path50.join(worktreesDir, candidate.runId, candidate.worker);
8328
8999
  }
8329
- return path47.resolve(candidate.path, "..");
9000
+ return path50.resolve(candidate.path, "..");
8330
9001
  }
8331
9002
  function runHarnessCleanup(options = {}) {
8332
9003
  let retention = resolveHarnessRetention(options);
@@ -8343,7 +9014,7 @@ function runHarnessCleanup(options = {}) {
8343
9014
  const atSweepCap = () => actions.length >= maxActions;
8344
9015
  for (const harnessRoot of paths.scanRoots) {
8345
9016
  if (atSweepCap()) break;
8346
- const worktreesDir = path47.join(harnessRoot, "worktrees");
9017
+ const worktreesDir = path50.join(harnessRoot, "worktrees");
8347
9018
  const scanOpts = {
8348
9019
  harnessRoot,
8349
9020
  worktreesDir,
@@ -8357,7 +9028,7 @@ function runHarnessCleanup(options = {}) {
8357
9028
  for (const raw of scanDependencyCacheCandidates(scanOpts)) {
8358
9029
  if (atSweepCap()) break;
8359
9030
  const candidate = attachCandidateBytes(raw, retention.accountBytes);
8360
- const resolved = path47.resolve(candidate.path);
9031
+ const resolved = path50.resolve(candidate.path);
8361
9032
  if (processedPaths.has(resolved)) continue;
8362
9033
  processedPaths.add(resolved);
8363
9034
  const pathSkip = pathGuardForDependencyCache(candidate, harnessRoot, worktreesDir);
@@ -8367,7 +9038,7 @@ function runHarnessCleanup(options = {}) {
8367
9038
  continue;
8368
9039
  }
8369
9040
  const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
8370
- const indexed = index.get(path47.resolve(worktreePath)) ?? null;
9041
+ const indexed = index.get(path50.resolve(worktreePath)) ?? null;
8371
9042
  const guardReason = skipDependencyCacheRemoval({
8372
9043
  indexed,
8373
9044
  includeOrphans: true,
@@ -8387,7 +9058,7 @@ function runHarnessCleanup(options = {}) {
8387
9058
  for (const raw of scanBuildCacheCandidates(scanOpts)) {
8388
9059
  if (atSweepCap()) break;
8389
9060
  const candidate = attachCandidateBytes(raw, retention.accountBytes);
8390
- const resolved = path47.resolve(candidate.path);
9061
+ const resolved = path50.resolve(candidate.path);
8391
9062
  if (processedPaths.has(resolved)) continue;
8392
9063
  processedPaths.add(resolved);
8393
9064
  const pathSkip = isHarnessBuildCachePath(candidate.path, harnessRoot, worktreesDir);
@@ -8397,7 +9068,7 @@ function runHarnessCleanup(options = {}) {
8397
9068
  continue;
8398
9069
  }
8399
9070
  const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
8400
- const indexed = index.get(path47.resolve(worktreePath)) ?? null;
9071
+ const indexed = index.get(path50.resolve(worktreePath)) ?? null;
8401
9072
  const guardReason = skipBuildCacheRemoval({
8402
9073
  indexed,
8403
9074
  includeOrphans: true,
@@ -8421,11 +9092,11 @@ function runHarnessCleanup(options = {}) {
8421
9092
  const worktreeSeen = /* @__PURE__ */ new Set();
8422
9093
  for (const raw of worktreeCandidates) {
8423
9094
  if (atSweepCap()) break;
8424
- const resolved = path47.resolve(raw.path);
9095
+ const resolved = path50.resolve(raw.path);
8425
9096
  if (worktreeSeen.has(resolved)) continue;
8426
9097
  worktreeSeen.add(resolved);
8427
9098
  const candidate = attachCandidateBytes({ ...raw, path: resolved }, retention.accountBytes);
8428
- const indexed = index.get(path47.resolve(candidate.path)) ?? null;
9099
+ const indexed = index.get(path50.resolve(candidate.path)) ?? null;
8429
9100
  const orphanSafety = indexed ? null : assessOrphanWorktreeSafety({
8430
9101
  worktreePath: candidate.path,
8431
9102
  harnessRoot,
@@ -8435,7 +9106,7 @@ function runHarnessCleanup(options = {}) {
8435
9106
  });
8436
9107
  const guardSkip = skipWorktreeRemoval({
8437
9108
  indexed,
8438
- worktreePath: path47.resolve(candidate.path),
9109
+ worktreePath: path50.resolve(candidate.path),
8439
9110
  includeOrphans: retention.includeOrphans,
8440
9111
  worktreesAgeMs: retention.worktreesAgeMs,
8441
9112
  terminalWorktreesAgeMs: retention.terminalWorktreesAgeMs,
@@ -8462,10 +9133,10 @@ function runHarnessCleanup(options = {}) {
8462
9133
  })) {
8463
9134
  if (atSweepCap()) break;
8464
9135
  const candidate = attachCandidateBytes(raw, retention.accountBytes);
8465
- const resolved = path47.resolve(candidate.path);
9136
+ const resolved = path50.resolve(candidate.path);
8466
9137
  if (processedPaths.has(resolved)) continue;
8467
9138
  processedPaths.add(resolved);
8468
- const runId = candidate.runId ?? path47.basename(resolved);
9139
+ const runId = candidate.runId ?? path50.basename(resolved);
8469
9140
  const dirSkip = skipRunDirectoryRemoval({
8470
9141
  harnessRoot,
8471
9142
  runId,
@@ -8554,8 +9225,8 @@ function isPipelineCleanupEnabled() {
8554
9225
 
8555
9226
  // src/installed-package-versions.ts
8556
9227
  import { readFile } from "node:fs/promises";
8557
- import { homedir as homedir12 } from "node:os";
8558
- import path48 from "node:path";
9228
+ import { homedir as homedir13 } from "node:os";
9229
+ import path51 from "node:path";
8559
9230
  var MANAGED_PACKAGES = [
8560
9231
  "@kynver-app/runtime",
8561
9232
  "@kynver-app/openclaw-agent-os",
@@ -8569,13 +9240,13 @@ function unique(values) {
8569
9240
  return [...new Set(values.filter((value) => Boolean(value)))];
8570
9241
  }
8571
9242
  function moduleRoots() {
8572
- const home = homedir12();
8573
- const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ?? path48.join(home, ".openclaw", "npm");
8574
- const npmGlobalRoot = trim(process.env.KYNVER_NPM_GLOBAL_ROOT) ?? trim(process.env.KYNVER_NPM_GLOBAL_MODULES_ROOT) ?? (trim(process.env.NPM_CONFIG_PREFIX) ? path48.join(trim(process.env.NPM_CONFIG_PREFIX), "lib", "node_modules") : path48.join(home, ".npm-global", "lib", "node_modules"));
9243
+ const home = homedir13();
9244
+ const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ?? path51.join(home, ".openclaw", "npm");
9245
+ const npmGlobalRoot = trim(process.env.KYNVER_NPM_GLOBAL_ROOT) ?? trim(process.env.KYNVER_NPM_GLOBAL_MODULES_ROOT) ?? (trim(process.env.NPM_CONFIG_PREFIX) ? path51.join(trim(process.env.NPM_CONFIG_PREFIX), "lib", "node_modules") : path51.join(home, ".npm-global", "lib", "node_modules"));
8575
9246
  return unique([
8576
- path48.join(openClawPrefix, "lib", "node_modules"),
8577
- path48.join(openClawPrefix, "node_modules"),
8578
- npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot : path48.join(npmGlobalRoot, "lib", "node_modules")
9247
+ path51.join(openClawPrefix, "lib", "node_modules"),
9248
+ path51.join(openClawPrefix, "node_modules"),
9249
+ npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot : path51.join(npmGlobalRoot, "lib", "node_modules")
8579
9250
  ]);
8580
9251
  }
8581
9252
  async function readVersion(packageJsonPath) {
@@ -8591,7 +9262,7 @@ async function collectInstalledPackageVersions(observedAt = (/* @__PURE__ */ new
8591
9262
  const out = {};
8592
9263
  for (const packageName of MANAGED_PACKAGES) {
8593
9264
  for (const root of roots) {
8594
- const packageJsonPath = path48.join(root, packageName, "package.json");
9265
+ const packageJsonPath = path51.join(root, packageName, "package.json");
8595
9266
  const version = await readVersion(packageJsonPath);
8596
9267
  if (!version) continue;
8597
9268
  out[packageName] = { version, observedAt, path: packageJsonPath };
@@ -8607,7 +9278,7 @@ async function completeFinishedWorkers(runId, args) {
8607
9278
  const outcomes = [];
8608
9279
  for (const name of Object.keys(run.workers || {})) {
8609
9280
  const worker = readJson(
8610
- path49.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
9281
+ path52.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
8611
9282
  void 0
8612
9283
  );
8613
9284
  if (!worker?.taskId || worker.localOnly) continue;
@@ -8718,6 +9389,7 @@ async function runPipelineTick(args) {
8718
9389
  skipped: true,
8719
9390
  reason: execute ? dispatchResourceGate.reason ?? "no slots or queued work" : "execute disabled",
8720
9391
  maxStarts: 0,
9392
+ dispatchAdvice: maxStartsAdvice,
8721
9393
  ...exactTargetTaskIds.length ? { exactTargetTaskIds, exactOnly: true } : {}
8722
9394
  };
8723
9395
  }
@@ -8739,6 +9411,7 @@ async function runPipelineTick(args) {
8739
9411
  completionAckSync,
8740
9412
  operatorTick,
8741
9413
  sweep,
9414
+ dispatchAdvice: maxStartsAdvice,
8742
9415
  dispatch,
8743
9416
  idle
8744
9417
  };
@@ -8762,8 +9435,18 @@ async function runDaemon(args) {
8762
9435
  stopping = true;
8763
9436
  });
8764
9437
  console.error(JSON.stringify({ event: "daemon_start", runId, agentOsId, execute, intervalMs }));
9438
+ const cronEnv = resolveKynverCronEnv();
8765
9439
  while (!stopping) {
8766
9440
  try {
9441
+ if (cronEnv.tickEnabled) {
9442
+ const cronTick = await runKynverCronTick({
9443
+ env: cronEnv,
9444
+ agentOsIdFilter: agentOsId
9445
+ });
9446
+ if (cronTick.enabled && (cronTick.fired > 0 || cronTick.errors > 0)) {
9447
+ console.error(JSON.stringify({ event: "daemon_cron_tick", ...cronTick }));
9448
+ }
9449
+ }
8767
9450
  const tick = await runPipelineTick({ run: runId, agentOsId, execute, ...args });
8768
9451
  console.error(JSON.stringify({ event: "daemon_tick", ...tick }));
8769
9452
  if (tick.idle) {
@@ -8782,7 +9465,7 @@ async function runDaemon(args) {
8782
9465
  }
8783
9466
 
8784
9467
  // src/plan-progress.ts
8785
- import path50 from "node:path";
9468
+ import path53 from "node:path";
8786
9469
 
8787
9470
  // src/bounded-build/constants.ts
8788
9471
  var DEFAULT_BUILD_MEM_BUDGET_BYTES = 1536 * 1024 * 1024;
@@ -9069,7 +9752,7 @@ async function emitPlanProgress(args) {
9069
9752
  }
9070
9753
  function verifyPlanLocal(args) {
9071
9754
  const worktree = required(args.worktree ? String(args.worktree) : void 0, "worktree");
9072
- const cwd = path50.resolve(worktree);
9755
+ const cwd = path53.resolve(worktree);
9073
9756
  const summary = runHarnessVerifyCommands(cwd);
9074
9757
  const emitJson = args.json === true || args.json === "true";
9075
9758
  const payload = { passed: summary.passed, worktree: cwd, steps: summary.steps };
@@ -9118,9 +9801,9 @@ async function verifyPlan(args) {
9118
9801
  }
9119
9802
 
9120
9803
  // src/harness-verify-cli.ts
9121
- import path51 from "node:path";
9804
+ import path54 from "node:path";
9122
9805
  function runHarnessVerifyCli(args) {
9123
- const cwd = path51.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
9806
+ const cwd = path54.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
9124
9807
  const emitJson = args.json === true || args.json === "true" || args.emitJson === true || args.emitJson === "true";
9125
9808
  const commands = [];
9126
9809
  const rawCmd = args.command;
@@ -9164,7 +9847,7 @@ function runHarnessVerifyCli(args) {
9164
9847
  }
9165
9848
 
9166
9849
  // src/plan-persist-cli.ts
9167
- import { readFileSync as readFileSync11 } from "node:fs";
9850
+ import { readFileSync as readFileSync12 } from "node:fs";
9168
9851
  var OPERATIONS = ["create", "add_version", "update_metadata"];
9169
9852
  var FAILURE_KINDS = [
9170
9853
  "approval_guard",
@@ -9176,7 +9859,7 @@ var FAILURE_KINDS = [
9176
9859
  function readBodyArg(args) {
9177
9860
  const bodyFile = args.bodyFile ? String(args.bodyFile) : void 0;
9178
9861
  if (bodyFile) {
9179
- return { body: readFileSync11(bodyFile, "utf8"), bodyPathHint: bodyFile };
9862
+ return { body: readFileSync12(bodyFile, "utf8"), bodyPathHint: bodyFile };
9180
9863
  }
9181
9864
  const inline = args.body ? String(args.body) : void 0;
9182
9865
  if (inline) return { body: inline };
@@ -9320,7 +10003,7 @@ function formatMonitorTickNotice(tick) {
9320
10003
  }
9321
10004
 
9322
10005
  // src/monitor/monitor.service.ts
9323
- import path53 from "node:path";
10006
+ import path56 from "node:path";
9324
10007
 
9325
10008
  // src/monitor/monitor.classify.ts
9326
10009
  function classifyWorkerHealth(input) {
@@ -9372,11 +10055,11 @@ function classifyWorkerHealth(input) {
9372
10055
  }
9373
10056
 
9374
10057
  // src/monitor/monitor.store.ts
9375
- import { existsSync as existsSync34, mkdirSync as mkdirSync6, readdirSync as readdirSync11, unlinkSync as unlinkSync2 } from "node:fs";
9376
- import path52 from "node:path";
10058
+ import { existsSync as existsSync37, mkdirSync as mkdirSync6, readdirSync as readdirSync12, unlinkSync as unlinkSync3 } from "node:fs";
10059
+ import path55 from "node:path";
9377
10060
  function monitorsDir() {
9378
10061
  const { harnessRoot } = getHarnessPaths();
9379
- const dir = path52.join(harnessRoot, "monitors");
10062
+ const dir = path55.join(harnessRoot, "monitors");
9380
10063
  mkdirSync6(dir, { recursive: true });
9381
10064
  return dir;
9382
10065
  }
@@ -9384,7 +10067,7 @@ function monitorIdFor(runId, workerName) {
9384
10067
  return workerName ? `${safeSlug(runId)}--${safeSlug(workerName)}` : safeSlug(runId);
9385
10068
  }
9386
10069
  function monitorPath(monitorId) {
9387
- return path52.join(monitorsDir(), `${monitorId}.json`);
10070
+ return path55.join(monitorsDir(), `${monitorId}.json`);
9388
10071
  }
9389
10072
  function loadMonitorSession(monitorId) {
9390
10073
  return readJson(monitorPath(monitorId), void 0);
@@ -9394,18 +10077,18 @@ function saveMonitorSession(session) {
9394
10077
  }
9395
10078
  function deleteMonitorSession(monitorId) {
9396
10079
  const file = monitorPath(monitorId);
9397
- if (!existsSync34(file)) return false;
9398
- unlinkSync2(file);
10080
+ if (!existsSync37(file)) return false;
10081
+ unlinkSync3(file);
9399
10082
  return true;
9400
10083
  }
9401
10084
  function listMonitorSessions() {
9402
10085
  const dir = monitorsDir();
9403
- if (!existsSync34(dir)) return [];
10086
+ if (!existsSync37(dir)) return [];
9404
10087
  const entries = [];
9405
- for (const name of readdirSync11(dir)) {
10088
+ for (const name of readdirSync12(dir)) {
9406
10089
  if (!name.endsWith(".json")) continue;
9407
10090
  const session = readJson(
9408
- path52.join(dir, name),
10091
+ path55.join(dir, name),
9409
10092
  void 0
9410
10093
  );
9411
10094
  if (!session?.monitorId) continue;
@@ -9496,7 +10179,7 @@ async function fetchTaskLeasesForWorkers(input) {
9496
10179
  // src/monitor/monitor.service.ts
9497
10180
  function workerRecord2(runId, name) {
9498
10181
  return readJson(
9499
- path53.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
10182
+ path56.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
9500
10183
  void 0
9501
10184
  );
9502
10185
  }
@@ -9702,21 +10385,21 @@ async function runMonitorLoop(args) {
9702
10385
 
9703
10386
  // src/monitor/monitor-spawn.ts
9704
10387
  import { spawn as spawn6 } from "node:child_process";
9705
- import { closeSync as closeSync6, existsSync as existsSync35, openSync as openSync6 } from "node:fs";
9706
- import path54 from "node:path";
10388
+ import { closeSync as closeSync7, existsSync as existsSync38, openSync as openSync7 } from "node:fs";
10389
+ import path57 from "node:path";
9707
10390
  import { fileURLToPath as fileURLToPath3 } from "node:url";
9708
10391
  function resolveDefaultCliPath2() {
9709
- return path54.join(fileURLToPath3(new URL(".", import.meta.url)), "cli.js");
10392
+ return path57.join(fileURLToPath3(new URL(".", import.meta.url)), "cli.js");
9710
10393
  }
9711
10394
  function spawnMonitorSidecar(opts) {
9712
10395
  const cliPath = opts.cliPath ?? resolveDefaultCliPath2();
9713
- if (!existsSync35(cliPath)) return void 0;
10396
+ if (!existsSync38(cliPath)) return void 0;
9714
10397
  const monitorId = monitorIdFor(opts.runId, opts.workerName);
9715
10398
  const { harnessRoot } = getHarnessPaths();
9716
- const logPath = path54.join(harnessRoot, "monitors", `${monitorId}.log`);
10399
+ const logPath = path57.join(harnessRoot, "monitors", `${monitorId}.log`);
9717
10400
  let logFd;
9718
10401
  try {
9719
- logFd = openSync6(logPath, "a");
10402
+ logFd = openSync7(logPath, "a");
9720
10403
  } catch {
9721
10404
  logFd = void 0;
9722
10405
  }
@@ -9756,7 +10439,7 @@ function spawnMonitorSidecar(opts) {
9756
10439
  env: process.env
9757
10440
  })
9758
10441
  );
9759
- if (logFd !== void 0) closeSync6(logFd);
10442
+ if (logFd !== void 0) closeSync7(logFd);
9760
10443
  child.unref();
9761
10444
  const session = {
9762
10445
  monitorId,
@@ -9773,7 +10456,7 @@ function spawnMonitorSidecar(opts) {
9773
10456
  } catch {
9774
10457
  if (logFd !== void 0) {
9775
10458
  try {
9776
- closeSync6(logFd);
10459
+ closeSync7(logFd);
9777
10460
  } catch {
9778
10461
  }
9779
10462
  }
@@ -9833,13 +10516,13 @@ async function monitorTickCli(args) {
9833
10516
  }
9834
10517
 
9835
10518
  // src/package-version.ts
9836
- import { existsSync as existsSync36, readFileSync as readFileSync12 } from "node:fs";
10519
+ import { existsSync as existsSync39, readFileSync as readFileSync13 } from "node:fs";
9837
10520
  import { dirname, join } from "node:path";
9838
10521
  import { fileURLToPath as fileURLToPath4 } from "node:url";
9839
10522
  function resolvePackageRoot(moduleUrl) {
9840
10523
  let dir = dirname(fileURLToPath4(moduleUrl));
9841
10524
  for (let depth = 0; depth < 6; depth += 1) {
9842
- if (existsSync36(join(dir, "package.json"))) return dir;
10525
+ if (existsSync39(join(dir, "package.json"))) return dir;
9843
10526
  const parent = dirname(dir);
9844
10527
  if (parent === dir) break;
9845
10528
  dir = parent;
@@ -9848,7 +10531,7 @@ function resolvePackageRoot(moduleUrl) {
9848
10531
  }
9849
10532
  function readOwnPackageVersion(moduleUrl = import.meta.url) {
9850
10533
  const pkgPath = join(resolvePackageRoot(moduleUrl), "package.json");
9851
- const pkg = JSON.parse(readFileSync12(pkgPath, "utf8"));
10534
+ const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
9852
10535
  if (typeof pkg.version !== "string" || !pkg.version.trim()) {
9853
10536
  throw new Error(`Missing package.json version at ${pkgPath}`);
9854
10537
  }
@@ -9869,7 +10552,7 @@ function handleCliVersionFlag(argv, moduleUrl = import.meta.url, binName) {
9869
10552
  }
9870
10553
 
9871
10554
  // src/post-restart-unblock.ts
9872
- import path55 from "node:path";
10555
+ import path58 from "node:path";
9873
10556
  function skip(runId, worker, taskId, agentOsId, leaseOwner, reason) {
9874
10557
  return { runId, worker, taskId, agentOsId, leaseOwner, action: "skipped", reason };
9875
10558
  }
@@ -9882,7 +10565,7 @@ async function postRestartUnblock(args) {
9882
10565
  const errors = [];
9883
10566
  for (const run of listRunRecords()) {
9884
10567
  for (const name of Object.keys(run.workers ?? {})) {
9885
- const workerPath = path55.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
10568
+ const workerPath = path58.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
9886
10569
  const worker = readJson(workerPath, void 0);
9887
10570
  if (!worker) {
9888
10571
  skipped.push(skip(run.id, name, "", "", "", "worker.json missing"));
@@ -9994,12 +10677,12 @@ async function postRestartUnblockCli(args) {
9994
10677
  }
9995
10678
 
9996
10679
  // src/doctor/runtime-takeover.ts
9997
- import path57 from "node:path";
10680
+ import path60 from "node:path";
9998
10681
 
9999
10682
  // src/doctor/runtime-takeover.probes.ts
10000
- import { accessSync, constants, existsSync as existsSync37, readFileSync as readFileSync13 } from "node:fs";
10001
- import { homedir as homedir13 } from "node:os";
10002
- import path56 from "node:path";
10683
+ import { accessSync, constants, existsSync as existsSync40, readFileSync as readFileSync14 } from "node:fs";
10684
+ import { homedir as homedir14 } from "node:os";
10685
+ import path59 from "node:path";
10003
10686
  import { spawnSync as spawnSync7 } from "node:child_process";
10004
10687
  function captureCommand(bin, args) {
10005
10688
  try {
@@ -10028,7 +10711,7 @@ function tokenPrefix(token) {
10028
10711
  return trimmed.length <= 12 ? `${trimmed}\u2026` : `${trimmed.slice(0, 12)}\u2026`;
10029
10712
  }
10030
10713
  function isWritable(target) {
10031
- if (!existsSync37(target)) return false;
10714
+ if (!existsSync40(target)) return false;
10032
10715
  try {
10033
10716
  accessSync(target, constants.W_OK);
10034
10717
  return true;
@@ -10041,15 +10724,15 @@ var defaultRuntimeTakeoverProbes = {
10041
10724
  commandOnPath: (bin) => captureCommand(process.platform === "win32" ? "where" : "which", [bin]),
10042
10725
  kynverVersion: (bin) => captureCommand(bin, ["--version"]),
10043
10726
  loadConfig: () => loadUserConfig(),
10044
- configFilePath: () => path56.join(homedir13(), ".kynver", "config.json"),
10045
- credentialsFilePath: () => path56.join(homedir13(), ".kynver", "credentials"),
10727
+ configFilePath: () => path59.join(homedir14(), ".kynver", "config.json"),
10728
+ credentialsFilePath: () => path59.join(homedir14(), ".kynver", "credentials"),
10046
10729
  readCredentials: () => {
10047
- const credPath = path56.join(homedir13(), ".kynver", "credentials");
10048
- if (!existsSync37(credPath)) {
10730
+ const credPath = path59.join(homedir14(), ".kynver", "credentials");
10731
+ if (!existsSync40(credPath)) {
10049
10732
  return { hasApiKey: false };
10050
10733
  }
10051
10734
  try {
10052
- const parsed = JSON.parse(readFileSync13(credPath, "utf8"));
10735
+ const parsed = JSON.parse(readFileSync14(credPath, "utf8"));
10053
10736
  return {
10054
10737
  hasApiKey: Boolean(parsed.apiKey?.trim()),
10055
10738
  runnerTokenPrefix: tokenPrefix(parsed.runnerToken),
@@ -10068,7 +10751,10 @@ var defaultRuntimeTakeoverProbes = {
10068
10751
  kynverHarnessRoot: process.env.KYNVER_HARNESS_ROOT?.trim() || void 0,
10069
10752
  opusHarnessRoot: process.env.OPUS_HARNESS_ROOT?.trim() || void 0,
10070
10753
  kynverSchedulerProvider: process.env.KYNVER_SCHEDULER_PROVIDER?.trim() || void 0,
10071
- openclawCronStorePath: Boolean(process.env.OPENCLAW_CRON_STORE_PATH?.trim()),
10754
+ openclawCronStorePath: Boolean(
10755
+ process.env.KYNVER_CRON_STORE_PATH?.trim() || process.env.OPENCLAW_CRON_STORE_PATH?.trim()
10756
+ ),
10757
+ kynverCronDaemonPrimary: isKynverCronDaemonPrimary(),
10072
10758
  qstashTokenPresent: Boolean(process.env.QSTASH_TOKEN?.trim()),
10073
10759
  kynverHostedDeployment: (() => {
10074
10760
  const v = process.env.KYNVER_HOSTED_DEPLOYMENT?.trim().toLowerCase();
@@ -10076,8 +10762,8 @@ var defaultRuntimeTakeoverProbes = {
10076
10762
  })()
10077
10763
  }),
10078
10764
  harnessRoot: () => resolveHarnessRoot(),
10079
- legacyOpenclawHarnessRoot: () => path56.join(homedir13(), ".openclaw", "harness"),
10080
- pathExists: (target) => existsSync37(target),
10765
+ legacyOpenclawHarnessRoot: () => path59.join(homedir14(), ".openclaw", "harness"),
10766
+ pathExists: (target) => existsSync40(target),
10081
10767
  pathWritable: (target) => isWritable(target),
10082
10768
  vercelVersion: () => captureCommand("vercel", ["--version"]),
10083
10769
  vercelWhoami: () => captureCommand("vercel", ["whoami"])
@@ -10097,8 +10783,10 @@ function assessRuntimeTakeoverScheduler(env, ctx) {
10097
10783
  const schedulerDetails = {
10098
10784
  schedulerProvider: env.kynverSchedulerProvider ?? null,
10099
10785
  deploymentSchedulerProvider: ctx.deploymentSchedulerProvider ?? null,
10100
- openclawCronStorePath: Boolean(env.openclawCronStorePath)
10786
+ openclawCronStorePath: Boolean(env.openclawCronStorePath),
10787
+ kynverCronDaemonPrimary: Boolean(env.kynverCronDaemonPrimary)
10101
10788
  };
10789
+ const daemonDispatchReady = Boolean(ctx.agentOsId?.trim()) && Boolean(ctx.apiBaseUrl?.trim()) && ctx.hasScopedRunnerToken;
10102
10790
  if (hasQstashCutover(env, ctx) && !hasLocalOpenClawDependency(env, ctx)) {
10103
10791
  const source = env.kynverSchedulerProvider === "qstash" ? "KYNVER_SCHEDULER_PROVIDER=qstash on this host" : "deploymentSchedulerProvider=qstash in ~/.kynver/config.json";
10104
10792
  return check({
@@ -10110,6 +10798,19 @@ function assessRuntimeTakeoverScheduler(env, ctx) {
10110
10798
  });
10111
10799
  }
10112
10800
  if (hasLocalOpenClawDependency(env, ctx)) {
10801
+ if (env.kynverCronDaemonPrimary && daemonDispatchReady) {
10802
+ return check({
10803
+ id: "hotspot_openclaw_scheduler",
10804
+ label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
10805
+ status: "pass",
10806
+ summary: "Kynver Cron local store present; `kynver daemon` owns schedule fires (kynver-cron tick loop)",
10807
+ details: {
10808
+ ...schedulerDetails,
10809
+ dispatchPath: "kynver-daemon-cron-tick",
10810
+ kynverCronDaemonPrimary: true
10811
+ }
10812
+ });
10813
+ }
10113
10814
  const parts = [];
10114
10815
  if (env.kynverSchedulerProvider === "openclaw-cron") {
10115
10816
  parts.push("KYNVER_SCHEDULER_PROVIDER=openclaw-cron");
@@ -10118,21 +10819,20 @@ function assessRuntimeTakeoverScheduler(env, ctx) {
10118
10819
  parts.push("deploymentSchedulerProvider=openclaw-cron in config");
10119
10820
  }
10120
10821
  if (env.openclawCronStorePath) {
10121
- parts.push("OPENCLAW_CRON_STORE_PATH set (local cron bridge)");
10822
+ parts.push("KYNVER_CRON_STORE_PATH or OPENCLAW_CRON_STORE_PATH set (local cron store)");
10122
10823
  }
10123
10824
  return check({
10124
10825
  id: "hotspot_openclaw_scheduler",
10125
10826
  label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
10126
10827
  status: "warn",
10127
- summary: `OpenClaw local cron still active (${parts.join("; ")})`,
10128
- remediation: "On the Kynver deployment: set KYNVER_SCHEDULER_PROVIDER=qstash with QSTASH_TOKEN configured. On user runners: unset KYNVER_SCHEDULER_PROVIDER and OPENCLAW_CRON_STORE_PATH; after Vercel env is updated run `kynver scheduler attest-cutover`.",
10828
+ summary: `Local cron store without daemon tick (${parts.join("; ")})`,
10829
+ remediation: "Run `kynver daemon` with KYNVER_CRON_SECRET + KYNVER_API_URL (or KYNVER_CRON_FIRE_BASE_URL) so the daemon-owned cron tick fires schedules. On hosted deploys use QStash (KYNVER_SCHEDULER_PROVIDER=qstash). Legacy OpenClaw cron env aliases still work during cutover.",
10129
10830
  details: schedulerDetails
10130
10831
  });
10131
10832
  }
10132
10833
  const runnerOpenclaw = env.kynverSchedulerProvider === "openclaw-cron";
10133
10834
  const runnerQstash = env.kynverSchedulerProvider === "qstash";
10134
10835
  const hostedDeployment = Boolean(env.qstashTokenPresent) || Boolean(env.kynverHostedDeployment);
10135
- const daemonDispatchReady = Boolean(ctx.agentOsId?.trim()) && Boolean(ctx.apiBaseUrl?.trim()) && ctx.hasScopedRunnerToken;
10136
10836
  const hostedSchedulerProcess = hostedDeployment && !daemonDispatchReady;
10137
10837
  const deploymentNeedsQstash = hostedSchedulerProcess && !env.qstashTokenPresent && env.kynverSchedulerProvider !== "qstash";
10138
10838
  const deploymentOpenclaw = hostedSchedulerProcess && env.kynverSchedulerProvider === "openclaw-cron";
@@ -10405,8 +11105,8 @@ function assessVercelCli(probes) {
10405
11105
  }
10406
11106
  function assessHarnessDirs(probes) {
10407
11107
  const harnessRoot = probes.harnessRoot();
10408
- const runsDir = path57.join(harnessRoot, "runs");
10409
- const worktreesDir = path57.join(harnessRoot, "worktrees");
11108
+ const runsDir = path60.join(harnessRoot, "runs");
11109
+ const worktreesDir = path60.join(harnessRoot, "worktrees");
10410
11110
  const displayHarnessRoot = redactHomePath(harnessRoot);
10411
11111
  const displayRunsDir = redactHomePath(runsDir);
10412
11112
  const displayWorktreesDir = redactHomePath(worktreesDir);
@@ -10609,6 +11309,11 @@ var RUNNER_SCHEDULER_CUTOVER_STEPS = [
10609
11309
  function readSchedulerCutoverEnv(env = process.env) {
10610
11310
  return {
10611
11311
  kynverSchedulerProvider: env.KYNVER_SCHEDULER_PROVIDER?.trim() || null,
11312
+ kynverCronStorePath: env.KYNVER_CRON_STORE_PATH?.trim() || env.OPENCLAW_CRON_STORE_PATH?.trim() || null,
11313
+ kynverCronSecret: Boolean(
11314
+ env.KYNVER_CRON_SECRET?.trim() || env.OPENCLAW_CRON_SECRET?.trim()
11315
+ ),
11316
+ kynverCronFireBaseUrl: env.KYNVER_CRON_FIRE_BASE_URL?.trim() || env.OPENCLAW_CRON_FIRE_BASE_URL?.trim() || null,
10612
11317
  openclawCronStorePath: env.OPENCLAW_CRON_STORE_PATH?.trim() || null,
10613
11318
  openclawCronSecret: Boolean(env.OPENCLAW_CRON_SECRET?.trim()),
10614
11319
  openclawCronFireBaseUrl: env.OPENCLAW_CRON_FIRE_BASE_URL?.trim() || null
@@ -10619,8 +11324,13 @@ function assessSchedulerCutover(config, env = readSchedulerCutoverEnv()) {
10619
11324
  if (env.kynverSchedulerProvider === "openclaw-cron") {
10620
11325
  blockers.push("Runner still has KYNVER_SCHEDULER_PROVIDER=openclaw-cron");
10621
11326
  }
10622
- if (env.openclawCronStorePath) {
10623
- blockers.push("Runner still has OPENCLAW_CRON_STORE_PATH");
11327
+ if (env.kynverCronStorePath && env.kynverSchedulerProvider !== "kynver-cron") {
11328
+ blockers.push(
11329
+ "Runner has KYNVER_CRON_STORE_PATH but KYNVER_SCHEDULER_PROVIDER is not kynver-cron \u2014 use `kynver daemon` cron tick or unset the store for QStash-only runners"
11330
+ );
11331
+ }
11332
+ if (env.openclawCronStorePath && !env.kynverCronStorePath) {
11333
+ blockers.push("Runner still has legacy OPENCLAW_CRON_STORE_PATH (prefer KYNVER_CRON_STORE_PATH)");
10624
11334
  }
10625
11335
  if (config.deploymentSchedulerProvider === "openclaw-cron") {
10626
11336
  blockers.push("~/.kynver/config.json deploymentSchedulerProvider is still openclaw-cron");
@@ -10642,9 +11352,9 @@ function applySchedulerCutoverAttestation(config) {
10642
11352
  }
10643
11353
 
10644
11354
  // src/scheduler-cutover-cli.ts
10645
- import path58 from "node:path";
10646
- import { homedir as homedir14 } from "node:os";
10647
- var CONFIG_FILE2 = path58.join(homedir14(), ".kynver", "config.json");
11355
+ import path61 from "node:path";
11356
+ import { homedir as homedir15 } from "node:os";
11357
+ var CONFIG_FILE2 = path61.join(homedir15(), ".kynver", "config.json");
10648
11358
  function runSchedulerCutoverCheckCli(json = false) {
10649
11359
  const config = loadUserConfig();
10650
11360
  const report = assessSchedulerCutover(config);
@@ -10672,7 +11382,10 @@ function runSchedulerCutoverCheckCli(json = false) {
10672
11382
  ` KYNVER_SCHEDULER_PROVIDER: ${report.runnerEnv.kynverSchedulerProvider ?? "(unset)"}`
10673
11383
  );
10674
11384
  console.log(
10675
- ` OPENCLAW_CRON_STORE_PATH: ${report.runnerEnv.openclawCronStorePath ?? "(unset)"}`
11385
+ ` KYNVER_CRON_STORE_PATH: ${report.runnerEnv.kynverCronStorePath ?? "(unset)"}`
11386
+ );
11387
+ console.log(
11388
+ ` OPENCLAW_CRON_STORE_PATH (legacy): ${report.runnerEnv.openclawCronStorePath ?? "(unset)"}`
10676
11389
  );
10677
11390
  if (report.blockers.length) {
10678
11391
  console.log("\nBlockers:");
@@ -10719,6 +11432,65 @@ function runSchedulerAttestCutoverCli(json = false) {
10719
11432
  console.log(` config: ${payload.configPath}`);
10720
11433
  }
10721
11434
 
11435
+ // src/cron/cron-status.ts
11436
+ async function buildKynverCronStatusReport(env = resolveKynverCronEnv()) {
11437
+ const jobs = await loadCronJobs(env.storePath).catch(() => []);
11438
+ const state = await loadCronTickState(env.statePath).catch(() => ({ version: 1, jobs: {} }));
11439
+ const credentialsReady = Boolean(env.fireBaseUrl && env.secret);
11440
+ const daemonPrimary = isKynverCronDaemonPrimary(env);
11441
+ let primary = "disabled";
11442
+ if (daemonPrimary) primary = "kynver-cron-daemon";
11443
+ else if (process.env.QSTASH_TOKEN?.trim()) primary = "qstash";
11444
+ return {
11445
+ primary,
11446
+ env: {
11447
+ storePath: env.storePath,
11448
+ statePath: env.statePath,
11449
+ tickEnabled: env.tickEnabled,
11450
+ fireBaseUrl: env.fireBaseUrl,
11451
+ missedRunPolicy: env.missedRunPolicy,
11452
+ maxCatchUpPerTick: env.maxCatchUpPerTick,
11453
+ maxRetries: env.maxRetries
11454
+ },
11455
+ jobCount: jobs.length,
11456
+ stateJobCount: Object.keys(state.jobs).length,
11457
+ credentialsReady,
11458
+ daemonPrimary
11459
+ };
11460
+ }
11461
+
11462
+ // src/cron/cron-tick-cli.ts
11463
+ async function runCronStatusCli(json) {
11464
+ const report = await buildKynverCronStatusReport();
11465
+ if (json) {
11466
+ console.log(JSON.stringify(report, null, 2));
11467
+ return;
11468
+ }
11469
+ console.log(`Kynver Cron primary: ${report.primary}`);
11470
+ console.log(` store: ${report.env.storePath} (${report.jobCount} jobs)`);
11471
+ console.log(` tick state: ${report.env.statePath} (${report.stateJobCount} tracked)`);
11472
+ console.log(` tick enabled: ${report.env.tickEnabled}`);
11473
+ console.log(` fire base URL: ${report.env.fireBaseUrl ?? "(unset)"}`);
11474
+ console.log(` credentials ready: ${report.credentialsReady}`);
11475
+ console.log(` daemon-owned primary: ${report.daemonPrimary}`);
11476
+ }
11477
+ async function runCronTickCli(args) {
11478
+ const agentOsId = typeof args.agentOsId === "string" ? args.agentOsId : void 0;
11479
+ const result = await runKynverCronTick({
11480
+ agentOsIdFilter: agentOsId ?? null
11481
+ });
11482
+ if (args.json === true) {
11483
+ console.log(JSON.stringify(result, null, 2));
11484
+ return;
11485
+ }
11486
+ console.log(
11487
+ JSON.stringify({
11488
+ event: "kynver_cron_tick",
11489
+ ...result
11490
+ })
11491
+ );
11492
+ }
11493
+
10722
11494
  // src/cli.ts
10723
11495
  function isHelpFlag(arg) {
10724
11496
  return arg === "help" || arg === "--help" || arg === "-h";
@@ -10769,6 +11541,8 @@ function usage(code = 0) {
10769
11541
  " kynver doctor runtime-takeover",
10770
11542
  " kynver scheduler cutover-check [--json]",
10771
11543
  " kynver scheduler attest-cutover [--json]",
11544
+ " kynver cron status [--json]",
11545
+ " kynver cron tick [--agent-os-id AOS_ID] [--json]",
10772
11546
  " kynver board contract [--agent-os-id ID] [--base-url URL] [--since ISO] [--limit N]"
10773
11547
  ].join("\n")
10774
11548
  );
@@ -10780,7 +11554,7 @@ async function main(argv = process.argv.slice(2)) {
10780
11554
  const scope = argv.shift();
10781
11555
  let action;
10782
11556
  let rest;
10783
- if (scope === "run" || scope === "worker" || scope === "plan" || scope === "runner" || scope === "harness" || scope === "monitor" || scope === "doctor" || scope === "scheduler" || scope === "board") {
11557
+ if (scope === "run" || scope === "worker" || scope === "plan" || scope === "runner" || scope === "harness" || scope === "monitor" || scope === "doctor" || scope === "scheduler" || scope === "cron" || scope === "board") {
10784
11558
  action = argv.shift();
10785
11559
  rest = argv;
10786
11560
  } else {
@@ -10813,6 +11587,12 @@ async function main(argv = process.argv.slice(2)) {
10813
11587
  if (scope === "scheduler" && action === "attest-cutover") {
10814
11588
  return runSchedulerAttestCutoverCli(args.json === true);
10815
11589
  }
11590
+ if (scope === "cron" && action === "status") {
11591
+ return void await runCronStatusCli(args.json === true);
11592
+ }
11593
+ if (scope === "cron" && action === "tick") {
11594
+ return void await runCronTickCli(args);
11595
+ }
10816
11596
  if (scope === "board" && action === "contract") {
10817
11597
  return void await runCommandCenterContractCli(args);
10818
11598
  }