progmune-runtime 2.1.6 → 3.2.0

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 (356) hide show
  1. package/README.md +108 -468
  2. package/dist/ablation-study.js +144 -0
  3. package/dist/ablation-study.test.js +18 -0
  4. package/dist/action-runtime.js +3 -1
  5. package/dist/active-learning.js +211 -0
  6. package/dist/analytics.js +139 -0
  7. package/dist/asset-factory.js +309 -0
  8. package/dist/asset-growth.js +244 -0
  9. package/dist/asset-promotion.js +382 -0
  10. package/dist/asset-quality.js +550 -0
  11. package/dist/audit/business-translator.js +285 -0
  12. package/dist/audit/cli.js +66 -0
  13. package/dist/audit/formatters/html.js +379 -0
  14. package/dist/audit/formatters/json.js +11 -0
  15. package/dist/audit/formatters/markdown.js +192 -0
  16. package/dist/audit/formatters/terminal.js +189 -0
  17. package/dist/audit/index.js +25 -0
  18. package/dist/audit/report-builder.js +318 -0
  19. package/dist/audit/types.js +8 -0
  20. package/dist/audit.js +3 -3
  21. package/dist/auto-benchmark-generator.js +137 -0
  22. package/dist/auto-benchmark-generator.test.js +45 -0
  23. package/dist/auto-protocol-synthesizer.js +362 -0
  24. package/dist/auto-protocol-synthesizer.test.js +82 -0
  25. package/dist/autonomous-patch.js +175 -0
  26. package/dist/autonomous-patch.test.js +128 -0
  27. package/dist/badge/badge-server.js +98 -0
  28. package/dist/behavior-miner.js +442 -0
  29. package/dist/belief-layer.js +475 -0
  30. package/dist/benchmark-count.js +5 -0
  31. package/dist/benchmark-generator.js +211 -0
  32. package/dist/benchmark-harness.js +201 -0
  33. package/dist/benchmark-pass-rate.js +7 -0
  34. package/dist/benchmark-report.js +8 -3
  35. package/dist/benchmark-save.js +14 -1
  36. package/dist/bootstrap-validation.js +197 -0
  37. package/dist/bootstrap-validation.test.js +51 -0
  38. package/dist/branch-ledger.js +1 -1
  39. package/dist/capability-gap.js +130 -0
  40. package/dist/certify-html.js +351 -0
  41. package/dist/certify.js +326 -0
  42. package/dist/check.js +4 -4
  43. package/dist/compliance-miner.js +447 -0
  44. package/dist/continuous-benchmark.js +194 -0
  45. package/dist/continuous-benchmark.test.js +116 -0
  46. package/dist/corpus-stats.js +173 -0
  47. package/dist/counterfactual-engine.js +288 -0
  48. package/dist/coverage-dashboard.js +109 -0
  49. package/dist/coverage-system.test.js +205 -0
  50. package/dist/cross-repo-precision.js +352 -0
  51. package/dist/cve-benchmark.js +180 -0
  52. package/dist/cve-benchmark.test.js +28 -0
  53. package/dist/cve-collector.js +73 -0
  54. package/dist/data-quality.js +141 -0
  55. package/dist/decision-engine.js +388 -0
  56. package/dist/derive-metadata.js +250 -0
  57. package/dist/difficulty-active.test.js +198 -0
  58. package/dist/difficulty-map.js +244 -0
  59. package/dist/discovery-analytics.js +125 -0
  60. package/dist/discovery-model.js +149 -0
  61. package/dist/discovery-optimize.test.js +199 -0
  62. package/dist/discovery-trace.js +276 -0
  63. package/dist/discovery-trace.test.js +97 -0
  64. package/dist/emitter.js +83 -1
  65. package/dist/enterprise-dashboard.js +405 -0
  66. package/dist/eval-hardening.js +297 -0
  67. package/dist/eval-hardening.test.js +85 -0
  68. package/dist/evaluation-campaign.js +359 -0
  69. package/dist/evaluation-campaign.test.js +181 -0
  70. package/dist/evidence-growth.js +143 -0
  71. package/dist/evidence-repository.js +209 -0
  72. package/dist/evidence-system.js +441 -0
  73. package/dist/execute.js +15 -7
  74. package/dist/experimental/software-physics.js +291 -0
  75. package/dist/experimental/state-inference.js +516 -0
  76. package/dist/experimental/unsupervised-physics.js +230 -0
  77. package/dist/extract-ir-python.js +54 -7
  78. package/dist/extract-ir.js +376 -12
  79. package/dist/failure-collector.js +2 -2
  80. package/dist/failure-corpus.js +322 -9
  81. package/dist/feedback.js +16 -5
  82. package/dist/feedback.test.js +49 -0
  83. package/dist/file-lock.js +1 -1
  84. package/dist/flywheel-batch.js +292 -0
  85. package/dist/frameworks/express-cli.js +237 -0
  86. package/dist/frameworks/express-detector.js +445 -0
  87. package/dist/frameworks/express-detector.test.js +206 -0
  88. package/dist/frameworks/index.js +30 -0
  89. package/dist/frameworks/nestjs-detector.js +302 -0
  90. package/dist/frameworks/trpc-detector.js +161 -0
  91. package/dist/frameworks/version-awareness.js +179 -0
  92. package/dist/function-synonyms.js +164 -0
  93. package/dist/function-synonyms.test.js +68 -0
  94. package/dist/generalization.test.js +352 -0
  95. package/dist/goal-annotator.js +113 -0
  96. package/dist/goal-planner.js +563 -0
  97. package/dist/gold-cve.js +164 -0
  98. package/dist/gold-cve.test.js +104 -0
  99. package/dist/gold-quality.js +206 -0
  100. package/dist/gold-tiers.js +241 -0
  101. package/dist/governance-dashboard.js +327 -0
  102. package/dist/graph-viz.js +240 -0
  103. package/dist/guided-frontier.js +195 -0
  104. package/dist/hierarchical-planner.js +148 -0
  105. package/dist/identifier-parser.js +260 -0
  106. package/dist/immune-metrics.js +93 -0
  107. package/dist/immune-receiver.js +158 -0
  108. package/dist/immune-reporter.js +1 -1
  109. package/dist/improvement-orchestrator.js +206 -0
  110. package/dist/inject-p0-vocabulary.js +300 -0
  111. package/dist/intent-parser.js +218 -0
  112. package/dist/invariant-algebra.js +476 -0
  113. package/dist/invariant-calculus.js +533 -0
  114. package/dist/ir-utils.js +70 -0
  115. package/dist/ir-utils.test.js +50 -0
  116. package/dist/knowledge-api.js +312 -0
  117. package/dist/knowledge-evolution.js +452 -0
  118. package/dist/knowledge-explorer.js +506 -0
  119. package/dist/knowledge-flywheel.js +274 -0
  120. package/dist/knowledge-governance.js +338 -0
  121. package/dist/knowledge-governance.test.js +150 -0
  122. package/dist/knowledge-graph.js +181 -0
  123. package/dist/knowledge-guided-synth.js +246 -0
  124. package/dist/knowledge-loop.test.js +77 -0
  125. package/dist/knowledge-object.js +316 -0
  126. package/dist/knowledge-package.js +98 -0
  127. package/dist/kpi-dashboard.js +561 -0
  128. package/dist/l3-cross-function.js +280 -0
  129. package/dist/learning-ranker.js +148 -0
  130. package/dist/learning-ranker.test.js +291 -0
  131. package/dist/ledger/accountability.js +322 -0
  132. package/dist/ledger/chain-builder.js +185 -0
  133. package/dist/ledger/cli.js +222 -0
  134. package/dist/ledger/index.js +13 -0
  135. package/dist/ledger/signatures.js +193 -0
  136. package/dist/ledger/types.js +9 -0
  137. package/dist/llm.js +74 -3
  138. package/dist/load-benchmarks.js +8 -3
  139. package/dist/logger.js +66 -0
  140. package/dist/logger.test.js +37 -0
  141. package/dist/logistic-reward.js +339 -0
  142. package/dist/logistic-reward.test.js +180 -0
  143. package/dist/macro-graph.js +193 -0
  144. package/dist/macro-repair.js +183 -0
  145. package/dist/mcp-server.mjs +1202 -483
  146. package/dist/memory-layer.js +42 -5
  147. package/dist/multi-repo-precision.js +422 -0
  148. package/dist/name-free-protocol.js +425 -0
  149. package/dist/name-free-protocol.test.js +170 -0
  150. package/dist/name-scrambling.js +138 -0
  151. package/dist/name-scrambling.test.js +16 -0
  152. package/dist/p3-observability.test.js +281 -0
  153. package/dist/p5-orchestrator.test.js +225 -0
  154. package/dist/pairwise-preference.js +294 -0
  155. package/dist/pairwise-preference.test.js +140 -0
  156. package/dist/planner-constraints.js +104 -0
  157. package/dist/planner-prompts.js +155 -0
  158. package/dist/planner-telemetry.js +415 -0
  159. package/dist/planner-trace.js +214 -0
  160. package/dist/planner.js +162 -167
  161. package/dist/plsb/artifact.js +116 -0
  162. package/dist/plsb/cli.js +71 -0
  163. package/dist/plsb/index.js +19 -0
  164. package/dist/plsb/leaderboard.js +249 -0
  165. package/dist/plsb/report-md.js +156 -0
  166. package/dist/plsb/schema.js +179 -0
  167. package/dist/plsb-benchmark.js +284 -0
  168. package/dist/plsb-benchmark.test.js +119 -0
  169. package/dist/policy/cli.js +134 -0
  170. package/dist/policy/engine.js +333 -0
  171. package/dist/policy/index.js +12 -0
  172. package/dist/policy/types.js +59 -0
  173. package/dist/policy-miner.js +505 -0
  174. package/dist/precision-analyze.js +229 -0
  175. package/dist/precision-benchmark.js +147 -0
  176. package/dist/precision-label-c.js +134 -0
  177. package/dist/precision-label.js +193 -0
  178. package/dist/precision-report-c.js +149 -0
  179. package/dist/precision-report.js +246 -0
  180. package/dist/progmune-status.js +108 -0
  181. package/dist/proof-engine.js +479 -0
  182. package/dist/proof-provenance.js +315 -0
  183. package/dist/protocol-coverage.js +294 -0
  184. package/dist/protocol-detector.js +1189 -0
  185. package/dist/protocol-embedding-expanded.js +297 -0
  186. package/dist/protocol-embedding-expanded.test.js +97 -0
  187. package/dist/protocol-embedding.js +195 -0
  188. package/dist/protocol-embedding.test.js +82 -0
  189. package/dist/protocol-extractor-v2.js +354 -0
  190. package/dist/protocol-extractor-v2.test.js +140 -0
  191. package/dist/protocol-extractor.js +310 -0
  192. package/dist/protocol-extractor.test.js +113 -0
  193. package/dist/protocol-foundation.js +322 -0
  194. package/dist/protocol-foundation.test.js +163 -0
  195. package/dist/protocol-frontier.js +243 -0
  196. package/dist/protocol-frontier.test.js +92 -0
  197. package/dist/protocol-gap-analyzer.js +228 -0
  198. package/dist/protocol-gap-analyzer.test.js +49 -0
  199. package/dist/protocol-invariants.js +276 -0
  200. package/dist/protocol-invariants.test.js +111 -0
  201. package/dist/protocol-knowledge.js +464 -0
  202. package/dist/protocol-miner.js +343 -0
  203. package/dist/protocol-mining.js +207 -0
  204. package/dist/protocol-mining.test.js +37 -0
  205. package/dist/protocol-registry.js +1 -1
  206. package/dist/protocol-security-benchmark.js +222 -0
  207. package/dist/protocol-vulnerability.js +257 -0
  208. package/dist/protocol-vulnerability.test.js +60 -0
  209. package/dist/python-benchmark.js +120 -0
  210. package/dist/python-emitter.js +163 -45
  211. package/dist/python-protocol-extractor.js +187 -0
  212. package/dist/python-protocol-extractor.test.js +116 -0
  213. package/dist/realworld-benchmark.js +646 -0
  214. package/dist/realworld-benchmark.test.js +36 -0
  215. package/dist/repair-arch.test.js +411 -0
  216. package/dist/repair-evolution.test.js +454 -0
  217. package/dist/repair-executor.js +719 -0
  218. package/dist/repair-proposal.js +4 -4
  219. package/dist/repair-ranker.js +141 -0
  220. package/dist/repair-strategies.js +419 -0
  221. package/dist/repair-taxonomy.js +234 -0
  222. package/dist/repair-types.js +12 -0
  223. package/dist/repo-evaluator.js +250 -0
  224. package/dist/repo-evaluator.test.js +128 -0
  225. package/dist/resource-abstraction.js +242 -0
  226. package/dist/resource-detector.js +211 -0
  227. package/dist/result.test.js +43 -0
  228. package/dist/reward-system.js +411 -0
  229. package/dist/reward-system.test.js +175 -0
  230. package/dist/risk-model.js +215 -0
  231. package/dist/rule-miner.js +234 -7
  232. package/dist/rule-specificity.js +254 -0
  233. package/dist/runtime-types.js +27 -0
  234. package/dist/scaffold.js +208 -0
  235. package/dist/scale-collector.test.js +101 -0
  236. package/dist/scale-trajectory-collector.js +128 -0
  237. package/dist/sdk.js +250 -0
  238. package/dist/search-planner.js +4 -41
  239. package/dist/semantic-snapshot.js +1 -1
  240. package/dist/semantic-topology.js +121 -0
  241. package/dist/semantic-trace.js +310 -317
  242. package/dist/sequence-extractor.js +343 -0
  243. package/dist/skill-library.js +245 -0
  244. package/dist/skill-planner.test.js +189 -0
  245. package/dist/software-physics.js +291 -0
  246. package/dist/software-physics.test.js +81 -0
  247. package/dist/ssg-precision.js +478 -0
  248. package/dist/ssg-validator.js +71 -21
  249. package/dist/state-inference-doubleblind.test.js +160 -0
  250. package/dist/state-inference.js +516 -0
  251. package/dist/state-inference.test.js +115 -0
  252. package/dist/state-machine-fingerprint.js +345 -0
  253. package/dist/state-machine-fingerprint.test.js +120 -0
  254. package/dist/state-miner.js +386 -0
  255. package/dist/state-name-inference.js +213 -0
  256. package/dist/state-name-inference.test.js +69 -0
  257. package/dist/strategy-planner.js +262 -96
  258. package/dist/strategy-planner.test.js +135 -0
  259. package/dist/telemetry-analytics.test.js +402 -0
  260. package/dist/terminal-format.js +68 -0
  261. package/dist/terminal-format.test.js +83 -0
  262. package/dist/topology-factory.js +196 -0
  263. package/dist/topology-representation.js +242 -0
  264. package/dist/topology-representation.test.js +27 -0
  265. package/dist/trajectory-augmentation.js +254 -0
  266. package/dist/trajectory-augmentation.test.js +63 -0
  267. package/dist/trajectory-corpus.js +440 -0
  268. package/dist/trajectory-corpus.test.js +32 -0
  269. package/dist/trajectory-feedback.test.js +116 -0
  270. package/dist/transition-synthesizer.js +286 -0
  271. package/dist/transition-synthesizer.test.js +123 -0
  272. package/dist/trust/api-semantic-mapper.js +809 -0
  273. package/dist/trust/call-graph-propagator.js +225 -0
  274. package/dist/trust/cli.js +122 -0
  275. package/dist/trust/compliance-scorer.js +283 -0
  276. package/dist/trust/confidence-calculator.js +261 -0
  277. package/dist/trust/engine.js +1145 -0
  278. package/dist/trust/explainability.js +85 -0
  279. package/dist/trust/formatters/ci.js +42 -0
  280. package/dist/trust/formatters/json.js +11 -0
  281. package/dist/trust/formatters/terminal.js +152 -0
  282. package/dist/trust/index.js +39 -0
  283. package/dist/trust/phase1-verify.js +171 -0
  284. package/dist/trust/protocol-domain-validator.js +697 -0
  285. package/dist/trust/score-calculator.js +282 -0
  286. package/dist/trust/ssg-bridge.js +641 -0
  287. package/dist/trust/ssg-bridge.test.js +269 -0
  288. package/dist/trust/types.js +67 -0
  289. package/dist/trust/violation-trace.js +335 -0
  290. package/dist/trust-api.js +179 -0
  291. package/dist/trust-calibration.js +279 -0
  292. package/dist/unknown-protocol-discovery.js +339 -0
  293. package/dist/unknown-protocol-discovery.test.js +102 -0
  294. package/dist/unsupervised-physics.js +230 -0
  295. package/dist/unsupervised-physics.test.js +95 -0
  296. package/dist/utils.test.js +37 -0
  297. package/dist/validator.js +187 -10
  298. package/dist/verification-intelligence.js +475 -0
  299. package/dist/verify-api.js +432 -0
  300. package/dist/vi-impact-report.js +293 -0
  301. package/dist/wl-fingerprint.js +162 -0
  302. package/dist/wl-fingerprint.test.js +130 -0
  303. package/dist/zeroshot-strategy.js +139 -0
  304. package/docs/Progmune_/346/212/225/350/265/204/344/272/272/347/231/275/347/232/256/344/271/246_v2.0.html +576 -0
  305. package/docs/Progmune_/351/241/271/347/233/256/345/205/250/350/247/243.html +710 -0
  306. package/package.json +74 -7
  307. package/protocols.json +1956 -50
  308. package/.dockerignore +0 -14
  309. package/.mcp.json +0 -11
  310. package/.progmune_allowlist +0 -50
  311. package/.test_report/test_report.md +0 -87
  312. package/Dockerfile +0 -9
  313. package/FAQ.md +0 -167
  314. package/WHITEPAPER.md +0 -540
  315. package/demo-project/auth.ts +0 -55
  316. package/demo-project/tsconfig.json +0 -8
  317. package/dist/acl-breakdown.js +0 -13
  318. package/dist/all-sessions.js +0 -11
  319. package/dist/antibody-stats.js +0 -11
  320. package/dist/branch-tree-count.js +0 -14
  321. package/dist/common-fixpath.js +0 -12
  322. package/dist/constraint-types.js +0 -12
  323. package/dist/exec-metrics.js +0 -11
  324. package/dist/failure-report.js +0 -11
  325. package/dist/fast-path-hits.js +0 -13
  326. package/dist/fingerprint-list.js +0 -15
  327. package/dist/gen-history-log.js +0 -13
  328. package/dist/heatmap-data.js +0 -11
  329. package/dist/recent-session.js +0 -12
  330. package/dist/svl-distribution.js +0 -11
  331. package/dist/terminal-status.js +0 -11
  332. package/dist/token-savings.js +0 -11
  333. package/dist/total-repairs.js +0 -12
  334. package/dist/unresolved-count.js +0 -12
  335. package/dist/valid-fingerprints.js +0 -13
  336. package/dist/verify-ledgers.js +0 -11
  337. package/docs/whitepaper-style.css +0 -77
  338. package/docs/whitepaper-v2.1.md +0 -609
  339. package/docs/whitepaper-v2.2.md +0 -1064
  340. package/docs/whitepaper-v2.2.pdf +0 -0
  341. package/fly.toml +0 -31
  342. package/public/dashboard.html +0 -119
  343. package/server/hub.js +0 -116
  344. package/test/replay-golden/sess_1780063202050_mgeld.json +0 -9
  345. package/test/replay-golden/sess_1780064032560_gocld.json +0 -354
  346. package/test/replay-golden/sess_1780064413331_s2709.json +0 -606
  347. package/test/replay-golden/sess_1780064792710_y3avo.json +0 -614
  348. package/test/replay-golden.ts +0 -84
  349. package/test_benchmark.js +0 -165
  350. package/test_comprehensive.mjs +0 -638
  351. package/test_concurrency.js +0 -129
  352. package/test_ir_robustness.js +0 -85
  353. package/test_semantic_contracts.js +0 -269
  354. package/test_ssg_stress.js +0 -156
  355. package/test_svl3.js +0 -58
  356. package/tsconfig.json +0 -17
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ /**
3
+ * Phase 9: Terminal Formatter
4
+ *
5
+ * Human-readable governance report with ANSI color coding.
6
+ * Pattern follows semantic-trace.ts rendering conventions.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.formatAsTerminal = formatAsTerminal;
10
+ // ANSI color helpers (same palette as semantic-trace.ts)
11
+ const C = {
12
+ reset: "\x1b[0m",
13
+ bold: "\x1b[1m",
14
+ dim: "\x1b[2m",
15
+ green: "\x1b[32m",
16
+ yellow: "\x1b[33m",
17
+ red: "\x1b[31m",
18
+ cyan: "\x1b[36m",
19
+ blue: "\x1b[34m",
20
+ magenta: "\x1b[35m",
21
+ white: "\x1b[37m",
22
+ };
23
+ const BOX_H = "─";
24
+ const BOX_V = "│";
25
+ const BOX_TL = "┌";
26
+ const BOX_TR = "┐";
27
+ const BOX_BL = "└";
28
+ const BOX_BR = "┘";
29
+ function bar(value, max, width = 20) {
30
+ const filled = Math.round((value / (max || 1)) * width);
31
+ return "█".repeat(filled) + "░".repeat(width - filled);
32
+ }
33
+ function percent(v) {
34
+ return `${(v * 100).toFixed(0)}%`;
35
+ }
36
+ function verdictColor(v) {
37
+ if (v === "PASS")
38
+ return C.green;
39
+ if (v === "WARN")
40
+ return C.yellow;
41
+ return C.red;
42
+ }
43
+ function severityColor(s) {
44
+ if (s === "critical")
45
+ return C.red;
46
+ if (s === "high")
47
+ return C.yellow;
48
+ if (s === "medium")
49
+ return C.cyan;
50
+ return C.dim;
51
+ }
52
+ function formatAsTerminal(report) {
53
+ const { metadata, sessions, ssv, plsb, provenance, antibodies, verdict, recommendations, business } = report;
54
+ const lines = [];
55
+ // ── Header ──
56
+ lines.push("");
57
+ lines.push(`${C.bold}${C.cyan}${BOX_TL}${BOX_H.repeat(58)}${BOX_TR}${C.reset}`);
58
+ lines.push(`${C.bold}${C.cyan}${BOX_V}${C.reset} ${C.bold}AI Code Governance Report${C.reset}${" ".repeat(30)}${C.bold}${C.cyan}${BOX_V}${C.reset}`);
59
+ lines.push(`${C.bold}${C.cyan}${BOX_V}${C.reset} ${C.dim}Progmune Runtime — AI Generated Software Governance${C.reset}${" ".repeat(3)}${C.bold}${C.cyan}${BOX_V}${C.reset}`);
60
+ lines.push(`${C.bold}${C.cyan}${BOX_BL}${BOX_H.repeat(58)}${BOX_BR}${C.reset}`);
61
+ lines.push("");
62
+ // ── Metadata ──
63
+ lines.push(`${C.bold}Metadata${C.reset}`);
64
+ lines.push(` Generator: ${metadata.generator} v${metadata.version}`);
65
+ lines.push(` Timestamp: ${metadata.timestamp}`);
66
+ lines.push(` Project: ${metadata.projectId}`);
67
+ lines.push("");
68
+ // ── Verdict ──
69
+ const vc = verdictColor(verdict);
70
+ lines.push(`${C.bold}Verdict:${C.reset} ${vc}${C.bold}${verdict}${C.reset}`);
71
+ lines.push("");
72
+ // ── Sessions ──
73
+ lines.push(`${C.bold}Sessions${C.reset}`);
74
+ lines.push(` Total: ${sessions.total}`);
75
+ lines.push(` Verified: ${C.green}${sessions.verified}${C.reset}`);
76
+ lines.push(` Compromised: ${sessions.compromised > 0 ? C.red : C.green}${sessions.compromised}${C.reset}`);
77
+ if (sessions.total > 0) {
78
+ lines.push(` Coverage: ${bar(sessions.verified, sessions.total)} ${percent(sessions.verified / sessions.total)}`);
79
+ }
80
+ lines.push("");
81
+ // ── SSV ──
82
+ lines.push(`${C.bold}Semantic State Verification (SSV)${C.reset}`);
83
+ lines.push(` Checks: ${ssv.totalChecks} total, ${C.green}${ssv.passed} passed${C.reset}, ${ssv.failed > 0 ? C.red : ""}${ssv.failed} failed${C.reset}`);
84
+ if (ssv.failed > 0) {
85
+ lines.push(` ${C.red}⚠ Ledger inconsistencies detected${C.reset}`);
86
+ }
87
+ lines.push("");
88
+ // ── PLSB ──
89
+ lines.push(`${C.bold}Protocol Lifecycle Security Benchmark (PLSB)${C.reset}`);
90
+ lines.push(` Version: v${plsb.version}`);
91
+ lines.push(` Entries: ${plsb.totalEntries} (${plsb.verifiedEntries} verified)`);
92
+ lines.push(` Coverage: ${bar(plsb.coverage, 1)} ${percent(plsb.coverage)}`);
93
+ lines.push(` Recall: ${percent(plsb.recall)}`);
94
+ lines.push(` Precision: ${percent(plsb.precision)}`);
95
+ lines.push(` Covered: ${plsb.matchedCategories.length > 0 ? plsb.matchedCategories.join(", ") : C.dim + "(none)" + C.reset}`);
96
+ if (plsb.unmatchedCategories.length > 0) {
97
+ lines.push(` Uncovered: ${C.yellow}${plsb.unmatchedCategories.join(", ")}${C.reset}`);
98
+ }
99
+ lines.push("");
100
+ // ── Provenance ──
101
+ lines.push(`${C.bold}Provenance (Ledger Integrity)${C.reset}`);
102
+ lines.push(` Fingerprints: ${provenance.totalFingerprints} total`);
103
+ lines.push(` Verified: ${C.green}${provenance.verified}${C.reset}`);
104
+ lines.push(` Tampered: ${provenance.tampered > 0 ? C.red : C.green}${provenance.tampered}${C.reset}`);
105
+ lines.push(` Not Found: ${provenance.notFound}`);
106
+ lines.push("");
107
+ // ── Antibodies ──
108
+ lines.push(`${C.bold}Immune System (Antibodies)${C.reset}`);
109
+ lines.push(` Hits: ${antibodies.totalHits}`);
110
+ lines.push(` Fast-path: ${antibodies.fastPathHits}`);
111
+ lines.push(` LLM saved: ${antibodies.llmCallsSaved} calls, ${antibodies.tokensSaved.toLocaleString()} tokens`);
112
+ if (antibodies.topSignatures.length > 0) {
113
+ lines.push(` Top patterns: ${antibodies.topSignatures.slice(0, 3).join(", ")}`);
114
+ }
115
+ lines.push("");
116
+ // ── Session Details ──
117
+ if (sessions.details.length > 0 && sessions.details.length <= 10) {
118
+ lines.push(`${C.bold}Session Details${C.reset}`);
119
+ for (const d of sessions.details) {
120
+ const status = d.fingerprintTampered ? `${C.red}⚠ TAMPERED${C.reset}`
121
+ : d.consistencyPassed ? `${C.green}✓${C.reset}`
122
+ : `${C.yellow}⚠ VIOLATIONS${C.reset}`;
123
+ lines.push(` ${status} ${d.sessionId.slice(0, 24)}... | ${d.validTransitions}/${d.transitionCount} valid | intent: ${d.intent.slice(0, 40)}`);
124
+ }
125
+ lines.push("");
126
+ }
127
+ // ── Business Translation (Trust Report) ──
128
+ if (business) {
129
+ const biz = business;
130
+ lines.push(`${C.bold}╔══════════════════════════════════════════════════════╗${C.reset}`);
131
+ lines.push(`${C.bold}║${C.reset} ${C.bold}Governance Summary — 治理摘要${C.reset} ${C.bold}║${C.reset}`);
132
+ lines.push(`${C.bold}╚══════════════════════════════════════════════════════╝${C.reset}`);
133
+ lines.push("");
134
+ // Summary stats
135
+ lines.push(`${C.bold}Protected Against${C.reset}`);
136
+ lines.push(` ${C.green}✓${C.reset} ${biz.summary.totalRisksMitigated} risk categories mitigated`);
137
+ lines.push(` ${C.green}✓${C.reset} ${biz.summary.knowledgeDomainsCovered} business knowledge domains covered`);
138
+ lines.push(` ${C.green}✓${C.reset} ${biz.summary.businessProtocolsIntact} business protocol edges verified`);
139
+ lines.push("");
140
+ // Risk table
141
+ lines.push(`${C.bold}Risk Protection${C.reset}`);
142
+ for (const r of biz.risks) {
143
+ const icon = r.status === "protected" ? `${C.green}🛡` : r.status === "partial" ? `${C.yellow}⚠` : `${C.red}✗`;
144
+ const statusLabel = r.status === "protected" ? "已防护" : r.status === "partial" ? "部分防护" : "未防护";
145
+ lines.push(` ${icon} ${r.category} [${statusLabel}]`);
146
+ lines.push(` ${C.dim}${r.description}${C.reset}`);
147
+ lines.push(` ${C.dim}协议覆盖: ${r.protocolsCovered} | 违规预防: ${r.violationsPrevented}${C.reset}`);
148
+ }
149
+ lines.push("");
150
+ // Knowledge Coverage
151
+ lines.push(`${C.bold}Knowledge Coverage — 业务知识覆盖${C.reset}`);
152
+ for (const k of biz.knowledgeCoverage) {
153
+ const icon = k.coverage === "full" ? C.green + "✓" : k.coverage === "partial" ? C.yellow + "◐" : C.red + "✗";
154
+ const label = k.coverage === "full" ? "完整覆盖" : k.coverage === "partial" ? "部分覆盖" : "未覆盖";
155
+ const entities = k.entities?.length ? ` (${k.entities.join(", ")})` : "";
156
+ lines.push(` ${icon} ${k.domain}${entities} ${C.dim}[${label}]${C.reset}`);
157
+ }
158
+ lines.push("");
159
+ // Protocol Graph
160
+ if (biz.protocolGraph.length > 0) {
161
+ lines.push(`${C.bold}Business Protocol Graph — 业务协议图${C.reset}`);
162
+ lines.push(` ${C.dim}All ${biz.summary.businessProtocolsIntact} edges verified — no AI-generated code violated business protocol edges${C.reset}`);
163
+ // Show first 8 edges in compact form
164
+ for (const e of biz.protocolGraph.slice(0, 8)) {
165
+ const check = e.verified ? C.green + "✓" : C.red + "✗";
166
+ lines.push(` ${check} ${e.from} ${C.dim}→${C.reset} ${e.to} ${C.dim}${e.label}${C.reset}`);
167
+ }
168
+ if (biz.protocolGraph.length > 8) {
169
+ lines.push(` ${C.dim}... and ${biz.protocolGraph.length - 8} more protocol edges${C.reset}`);
170
+ }
171
+ lines.push("");
172
+ }
173
+ }
174
+ // ── Recommendations ──
175
+ if (recommendations.length > 0) {
176
+ lines.push(`${C.bold}Recommendations${C.reset}`);
177
+ for (const r of recommendations) {
178
+ const sc = severityColor(r.severity);
179
+ lines.push(` ${sc}[${r.severity.toUpperCase()}]${C.reset} ${r.message}`);
180
+ lines.push(` ${C.dim}→ ${r.action}${C.reset}`);
181
+ }
182
+ lines.push("");
183
+ }
184
+ // ── Footer ──
185
+ lines.push(`${C.dim}${BOX_H.repeat(60)}${C.reset}`);
186
+ lines.push(`${C.dim}Report generated by Progmune Runtime — AI Generated Software Governance${C.reset}`);
187
+ lines.push("");
188
+ return lines.join("\n");
189
+ }
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ /**
3
+ * Phase 9: Governance Audit Module
4
+ *
5
+ * Public API for AI Code Governance Reports.
6
+ * Re-exports the builder, types, and formatters.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.detectProjectType = exports.buildBusinessSummary = exports.getProtocolGraph = exports.getKnowledgeCoverage = exports.translateToBusinessRisks = exports.formatAsHTML = exports.formatAsMarkdown = exports.formatAsTerminal = exports.formatAsJSON = exports.buildGovernanceReport = void 0;
10
+ var report_builder_1 = require("./report-builder");
11
+ Object.defineProperty(exports, "buildGovernanceReport", { enumerable: true, get: function () { return report_builder_1.buildGovernanceReport; } });
12
+ var json_1 = require("./formatters/json");
13
+ Object.defineProperty(exports, "formatAsJSON", { enumerable: true, get: function () { return json_1.formatAsJSON; } });
14
+ var terminal_1 = require("./formatters/terminal");
15
+ Object.defineProperty(exports, "formatAsTerminal", { enumerable: true, get: function () { return terminal_1.formatAsTerminal; } });
16
+ var markdown_1 = require("./formatters/markdown");
17
+ Object.defineProperty(exports, "formatAsMarkdown", { enumerable: true, get: function () { return markdown_1.formatAsMarkdown; } });
18
+ var html_1 = require("./formatters/html");
19
+ Object.defineProperty(exports, "formatAsHTML", { enumerable: true, get: function () { return html_1.formatAsHTML; } });
20
+ var business_translator_1 = require("./business-translator");
21
+ Object.defineProperty(exports, "translateToBusinessRisks", { enumerable: true, get: function () { return business_translator_1.translateToBusinessRisks; } });
22
+ Object.defineProperty(exports, "getKnowledgeCoverage", { enumerable: true, get: function () { return business_translator_1.getKnowledgeCoverage; } });
23
+ Object.defineProperty(exports, "getProtocolGraph", { enumerable: true, get: function () { return business_translator_1.getProtocolGraph; } });
24
+ Object.defineProperty(exports, "buildBusinessSummary", { enumerable: true, get: function () { return business_translator_1.buildBusinessSummary; } });
25
+ Object.defineProperty(exports, "detectProjectType", { enumerable: true, get: function () { return business_translator_1.detectProjectType; } });
@@ -0,0 +1,318 @@
1
+ "use strict";
2
+ /**
3
+ * Phase 9: Governance Report Builder
4
+ *
5
+ * Aggregates data from the corpus, fingerprints, PLSB benchmark,
6
+ * and immune system into a single GovernanceReport.
7
+ *
8
+ * Pure aggregation — no new verification logic.
9
+ * All data already exists in .progmune_corpus/ and benchmarks/.
10
+ */
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || (function () {
28
+ var ownKeys = function(o) {
29
+ ownKeys = Object.getOwnPropertyNames || function (o) {
30
+ var ar = [];
31
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
+ return ar;
33
+ };
34
+ return ownKeys(o);
35
+ };
36
+ return function (mod) {
37
+ if (mod && mod.__esModule) return mod;
38
+ var result = {};
39
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
+ __setModuleDefault(result, mod);
41
+ return result;
42
+ };
43
+ })();
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.buildGovernanceReport = buildGovernanceReport;
46
+ const crypto = __importStar(require("crypto"));
47
+ // ── Main Entry Point ──
48
+ function buildGovernanceReport(projectPath, options = {}) {
49
+ const metadata = buildMetadata(projectPath);
50
+ const sessions = buildSessions(options.sessionId);
51
+ const ssv = buildSSV(sessions, options);
52
+ const plsb = options.fast ? emptyPLSB() : buildPLSBFromBenchmark();
53
+ const provenance = buildProvenance();
54
+ const antibodies = buildAntibodies();
55
+ const business = buildBusinessSection(plsb, sessions, projectPath, options);
56
+ const recommendations = generateRecommendations(sessions, ssv, provenance, plsb);
57
+ const verdict = computeVerdict(sessions, ssv, provenance, recommendations);
58
+ return {
59
+ metadata,
60
+ sessions,
61
+ ssv,
62
+ plsb,
63
+ provenance,
64
+ antibodies,
65
+ verdict,
66
+ recommendations,
67
+ business, // Phase 10: Business Translation
68
+ };
69
+ }
70
+ // ── Metadata ──
71
+ function buildMetadata(projectPath) {
72
+ return {
73
+ generator: "progmune-runtime",
74
+ version: "3.2.0",
75
+ timestamp: new Date().toISOString(),
76
+ projectId: crypto.createHash("sha256").update(projectPath).digest("hex").slice(0, 16),
77
+ validator: "SSG + Ledger + PLSB",
78
+ };
79
+ }
80
+ // ── Sessions ──
81
+ function buildSessions(sessionId) {
82
+ const { getAllSessions } = require("../failure-corpus");
83
+ const sessions = getAllSessions();
84
+ let filtered = sessions;
85
+ if (sessionId) {
86
+ filtered = sessions.filter((s) => s.sessionId === sessionId || s.sessionId?.startsWith(sessionId));
87
+ }
88
+ const details = [];
89
+ for (const s of filtered) {
90
+ let trans = [];
91
+ // Collect transitions from attempts
92
+ for (const a of s.attempts || []) {
93
+ trans = trans.concat(a.transitions || []);
94
+ }
95
+ // Check ledger consistency
96
+ let consistencyPassed = false;
97
+ let violations = 0;
98
+ try {
99
+ const { checkLedgerConsistency } = require("../ssg-validator");
100
+ const { getNsInit } = require("../protocol-registry");
101
+ const result = checkLedgerConsistency(trans, getNsInit());
102
+ consistencyPassed = result.consistent;
103
+ violations = (result.violations || []).length;
104
+ }
105
+ catch { /* best-effort */ }
106
+ // Verify fingerprint
107
+ let fingerprintVerified = false;
108
+ let fingerprintTampered = false;
109
+ try {
110
+ const { verifyFingerprint } = require("../ledger-registry");
111
+ const fp = verifyFingerprint(s.sessionId, trans);
112
+ fingerprintVerified = fp.valid;
113
+ fingerprintTampered = fp.tampered;
114
+ }
115
+ catch { /* best-effort */ }
116
+ const validTrans = trans.filter((t) => t.valid !== false).length;
117
+ details.push({
118
+ sessionId: s.sessionId || "unknown",
119
+ intent: s.intent || "",
120
+ transitionCount: trans.length,
121
+ validTransitions: validTrans,
122
+ invalidTransitions: trans.length - validTrans,
123
+ fingerprintVerified,
124
+ fingerprintTampered,
125
+ consistencyPassed,
126
+ violations,
127
+ });
128
+ }
129
+ const verified = details.filter((d) => d.fingerprintVerified).length;
130
+ const compromised = details.filter((d) => d.fingerprintTampered).length;
131
+ return { total: filtered.length, verified, compromised, details };
132
+ }
133
+ // ── SSV (Semantic State Verification) ──
134
+ function buildSSV(sessions, _options) {
135
+ let totalChecks = 0;
136
+ let passed = 0;
137
+ let failed = 0;
138
+ const byCategory = {};
139
+ const allViolations = [];
140
+ for (const d of sessions.details) {
141
+ totalChecks++;
142
+ if (d.consistencyPassed) {
143
+ passed++;
144
+ }
145
+ else {
146
+ failed++;
147
+ }
148
+ // Category is "ledger" — all violations are ledger consistency
149
+ const cat = "ledger";
150
+ if (!byCategory[cat])
151
+ byCategory[cat] = { total: 0, passed: 0, failed: 0 };
152
+ byCategory[cat].total++;
153
+ if (d.consistencyPassed) {
154
+ byCategory[cat].passed++;
155
+ }
156
+ else {
157
+ byCategory[cat].failed++;
158
+ }
159
+ }
160
+ return { totalChecks, passed, failed, byCategory, violations: allViolations };
161
+ }
162
+ // ── PLSB ──
163
+ function buildPLSBFromBenchmark() {
164
+ try {
165
+ const { buildPLSB, PROTOCOL_WEAKNESS_TAXONOMY } = require("../plsb-benchmark");
166
+ const benchmark = buildPLSB();
167
+ const taxonomyIds = PROTOCOL_WEAKNESS_TAXONOMY.map((t) => t.id);
168
+ const matched = new Set(Object.keys(benchmark.metadata?.byPLS || {}));
169
+ const matchedCategories = taxonomyIds.filter((id) => matched.has(id));
170
+ const unmatchedCategories = taxonomyIds.filter((id) => !matched.has(id));
171
+ return {
172
+ version: benchmark.version || "1.0",
173
+ totalEntries: benchmark.metadata?.total || 0,
174
+ verifiedEntries: benchmark.metadata?.verified || 0,
175
+ coverage: matchedCategories.length / (taxonomyIds.length || 1),
176
+ recall: benchmark.metadata?.recall || 0,
177
+ precision: benchmark.metadata?.precision || 0,
178
+ matchedCategories,
179
+ unmatchedCategories,
180
+ };
181
+ }
182
+ catch {
183
+ return emptyPLSB();
184
+ }
185
+ }
186
+ function emptyPLSB() {
187
+ return {
188
+ version: "1.0",
189
+ totalEntries: 0,
190
+ verifiedEntries: 0,
191
+ coverage: 0,
192
+ recall: 0,
193
+ precision: 0,
194
+ matchedCategories: [],
195
+ unmatchedCategories: [],
196
+ };
197
+ }
198
+ // ── Provenance ──
199
+ function buildProvenance() {
200
+ try {
201
+ const { verifyAllFingerprints } = require("../ledger-registry");
202
+ const summary = verifyAllFingerprints();
203
+ return {
204
+ totalFingerprints: summary.total || 0,
205
+ verified: summary.valid || 0,
206
+ tampered: summary.tampered || 0,
207
+ notFound: summary.notFound || 0,
208
+ };
209
+ }
210
+ catch {
211
+ return { totalFingerprints: 0, verified: 0, tampered: 0, notFound: 0 };
212
+ }
213
+ }
214
+ // ── Antibodies ──
215
+ function buildAntibodies() {
216
+ try {
217
+ const { getAntibodyStats } = require("../failure-corpus");
218
+ const stats = getAntibodyStats();
219
+ return {
220
+ totalHits: stats.totalHits || 0,
221
+ fastPathHits: stats.fastPathHits || 0,
222
+ llmCallsSaved: stats.totalLLMCallsSaved || 0,
223
+ tokensSaved: stats.totalTokensSaved || 0,
224
+ topSignatures: (stats.topSignatures || []).slice(0, 5).map((s) => typeof s === "string" ? s : s.signature || "").filter(Boolean),
225
+ byLevel: stats.byLevel || {},
226
+ };
227
+ }
228
+ catch {
229
+ return {
230
+ totalHits: 0, fastPathHits: 0, llmCallsSaved: 0, tokensSaved: 0,
231
+ topSignatures: [], byLevel: {},
232
+ };
233
+ }
234
+ }
235
+ // ── Business Translation (Phase 10) ──
236
+ function buildBusinessSection(plsb, sessions, _projectPath, options) {
237
+ if (options.business === false)
238
+ return undefined;
239
+ try {
240
+ const { translateToBusinessRisks, getKnowledgeCoverage, getProtocolGraph, buildBusinessSummary, detectProjectType } = require("./business-translator");
241
+ // Auto-detect project type for domain-specific knowledge
242
+ const detectedType = detectProjectType(_projectPath);
243
+ const violationsByCategory = {};
244
+ for (const d of sessions.details) {
245
+ if (d.violations > 0) {
246
+ // Categorize violations (simplified)
247
+ violationsByCategory["认证与授权"] = (violationsByCategory["认证与授权"] || 0) + d.violations;
248
+ }
249
+ }
250
+ const risks = translateToBusinessRisks(plsb.matchedCategories, plsb.unmatchedCategories, violationsByCategory);
251
+ const knowledge = getKnowledgeCoverage(detectedType);
252
+ const protocolGraph = getProtocolGraph(detectedType);
253
+ const summary = buildBusinessSummary(risks, knowledge, violationsByCategory, detectedType);
254
+ return { risks, knowledgeCoverage: knowledge, protocolGraph, summary };
255
+ }
256
+ catch (e) {
257
+ // Best-effort: if business translator fails, skip the section
258
+ return undefined;
259
+ }
260
+ }
261
+ // ── Recommendations ──
262
+ function generateRecommendations(sessions, ssv, provenance, plsb) {
263
+ const recs = [];
264
+ if (provenance.tampered > 0) {
265
+ recs.push({
266
+ severity: "critical",
267
+ category: "provenance",
268
+ message: `${provenance.tampered} fingerprint(s) show tampered hashes.`,
269
+ action: `Run 'progmune_check' and review .progmune_corpus/fingerprints/ for tampered sessions.`,
270
+ });
271
+ }
272
+ if (ssv.failed > 0) {
273
+ recs.push({
274
+ severity: "critical",
275
+ category: "ssv",
276
+ message: `${ssv.failed} session(s) have ledger consistency violations.`,
277
+ action: `Run 'progmune_repair(sessionId="...")' to generate fix proposals for each failed session.`,
278
+ });
279
+ }
280
+ if (sessions.total > 0 && sessions.verified / sessions.total < 0.8) {
281
+ recs.push({
282
+ severity: "high",
283
+ category: "coverage",
284
+ message: `Only ${sessions.verified}/${sessions.total} sessions verified.`,
285
+ action: `Run 'npm run check' to register missing fingerprints.`,
286
+ });
287
+ }
288
+ if (provenance.notFound > 0) {
289
+ recs.push({
290
+ severity: "medium",
291
+ category: "provenance",
292
+ message: `${provenance.notFound} fingerprint(s) reference missing session files.`,
293
+ action: `Fingerprints without sessions can be manually reviewed or removed from .progmune_corpus/fingerprints/.`,
294
+ });
295
+ }
296
+ if (plsb.coverage < 0.7) {
297
+ recs.push({
298
+ severity: "medium",
299
+ category: "plsb",
300
+ message: `PLSB coverage is ${(plsb.coverage * 100).toFixed(0)}% — ${plsb.unmatchedCategories.length} categories uncovered: ${plsb.unmatchedCategories.join(", ")}.`,
301
+ action: `Add protocol rules for uncovered PLS categories to improve detection coverage.`,
302
+ });
303
+ }
304
+ return recs;
305
+ }
306
+ // ── Verdict ──
307
+ function computeVerdict(sessions, ssv, provenance, recommendations) {
308
+ const hasCritical = recommendations.some((r) => r.severity === "critical");
309
+ if (provenance.tampered > 0)
310
+ return "FAIL";
311
+ if (ssv.failed > 0)
312
+ return "FAIL";
313
+ if (hasCritical)
314
+ return "FAIL";
315
+ if (sessions.total > 0 && sessions.verified / sessions.total < 0.8)
316
+ return "WARN";
317
+ return "PASS";
318
+ }
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ /**
3
+ * Phase 9: Governance Report Types
4
+ *
5
+ * Structured data model for the AI Code Governance Report.
6
+ * Every field maps to data already present in .progmune_corpus/.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/audit.js CHANGED
@@ -161,7 +161,7 @@ function formatAuditResult(result) {
161
161
  lines.push("");
162
162
  }
163
163
  }
164
- catch { }
164
+ catch { /* audit step — best-effort */ }
165
165
  // Phase 7: Violation Analytics
166
166
  try {
167
167
  const repairDir = ".progmune_corpus/repairs";
@@ -176,7 +176,7 @@ function formatAuditResult(result) {
176
176
  bySvl[r.violation] = (bySvl[r.violation] || 0) + 1;
177
177
  byConstraint[r.constraint] = (byConstraint[r.constraint] || 0) + 1;
178
178
  }
179
- catch { }
179
+ catch { /* audit step — best-effort */ }
180
180
  }
181
181
  lines.push(` Repairs recorded: ${repairs.length}`);
182
182
  const svlSummary = Object.entries(bySvl).map(([k, v]) => `${k}: ${v}`).join(" | ");
@@ -189,7 +189,7 @@ function formatAuditResult(result) {
189
189
  }
190
190
  }
191
191
  }
192
- catch { }
192
+ catch { /* audit step — best-effort */ }
193
193
  if (result.sessions.length > 0) {
194
194
  lines.push(` Last generation: ${result.lastGeneration || "unknown"}`);
195
195
  lines.push(` Unique sessions: ${new Set(result.sessions.map(s => s.sessionId)).size}`);