@kynver-app/runtime 0.1.42 → 0.1.48

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.
Files changed (118) hide show
  1. package/README.md +15 -0
  2. package/dist/auto-complete.d.ts +60 -0
  3. package/dist/bounded-build/admission.d.ts +30 -0
  4. package/dist/bounded-build/constants.d.ts +10 -0
  5. package/dist/bounded-build/exec.d.ts +26 -0
  6. package/dist/bounded-build/index.d.ts +6 -0
  7. package/dist/bounded-build/meminfo.d.ts +5 -0
  8. package/dist/bounded-build/node-options.d.ts +9 -0
  9. package/dist/bounded-build/systemd-wrap.d.ts +17 -0
  10. package/dist/callback-headers.d.ts +2 -0
  11. package/dist/callbacks.d.ts +38 -0
  12. package/dist/cleanup-cli.d.ts +1 -0
  13. package/dist/cleanup-dir-size.d.ts +4 -0
  14. package/dist/cleanup-execute.d.ts +4 -0
  15. package/dist/cleanup-guards.d.ts +18 -0
  16. package/dist/cleanup-scan.d.ts +14 -0
  17. package/dist/cleanup-types.d.ts +56 -0
  18. package/dist/cleanup-worktree-index.d.ts +13 -0
  19. package/dist/cleanup.d.ts +5 -0
  20. package/dist/cli.d.ts +3 -0
  21. package/dist/cli.js +840 -77
  22. package/dist/cli.js.map +4 -4
  23. package/dist/command-center-contract-cli.d.ts +1 -0
  24. package/dist/completion-ack.d.ts +10 -0
  25. package/dist/completion-response.d.ts +15 -0
  26. package/dist/config.d.ts +61 -0
  27. package/dist/daemon.d.ts +5 -0
  28. package/dist/disk-gate.d.ts +9 -0
  29. package/dist/dispatch.d.ts +16 -0
  30. package/dist/doctor/doctor.types.d.ts +25 -0
  31. package/dist/doctor/index.d.ts +4 -0
  32. package/dist/doctor/runtime-takeover-cli.d.ts +1 -0
  33. package/dist/doctor/runtime-takeover.d.ts +3 -0
  34. package/dist/doctor/runtime-takeover.probes.d.ts +37 -0
  35. package/dist/exit-classify.d.ts +12 -0
  36. package/dist/exited-salvage.d.ts +22 -0
  37. package/dist/finalize.d.ts +16 -0
  38. package/dist/fortress-engagement-gate.d.ts +5 -0
  39. package/dist/git.d.ts +37 -0
  40. package/dist/github-repo.d.ts +5 -0
  41. package/dist/harness-expert-review.d.ts +8 -0
  42. package/dist/harness-verify-cli.d.ts +1 -0
  43. package/dist/harness-verify.d.ts +19 -0
  44. package/dist/heartbeat.d.ts +22 -0
  45. package/dist/index.d.ts +33 -0
  46. package/dist/index.js +880 -81
  47. package/dist/index.js.map +4 -4
  48. package/dist/installed-package-versions.d.ts +6 -0
  49. package/dist/landing-contract-gate.d.ts +24 -0
  50. package/dist/landing-gate.d.ts +24 -0
  51. package/dist/lease-renewal.d.ts +15 -0
  52. package/dist/model-routing-task-enrich.d.ts +8 -0
  53. package/dist/model-routing.d.ts +29 -0
  54. package/dist/monitor/index.d.ts +7 -0
  55. package/dist/monitor/monitor-cli.d.ts +9 -0
  56. package/dist/monitor/monitor-loop.d.ts +1 -0
  57. package/dist/monitor/monitor-spawn.d.ts +18 -0
  58. package/dist/monitor/monitor.classify.d.ts +15 -0
  59. package/dist/monitor/monitor.service.d.ts +17 -0
  60. package/dist/monitor/monitor.store.d.ts +6 -0
  61. package/dist/monitor/monitor.task-lease.d.ts +11 -0
  62. package/dist/monitor/monitor.terminal.d.ts +11 -0
  63. package/dist/monitor/monitor.types.d.ts +74 -0
  64. package/dist/package-version.d.ts +5 -0
  65. package/dist/path-values.d.ts +5 -0
  66. package/dist/paths.d.ts +7 -0
  67. package/dist/pipeline-dispatch.d.ts +7 -0
  68. package/dist/pipeline-tick.d.ts +45 -0
  69. package/dist/plan-persist/agentos-api.d.ts +28 -0
  70. package/dist/plan-persist/body-hash.d.ts +3 -0
  71. package/dist/plan-persist/drain.d.ts +7 -0
  72. package/dist/plan-persist/errors.d.ts +9 -0
  73. package/dist/plan-persist/handoff.d.ts +7 -0
  74. package/dist/plan-persist/idempotency.d.ts +2 -0
  75. package/dist/plan-persist/index.d.ts +8 -0
  76. package/dist/plan-persist/outbox-store.d.ts +18 -0
  77. package/dist/plan-persist/paths.d.ts +8 -0
  78. package/dist/plan-persist/persist.d.ts +10 -0
  79. package/dist/plan-persist/readback.d.ts +17 -0
  80. package/dist/plan-persist/types.d.ts +91 -0
  81. package/dist/plan-persist-cli.d.ts +3 -0
  82. package/dist/plan-progress-daemon-sync.d.ts +7 -0
  83. package/dist/plan-progress-sync.d.ts +21 -0
  84. package/dist/plan-progress.d.ts +10 -0
  85. package/dist/pr-handoff/index.d.ts +4 -0
  86. package/dist/pr-handoff/pr-handoff-assess.d.ts +33 -0
  87. package/dist/pr-handoff/pr-handoff-gh.d.ts +44 -0
  88. package/dist/pr-handoff/pr-handoff.d.ts +8 -0
  89. package/dist/pr-handoff/pr-handoff.types.d.ts +45 -0
  90. package/dist/prompt.d.ts +14 -0
  91. package/dist/providers/claude.d.ts +4 -0
  92. package/dist/providers/cursor-windows.d.ts +7 -0
  93. package/dist/providers/cursor.d.ts +11 -0
  94. package/dist/providers/model-preflight.d.ts +31 -0
  95. package/dist/providers/registry.d.ts +4 -0
  96. package/dist/providers/types.d.ts +32 -0
  97. package/dist/redact.d.ts +1 -0
  98. package/dist/resource-gate.d.ts +53 -0
  99. package/dist/retry-limits.d.ts +8 -0
  100. package/dist/run-store.d.ts +30 -0
  101. package/dist/shell-command-outcome.d.ts +32 -0
  102. package/dist/stale-reconcile.d.ts +25 -0
  103. package/dist/status.d.ts +166 -0
  104. package/dist/stream.d.ts +20 -0
  105. package/dist/supervisor.d.ts +29 -0
  106. package/dist/sweep.d.ts +1 -0
  107. package/dist/util.d.ts +22 -0
  108. package/dist/validate.d.ts +5 -0
  109. package/dist/vercel/index.d.ts +3 -0
  110. package/dist/vercel/vercel-evidence.d.ts +48 -0
  111. package/dist/vercel/vercel-github-status.d.ts +19 -0
  112. package/dist/vercel/vercel-url.d.ts +16 -0
  113. package/dist/worker-env.d.ts +15 -0
  114. package/dist/worker-lifecycle.d.ts +28 -0
  115. package/dist/worker-ops.d.ts +20 -0
  116. package/dist/workspace-runtime-config.d.ts +8 -0
  117. package/dist/worktree.d.ts +4 -0
  118. package/package.json +4 -2
package/dist/cli.js CHANGED
@@ -471,12 +471,12 @@ var DEFAULT_CRITICAL_FREE_BYTES = 15 * 1024 * 1024 * 1024;
471
471
  var DEFAULT_MAX_USED_PERCENT = 80;
472
472
  var DEFAULT_HARD_MAX_USED_PERCENT = 90;
