@kynver-app/runtime 0.1.93 → 0.1.99

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { mkdirSync as mkdirSync8, realpathSync } from "node:fs";
4
+ import { mkdirSync as mkdirSync10, realpathSync } from "node:fs";
5
5
  import { fileURLToPath as fileURLToPath5 } from "node:url";
6
6
 
7
7
  // src/config.ts
8
- import { existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "node:fs";
8
+ import { existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as readFileSync8, writeFileSync as writeFileSync2 } from "node:fs";
9
9
  import { homedir as homedir4, totalmem } from "node:os";
10
10
  import path8 from "node:path";
11
11
 
@@ -315,7 +315,13 @@ function displayUserPath(value) {
315
315
  }
316
316
 
317
317
  // src/default-repo-discovery.ts
318
- var WELL_KNOWN_REPO_DIRS = ["Kynver", "repos/Kynver", "code/Kynver", "projects/Kynver"];
318
+ var WELL_KNOWN_REPO_DIRS = [
319
+ "Kynver",
320
+ "repos/Kynver",
321
+ "repos/kynver-source-main",
322
+ "code/Kynver",
323
+ "projects/Kynver"
324
+ ];
319
325
  function readPackageName(repoRoot) {
320
326
  const pkgPath = path3.join(repoRoot, "package.json");
321
327
  if (!existsSync2(pkgPath)) return null;
@@ -455,6 +461,7 @@ function readMemAvailableBytes(meminfoText) {
455
461
 
456
462
  // src/resource-gate.ts
457
463
  import path7 from "node:path";
464
+ import { readFileSync as readFileSync7 } from "node:fs";
458
465
 
459
466
  // src/disk-gate.ts
460
467
  import { statfsSync as statfsSync2 } from "node:fs";
@@ -479,23 +486,23 @@ function isWslHost() {
479
486
  function observeWslHostDisk(options = {}) {
480
487
  const wsl = options.forceWsl === void 0 ? isWslHost() : options.forceWsl;
481
488
  if (!wsl) return null;
482
- const path67 = options.wslHostMount?.trim() || process.env.KYNVER_WSL_HOST_MOUNT?.trim() || DEFAULT_WSL_HOST_MOUNT;
489
+ const path69 = options.wslHostMount?.trim() || process.env.KYNVER_WSL_HOST_MOUNT?.trim() || DEFAULT_WSL_HOST_MOUNT;
483
490
  const warnBelowBytes = options.wslHostFreeWarnBytes ?? DEFAULT_WSL_HOST_WARN_FREE_BYTES;
484
491
  const criticalBelowBytes = options.wslHostFreeCriticalBytes ?? DEFAULT_WSL_HOST_CRITICAL_FREE_BYTES;
485
492
  const statfs = options.statfs ?? statfsSync;
486
493
  let stats;
487
494
  try {
488
- stats = statfs(path67);
495
+ stats = statfs(path69);
489
496
  } catch (error) {
490
497
  return {
491
498
  ok: false,
492
- path: path67,
499
+ path: path69,
493
500
  freeBytes: 0,
494
501
  totalBytes: 0,
495
502
  usedPercent: 100,
496
503
  warnBelowBytes,
497
504
  criticalBelowBytes,
498
- reason: `Windows host disk probe failed at ${path67}: ${error.message}`,
505
+ reason: `Windows host disk probe failed at ${path69}: ${error.message}`,
499
506
  probeError: error.message
500
507
  };
501
508
  }
@@ -509,11 +516,11 @@ function observeWslHostDisk(options = {}) {
509
516
  let reason = null;
510
517
  if (!ok) {
511
518
  const tag = criticalFree ? "critical" : "warning";
512
- reason = `Windows host disk ${path67} at ${tag}: ${freeGiB} GiB free (<${(criticalFree ? criticalBelowBytes : warnBelowBytes) / 1024 / 1024 / 1024} GiB); WSL VHDX cannot grow safely. ${summarizeWslRecoverySteps()}`;
519
+ reason = `Windows host disk ${path69} at ${tag}: ${freeGiB} GiB free (<${(criticalFree ? criticalBelowBytes : warnBelowBytes) / 1024 / 1024 / 1024} GiB); WSL VHDX cannot grow safely. ${summarizeWslRecoverySteps()}`;
513
520
  }
514
521
  return {
515
522
  ok,
516
- path: path67,
523
+ path: path69,
517
524
  freeBytes,
518
525
  totalBytes,
519
526
  usedPercent,
@@ -533,12 +540,12 @@ var DEFAULT_CRITICAL_FREE_BYTES = 15 * 1024 * 1024 * 1024;
533
540
  var DEFAULT_MAX_USED_PERCENT = 80;
534
541
  var DEFAULT_HARD_MAX_USED_PERCENT = 90;
535
542
  function observeRunnerDiskGate(input = {}) {
536
- const path67 = input.diskPath?.trim() || "/";
543
+ const path69 = input.diskPath?.trim() || "/";
537
544
  const warnBelowBytes = input.diskFreeWarnBytes ?? DEFAULT_WARN_FREE_BYTES;
538
545
  const criticalBelowBytes = input.diskFreeCriticalBytes ?? DEFAULT_CRITICAL_FREE_BYTES;
539
546
  const maxUsedPercent = input.diskMaxUsedPercent ?? DEFAULT_MAX_USED_PERCENT;
540
547
  const hardMaxUsedPercent = input.diskHardMaxUsedPercent ?? DEFAULT_HARD_MAX_USED_PERCENT;
541
- const stats = statfsSync2(path67);
548
+ const stats = statfsSync2(path69);
542
549
  const freeBytes = Number(stats.bavail) * Number(stats.bsize);
543
550
  const totalBytes = Number(stats.blocks) * Number(stats.bsize);
544
551
  const usedPercent = totalBytes > 0 ? (totalBytes - freeBytes) / totalBytes * 100 : 100;
@@ -561,7 +568,7 @@ function observeRunnerDiskGate(input = {}) {
561
568
  }
562
569
  return {
563
570
  ok,
564
- path: path67,
571
+ path: path69,
565
572
  freeBytes,
566
573
  totalBytes,
567
574
  usedPercent,
@@ -1985,11 +1992,29 @@ function computeAutoMaxWorkers(totalMemBytes, opts = {}) {
1985
1992
  function readAvailableMemBytes() {
1986
1993
  return readMemAvailableBytes();
1987
1994
  }
1995
+ function pidCommandLine(pid) {
1996
+ if (!pid || process.platform !== "linux") return null;
1997
+ try {
1998
+ return readFileSync7(`/proc/${pid}/cmdline`, "utf8").replace(/\0/g, " ");
1999
+ } catch {
2000
+ return null;
2001
+ }
2002
+ }
2003
+ function workerProcessMatchesRecord(worker) {
2004
+ if (!worker.pid || process.platform !== "linux") return true;
2005
+ const cmdline = pidCommandLine(worker.pid);
2006
+ if (!cmdline) return false;
2007
+ const probes = [worker.worktreePath, worker.workerDir, worker.heartbeatPath].filter(
2008
+ (value) => typeof value === "string" && value.trim().length > 0
2009
+ );
2010
+ return probes.some((probe) => cmdline.includes(probe));
2011
+ }
1988
2012
  function isActiveHarnessWorker(worker) {
1989
2013
  if (typeof worker.completionBlocker === "string" && worker.completionBlocker.trim()) {
1990
2014
  return false;
1991
2015
  }
1992
2016
  const status = computeWorkerStatus(worker);
2017
+ if (status.alive && !workerProcessMatchesRecord(worker)) return false;
1993
2018
  return status.alive && !status.finalResult && status.attention.state !== "done";
1994
2019
  }
1995
2020
  function countActiveWorkersForRun(run) {
@@ -2140,7 +2165,7 @@ var CREDENTIALS_FILE = path8.join(CONFIG_DIR, "credentials");
2140
2165
  function loadUserConfig() {
2141
2166
  if (!existsSync9(CONFIG_FILE)) return {};
2142
2167
  try {
2143
- return JSON.parse(readFileSync7(CONFIG_FILE, "utf8"));
2168
+ return JSON.parse(readFileSync8(CONFIG_FILE, "utf8"));
2144
2169
  } catch {
2145
2170
  return {};
2146
2171
  }
@@ -2217,7 +2242,7 @@ function resolveSetupWorkerConfig(existing, args, totalMemBytes = totalmem()) {
2217
2242
  function loadCredentialsFile() {
2218
2243
  if (!existsSync9(CREDENTIALS_FILE)) return {};
2219
2244
  try {
2220
- return JSON.parse(readFileSync7(CREDENTIALS_FILE, "utf8"));
2245
+ return JSON.parse(readFileSync8(CREDENTIALS_FILE, "utf8"));
2221
2246
  } catch {
2222
2247
  return {};
2223
2248
  }
@@ -2487,15 +2512,30 @@ async function withTimeout(fn) {
2487
2512
  clearTimeout(timeout);
2488
2513
  }
2489
2514
  }
2515
+ function callbackFetchError(error) {
2516
+ return {
2517
+ ok: false,
2518
+ status: 0,
2519
+ response: {
2520
+ error: error instanceof Error ? error.message : String(error),
2521
+ timeoutMs: callbackTimeoutMs()
2522
+ }
2523
+ };
2524
+ }
2490
2525
  async function postJson(url, secret, body) {
2491
- const res = await withTimeout(
2492
- (signal) => fetch(url, {
2493
- method: "POST",
2494
- headers: buildHarnessCallbackHeaders(secret),
2495
- body: JSON.stringify(body),
2496
- signal
2497
- })
2498
- );
2526
+ let res;
2527
+ try {
2528
+ res = await withTimeout(
2529
+ (signal) => fetch(url, {
2530
+ method: "POST",
2531
+ headers: buildHarnessCallbackHeaders(secret),
2532
+ body: JSON.stringify(body),
2533
+ signal
2534
+ })
2535
+ );
2536
+ } catch (error) {
2537
+ return callbackFetchError(error);
2538
+ }
2499
2539
  let response = null;
2500
2540
  try {
2501
2541
  response = await res.json();
@@ -2513,13 +2553,18 @@ async function postJsonWithCredentialRefresh(url, secret, body, opts) {
2513
2553
  return { ...retry, refreshedAuth: true };
2514
2554
  }
2515
2555
  async function getJson(url, secret) {
2516
- const res = await withTimeout(
2517
- (signal) => fetch(url, {
2518
- method: "GET",
2519
- headers: buildHarnessCallbackHeaders(secret),
2520
- signal
2521
- })
2522
- );
2556
+ let res;
2557
+ try {
2558
+ res = await withTimeout(
2559
+ (signal) => fetch(url, {
2560
+ method: "GET",
2561
+ headers: buildHarnessCallbackHeaders(secret),
2562
+ signal
2563
+ })
2564
+ );
2565
+ } catch (error) {
2566
+ return callbackFetchError(error);
2567
+ }
2523
2568
  let response = null;
2524
2569
  try {
2525
2570
  response = await res.json();
@@ -2529,41 +2574,6 @@ async function getJson(url, secret) {
2529
2574
  return { ok: res.ok, status: res.status, response };
2530
2575
  }
2531
2576
 
2532
- // src/dispatch-lane-normalization.ts
2533
- function trimLower(value) {
2534
- return (value ?? "").trim().toLowerCase();
2535
- }
2536
- function roleLaneToDispatchLane(roleLane) {
2537
- switch (trimLower(roleLane)) {
2538
- case "implementer":
2539
- case "repair_implementer":
2540
- case "plan_author":
2541
- case "runtime_verifier":
2542
- return "implementation";
2543
- case "plan_reviewer":
2544
- case "report_reviewer":
2545
- case "deep_reviewer":
2546
- return "review";
2547
- default:
2548
- return null;
2549
- }
2550
- }
2551
- function normalizeDispatchNextLaneFilter(raw) {
2552
- const key = trimLower(raw);
2553
- if (!key) return void 0;
2554
- if (key === "implementation" || key === "review" || key === "landing" || key === "any") {
2555
- return key;
2556
- }
2557
- const mapped = roleLaneToDispatchLane(key);
2558
- if (mapped) return mapped;
2559
- if (key === "implement" || key === "repair" || key === "coding") return "implementation";
2560
- if (key === "land" || key === "merge") return "landing";
2561
- return void 0;
2562
- }
2563
- function resolveDispatchNextLaneFilter(raw) {
2564
- return normalizeDispatchNextLaneFilter(raw) ?? "any";
2565
- }
2566
-
2567
2577
  // src/worker-persona-catalog.ts
2568
2578
  var WORKER_PERSONA_CATALOG = [
2569
2579
  {
@@ -2666,6 +2676,41 @@ function workerPersonaReviewSlugs() {
2666
2676
  );
2667
2677
  }
2668
2678
 
2679
+ // src/dispatch-lane-normalization.ts
2680
+ function trimLower(value) {
2681
+ return (value ?? "").trim().toLowerCase();
2682
+ }
2683
+ function roleLaneToDispatchLane(roleLane) {
2684
+ switch (trimLower(roleLane)) {
2685
+ case "implementer":
2686
+ case "repair_implementer":
2687
+ case "plan_author":
2688
+ case "runtime_verifier":
2689
+ return "implementation";
2690
+ case "plan_reviewer":
2691
+ case "report_reviewer":
2692
+ case "deep_reviewer":
2693
+ return "review";
2694
+ default:
2695
+ return null;
2696
+ }
2697
+ }
2698
+ function normalizeDispatchNextLaneFilter(raw) {
2699
+ const key = trimLower(raw);
2700
+ if (!key) return void 0;
2701
+ if (key === "implementation" || key === "review" || key === "landing" || key === "any") {
2702
+ return key;
2703
+ }
2704
+ const mapped = roleLaneToDispatchLane(key);
2705
+ if (mapped) return mapped;
2706
+ if (key === "implement" || key === "repair" || key === "coding") return "implementation";
2707
+ if (key === "land" || key === "merge") return "landing";
2708
+ return void 0;
2709
+ }
2710
+ function resolveDispatchNextLaneFilter(raw) {
2711
+ return normalizeDispatchNextLaneFilter(raw) ?? "any";
2712
+ }
2713
+
2669
2714
  // src/model-routing-task-enrich.ts
2670
2715
  function taskString(task, key) {
2671
2716
  const v = task[key];
@@ -3123,7 +3168,7 @@ function probeCursorOAuthBinding(nowIso = (/* @__PURE__ */ new Date()).toISOStri
3123
3168
  }
3124
3169
 
3125
3170
  // src/orchestration-providers/hermes-cli-adapter.ts
3126
- import { existsSync as existsSync14, readFileSync as readFileSync8 } from "node:fs";
3171
+ import { existsSync as existsSync14, readFileSync as readFileSync9 } from "node:fs";
3127
3172
  import { homedir as homedir9 } from "node:os";
3128
3173
  import path13 from "node:path";
3129
3174
  var PROFILE_ENV_KEYS = [
@@ -3139,7 +3184,7 @@ function hermesProfileEnvPath() {
3139
3184
  }
3140
3185
  function profileEnvKeyPresence(envPath) {
3141
3186
  try {
3142
- const text = readFileSync8(envPath, "utf8");
3187
+ const text = readFileSync9(envPath, "utf8");
3143
3188
  const present = [];
3144
3189
  for (const key of PROFILE_ENV_KEYS) {
3145
3190
  const re = new RegExp(`^${key}=`, "m");
@@ -4069,14 +4114,15 @@ function buildPrompt(input) {
4069
4114
  input.planId ? `Active planId: ${input.planId}${input.taskId ? ` \xB7 taskId: ${input.taskId}` : ""}` : "No planId on this worker \u2014 still emit progress when you touch plan-scoped work."
4070
4115
  ];
4071
4116
  const mergeGateLines = compact ? [
4072
- "Merge-gate cost control: do not use Vercel previews/builds for PR verification. Run `node scripts/agent-os-pr-merge-gate.mjs --pr <url> --agent-os-id <id>` (or `verify-pr-local.mjs --from-pr` + POST pr-merge-gate/refresh) before any GitHub Actions run; request merge-gate only via refresh then POST pr-merge-gate/request-run (one Actions run per PR head unless human approves extra)."
4117
+ "Merge-gate cost control: do not use Vercel previews/builds for PR verification. Run `node scripts/agent-os-pr-merge-gate.mjs --pr <url> --agent-os-id <id>` (or `verify-pr-local.mjs --from-pr` + POST pr-merge-gate/refresh) before any GitHub Actions run; request merge-gate only via refresh then POST pr-merge-gate/request-run (one Actions run per PR head unless human approves extra). Per-PR Vercel preview builds are off by default \u2014 production deploy runs via release batch after merge."
4073
4118
  ] : [
4074
4119
  "GitHub Actions merge-gate cost control (Kynver/Hermes PRs):",
4075
4120
  "- Prefer local cached package verification (`node scripts/verify-pr-local.mjs --emit-json`) before GitHub Actions. Do not use Vercel previews/builds as PR evidence.",
4076
4121
  "- Do not push empty commits to re-trigger CI. One budgeted merge-gate Actions run per PR candidate (head SHA) unless a human approves extra.",
4077
4122
  "- Record evidence: POST `/api/agent-os/by-id/<agentOsId>/pr-merge-gate/refresh` with prUrl + local payloads.",
4078
4123
  "- Request the final Actions run only when local verification is green: POST `.../pr-merge-gate/request-run` (applies `merge-gate` label).",
4079
- "- Empty failed Actions jobs (no runner/steps/logs) are infra/quota \u2014 do not enter repair loops; escalate to operator."
4124
+ "- Empty failed Actions jobs (no runner/steps/logs) are infra/quota \u2014 do not enter repair loops; escalate to operator.",
4125
+ "- After merge, landed PRs accumulate into a release batch; run batch verification then manual Vercel production deploy from Command Center (release train). PR landing completes with: verification complete, awaiting release batch/deploy."
4080
4126
  ];
4081
4127
  const planArtifactLines = compact ? [
4082
4128
  "Plan artifacts: when authoring/revising docs/superpowers/plans/, open a GitHub PR early and iterate from that PR branch; do not leave the canonical plan only in the harness worktree."
@@ -4099,7 +4145,7 @@ function buildPrompt(input) {
4099
4145
  "Completion handoff (required): before you stop, ensure the harness records a final result \u2014 summarize outcome in your last message and append a heartbeat line with phase `complete`. If you leave uncommitted changes or committed work without a PR, the orchestrator blocks completion until a GitHub PR exists (or you discard/commit cleanly). One-off helper scripts must be removed (`kynver worker discard-disposable --path <file>`) or committed before completion \u2014 maintenance/board-drain workers are not exempt. Exiting with only dirty files and no PR routes to salvage review, not production review.",
4100
4146
  "PR-ready handoff: for substantial implementation work, commit, push, and open a GitHub PR (draft OK) on your branch before finishing \u2014 or rely on the harness to run `gh pr create` at completion when `gh` is authenticated.",
4101
4147
  "Expert review / production-review workers (Dalton/Lorentz, plan-review-task, scheduledJob reviewer children): do NOT open new implementation PRs \u2014 review the parent task's existing PR and record reviewVerdict in finalResult; landing-contract targetPrReconciliation does not apply.",
4102
- "Worker resource guard: do not run full monorepo verification (`npm run typecheck`, `npm run build`, or equivalent) from this worker lane unless an operator explicitly requests it. Use targeted checks for touched paths and rely on CI/operator lanes for heavy gates.",
4148
+ "Worker resource guard: do not run full monorepo verification (`npm run typecheck`, `npm run build`, or equivalent) from this worker lane unless an operator explicitly requests it. Use targeted checks for touched paths and rely on CI/operator lanes for heavy gates. When heavy verification is required, route through `node scripts/verify-pr-local.mjs` or `kynver harness verify` \u2014 they acquire the global heavy-verification lease so parallel workers do not launch simultaneous typechecks.",
4103
4149
  "npm publish boundary: do not run `npm publish`, do not republish `@kynver-app/*` packages, and do not block on an operator to publish. When you need newer runtime code than npm, use this repo checkout (`npm run kynver:build`, `npm run kynver`) and record evidence: packages/kynver-runtime/package.json version + git ref in your completion report.",
4104
4150
  "If verification fails (including OOM), append a heartbeat line immediately with the last command, failure reason, dirty-file status, commit/PR handoff state, and next action so recovery does not require log spelunking.",
4105
4151
  "Landing-wrapper cleanup on a git ref: use `node scripts/agent-os-land-pr-cleanup-verify.mjs --ref origin/main` (JSON, exit 0). Do not use `git show <ref>:scripts/agent-os-land-pr.mjs | rg \u2026` \u2014 ripgrep exit 1 when markers are absent is reported as a failed command in Telegram/status tooling.",
@@ -4975,8 +5021,8 @@ function dirtyPathsCoveredByDisposableRemoval(changedFiles, removed) {
4975
5021
  if (removed.length === 0) return false;
4976
5022
  const removedSet = new Set(removed.map((p) => normalizeRelativePath(p)));
4977
5023
  return material.every((line) => {
4978
- const path67 = normalizeRelativePath(pathFromGitStatusLine(line));
4979
- return removedSet.has(path67);
5024
+ const path69 = normalizeRelativePath(pathFromGitStatusLine(line));
5025
+ return removedSet.has(path69);
4980
5026
  });
4981
5027
  }
4982
5028
 
@@ -6270,7 +6316,7 @@ function collectRunActiveHarnessWorkers(runId) {
6270
6316
  path22.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
6271
6317
  void 0
6272
6318
  );
6273
- if (!worker?.taskId || !isPidAlive(worker.pid)) continue;
6319
+ if (!worker?.taskId || !workerProcessMatchesRecord(worker)) continue;
6274
6320
  out.push({
6275
6321
  runId: run.id,
6276
6322
  workerName: name,
@@ -6509,7 +6555,7 @@ function isTmpOnlyPath(filePath) {
6509
6555
  // src/plan-persist/outbox-store.ts
6510
6556
  import {
6511
6557
  existsSync as existsSync21,
6512
- readFileSync as readFileSync9,
6558
+ readFileSync as readFileSync10,
6513
6559
  renameSync,
6514
6560
  readdirSync as readdirSync5,
6515
6561
  writeFileSync as writeFileSync3,
@@ -6537,7 +6583,7 @@ function findOutboxByIdempotencyKey(key) {
6537
6583
  function readOutboxItem(jsonPath) {
6538
6584
  if (!existsSync21(jsonPath)) return null;
6539
6585
  try {
6540
- return JSON.parse(readFileSync9(jsonPath, "utf8"));
6586
+ return JSON.parse(readFileSync10(jsonPath, "utf8"));
6541
6587
  } catch {
6542
6588
  return null;
6543
6589
  }
@@ -6545,7 +6591,7 @@ function readOutboxItem(jsonPath) {
6545
6591
  function readOutboxBody(item) {
6546
6592
  const { outboxDir } = ensurePlanOutboxDirs();
6547
6593
  const bodyFile = path24.join(outboxDir, item.bodyPath);
6548
- return readFileSync9(bodyFile, "utf8");
6594
+ return readFileSync10(bodyFile, "utf8");
6549
6595
  }
6550
6596
  function writeOutboxItem(input, opts) {
6551
6597
  const { outboxDir } = ensurePlanOutboxDirs();
@@ -7027,7 +7073,7 @@ async function dispatchRun(args) {
7027
7073
  runnerPresence,
7028
7074
  harnessBoardSnapshot: buildRunBoard(run.id),
7029
7075
  ...args.lane ? { lane: resolveDispatchNextLaneFilter(String(args.lane)) } : {},
7030
- executor: args.executor ? String(args.executor) : "harness",
7076
+ ...args.executor ? { executor: String(args.executor) } : {},
7031
7077
  ...args.diskPath ? { diskPath: String(args.diskPath) } : {},
7032
7078
  ...args.targetTaskId ? { targetTaskId: String(args.targetTaskId) } : {},
7033
7079
  ...!args.targetTaskId && args.targetTaskIds ? {
@@ -7366,7 +7412,7 @@ import { existsSync as existsSync25, mkdirSync as mkdirSync6 } from "node:fs";
7366
7412
  import path34 from "node:path";
7367
7413
 
7368
7414
  // src/run-list.ts
7369
- import { existsSync as existsSync24, readFileSync as readFileSync10 } from "node:fs";
7415
+ import { existsSync as existsSync24, readFileSync as readFileSync11 } from "node:fs";
7370
7416
  import path33 from "node:path";
7371
7417
 
7372
7418
  // src/stale-reconcile.ts
@@ -8085,7 +8131,7 @@ function reconcileRunsCli() {
8085
8131
  function heartbeatByteLength(heartbeatPath) {
8086
8132
  if (!heartbeatPath || !existsSync24(heartbeatPath)) return 0;
8087
8133
  try {
8088
- return readFileSync10(heartbeatPath, "utf8").trim().length;
8134
+ return readFileSync11(heartbeatPath, "utf8").trim().length;
8089
8135
  } catch {
8090
8136
  return 0;
8091
8137
  }
@@ -8420,12 +8466,12 @@ async function fireKynverCronJob(input) {
8420
8466
  }
8421
8467
 
8422
8468
  // src/cron/cron-lock.ts
8423
- import { closeSync as closeSync6, existsSync as existsSync28, openSync as openSync6, readFileSync as readFileSync11, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "node:fs";
8469
+ import { closeSync as closeSync6, existsSync as existsSync28, openSync as openSync6, readFileSync as readFileSync12, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "node:fs";
8424
8470
  var STALE_LOCK_MS = 10 * 6e4;
8425
8471
  function readLockInfo(lockPath) {
8426
8472
  if (!existsSync28(lockPath)) return null;
8427
8473
  try {
8428
- const parsed = JSON.parse(readFileSync11(lockPath, "utf8"));
8474
+ const parsed = JSON.parse(readFileSync12(lockPath, "utf8"));
8429
8475
  if (typeof parsed.pid === "number" && typeof parsed.at === "string") return parsed;
8430
8476
  } catch {
8431
8477
  return null;
@@ -8793,7 +8839,7 @@ async function runKynverCronTick(opts = {}) {
8793
8839
  }
8794
8840
 
8795
8841
  // src/pipeline-tick.ts
8796
- import path54 from "node:path";
8842
+ import path55 from "node:path";
8797
8843
 
8798
8844
  // src/pipeline-dispatch.ts
8799
8845
  var RESERVED_REVIEW_STARTS = 1;
@@ -9201,11 +9247,11 @@ var LIVE_SKIP_REASONS = /* @__PURE__ */ new Set([
9201
9247
  function collectPreservedLivePaths(actions, skips) {
9202
9248
  const out = [];
9203
9249
  const seen = /* @__PURE__ */ new Set();
9204
- const push = (path67, reason, detail) => {
9205
- const key = `${path67}\0${reason}`;
9250
+ const push = (path69, reason, detail) => {
9251
+ const key = `${path69}\0${reason}`;
9206
9252
  if (seen.has(key) || out.length >= MAX_PRESERVED_LIVE_PATH_SAMPLES) return;
9207
9253
  seen.add(key);
9208
- out.push({ path: path67, reason, ...detail ? { detail } : {} });
9254
+ out.push({ path: path69, reason, ...detail ? { detail } : {} });
9209
9255
  };
9210
9256
  for (const skip2 of skips) {
9211
9257
  if (!LIVE_SKIP_REASONS.has(skip2.reason)) continue;
@@ -10683,93 +10729,293 @@ function isPipelineCleanupEnabled() {
10683
10729
  // src/installed-package-versions.ts
10684
10730
  import { readFile } from "node:fs/promises";
10685
10731
  import { homedir as homedir13 } from "node:os";
10732
+ import path54 from "node:path";
10733
+
10734
+ // src/memory-cost-package-version-guard.ts
10735
+ import { existsSync as existsSync40, readFileSync as readFileSync13 } from "node:fs";
10686
10736
  import path53 from "node:path";
10687
- var MANAGED_PACKAGES = [
10688
- "@kynver-app/runtime",
10689
- "@kynver-app/openclaw-agent-os",
10690
- "@kynver-app/mcp-agent-os"
10691
- ];
10692
- function trim(value) {
10693
- const out = value?.trim();
10694
- return out ? out : null;
10737
+ var MEMORY_COST_PACKAGE_MIN_VERSIONS = {
10738
+ "@kynver-app/runtime": "0.1.83",
10739
+ "@kynver-app/openclaw-agent-os": "0.1.43",
10740
+ "@kynver-app/mcp-agent-os": "0.3.34"
10741
+ };
10742
+ var MEMORY_COST_MANAGED_PACKAGES = Object.keys(
10743
+ MEMORY_COST_PACKAGE_MIN_VERSIONS
10744
+ );
10745
+ var DISPLAY_NAMES = {
10746
+ "@kynver-app/runtime": "Kynver runtime",
10747
+ "@kynver-app/openclaw-agent-os": "OpenClaw AgentOS plugin",
10748
+ "@kynver-app/mcp-agent-os": "AgentOS MCP server"
10749
+ };
10750
+ var REPO_PACKAGE_JSON_RELATIVE = {
10751
+ "@kynver-app/runtime": "packages/kynver-runtime/package.json",
10752
+ "@kynver-app/openclaw-agent-os": "packages/kynver-openclaw-agent-os/package.json",
10753
+ "@kynver-app/mcp-agent-os": "packages/kynver-mcp-agent-os/package.json"
10754
+ };
10755
+ function parseSemverParts(version) {
10756
+ const core = version.trim().split("-")[0]?.split("+")[0];
10757
+ if (!core) return null;
10758
+ const parts = core.split(".");
10759
+ if (parts.length < 1 || parts.length > 3) return null;
10760
+ const nums = parts.map((p) => Number.parseInt(p, 10));
10761
+ if (nums.some((n) => !Number.isFinite(n) || n < 0)) return null;
10762
+ while (nums.length < 3) nums.push(0);
10763
+ return [nums[0], nums[1], nums[2]];
10695
10764
  }
10696
- function unique(values) {
10697
- return [...new Set(values.filter((value) => Boolean(value)))];
10765
+ function compareSemver(a, b) {
10766
+ const pa = parseSemverParts(a);
10767
+ const pb = parseSemverParts(b);
10768
+ if (!pa || !pb) return 0;
10769
+ for (let i = 0; i < 3; i += 1) {
10770
+ if (pa[i] > pb[i]) return 1;
10771
+ if (pa[i] < pb[i]) return -1;
10772
+ }
10773
+ return 0;
10698
10774
  }
10699
- function moduleRoots() {
10700
- const home = homedir13();
10701
- const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ?? path53.join(home, ".openclaw", "npm");
10702
- const npmGlobalRoot = trim(process.env.KYNVER_NPM_GLOBAL_ROOT) ?? trim(process.env.KYNVER_NPM_GLOBAL_MODULES_ROOT) ?? (trim(process.env.NPM_CONFIG_PREFIX) ? path53.join(trim(process.env.NPM_CONFIG_PREFIX), "lib", "node_modules") : path53.join(home, ".npm-global", "lib", "node_modules"));
10703
- return unique([
10704
- path53.join(openClawPrefix, "lib", "node_modules"),
10705
- path53.join(openClawPrefix, "node_modules"),
10706
- npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot : path53.join(npmGlobalRoot, "lib", "node_modules")
10707
- ]);
10775
+ function semverAtLeast(version, minimum) {
10776
+ return compareSemver(version, minimum) >= 0;
10708
10777
  }
10709
- async function readVersion(packageJsonPath) {
10778
+ function maxSemver(versions) {
10779
+ let best = null;
10780
+ for (const version of versions) {
10781
+ if (!best || compareSemver(version, best) > 0) best = version;
10782
+ }
10783
+ return best;
10784
+ }
10785
+ function readPackageJsonVersion(packageJsonPath) {
10710
10786
  try {
10711
- const parsed = JSON.parse(await readFile(packageJsonPath, "utf8"));
10787
+ const parsed = JSON.parse(readFileSync13(packageJsonPath, "utf8"));
10712
10788
  return typeof parsed.version === "string" && parsed.version.trim() ? parsed.version.trim() : null;
10713
10789
  } catch {
10714
10790
  return null;
10715
10791
  }
10716
10792
  }
10717
- async function collectInstalledPackageVersions(observedAt = (/* @__PURE__ */ new Date()).toISOString()) {
10718
- const roots = moduleRoots();
10719
- const out = {};
10720
- for (const packageName of MANAGED_PACKAGES) {
10721
- for (const root of roots) {
10722
- const packageJsonPath = path53.join(root, packageName, "package.json");
10723
- const version = await readVersion(packageJsonPath);
10724
- if (!version) continue;
10725
- out[packageName] = { version, observedAt, path: packageJsonPath };
10726
- break;
10793
+ function resolveRepoRoot(cwd, explicitRepoRoot) {
10794
+ const candidates = [explicitRepoRoot, process.env.KYNVER_REPO, cwd].filter(
10795
+ (value) => Boolean(value?.trim())
10796
+ );
10797
+ for (const candidate of candidates) {
10798
+ const resolved = path53.resolve(candidate);
10799
+ if (existsSync40(path53.join(resolved, "packages/kynver-runtime/package.json")) && existsSync40(path53.join(resolved, "package.json"))) {
10800
+ return resolved;
10727
10801
  }
10728
10802
  }
10803
+ const discovered = discoverDefaultRepo({ cwd });
10804
+ return discovered?.repo ?? null;
10805
+ }
10806
+ function probeRepoPackageVersions(input = {}) {
10807
+ const cwd = input.cwd ?? process.cwd();
10808
+ const repoRoot = resolveRepoRoot(cwd, input.repoRoot);
10809
+ if (!repoRoot) return {};
10810
+ const out = {};
10811
+ for (const packageName of MEMORY_COST_MANAGED_PACKAGES) {
10812
+ const packageJsonPath = path53.join(repoRoot, REPO_PACKAGE_JSON_RELATIVE[packageName]);
10813
+ const version = readPackageJsonVersion(packageJsonPath);
10814
+ if (!version) continue;
10815
+ out[packageName] = { version, source: "repo", path: packageJsonPath };
10816
+ }
10729
10817
  return out;
10730
10818
  }
10731
-
10732
- // src/pipeline-tick.ts
10733
- async function completeFinishedWorkers(runId, args) {
10734
- const run = loadRun(runId);
10735
- const outcomes = [];
10736
- for (const name of Object.keys(run.workers || {})) {
10737
- const worker = readJson(
10738
- path54.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
10739
- void 0
10819
+ function repoSourceCommands(packageName) {
10820
+ if (packageName === "@kynver-app/runtime") {
10821
+ return ["npm run kynver:build", "npm run kynver"];
10822
+ }
10823
+ return [`npm run build -w ${packageName}`];
10824
+ }
10825
+ function buildRemediation(input) {
10826
+ const { packageName, minimumVersion, effectiveVersion, effectiveSource, repoVersion } = input;
10827
+ const lines = [];
10828
+ if (repoVersion && semverAtLeast(repoVersion, minimumVersion)) {
10829
+ lines.push(
10830
+ `Use the monorepo checkout (${repoVersion}) instead of a stale npm install: ${repoSourceCommands(packageName).join("; ")}.`
10740
10831
  );
10741
- if (!worker?.taskId || worker.localOnly) continue;
10742
- if (hasTerminalCompletionAck(worker)) {
10743
- outcomes.push({ worker: name, ok: true, taskId: worker.taskId ?? null, skipped: true });
10744
- continue;
10745
- }
10746
- const status = computeWorkerStatus(worker);
10747
- if (!isFinishedWorkerStatus(status)) continue;
10748
- const exitedSalvage = assessExitedWorkerSalvage({
10749
- alive: status.alive,
10750
- finalResult: status.finalResult,
10751
- changedFiles: status.changedFiles,
10752
- gitAncestry: status.gitAncestry
10753
- });
10754
- if (!worker.dispatched && !status.finalResult && !exitedSalvage?.salvageable) continue;
10755
- const result = await tryCompleteWorker({
10756
- run: runId,
10757
- name,
10758
- agentOsId: String(args.agentOsId || worker.agentOsId || ""),
10759
- ...args
10760
- });
10761
- outcomes.push({ worker: name, ok: result.ok, taskId: worker.taskId ?? null });
10832
+ lines.push("Do not publish npm packages or wait on an operator release.");
10833
+ return lines;
10762
10834
  }
10763
- return outcomes;
10835
+ if (effectiveVersion) {
10836
+ lines.push(
10837
+ `Upgrade ${packageName} from ${effectiveVersion} to >= ${minimumVersion} (npm install -g ${packageName}@latest or align OpenClaw npm prefix).`
10838
+ );
10839
+ } else {
10840
+ lines.push(`Install ${packageName} >= ${minimumVersion} before running memory-heavy AgentOS paths.`);
10841
+ }
10842
+ if (packageName === "@kynver-app/runtime") {
10843
+ lines.push("Repo-source alternative: npm run kynver:build && npm run kynver");
10844
+ }
10845
+ if (effectiveSource === "installed" && repoVersion) {
10846
+ lines.push(`Repo checkout reports ${repoVersion}; rebuild/link repo source if you develop from the monorepo.`);
10847
+ }
10848
+ return lines;
10764
10849
  }
10765
- async function postOperatorTick(agentOsId, runId, resourceGate, args, harnessCleanup) {
10766
- const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
10767
- const secret = await resolveCallbackSecretWithMint(args.secret ? String(args.secret) : void 0, agentOsId, { baseUrl: base });
10768
- const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/operator/tick`;
10769
- const packageVersions = await collectInstalledPackageVersions();
10770
- const activeHarnessWorkers = collectRunActiveHarnessWorkers(runId);
10771
- const res = await postJson(url, secret, {
10772
- agentOsId,
10850
+ function pickEffectiveCandidate(candidates) {
10851
+ if (candidates.length === 0) return { version: null, source: "unknown" };
10852
+ const best = maxSemver(candidates.map((candidate) => candidate.version));
10853
+ if (!best) return { version: null, source: "unknown" };
10854
+ const winner = candidates.find((candidate) => candidate.version === best) ?? candidates[0];
10855
+ return { version: winner.version, source: winner.source };
10856
+ }
10857
+ function evaluateMemoryCostPackageVersionGuard(input = {}) {
10858
+ const normalize2 = (value, fallbackSource) => {
10859
+ if (!value) return null;
10860
+ if (typeof value === "string") return { version: value, source: fallbackSource };
10861
+ return value;
10862
+ };
10863
+ const packages = MEMORY_COST_MANAGED_PACKAGES.map((packageName) => {
10864
+ const minimumVersion = MEMORY_COST_PACKAGE_MIN_VERSIONS[packageName];
10865
+ const candidates = [];
10866
+ const installed = normalize2(input.installed?.[packageName], "installed");
10867
+ const repo = normalize2(input.repo?.[packageName], "repo");
10868
+ const self = normalize2(input.self?.[packageName], "self");
10869
+ if (installed) candidates.push(installed);
10870
+ if (repo) candidates.push(repo);
10871
+ if (self) candidates.push(self);
10872
+ const { version: effectiveVersion, source: effectiveSource } = pickEffectiveCandidate(candidates);
10873
+ const repoVersion = repo?.version ?? null;
10874
+ const ok2 = effectiveVersion ? semverAtLeast(effectiveVersion, minimumVersion) : false;
10875
+ const remediation = ok2 ? [] : buildRemediation({
10876
+ packageName,
10877
+ minimumVersion,
10878
+ effectiveVersion,
10879
+ effectiveSource,
10880
+ repoVersion
10881
+ });
10882
+ const summary2 = ok2 ? `${DISPLAY_NAMES[packageName]} ${effectiveVersion} meets memory-cost minimum ${minimumVersion} (${effectiveSource}).` : `${DISPLAY_NAMES[packageName]} is below memory-cost minimum ${minimumVersion}` + (effectiveVersion ? ` (effective ${effectiveVersion} via ${effectiveSource})` : " (no version detected)") + ".";
10883
+ return {
10884
+ packageName,
10885
+ displayName: DISPLAY_NAMES[packageName],
10886
+ minimumVersion,
10887
+ effectiveVersion,
10888
+ effectiveSource,
10889
+ ok: ok2,
10890
+ summary: summary2,
10891
+ remediation
10892
+ };
10893
+ });
10894
+ const violations = packages.filter((row) => !row.ok);
10895
+ const ok = violations.length === 0;
10896
+ const summary = ok ? "All managed AgentOS packages meet memory-cost minimum versions." : `Memory-cost package guard blocked ${violations.length} stale package(s): ${violations.map((row) => `${row.packageName} < ${row.minimumVersion}`).join("; ")}.`;
10897
+ return { ok, summary, packages };
10898
+ }
10899
+ var MemoryCostPackageVersionGuardError = class extends Error {
10900
+ result;
10901
+ constructor(result) {
10902
+ const lines = [
10903
+ result.summary,
10904
+ ...result.packages.filter((row) => !row.ok).flatMap((row) => [`- ${row.summary}`, ...row.remediation.map((line) => ` \u2192 ${line}`)])
10905
+ ];
10906
+ super(lines.join("\n"));
10907
+ this.name = "MemoryCostPackageVersionGuardError";
10908
+ this.result = result;
10909
+ }
10910
+ };
10911
+ function assertMemoryCostPackageVersionGuard(input = {}) {
10912
+ const result = evaluateMemoryCostPackageVersionGuard(input);
10913
+ if (!result.ok) throw new MemoryCostPackageVersionGuardError(result);
10914
+ return result;
10915
+ }
10916
+
10917
+ // src/installed-package-versions.ts
10918
+ var MANAGED_PACKAGES = [
10919
+ "@kynver-app/runtime",
10920
+ "@kynver-app/openclaw-agent-os",
10921
+ "@kynver-app/mcp-agent-os"
10922
+ ];
10923
+ function trim(value) {
10924
+ const out = value?.trim();
10925
+ return out ? out : null;
10926
+ }
10927
+ function unique(values) {
10928
+ return [...new Set(values.filter((value) => Boolean(value)))];
10929
+ }
10930
+ function moduleRoots() {
10931
+ const home = homedir13();
10932
+ const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ?? path54.join(home, ".openclaw", "npm");
10933
+ const npmGlobalRoot = trim(process.env.KYNVER_NPM_GLOBAL_ROOT) ?? trim(process.env.KYNVER_NPM_GLOBAL_MODULES_ROOT) ?? (trim(process.env.NPM_CONFIG_PREFIX) ? path54.join(trim(process.env.NPM_CONFIG_PREFIX), "lib", "node_modules") : path54.join(home, ".npm-global", "lib", "node_modules"));
10934
+ return unique([
10935
+ path54.join(openClawPrefix, "lib", "node_modules"),
10936
+ path54.join(openClawPrefix, "node_modules"),
10937
+ npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot : path54.join(npmGlobalRoot, "lib", "node_modules")
10938
+ ]);
10939
+ }
10940
+ async function readVersion(packageJsonPath) {
10941
+ try {
10942
+ const parsed = JSON.parse(await readFile(packageJsonPath, "utf8"));
10943
+ return typeof parsed.version === "string" && parsed.version.trim() ? parsed.version.trim() : null;
10944
+ } catch {
10945
+ return null;
10946
+ }
10947
+ }
10948
+ function installedPackageJsonCandidates(packageName) {
10949
+ const roots = moduleRoots();
10950
+ const seen = /* @__PURE__ */ new Set();
10951
+ const out = [];
10952
+ for (const root of roots) {
10953
+ const candidate = path54.join(root, packageName, "package.json");
10954
+ if (seen.has(candidate)) continue;
10955
+ seen.add(candidate);
10956
+ out.push(candidate);
10957
+ }
10958
+ return out;
10959
+ }
10960
+ async function collectInstalledPackageVersions(observedAt = (/* @__PURE__ */ new Date()).toISOString()) {
10961
+ const out = {};
10962
+ for (const packageName of MANAGED_PACKAGES) {
10963
+ let best = null;
10964
+ for (const packageJsonPath of installedPackageJsonCandidates(packageName)) {
10965
+ const version = await readVersion(packageJsonPath);
10966
+ if (!version) continue;
10967
+ if (!best || compareSemver(version, best.version) > 0) {
10968
+ best = { version, path: packageJsonPath };
10969
+ }
10970
+ }
10971
+ if (best) {
10972
+ out[packageName] = { version: best.version, observedAt, path: best.path };
10973
+ }
10974
+ }
10975
+ return out;
10976
+ }
10977
+
10978
+ // src/pipeline-tick.ts
10979
+ async function completeFinishedWorkers(runId, args) {
10980
+ const run = loadRun(runId);
10981
+ const outcomes = [];
10982
+ for (const name of Object.keys(run.workers || {})) {
10983
+ const worker = readJson(
10984
+ path55.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
10985
+ void 0
10986
+ );
10987
+ if (!worker?.taskId || worker.localOnly) continue;
10988
+ if (hasTerminalCompletionAck(worker)) {
10989
+ outcomes.push({ worker: name, ok: true, taskId: worker.taskId ?? null, skipped: true });
10990
+ continue;
10991
+ }
10992
+ const status = computeWorkerStatus(worker);
10993
+ if (!isFinishedWorkerStatus(status)) continue;
10994
+ const exitedSalvage = assessExitedWorkerSalvage({
10995
+ alive: status.alive,
10996
+ finalResult: status.finalResult,
10997
+ changedFiles: status.changedFiles,
10998
+ gitAncestry: status.gitAncestry
10999
+ });
11000
+ if (!worker.dispatched && !status.finalResult && !exitedSalvage?.salvageable) continue;
11001
+ const result = await tryCompleteWorker({
11002
+ run: runId,
11003
+ name,
11004
+ agentOsId: String(args.agentOsId || worker.agentOsId || ""),
11005
+ ...args
11006
+ });
11007
+ outcomes.push({ worker: name, ok: result.ok, taskId: worker.taskId ?? null });
11008
+ }
11009
+ return outcomes;
11010
+ }
11011
+ async function postOperatorTick(agentOsId, runId, resourceGate, args, harnessCleanup) {
11012
+ const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
11013
+ const secret = await resolveCallbackSecretWithMint(args.secret ? String(args.secret) : void 0, agentOsId, { baseUrl: base });
11014
+ const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/operator/tick`;
11015
+ const packageVersions = await collectInstalledPackageVersions();
11016
+ const activeHarnessWorkers = collectRunActiveHarnessWorkers(runId);
11017
+ const res = await postJson(url, secret, {
11018
+ agentOsId,
10773
11019
  runId,
10774
11020
  ingestHarness: true,
10775
11021
  harnessBoardSnapshot: buildRunBoard(runId),
@@ -10939,17 +11185,17 @@ async function runDaemon(args) {
10939
11185
  idleStreak = 0;
10940
11186
  }
10941
11187
  const backoff = idleStreak >= MAX_IDLE_STREAK ? IDLE_INTERVAL_MS : intervalMs;
10942
- sleepMs(backoff);
11188
+ await sleepMs(backoff);
10943
11189
  } catch (error) {
10944
11190
  console.error(JSON.stringify({ event: "daemon_tick_error", error: error.message }));
10945
- sleepMs(intervalMs);
11191
+ await sleepMs(intervalMs);
10946
11192
  }
10947
11193
  }
10948
11194
  console.error(JSON.stringify({ event: "daemon_stop", runId, agentOsId }));
10949
11195
  }
10950
11196
 
10951
11197
  // src/plan-progress.ts
10952
- import path56 from "node:path";
11198
+ import path59 from "node:path";
10953
11199
 
10954
11200
  // src/bounded-build/constants.ts
10955
11201
  var DEFAULT_BUILD_MEM_BUDGET_BYTES = 1536 * 1024 * 1024;
@@ -11079,14 +11325,235 @@ function waitForBuildAdmission(timeoutMs, pollMs = 2e3, opts = {}) {
11079
11325
  }
11080
11326
 
11081
11327
  // src/bounded-build/exec.ts
11328
+ import { spawnSync as spawnSync8 } from "node:child_process";
11329
+
11330
+ // src/heavy-verification/slot.ts
11331
+ import {
11332
+ closeSync as closeSync7,
11333
+ existsSync as existsSync41,
11334
+ mkdirSync as mkdirSync8,
11335
+ openSync as openSync7,
11336
+ readdirSync as readdirSync15,
11337
+ readFileSync as readFileSync14,
11338
+ unlinkSync as unlinkSync4,
11339
+ writeFileSync as writeFileSync5
11340
+ } from "node:fs";
11341
+ import path57 from "node:path";
11342
+
11343
+ // src/heavy-verification/paths.ts
11344
+ import { mkdirSync as mkdirSync7 } from "node:fs";
11345
+ import path56 from "node:path";
11346
+ function resolveHeavyVerificationRoot() {
11347
+ return path56.join(resolveKynverStateRoot(), "heavy-verification");
11348
+ }
11349
+ function heavyVerificationSlotsDir() {
11350
+ return path56.join(resolveHeavyVerificationRoot(), "slots");
11351
+ }
11352
+ function ensureHeavyVerificationDirs() {
11353
+ const dir = heavyVerificationSlotsDir();
11354
+ mkdirSync7(dir, { recursive: true });
11355
+ return dir;
11356
+ }
11357
+
11358
+ // src/heavy-verification/slot.ts
11359
+ var DEFAULT_HEAVY_VERIFICATION_STALE_MS = 2 * 60 * 6e4;
11360
+ var DEFAULT_HEAVY_VERIFICATION_MAX_CONCURRENT = 1;
11361
+ function positiveInt5(value, fallback) {
11362
+ const n = Number(value);
11363
+ if (!Number.isFinite(n) || n <= 0) return fallback;
11364
+ return Math.floor(n);
11365
+ }
11366
+ function isHeavyVerificationGateSkipped() {
11367
+ const v = process.env.KYNVER_HEAVY_VERIFICATION_SKIP?.trim().toLowerCase();
11368
+ return v === "1" || v === "true" || v === "yes";
11369
+ }
11370
+ function resolveHeavyVerificationMaxConcurrent() {
11371
+ const env = process.env.KYNVER_HEAVY_VERIFICATION_MAX_CONCURRENT;
11372
+ if (env) return positiveInt5(env, DEFAULT_HEAVY_VERIFICATION_MAX_CONCURRENT);
11373
+ return DEFAULT_HEAVY_VERIFICATION_MAX_CONCURRENT;
11374
+ }
11375
+ function indexedSlotId(index) {
11376
+ return `slot-${index}`;
11377
+ }
11378
+ function slotFilePath(slotId, slotsDir = heavyVerificationSlotsDir()) {
11379
+ return path57.join(slotsDir, `${slotId}.json`);
11380
+ }
11381
+ function readSlotRecord(filePath) {
11382
+ if (!existsSync41(filePath)) return null;
11383
+ try {
11384
+ const parsed = JSON.parse(readFileSync14(filePath, "utf8"));
11385
+ if (typeof parsed.slotId === "string" && typeof parsed.pid === "number" && typeof parsed.acquiredAt === "string" && typeof parsed.command === "string") {
11386
+ return parsed;
11387
+ }
11388
+ } catch {
11389
+ return null;
11390
+ }
11391
+ return null;
11392
+ }
11393
+ function slotIsStale(record, staleMs = DEFAULT_HEAVY_VERIFICATION_STALE_MS) {
11394
+ if (!record) return true;
11395
+ if (!isPidAlive(record.pid)) return true;
11396
+ const atMs = Date.parse(record.acquiredAt);
11397
+ if (Number.isNaN(atMs)) return true;
11398
+ return Date.now() - atMs > staleMs;
11399
+ }
11400
+ function reclaimStaleSlot(filePath, staleMs) {
11401
+ const record = readSlotRecord(filePath);
11402
+ if (!slotIsStale(record, staleMs)) return;
11403
+ try {
11404
+ unlinkSync4(filePath);
11405
+ } catch {
11406
+ }
11407
+ }
11408
+ function ensureSlotsDir(slotsDir) {
11409
+ mkdirSync8(slotsDir, { recursive: true });
11410
+ return slotsDir;
11411
+ }
11412
+ function reclaimStaleHeavyVerificationSlots(opts = {}) {
11413
+ const slotsDir = ensureSlotsDir(opts.slotsDir ?? ensureHeavyVerificationDirs());
11414
+ const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
11415
+ let reclaimed = 0;
11416
+ for (const name of readdirSync15(slotsDir)) {
11417
+ if (!name.endsWith(".json")) continue;
11418
+ const filePath = path57.join(slotsDir, name);
11419
+ const before = existsSync41(filePath);
11420
+ reclaimStaleSlot(filePath, staleMs);
11421
+ if (before && !existsSync41(filePath)) reclaimed += 1;
11422
+ }
11423
+ return reclaimed;
11424
+ }
11425
+ function listActiveHeavyVerificationSlots(opts = {}) {
11426
+ const slotsDir = opts.slotsDir ?? ensureHeavyVerificationDirs();
11427
+ const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
11428
+ reclaimStaleHeavyVerificationSlots({ slotsDir, staleMs });
11429
+ const active = [];
11430
+ for (const name of readdirSync15(slotsDir)) {
11431
+ if (!name.endsWith(".json")) continue;
11432
+ const record = readSlotRecord(path57.join(slotsDir, name));
11433
+ if (record && !slotIsStale(record, staleMs)) active.push(record);
11434
+ }
11435
+ return active;
11436
+ }
11437
+ function countActiveHeavyVerificationSlots(opts = {}) {
11438
+ return listActiveHeavyVerificationSlots(opts).length;
11439
+ }
11440
+ function tryAcquireHeavyVerificationSlot(command, opts = {}) {
11441
+ if (isHeavyVerificationGateSkipped()) {
11442
+ return {
11443
+ admitted: true,
11444
+ slotId: null,
11445
+ activeSlots: 0,
11446
+ maxSlots: resolveHeavyVerificationMaxConcurrent(),
11447
+ reason: null
11448
+ };
11449
+ }
11450
+ const slotsDir = opts.slotsDir ?? ensureHeavyVerificationDirs();
11451
+ const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
11452
+ const maxSlots = opts.maxSlots ?? resolveHeavyVerificationMaxConcurrent();
11453
+ reclaimStaleHeavyVerificationSlots({ slotsDir, staleMs });
11454
+ for (let index = 0; index < maxSlots; index += 1) {
11455
+ const slotId = indexedSlotId(index);
11456
+ const filePath = slotFilePath(slotId, slotsDir);
11457
+ const existing = readSlotRecord(filePath);
11458
+ if (existing && slotIsStale(existing, staleMs)) {
11459
+ try {
11460
+ unlinkSync4(filePath);
11461
+ } catch {
11462
+ }
11463
+ } else if (existing && !slotIsStale(existing, staleMs)) {
11464
+ continue;
11465
+ }
11466
+ const record = {
11467
+ slotId,
11468
+ pid: process.pid,
11469
+ acquiredAt: (/* @__PURE__ */ new Date()).toISOString(),
11470
+ command
11471
+ };
11472
+ try {
11473
+ const fd = openSync7(filePath, "wx");
11474
+ writeFileSync5(fd, JSON.stringify(record, null, 2), "utf8");
11475
+ closeSync7(fd);
11476
+ const activeSlots2 = countActiveHeavyVerificationSlots({ slotsDir, staleMs });
11477
+ return {
11478
+ admitted: true,
11479
+ slotId,
11480
+ activeSlots: activeSlots2,
11481
+ maxSlots,
11482
+ reason: null
11483
+ };
11484
+ } catch (err) {
11485
+ if (err.code === "EEXIST") {
11486
+ continue;
11487
+ }
11488
+ throw err;
11489
+ }
11490
+ }
11491
+ const activeSlots = countActiveHeavyVerificationSlots({ slotsDir, staleMs });
11492
+ return {
11493
+ admitted: false,
11494
+ slotId: null,
11495
+ activeSlots,
11496
+ maxSlots,
11497
+ reason: `heavy verification at capacity (${activeSlots}/${maxSlots} slots)`
11498
+ };
11499
+ }
11500
+ function releaseHeavyVerificationSlot(slotId, opts = {}) {
11501
+ if (!slotId) return;
11502
+ const filePath = slotFilePath(slotId, opts.slotsDir ?? heavyVerificationSlotsDir());
11503
+ try {
11504
+ unlinkSync4(filePath);
11505
+ } catch {
11506
+ }
11507
+ }
11508
+ function assessHeavyVerificationGate(command, opts = {}) {
11509
+ if (isHeavyVerificationGateSkipped()) {
11510
+ return {
11511
+ admitted: true,
11512
+ slotId: null,
11513
+ activeSlots: 0,
11514
+ maxSlots: resolveHeavyVerificationMaxConcurrent(),
11515
+ reason: null
11516
+ };
11517
+ }
11518
+ const slotsDir = opts.slotsDir ?? ensureHeavyVerificationDirs();
11519
+ const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
11520
+ const maxSlots = opts.maxSlots ?? resolveHeavyVerificationMaxConcurrent();
11521
+ reclaimStaleHeavyVerificationSlots({ slotsDir, staleMs });
11522
+ const activeSlots = countActiveHeavyVerificationSlots({ slotsDir, staleMs });
11523
+ const admitted = activeSlots < maxSlots;
11524
+ return {
11525
+ admitted,
11526
+ slotId: null,
11527
+ activeSlots,
11528
+ maxSlots,
11529
+ reason: admitted ? null : `heavy verification at capacity (${activeSlots}/${maxSlots} slots); waiting for ${command}`
11530
+ };
11531
+ }
11532
+
11533
+ // src/heavy-verification/gate.ts
11082
11534
  import { spawnSync as spawnSync7 } from "node:child_process";
11535
+ function sleepMs3(ms) {
11536
+ if (ms <= 0) return;
11537
+ spawnSync7(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
11538
+ stdio: "ignore"
11539
+ });
11540
+ }
11541
+ function waitForHeavyVerificationSlot(command, timeoutMs, pollMs = 2e3, opts = {}) {
11542
+ const deadline = Date.now() + Math.max(0, timeoutMs);
11543
+ let verdict = tryAcquireHeavyVerificationSlot(command, opts);
11544
+ while (!verdict.admitted && Date.now() < deadline) {
11545
+ sleepMs3(Math.min(pollMs, deadline - Date.now()));
11546
+ verdict = tryAcquireHeavyVerificationSlot(command, opts);
11547
+ }
11548
+ return verdict;
11549
+ }
11083
11550
 
11084
11551
  // src/harness-worktree-build-guard.ts
11085
- import path55 from "node:path";
11552
+ import path58 from "node:path";
11086
11553
  function isPathUnderHarnessWorktree(cwd) {
11087
11554
  const worktreesDir = harnessWorktreesDir(resolveHarnessRoot());
11088
- const rel = path55.relative(worktreesDir, path55.resolve(cwd));
11089
- return rel.length > 0 && !rel.startsWith("..") && !path55.isAbsolute(rel);
11555
+ const rel = path58.relative(worktreesDir, path58.resolve(cwd));
11556
+ return rel.length > 0 && !rel.startsWith("..") && !path58.isAbsolute(rel);
11090
11557
  }
11091
11558
  function assessHarnessWorktreeBuildGuard(cwd) {
11092
11559
  if (!isPathUnderHarnessWorktree(cwd)) return { ok: true };
@@ -11110,7 +11577,7 @@ function envArgv(env) {
11110
11577
  return out;
11111
11578
  }
11112
11579
  function runSpawn(argv, opts) {
11113
- const res = spawnSync7(argv[0], argv.slice(1), {
11580
+ const res = spawnSync8(argv[0], argv.slice(1), {
11114
11581
  cwd: opts.cwd,
11115
11582
  env: opts.env,
11116
11583
  encoding: "utf8",
@@ -11126,8 +11593,25 @@ function runSpawn(argv, opts) {
11126
11593
  }
11127
11594
  function runBoundedBuildCheck(input) {
11128
11595
  const waitMs = input.waitForAdmissionMs ?? 6e5;
11596
+ const verificationGate = waitMs > 0 ? waitForHeavyVerificationSlot(input.command, waitMs) : tryAcquireOrAssessVerificationGate(input.command);
11597
+ if (!verificationGate.admitted) {
11598
+ return {
11599
+ ok: false,
11600
+ exitCode: 1,
11601
+ stdout: "",
11602
+ stderr: verificationGate.reason ?? "heavy verification gate denied",
11603
+ admitted: false,
11604
+ wrappedWithSystemd: false,
11605
+ nodeOptionsFlag: formatNodeOptionsFlag(),
11606
+ admission: assessBuildAdmission(),
11607
+ verificationGate,
11608
+ command: input.command
11609
+ };
11610
+ }
11611
+ const slotId = verificationGate.slotId;
11129
11612
  const admission = waitMs > 0 ? waitForBuildAdmission(waitMs) : assessBuildAdmission();
11130
11613
  if (!admission.admitted) {
11614
+ releaseHeavyVerificationSlot(slotId);
11131
11615
  return {
11132
11616
  ok: false,
11133
11617
  exitCode: 1,
@@ -11137,11 +11621,13 @@ function runBoundedBuildCheck(input) {
11137
11621
  wrappedWithSystemd: false,
11138
11622
  nodeOptionsFlag: formatNodeOptionsFlag(),
11139
11623
  admission,
11624
+ verificationGate,
11140
11625
  command: input.command
11141
11626
  };
11142
11627
  }
11143
11628
  const worktreeGuard = assessHarnessWorktreeBuildGuard(input.cwd);
11144
11629
  if (!worktreeGuard.ok) {
11630
+ releaseHeavyVerificationSlot(slotId);
11145
11631
  return {
11146
11632
  ok: false,
11147
11633
  exitCode: 1,
@@ -11151,6 +11637,7 @@ function runBoundedBuildCheck(input) {
11151
11637
  wrappedWithSystemd: false,
11152
11638
  nodeOptionsFlag: formatNodeOptionsFlag(),
11153
11639
  admission,
11640
+ verificationGate,
11154
11641
  command: input.command
11155
11642
  };
11156
11643
  }
@@ -11183,12 +11670,19 @@ function runBoundedBuildCheck(input) {
11183
11670
  wrappedWithSystemd: useSystemd,
11184
11671
  nodeOptionsFlag,
11185
11672
  admission,
11673
+ verificationGate,
11186
11674
  command: input.command
11187
11675
  };
11188
11676
  } finally {
11189
11677
  registerBuildEnd();
11678
+ releaseHeavyVerificationSlot(slotId);
11190
11679
  }
11191
11680
  }
11681
+ function tryAcquireOrAssessVerificationGate(command) {
11682
+ const acquired = waitForHeavyVerificationSlot(command, 0);
11683
+ if (acquired.admitted) return acquired;
11684
+ return { ...assessHeavyVerificationGate(command), slotId: null };
11685
+ }
11192
11686
 
11193
11687
  // src/harness-verify.ts
11194
11688
  var DEFAULT_HARNESS_VERIFY_COMMANDS = ["npm run typecheck", "npm run test"];
@@ -11271,7 +11765,7 @@ async function emitPlanProgress(args) {
11271
11765
  }
11272
11766
  function verifyPlanLocal(args) {
11273
11767
  const worktree = required(args.worktree ? String(args.worktree) : void 0, "worktree");
11274
- const cwd = path56.resolve(worktree);
11768
+ const cwd = path59.resolve(worktree);
11275
11769
  const summary = runHarnessVerifyCommands(cwd);
11276
11770
  const emitJson = args.json === true || args.json === "true";
11277
11771
  const payload = { passed: summary.passed, worktree: cwd, steps: summary.steps };
@@ -11320,9 +11814,9 @@ async function verifyPlan(args) {
11320
11814
  }
11321
11815
 
11322
11816
  // src/harness-verify-cli.ts
11323
- import path57 from "node:path";
11817
+ import path60 from "node:path";
11324
11818
  function runHarnessVerifyCli(args) {
11325
- const cwd = path57.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
11819
+ const cwd = path60.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
11326
11820
  const emitJson = args.json === true || args.json === "true" || args.emitJson === true || args.emitJson === "true";
11327
11821
  const commands = [];
11328
11822
  const rawCmd = args.command;
@@ -11350,6 +11844,7 @@ function runHarnessVerifyCli(args) {
11350
11844
  wrappedWithSystemd: s.result.wrappedWithSystemd,
11351
11845
  nodeOptionsFlag: s.result.nodeOptionsFlag,
11352
11846
  admission: s.result.admission,
11847
+ verificationGate: s.result.verificationGate,
11353
11848
  stderr: s.result.stderr.slice(0, 4e3)
11354
11849
  }))
11355
11850
  };
@@ -11366,7 +11861,7 @@ function runHarnessVerifyCli(args) {
11366
11861
  }
11367
11862
 
11368
11863
  // src/plan-persist-cli.ts
11369
- import { readFileSync as readFileSync12 } from "node:fs";
11864
+ import { readFileSync as readFileSync15 } from "node:fs";
11370
11865
  var OPERATIONS = ["create", "add_version", "update_metadata"];
11371
11866
  var FAILURE_KINDS = [
11372
11867
  "approval_guard",
@@ -11378,7 +11873,7 @@ var FAILURE_KINDS = [
11378
11873
  function readBodyArg(args) {
11379
11874
  const bodyFile = args.bodyFile ? String(args.bodyFile) : void 0;
11380
11875
  if (bodyFile) {
11381
- return { body: readFileSync12(bodyFile, "utf8"), bodyPathHint: bodyFile };
11876
+ return { body: readFileSync15(bodyFile, "utf8"), bodyPathHint: bodyFile };
11382
11877
  }
11383
11878
  const inline = args.body ? String(args.body) : void 0;
11384
11879
  if (inline) return { body: inline };
@@ -11529,7 +12024,7 @@ function formatMonitorTickNotice(tick) {
11529
12024
  }
11530
12025
 
11531
12026
  // src/monitor/monitor.service.ts
11532
- import path59 from "node:path";
12027
+ import path62 from "node:path";
11533
12028
 
11534
12029
  // src/monitor/monitor.classify.ts
11535
12030
  function classifyWorkerHealth(input) {
@@ -11581,19 +12076,19 @@ function classifyWorkerHealth(input) {
11581
12076
  }
11582
12077
 
11583
12078
  // src/monitor/monitor.store.ts
11584
- import { existsSync as existsSync40, mkdirSync as mkdirSync7, readdirSync as readdirSync15, unlinkSync as unlinkSync4 } from "node:fs";
11585
- import path58 from "node:path";
12079
+ import { existsSync as existsSync42, mkdirSync as mkdirSync9, readdirSync as readdirSync16, unlinkSync as unlinkSync5 } from "node:fs";
12080
+ import path61 from "node:path";
11586
12081
  function monitorsDir() {
11587
12082
  const { harnessRoot } = getHarnessPaths();
11588
- const dir = path58.join(harnessRoot, "monitors");
11589
- mkdirSync7(dir, { recursive: true });
12083
+ const dir = path61.join(harnessRoot, "monitors");
12084
+ mkdirSync9(dir, { recursive: true });
11590
12085
  return dir;
11591
12086
  }
11592
12087
  function monitorIdFor(runId, workerName) {
11593
12088
  return workerName ? `${safeSlug(runId)}--${safeSlug(workerName)}` : safeSlug(runId);
11594
12089
  }
11595
12090
  function monitorPath(monitorId) {
11596
- return path58.join(monitorsDir(), `${monitorId}.json`);
12091
+ return path61.join(monitorsDir(), `${monitorId}.json`);
11597
12092
  }
11598
12093
  function loadMonitorSession(monitorId) {
11599
12094
  return readJson(monitorPath(monitorId), void 0);
@@ -11603,18 +12098,18 @@ function saveMonitorSession(session) {
11603
12098
  }
11604
12099
  function deleteMonitorSession(monitorId) {
11605
12100
  const file = monitorPath(monitorId);
11606
- if (!existsSync40(file)) return false;
11607
- unlinkSync4(file);
12101
+ if (!existsSync42(file)) return false;
12102
+ unlinkSync5(file);
11608
12103
  return true;
11609
12104
  }
11610
12105
  function listMonitorSessions() {
11611
12106
  const dir = monitorsDir();
11612
- if (!existsSync40(dir)) return [];
12107
+ if (!existsSync42(dir)) return [];
11613
12108
  const entries = [];
11614
- for (const name of readdirSync15(dir)) {
12109
+ for (const name of readdirSync16(dir)) {
11615
12110
  if (!name.endsWith(".json")) continue;
11616
12111
  const session = readJson(
11617
- path58.join(dir, name),
12112
+ path61.join(dir, name),
11618
12113
  void 0
11619
12114
  );
11620
12115
  if (!session?.monitorId) continue;
@@ -11705,7 +12200,7 @@ async function fetchTaskLeasesForWorkers(input) {
11705
12200
  // src/monitor/monitor.service.ts
11706
12201
  function workerRecord2(runId, name) {
11707
12202
  return readJson(
11708
- path59.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
12203
+ path62.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
11709
12204
  void 0
11710
12205
  );
11711
12206
  }
@@ -11911,21 +12406,21 @@ async function runMonitorLoop(args) {
11911
12406
 
11912
12407
  // src/monitor/monitor-spawn.ts
11913
12408
  import { spawn as spawn6 } from "node:child_process";
11914
- import { closeSync as closeSync7, existsSync as existsSync41, openSync as openSync7 } from "node:fs";
11915
- import path60 from "node:path";
12409
+ import { closeSync as closeSync8, existsSync as existsSync43, openSync as openSync8 } from "node:fs";
12410
+ import path63 from "node:path";
11916
12411
  import { fileURLToPath as fileURLToPath3 } from "node:url";
11917
12412
  function resolveDefaultCliPath2() {
11918
- return path60.join(fileURLToPath3(new URL(".", import.meta.url)), "cli.js");
12413
+ return path63.join(fileURLToPath3(new URL(".", import.meta.url)), "cli.js");
11919
12414
  }
11920
12415
  function spawnMonitorSidecar(opts) {
11921
12416
  const cliPath = opts.cliPath ?? resolveDefaultCliPath2();
11922
- if (!existsSync41(cliPath)) return void 0;
12417
+ if (!existsSync43(cliPath)) return void 0;
11923
12418
  const monitorId = monitorIdFor(opts.runId, opts.workerName);
11924
12419
  const { harnessRoot } = getHarnessPaths();
11925
- const logPath = path60.join(harnessRoot, "monitors", `${monitorId}.log`);
12420
+ const logPath = path63.join(harnessRoot, "monitors", `${monitorId}.log`);
11926
12421
  let logFd;
11927
12422
  try {
11928
- logFd = openSync7(logPath, "a");
12423
+ logFd = openSync8(logPath, "a");
11929
12424
  } catch {
11930
12425
  logFd = void 0;
11931
12426
  }
@@ -11965,7 +12460,7 @@ function spawnMonitorSidecar(opts) {
11965
12460
  env: process.env
11966
12461
  })
11967
12462
  );
11968
- if (logFd !== void 0) closeSync7(logFd);
12463
+ if (logFd !== void 0) closeSync8(logFd);
11969
12464
  child.unref();
11970
12465
  const session = {
11971
12466
  monitorId,
@@ -11982,7 +12477,7 @@ function spawnMonitorSidecar(opts) {
11982
12477
  } catch {
11983
12478
  if (logFd !== void 0) {
11984
12479
  try {
11985
- closeSync7(logFd);
12480
+ closeSync8(logFd);
11986
12481
  } catch {
11987
12482
  }
11988
12483
  }
@@ -12042,13 +12537,13 @@ async function monitorTickCli(args) {
12042
12537
  }
12043
12538
 
12044
12539
  // src/package-version.ts
12045
- import { existsSync as existsSync42, readFileSync as readFileSync13 } from "node:fs";
12540
+ import { existsSync as existsSync44, readFileSync as readFileSync16 } from "node:fs";
12046
12541
  import { dirname, join } from "node:path";
12047
12542
  import { fileURLToPath as fileURLToPath4 } from "node:url";
12048
12543
  function resolvePackageRoot(moduleUrl) {
12049
12544
  let dir = dirname(fileURLToPath4(moduleUrl));
12050
12545
  for (let depth = 0; depth < 6; depth += 1) {
12051
- if (existsSync42(join(dir, "package.json"))) return dir;
12546
+ if (existsSync44(join(dir, "package.json"))) return dir;
12052
12547
  const parent = dirname(dir);
12053
12548
  if (parent === dir) break;
12054
12549
  dir = parent;
@@ -12057,7 +12552,7 @@ function resolvePackageRoot(moduleUrl) {
12057
12552
  }
12058
12553
  function readOwnPackageVersion(moduleUrl = import.meta.url) {
12059
12554
  const pkgPath = join(resolvePackageRoot(moduleUrl), "package.json");
12060
- const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
12555
+ const pkg = JSON.parse(readFileSync16(pkgPath, "utf8"));
12061
12556
  if (typeof pkg.version !== "string" || !pkg.version.trim()) {
12062
12557
  throw new Error(`Missing package.json version at ${pkgPath}`);
12063
12558
  }
@@ -12077,188 +12572,6 @@ function handleCliVersionFlag(argv, moduleUrl = import.meta.url, binName) {
12077
12572
  return true;
12078
12573
  }
12079
12574
 
12080
- // src/memory-cost-package-version-guard.ts
12081
- import { existsSync as existsSync43, readFileSync as readFileSync14 } from "node:fs";
12082
- import path61 from "node:path";
12083
- var MEMORY_COST_PACKAGE_MIN_VERSIONS = {
12084
- "@kynver-app/runtime": "0.1.83",
12085
- "@kynver-app/openclaw-agent-os": "0.1.43",
12086
- "@kynver-app/mcp-agent-os": "0.3.34"
12087
- };
12088
- var MEMORY_COST_MANAGED_PACKAGES = Object.keys(
12089
- MEMORY_COST_PACKAGE_MIN_VERSIONS
12090
- );
12091
- var DISPLAY_NAMES = {
12092
- "@kynver-app/runtime": "Kynver runtime",
12093
- "@kynver-app/openclaw-agent-os": "OpenClaw AgentOS plugin",
12094
- "@kynver-app/mcp-agent-os": "AgentOS MCP server"
12095
- };
12096
- var REPO_PACKAGE_JSON_RELATIVE = {
12097
- "@kynver-app/runtime": "packages/kynver-runtime/package.json",
12098
- "@kynver-app/openclaw-agent-os": "packages/kynver-openclaw-agent-os/package.json",
12099
- "@kynver-app/mcp-agent-os": "packages/kynver-mcp-agent-os/package.json"
12100
- };
12101
- function parseSemverParts(version) {
12102
- const core = version.trim().split("-")[0]?.split("+")[0];
12103
- if (!core) return null;
12104
- const parts = core.split(".");
12105
- if (parts.length < 1 || parts.length > 3) return null;
12106
- const nums = parts.map((p) => Number.parseInt(p, 10));
12107
- if (nums.some((n) => !Number.isFinite(n) || n < 0)) return null;
12108
- while (nums.length < 3) nums.push(0);
12109
- return [nums[0], nums[1], nums[2]];
12110
- }
12111
- function compareSemver(a, b) {
12112
- const pa = parseSemverParts(a);
12113
- const pb = parseSemverParts(b);
12114
- if (!pa || !pb) return 0;
12115
- for (let i = 0; i < 3; i += 1) {
12116
- if (pa[i] > pb[i]) return 1;
12117
- if (pa[i] < pb[i]) return -1;
12118
- }
12119
- return 0;
12120
- }
12121
- function semverAtLeast(version, minimum) {
12122
- return compareSemver(version, minimum) >= 0;
12123
- }
12124
- function maxSemver(versions) {
12125
- let best = null;
12126
- for (const version of versions) {
12127
- if (!best || compareSemver(version, best) > 0) best = version;
12128
- }
12129
- return best;
12130
- }
12131
- function readPackageJsonVersion(packageJsonPath) {
12132
- try {
12133
- const parsed = JSON.parse(readFileSync14(packageJsonPath, "utf8"));
12134
- return typeof parsed.version === "string" && parsed.version.trim() ? parsed.version.trim() : null;
12135
- } catch {
12136
- return null;
12137
- }
12138
- }
12139
- function resolveRepoRoot(cwd, explicitRepoRoot) {
12140
- const candidates = [explicitRepoRoot, process.env.KYNVER_REPO, cwd].filter(
12141
- (value) => Boolean(value?.trim())
12142
- );
12143
- for (const candidate of candidates) {
12144
- const resolved = path61.resolve(candidate);
12145
- if (existsSync43(path61.join(resolved, "packages/kynver-runtime/package.json")) && existsSync43(path61.join(resolved, "package.json"))) {
12146
- return resolved;
12147
- }
12148
- }
12149
- return null;
12150
- }
12151
- function probeRepoPackageVersions(input = {}) {
12152
- const cwd = input.cwd ?? process.cwd();
12153
- const repoRoot = resolveRepoRoot(cwd, input.repoRoot);
12154
- if (!repoRoot) return {};
12155
- const out = {};
12156
- for (const packageName of MEMORY_COST_MANAGED_PACKAGES) {
12157
- const packageJsonPath = path61.join(repoRoot, REPO_PACKAGE_JSON_RELATIVE[packageName]);
12158
- const version = readPackageJsonVersion(packageJsonPath);
12159
- if (!version) continue;
12160
- out[packageName] = { version, source: "repo", path: packageJsonPath };
12161
- }
12162
- return out;
12163
- }
12164
- function repoSourceCommands(packageName) {
12165
- if (packageName === "@kynver-app/runtime") {
12166
- return ["npm run kynver:build", "npm run kynver"];
12167
- }
12168
- return [`npm run build -w ${packageName}`];
12169
- }
12170
- function buildRemediation(input) {
12171
- const { packageName, minimumVersion, effectiveVersion, effectiveSource, repoVersion } = input;
12172
- const lines = [];
12173
- if (repoVersion && semverAtLeast(repoVersion, minimumVersion)) {
12174
- lines.push(
12175
- `Use the monorepo checkout (${repoVersion}) instead of a stale npm install: ${repoSourceCommands(packageName).join("; ")}.`
12176
- );
12177
- lines.push("Do not publish npm packages or wait on an operator release.");
12178
- return lines;
12179
- }
12180
- if (effectiveVersion) {
12181
- lines.push(
12182
- `Upgrade ${packageName} from ${effectiveVersion} to >= ${minimumVersion} (npm install -g ${packageName}@latest or align OpenClaw npm prefix).`
12183
- );
12184
- } else {
12185
- lines.push(`Install ${packageName} >= ${minimumVersion} before running memory-heavy AgentOS paths.`);
12186
- }
12187
- if (packageName === "@kynver-app/runtime") {
12188
- lines.push("Repo-source alternative: npm run kynver:build && npm run kynver");
12189
- }
12190
- if (effectiveSource === "installed" && repoVersion) {
12191
- lines.push(`Repo checkout reports ${repoVersion}; rebuild/link repo source if you develop from the monorepo.`);
12192
- }
12193
- return lines;
12194
- }
12195
- function pickEffectiveCandidate(candidates) {
12196
- if (candidates.length === 0) return { version: null, source: "unknown" };
12197
- const best = maxSemver(candidates.map((candidate) => candidate.version));
12198
- if (!best) return { version: null, source: "unknown" };
12199
- const winner = candidates.find((candidate) => candidate.version === best) ?? candidates[0];
12200
- return { version: winner.version, source: winner.source };
12201
- }
12202
- function evaluateMemoryCostPackageVersionGuard(input = {}) {
12203
- const normalize2 = (value, fallbackSource) => {
12204
- if (!value) return null;
12205
- if (typeof value === "string") return { version: value, source: fallbackSource };
12206
- return value;
12207
- };
12208
- const packages = MEMORY_COST_MANAGED_PACKAGES.map((packageName) => {
12209
- const minimumVersion = MEMORY_COST_PACKAGE_MIN_VERSIONS[packageName];
12210
- const candidates = [];
12211
- const installed = normalize2(input.installed?.[packageName], "installed");
12212
- const repo = normalize2(input.repo?.[packageName], "repo");
12213
- const self = normalize2(input.self?.[packageName], "self");
12214
- if (installed) candidates.push(installed);
12215
- if (repo) candidates.push(repo);
12216
- if (self) candidates.push(self);
12217
- const { version: effectiveVersion, source: effectiveSource } = pickEffectiveCandidate(candidates);
12218
- const repoVersion = repo?.version ?? null;
12219
- const ok2 = effectiveVersion ? semverAtLeast(effectiveVersion, minimumVersion) : false;
12220
- const remediation = ok2 ? [] : buildRemediation({
12221
- packageName,
12222
- minimumVersion,
12223
- effectiveVersion,
12224
- effectiveSource,
12225
- repoVersion
12226
- });
12227
- const summary2 = ok2 ? `${DISPLAY_NAMES[packageName]} ${effectiveVersion} meets memory-cost minimum ${minimumVersion} (${effectiveSource}).` : `${DISPLAY_NAMES[packageName]} is below memory-cost minimum ${minimumVersion}` + (effectiveVersion ? ` (effective ${effectiveVersion} via ${effectiveSource})` : " (no version detected)") + ".";
12228
- return {
12229
- packageName,
12230
- displayName: DISPLAY_NAMES[packageName],
12231
- minimumVersion,
12232
- effectiveVersion,
12233
- effectiveSource,
12234
- ok: ok2,
12235
- summary: summary2,
12236
- remediation
12237
- };
12238
- });
12239
- const violations = packages.filter((row) => !row.ok);
12240
- const ok = violations.length === 0;
12241
- const summary = ok ? "All managed AgentOS packages meet memory-cost minimum versions." : `Memory-cost package guard blocked ${violations.length} stale package(s): ${violations.map((row) => `${row.packageName} < ${row.minimumVersion}`).join("; ")}.`;
12242
- return { ok, summary, packages };
12243
- }
12244
- var MemoryCostPackageVersionGuardError = class extends Error {
12245
- result;
12246
- constructor(result) {
12247
- const lines = [
12248
- result.summary,
12249
- ...result.packages.filter((row) => !row.ok).flatMap((row) => [`- ${row.summary}`, ...row.remediation.map((line) => ` \u2192 ${line}`)])
12250
- ];
12251
- super(lines.join("\n"));
12252
- this.name = "MemoryCostPackageVersionGuardError";
12253
- this.result = result;
12254
- }
12255
- };
12256
- function assertMemoryCostPackageVersionGuard(input = {}) {
12257
- const result = evaluateMemoryCostPackageVersionGuard(input);
12258
- if (!result.ok) throw new MemoryCostPackageVersionGuardError(result);
12259
- return result;
12260
- }
12261
-
12262
12575
  // src/memory-cost-package-version-guard-enforce.ts
12263
12576
  function installedVersionMap(observed) {
12264
12577
  const out = {};
@@ -12278,10 +12591,10 @@ async function buildMemoryCostPackageGuardInput(input = {}) {
12278
12591
  Promise.resolve(probeRepoPackageVersions({ cwd: input.cwd, repoRoot: input.repoRoot }))
12279
12592
  ]);
12280
12593
  const self = {};
12281
- if (input.selfPackageName && input.selfVersion) {
12594
+ const runtimeSelfVersion = input.selfPackageName === "@kynver-app/runtime" && input.selfVersion ? input.selfVersion : PACKAGE_VERSION;
12595
+ self["@kynver-app/runtime"] = { version: runtimeSelfVersion, source: "self" };
12596
+ if (input.selfPackageName && input.selfVersion && input.selfPackageName !== "@kynver-app/runtime") {
12282
12597
  self[input.selfPackageName] = { version: input.selfVersion, source: "self" };
12283
- } else {
12284
- self["@kynver-app/runtime"] = { version: PACKAGE_VERSION, source: "self" };
12285
12598
  }
12286
12599
  return {
12287
12600
  installed: installedVersionMap(installed),
@@ -12306,7 +12619,7 @@ function shouldEnforceMemoryCostPackageGuardCli(scope, action) {
12306
12619
  }
12307
12620
 
12308
12621
  // src/post-restart-unblock.ts
12309
- import path62 from "node:path";
12622
+ import path64 from "node:path";
12310
12623
  function skip(runId, worker, taskId, agentOsId, leaseOwner, reason) {
12311
12624
  return { runId, worker, taskId, agentOsId, leaseOwner, action: "skipped", reason };
12312
12625
  }
@@ -12319,7 +12632,7 @@ async function postRestartUnblock(args) {
12319
12632
  const errors = [];
12320
12633
  for (const run of listRunRecords()) {
12321
12634
  for (const name of Object.keys(run.workers ?? {})) {
12322
- const workerPath = path62.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
12635
+ const workerPath = path64.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
12323
12636
  const worker = readJson(workerPath, void 0);
12324
12637
  if (!worker) {
12325
12638
  skipped.push(skip(run.id, name, "", "", "", "worker.json missing"));
@@ -12431,9 +12744,9 @@ async function postRestartUnblockCli(args) {
12431
12744
  }
12432
12745
 
12433
12746
  // src/default-repo-cli.ts
12434
- import path63 from "node:path";
12747
+ import path65 from "node:path";
12435
12748
  import { homedir as homedir14 } from "node:os";
12436
- var CONFIG_FILE2 = path63.join(homedir14(), ".kynver", "config.json");
12749
+ var CONFIG_FILE2 = path65.join(homedir14(), ".kynver", "config.json");
12437
12750
  function ensureDefaultRepo(opts) {
12438
12751
  const existing = loadUserConfig();
12439
12752
  const resolved = resolveDefaultRepo({ ...opts, config: existing });
@@ -12514,16 +12827,16 @@ function summarizeResolvedDefaultRepo(resolved) {
12514
12827
  }
12515
12828
 
12516
12829
  // src/doctor/runtime-takeover.ts
12517
- import path65 from "node:path";
12830
+ import path67 from "node:path";
12518
12831
 
12519
12832
  // src/doctor/runtime-takeover.probes.ts
12520
- import { accessSync, constants, existsSync as existsSync44, readFileSync as readFileSync15 } from "node:fs";
12833
+ import { accessSync, constants, existsSync as existsSync45, readFileSync as readFileSync17 } from "node:fs";
12521
12834
  import { homedir as homedir15 } from "node:os";
12522
- import path64 from "node:path";
12523
- import { spawnSync as spawnSync8 } from "node:child_process";
12835
+ import path66 from "node:path";
12836
+ import { spawnSync as spawnSync9 } from "node:child_process";
12524
12837
  function captureCommand(bin, args) {
12525
12838
  try {
12526
- const res = spawnSync8(bin, args, { encoding: "utf8" });
12839
+ const res = spawnSync9(bin, args, { encoding: "utf8" });
12527
12840
  const stdout = (res.stdout || "").trim();
12528
12841
  const stderr = (res.stderr || "").trim();
12529
12842
  const ok = res.status === 0;
@@ -12548,7 +12861,7 @@ function tokenPrefix(token) {
12548
12861
  return trimmed.length <= 12 ? `${trimmed}\u2026` : `${trimmed.slice(0, 12)}\u2026`;
12549
12862
  }
12550
12863
  function isWritable(target) {
12551
- if (!existsSync44(target)) return false;
12864
+ if (!existsSync45(target)) return false;
12552
12865
  try {
12553
12866
  accessSync(target, constants.W_OK);
12554
12867
  return true;
@@ -12561,15 +12874,15 @@ var defaultRuntimeTakeoverProbes = {
12561
12874
  commandOnPath: (bin) => captureCommand(process.platform === "win32" ? "where" : "which", [bin]),
12562
12875
  kynverVersion: (bin) => captureCommand(bin, ["--version"]),
12563
12876
  loadConfig: () => loadUserConfig(),
12564
- configFilePath: () => path64.join(homedir15(), ".kynver", "config.json"),
12565
- credentialsFilePath: () => path64.join(homedir15(), ".kynver", "credentials"),
12877
+ configFilePath: () => path66.join(homedir15(), ".kynver", "config.json"),
12878
+ credentialsFilePath: () => path66.join(homedir15(), ".kynver", "credentials"),
12566
12879
  readCredentials: () => {
12567
- const credPath = path64.join(homedir15(), ".kynver", "credentials");
12568
- if (!existsSync44(credPath)) {
12880
+ const credPath = path66.join(homedir15(), ".kynver", "credentials");
12881
+ if (!existsSync45(credPath)) {
12569
12882
  return { hasApiKey: false };
12570
12883
  }
12571
12884
  try {
12572
- const parsed = JSON.parse(readFileSync15(credPath, "utf8"));
12885
+ const parsed = JSON.parse(readFileSync17(credPath, "utf8"));
12573
12886
  return {
12574
12887
  hasApiKey: Boolean(parsed.apiKey?.trim()),
12575
12888
  runnerTokenPrefix: tokenPrefix(parsed.runnerToken),
@@ -12599,11 +12912,9 @@ var defaultRuntimeTakeoverProbes = {
12599
12912
  })()
12600
12913
  }),
12601
12914
  harnessRoot: () => resolveHarnessRoot(),
12602
- legacyOpenclawHarnessRoot: () => path64.join(homedir15(), ".openclaw", "harness"),
12603
- pathExists: (target) => existsSync44(target),
12604
- pathWritable: (target) => isWritable(target),
12605
- vercelVersion: () => captureCommand("vercel", ["--version"]),
12606
- vercelWhoami: () => captureCommand("vercel", ["whoami"])
12915
+ legacyOpenclawHarnessRoot: () => path66.join(homedir15(), ".openclaw", "harness"),
12916
+ pathExists: (target) => existsSync45(target),
12917
+ pathWritable: (target) => isWritable(target)
12607
12918
  };
12608
12919
 
12609
12920
  // src/doctor/runtime-takeover-scheduler.ts
@@ -12970,37 +13281,46 @@ function assessRunnerToken(probes) {
12970
13281
  ];
12971
13282
  return { id: "runner_token", label: "Runner token readiness", checks };
12972
13283
  }
12973
- function assessVercelCli(probes) {
12974
- const version = probes.vercelVersion();
12975
- const whoami = probes.vercelWhoami();
12976
- const installed = version.ok;
13284
+ function assessVercelDeployEvidence(probes) {
13285
+ const globalCli = probes.commandOnPath("vercel");
13286
+ const cliInstalled = globalCli.ok;
13287
+ const githubToken = Boolean(
13288
+ process.env.GITHUB_TOKEN?.trim() || process.env.GH_TOKEN?.trim()
13289
+ );
13290
+ const vercelToken = Boolean(process.env.VERCEL_TOKEN?.trim());
12977
13291
  return {
12978
- id: "vercel_cli",
12979
- label: "Vercel CLI",
13292
+ id: "vercel_deploy_evidence",
13293
+ label: "Vercel deploy evidence",
12980
13294
  checks: [
12981
13295
  check2({
12982
- id: "vercel_installed",
12983
- label: "Vercel CLI installed",
12984
- status: installed ? "pass" : "warn",
12985
- summary: installed ? version.stdout || "vercel CLI found" : version.error ?? "vercel not found on PATH",
12986
- remediation: installed ? void 0 : "Install Vercel CLI (`npm i -g vercel`) for deploy evidence and env pulls.",
12987
- details: { stderr: version.stderr || null }
13296
+ id: "vercel_global_cli_absent",
13297
+ label: "Global Vercel CLI not installed",
13298
+ status: cliInstalled ? "warn" : "pass",
13299
+ summary: cliInstalled ? `Global Vercel CLI found (${globalCli.stdout || "on PATH"}) \u2014 uninstall after operator approval` : "No global Vercel CLI on PATH",
13300
+ remediation: cliInstalled ? "Uninstall global Vercel CLI (`npm uninstall -g vercel`) \u2014 use GitHub commit status or scoped VERCEL_TOKEN + REST API instead." : void 0,
13301
+ details: { path: cliInstalled ? globalCli.stdout || null : null }
13302
+ }),
13303
+ check2({
13304
+ id: "github_token_for_vercel_status",
13305
+ label: "GitHub token for Vercel status",
13306
+ status: githubToken ? "pass" : "warn",
13307
+ summary: githubToken ? "GITHUB_TOKEN/GH_TOKEN present for GitHub Vercel StatusContext evidence" : "No GITHUB_TOKEN \u2014 merge-gate Vercel evidence requires GitHub commit status API",
13308
+ remediation: githubToken ? void 0 : "Export GITHUB_TOKEN (or GH_TOKEN) for PR head commit status lookups."
12988
13309
  }),
12989
13310
  check2({
12990
- id: "vercel_authenticated",
12991
- label: "Vercel CLI authenticated",
12992
- status: !installed ? "warn" : whoami.ok ? "pass" : "warn",
12993
- summary: !installed ? "Skipped \u2014 Vercel CLI not installed" : whoami.ok ? `Authenticated as ${whoami.stdout}` : whoami.stderr || whoami.error || "Not logged in",
12994
- remediation: installed && !whoami.ok ? "Run `vercel login` and link the Kynver project if needed." : void 0,
12995
- details: { account: whoami.ok ? whoami.stdout : null }
13311
+ id: "vercel_api_token_optional",
13312
+ label: "Vercel API token (optional fallback)",
13313
+ status: vercelToken ? "pass" : "warn",
13314
+ summary: vercelToken ? "VERCEL_TOKEN configured for optional REST API deployment lookup" : "VERCEL_TOKEN unset \u2014 GitHub status is the default path; API fallback disabled",
13315
+ remediation: vercelToken ? void 0 : "Set ephemeral/scoped VERCEL_TOKEN only when GitHub status is insufficient; avoid persistent ~/.vercel auth on runner hosts."
12996
13316
  })
12997
13317
  ]
12998
13318
  };
12999
13319
  }
13000
13320
  function assessHarnessDirs(probes) {
13001
13321
  const harnessRoot = probes.harnessRoot();
13002
- const runsDir = path65.join(harnessRoot, "runs");
13003
- const worktreesDir = path65.join(harnessRoot, "worktrees");
13322
+ const runsDir = path67.join(harnessRoot, "runs");
13323
+ const worktreesDir = path67.join(harnessRoot, "worktrees");
13004
13324
  const displayHarnessRoot = redactHomePath(harnessRoot);
13005
13325
  const displayRunsDir = redactHomePath(runsDir);
13006
13326
  const displayWorktreesDir = redactHomePath(worktreesDir);
@@ -13131,7 +13451,7 @@ function assessRuntimeTakeoverReadiness(probes = defaultRuntimeTakeoverProbes) {
13131
13451
  assessCliPackage(probes),
13132
13452
  assessUserConfig(probes),
13133
13453
  assessRunnerToken(probes),
13134
- assessVercelCli(probes),
13454
+ assessVercelDeployEvidence(probes),
13135
13455
  assessHarnessDirs(probes),
13136
13456
  assessCallbackAuth(probes),
13137
13457
  assessOpenclawHotspots(probes)
@@ -13264,9 +13584,9 @@ function applySchedulerCutoverAttestation(config) {
13264
13584
  }
13265
13585
 
13266
13586
  // src/scheduler-cutover-cli.ts
13267
- import path66 from "node:path";
13587
+ import path68 from "node:path";
13268
13588
  import { homedir as homedir16 } from "node:os";
13269
- var CONFIG_FILE3 = path66.join(homedir16(), ".kynver", "config.json");
13589
+ var CONFIG_FILE3 = path68.join(homedir16(), ".kynver", "config.json");
13270
13590
  function runSchedulerCutoverCheckCli(json = false) {
13271
13591
  const config = loadUserConfig();
13272
13592
  const report = assessSchedulerCutover(config);
@@ -13478,10 +13798,22 @@ async function main(argv = process.argv.slice(2)) {
13478
13798
  if (action && isHelpFlag(action) || rest.some(isHelpFlag)) return usage(0);
13479
13799
  const args = parseArgs(rest);
13480
13800
  const { runsDir, worktreesDir } = getPaths();
13481
- mkdirSync8(runsDir, { recursive: true });
13482
- mkdirSync8(worktreesDir, { recursive: true });
13801
+ mkdirSync10(runsDir, { recursive: true });
13802
+ mkdirSync10(worktreesDir, { recursive: true });
13483
13803
  if (shouldEnforceMemoryCostPackageGuardCli(scope, action)) {
13484
- await enforceMemoryCostPackageGuardAtStartup();
13804
+ let repoRoot;
13805
+ const runId = args.run ? String(args.run).trim() : "";
13806
+ if (runId) {
13807
+ try {
13808
+ repoRoot = loadRun(runId).repo;
13809
+ } catch {
13810
+ repoRoot = void 0;
13811
+ }
13812
+ }
13813
+ await enforceMemoryCostPackageGuardAtStartup({
13814
+ repoRoot,
13815
+ cwd: repoRoot
13816
+ });
13485
13817
  }
13486
13818
  if (scope === "login") return void await runLogin(args);
13487
13819
  if (scope === "runner" && action === "credential") return void await mintRunnerCredential(args);