@karajan-family/code 4.21.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 (746) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +98 -0
  3. package/bin/karajan-mcp.js +2 -0
  4. package/bin/kj-rag-mcp.js +75 -0
  5. package/bin/kj-tail +361 -0
  6. package/bin/kj.js +2 -0
  7. package/docs/README.es.md +80 -0
  8. package/docs/karajan-code-logo-small.png +0 -0
  9. package/library/logical-clocks-lamport.md +40 -0
  10. package/package.json +140 -0
  11. package/packages/ai-trash/README.md +37 -0
  12. package/packages/ai-trash/bin/kj-trash.js +10 -0
  13. package/packages/ai-trash/package.json +43 -0
  14. package/packages/ai-trash/src/cli.js +200 -0
  15. package/packages/ai-trash/src/destructive-parser.js +74 -0
  16. package/packages/ai-trash/src/git-snapshot.js +128 -0
  17. package/packages/ai-trash/src/hook.js +137 -0
  18. package/packages/ai-trash/src/index.js +14 -0
  19. package/packages/ai-trash/src/install.js +73 -0
  20. package/packages/ai-trash/src/logger.js +60 -0
  21. package/packages/ai-trash/src/manifest.js +91 -0
  22. package/packages/ai-trash/src/permissions.js +26 -0
  23. package/packages/ai-trash/src/snapshot.js +66 -0
  24. package/packages/ai-trash/src/ulid.js +66 -0
  25. package/packages/hu-board/README.md +69 -0
  26. package/packages/hu-board/package.json +28 -0
  27. package/packages/hu-board/public/app.js +259 -0
  28. package/packages/hu-board/public/index.html +77 -0
  29. package/packages/hu-board/public/pipeline.css +132 -0
  30. package/packages/hu-board/public/pipeline.html +48 -0
  31. package/packages/hu-board/public/pipeline.js +200 -0
  32. package/packages/hu-board/public/rag.html +21 -0
  33. package/packages/hu-board/public/rag.js +39 -0
  34. package/packages/hu-board/public/styles.css +973 -0
  35. package/packages/hu-board/public/utils/api.js +139 -0
  36. package/packages/hu-board/public/utils/board-view.js +473 -0
  37. package/packages/hu-board/public/utils/command-launcher.js +213 -0
  38. package/packages/hu-board/public/utils/config-editor.js +181 -0
  39. package/packages/hu-board/public/utils/dashboard-view.js +103 -0
  40. package/packages/hu-board/public/utils/formatters.js +300 -0
  41. package/packages/hu-board/public/utils/graph-view.js +216 -0
  42. package/packages/hu-board/public/utils/hu-actions.js +224 -0
  43. package/packages/hu-board/public/utils/init-listeners.js +180 -0
  44. package/packages/hu-board/public/utils/log-panel.js +222 -0
  45. package/packages/hu-board/public/utils/modals.js +309 -0
  46. package/packages/hu-board/public/utils/plan-rollup-view.js +53 -0
  47. package/packages/hu-board/public/utils/preflight-view.js +89 -0
  48. package/packages/hu-board/public/utils/project-actions.js +143 -0
  49. package/packages/hu-board/public/utils/project-picker-view.js +139 -0
  50. package/packages/hu-board/public/utils/run-launcher.js +169 -0
  51. package/packages/hu-board/public/utils/server-push.js +252 -0
  52. package/packages/hu-board/public/utils/sessions-view.js +199 -0
  53. package/packages/hu-board/public/utils/story-detail-view.js +324 -0
  54. package/packages/hu-board/public/utils/story-edit-form.js +271 -0
  55. package/packages/hu-board/public/wiki.html +32 -0
  56. package/packages/hu-board/public/wiki.js +53 -0
  57. package/packages/hu-board/src/auth.js +92 -0
  58. package/packages/hu-board/src/boot-info.js +16 -0
  59. package/packages/hu-board/src/cleanup-zombies.js +145 -0
  60. package/packages/hu-board/src/command-runner.js +133 -0
  61. package/packages/hu-board/src/config-yaml.js +531 -0
  62. package/packages/hu-board/src/db.js +963 -0
  63. package/packages/hu-board/src/ephemeral-cleaner.js +196 -0
  64. package/packages/hu-board/src/event-bus.js +58 -0
  65. package/packages/hu-board/src/format.js +207 -0
  66. package/packages/hu-board/src/hu-zombie-reaper.js +103 -0
  67. package/packages/hu-board/src/journal-parser.js +180 -0
  68. package/packages/hu-board/src/plan-mutations.js +424 -0
  69. package/packages/hu-board/src/preflight.js +286 -0
  70. package/packages/hu-board/src/routes/api.js +1374 -0
  71. package/packages/hu-board/src/routes/pipeline.js +45 -0
  72. package/packages/hu-board/src/routes/rag.js +42 -0
  73. package/packages/hu-board/src/routes/wiki.js +43 -0
  74. package/packages/hu-board/src/run-tracker.js +121 -0
  75. package/packages/hu-board/src/server.js +438 -0
  76. package/packages/hu-board/src/sync.js +732 -0
  77. package/packages/hu-board/src/token-store.js +56 -0
  78. package/packages/hu-board/src/zombie-reaper.js +206 -0
  79. package/scripts/ai-attribution-guard.yml +84 -0
  80. package/scripts/audit-test-diet.mjs +151 -0
  81. package/scripts/build-sea.mjs +150 -0
  82. package/scripts/diet-baseline.mjs +170 -0
  83. package/scripts/dual-publish.mjs +67 -0
  84. package/scripts/esbuild-sea.config.mjs +273 -0
  85. package/scripts/git-hooks/post-merge +18 -0
  86. package/scripts/install-binary.ps1 +174 -0
  87. package/scripts/install-binary.sh +159 -0
  88. package/scripts/install-guard-all-repos.sh +85 -0
  89. package/scripts/install-kj.sh +193 -0
  90. package/scripts/install.js +901 -0
  91. package/scripts/install.sh +7 -0
  92. package/scripts/postinstall.js +117 -0
  93. package/scripts/protect-all-repos.sh +88 -0
  94. package/scripts/quickstart-gate.mjs +97 -0
  95. package/scripts/regen-arch-stats.sh +71 -0
  96. package/scripts/setup-multi-instance.sh +150 -0
  97. package/scripts/verify-pack.mjs +278 -0
  98. package/src/activity-log.js +59 -0
  99. package/src/agents/agy-agent.js +62 -0
  100. package/src/agents/aider-agent.js +78 -0
  101. package/src/agents/availability.js +52 -0
  102. package/src/agents/base-agent.js +134 -0
  103. package/src/agents/claude-agent.js +413 -0
  104. package/src/agents/codex-agent.js +112 -0
  105. package/src/agents/copilot-agent.js +65 -0
  106. package/src/agents/gemini-agent.js +85 -0
  107. package/src/agents/host-agent.js +38 -0
  108. package/src/agents/index.js +59 -0
  109. package/src/agents/kimi-agent.js +45 -0
  110. package/src/agents/model-registry.js +137 -0
  111. package/src/agents/opencode-agent.js +72 -0
  112. package/src/agents/qwen-agent.js +20 -0
  113. package/src/agents/resolve-bin.js +73 -0
  114. package/src/audit/agent-readiness.js +259 -0
  115. package/src/audit/ai-slop-findings.js +102 -0
  116. package/src/audit/audit-fallback.js +66 -0
  117. package/src/audit/audit-history-display.js +61 -0
  118. package/src/audit/audit-history.js +102 -0
  119. package/src/audit/basal-cost.js +281 -0
  120. package/src/audit/circular-deps.js +144 -0
  121. package/src/audit/dead-exports.js +186 -0
  122. package/src/audit/deterministic-summary.js +327 -0
  123. package/src/audit/harness-scorecard.js +100 -0
  124. package/src/audit/harness-section.js +81 -0
  125. package/src/audit/infra-findings.js +100 -0
  126. package/src/audit/injection-findings.js +114 -0
  127. package/src/audit/issue-filter.js +133 -0
  128. package/src/audit/osv-findings.js +167 -0
  129. package/src/audit/plan-adherence.js +224 -0
  130. package/src/audit/semgrep-findings.js +129 -0
  131. package/src/audit/sonar-findings.js +93 -0
  132. package/src/audit/webperf-input.js +133 -0
  133. package/src/autonomy/arbiter-role.js +71 -0
  134. package/src/autonomy/arbiter.js +22 -0
  135. package/src/autonomy/decider.js +18 -0
  136. package/src/autonomy/decision-resolver.js +58 -0
  137. package/src/autonomy/outcome-report.js +55 -0
  138. package/src/autonomy/policy.js +41 -0
  139. package/src/autonomy/stage-ask.js +30 -0
  140. package/src/bootstrap.js +276 -0
  141. package/src/brain/agent-error-classifier.js +116 -0
  142. package/src/brain/decision-tracker.js +125 -0
  143. package/src/brain/decisor.js +243 -0
  144. package/src/brain/solomon-consult.js +171 -0
  145. package/src/brain/standby-scheduler.js +8 -0
  146. package/src/brain/standby-store.js +12 -0
  147. package/src/brain/with-brain-recovery.js +235 -0
  148. package/src/budget/comparison.js +96 -0
  149. package/src/canvas/parse-md.js +238 -0
  150. package/src/canvas/schema.js +101 -0
  151. package/src/canvas/to-flat.js +85 -0
  152. package/src/canvas/validate.js +163 -0
  153. package/src/checks/ai-surface.js +79 -0
  154. package/src/checks/ai-trash.js +34 -0
  155. package/src/checks/binaries.js +207 -0
  156. package/src/checks/ci.js +105 -0
  157. package/src/checks/config-files.js +217 -0
  158. package/src/checks/dir-setup.js +103 -0
  159. package/src/checks/guarantee-level.js +38 -0
  160. package/src/checks/hardware.js +150 -0
  161. package/src/checks/harness-scorecard.js +41 -0
  162. package/src/checks/mcp-health.js +169 -0
  163. package/src/checks/method.js +73 -0
  164. package/src/checks/native-build.js +67 -0
  165. package/src/checks/node.js +70 -0
  166. package/src/checks/ollama.js +41 -0
  167. package/src/checks/orphans.js +77 -0
  168. package/src/checks/ports.js +188 -0
  169. package/src/checks/project-checks.js +245 -0
  170. package/src/checks/qmd.js +41 -0
  171. package/src/checks/rag-hooks.js +54 -0
  172. package/src/checks/release-check.js +102 -0
  173. package/src/checks/rtk.js +36 -0
  174. package/src/checks/runner.js +326 -0
  175. package/src/checks/skills.js +53 -0
  176. package/src/checks/sonar.js +67 -0
  177. package/src/checks/squeezr.js +36 -0
  178. package/src/checks/system.js +53 -0
  179. package/src/checks/tokens.js +172 -0
  180. package/src/checks/types.js +103 -0
  181. package/src/ci/dispatch.js +99 -0
  182. package/src/ci/index.js +3 -0
  183. package/src/ci/pr-diff.js +26 -0
  184. package/src/ci/repo.js +45 -0
  185. package/src/cli/_shared.js +30 -0
  186. package/src/cli/advanced-commands.js +61 -0
  187. package/src/cli/register-meta.js +772 -0
  188. package/src/cli/register-pipeline.js +335 -0
  189. package/src/cli/register-plan.js +224 -0
  190. package/src/cli/register-roles-skills.js +54 -0
  191. package/src/cli/register-sonar.js +28 -0
  192. package/src/cli/register-standby.js +26 -0
  193. package/src/cli.js +155 -0
  194. package/src/commands/advanced.js +31 -0
  195. package/src/commands/agent-run.js +63 -0
  196. package/src/commands/agents.js +105 -0
  197. package/src/commands/architect.js +123 -0
  198. package/src/commands/audit.js +430 -0
  199. package/src/commands/autorun.js +49 -0
  200. package/src/commands/board.js +458 -0
  201. package/src/commands/check.js +40 -0
  202. package/src/commands/clean.js +145 -0
  203. package/src/commands/code.js +45 -0
  204. package/src/commands/config.js +74 -0
  205. package/src/commands/discover.js +105 -0
  206. package/src/commands/doctor.js +251 -0
  207. package/src/commands/env.js +207 -0
  208. package/src/commands/harden.js +203 -0
  209. package/src/commands/hu.js +210 -0
  210. package/src/commands/identity.js +60 -0
  211. package/src/commands/init.js +950 -0
  212. package/src/commands/install-tools.js +432 -0
  213. package/src/commands/mutate.js +94 -0
  214. package/src/commands/ollama.js +44 -0
  215. package/src/commands/onboard.js +45 -0
  216. package/src/commands/plan/_shared.js +55 -0
  217. package/src/commands/plan/add-hu.js +23 -0
  218. package/src/commands/plan/delete.js +9 -0
  219. package/src/commands/plan/fix.js +99 -0
  220. package/src/commands/plan/generate.js +532 -0
  221. package/src/commands/plan/index.js +13 -0
  222. package/src/commands/plan/list.js +21 -0
  223. package/src/commands/plan/migrate-result.js +41 -0
  224. package/src/commands/plan/ready.js +46 -0
  225. package/src/commands/plan/remove-hu.js +17 -0
  226. package/src/commands/plan/share.js +85 -0
  227. package/src/commands/plan/show.js +81 -0
  228. package/src/commands/plan/unshare.js +41 -0
  229. package/src/commands/plan/validate.js +20 -0
  230. package/src/commands/plan.js +17 -0
  231. package/src/commands/policy.js +164 -0
  232. package/src/commands/privacy.js +45 -0
  233. package/src/commands/qmd.js +86 -0
  234. package/src/commands/rag-mcp.js +18 -0
  235. package/src/commands/rag.js +173 -0
  236. package/src/commands/report-issue.js +77 -0
  237. package/src/commands/report.js +469 -0
  238. package/src/commands/researcher.js +54 -0
  239. package/src/commands/resume.js +68 -0
  240. package/src/commands/review-gate.js +355 -0
  241. package/src/commands/review.js +89 -0
  242. package/src/commands/roles.js +116 -0
  243. package/src/commands/run.js +185 -0
  244. package/src/commands/scan.js +18 -0
  245. package/src/commands/skills.js +86 -0
  246. package/src/commands/sonar.js +53 -0
  247. package/src/commands/standby.js +117 -0
  248. package/src/commands/start.js +121 -0
  249. package/src/commands/status.js +36 -0
  250. package/src/commands/sync.js +103 -0
  251. package/src/commands/telemetry.js +70 -0
  252. package/src/commands/tournament.js +85 -0
  253. package/src/commands/triage.js +78 -0
  254. package/src/commands/undo.js +62 -0
  255. package/src/commands/watch.js +46 -0
  256. package/src/commands/webperf.js +162 -0
  257. package/src/commands/worktree.js +76 -0
  258. package/src/config/defaults.js +270 -0
  259. package/src/config/loader.js +204 -0
  260. package/src/config/overrides.js +220 -0
  261. package/src/config/role-resolver.js +171 -0
  262. package/src/config/schema.js +299 -0
  263. package/src/config/test-harness.js +78 -0
  264. package/src/config.js +34 -0
  265. package/src/domains/domain-loader.js +245 -0
  266. package/src/domains/domain-registry.js +140 -0
  267. package/src/domains/domain-synthesizer.js +129 -0
  268. package/src/environment/adr.js +45 -0
  269. package/src/environment/board-access.js +97 -0
  270. package/src/environment/board-select.js +59 -0
  271. package/src/environment/briefs.js +129 -0
  272. package/src/environment/issue-report.js +72 -0
  273. package/src/environment/mcp-wiring.js +44 -0
  274. package/src/environment/playbook.js +127 -0
  275. package/src/git/automation.js +334 -0
  276. package/src/git/hu-automation.js +171 -0
  277. package/src/git/hu-snapshot.js +10 -0
  278. package/src/golden/asserter.js +92 -0
  279. package/src/golden/loader.js +52 -0
  280. package/src/golden/runner.js +92 -0
  281. package/src/golden/schema.js +41 -0
  282. package/src/guards/intent-guard.js +161 -0
  283. package/src/guards/output-guard.js +149 -0
  284. package/src/guards/perf-guard.js +126 -0
  285. package/src/guards/policy-guard.js +90 -0
  286. package/src/guards/policy-resolver.js +85 -0
  287. package/src/guards/secret-redactor.js +71 -0
  288. package/src/harden/advisory.js +203 -0
  289. package/src/harden/alternatives.js +50 -0
  290. package/src/harden/check.js +81 -0
  291. package/src/harden/config-engine.js +128 -0
  292. package/src/harden/config-templates.js +122 -0
  293. package/src/harden/guidelines-engine.js +48 -0
  294. package/src/harden/guidelines-templates.js +36 -0
  295. package/src/harden/harden-engine.js +80 -0
  296. package/src/harden/harness-hooks.js +107 -0
  297. package/src/harden/hook-commands.js +25 -0
  298. package/src/harden/hook-templates.js +183 -0
  299. package/src/harden/interactive.js +76 -0
  300. package/src/harden/sentinel-hooks.js +721 -0
  301. package/src/harden/stack-roots.js +105 -0
  302. package/src/harden/test-on-push.js +38 -0
  303. package/src/harden/workflow-engine.js +116 -0
  304. package/src/harden/workflow-templates.js +285 -0
  305. package/src/hu/acceptance-runner.js +138 -0
  306. package/src/hu/auto-generator.js +358 -0
  307. package/src/hu/graph.js +71 -0
  308. package/src/hu/lazy-planner.js +109 -0
  309. package/src/hu/model-router.js +108 -0
  310. package/src/hu/parallel-executor.js +94 -0
  311. package/src/hu/split-validator.js +143 -0
  312. package/src/hu/splitting-detector.js +158 -0
  313. package/src/hu/splitting-generator.js +175 -0
  314. package/src/hu/store.js +392 -0
  315. package/src/hu/worktree-bootstrap.js +59 -0
  316. package/src/identity/bootstrap.js +48 -0
  317. package/src/identity/compare.js +38 -0
  318. package/src/identity/detect.js +51 -0
  319. package/src/identity/store.js +66 -0
  320. package/src/infrastructure/command-runner.js +44 -0
  321. package/src/infrastructure/environment.js +38 -0
  322. package/src/infrastructure/file-system-service.js +63 -0
  323. package/src/infrastructure/mocks.js +182 -0
  324. package/src/lang/chunk-go.js +94 -0
  325. package/src/lang/chunk-java.js +90 -0
  326. package/src/lang/chunk-python.js +88 -0
  327. package/src/lang/chunk-rust.js +102 -0
  328. package/src/lang/go.js +14 -0
  329. package/src/lang/java.js +16 -0
  330. package/src/lang/js.js +8 -0
  331. package/src/lang/python.js +14 -0
  332. package/src/lang/registry.js +79 -0
  333. package/src/lang/rust.js +14 -0
  334. package/src/lang/tree-sitter-loader.js +45 -0
  335. package/src/mcp/direct-role-runner.js +112 -0
  336. package/src/mcp/error-classifiers.js +66 -0
  337. package/src/mcp/handlers/direct-handlers.js +486 -0
  338. package/src/mcp/handlers/hu-handlers.js +93 -0
  339. package/src/mcp/handlers/management-handlers.js +265 -0
  340. package/src/mcp/handlers/rag-handler.js +52 -0
  341. package/src/mcp/handlers/run-handler.js +313 -0
  342. package/src/mcp/orphan-guard.js +131 -0
  343. package/src/mcp/preflight.js +47 -0
  344. package/src/mcp/progress.js +149 -0
  345. package/src/mcp/response-compressor.js +81 -0
  346. package/src/mcp/run-kj.js +135 -0
  347. package/src/mcp/server-handlers.js +79 -0
  348. package/src/mcp/server.js +66 -0
  349. package/src/mcp/shared-helpers.js +297 -0
  350. package/src/mcp/sovereignty-guard.js +112 -0
  351. package/src/mcp/suggest-handler.js +92 -0
  352. package/src/mcp/tool-arg-normalizers.js +16 -0
  353. package/src/mcp/tools.js +432 -0
  354. package/src/mutate/diff-scope.js +105 -0
  355. package/src/mutate/reviewer-signal.js +76 -0
  356. package/src/mutate/runner.js +72 -0
  357. package/src/mutate/tool-registry.js +90 -0
  358. package/src/onboarder/cache.js +24 -0
  359. package/src/onboarder/collectors/framework-multilang.js +64 -0
  360. package/src/onboarder/collectors/index.js +136 -0
  361. package/src/orchestrator/agent-fallback.js +81 -0
  362. package/src/orchestrator/brain-coordinator.js +186 -0
  363. package/src/orchestrator/ci-integration.js +139 -0
  364. package/src/orchestrator/config-init.js +510 -0
  365. package/src/orchestrator/direct-actions.js +217 -0
  366. package/src/orchestrator/drivers/error-recovery.js +287 -0
  367. package/src/orchestrator/drivers/init-context.js +227 -0
  368. package/src/orchestrator/drivers/iteration-loop.js +380 -0
  369. package/src/orchestrator/drivers/iteration-phases/coder-and-refactorer.js +69 -0
  370. package/src/orchestrator/drivers/iteration-phases/guards.js +84 -0
  371. package/src/orchestrator/drivers/iteration-phases/handle-approved.js +29 -0
  372. package/src/orchestrator/drivers/iteration-phases/quality-gates.js +114 -0
  373. package/src/orchestrator/drivers/iteration-phases/reviewer-gate.js +60 -0
  374. package/src/orchestrator/drivers/post-loop.js +383 -0
  375. package/src/orchestrator/drivers/pre-loop-phases/auto-hu-batch.js +136 -0
  376. package/src/orchestrator/drivers/pre-loop-phases/config-deprecations.js +47 -0
  377. package/src/orchestrator/drivers/pre-loop-phases/ensure-addyosmani-skills.js +84 -0
  378. package/src/orchestrator/drivers/pre-loop-phases/inject-loaded-plan.js +391 -0
  379. package/src/orchestrator/drivers/pre-loop.js +507 -0
  380. package/src/orchestrator/drivers/run-hu-batch.js +427 -0
  381. package/src/orchestrator/feedback-enrichment.js +140 -0
  382. package/src/orchestrator/feedback-queue.js +151 -0
  383. package/src/orchestrator/flow-control.js +170 -0
  384. package/src/orchestrator/flow-runner.js +364 -0
  385. package/src/orchestrator/fs-leak-detector.js +210 -0
  386. package/src/orchestrator/hu-scheduler.js +114 -0
  387. package/src/orchestrator/hu-sub-pipeline.js +616 -0
  388. package/src/orchestrator/integrations.js +81 -0
  389. package/src/orchestrator/iteration-gate.js +73 -0
  390. package/src/orchestrator/iteration-stages.js +9 -0
  391. package/src/orchestrator/parallel-limiter.js +74 -0
  392. package/src/orchestrator/pipeline-context.js +72 -0
  393. package/src/orchestrator/post-loop-stages.js +468 -0
  394. package/src/orchestrator/pre-loop-stages.js +11 -0
  395. package/src/orchestrator/preflight-checks.js +462 -0
  396. package/src/orchestrator/repair/repair-detector.js +117 -0
  397. package/src/orchestrator/repair/repair-runner.js +142 -0
  398. package/src/orchestrator/reviewer-fallback.js +45 -0
  399. package/src/orchestrator/role-output-compressor.js +189 -0
  400. package/src/orchestrator/session-journal.js +308 -0
  401. package/src/orchestrator/solomon-escalation.js +305 -0
  402. package/src/orchestrator/solomon-rules.js +212 -0
  403. package/src/orchestrator/stages/architect-stage.js +172 -0
  404. package/src/orchestrator/stages/coder-stage.js +480 -0
  405. package/src/orchestrator/stages/domain-curator-stage.js +82 -0
  406. package/src/orchestrator/stages/hu-reviewer-phases/detect-and-apply-splits.js +127 -0
  407. package/src/orchestrator/stages/hu-reviewer-stage.js +394 -0
  408. package/src/orchestrator/stages/perf-stage.js +140 -0
  409. package/src/orchestrator/stages/planner-stage.js +85 -0
  410. package/src/orchestrator/stages/rag-context-stage.js +77 -0
  411. package/src/orchestrator/stages/rag-preload-decisor.js +58 -0
  412. package/src/orchestrator/stages/research-stage.js +69 -0
  413. package/src/orchestrator/stages/reviewer-stage.js +422 -0
  414. package/src/orchestrator/stages/sonar-stage.js +421 -0
  415. package/src/orchestrator/stages/stage-classes.js +84 -0
  416. package/src/orchestrator/stages/stage-executor.js +155 -0
  417. package/src/orchestrator/stages/tdd-discipline-stage.js +56 -0
  418. package/src/orchestrator/stages/tool-judge-stage.js +53 -0
  419. package/src/orchestrator/stages/triage-stage.js +172 -0
  420. package/src/orchestrator/standby.js +71 -0
  421. package/src/orchestrator/verification-gate.js +183 -0
  422. package/src/orchestrator.js +22 -0
  423. package/src/plan/adr-generator.js +170 -0
  424. package/src/plan/adr-loader.js +60 -0
  425. package/src/plan/plan-executor.js +100 -0
  426. package/src/plan/plan-fixer.js +192 -0
  427. package/src/plan/plan-hu-ops.js +16 -0
  428. package/src/plan/plan-id.js +4 -0
  429. package/src/plan/plan-migrate-result.js +24 -0
  430. package/src/plan/plan-reviewer.js +236 -0
  431. package/src/plan/plan-schema.js +334 -0
  432. package/src/plan/plan-store.js +260 -0
  433. package/src/plan/plan-structural-pass.js +122 -0
  434. package/src/plan/plan-validation.js +4 -0
  435. package/src/plan/preflight-hu.js +105 -0
  436. package/src/plan/spec-detector.js +61 -0
  437. package/src/plan/tests-synthesizer.js +131 -0
  438. package/src/planning-game/adapter.js +108 -0
  439. package/src/planning-game/architect-adrs.js +45 -0
  440. package/src/planning-game/client.js +108 -0
  441. package/src/planning-game/decomposition.js +87 -0
  442. package/src/planning-game/pipeline-adapter.js +251 -0
  443. package/src/plugins/loader.js +67 -0
  444. package/src/policy/add.js +111 -0
  445. package/src/policy/decisions.js +42 -0
  446. package/src/policy/engine.js +82 -0
  447. package/src/policy/exceptions.js +60 -0
  448. package/src/privacy/onboarding.js +35 -0
  449. package/src/privacy/scan.js +120 -0
  450. package/src/prompts/arbiter.js +38 -0
  451. package/src/prompts/architect.js +103 -0
  452. package/src/prompts/audit.js +474 -0
  453. package/src/prompts/coder.js +287 -0
  454. package/src/prompts/diff-clip.js +27 -0
  455. package/src/prompts/discover.js +222 -0
  456. package/src/prompts/hu-reviewer.js +219 -0
  457. package/src/prompts/planner.js +312 -0
  458. package/src/prompts/prompt-layout.js +80 -0
  459. package/src/prompts/prompt-resolver.js +120 -0
  460. package/src/prompts/reviewer.js +75 -0
  461. package/src/prompts/rtk-snippet.js +25 -0
  462. package/src/prompts/spec-reviewer.js +66 -0
  463. package/src/prompts/split-signal.js +82 -0
  464. package/src/prompts/start-decision.js +52 -0
  465. package/src/prompts/tool-correctness-judge.js +31 -0
  466. package/src/prompts/triage.js +57 -0
  467. package/src/rag/auto-update.js +72 -0
  468. package/src/rag/chunker-ast.js +56 -0
  469. package/src/rag/chunker.js +132 -0
  470. package/src/rag/embedder.js +2 -0
  471. package/src/rag/embedders/_cloud-base.js +2 -0
  472. package/src/rag/embedders/cohere.js +2 -0
  473. package/src/rag/embedders/factory.js +2 -0
  474. package/src/rag/embedders/mistral.js +2 -0
  475. package/src/rag/embedders/onnx.js +2 -0
  476. package/src/rag/embedders/openai.js +2 -0
  477. package/src/rag/embedders/voyage.js +2 -0
  478. package/src/rag/eval.js +61 -0
  479. package/src/rag/governed-embedder.js +39 -0
  480. package/src/rag/indexer.js +199 -0
  481. package/src/rag/library.js +62 -0
  482. package/src/rag/ollama-capability.js +59 -0
  483. package/src/rag/ollama-manager.js +98 -0
  484. package/src/rag/onnx-fallback.js +69 -0
  485. package/src/rag/rerank.js +4 -0
  486. package/src/rag/retriever.js +2 -0
  487. package/src/rag/vec-store.js +17 -0
  488. package/src/rag/watcher.js +62 -0
  489. package/src/rag/where-parser.js +4 -0
  490. package/src/repeat-detector.js +88 -0
  491. package/src/review/card-first.js +99 -0
  492. package/src/review/card-verify.js +83 -0
  493. package/src/review/diff-generator.js +112 -0
  494. package/src/review/gate-gitignore.js +77 -0
  495. package/src/review/git-stash-helper.js +104 -0
  496. package/src/review/mutation-pregate.js +48 -0
  497. package/src/review/one-shot-review.js +122 -0
  498. package/src/review/parser.js +80 -0
  499. package/src/review/policy-gate.js +26 -0
  500. package/src/review/profiles.js +66 -0
  501. package/src/review/reviewer-fallback.js +125 -0
  502. package/src/review/schema.js +31 -0
  503. package/src/review/scope-filter.js +152 -0
  504. package/src/review/snapshot-diff.js +122 -0
  505. package/src/review/solomon-arbitration.js +113 -0
  506. package/src/review/sonar-pregate.js +63 -0
  507. package/src/review/tdd-discipline.js +90 -0
  508. package/src/review/tdd-policy.js +69 -0
  509. package/src/review/tests-with-code.js +33 -0
  510. package/src/review/verdict-store.js +87 -0
  511. package/src/review/workspace.js +21 -0
  512. package/src/roles/agent-role.js +194 -0
  513. package/src/roles/architect-role.js +77 -0
  514. package/src/roles/audit-role.js +258 -0
  515. package/src/roles/base-role.js +165 -0
  516. package/src/roles/coder-role.js +107 -0
  517. package/src/roles/commiter-role.js +110 -0
  518. package/src/roles/discover-role.js +81 -0
  519. package/src/roles/domain-curator-role.js +141 -0
  520. package/src/roles/hu-reviewer-role.js +64 -0
  521. package/src/roles/impeccable-role.js +85 -0
  522. package/src/roles/index.js +18 -0
  523. package/src/roles/karajan-brain-role.js +134 -0
  524. package/src/roles/onboarder-role.js +61 -0
  525. package/src/roles/perf-role.js +122 -0
  526. package/src/roles/planner-role.js +151 -0
  527. package/src/roles/refactorer-role.js +30 -0
  528. package/src/roles/repairer-role.js +181 -0
  529. package/src/roles/researcher-role.js +70 -0
  530. package/src/roles/reviewer-role.js +104 -0
  531. package/src/roles/security-role.js +73 -0
  532. package/src/roles/solomon-role.js +265 -0
  533. package/src/roles/sonar-role.js +70 -0
  534. package/src/roles/spec-reviewer-role.js +106 -0
  535. package/src/roles/tester-role.js +235 -0
  536. package/src/roles/tool-correctness-judge-role.js +78 -0
  537. package/src/roles/triage-role.js +99 -0
  538. package/src/session/journal/decisions-writer.js +200 -0
  539. package/src/session/journal/iteration-logger.js +194 -0
  540. package/src/session/journal/summary-writer.js +320 -0
  541. package/src/session/journal/tree-writer.js +164 -0
  542. package/src/session/journal/write-all.js +89 -0
  543. package/src/session/mutators.js +339 -0
  544. package/src/session/runtime-overrides.js +59 -0
  545. package/src/session/store.js +106 -0
  546. package/src/session-cleanup.js +116 -0
  547. package/src/skills/addyosmani-catalog.js +315 -0
  548. package/src/skills/addyosmani-role-map.js +139 -0
  549. package/src/skills/openskills-client.js +157 -0
  550. package/src/skills/semantic-detector.js +167 -0
  551. package/src/skills/skill-detector.js +385 -0
  552. package/src/skills/skill-loader.js +219 -0
  553. package/src/skills/skills-cache.js +128 -0
  554. package/src/sonar/api.js +128 -0
  555. package/src/sonar/cloud-scanner.js +78 -0
  556. package/src/sonar/config-resolver.js +88 -0
  557. package/src/sonar/credentials.js +60 -0
  558. package/src/sonar/discovery.js +124 -0
  559. package/src/sonar/enforcer.js +19 -0
  560. package/src/sonar/issue-filter.js +12 -0
  561. package/src/sonar/manager.js +181 -0
  562. package/src/sonar/project-key.js +120 -0
  563. package/src/sonar/scanner.js +307 -0
  564. package/src/sonar/token-bootstrap.js +205 -0
  565. package/src/sonar/token-recovery.js +67 -0
  566. package/src/spec-review/refine-loop.js +58 -0
  567. package/src/spec-review/run-spec-review.js +119 -0
  568. package/src/start/assessment.js +74 -0
  569. package/src/start/maturity.js +73 -0
  570. package/src/start/start-decider-role.js +79 -0
  571. package/src/start/sweep.js +157 -0
  572. package/src/sync/apply-canvas-patch.js +55 -0
  573. package/src/sync/detect-drift.js +113 -0
  574. package/src/tournament/crown.js +88 -0
  575. package/src/tournament/judge.js +150 -0
  576. package/src/tournament/run.js +142 -0
  577. package/src/tournament/scoreboard.js +124 -0
  578. package/src/tracker-bootstrap.js +44 -0
  579. package/src/types/agent.js +55 -0
  580. package/src/types/config.js +114 -0
  581. package/src/types/hu.js +47 -0
  582. package/src/types/index.js +38 -0
  583. package/src/types/policy.js +43 -0
  584. package/src/types/session.js +75 -0
  585. package/src/types/solomon.js +87 -0
  586. package/src/types/stage.js +68 -0
  587. package/src/utils/agent-detect.js +93 -0
  588. package/src/utils/ai-trash.js +48 -0
  589. package/src/utils/async-lock.js +31 -0
  590. package/src/utils/atomic-write.js +6 -0
  591. package/src/utils/banner.js +66 -0
  592. package/src/utils/binary-sources.js +70 -0
  593. package/src/utils/board-prompt-bridge.js +108 -0
  594. package/src/utils/budget.js +229 -0
  595. package/src/utils/cli-ask-question.js +93 -0
  596. package/src/utils/cli-progress.js +220 -0
  597. package/src/utils/cli-run-log.js +83 -0
  598. package/src/utils/cwd-confirm.js +39 -0
  599. package/src/utils/derive-project-name-from-cwd.js +119 -0
  600. package/src/utils/display/event-handlers.js +403 -0
  601. package/src/utils/display/formatters.js +132 -0
  602. package/src/utils/display/header.js +47 -0
  603. package/src/utils/display/resume-hint.js +41 -0
  604. package/src/utils/display/session-summary.js +118 -0
  605. package/src/utils/display/solomon.js +42 -0
  606. package/src/utils/display/spec-findings.js +44 -0
  607. package/src/utils/doc-links.js +29 -0
  608. package/src/utils/docker-install.js +34 -0
  609. package/src/utils/escape-regexp.js +15 -0
  610. package/src/utils/events.js +35 -0
  611. package/src/utils/fs.js +19 -0
  612. package/src/utils/garbage-collector.js +481 -0
  613. package/src/utils/git.js +279 -0
  614. package/src/utils/home-migration.js +87 -0
  615. package/src/utils/injection-guard.js +186 -0
  616. package/src/utils/install-hints.js +180 -0
  617. package/src/utils/json-extract.js +64 -0
  618. package/src/utils/locale.js +59 -0
  619. package/src/utils/logger.js +96 -0
  620. package/src/utils/managed-markers.js +93 -0
  621. package/src/utils/manifest-sniff.js +82 -0
  622. package/src/utils/messages.js +61 -0
  623. package/src/utils/model-selector.js +107 -0
  624. package/src/utils/os-detect.js +79 -0
  625. package/src/utils/paths.js +13 -0
  626. package/src/utils/pending-user-action.js +95 -0
  627. package/src/utils/port-check.js +6 -0
  628. package/src/utils/port-occupant.js +96 -0
  629. package/src/utils/pricing.js +29 -0
  630. package/src/utils/process.js +4 -0
  631. package/src/utils/project-detect.js +160 -0
  632. package/src/utils/prompt-project-name.js +35 -0
  633. package/src/utils/provider-env.js +116 -0
  634. package/src/utils/qmd-collection.js +72 -0
  635. package/src/utils/qmd-detect.js +18 -0
  636. package/src/utils/qmd-install.js +37 -0
  637. package/src/utils/quota-simulator.js +56 -0
  638. package/src/utils/rate-limit-detector.js +181 -0
  639. package/src/utils/repo-cleaner.js +79 -0
  640. package/src/utils/retry.js +88 -0
  641. package/src/utils/role-assigner.js +137 -0
  642. package/src/utils/role-env.js +58 -0
  643. package/src/utils/rtk-detect.js +18 -0
  644. package/src/utils/rtk-install.js +38 -0
  645. package/src/utils/rtk-wrapper.js +87 -0
  646. package/src/utils/run-context.js +75 -0
  647. package/src/utils/run-log.js +217 -0
  648. package/src/utils/run-registry.js +9 -0
  649. package/src/utils/shared-paths.js +12 -0
  650. package/src/utils/squeezr-detect.js +21 -0
  651. package/src/utils/squeezr-install.js +37 -0
  652. package/src/utils/stack-detect.js +135 -0
  653. package/src/utils/stall-detector.js +159 -0
  654. package/src/utils/status-dashboard.js +254 -0
  655. package/src/utils/task-file.js +109 -0
  656. package/src/utils/telemetry.js +124 -0
  657. package/src/utils/templates-root.js +61 -0
  658. package/src/utils/tool-call-extractor.js +56 -0
  659. package/src/utils/tool-governor.js +116 -0
  660. package/src/utils/tool-installer.js +93 -0
  661. package/src/utils/update-check.js +280 -0
  662. package/src/utils/vector-store-orphans.js +89 -0
  663. package/src/utils/welcome.js +47 -0
  664. package/src/utils/with-timeout.js +42 -0
  665. package/src/utils/wizard.js +47 -0
  666. package/src/webperf/cwv-gate.js +94 -0
  667. package/src/webperf/devtools-detect.js +112 -0
  668. package/src/webperf/scanner.js +209 -0
  669. package/src/webperf/webperf-role-integration.js +66 -0
  670. package/templates/coder-rules.md +45 -0
  671. package/templates/docker-compose.sonar.yml +60 -0
  672. package/templates/domains/example-dental.md +57 -0
  673. package/templates/kj.config.yml +128 -0
  674. package/templates/review-rules.md +17 -0
  675. package/templates/roles/architect/anthropic.md +74 -0
  676. package/templates/roles/architect/default.md +72 -0
  677. package/templates/roles/architect/google.md +61 -0
  678. package/templates/roles/architect/openai.md +60 -0
  679. package/templates/roles/architect/opencode.md +59 -0
  680. package/templates/roles/architect.md +76 -0
  681. package/templates/roles/audit.md +68 -0
  682. package/templates/roles/coder/anthropic.md +81 -0
  683. package/templates/roles/coder/default.md +78 -0
  684. package/templates/roles/coder/google.md +87 -0
  685. package/templates/roles/coder/openai.md +68 -0
  686. package/templates/roles/coder/opencode.md +58 -0
  687. package/templates/roles/coder.md +152 -0
  688. package/templates/roles/commiter.md +44 -0
  689. package/templates/roles/discover.md +167 -0
  690. package/templates/roles/domain-curator.md +52 -0
  691. package/templates/roles/hu-reviewer.md +236 -0
  692. package/templates/roles/impeccable-design.md +87 -0
  693. package/templates/roles/impeccable.md +125 -0
  694. package/templates/roles/karajan-brain.md +95 -0
  695. package/templates/roles/onboarder.md +45 -0
  696. package/templates/roles/planner/anthropic.md +59 -0
  697. package/templates/roles/planner/default.md +57 -0
  698. package/templates/roles/planner/google.md +75 -0
  699. package/templates/roles/planner/openai.md +55 -0
  700. package/templates/roles/planner/opencode.md +62 -0
  701. package/templates/roles/planner.md +61 -0
  702. package/templates/roles/refactorer.md +48 -0
  703. package/templates/roles/repairer.md +103 -0
  704. package/templates/roles/researcher.md +41 -0
  705. package/templates/roles/reviewer/anthropic.md +61 -0
  706. package/templates/roles/reviewer/default.md +62 -0
  707. package/templates/roles/reviewer/google.md +63 -0
  708. package/templates/roles/reviewer/openai.md +57 -0
  709. package/templates/roles/reviewer/opencode.md +42 -0
  710. package/templates/roles/reviewer-paranoid.md +38 -0
  711. package/templates/roles/reviewer-relaxed.md +34 -0
  712. package/templates/roles/reviewer-strict.md +37 -0
  713. package/templates/roles/reviewer.md +62 -0
  714. package/templates/roles/security.md +54 -0
  715. package/templates/roles/solomon/anthropic.md +71 -0
  716. package/templates/roles/solomon/default.md +83 -0
  717. package/templates/roles/solomon/google.md +61 -0
  718. package/templates/roles/solomon/openai.md +53 -0
  719. package/templates/roles/solomon/opencode.md +55 -0
  720. package/templates/roles/solomon.md +83 -0
  721. package/templates/roles/sonar.md +49 -0
  722. package/templates/roles/spec-reviewer.md +47 -0
  723. package/templates/roles/tester.md +50 -0
  724. package/templates/roles/triage.md +69 -0
  725. package/templates/skills/kj-architect.md +45 -0
  726. package/templates/skills/kj-audit.md +63 -0
  727. package/templates/skills/kj-board.md +16 -0
  728. package/templates/skills/kj-code.md +51 -0
  729. package/templates/skills/kj-csv-transform.md +40 -0
  730. package/templates/skills/kj-data-report.md +44 -0
  731. package/templates/skills/kj-discover.md +24 -0
  732. package/templates/skills/kj-rag-query.md +50 -0
  733. package/templates/skills/kj-review.md +47 -0
  734. package/templates/skills/kj-run.md +73 -0
  735. package/templates/skills/kj-security.md +49 -0
  736. package/templates/skills/kj-sonar.md +41 -0
  737. package/templates/skills/kj-sql-analysis.md +40 -0
  738. package/templates/skills/kj-test.md +40 -0
  739. package/templates/workflows/automerge.yml +30 -0
  740. package/templates/workflows/houston-override.yml +46 -0
  741. package/templates/workflows/kj-ci-gateway.yml +58 -0
  742. package/vendor/tree-sitter-grammars/README.md +21 -0
  743. package/vendor/tree-sitter-grammars/go.wasm +0 -0
  744. package/vendor/tree-sitter-grammars/java.wasm +0 -0
  745. package/vendor/tree-sitter-grammars/python.wasm +0 -0
  746. package/vendor/tree-sitter-grammars/rust.wasm +0 -0
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Verdict store (ENV-B1, KJC-TSK-0637) — persists cross-AI review verdicts
3
+ * keyed by the sha256 of the RAW diff they reviewed.
4
+ *
5
+ * The hash is the contract: a verdict only counts for byte-identical
6
+ * content, so any change after the review voids it and forces a new one
7
+ * (resolve-until-pass by construction). The pre-commit hook (ENV-C)
8
+ * calls checkVerdict() with the staged diff to decide whether the
9
+ * commit may enter. Diffs must be raw git output — never rtk-compressed
10
+ * (KJC-BUG-0115).
11
+ */
12
+ import crypto from "node:crypto";
13
+ import fs from "node:fs/promises";
14
+ import path from "node:path";
15
+ import { runCommand } from "../utils/process.js";
16
+
17
+ const STORE_DIR = path.join(".karajan", "reviews");
18
+
19
+ export function diffHash(diff) {
20
+ // trimEnd: runners differ on the final newline (execa strips it, raw
21
+ // git keeps it) — trailing whitespace must not void a verdict.
22
+ return crypto.createHash("sha256").update(diff.trimEnd(), "utf8").digest("hex");
23
+ }
24
+
25
+ function verdictPath(projectDir, hash) {
26
+ return path.join(projectDir, STORE_DIR, `${hash}.json`);
27
+ }
28
+
29
+ export async function saveVerdict(projectDir, diff, verdict) {
30
+ const hash = diffHash(diff);
31
+ const record = {
32
+ ...verdict,
33
+ diffHash: hash,
34
+ timestamp: new Date().toISOString(),
35
+ };
36
+ const file = verdictPath(projectDir, hash);
37
+ await fs.mkdir(path.dirname(file), { recursive: true });
38
+ await fs.writeFile(file, `${JSON.stringify(record, null, 2)}\n`);
39
+ return record;
40
+ }
41
+
42
+ export async function loadVerdict(projectDir, hash) {
43
+ try {
44
+ return JSON.parse(await fs.readFile(verdictPath(projectDir, hash), "utf8"));
45
+ } catch {
46
+ return null;
47
+ }
48
+ }
49
+
50
+ /**
51
+ * ENV-F1 (KJC-TSK-0643): headless pipeline sessions call this after staging
52
+ * and before committing. Their reviewer ALREADY cross-AI-reviewed the work,
53
+ * so the verdict is recorded for the staged diff and the v4 pre-commit gate
54
+ * accepts the pipeline's commit. No gate marker → no-op (zero overhead for
55
+ * repos that never opted in). Raw git only — never a wrapped runner
56
+ * (KJC-BUG-0115).
57
+ * @returns {Promise<{stamped: boolean}>}
58
+ */
59
+ export async function stampStagedVerdict({ projectDir, reviewer, summary = "" }) {
60
+ const dir = projectDir || process.cwd();
61
+ try {
62
+ await fs.access(path.join(dir, ".karajan", "review-gate"));
63
+ } catch {
64
+ return { stamped: false };
65
+ }
66
+ const res = await runCommand("git", ["diff", "--cached"], { cwd: dir });
67
+ if (res.exitCode !== 0 || !res.stdout?.trim()) return { stamped: false };
68
+ await saveVerdict(dir, res.stdout, {
69
+ verdict: "approved", reviewer, host: "kj-pipeline", issues: [], summary,
70
+ });
71
+ return { stamped: true };
72
+ }
73
+
74
+ /**
75
+ * Is there an APPROVED verdict for exactly this diff?
76
+ * @returns {Promise<{ok: boolean, verdict?: object, reason?: string}>}
77
+ */
78
+ export async function checkVerdict(projectDir, diff) {
79
+ const verdict = await loadVerdict(projectDir, diffHash(diff));
80
+ if (!verdict) {
81
+ return { ok: false, reason: "no verdict recorded for the current diff — run `kj review`" };
82
+ }
83
+ if (verdict.verdict !== "approved") {
84
+ return { ok: false, verdict, reason: `review was rejected by ${verdict.reviewer} — fix the issues and run \`kj review\` again` };
85
+ }
86
+ return { ok: true, verdict };
87
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Workspace detection for the review gate (KJC-TSK-0680). Field case: an
3
+ * agent claimed worktree isolation while editing the project root. The
4
+ * verdict now stamps WHERE the review actually ran, using the same proof
5
+ * the playbook demands: in a linked worktree, `git rev-parse --git-dir`
6
+ * differs from `--git-common-dir`. "root" | "worktree:<name>" | null
7
+ * (not a git repo — the gate itself will fail later with its own error).
8
+ */
9
+ import path from "node:path";
10
+ import { runCommand } from "../utils/process.js";
11
+
12
+ export async function detectWorkspace(dir) {
13
+ const res = await runCommand("git", ["rev-parse", "--git-dir", "--git-common-dir", "--show-toplevel"], { cwd: dir });
14
+ if (res.exitCode !== 0) return null;
15
+ const [gitDir, commonDir, toplevel] = String(res.stdout).trim().split("\n").map((l) => l.trim());
16
+ if (!gitDir || !commonDir) return null;
17
+ const resolvedGit = path.resolve(dir, gitDir);
18
+ const resolvedCommon = path.resolve(dir, commonDir);
19
+ if (resolvedGit === resolvedCommon) return "root";
20
+ return `worktree:${path.basename(toplevel || dir)}`;
21
+ }
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Base class for LLM-backed roles.
3
+ * Handles the common execute() flow: resolve provider → create agent → call LLM → parse output.
4
+ * Subclasses override hooks (buildPrompt, parseOutput, buildSuccessResult, buildSummary)
5
+ * instead of reimplementing the full execute() boilerplate.
6
+ *
7
+ * Conceptual mapping: `AgentRole` is a Karajan Role that delegates to an AI CLI
8
+ * subprocess (not to a provider HTTP API). From the outside it plays the same
9
+ * role as a Genkit Flow — named unit of work with a prompt and a parsed output —
10
+ * but every invocation spawns a fresh `execa` subprocess (claude, codex, gemini,
11
+ * aider, opencode) and the state lives in Karajan's session store, not in a
12
+ * provider-side conversation. See `docs/COMPARISON.md`.
13
+ *
14
+ * @alias Flow — similar to Genkit Flow but executed over subprocess CLIs, not API calls.
15
+ */
16
+ import { BaseRole } from "./base-role.js";
17
+ import { createAgent as defaultCreateAgent } from "../agents/index.js";
18
+ import { withBrainRecovery } from "../brain/with-brain-recovery.js";
19
+
20
+ /**
21
+ * Silence timeout (ms) for an agent subprocess, from
22
+ * config.session.max_agent_silence_minutes. runCommand kills a child
23
+ * that produces no output for this long — without it a hung coder /
24
+ * reviewer leaves `kj run` waiting forever. Returns null when unset.
25
+ * @param {object} config
26
+ * @returns {number|null}
27
+ */
28
+ function resolveAgentSilenceTimeoutMs(config) {
29
+ const minutes = Number(config?.session?.max_agent_silence_minutes);
30
+ return Number.isFinite(minutes) && minutes > 0 ? Math.round(minutes * 60 * 1000) : null;
31
+ }
32
+
33
+ export class AgentRole extends BaseRole {
34
+ constructor({ name, config, logger, emitter = null, createAgentFn = null }) {
35
+ super({ name, config, logger, emitter });
36
+ this._createAgent = createAgentFn || defaultCreateAgent;
37
+ }
38
+
39
+ /** Provider fallback chain. Override for roles with non-standard chains. */
40
+ resolveProvider() {
41
+ return (
42
+ this.config?.roles?.[this.name]?.provider ||
43
+ this.config?.roles?.coder?.provider ||
44
+ "claude"
45
+ );
46
+ }
47
+
48
+ /** Agent method to call. Override with "reviewTask" for reviewer. */
49
+ get agentMethod() { return "runTask"; }
50
+
51
+ /** Create the agent. Override for special logic (e.g. HostAgent in coder). */
52
+ createAgentInstance(provider) {
53
+ return this._createAgent(provider, this.config, this.logger);
54
+ }
55
+
56
+ /**
57
+ * Build the prompt for this role. MUST be overridden.
58
+ * @param {*} input - raw input from pipeline
59
+ * @returns {Promise<{prompt: string, onOutput?: Function}>|{prompt: string, onOutput?: Function}|string}
60
+ */
61
+ async buildPrompt(_input) {
62
+ throw new Error(`${this.name}: buildPrompt() not implemented`);
63
+ }
64
+
65
+ /**
66
+ * Parse raw LLM output into structured data. Return null to trigger handleParseNull.
67
+ * @param {string} raw - raw agent output
68
+ * @returns {*} parsed result
69
+ */
70
+ parseOutput(raw) { return raw; }
71
+
72
+ /**
73
+ * Build the result object for a successful parse.
74
+ * @param {*} parsed - output of parseOutput
75
+ * @param {string} provider
76
+ * @param {object} agentResult - full agent result (for accessing .usage, .output, etc.)
77
+ * @returns {object} result to include in { ok, result, summary, usage }
78
+ */
79
+ buildSuccessResult(parsed, provider, _agentResult) { return { ...parsed, provider }; }
80
+
81
+ /** Build human-readable summary from parsed output. */
82
+ buildSummary(_parsed) { return `${this.name} completed`; }
83
+
84
+ /** Determine ok status from parsed result. Default: true. Override for verdict-based roles. */
85
+ isSuccessful(_parsed) { return true; }
86
+
87
+ /** Handle null parse (no JSON found). Default returns error. Override for lenient roles. */
88
+ handleParseNull(agentResult, provider) {
89
+ return {
90
+ ok: false,
91
+ result: { error: `Failed to parse ${this.name} output: no JSON found`, provider },
92
+ summary: `${this.name} output parse error: no JSON found`,
93
+ usage: agentResult.usage
94
+ };
95
+ }
96
+
97
+ /** Handle parse exception. Default returns error. Override for lenient roles. */
98
+ handleParseError(err, agentResult, provider) {
99
+ return {
100
+ ok: false,
101
+ result: { error: `Failed to parse ${this.name} output: ${err.message}`, provider },
102
+ summary: `${this.name} output parse error: ${err.message}`,
103
+ usage: agentResult.usage
104
+ };
105
+ }
106
+
107
+ /**
108
+ * Extract input fields from raw input. Default handles string-or-object.
109
+ * Override for roles needing extra fields.
110
+ */
111
+ extractInput(input) {
112
+ if (typeof input === "string") return { task: input, onOutput: null };
113
+ return {
114
+ task: input?.task || this.context?.task || "",
115
+ onOutput: input?.onOutput || null,
116
+ ...input
117
+ };
118
+ }
119
+
120
+ async execute(input) {
121
+ const extracted = this.extractInput(input);
122
+ const provider = this.resolveProvider();
123
+ const agent = this.createAgentInstance(provider);
124
+
125
+ const promptResult = await this.buildPrompt(extracted);
126
+ const { prompt, onOutput, stablePrompt, volatilePrompt } = typeof promptResult === "string"
127
+ ? { prompt: promptResult, onOutput: extracted.onOutput }
128
+ : { onOutput: extracted.onOutput, ...promptResult };
129
+
130
+ const runArgs = { prompt, role: this.name };
131
+ if (onOutput) runArgs.onOutput = onOutput;
132
+ // PAR-H (KJC-TSK-0631): per-call env additions for the agent
133
+ // subprocess (lane slot vars). Agents that ignore task.env simply
134
+ // run without them.
135
+ if (extracted.env) runArgs.env = extracted.env;
136
+ // Φ1-D (KJC-PCS-0057): roles that build their prompt as a
137
+ // stable/volatile layout forward both buckets so cache-aware agents
138
+ // (ClaudeAgent) can ship the stable block as system prompt. Agents
139
+ // that don't know the fields keep reading `prompt` as before.
140
+ if (stablePrompt && volatilePrompt) {
141
+ runArgs.stablePrompt = stablePrompt;
142
+ runArgs.volatilePrompt = volatilePrompt;
143
+ }
144
+ // Kill a hung agent: without a silence timeout a coder / reviewer
145
+ // that stalls with no output left `kj run` waiting indefinitely.
146
+ // PlannerRole set this itself; every other role was unprotected.
147
+ const silenceTimeoutMs = resolveAgentSilenceTimeoutMs(this.config);
148
+ if (silenceTimeoutMs) runArgs.silenceTimeoutMs = silenceTimeoutMs;
149
+
150
+ // KJC-TSK-0413 step B: TODAS las roles que heredan de AgentRole pasan
151
+ // por Brain Recovery aquí — cubre AuditRole, SecurityRole, HuReviewerRole,
152
+ // CoderRole, ArchitectRole, DiscoverRole, ImpeccableRole, RefactorerRole,
153
+ // ResearcherRole, TriageRole, etc. Roles con agentMethod custom (ej.
154
+ // reviewer usa reviewTask) también funcionan: el wrapper sólo necesita
155
+ // un objeto con la firma `runTask(args) → { ok, output, error, exitCode }`.
156
+ const result = await withBrainRecovery({
157
+ agent: { runTask: (args) => agent[this.agentMethod](args), provider },
158
+ taskArgs: runArgs,
159
+ role: this.name,
160
+ provider,
161
+ emitter: this.emitter,
162
+ logger: this.logger,
163
+ // KJC: sessionState lets withBrainRecovery persist a hibernating run
164
+ // to ~/.kj/standby/ so it can be resumed. Without it a quota-exhausted
165
+ // run had nothing to resume from. Caller (coder-stage etc.) passes it
166
+ // through the input object; extractInput forwards it via `...input`.
167
+ sessionState: extracted.sessionState || this.context?.sessionState || null,
168
+ });
169
+
170
+ if (!result.ok) {
171
+ const recoveryNote = result.recovery?.class ? ` [${result.recovery.class}]` : "";
172
+ return {
173
+ ok: false,
174
+ result: { error: result.error || result.output || `${this.name} failed${recoveryNote}`, provider, recovery: result.recovery, action: result.action, standbyFile: result.standbyFile || null },
175
+ summary: `${this.name} failed${recoveryNote}: ${result.recovery?.message || result.error || "unknown error"}`,
176
+ usage: result.usage
177
+ };
178
+ }
179
+
180
+ try {
181
+ const parsed = this.parseOutput(result.output);
182
+ if (parsed === null) return this.handleParseNull(result, provider);
183
+
184
+ return {
185
+ ok: this.isSuccessful(parsed),
186
+ result: this.buildSuccessResult(parsed, provider, result),
187
+ summary: this.buildSummary(parsed),
188
+ usage: result.usage
189
+ };
190
+ } catch (err) {
191
+ return this.handleParseError(err, result, provider);
192
+ }
193
+ }
194
+ }
@@ -0,0 +1,77 @@
1
+ import { AgentRole } from "./agent-role.js";
2
+ import { buildArchitectPrompt, parseArchitectOutput } from "../prompts/architect.js";
3
+
4
+ const EMPTY_ARCHITECTURE = {
5
+ type: "", layers: [], patterns: [], dataModel: { entities: [] },
6
+ apiContracts: [], dependencies: [], tradeoffs: []
7
+ };
8
+
9
+ export class ArchitectRole extends AgentRole {
10
+ constructor(opts) {
11
+ super({ ...opts, name: "architect" });
12
+ }
13
+
14
+ extractInput(input) {
15
+ if (typeof input === "string") return { task: input, researchContext: null, onOutput: null };
16
+ return {
17
+ task: input?.task || this.context?.task || "",
18
+ researchContext: input?.researchContext || null,
19
+ onOutput: input?.onOutput || null
20
+ };
21
+ }
22
+
23
+ async buildPrompt({ task, researchContext }) {
24
+ const prompt = await buildArchitectPrompt({
25
+ task, instructions: this.instructions, researchContext,
26
+ productContext: this.config?.productContext || null,
27
+ domainContext: this.config?.domainContext || null,
28
+ projectDir: this.config?.projectDir || null,
29
+ provider: this._resolvedProvider || (typeof this.resolveProvider === "function" ? this.resolveProvider() : null)
30
+ });
31
+ return { prompt };
32
+ }
33
+
34
+ parseOutput(raw) { return parseArchitectOutput(raw); }
35
+
36
+ buildSuccessResult(parsed, provider) {
37
+ return {
38
+ verdict: parsed.verdict,
39
+ architecture: parsed.architecture,
40
+ questions: parsed.questions,
41
+ provider
42
+ };
43
+ }
44
+
45
+ buildSummary(parsed) {
46
+ const parts = [];
47
+ const arch = parsed.architecture;
48
+ if (arch.type) parts.push(arch.type);
49
+ const layers = arch.layers?.length || 0;
50
+ if (layers) parts.push(`${layers} layer${layers === 1 ? "" : "s"}`);
51
+ const patterns = arch.patterns?.length || 0;
52
+ if (patterns) parts.push(`${patterns} pattern${patterns === 1 ? "" : "s"}`);
53
+ const entities = arch.dataModel?.entities?.length || 0;
54
+ if (entities) parts.push(`${entities} entit${entities === 1 ? "y" : "ies"}`);
55
+ const questions = parsed.questions?.length || 0;
56
+ if (questions) parts.push(`${questions} question${questions === 1 ? "" : "s"}`);
57
+ return parts.length
58
+ ? `Architecture complete: ${parts.join(", ")} (verdict: ${parsed.verdict})`
59
+ : "Architecture complete";
60
+ }
61
+
62
+ handleParseNull(agentResult, provider) {
63
+ return {
64
+ ok: true,
65
+ result: {
66
+ verdict: "needs_clarification", architecture: { ...EMPTY_ARCHITECTURE },
67
+ questions: [], raw: agentResult.output, provider
68
+ },
69
+ summary: "Architecture complete (unstructured output)",
70
+ usage: agentResult.usage
71
+ };
72
+ }
73
+
74
+ handleParseError(_err, agentResult, provider) {
75
+ return this.handleParseNull(agentResult, provider);
76
+ }
77
+ }
@@ -0,0 +1,258 @@
1
+ import { AgentRole } from "./agent-role.js";
2
+ import { buildAuditPrompt, parseAuditOutput, AUDIT_DIMENSIONS } from "../prompts/audit.js";
3
+ import { measureBasalCost, loadPreviousAudit, saveAuditSnapshot, computeGrowthDelta } from "../audit/basal-cost.js";
4
+ import { detectProjectStack } from "../utils/stack-detect.js";
5
+ import { collectSonarFindings } from "../audit/sonar-findings.js";
6
+ import { collectWebPerfInput } from "../audit/webperf-input.js";
7
+ import { collectOsvFindings } from "../audit/osv-findings.js";
8
+ import { collectSemgrepFindings } from "../audit/semgrep-findings.js";
9
+ import { collectInfraFindings } from "../audit/infra-findings.js";
10
+ import { collectCircularDeps } from "../audit/circular-deps.js";
11
+ import { collectDeadExports } from "../audit/dead-exports.js";
12
+ import { collectInjectionFindings } from "../audit/injection-findings.js";
13
+ import { collectAiSlop } from "../audit/ai-slop-findings.js";
14
+
15
+ function parseDimensions(dimensionsStr) {
16
+ if (!dimensionsStr || dimensionsStr === "all") return null;
17
+ const requested = dimensionsStr.split(",").map(d => d.trim().toLowerCase());
18
+ const mapped = requested.map(d => d === "quality" ? "codeQuality" : d);
19
+ const valid = mapped.filter(d => AUDIT_DIMENSIONS.includes(d));
20
+ return valid.length > 0 ? valid : null;
21
+ }
22
+
23
+ function buildSummary(parsed) {
24
+ const { summary } = parsed;
25
+ const parts = [];
26
+ if (summary.critical > 0) parts.push(`${summary.critical} critical`);
27
+ if (summary.high > 0) parts.push(`${summary.high} high`);
28
+ if (summary.medium > 0) parts.push(`${summary.medium} medium`);
29
+ if (summary.low > 0) parts.push(`${summary.low} low`);
30
+ const findingsStr = parts.length > 0 ? parts.join(", ") : "no issues";
31
+ return `Overall health: ${summary.overallHealth}. ${summary.totalFindings} findings (${findingsStr})`;
32
+ }
33
+
34
+ export class AuditRole extends AgentRole {
35
+ constructor(opts) {
36
+ super({ ...opts, name: "audit" });
37
+ }
38
+
39
+ /**
40
+ * Run the deterministic side of an audit — basalCost, stack detection,
41
+ * Sonar issues, WebPerf input — without invoking the LLM. Cheap (no
42
+ * tokens, no API call). KJC-TSK-0364 splits the original execute() into
43
+ * this collector + executeWithDeterministic() so the CLI can show
44
+ * deterministic findings first and ask the user before paying for the
45
+ * LLM phase.
46
+ *
47
+ * @param {object|string} input - same shape execute() accepts
48
+ * @returns {Promise<object>} deterministic context (the same data
49
+ * execute() would gather internally)
50
+ */
51
+ async collectDeterministic(input) {
52
+ // KJC-TSK-0695: securityOnly = the focused pass an agent self-invokes
53
+ // when a task touches sensitive surface. Only the security collectors
54
+ // run (sonar, osv, semgrep, injection) — zero tokens, fast.
55
+ const securityOnly = typeof input === "object" ? Boolean(input?.securityOnly) : false;
56
+ const noSonar = typeof input === "object" ? Boolean(input?.noSonar) : false;
57
+ const noOsv = typeof input === "object" ? Boolean(input?.noOsv) : false;
58
+ const noSemgrep = typeof input === "object" ? Boolean(input?.noSemgrep) : false;
59
+ const noMadge = (typeof input === "object" ? Boolean(input?.noMadge) : false) || securityOnly;
60
+ const noKnip = (typeof input === "object" ? Boolean(input?.noKnip) : false) || securityOnly;
61
+ const noInjectionScan = typeof input === "object" ? Boolean(input?.noInjectionScan) : false;
62
+ const noInfraScan = typeof input === "object" ? Boolean(input?.noInfraScan) : false;
63
+ const noAiSlop = (typeof input === "object" ? Boolean(input?.noAiSlop) : false) || securityOnly;
64
+ const projectDir = this.config?.projectDir || process.cwd();
65
+ let basalCost = null;
66
+ let growthDelta = null;
67
+ let stack = null;
68
+ let sonarFindings = null;
69
+ let webperf = null;
70
+ let osvFindings = null;
71
+ let semgrepFindings = null;
72
+ let circularDeps = null;
73
+ let deadExports = null;
74
+ let injectionFindings = null;
75
+ let infraFindings = null;
76
+ let aiSlop = null;
77
+ if (!securityOnly) {
78
+ try {
79
+ basalCost = await measureBasalCost(projectDir);
80
+ const previous = await loadPreviousAudit(projectDir);
81
+ growthDelta = computeGrowthDelta(basalCost, previous);
82
+ } catch { /* basal cost is best-effort */ }
83
+ }
84
+ try {
85
+ stack = await detectProjectStack(projectDir);
86
+ } catch { /* stack detect is best-effort */ }
87
+ if (!noSonar) {
88
+ try {
89
+ sonarFindings = await collectSonarFindings(this.config, this.logger);
90
+ } catch { /* sonar fetch is best-effort */ }
91
+ }
92
+ if (!securityOnly) {
93
+ try {
94
+ webperf = collectWebPerfInput(stack, this.config);
95
+ } catch { /* webperf input is best-effort */ }
96
+ }
97
+ // OSV vulnerabilities — KJC-TSK-0365. Best-effort: missing
98
+ // osv-scanner binary returns available:false and the audit
99
+ // continues without the section.
100
+ if (!noOsv) {
101
+ try {
102
+ osvFindings = await collectOsvFindings(projectDir, this.logger);
103
+ } catch { /* osv-scanner is best-effort */ }
104
+ }
105
+ // Semgrep SAST — KJC-TSK-0366. Best-effort: missing semgrep binary
106
+ // returns available:false and the audit continues without the section.
107
+ if (!noSemgrep) {
108
+ try {
109
+ semgrepFindings = await collectSemgrepFindings(projectDir, this.logger);
110
+ } catch { /* semgrep is best-effort */ }
111
+ }
112
+ // Madge circular-import deps — KJC-TSK v2.17. Stack-aware: skipped for
113
+ // non-JS/TS projects. Best-effort: any failure returns available:false
114
+ // and the audit continues without the section.
115
+ if (!noMadge) {
116
+ try {
117
+ circularDeps = await collectCircularDeps(projectDir, stack, this.config, this.logger);
118
+ } catch { /* madge is best-effort */ }
119
+ }
120
+ // Knip dead-exports — KJC-TSK v2.17. Stack-aware, JS/TS only, needs
121
+ // package.json. Best-effort: any failure returns available:false and
122
+ // the audit continues without the section.
123
+ if (!noKnip) {
124
+ try {
125
+ deadExports = await collectDeadExports(projectDir, stack, this.config, this.logger);
126
+ } catch { /* knip is best-effort */ }
127
+ }
128
+ if (!noInjectionScan) {
129
+ try {
130
+ injectionFindings = await collectInjectionFindings(projectDir, this.logger);
131
+ } catch { /* injection scan is best-effort (KJC-TSK-0498) */ }
132
+ }
133
+ // Checkov infra misconfigs — INF-C (KJC-TSK-0760). Security surface, so
134
+ // it ALSO runs in securityOnly. Best-effort: no infra markers or missing
135
+ // checkov return available:false declaring why.
136
+ if (!noInfraScan) {
137
+ try {
138
+ infraFindings = await collectInfraFindings(projectDir, this.logger);
139
+ } catch { /* checkov is best-effort */ }
140
+ }
141
+ // AI-slop tells (KJC-TSK-0503). Deterministic, offline, regex-based.
142
+ // Local equivalent of Deslopify-style review. Best-effort.
143
+ if (!noAiSlop) {
144
+ try {
145
+ aiSlop = await collectAiSlop(projectDir);
146
+ } catch { /* ai-slop scan is best-effort */ }
147
+ }
148
+ return { projectDir, basalCost, growthDelta, stack, sonarFindings, webperf, osvFindings, semgrepFindings, circularDeps, deadExports, injectionFindings, infraFindings, aiSlop };
149
+ }
150
+
151
+ /**
152
+ * Run the LLM phase of an audit using a pre-collected deterministic
153
+ * context. KJC-TSK-0364: separated from execute() so the CLI can run
154
+ * collectDeterministic() first, show findings, prompt, and only then
155
+ * call this if the user wants to spend tokens.
156
+ */
157
+ async executeWithDeterministic(input, deterministicCtx) {
158
+ const task = typeof input === "string" ? input : input?.task || this.context?.task || "";
159
+ const onOutput = typeof input === "string" ? null : input?.onOutput || null;
160
+ const rawDimensions = typeof input === "object" ? input?.dimensions || null : null;
161
+ const context = typeof input === "object" ? input?.context || null : null;
162
+ const dimensions = typeof rawDimensions === "string" ? parseDimensions(rawDimensions) : rawDimensions;
163
+
164
+ const { projectDir, basalCost, growthDelta, stack, sonarFindings, webperf, osvFindings, semgrepFindings, circularDeps, deadExports } = deterministicCtx;
165
+
166
+ const provider = this.resolveProvider();
167
+ const agent = this.createAgentInstance(provider);
168
+ const prompt = buildAuditPrompt({ task, instructions: this.instructions, dimensions, context, basalCost, growthDelta, stack, sonarFindings, webperf, osvFindings, semgrepFindings, circularDeps, deadExports });
169
+ const runArgs = { prompt, role: "audit" };
170
+ if (onOutput) runArgs.onOutput = onOutput;
171
+ const startedAt = Date.now();
172
+ const result = await agent.runTask(runArgs);
173
+ const durationMs = Date.now() - startedAt;
174
+
175
+ const usage = extractUsage(result, { provider, durationMs });
176
+
177
+ if (!result.ok) {
178
+ return { ok: false, result: { error: result.error || result.output || "Audit failed", provider }, summary: `Audit failed: ${result.error || "unknown error"}`, usage };
179
+ }
180
+
181
+ try {
182
+ const parsed = parseAuditOutput(result.output);
183
+ if (!parsed) {
184
+ return { ok: true, result: { raw: result.output, provider }, summary: "Audit complete (unstructured output)", usage };
185
+ }
186
+ if (basalCost) { try { await saveAuditSnapshot(projectDir, basalCost); } catch { /* best-effort */ } }
187
+
188
+ return {
189
+ ok: true,
190
+ result: {
191
+ summary: parsed.summary, dimensions: parsed.dimensions,
192
+ topRecommendations: parsed.topRecommendations,
193
+ textSummary: parsed.textSummary || undefined,
194
+ basalCost: basalCost || undefined, growthDelta: growthDelta || undefined,
195
+ stack: stack || undefined,
196
+ sonarFindings: sonarFindings?.available ? sonarFindings : undefined,
197
+ webperf: webperf?.available ? webperf : undefined,
198
+ osvFindings: osvFindings?.available ? osvFindings : undefined,
199
+ semgrepFindings: semgrepFindings?.available ? semgrepFindings : undefined,
200
+ circularDeps: circularDeps?.available ? circularDeps : undefined,
201
+ deadExports: deadExports?.available ? deadExports : undefined,
202
+ provider
203
+ },
204
+ summary: buildSummary(parsed),
205
+ usage
206
+ };
207
+ } catch {
208
+ return { ok: true, result: { raw: result.output, provider }, summary: "Audit complete (unstructured output)", usage };
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Convenience: run both phases in one call. Identical to the previous
214
+ * (single-phase) execute() so MCP and pipeline callers don't change.
215
+ */
216
+ async execute(input) {
217
+ const deterministicCtx = await this.collectDeterministic(input);
218
+ return this.executeWithDeterministic(input, deterministicCtx);
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Consolidate the usage telemetry the agent spreads across its top-level
224
+ * result fields. Returns a single object so consumers (CLI formatter,
225
+ * report-file writer, MCP) don't need to know about the spread shape.
226
+ *
227
+ * Consumed by tests via vi.importActual (knip blind spot) — keep exported.
228
+ * @public
229
+ * @param {object} result - raw agent.runTask() return
230
+ * @param {{provider: string, durationMs: number}} ctx
231
+ * @returns {{available: boolean, provider: string, model?: string, tokens_in?: number, tokens_out?: number, cached_tokens?: number, total_tokens?: number, cost_usd?: number, durationMs: number, reason?: string}}
232
+ */
233
+ export function extractUsage(result, { provider, durationMs }) {
234
+ if (!result || typeof result !== "object") {
235
+ return { available: false, provider, durationMs, reason: "no agent result" };
236
+ }
237
+ const tokens_in = Number.isFinite(result.tokens_in) ? result.tokens_in : null;
238
+ const tokens_out = Number.isFinite(result.tokens_out) ? result.tokens_out : null;
239
+ if (tokens_in === null && tokens_out === null) {
240
+ return { available: false, provider, model: result.model, durationMs, reason: `provider "${provider}" did not report token usage` };
241
+ }
242
+ // KJC-BUG-0085: cached_tokens must survive this consolidation — the
243
+ // budget chain (computeUsage → BudgetTracker → summary.md Cache hits →
244
+ // telemetry) reads it from `usage` and the audit is the most expensive
245
+ // role of the session.
246
+ const cached_tokens = Number.isFinite(result.cached_tokens) ? result.cached_tokens : 0;
247
+ return {
248
+ available: true,
249
+ provider,
250
+ model: result.model,
251
+ tokens_in: tokens_in ?? 0,
252
+ tokens_out: tokens_out ?? 0,
253
+ cached_tokens,
254
+ total_tokens: (tokens_in ?? 0) + (tokens_out ?? 0),
255
+ cost_usd: typeof result.cost_usd === "number" ? result.cost_usd : null,
256
+ durationMs,
257
+ };
258
+ }