473
473
  function observeRunnerDiskGate(input = {}) {
474
- const path35 = input.diskPath?.trim() || "/";
474
+ const path38 = input.diskPath?.trim() || "/";
475
475
  const warnBelowBytes = input.diskFreeWarnBytes ?? DEFAULT_WARN_FREE_BYTES;
476
476
  const criticalBelowBytes = input.diskFreeCriticalBytes ?? DEFAULT_CRITICAL_FREE_BYTES;
477
477
  const maxUsedPercent = input.diskMaxUsedPercent ?? DEFAULT_MAX_USED_PERCENT;
478
478
  const hardMaxUsedPercent = input.diskHardMaxUsedPercent ?? DEFAULT_HARD_MAX_USED_PERCENT;
479
- const stats = statfsSync(path35);
479
+ const stats = statfsSync(path38);
480
480
  const freeBytes = Number(stats.bavail) * Number(stats.bsize);
481
481
  const totalBytes = Number(stats.blocks) * Number(stats.bsize);
482
482
  const usedPercent = totalBytes > 0 ? (totalBytes - freeBytes) / totalBytes * 100 : 100;
@@ -496,7 +496,7 @@ function observeRunnerDiskGate(input = {}) {
496
496
  }
497
497
  return {
498
498
  ok,
499
- path: path35,
499
+ path: path38,
500
500
  freeBytes,
501
501
  totalBytes,
502
502
  usedPercent,
@@ -509,8 +509,29 @@ function observeRunnerDiskGate(input = {}) {
509
509
  }
510
510
 
511
511
  // src/resource-gate.ts
512
- import { readFileSync as readFileSync5 } from "node:fs";
512
+ import os2 from "node:os";
513
+
514
+ // src/bounded-build/meminfo.ts
515
+ import { readFileSync as readFileSync3 } from "node:fs";
513
516
  import os from "node:os";
517
+ function readMemAvailableBytes(meminfoText) {
518
+ if (meminfoText !== void 0) {
519
+ const match = meminfoText.match(/^MemAvailable:\s+(\d+)\s*kB/m);
520
+ if (match) return Number(match[1]) * 1024;
521
+ return os.freemem();
522
+ }
523
+ if (process.platform === "linux") {
524
+ try {
525
+ const meminfo = readFileSync3("/proc/meminfo", "utf8");
526
+ const match = meminfo.match(/^MemAvailable:\s+(\d+)\s*kB/m);
527
+ if (match) return Number(match[1]) * 1024;
528
+ } catch {
529
+ }
530
+ }
531
+ return os.freemem();
532
+ }
533
+
534
+ // src/resource-gate.ts
514
535
  import path6 from "node:path";
515
536
 
516
537
  // src/run-store.ts
@@ -586,7 +607,7 @@ function runDirectory(id) {
586
607
  }
587
608
 
588
609
  // src/heartbeat.ts
589
- import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
610
+ import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:fs";
590
611
  var HEARTBEAT_FUTURE_SKEW_MS = 6e4;
591
612
  function isTerminalHeartbeatPhase(phase) {
592
613
  return phase === "complete";
@@ -608,7 +629,7 @@ function parseHeartbeat(file) {
608
629
  if (!existsSync5(file)) return result;
609
630
  const maxFutureMs = Date.now() + HEARTBEAT_FUTURE_SKEW_MS;
610
631
  const clampedTo = new Date(maxFutureMs).toISOString();
611
- const lines = readFileSync3(file, "utf8").split("\n").filter(Boolean);
632
+ const lines = readFileSync4(file, "utf8").split("\n").filter(Boolean);
612
633
  for (const line of lines) {
613
634
  const entry = safeJson(line);
614
635
  if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
@@ -635,7 +656,155 @@ function parseHeartbeat(file) {
635
656
  }
636
657
 
637
658
  // src/stream.ts
638
- import { existsSync as existsSync6, readFileSync as readFileSync4 } from "node:fs";
659
+ import { existsSync as existsSync6, readFileSync as readFileSync5 } from "node:fs";
660
+
661
+ // src/shell-command-outcome.ts
662
+ var NPM_AUDIT_RE = /\bnpm\s+audit\b/i;
663
+ function tidy(text, max = 200) {
664
+ const one = text.replace(/\s+/g, " ").trim();
665
+ return one.length > max ? `${one.slice(0, max - 1)}\u2026` : one;
666
+ }
667
+ function extractJsonObject(text) {
668
+ const trimmed = text.trim();
669
+ if (!trimmed) return null;
670
+ if (trimmed.startsWith("{")) {
671
+ try {
672
+ return JSON.parse(trimmed);
673
+ } catch {
674
+ }
675
+ }
676
+ const start = trimmed.indexOf("{");
677
+ const end = trimmed.lastIndexOf("}");
678
+ if (start >= 0 && end > start) {
679
+ try {
680
+ return JSON.parse(trimmed.slice(start, end + 1));
681
+ } catch {
682
+ return null;
683
+ }
684
+ }
685
+ return null;
686
+ }
687
+ function isRecord(value) {
688
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
689
+ }
690
+ function summarizeNpmAuditReport(report) {
691
+ const meta = report.metadata;
692
+ if (!isRecord(meta)) return null;
693
+ const vuln = meta.vulnerabilities;
694
+ if (!isRecord(vuln)) return null;
695
+ const num = (key) => typeof vuln[key] === "number" ? vuln[key] : 0;
696
+ const summary = {
697
+ info: num("info"),
698
+ low: num("low"),
699
+ moderate: num("moderate"),
700
+ high: num("high"),
701
+ critical: num("critical"),
702
+ total: num("total")
703
+ };
704
+ if (typeof vuln.total !== "number" && !summary.critical && !summary.high && !summary.moderate && !summary.low && !summary.info) {
705
+ return null;
706
+ }
707
+ return summary;
708
+ }
709
+ function formatAuditSummaryLine(audit) {
710
+ const parts = [];
711
+ if (audit.critical) parts.push(`${audit.critical} critical`);
712
+ if (audit.high) parts.push(`${audit.high} high`);
713
+ if (audit.moderate) parts.push(`${audit.moderate} moderate`);
714
+ if (audit.low) parts.push(`${audit.low} low`);
715
+ if (audit.info) parts.push(`${audit.info} info`);
716
+ const breakdown = parts.length ? parts.join(", ") : "see report";
717
+ return `npm audit: ${audit.total} vulnerabilit${audit.total === 1 ? "y" : "ies"} (${breakdown}) \u2014 remediation required`;
718
+ }
719
+ function npmAuditFailureReason(report, stderr) {
720
+ const err = report.error;
721
+ if (isRecord(err)) {
722
+ const summary = typeof err.summary === "string" ? err.summary.trim() : "";
723
+ const code = typeof err.code === "string" ? err.code.trim() : "";
724
+ if (summary) return code ? `${code}: ${summary}` : summary;
725
+ if (code) return code;
726
+ }
727
+ const detail = typeof report.message === "string" ? report.message.trim() : "";
728
+ if (detail) return detail;
729
+ const errTail = stderr.trim();
730
+ if (errTail) return tidy(errTail.split("\n").find(Boolean) ?? errTail, 160);
731
+ return "npm audit failed";
732
+ }
733
+ function classifyNpmAuditOutcome(input) {
734
+ const combined = `${input.stdout}
735
+ ${input.stderr}`.trim();
736
+ const parsed = extractJsonObject(combined);
737
+ if (!parsed || !isRecord(parsed)) {
738
+ const tail = tidy(combined || `exit ${input.exitCode}`, 180);
739
+ return {
740
+ kind: "command_failure",
741
+ exitCode: input.exitCode,
742
+ summary: `npm audit failed (invalid or missing JSON): ${tail}`,
743
+ parseError: "invalid_json"
744
+ };
745
+ }
746
+ if (isRecord(parsed.error)) {
747
+ return {
748
+ kind: "command_failure",
749
+ exitCode: input.exitCode,
750
+ summary: `npm audit command failed: ${npmAuditFailureReason(parsed, input.stderr)}`
751
+ };
752
+ }
753
+ const audit = summarizeNpmAuditReport(parsed);
754
+ if (!audit) {
755
+ return {
756
+ kind: "command_failure",
757
+ exitCode: input.exitCode,
758
+ summary: "npm audit failed: JSON response missing vulnerability metadata",
759
+ parseError: "missing_metadata"
760
+ };
761
+ }
762
+ if (input.exitCode === 0 && audit.total === 0) {
763
+ return {
764
+ kind: "success",
765
+ exitCode: 0,
766
+ summary: "npm audit: no vulnerabilities reported",
767
+ audit
768
+ };
769
+ }
770
+ return {
771
+ kind: "audit_findings",
772
+ exitCode: input.exitCode,
773
+ summary: formatAuditSummaryLine(audit),
774
+ audit
775
+ };
776
+ }
777
+ function isNpmAuditCommand(command) {
778
+ return NPM_AUDIT_RE.test(command);
779
+ }
780
+ function classifyShellCommandOutcome(input) {
781
+ const stdout = input.stdout ?? "";
782
+ const stderr = input.stderr ?? "";
783
+ const interleaved = input.interleavedOutput ?? "";
784
+ if (isNpmAuditCommand(input.command)) {
785
+ const body = stdout.trim() || interleaved.trim() || stderr.trim();
786
+ return classifyNpmAuditOutcome({
787
+ exitCode: input.exitCode,
788
+ stdout: body,
789
+ stderr
790
+ });
791
+ }
792
+ if (input.exitCode === 0) {
793
+ return {
794
+ kind: "success",
795
+ exitCode: 0,
796
+ summary: `command succeeded (exit 0)`
797
+ };
798
+ }
799
+ const tail = tidy(interleaved || stdout || stderr || `exit ${input.exitCode}`, 180);
800
+ return {
801
+ kind: "command_failure",
802
+ exitCode: input.exitCode,
803
+ summary: `command failed (exit ${input.exitCode}): ${tail}`
804
+ };
805
+ }
806
+
807
+ // src/stream.ts
639
808
  function eventTimestampIso(event) {
640
809
  const tsMs = event.timestamp_ms;
641
810
  return event.timestamp || event.ts || (tsMs ? new Date(tsMs).toISOString() : void 0);
@@ -656,16 +825,43 @@ function recordStreamResult(result, event) {
656
825
  result.error = String(event.result || event.api_error_status || "stream result error");
657
826
  }
658
827
  }
828
+ function shellPayloadFromCursorEvent(event) {
829
+ if (event.type !== "tool_call" || event.subtype !== "completed") return null;
830
+ const toolCall = event.tool_call && typeof event.tool_call === "object" && !Array.isArray(event.tool_call) ? event.tool_call : null;
831
+ const shell = toolCall?.shellToolCall;
832
+ if (!shell || typeof shell !== "object" || Array.isArray(shell)) return null;
833
+ const shellObj = shell;
834
+ const args = shellObj.args;
835
+ const command = args && typeof args === "object" && !Array.isArray(args) && typeof args.command === "string" ? String(args.command) : "";
836
+ const result = shellObj.result;
837
+ if (!result || typeof result !== "object" || Array.isArray(result)) return null;
838
+ const body = result.success ?? result.failure;
839
+ if (!body || typeof body !== "object" || Array.isArray(body)) return null;
840
+ const row = body;
841
+ const exitCode = typeof row.exitCode === "number" ? row.exitCode : 0;
842
+ return {
843
+ command,
844
+ exitCode,
845
+ stdout: typeof row.stdout === "string" ? row.stdout : "",
846
+ stderr: typeof row.stderr === "string" ? row.stderr : "",
847
+ interleaved: typeof row.interleavedOutput === "string" ? row.interleavedOutput : ""
848
+ };
849
+ }
850
+ function applyShellOutcome(parsed, outcome) {
851
+ if (outcome.kind === "success") return;
852
+ parsed.lastShellOutcome = outcome;
853
+ }
659
854
  function parseHarnessStream(file) {
660
855
  const result = {
661
856
  firstEventAt: null,
662
857
  lastEventAt: null,
663
858
  currentTool: null,
664
859
  finalResult: null,
665
- error: null
860
+ error: null,
861
+ lastShellOutcome: null
666
862
  };
667
863
  if (!existsSync6(file)) return result;
668
- const lines = readFileSync4(file, "utf8").split("\n").filter(Boolean);
864
+ const lines = readFileSync5(file, "utf8").split("\n").filter(Boolean);
669
865
  for (const line of lines) {
670
866
  const event = safeJson(line);
671
867
  if (!event) continue;
@@ -690,12 +886,44 @@ function parseHarnessStream(file) {
690
886
  const name = cursorToolNameFromCall(toolCall);
691
887
  if (name) result.currentTool = name;
692
888
  }
889
+ const shell = shellPayloadFromCursorEvent(event);
890
+ if (shell) {
891
+ applyShellOutcome(
892
+ result,
893
+ classifyShellCommandOutcome({
894
+ command: shell.command,
895
+ exitCode: shell.exitCode,
896
+ stdout: shell.stdout,
897
+ stderr: shell.stderr,
898
+ interleavedOutput: shell.interleaved
899
+ })
900
+ );
901
+ }
693
902
  if (event.type === "result") {
694
903
  recordStreamResult(result, event);
695
904
  }
696
905
  }
697
906
  return result;
698
907
  }
908
+ function summarizeShellToolCallEvent(event) {
909
+ const shell = shellPayloadFromCursorEvent(event);
910
+ if (!shell) return void 0;
911
+ const outcome = classifyShellCommandOutcome({
912
+ command: shell.command,
913
+ exitCode: shell.exitCode,
914
+ stdout: shell.stdout,
915
+ stderr: shell.stderr,
916
+ interleavedOutput: shell.interleaved
917
+ });
918
+ const cmd = oneLine(shell.command).slice(0, 120);
919
+ if (outcome.kind === "audit_findings") {
920
+ return `[audit:findings] ${outcome.summary}${cmd ? ` \xB7 ${cmd}` : ""}`;
921
+ }
922
+ if (outcome.kind === "command_failure") {
923
+ return `[command:failed] ${outcome.summary}${cmd ? ` \xB7 ${cmd}` : ""}`;
924
+ }
925
+ return `[command:ok] exit 0${cmd ? ` \xB7 ${cmd}` : ""}`;
926
+ }
699
927
  function summarizeEvent(event) {
700
928
  if (event.type === "system" && event.subtype) {
701
929
  return `[system:${event.subtype}] ${String(event.status || event.cwd || "")}`.trim();
@@ -728,6 +956,8 @@ function summarizeEvent(event) {
728
956
  }
729
957
  if (event.type === "tool_call") {
730
958
  const subtype = String(event.subtype || "");
959
+ const shellSummary = subtype === "completed" ? summarizeShellToolCallEvent(event) : void 0;
960
+ if (shellSummary) return shellSummary;
731
961
  const toolCall = event.tool_call && typeof event.tool_call === "object" && !Array.isArray(event.tool_call) ? event.tool_call : void 0;
732
962
  const name = cursorToolNameFromCall(toolCall) ?? "tool";
733
963
  return `[tool:${subtype}] ${name}`;
@@ -769,7 +999,7 @@ var FAILURE_PATTERNS = [
769
999
  label: "provider authentication failed"
770
1000
  }
771
1001
  ];
772
- function tidy(errorText, max = 240) {
1002
+ function tidy2(errorText, max = 240) {
773
1003
  const oneLine2 = errorText.replace(/\s+/g, " ").trim();
774
1004
  return oneLine2.length > max ? `${oneLine2.slice(0, max - 1)}\u2026` : oneLine2;
775
1005
  }
@@ -778,7 +1008,7 @@ function classifyExitFailure(errorText) {
778
1008
  if (!text) return null;
779
1009
  for (const pattern of FAILURE_PATTERNS) {
780
1010
  if (pattern.test.test(text)) {
781
- return { blocked: true, reason: `${pattern.label}: ${tidy(text)}` };
1011
+ return { blocked: true, reason: `${pattern.label}: ${tidy2(text)}` };
782
1012
  }
783
1013
  }
784
1014
  return null;
@@ -844,6 +1074,53 @@ function assessExitedWorkerSalvage(input) {
844
1074
 
845
1075
  // src/git.ts
846
1076
  import { spawnSync } from "node:child_process";
1077
+
1078
+ // src/worker-env.ts
1079
+ var FORBIDDEN_WORKER_ENV_KEYS = [
1080
+ "ANTHROPIC_API_KEY",
1081
+ "ANALYST_API_KEY",
1082
+ "RECRUITER_API_KEY",
1083
+ "AUTH_SECRET",
1084
+ "NEXTAUTH_SECRET",
1085
+ "DATABASE_URL",
1086
+ "PRODUCTION_DATABASE_URL",
1087
+ "REDIS_URL",
1088
+ "GOOGLE_CLIENT_SECRET",
1089
+ "GITHUB_CLIENT_SECRET",
1090
+ "KYNVER_API_KEY",
1091
+ "KYNVER_SERVICE_SECRET",
1092
+ "KYNVER_RUNTIME_SECRET",
1093
+ "OPENCLAW_CRON_SECRET",
1094
+ "QSTASH_TOKEN",
1095
+ "QSTASH_CURRENT_SIGNING_KEY",
1096
+ "QSTASH_NEXT_SIGNING_KEY",
1097
+ "TOOL_SECRETS_KEK",
1098
+ "TOOL_EXECUTOR_DISPATCH_SECRET",
1099
+ "CLOUDFLARE_API_TOKEN",
1100
+ "STRIPE_SECRET_KEY",
1101
+ "STRIPE_WEBHOOK_SECRET",
1102
+ "STRIPE_IDENTITY_WEBHOOK_SECRET",
1103
+ "VOYAGE_API_KEY",
1104
+ "PERPLEXITY_API_KEY",
1105
+ "FRED_API_KEY",
1106
+ "FMP_API_KEY",
1107
+ "CURSOR_API_KEY"
1108
+ ];
1109
+ var FORBIDDEN_KEY_SET = new Set(FORBIDDEN_WORKER_ENV_KEYS);
1110
+ var FORBIDDEN_SUFFIXES = ["_SECRET", "_API_KEY"];
1111
+ function isForbiddenWorkerEnvKey(key) {
1112
+ if (FORBIDDEN_KEY_SET.has(key)) return true;
1113
+ return FORBIDDEN_SUFFIXES.some((suffix) => key.endsWith(suffix));
1114
+ }
1115
+ function scrubWorkerEnv(env) {
1116
+ const next = { ...env };
1117
+ for (const key of Object.keys(next)) {
1118
+ if (isForbiddenWorkerEnvKey(key)) delete next[key];
1119
+ }
1120
+ return next;
1121
+ }
1122
+
1123
+ // src/git.ts
847
1124
  function git(cwd, args, options = {}) {
848
1125
  const res = spawnSync("git", args, { cwd, encoding: "utf8" });
849
1126
  if (res.status !== 0 && !options.allowFailure) {
@@ -959,11 +1236,6 @@ function unknownAncestry(base, error, head = null) {
959
1236
  error
960
1237
  };
961
1238
  }
962
- function scrubClaudeEnv(env) {
963
- const next = { ...env };
964
- delete next.ANTHROPIC_API_KEY;
965
- return next;
966
- }
967
1239
 
968
1240
  // src/landing-gate.ts
969
1241
  function trimOrNull2(value) {
@@ -1313,15 +1585,7 @@ function computeAutoMaxWorkers(totalMemBytes, opts = {}) {
1313
1585
  return Math.min(raw, AUTO_MAX_WORKERS_CEILING);
1314
1586
  }
1315
1587
  function readAvailableMemBytes() {
1316
- if (process.platform === "linux") {
1317
- try {
1318
- const meminfo = readFileSync5("/proc/meminfo", "utf8");
1319
- const match = meminfo.match(/^MemAvailable:\s+(\d+)\s*kB/m);
1320
- if (match) return Number(match[1]) * 1024;
1321
- } catch {
1322
- }
1323
- }
1324
- return os.freemem();
1588
+ return readMemAvailableBytes();
1325
1589
  }
1326
1590
  function isActiveHarnessWorker(worker) {
1327
1591
  const status = computeWorkerStatus(worker);
@@ -1349,7 +1613,7 @@ function observeRunnerResourceGate(input) {
1349
1613
  input.config,
1350
1614
  input.configuredMaxWorkersOverride
1351
1615
  );
1352
- const totalMemBytes = input.totalMemBytes ?? os.totalmem();
1616
+ const totalMemBytes = input.totalMemBytes ?? os2.totalmem();
1353
1617
  const freeMemBytes = input.freeMemBytes ?? readAvailableMemBytes();
1354
1618
  const activeWorkers = input.activeWorkers ?? countActiveWorkersGlobal();
1355
1619
  const budgetBytes = Math.max(0, Math.floor(totalMemBytes * memUtilization) - memReserveBytes);
@@ -1558,7 +1822,7 @@ var claudeProvider = {
1558
1822
  cwd: opts.worktreePath,
1559
1823
  detached: true,
1560
1824
  stdio,
1561
- env: scrubClaudeEnv(process.env)
1825
+ env: scrubWorkerEnv(process.env)
1562
1826
  })
1563
1827
  );
1564
1828
  closeSync(stdoutFd);
@@ -1835,6 +2099,7 @@ function buildPrompt(input) {
1835
2099
  "Structured final result (recommended): record completion as JSON with summary, laneExpertise { whatChanged, why, files, prUrls, verification, risks, blockers, lessonsLearned, laneGuidance }, and targetPrReconciliation [{ prUrl, outcome: merged|skipped|blocked, mergeCommit?, reason? }] for every target PR on landing-only tasks.",
1836
2100
  "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). Exiting with only dirty files and no PR routes to salvage review, not production review.",
1837
2101
  "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.",
2102
+ "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.",
1838
2103
  "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.",
1839
2104
  "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.",
1840
2105
  "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.",
@@ -1945,12 +2210,12 @@ function resolveAgentBin() {
1945
2210
  return "agent";
1946
2211
  }
1947
2212
  function cursorWorkerEnv(agentBin, spawnTarget) {
1948
- return {
2213
+ return scrubWorkerEnv({
1949
2214
  ...process.env,
1950
2215
  CI: "1",
1951
2216
  NO_COLOR: "1",
1952
2217
  ...spawnTarget.bundledVersionDir ? { CURSOR_INVOKED_AS: path9.basename(agentBin) || "agent.cmd" } : {}
1953
- };
2218
+ });
1954
2219
  }
1955
2220
  var cursorProvider = {
1956
2221
  name: "cursor",
@@ -2043,6 +2308,65 @@ function persistCompletionAck(worker, runId, fields) {
2043
2308
  // src/worker-ops.ts
2044
2309
  import path11 from "node:path";
2045
2310
 
2311
+ // src/completion-response.ts
2312
+ function asRecord(value) {
2313
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
2314
+ }
2315
+ function asString(value) {
2316
+ if (typeof value !== "string") return null;
2317
+ const trimmed = value.trim();
2318
+ return trimmed.length ? trimmed : null;
2319
+ }
2320
+ var ADVANCED_OUTCOMES = /* @__PURE__ */ new Set([
2321
+ "review_scheduled",
2322
+ "review_already_scheduled"
2323
+ ]);
2324
+ function summarizeHarnessCompletionResponse(parsed) {
2325
+ const record = asRecord(parsed);
2326
+ if (!record) {
2327
+ return { routeOutcome: null, taskAdvanced: false, detail: null };
2328
+ }
2329
+ const outcome = asString(record.outcome);
2330
+ const detail = asString(record.detail) ?? asString(record.error);
2331
+ const task = asRecord(record.task);
2332
+ const taskStatus = task ? asString(task.status) : null;
2333
+ const taskAdvanced = outcome !== null && ADVANCED_OUTCOMES.has(outcome) || taskStatus === "awaiting_review" || taskStatus === "done";
2334
+ return {
2335
+ routeOutcome: outcome,
2336
+ taskAdvanced,
2337
+ detail
2338
+ };
2339
+ }
2340
+ function completionPostSucceeded(summary) {
2341
+ return summary.taskAdvanced;
2342
+ }
2343
+
2344
+ // src/harness-expert-review.ts
2345
+ var EXPERT_LANE_REVIEW_REF = "expert-lane-pr-review:";
2346
+ var PLAN_REVIEW_EXECUTOR_REF = "plan-review-task";
2347
+ var SCHEDULED_JOB_EXECUTOR_REF = "scheduledjob:";
2348
+ function normalizePersonaSlug(value) {
2349
+ if (!value) return null;
2350
+ const t = value.trim().toLowerCase();
2351
+ return t.length ? t : null;
2352
+ }
2353
+ function isHarnessExpertReviewWorker(worker) {
2354
+ const ref = (worker.executorRef ?? "").toLowerCase();
2355
+ if (ref.startsWith(EXPERT_LANE_REVIEW_REF)) return true;
2356
+ if (ref === PLAN_REVIEW_EXECUTOR_REF || ref.startsWith("daemon-review:")) return true;
2357
+ if (ref.startsWith(SCHEDULED_JOB_EXECUTOR_REF) && worker.parentTaskId) {
2358
+ const persona = normalizePersonaSlug(worker.personaSlug);
2359
+ if (persona === "lorentz" || persona === "dalton") return true;
2360
+ }
2361
+ const title = (worker.title ?? "").toLowerCase();
2362
+ if (title.includes("expert pr review")) return true;
2363
+ if (worker.parentTaskId && (title.startsWith("review:") || title.includes("review required") || title.includes("runtime review"))) {
2364
+ const persona = normalizePersonaSlug(worker.personaSlug);
2365
+ if (persona === "lorentz" || persona === "dalton") return true;
2366
+ }
2367
+ return false;
2368
+ }
2369
+
2046
2370
  // src/pr-handoff/pr-handoff-assess.ts
2047
2371
  var REVIEW_LANE_RULE = /^(lane:)?(review|deep_review|planning|landing)(:|$)/i;
2048
2372
  function trimOrNull4(value) {
@@ -2073,6 +2397,14 @@ function assessPrHandoffRequirement(input) {
2073
2397
  if (!input.dispatched) {
2074
2398
  return { required: false, reason: "not_dispatched" };
2075
2399
  }
2400
+ if (isHarnessExpertReviewWorker({
2401
+ title: input.taskTitle ?? void 0,
2402
+ personaSlug: input.personaSlug,
2403
+ parentTaskId: input.parentTaskId,
2404
+ executorRef: input.executorRef
2405
+ })) {
2406
+ return { required: false, reason: "expert_review_task" };
2407
+ }
2076
2408
  const rule = trimOrNull4(input.routingRule) ?? "";
2077
2409
  if (rule && REVIEW_LANE_RULE.test(rule)) {
2078
2410
  return { required: false, reason: "review_lane" };
@@ -2080,7 +2412,7 @@ function assessPrHandoffRequirement(input) {
2080
2412
  if (trimOrNull4(input.patchPath) || trimOrNull4(input.artifactBundlePath)) {
2081
2413
  return { required: false, reason: "patch_or_bundle" };
2082
2414
  }
2083
- const prUrl = trimOrNull4(input.prUrl) ?? trimOrNull4(input.snapshot.prUrl);
2415
+ const prUrl = trimOrNull4(input.prUrl) ?? trimOrNull4(input.taskPrUrl) ?? trimOrNull4(input.snapshot.prUrl);
2084
2416
  if (prUrl) {
2085
2417
  return { required: false, reason: "already_has_pr" };
2086
2418
  }
@@ -2103,6 +2435,36 @@ function buildPrHandoffSnapshotFromStatus(status, extras) {
2103
2435
 
2104
2436
  // src/pr-handoff/pr-handoff-gh.ts
2105
2437
  import { spawnSync as spawnSync2 } from "node:child_process";
2438
+
2439
+ // src/github-repo.ts
2440
+ function parseGithubOwnerRepo(remoteUrl) {
2441
+ const trimmed = remoteUrl.trim();
2442
+ if (!trimmed) return null;
2443
+ const ssh = trimmed.match(/^git@github\.com:([^/]+\/[^/\s]+?)(?:\.git)?$/i);
2444
+ if (ssh) return normalizeOwnerRepo(ssh[1]);
2445
+ const scp = trimmed.match(/^ssh:\/\/git@github\.com\/([^/]+\/[^/\s]+?)(?:\.git)?$/i);
2446
+ if (scp) return normalizeOwnerRepo(scp[1]);
2447
+ try {
2448
+ const url = new URL(trimmed.includes("://") ? trimmed : `https://${trimmed}`);
2449
+ if (url.hostname.toLowerCase() !== "github.com") return null;
2450
+ const parts = url.pathname.replace(/^\/+|\/+$/g, "").split("/");
2451
+ if (parts.length < 2) return null;
2452
+ const [owner, repo] = parts;
2453
+ if (!owner || !repo) return null;
2454
+ return `${owner}/${repo.replace(/\.git$/i, "")}`;
2455
+ } catch {
2456
+ return null;
2457
+ }
2458
+ }
2459
+ function normalizeOwnerRepo(value) {
2460
+ const parts = value.split("/").filter(Boolean);
2461
+ if (parts.length < 2) return null;
2462
+ const owner = parts[0];
2463
+ const repo = parts[1].replace(/\.git$/i, "");
2464
+ return owner && repo ? `${owner}/${repo}` : null;
2465
+ }
2466
+
2467
+ // src/pr-handoff/pr-handoff-gh.ts
2106
2468
  function capture(bin, cwd, args) {
2107
2469
  try {
2108
2470
  const res = spawnSync2(bin, args, { cwd, encoding: "utf8" });
@@ -2125,21 +2487,13 @@ var defaultPrHandoffExec = {
2125
2487
  git: (cwd, args) => gitCapture(cwd, args),
2126
2488
  gh: (cwd, args) => capture("gh", cwd, args)
2127
2489
  };
2128
- function parseGithubRepo(remoteUrl) {
2129
- const trimmed = remoteUrl.trim();
2130
- const ssh = trimmed.match(/git@github\.com:([^/]+\/[^/.]+)(?:\.git)?/i);
2131
- if (ssh) return ssh[1];
2132
- const https = trimmed.match(/github\.com[/:]([^/]+\/[^/.]+?)(?:\.git)?/i);
2133
- if (https) return https[1];
2134
- return null;
2135
- }
2136
2490
  function firstLine(text) {
2137
2491
  return text.split("\n").map((l) => l.trim()).find(Boolean) ?? "";
2138
2492
  }
2139
2493
  function resolveGithubRepo(worktreePath, exec) {
2140
2494
  const remote = exec.git(worktreePath, ["remote", "get-url", "origin"]);
2141
2495
  if (remote.status !== 0) return null;
2142
- return parseGithubRepo(remote.stdout);
2496
+ return parseGithubOwnerRepo(remote.stdout);
2143
2497
  }
2144
2498
  function resolveHeadCommit(worktreePath, exec) {
2145
2499
  const head = exec.git(worktreePath, ["rev-parse", "HEAD"]);
@@ -2275,6 +2629,11 @@ function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
2275
2629
  dispatched: input.worker.dispatched,
2276
2630
  routingRule: input.worker.routingRule,
2277
2631
  prUrl: prUrlHint,
2632
+ taskTitle: input.worker.taskTitle,
2633
+ executorRef: input.worker.executorRef,
2634
+ parentTaskId: input.worker.parentTaskId,
2635
+ personaSlug: input.worker.personaSlug,
2636
+ taskPrUrl: input.worker.taskPrUrl,
2278
2637
  snapshot
2279
2638
  });
2280
2639
  if (!requirement.required) {
@@ -2473,10 +2832,10 @@ function completionErrorText(parsed) {
2473
2832
  }
2474
2833
  return void 0;
2475
2834
  }
2476
- function asRecord(value) {
2835
+ function asRecord2(value) {
2477
2836
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
2478
2837
  }
2479
- function asString(value) {
2838
+ function asString2(value) {
2480
2839
  if (typeof value !== "string") return null;
2481
2840
  const trimmed = value.trim();
2482
2841
  return trimmed.length ? trimmed : null;
@@ -2596,7 +2955,27 @@ async function tryCompleteWorker(args) {
2596
2955
  }
2597
2956
  }
2598
2957
  if (result.ok) {
2958
+ const summary = summarizeHarnessCompletionResponse(result.parsed);
2959
+ if (!completionPostSucceeded(summary)) {
2960
+ const detail2 = summary.detail ?? (summary.routeOutcome ? `harness completion returned ${summary.routeOutcome}` : "harness completion did not advance the linked task");
2961
+ const reason2 = `completion acknowledged but board not advanced: ${detail2}`;
2962
+ persistCompletionBlocker(worker, reason2);
2963
+ const ack2 = {
2964
+ completionReportedAt: (/* @__PURE__ */ new Date()).toISOString(),
2965
+ completionOutcome: "rejected",
2966
+ completionResponse: result.parsed
2967
+ };
2968
+ persistCompletionAck(worker, worker.runId, ack2);
2969
+ return {
2970
+ ok: false,
2971
+ httpStatus: result.status,
2972
+ response: result.parsed,
2973
+ reason: reason2,
2974
+ completionBlocked: true
2975
+ };
2976
+ }
2599
2977
  persistCompletionBlocker(worker, void 0);
2978
+ const routeOutcome = summary.routeOutcome ?? "acknowledged";
2600
2979
  const ack = {
2601
2980
  completionReportedAt: (/* @__PURE__ */ new Date()).toISOString(),
2602
2981
  completionOutcome: "acknowledged",
@@ -2609,6 +2988,7 @@ async function tryCompleteWorker(args) {
2609
2988
  ok: true,
2610
2989
  httpStatus: result.status,
2611
2990
  response: result.parsed,
2991
+ reason: routeOutcome,
2612
2992
  ...prUrl ? { prHandoff: { prUrl } } : {}
2613
2993
  };
2614
2994
  }
@@ -2701,13 +3081,13 @@ function buildRunBoard(runId) {
2701
3081
  const completionBlocker = typeof rawBlocker === "string" && rawBlocker ? rawBlocker : void 0;
2702
3082
  const boardStatus = completionBlocker ? "blocked" : status.status;
2703
3083
  const boardAttention = completionBlocker ? "blocked" : status.attention.state;
2704
- const completionResponse = asRecord(worker.completionResponse);
2705
- const completionTask = asRecord(completionResponse?.task);
2706
- const completionOutcome = asString(completionResponse?.outcome);
2707
- const completionRouteStatus = asString(completionResponse?.status);
3084
+ const completionResponse = asRecord2(worker.completionResponse);
3085
+ const completionTask = asRecord2(completionResponse?.task);
3086
+ const completionOutcome = asString2(completionResponse?.outcome);
3087
+ const completionRouteStatus = asString2(completionResponse?.status);
2708
3088
  const completionWarnings = Array.isArray(completionResponse?.warnings) ? completionResponse.warnings.filter((w) => typeof w === "string" && w.trim().length > 0) : [];
2709
- const prUrl = asString(completionTask?.prUrl) ?? asString(completionResponse?.prUrl);
2710
- const completionReportedAt = asString(worker.completionReportedAt);
3089
+ const prUrl = asString2(completionTask?.prUrl) ?? asString2(completionResponse?.prUrl);
3090
+ const completionReportedAt = asString2(worker.completionReportedAt);
2711
3091
  const lifecycleStage = deriveLifecycleStage({
2712
3092
  finished: isFinishedWorkerStatus(status),
2713
3093
  completionBlocker,
@@ -3133,6 +3513,10 @@ function spawnWorkerProcess(run, opts) {
3133
3513
  ...!opts.agentOsId || !opts.taskId ? { localOnly: true } : {},
3134
3514
  routingRule: routing.rule,
3135
3515
  ...routing.requestedModel ? { requestedModel: routing.requestedModel } : {},
3516
+ ...opts.executorRef ? { executorRef: String(opts.executorRef) } : {},
3517
+ ...opts.parentTaskId ? { parentTaskId: String(opts.parentTaskId) } : {},
3518
+ ...opts.taskTitle ? { taskTitle: String(opts.taskTitle) } : {},
3519
+ ...opts.taskPrUrl ? { taskPrUrl: String(opts.taskPrUrl) } : {},
3136
3520
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
3137
3521
  };
3138
3522
  saveWorker(run.id, worker);
@@ -3827,7 +4211,7 @@ function readHarnessWorkerContext(decision) {
3827
4211
  personaInjectionReady
3828
4212
  };
3829
4213
  }
3830
- function normalizePersonaSlug(value) {
4214
+ function normalizePersonaSlug2(value) {
3831
4215
  if (typeof value !== "string") return null;
3832
4216
  const trimmed = value.trim().toLowerCase();
3833
4217
  return trimmed.length ? trimmed : null;
@@ -3898,7 +4282,8 @@ async function dispatchRun(args) {
3898
4282
  harnessBoardSnapshot: buildRunBoard(run.id),
3899
4283
  ...args.lane ? { lane: String(args.lane) } : {},
3900
4284
  executor: args.executor ? String(args.executor) : "harness",
3901
- ...args.diskPath ? { diskPath: String(args.diskPath) } : {}
4285
+ ...args.diskPath ? { diskPath: String(args.diskPath) } : {},
4286
+ ...args.targetTaskId ? { targetTaskId: String(args.targetTaskId) } : {}
3902
4287
  };
3903
4288
  const dispatch = await postJsonWithCredentialRefresh(dispatchUrl, secret, body, { agentOsId, baseUrl: base });
3904
4289
  const responseBody = dispatch.response;
@@ -3957,7 +4342,7 @@ async function dispatchRun(args) {
3957
4342
  const task = decision.task;
3958
4343
  const harnessContext = readHarnessWorkerContext(decision);
3959
4344
  const taskId = String(task.id);
3960
- const expectedPersona = normalizePersonaSlug(task.personaSlug);
4345
+ const expectedPersona = normalizePersonaSlug2(task.personaSlug);
3961
4346
  if (expectedPersona && (!harnessContext?.personaInjectionReady || !harnessContext.personaMarkdown)) {
3962
4347
  outcomes.push({
3963
4348
  taskId,
@@ -4001,6 +4386,10 @@ async function dispatchRun(args) {
4001
4386
  agentOsId,
4002
4387
  taskId: String(task.id),
4003
4388
  planId,
4389
+ executorRef: task.executorRef ? String(task.executorRef) : void 0,
4390
+ parentTaskId: task.parentTaskId ? String(task.parentTaskId) : void 0,
4391
+ taskTitle: task.title ? String(task.title) : void 0,
4392
+ taskPrUrl: task.prUrl ? String(task.prUrl) : void 0,
4004
4393
  instructionPolicyMarkdown: harnessContext?.instructionPolicyMarkdown ?? null,
4005
4394
  instructionPolicyFingerprint: harnessContext?.instructionPolicyFingerprint ?? null,
4006
4395
  instructionPolicyEvidence: harnessContext?.instructionPolicyEvidence ?? null,
@@ -4191,7 +4580,7 @@ function failExists(message) {
4191
4580
  }
4192
4581
 
4193
4582
  // src/pipeline-tick.ts
4194
- import path29 from "node:path";
4583
+ import path30 from "node:path";
4195
4584
 
4196
4585
  // src/pipeline-dispatch.ts
4197
4586
  var RESERVED_REVIEW_STARTS = 1;
@@ -4897,13 +5286,62 @@ function isPipelineCleanupEnabled() {
4897
5286
  return process.env.KYNVER_PIPELINE_CLEANUP !== "0";
4898
5287
  }
4899
5288
 
5289
+ // src/installed-package-versions.ts
5290
+ import { readFile } from "node:fs/promises";
5291
+ import { homedir as homedir5 } from "node:os";
5292
+ import path29 from "node:path";
5293
+ var MANAGED_PACKAGES = [
5294
+ "@kynver-app/runtime",
5295
+ "@kynver-app/openclaw-agent-os",
5296
+ "@kynver-app/mcp-agent-os"
5297
+ ];
5298
+ function trim(value) {
5299
+ const out = value?.trim();
5300
+ return out ? out : null;
5301
+ }
5302
+ function unique(values) {
5303
+ return [...new Set(values.filter((value) => Boolean(value)))];
5304
+ }
5305
+ function moduleRoots() {
5306
+ const home = homedir5();
5307
+ const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ?? path29.join(home, ".openclaw", "npm");
5308
+ const npmGlobalRoot = trim(process.env.KYNVER_NPM_GLOBAL_ROOT) ?? trim(process.env.KYNVER_NPM_GLOBAL_MODULES_ROOT) ?? (trim(process.env.NPM_CONFIG_PREFIX) ? path29.join(trim(process.env.NPM_CONFIG_PREFIX), "lib", "node_modules") : path29.join(home, ".npm-global", "lib", "node_modules"));
5309
+ return unique([
5310
+ path29.join(openClawPrefix, "lib", "node_modules"),
5311
+ path29.join(openClawPrefix, "node_modules"),
5312
+ npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot : path29.join(npmGlobalRoot, "lib", "node_modules")
5313
+ ]);
5314
+ }
5315
+ async function readVersion(packageJsonPath) {
5316
+ try {
5317
+ const parsed = JSON.parse(await readFile(packageJsonPath, "utf8"));
5318
+ return typeof parsed.version === "string" && parsed.version.trim() ? parsed.version.trim() : null;
5319
+ } catch {
5320
+ return null;
5321
+ }
5322
+ }
5323
+ async function collectInstalledPackageVersions(observedAt = (/* @__PURE__ */ new Date()).toISOString()) {
5324
+ const roots = moduleRoots();
5325
+ const out = {};
5326
+ for (const packageName of MANAGED_PACKAGES) {
5327
+ for (const root of roots) {
5328
+ const packageJsonPath = path29.join(root, packageName, "package.json");
5329
+ const version = await readVersion(packageJsonPath);
5330
+ if (!version) continue;
5331
+ out[packageName] = { version, observedAt, path: packageJsonPath };
5332
+ break;
5333
+ }
5334
+ }
5335
+ return out;
5336
+ }
5337
+
4900
5338
  // src/pipeline-tick.ts
4901
5339
  async function completeFinishedWorkers(runId, args) {
4902
5340
  const run = loadRun(runId);
4903
5341
  const outcomes = [];
4904
5342
  for (const name of Object.keys(run.workers || {})) {
4905
5343
  const worker = readJson(
4906
- path29.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
5344
+ path30.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
4907
5345
  void 0
4908
5346
  );
4909
5347
  if (!worker?.taskId || worker.localOnly) continue;
@@ -4934,12 +5372,14 @@ async function postOperatorTick(agentOsId, runId, resourceGate, args) {
4934
5372
  const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
4935
5373
  const secret = await resolveCallbackSecretWithMint(args.secret ? String(args.secret) : void 0, agentOsId, { baseUrl: base });
4936
5374
  const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/operator/tick`;
5375
+ const packageVersions = await collectInstalledPackageVersions();
4937
5376
  const res = await postJson(url, secret, {
4938
5377
  agentOsId,
4939
5378
  runId,
4940
5379
  ingestHarness: true,
4941
5380
  harnessBoardSnapshot: buildRunBoard(runId),
4942
- resourceGate
5381
+ resourceGate,
5382
+ packageVersions
4943
5383
  });
4944
5384
  return { ok: res.ok, httpStatus: res.status, response: res.response };
4945
5385
  }
@@ -5042,6 +5482,231 @@ async function runDaemon(args) {
5042
5482
  console.error(JSON.stringify({ event: "daemon_stop", runId, agentOsId }));
5043
5483
  }
5044
5484
 
5485
+ // src/plan-progress.ts
5486
+ import path31 from "node:path";
5487
+
5488
+ // src/bounded-build/constants.ts
5489
+ var DEFAULT_BUILD_MEM_BUDGET_BYTES = 1536 * 1024 * 1024;
5490
+ var DEFAULT_BUILD_MEM_RESERVE_BYTES = 2 * 1024 * 1024 * 1024;
5491
+ var DEFAULT_NODE_OLD_SPACE_SIZE_MB = 1024;
5492
+ var DEFAULT_SYSTEMD_MEMORY_MAX = "1.5G";
5493
+ var DEFAULT_SYSTEMD_MEMORY_SWAP_MAX = "2G";
5494
+
5495
+ // src/bounded-build/node-options.ts
5496
+ var MAX_OLD_SPACE_RE = /--max-old-space-size=(\d+)/;
5497
+ function parsePositiveInt(value, fallback) {
5498
+ const n = Number(value);
5499
+ if (!Number.isFinite(n) || n <= 0) return fallback;
5500
+ return Math.floor(n);
5501
+ }
5502
+ function resolveNodeOldSpaceSizeMb() {
5503
+ return parsePositiveInt(process.env.KYNVER_NODE_OLD_SPACE_SIZE_MB, DEFAULT_NODE_OLD_SPACE_SIZE_MB);
5504
+ }
5505
+ function mergeNodeOptionsForBuildCheck(baseEnv = process.env) {
5506
+ const env = { ...baseEnv };
5507
+ if (process.env.KYNVER_NODE_OLD_SPACE_SIZE_MB === "0") {
5508
+ return env;
5509
+ }
5510
+ const existing = env.NODE_OPTIONS ?? "";
5511
+ if (MAX_OLD_SPACE_RE.test(existing)) {
5512
+ return env;
5513
+ }
5514
+ const mb = resolveNodeOldSpaceSizeMb();
5515
+ const flag = `--max-old-space-size=${mb}`;
5516
+ env.NODE_OPTIONS = existing.trim() ? `${existing.trim()} ${flag}` : flag;
5517
+ return env;
5518
+ }
5519
+ function formatNodeOptionsFlag(mb = resolveNodeOldSpaceSizeMb()) {
5520
+ return `--max-old-space-size=${mb}`;
5521
+ }
5522
+
5523
+ // src/bounded-build/systemd-wrap.ts
5524
+ import { spawnSync as spawnSync3 } from "node:child_process";
5525
+ var systemdAvailableCache;
5526
+ function isSystemdRunAvailable() {
5527
+ if (process.env.KYNVER_BUILD_SKIP_SYSTEMD === "1" || process.env.KYNVER_BUILD_SKIP_SYSTEMD === "true") {
5528
+ return false;
5529
+ }
5530
+ if (systemdAvailableCache !== void 0) return systemdAvailableCache;
5531
+ if (process.platform !== "linux") {
5532
+ systemdAvailableCache = false;
5533
+ return false;
5534
+ }
5535
+ const res = spawnSync3("systemd-run", ["--version"], { encoding: "utf8", stdio: ["ignore", "ignore", "pipe"] });
5536
+ systemdAvailableCache = res.status === 0;
5537
+ return systemdAvailableCache;
5538
+ }
5539
+ function buildSystemdRunArgv(opts) {
5540
+ const memoryMax = opts.memoryMax ?? process.env.KYNVER_BUILD_SYSTEMD_MEMORY_MAX ?? DEFAULT_SYSTEMD_MEMORY_MAX;
5541
+ const memorySwapMax = opts.memorySwapMax ?? process.env.KYNVER_BUILD_SYSTEMD_MEMORY_SWAP_MAX ?? DEFAULT_SYSTEMD_MEMORY_SWAP_MAX;
5542
+ const argv = [
5543
+ "systemd-run",
5544
+ "--scope",
5545
+ "--collect",
5546
+ "-p",
5547
+ `MemoryMax=${memoryMax}`,
5548
+ "-p",
5549
+ `MemorySwapMax=${memorySwapMax}`
5550
+ ];
5551
+ if (opts.cwd) {
5552
+ argv.push("--working-directory", opts.cwd);
5553
+ }
5554
+ argv.push("--", ...opts.command);
5555
+ return argv;
5556
+ }
5557
+
5558
+ // src/bounded-build/admission.ts
5559
+ import { spawnSync as spawnSync4 } from "node:child_process";
5560
+ function positiveInt3(value, fallback) {
5561
+ const n = Number(value);
5562
+ if (!Number.isFinite(n) || n <= 0) return fallback;
5563
+ return Math.floor(n);
5564
+ }
5565
+ function resolveBuildAdmissionConfig(config = loadUserConfig()) {
5566
+ const envBudget = process.env.KYNVER_BUILD_MEM_BUDGET_BYTES ? positiveInt3(process.env.KYNVER_BUILD_MEM_BUDGET_BYTES, DEFAULT_BUILD_MEM_BUDGET_BYTES) : void 0;
5567
+ const envReserve = process.env.KYNVER_BUILD_MEM_RESERVE_BYTES ? positiveInt3(process.env.KYNVER_BUILD_MEM_RESERVE_BYTES, DEFAULT_BUILD_MEM_RESERVE_BYTES) : void 0;
5568
+ return {
5569
+ perBuildBudgetBytes: envBudget ?? positiveInt3(config.perWorkerMemBytes, DEFAULT_BUILD_MEM_BUDGET_BYTES),
5570
+ reserveBytes: envReserve ?? positiveInt3(config.memReserveBytes, DEFAULT_BUILD_MEM_RESERVE_BYTES)
5571
+ };
5572
+ }
5573
+ var activeBuilds = 0;
5574
+ function registerBuildStart() {
5575
+ activeBuilds += 1;
5576
+ }
5577
+ function registerBuildEnd() {
5578
+ activeBuilds = Math.max(0, activeBuilds - 1);
5579
+ }
5580
+ function assessBuildAdmission(opts = {}) {
5581
+ const cfg = { ...resolveBuildAdmissionConfig(), ...opts };
5582
+ const memAvailableBytes = opts.memAvailableBytes ?? readMemAvailableBytes();
5583
+ const requiredBytes = cfg.perBuildBudgetBytes + cfg.reserveBytes;
5584
+ const admitted = memAvailableBytes >= requiredBytes;
5585
+ return {
5586
+ admitted,
5587
+ memAvailableBytes,
5588
+ requiredBytes,
5589
+ activeBuilds,
5590
+ reason: admitted ? null : `insufficient memory: need ${requiredBytes} bytes available (budget ${cfg.perBuildBudgetBytes} + reserve ${cfg.reserveBytes}), have ${memAvailableBytes}`
5591
+ };
5592
+ }
5593
+ function sleepMs2(ms) {
5594
+ if (ms <= 0) return;
5595
+ spawnSync4(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
5596
+ stdio: "ignore"
5597
+ });
5598
+ }
5599
+ function waitForBuildAdmission(timeoutMs, pollMs = 2e3, opts = {}) {
5600
+ const deadline = Date.now() + Math.max(0, timeoutMs);
5601
+ let verdict = assessBuildAdmission({
5602
+ ...opts,
5603
+ memAvailableBytes: opts.memAvailableBytes?.()
5604
+ });
5605
+ while (!verdict.admitted && Date.now() < deadline) {
5606
+ sleepMs2(Math.min(pollMs, deadline - Date.now()));
5607
+ verdict = assessBuildAdmission({
5608
+ ...opts,
5609
+ memAvailableBytes: opts.memAvailableBytes?.()
5610
+ });
5611
+ }
5612
+ return verdict;
5613
+ }
5614
+
5615
+ // src/bounded-build/exec.ts
5616
+ import { spawnSync as spawnSync5 } from "node:child_process";
5617
+ function envArgv(env) {
5618
+ const out = [];
5619
+ for (const [key, value] of Object.entries(env)) {
5620
+ if (value === void 0) continue;
5621
+ out.push(`${key}=${value}`);
5622
+ }
5623
+ return out;
5624
+ }
5625
+ function runSpawn(argv, opts) {
5626
+ const res = spawnSync5(argv[0], argv.slice(1), {
5627
+ cwd: opts.cwd,
5628
+ env: opts.env,
5629
+ encoding: "utf8",
5630
+ stdio: ["ignore", "pipe", "pipe"],
5631
+ shell: opts.shell,
5632
+ timeout: opts.timeoutMs
5633
+ });
5634
+ return {
5635
+ exitCode: res.status ?? 1,
5636
+ stdout: (res.stdout ?? "").trim(),
5637
+ stderr: (res.stderr ?? "").trim()
5638
+ };
5639
+ }
5640
+ function runBoundedBuildCheck(input) {
5641
+ const waitMs = input.waitForAdmissionMs ?? 6e5;
5642
+ const admission = waitMs > 0 ? waitForBuildAdmission(waitMs) : assessBuildAdmission();
5643
+ if (!admission.admitted) {
5644
+ return {
5645
+ ok: false,
5646
+ exitCode: 1,
5647
+ stdout: "",
5648
+ stderr: admission.reason ?? "build admission denied",
5649
+ admitted: false,
5650
+ wrappedWithSystemd: false,
5651
+ nodeOptionsFlag: formatNodeOptionsFlag(),
5652
+ admission,
5653
+ command: input.command
5654
+ };
5655
+ }
5656
+ const env = mergeNodeOptionsForBuildCheck({ ...process.env, ...input.env });
5657
+ const nodeOptionsFlag = formatNodeOptionsFlag();
5658
+ const useSystemd = isSystemdRunAvailable();
5659
+ registerBuildStart();
5660
+ try {
5661
+ let result;
5662
+ if (useSystemd) {
5663
+ const argv = buildSystemdRunArgv({
5664
+ cwd: input.cwd,
5665
+ command: ["/usr/bin/env", ...envArgv(env), "/bin/bash", "-lc", input.command]
5666
+ });
5667
+ result = runSpawn(argv, { cwd: input.cwd, env, timeoutMs: input.timeoutMs });
5668
+ } else {
5669
+ result = runSpawn([input.command], {
5670
+ cwd: input.cwd,
5671
+ env,
5672
+ shell: true,
5673
+ timeoutMs: input.timeoutMs
5674
+ });
5675
+ }
5676
+ return {
5677
+ ok: result.exitCode === 0,
5678
+ exitCode: result.exitCode,
5679
+ stdout: result.stdout,
5680
+ stderr: result.stderr,
5681
+ admitted: true,
5682
+ wrappedWithSystemd: useSystemd,
5683
+ nodeOptionsFlag,
5684
+ admission,
5685
+ command: input.command
5686
+ };
5687
+ } finally {
5688
+ registerBuildEnd();
5689
+ }
5690
+ }
5691
+
5692
+ // src/harness-verify.ts
5693
+ var DEFAULT_HARNESS_VERIFY_COMMANDS = ["npm run typecheck", "npm run test"];
5694
+ function runHarnessVerifyCommands(cwd, commands = DEFAULT_HARNESS_VERIFY_COMMANDS, opts = {}) {
5695
+ const steps = [];
5696
+ let passed = true;
5697
+ for (const command of commands) {
5698
+ const result = runBoundedBuildCheck({
5699
+ cwd,
5700
+ command,
5701
+ waitForAdmissionMs: opts.waitForAdmissionMs,
5702
+ timeoutMs: opts.timeoutMs
5703
+ });
5704
+ steps.push({ command, result });
5705
+ if (!result.ok) passed = false;
5706
+ }
5707
+ return { passed, steps };
5708
+ }
5709
+
5045
5710
  // src/plan-progress.ts
5046
5711
  function parseEvidenceArg(raw) {
5047
5712
  const idx = raw.indexOf(":");
@@ -5102,8 +5767,23 @@ async function emitPlanProgress(args) {
5102
5767
  }
5103
5768
  console.log(JSON.stringify(parsed, null, 2));
5104
5769
  }
5770
+ function verifyPlanLocal(args) {
5771
+ const worktree = required(args.worktree ? String(args.worktree) : void 0, "worktree");
5772
+ const cwd = path31.resolve(worktree);
5773
+ const summary = runHarnessVerifyCommands(cwd);
5774
+ const emitJson = args.json === true || args.json === "true";
5775
+ const payload = { passed: summary.passed, worktree: cwd, steps: summary.steps };
5776
+ if (emitJson) console.log(JSON.stringify(payload, null, 2));
5777
+ else console.log(summary.passed ? "local plan verify passed" : "local plan verify failed");
5778
+ if (!summary.passed) process.exit(1);
5779
+ }
5105
5780
  async function verifyPlan(args) {
5106
5781
  const planId = required(args.plan ? String(args.plan) : void 0, "plan");
5782
+ const localOnly = args.local === true || args.local === "true";
5783
+ if (localOnly) {
5784
+ verifyPlanLocal(args);
5785
+ return;
5786
+ }
5107
5787
  const slug = loadUserConfig().agentOsSlug;
5108
5788
  if (!slug) {
5109
5789
  console.error("requires agentOsSlug in ~/.kynver/config.json for verify (session route)");
@@ -5137,6 +5817,52 @@ async function verifyPlan(args) {
5137
5817
  console.log(JSON.stringify(parsed, null, 2));
5138
5818
  }
5139
5819
 
5820
+ // src/harness-verify-cli.ts
5821
+ import path32 from "node:path";
5822
+ function runHarnessVerifyCli(args) {
5823
+ const cwd = path32.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
5824
+ const emitJson = args.json === true || args.json === "true" || args.emitJson === true || args.emitJson === "true";
5825
+ const commands = [];
5826
+ const rawCmd = args.command;
5827
+ if (Array.isArray(rawCmd)) {
5828
+ for (const c of rawCmd) commands.push(String(c));
5829
+ } else if (typeof rawCmd === "string") {
5830
+ commands.push(rawCmd);
5831
+ }
5832
+ const summary = runHarnessVerifyCommands(
5833
+ cwd,
5834
+ commands.length ? commands : DEFAULT_HARNESS_VERIFY_COMMANDS,
5835
+ {
5836
+ waitForAdmissionMs: args.waitForAdmissionMs ? Number(args.waitForAdmissionMs) : void 0,
5837
+ timeoutMs: args.timeoutMs ? Number(args.timeoutMs) : void 0
5838
+ }
5839
+ );
5840
+ const payload = {
5841
+ passed: summary.passed,
5842
+ worktree: cwd,
5843
+ steps: summary.steps.map((s) => ({
5844
+ command: s.command,
5845
+ ok: s.result.ok,
5846
+ exitCode: s.result.exitCode,
5847
+ admitted: s.result.admitted,
5848
+ wrappedWithSystemd: s.result.wrappedWithSystemd,
5849
+ nodeOptionsFlag: s.result.nodeOptionsFlag,
5850
+ admission: s.result.admission,
5851
+ stderr: s.result.stderr.slice(0, 4e3)
5852
+ }))
5853
+ };
5854
+ if (emitJson) {
5855
+ console.log(JSON.stringify(payload, null, 2));
5856
+ } else {
5857
+ console.log(summary.passed ? "harness verify passed" : "harness verify failed");
5858
+ for (const step of payload.steps) {
5859
+ console.log(` ${step.ok ? "\u2713" : "\u2717"} ${step.command} (exit ${step.exitCode}, systemd=${step.wrappedWithSystemd})`);
5860
+ if (!step.ok && step.stderr) console.log(` ${step.stderr.split("\n")[0]}`);
5861
+ }
5862
+ }
5863
+ process.exit(summary.passed ? 0 : 1);
5864
+ }
5865
+
5140
5866
  // src/plan-persist-cli.ts
5141
5867
  import { readFileSync as readFileSync7 } from "node:fs";
5142
5868
  var OPERATIONS = ["create", "add_version", "update_metadata"];
@@ -5238,7 +5964,7 @@ function runCleanupCli(args) {
5238
5964
  }
5239
5965
 
5240
5966
  // src/monitor/monitor.service.ts
5241
- import path31 from "node:path";
5967
+ import path34 from "node:path";
5242
5968
 
5243
5969
  // src/monitor/monitor.classify.ts
5244
5970
  function expectedLeaseOwner(runId) {
@@ -5295,10 +6021,10 @@ function classifyWorkerHealth(input) {
5295
6021
 
5296
6022
  // src/monitor/monitor.store.ts
5297
6023
  import { existsSync as existsSync17, mkdirSync as mkdirSync6, readdirSync as readdirSync7, unlinkSync as unlinkSync2 } from "node:fs";
5298
- import path30 from "node:path";
6024
+ import path33 from "node:path";
5299
6025
  function monitorsDir() {
5300
6026
  const { harnessRoot } = getHarnessPaths();
5301
- const dir = path30.join(harnessRoot, "monitors");
6027
+ const dir = path33.join(harnessRoot, "monitors");
5302
6028
  mkdirSync6(dir, { recursive: true });
5303
6029
  return dir;
5304
6030
  }
@@ -5306,7 +6032,7 @@ function monitorIdFor(runId, workerName) {
5306
6032
  return workerName ? `${safeSlug(runId)}--${safeSlug(workerName)}` : safeSlug(runId);
5307
6033
  }
5308
6034
  function monitorPath(monitorId) {
5309
- return path30.join(monitorsDir(), `${monitorId}.json`);
6035
+ return path33.join(monitorsDir(), `${monitorId}.json`);
5310
6036
  }
5311
6037
  function loadMonitorSession(monitorId) {
5312
6038
  return readJson(monitorPath(monitorId), void 0);
@@ -5327,7 +6053,7 @@ function listMonitorSessions() {
5327
6053
  for (const name of readdirSync7(dir)) {
5328
6054
  if (!name.endsWith(".json")) continue;
5329
6055
  const session = readJson(
5330
- path30.join(dir, name),
6056
+ path33.join(dir, name),
5331
6057
  void 0
5332
6058
  );
5333
6059
  if (!session?.monitorId) continue;
@@ -5418,7 +6144,7 @@ async function fetchTaskLeasesForWorkers(input) {
5418
6144
  // src/monitor/monitor.service.ts
5419
6145
  function workerRecord2(runId, name) {
5420
6146
  return readJson(
5421
- path31.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
6147
+ path34.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
5422
6148
  void 0
5423
6149
  );
5424
6150
  }
@@ -5621,17 +6347,17 @@ async function runMonitorLoop(args) {
5621
6347
  // src/monitor/monitor-spawn.ts
5622
6348
  import { spawn as spawn4 } from "node:child_process";
5623
6349
  import { closeSync as closeSync4, existsSync as existsSync18, openSync as openSync4 } from "node:fs";
5624
- import path32 from "node:path";
6350
+ import path35 from "node:path";
5625
6351
  import { fileURLToPath as fileURLToPath2 } from "node:url";
5626
6352
  function resolveDefaultCliPath2() {
5627
- return path32.join(fileURLToPath2(new URL(".", import.meta.url)), "..", "cli.js");
6353
+ return path35.join(fileURLToPath2(new URL(".", import.meta.url)), "..", "cli.js");
5628
6354
  }
5629
6355
  function spawnMonitorSidecar(opts) {
5630
6356
  const cliPath = opts.cliPath ?? resolveDefaultCliPath2();
5631
6357
  if (!existsSync18(cliPath)) return void 0;
5632
6358
  const monitorId = monitorIdFor(opts.runId, opts.workerName);
5633
6359
  const { harnessRoot } = getHarnessPaths();
5634
- const logPath = path32.join(harnessRoot, "monitors", `${monitorId}.log`);
6360
+ const logPath = path35.join(harnessRoot, "monitors", `${monitorId}.log`);
5635
6361
  let logFd;
5636
6362
  try {
5637
6363
  logFd = openSync4(logPath, "a");
@@ -5787,16 +6513,16 @@ function handleCliVersionFlag(argv, moduleUrl = import.meta.url, binName) {
5787
6513
  }
5788
6514
 
5789
6515
  // src/doctor/runtime-takeover.ts
5790
- import path34 from "node:path";
6516
+ import path37 from "node:path";
5791
6517
 
5792
6518
  // src/doctor/runtime-takeover.probes.ts
5793
6519
  import { accessSync, constants, existsSync as existsSync20, readFileSync as readFileSync9 } from "node:fs";
5794
- import { homedir as homedir5 } from "node:os";
5795
- import path33 from "node:path";
5796
- import { spawnSync as spawnSync3 } from "node:child_process";
6520
+ import { homedir as homedir6 } from "node:os";
6521
+ import path36 from "node:path";
6522
+ import { spawnSync as spawnSync6 } from "node:child_process";
5797
6523
  function captureCommand(bin, args) {
5798
6524
  try {
5799
- const res = spawnSync3(bin, args, { encoding: "utf8" });
6525
+ const res = spawnSync6(bin, args, { encoding: "utf8" });
5800
6526
  const stdout = (res.stdout || "").trim();
5801
6527
  const stderr = (res.stderr || "").trim();
5802
6528
  const ok = res.status === 0;
@@ -5834,10 +6560,10 @@ var defaultRuntimeTakeoverProbes = {
5834
6560
  commandOnPath: (bin) => captureCommand(process.platform === "win32" ? "where" : "which", [bin]),
5835
6561
  kynverVersion: (bin) => captureCommand(bin, ["--version"]),
5836
6562
  loadConfig: () => loadUserConfig(),
5837
- configFilePath: () => path33.join(homedir5(), ".kynver", "config.json"),
5838
- credentialsFilePath: () => path33.join(homedir5(), ".kynver", "credentials"),
6563
+ configFilePath: () => path36.join(homedir6(), ".kynver", "config.json"),
6564
+ credentialsFilePath: () => path36.join(homedir6(), ".kynver", "credentials"),
5839
6565
  readCredentials: () => {
5840
- const credPath = path33.join(homedir5(), ".kynver", "credentials");
6566
+ const credPath = path36.join(homedir6(), ".kynver", "credentials");
5841
6567
  if (!existsSync20(credPath)) {
5842
6568
  return { hasApiKey: false };
5843
6569
  }
@@ -5863,7 +6589,7 @@ var defaultRuntimeTakeoverProbes = {
5863
6589
  kynverSchedulerProvider: process.env.KYNVER_SCHEDULER_PROVIDER?.trim() || void 0
5864
6590
  }),
5865
6591
  harnessRoot: () => resolveHarnessRoot(),
5866
- legacyOpenclawHarnessRoot: () => path33.join(homedir5(), ".openclaw", "harness"),
6592
+ legacyOpenclawHarnessRoot: () => path36.join(homedir6(), ".openclaw", "harness"),
5867
6593
  pathExists: (target) => existsSync20(target),
5868
6594
  pathWritable: (target) => isWritable(target),
5869
6595
  vercelVersion: () => captureCommand("vercel", ["--version"]),
@@ -6049,8 +6775,8 @@ function assessVercelCli(probes) {
6049
6775
  }
6050
6776
  function assessHarnessDirs(probes) {
6051
6777
  const harnessRoot = probes.harnessRoot();
6052
- const runsDir = path34.join(harnessRoot, "runs");
6053
- const worktreesDir = path34.join(harnessRoot, "worktrees");
6778
+ const runsDir = path37.join(harnessRoot, "runs");
6779
+ const worktreesDir = path37.join(harnessRoot, "worktrees");
6054
6780
  const displayHarnessRoot = redactHomePath(harnessRoot);
6055
6781
  const displayRunsDir = redactHomePath(runsDir);
6056
6782
  const displayWorktreesDir = redactHomePath(worktreesDir);
@@ -6202,6 +6928,37 @@ function runRuntimeTakeoverDoctorCli() {
6202
6928
  }
6203
6929
  }
6204
6930
 
6931
+ // src/command-center-contract-cli.ts
6932
+ async function runCommandCenterContractCli(args) {
6933
+ const config = loadUserConfig();
6934
+ const agentOsId = (args.agentOsId ? String(args.agentOsId) : config.agentOsId) || "";
6935
+ if (!agentOsId) {
6936
+ console.error("requires --agent-os-id or agentOsId in ~/.kynver/config.json");
6937
+ process.exit(1);
6938
+ }
6939
+ const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : config.apiBaseUrl);
6940
+ const secret = await resolveCallbackSecretWithMint(
6941
+ args.secret ? String(args.secret) : void 0,
6942
+ agentOsId,
6943
+ { baseUrl: base }
6944
+ );
6945
+ const qs = new URLSearchParams();
6946
+ if (typeof args.since === "string" && args.since.trim()) qs.set("since", args.since.trim());
6947
+ if (args.limit != null && String(args.limit).trim()) {
6948
+ const n = Number(args.limit);
6949
+ if (Number.isFinite(n) && n > 0) qs.set("limit", String(Math.floor(n)));
6950
+ }
6951
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
6952
+ const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/command-center/dashboard-contract${suffix}`;
6953
+ const res = await getJson(url, secret);
6954
+ if (!res.ok) {
6955
+ console.error(`dashboard-contract GET failed: HTTP ${res.status}`);
6956
+ if (res.response) console.error(JSON.stringify(res.response, null, 2));
6957
+ process.exit(1);
6958
+ }
6959
+ console.log(JSON.stringify(res.response, null, 2));
6960
+ }
6961
+
6205
6962
  // src/cli.ts
6206
6963
  function isHelpFlag(arg) {
6207
6964
  return arg === "help" || arg === "--help" || arg === "-h";
@@ -6223,7 +6980,7 @@ function usage(code = 0) {
6223
6980
  " kynver run create --repo /path/repo [--name name] [--base origin/main]",
6224
6981
  " kynver run list",
6225
6982
  " kynver run status --run RUN_ID",
6226
- " kynver run dispatch --run RUN_ID --agent-os-id AOS_ID [--base-url URL] [--secret SECRET] [--execute] [--lane any|implementation|review|landing] [--executor harness] [--max-starts 1] [--lease-ms MS] [--owned path[,path]] [--model claude-opus-4-8] [--disk-path /]",
6983
+ " kynver run dispatch --run RUN_ID --agent-os-id AOS_ID [--base-url URL] [--secret SECRET] [--execute] [--lane any|implementation|review|landing] [--target-task-id TASK_ID] [--executor harness] [--max-starts 1] [--lease-ms MS] [--owned path[,path]] [--model claude-opus-4-8] [--disk-path /]",
6227
6984
  " kynver run sweep --run RUN_ID --agent-os-id AOS_ID [--base-url URL] [--secret SECRET] [--grace-ms MS]",
6228
6985
  ' kynver worker start --run RUN_ID --name worker --task "..." [--owned path[,path]] [--model MODEL] [--provider claude|cursor] [--agent-os-id AOS_ID] [--task-id TASK_ID] [--wait]',
6229
6986
  " kynver worker status --run RUN_ID --name worker",
@@ -6233,7 +6990,8 @@ function usage(code = 0) {
6233
6990
  " kynver worker auto-complete --run RUN_ID --name worker [--agent-os-id AOS_ID] [--poll-ms 5000] [--max-total-ms 21600000] [--complete-attempts 3] [--complete-backoff-ms 5000] [--base-url URL] [--secret SECRET]",
6234
6991
  " kynver run reconcile",
6235
6992
  " kynver plan progress --plan PLAN_ID --row ROW_KEY --role ROLE --status STATUS [--task TASK_ID] [--note NOTE] [--evidence type:value] [--agent-os-id AOS_ID]",
6236
- " kynver plan verify --plan PLAN_ID [--worktree PATH] [--task TASK_ID] [--human-override]",
6993
+ " kynver plan verify --plan PLAN_ID [--worktree PATH] [--task TASK_ID] [--human-override] [--local]",
6994
+ " kynver harness verify --worktree PATH [--command CMD] [--json] [--wait-for-admission-ms MS] [--timeout-ms MS]",
6237
6995
  " kynver plan persist --operation create|add_version|update_metadata --title TITLE (--body-file PATH | --body TEXT) [--slug SLUG] [--plan PLAN_ID] [--summary TEXT] [--failure-kind approval_guard|auth|network|server|tool_interruption]",
6238
6996
  " kynver plan outbox list",
6239
6997
  " kynver plan outbox drain [--max N] [--id OUTBOX_ID]",
@@ -6245,7 +7003,8 @@ function usage(code = 0) {
6245
7003
  " kynver monitor tick --run RUN_ID [--name worker] [--agent-os-id AOS_ID] [--auto-complete] [--renew-leases]",
6246
7004
  " kynver monitor auto-complete --run RUN_ID --name worker [--agent-os-id AOS_ID] [--base-url URL] [--secret SECRET]",
6247
7005
  " kynver monitor run-loop --run RUN_ID --monitor-id ID [--name worker] [--agent-os-id AOS_ID] [--poll-ms MS] [--auto-complete] [--renew-leases]",
6248
- " kynver doctor runtime-takeover"
7006
+ " kynver doctor runtime-takeover",
7007
+ " kynver board contract [--agent-os-id ID] [--base-url URL] [--since ISO] [--limit N]"
6249
7008
  ].join("\n")
6250
7009
  );
6251
7010
  process.exit(code);
@@ -6256,7 +7015,7 @@ async function main(argv = process.argv.slice(2)) {
6256
7015
  const scope = argv.shift();
6257
7016
  let action;
6258
7017
  let rest;
6259
- if (scope === "run" || scope === "worker" || scope === "plan" || scope === "runner" || scope === "monitor" || scope === "doctor") {
7018
+ if (scope === "run" || scope === "worker" || scope === "plan" || scope === "runner" || scope === "harness" || scope === "monitor" || scope === "doctor" || scope === "board") {
6260
7019
  action = argv.shift();
6261
7020
  rest = argv;
6262
7021
  } else {
@@ -6273,6 +7032,7 @@ async function main(argv = process.argv.slice(2)) {
6273
7032
  if (scope === "daemon") return void await runDaemon(args);
6274
7033
  if (scope === "plan" && action === "progress") return void await emitPlanProgress(args);
6275
7034
  if (scope === "plan" && action === "verify") return void await verifyPlan(args);
7035
+ if (scope === "harness" && action === "verify") return runHarnessVerifyCli(args);
6276
7036
  if (scope === "plan" && action === "persist") return void await runPlanPersist(args);
6277
7037
  if (scope === "plan" && action === "outbox") {
6278
7038
  const outboxAction = rest.shift();
@@ -6282,6 +7042,9 @@ async function main(argv = process.argv.slice(2)) {
6282
7042
  }
6283
7043
  if (scope === "cleanup") return runCleanupCli(args);
6284
7044
  if (scope === "doctor" && action === "runtime-takeover") return runRuntimeTakeoverDoctorCli();
7045
+ if (scope === "board" && action === "contract") {
7046
+ return void await runCommandCenterContractCli(args);
7047
+ }
6285
7048
  if (scope === "run" && action === "create") return createRun(args);
6286
7049
  if (scope === "run" && action === "list") return listRuns();
6287
7050
  if (scope === "run" && action === "status") return runStatus(args);