akm-cli 0.9.17-alpha.3 → 0.9.17-alpha.5

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 (346) hide show
  1. package/CHANGELOG.md +760 -0
  2. package/dist/akm +94 -196
  3. package/dist/cli/shared.js +6 -2
  4. package/dist/cli.js +22 -9
  5. package/dist/commands/agent/agent-dispatch.js +1 -1
  6. package/dist/commands/command/command-execution.js +24 -62
  7. package/dist/commands/feedback-cli.js +0 -1
  8. package/dist/commands/health/accept-rate.js +2 -2
  9. package/dist/commands/health/checks.js +30 -75
  10. package/dist/commands/health/config-skew.js +38 -0
  11. package/dist/commands/health/egress.js +54 -0
  12. package/dist/commands/health/html-report.js +0 -38
  13. package/dist/commands/health/improve-metrics.js +123 -562
  14. package/dist/commands/health/plugin-staleness.js +53 -3
  15. package/dist/commands/health/renderers.js +12 -4
  16. package/dist/commands/health/report-view-model.js +11 -106
  17. package/dist/commands/health/types-improve.js +4 -19
  18. package/dist/commands/health/windows.js +64 -73
  19. package/dist/commands/health.js +122 -143
  20. package/dist/commands/improve/consolidate/chunking.js +25 -100
  21. package/dist/commands/improve/consolidate/sanitize.js +54 -149
  22. package/dist/commands/improve/consolidate.js +538 -1075
  23. package/dist/commands/improve/content-hash.js +16 -24
  24. package/dist/commands/improve/distill/content-repair.js +18 -100
  25. package/dist/commands/improve/distill-guards.js +20 -81
  26. package/dist/commands/improve/distill-promotion-policy.js +23 -243
  27. package/dist/commands/improve/distill.js +608 -1075
  28. package/dist/commands/improve/eligibility.js +126 -400
  29. package/dist/commands/improve/execution.js +3 -5
  30. package/dist/commands/improve/extract.js +487 -1046
  31. package/dist/commands/improve/feedback-valence.js +0 -25
  32. package/dist/commands/improve/improve-cli.js +29 -166
  33. package/dist/commands/improve/improve-result-file.js +10 -66
  34. package/dist/commands/improve/improve-strategies.js +12 -7
  35. package/dist/commands/improve/improve-usage-report.js +18 -64
  36. package/dist/commands/improve/improve.js +443 -1063
  37. package/dist/commands/improve/ledger.js +114 -0
  38. package/dist/commands/improve/locks.js +2 -8
  39. package/dist/commands/improve/loop-stages.js +459 -1172
  40. package/dist/commands/improve/memory/derived-ref.js +12 -77
  41. package/dist/commands/improve/memory/memory-belief.js +14 -118
  42. package/dist/commands/improve/memory/memory-improve.js +4 -3
  43. package/dist/commands/improve/outcome-loop.js +28 -156
  44. package/dist/commands/improve/planner.js +5 -10
  45. package/dist/commands/improve/preparation.js +851 -2339
  46. package/dist/commands/improve/proactive-maintenance.js +34 -101
  47. package/dist/commands/improve/reflect-noise.js +104 -280
  48. package/dist/commands/improve/reflect.js +621 -1367
  49. package/dist/commands/improve/salience.js +46 -232
  50. package/dist/commands/improve/session-asset.js +19 -100
  51. package/dist/commands/improve/stage.js +323 -0
  52. package/dist/commands/lint/base-linter.js +19 -5
  53. package/dist/commands/proposal/drain.js +251 -644
  54. package/dist/commands/proposal/proposal-cli.js +3 -18
  55. package/dist/commands/proposal/proposal-types.js +20 -41
  56. package/dist/commands/proposal/proposal.js +1 -2
  57. package/dist/commands/proposal/propose.js +134 -160
  58. package/dist/commands/proposal/repository.js +502 -1487
  59. package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
  60. package/dist/commands/proposal/validators/proposal-validators.js +1 -1
  61. package/dist/commands/proposal/validators/proposals.js +13 -89
  62. package/dist/commands/read/curate.js +63 -413
  63. package/dist/commands/read/search-cli.js +16 -33
  64. package/dist/commands/read/search.js +17 -23
  65. package/dist/commands/read/show.js +2 -13
  66. package/dist/commands/sources/bundle-cli.js +25 -2
  67. package/dist/commands/sources/bundle-config-ops.js +4 -0
  68. package/dist/commands/sources/dangerous-env-audit.js +1 -2
  69. package/dist/commands/sources/info.js +2 -11
  70. package/dist/commands/sources/installed-stashes.js +197 -746
  71. package/dist/commands/sources/schema-repair.js +98 -129
  72. package/dist/commands/sources/source-add.js +62 -12
  73. package/dist/commands/sources/source-manage.js +9 -2
  74. package/dist/commands/sources/stash-cli.js +1 -1
  75. package/dist/commands/tasks/explain.js +10 -13
  76. package/dist/commands/tasks/tasks-cli.js +9 -8
  77. package/dist/commands/tasks/tasks.js +326 -930
  78. package/dist/commands/tasks/validate.js +42 -21
  79. package/dist/commands/workflow/plan.js +22 -29
  80. package/dist/commands/workflow-cli.js +4 -4
  81. package/dist/core/adapter/adapters/akm-adapter.js +0 -1
  82. package/dist/core/adapter/adapters/akm-lint.js +2 -3
  83. package/dist/core/adapter/adapters/akm-metadata.js +11 -12
  84. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  85. package/dist/core/adapter/execution-source.js +17 -29
  86. package/dist/core/asset/asset-placement.js +4 -13
  87. package/dist/core/asset/resolve-ref.js +1 -1
  88. package/dist/core/bundle-id.js +42 -5
  89. package/dist/core/bundle-rename.js +291 -0
  90. package/dist/core/config/config-io.js +1 -2
  91. package/dist/core/config/config-schema.js +1 -33
  92. package/dist/core/config/config-walker.js +1 -1
  93. package/dist/core/config/config.js +163 -68
  94. package/dist/core/config/legacy-source-shape-shim.js +38 -9
  95. package/dist/core/config/schema/embedding.js +20 -5
  96. package/dist/core/config/schema/engines.js +5 -0
  97. package/dist/core/config/schema/execution.js +1 -1
  98. package/dist/core/config/schema/experimental.js +1 -1
  99. package/dist/core/config/schema/improve-processes.js +21 -95
  100. package/dist/core/config/schema/improve.js +4 -42
  101. package/dist/core/config/schema/scheduler.js +12 -12
  102. package/dist/core/config/schema/search.js +6 -22
  103. package/dist/core/env-secret-ref.js +0 -1
  104. package/dist/core/errors.js +8 -9
  105. package/dist/core/file-lock.js +76 -173
  106. package/dist/core/logs-db.js +2 -2
  107. package/dist/core/paths.js +0 -27
  108. package/dist/core/redaction.js +109 -2
  109. package/dist/core/run-lock.js +2 -5
  110. package/dist/core/spawn-env.js +1 -1
  111. package/dist/core/state/migrations.js +108 -61
  112. package/dist/core/state-db-scope.js +2 -4
  113. package/dist/core/state-db.js +126 -692
  114. package/dist/core/type-presentation.js +1 -9
  115. package/dist/core/write-source.js +293 -1012
  116. package/dist/execution/input-contract.js +1 -1
  117. package/dist/execution/resolved-request.js +135 -689
  118. package/dist/execution/source.js +63 -257
  119. package/dist/execution/target-ref.js +1 -1
  120. package/dist/indexer/bundle-identity-guard.js +2 -2
  121. package/dist/indexer/db/graph-db.js +106 -46
  122. package/dist/indexer/ensure-index.js +44 -85
  123. package/dist/indexer/graph/graph-extraction.js +340 -562
  124. package/dist/indexer/graph/graph-related.js +130 -0
  125. package/dist/indexer/index-rebuild-lock.js +3 -11
  126. package/dist/indexer/index-writer-lock.js +8 -17
  127. package/dist/indexer/index-written-assets.js +139 -151
  128. package/dist/indexer/indexer.js +524 -846
  129. package/dist/indexer/materialize-embeddings.js +60 -397
  130. package/dist/indexer/passes/memory-inference.js +81 -90
  131. package/dist/indexer/passes/metadata.js +132 -200
  132. package/dist/indexer/read-preflight.js +0 -7
  133. package/dist/indexer/scan/doc-to-entry.js +1 -3
  134. package/dist/indexer/scan/drain-dir.js +1 -1
  135. package/dist/indexer/search/db-search.js +181 -590
  136. package/dist/indexer/search/fts-query.js +30 -41
  137. package/dist/indexer/search/ranking.js +28 -154
  138. package/dist/indexer/search/search-attribution.js +12 -32
  139. package/dist/indexer/search/search-fields.js +11 -15
  140. package/dist/indexer/search/search-hit-enrichers.js +54 -85
  141. package/dist/indexer/search/search-source.js +1 -4
  142. package/dist/indexer/usage/usage-events.js +2 -7
  143. package/dist/integrations/agent/engine-fallback.js +23 -40
  144. package/dist/integrations/agent/engine-resolution.js +93 -183
  145. package/dist/integrations/agent/execution.js +507 -0
  146. package/dist/integrations/agent/model-map.js +28 -156
  147. package/dist/integrations/agent/request-lowering.js +66 -141
  148. package/dist/integrations/agent/runner-dispatch.js +143 -321
  149. package/dist/integrations/agent/runner.js +54 -14
  150. package/dist/integrations/lockfile.js +53 -101
  151. package/dist/llm/embedders/deterministic.js +2 -3
  152. package/dist/llm/embedders/profile.js +71 -0
  153. package/dist/llm/embedders/remote.js +10 -15
  154. package/dist/llm/graph-extract.js +3 -12
  155. package/dist/llm/index-passes.js +3 -5
  156. package/dist/llm/memory-infer.js +1 -2
  157. package/dist/llm/metadata-enhance.js +1 -2
  158. package/dist/llm/structured-call.js +5 -24
  159. package/dist/output/generic-render.js +23 -11
  160. package/dist/output/html-render.js +13 -10
  161. package/dist/output/render-registry.js +3 -32
  162. package/dist/output/shapes/helpers.js +2 -34
  163. package/dist/output/shapes/passthrough.js +1 -9
  164. package/dist/{indexer/search/ranking-types.js → output/text/bundle-rename.js} +4 -1
  165. package/dist/output/text/command-format.js +60 -23
  166. package/dist/output/text/helpers.js +1 -1
  167. package/dist/output/text/migrate.js +5 -14
  168. package/dist/output/text/proposal-format.js +1 -2
  169. package/dist/output/text/workflow-format.js +0 -32
  170. package/dist/output/text.js +2 -0
  171. package/dist/registry/factory.js +4 -19
  172. package/dist/registry/network.js +66 -220
  173. package/dist/registry/providers/index.js +0 -2
  174. package/dist/registry/providers/skills-sh.js +3 -14
  175. package/dist/registry/providers/static-index.js +24 -26
  176. package/dist/registry/resolve.js +55 -131
  177. package/dist/scripts/akm-migrate-node.js +43940 -93320
  178. package/dist/scripts/akm-migrate.js +43700 -93078
  179. package/dist/setup/registry-stash-loader.js +4 -13
  180. package/dist/setup/semantic-assets.js +3 -44
  181. package/dist/setup/setup.js +1 -1
  182. package/dist/setup/steps/tasks.js +25 -15
  183. package/dist/sources/provider-factory.js +17 -18
  184. package/dist/sources/providers/filesystem.js +2 -3
  185. package/dist/sources/providers/git-install.js +7 -1
  186. package/dist/sources/providers/git-provider.js +0 -3
  187. package/dist/sources/providers/git-stash.js +0 -17
  188. package/dist/sources/providers/npm.js +2 -4
  189. package/dist/sources/providers/provider-utils.js +5 -10
  190. package/dist/sources/providers/website.js +0 -2
  191. package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
  192. package/dist/sources/website-url.js +2 -2
  193. package/dist/storage/database.js +9 -35
  194. package/dist/storage/repositories/improve-ledger-repository.js +168 -0
  195. package/dist/storage/repositories/index-connection.js +34 -70
  196. package/dist/storage/repositories/index-entries-repository.js +69 -111
  197. package/dist/storage/repositories/index-entry-mapper.js +1 -2
  198. package/dist/storage/repositories/index-entry-schema.js +83 -269
  199. package/dist/storage/repositories/index-fts-repository.js +86 -256
  200. package/dist/storage/repositories/index-llm-cache-repository.js +17 -0
  201. package/dist/storage/repositories/index-meta-repository.js +6 -4
  202. package/dist/storage/repositories/index-schema.js +192 -220
  203. package/dist/storage/repositories/index-utility-repository.js +8 -29
  204. package/dist/storage/repositories/index-vec-repository.js +133 -414
  205. package/dist/storage/repositories/outcome-repository.js +2 -1
  206. package/dist/storage/repositories/proposals-repository.js +35 -0
  207. package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
  208. package/dist/storage/repositories/task-history-repository.js +26 -4
  209. package/dist/storage/repositories/workflow-runs-repository.js +53 -244
  210. package/dist/storage/sqlite-migrations.js +136 -0
  211. package/dist/storage/sqlite-pragmas.js +11 -9
  212. package/dist/storage/sqlite-transaction.js +170 -0
  213. package/dist/storage/state-db-integrity.js +34 -27
  214. package/dist/tasks/activation-config.js +134 -62
  215. package/dist/tasks/backends/cron.js +129 -277
  216. package/dist/tasks/backends/exec-utils.js +2 -5
  217. package/dist/tasks/backends/launchd.js +125 -745
  218. package/dist/tasks/backends/schtasks.js +101 -620
  219. package/dist/tasks/prepare/prepare-support.js +5 -15
  220. package/dist/tasks/prepare/prepare.js +0 -2
  221. package/dist/tasks/resolve-akm-bin.js +20 -79
  222. package/dist/tasks/run/attempt-lifecycle.js +0 -1
  223. package/dist/tasks/scheduler-binding.js +18 -238
  224. package/dist/tasks/scheduler-invocation.js +52 -52
  225. package/dist/tasks/scheduler-lock.js +53 -0
  226. package/dist/tasks/scheduler-sync.js +361 -751
  227. package/dist/tasks/source/parse-task-source.js +160 -10
  228. package/dist/tasks/source/task-source-v3-frozen.js +3 -4
  229. package/dist/tasks/source/task-to-v4.js +2 -2
  230. package/dist/workflows/authoring/authoring.js +3 -12
  231. package/dist/workflows/compile.js +211 -0
  232. package/dist/workflows/concurrency-policy.js +13 -74
  233. package/dist/workflows/exec/child-invocation.js +3 -17
  234. package/dist/workflows/exec/child-workflow.js +32 -141
  235. package/dist/workflows/exec/dispatch-redaction.js +13 -53
  236. package/dist/workflows/exec/environment.js +98 -0
  237. package/dist/workflows/exec/exec-unit.js +33 -140
  238. package/dist/workflows/exec/frozen-judge.js +7 -59
  239. package/dist/workflows/exec/native-executor.js +82 -341
  240. package/dist/workflows/exec/param-secrets.js +29 -47
  241. package/dist/workflows/exec/run-workflow.js +154 -387
  242. package/dist/workflows/exec/scheduler.js +9 -36
  243. package/dist/workflows/exec/step-work.js +127 -430
  244. package/dist/workflows/exec/unit-dispatch.js +11 -63
  245. package/dist/workflows/exec/unit-writer.js +8 -52
  246. package/dist/workflows/exec/worktree.js +39 -273
  247. package/dist/workflows/freeze/child-output-references.js +4 -15
  248. package/dist/workflows/freeze/environment.js +99 -92
  249. package/dist/workflows/freeze/freeze.js +172 -0
  250. package/dist/workflows/freeze/step-values.js +19 -21
  251. package/dist/workflows/freeze/targets/child-workflow.js +23 -92
  252. package/dist/workflows/freeze/targets/command.js +10 -33
  253. package/dist/workflows/freeze/targets/script.js +5 -12
  254. package/dist/workflows/freeze/targets/shell.js +3 -6
  255. package/dist/workflows/freeze/targets/task.js +25 -80
  256. package/dist/workflows/freeze/task-bindings.js +20 -67
  257. package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
  258. package/dist/workflows/ir/params.js +6 -51
  259. package/dist/workflows/ir/plan-hash.js +2 -34
  260. package/dist/workflows/parser.js +140 -43
  261. package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
  262. package/dist/workflows/renderer.js +36 -69
  263. package/dist/workflows/resource-limits.js +12 -120
  264. package/dist/workflows/runtime/agent-identity.js +8 -40
  265. package/dist/workflows/runtime/run-outputs.js +3 -6
  266. package/dist/workflows/runtime/run-plan.js +316 -0
  267. package/dist/workflows/runtime/runs.js +48 -200
  268. package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
  269. package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
  270. package/dist/workflows/validate-summary.js +2 -7
  271. package/docs/integration/bundling-akm.md +49 -42
  272. package/docs/migration/README.md +1 -0
  273. package/docs/migration/release-notes/0.9.17.md +41 -0
  274. package/docs/migration/v0.9.1-to-v0.9.2.md +19 -7
  275. package/docs/reference/cli.md +182 -125
  276. package/docs/reference/configuration.md +49 -56
  277. package/docs/reference/data-and-telemetry.md +19 -20
  278. package/docs/reference/tasks.md +86 -38
  279. package/docs/reference/workflow-schema.md +14 -18
  280. package/docs/reference/workflows.md +6 -9
  281. package/package.json +1 -1
  282. package/schemas/akm-config.json +87 -406
  283. package/dist/commands/health/advisories.js +0 -150
  284. package/dist/commands/health/metrics.js +0 -329
  285. package/dist/commands/health/surfaces.js +0 -102
  286. package/dist/commands/improve/anti-collapse.js +0 -83
  287. package/dist/commands/improve/collapse-detector.js +0 -432
  288. package/dist/commands/improve/consolidate/eligibility.js +0 -48
  289. package/dist/commands/improve/consolidate/merge.js +0 -146
  290. package/dist/commands/improve/distill/promote-memory.js +0 -329
  291. package/dist/commands/improve/distill/quality-gate.js +0 -500
  292. package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
  293. package/dist/commands/improve/proposal-envelope.js +0 -31
  294. package/dist/commands/improve/run-context.js +0 -123
  295. package/dist/commands/improve/shared.js +0 -21
  296. package/dist/commands/improve/source-identity.js +0 -28
  297. package/dist/commands/improve/triage.js +0 -96
  298. package/dist/commands/proposal/drain-policies.js +0 -151
  299. package/dist/commands/sources/update-transaction.js +0 -220
  300. package/dist/core/action-contributors.js +0 -28
  301. package/dist/core/config/config-version-shim.js +0 -101
  302. package/dist/core/config/retired-experimental-keys-shim.js +0 -62
  303. package/dist/core/fs-txn.js +0 -405
  304. package/dist/core/lexical-score.js +0 -25
  305. package/dist/core/maintenance-barrier.js +0 -167
  306. package/dist/execution/executable-identity.js +0 -105
  307. package/dist/execution/guarded-source.js +0 -441
  308. package/dist/indexer/graph/graph-boost.js +0 -427
  309. package/dist/indexer/graph/graph-dedup.js +0 -95
  310. package/dist/indexer/search/name-match.js +0 -35
  311. package/dist/indexer/search/ranking-contributors.js +0 -515
  312. package/dist/indexer/walk/project-context.js +0 -192
  313. package/dist/integrations/agent/execution-cascade.js +0 -566
  314. package/dist/integrations/agent/execution-definitions.js +0 -202
  315. package/dist/integrations/agent/execution-lowering.js +0 -841
  316. package/dist/integrations/agent/execution-preparation.js +0 -98
  317. package/dist/integrations/agent/inline-execution.js +0 -74
  318. package/dist/registry/create-provider-registry.js +0 -29
  319. package/dist/registry/pinned-request-helper.js +0 -247
  320. package/dist/registry/pinned-transport.js +0 -717
  321. package/dist/sources/providers/index.js +0 -14
  322. package/dist/storage/engines/sqlite-migrations.js +0 -271
  323. package/dist/storage/repositories/canaries-repository.js +0 -107
  324. package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
  325. package/dist/storage/repositories/registry-cache.js +0 -113
  326. package/dist/tasks/scheduler-sync-preview.js +0 -52
  327. package/dist/workflows/freeze/resolve-steps.js +0 -86
  328. package/dist/workflows/freeze/source-freeze.js +0 -64
  329. package/dist/workflows/ir/compile.js +0 -321
  330. package/dist/workflows/ir/environment-v4.js +0 -330
  331. package/dist/workflows/ir/freeze-v4.js +0 -153
  332. package/dist/workflows/ir/schema-v4.js +0 -745
  333. package/dist/workflows/ir/schema.js +0 -354
  334. package/dist/workflows/program/schema.js +0 -78
  335. package/dist/workflows/runtime/checkin.js +0 -57
  336. package/dist/workflows/runtime/plan-classifier.js +0 -196
  337. package/dist/workflows/runtime/unit-checkin.js +0 -45
  338. package/dist/workflows/runtime/unit-phases.js +0 -20
  339. package/dist/workflows/schema.js +0 -4
  340. package/dist/workflows/source-ir/compile.js +0 -200
  341. package/dist/workflows/source-ir/program.js +0 -50
  342. package/dist/workflows/source-ir/result.js +0 -26
  343. package/dist/workflows/source-ir/schema.js +0 -786
  344. package/dist/workflows/source-ir/triggers.js +0 -79
  345. package/dist/workflows/source-ir/uses.js +0 -40
  346. package/dist/workflows/validator.js +0 -60
@@ -63,6 +63,46 @@ import { isExactSemver, isSemverRange, maxSatisfying, satisfiesRange } from "../
63
63
  function claudePluginsDir() {
64
64
  return process.env.AKM_CLAUDE_PLUGINS_DIR ?? path.join(os.homedir(), ".claude", "plugins");
65
65
  }
66
+ /**
67
+ * Root of OpenCode's package cache, same override pattern as
68
+ * {@link claudePluginsDir} (`AKM_OPENCODE_CACHE_DIR` for tests).
69
+ * The OpenCode plugin (`akm-opencode`) bundles its own `akm-cli` under
70
+ * `packages/akm-opencode/node_modules/akm-cli` and runs it in-process,
71
+ * sharing the host's databases with every other akm install — its version
72
+ * lag matters the same way a stale Claude plugin's does.
73
+ */
74
+ function opencodeCacheDir() {
75
+ return process.env.AKM_OPENCODE_CACHE_DIR ?? path.join(os.homedir(), ".cache", "opencode");
76
+ }
77
+ /**
78
+ * Read the OpenCode plugin's bundled `akm-cli` version from its
79
+ * `package.json` — never executed, just parsed, mirroring
80
+ * {@link detectInstalledPlugins}'s manifest read. `undefined` when the
81
+ * OpenCode plugin, or its bundled akm-cli, is not installed.
82
+ */
83
+ function detectOpencodeBundledAkmVersion(cacheRoot) {
84
+ const pkgPath = path.join(cacheRoot, "packages", "akm-opencode", "node_modules", "akm-cli", "package.json");
85
+ try {
86
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
87
+ return typeof pkg.version === "string" ? pkg.version : undefined;
88
+ }
89
+ catch {
90
+ return undefined;
91
+ }
92
+ }
93
+ function buildOpencodeBundledAdvisory(bundledVersion, cliVersion) {
94
+ const stale = bundledVersion !== cliVersion;
95
+ return {
96
+ name: "opencode-plugin-version",
97
+ kind: "deterministic",
98
+ status: stale ? "warn" : "pass",
99
+ confidence: "high",
100
+ message: stale
101
+ ? `OpenCode's bundled akm-cli is v${bundledVersion}, but the running CLI is v${cliVersion} — it shares the same databases in-process; update the OpenCode plugin.`
102
+ : `OpenCode's bundled akm-cli matches the running v${cliVersion}.`,
103
+ evidence: { bundledVersion, cliVersion },
104
+ };
105
+ }
66
106
  /**
67
107
  * Scan `<pluginsRoot>/cache/<marketplace>/akm/<version>/` for every
68
108
  * `akm` plugin cache entry, picking the highest cached version per
@@ -165,14 +205,24 @@ const realListRemoteTags = (marketplaceDir) => {
165
205
  return [...new Set(tags)];
166
206
  };
167
207
  /**
168
- * Build one `plugin-version` advisory per detected `akm` harness plugin.
169
- * Returns `[]` when no plugin is installed — the benign, common case.
208
+ * Build one `plugin-version` advisory per detected `akm` harness plugin,
209
+ * plus one `opencode-plugin-version` advisory when the
210
+ * OpenCode plugin's bundled `akm-cli` is found — it shares the host's
211
+ * databases in-process, so its lag matters the same way a stale Claude
212
+ * plugin's does. Returns `[]` when neither is installed — the benign,
213
+ * common case.
170
214
  */
171
215
  export function collectPluginStalenessAdvisories(options) {
172
216
  const pluginsRoot = options.pluginsRoot ?? claudePluginsDir();
173
217
  const listRemoteTags = options.listRemoteTags ?? realListRemoteTags;
174
218
  const plugins = detectInstalledPlugins(pluginsRoot);
175
- return plugins.map((plugin) => buildAdvisory(plugin, options.cliVersion, pluginsRoot, listRemoteTags));
219
+ const results = plugins.map((plugin) => buildAdvisory(plugin, options.cliVersion, pluginsRoot, listRemoteTags));
220
+ const opencodeCacheRoot = options.opencodeCacheRoot ?? opencodeCacheDir();
221
+ const bundledVersion = detectOpencodeBundledAkmVersion(opencodeCacheRoot);
222
+ if (bundledVersion !== undefined) {
223
+ results.push(buildOpencodeBundledAdvisory(bundledVersion, options.cliVersion));
224
+ }
225
+ return results;
176
226
  }
177
227
  function buildAdvisory(plugin, cliVersion, pluginsRoot, listRemoteTags) {
178
228
  const pluginRef = `${plugin.pluginName}@${plugin.marketplace}`;
@@ -2,7 +2,7 @@
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  /**
5
- * `akm health` document renderers, registered rather than intercepted (D7).
5
+ * `akm health` document renderers.
6
6
  *
7
7
  * Both handlers are **pure functions of the shaped result**. Everything the
8
8
  * rich report needs — per-run rows, window-compare deltas, the pending
@@ -13,12 +13,19 @@
13
13
  * renderer to call order and made the command branch on the output format to
14
14
  * know when to bind it. Putting the data in the envelope removed both.
15
15
  *
16
+ * The Markdown renderer registers into the shared per-command registry (D7,
17
+ * `../../output/render-registry.ts`) alongside every other command's `--format
18
+ * md` renderer. The HTML renderer does not: `health` was, and remains, the
19
+ * only command with a bespoke HTML report, so `cli/shared.ts` calls
20
+ * {@link renderHealthHtml} directly instead of going through a registry with
21
+ * exactly one possible registrant.
22
+ *
16
23
  * Returning `null` falls through to the generic renderer, so a result without
17
24
  * the report dataset still renders — generically — instead of erroring or
18
25
  * emitting an empty table.
19
26
  */
20
27
  import { renderHtml, resolveTemplatePath } from "../../output/html-render.js";
21
- import { registerHtmlRenderer, registerMdRenderer } from "../../output/render-registry.js";
28
+ import { registerMdRenderer } from "../../output/render-registry.js";
22
29
  import { buildHealthHtmlReplacements } from "./html-report.js";
23
30
  import { renderRunsDetailMd, renderWindowCompareMd } from "./md-report.js";
24
31
  function isHealthResult(value) {
@@ -34,7 +41,8 @@ registerMdRenderer("health", (result) => {
34
41
  return renderRunsDetailMd(result.runs);
35
42
  return null;
36
43
  });
37
- registerHtmlRenderer("health", (result) => {
44
+ /** `--format html` renderer for `akm health`, called directly from `cli/shared.ts`. */
45
+ export function renderHealthHtml(result) {
38
46
  if (!isHealthResult(result) || !result.report)
39
47
  return null;
40
48
  return renderHtml(resolveTemplatePath("health"), buildHealthHtmlReplacements(result, {
@@ -44,4 +52,4 @@ registerHtmlRenderer("health", (result) => {
44
52
  proposals: result.report.pendingProposals,
45
53
  deltas: result.deltas,
46
54
  }));
47
- });
55
+ }
@@ -79,31 +79,18 @@ function reshapeRun(r) {
79
79
  completedAt: r.completedAt,
80
80
  wallTimeMs: wall,
81
81
  ok: r.ok,
82
- mode: r.scope.mode,
83
82
  consDurationMs: consMs,
84
83
  miDurationMs: miMs,
85
84
  geDurationMs: geMs,
86
85
  otherMs: Math.max(0, wall - consMs - miMs - geMs),
87
- consRan: cons.ran,
88
86
  promoted: cons.promoted,
89
87
  merged: cons.merged,
90
88
  deleted: cons.deleted,
91
89
  contradicted: cons.contradicted,
92
90
  judgedNoAction: cons.judgedNoAction,
93
- processed: cons.processed,
94
- failedChunks: cons.failedChunks,
95
- totalChunks: cons.totalChunks,
96
91
  miWritten: mi.written,
97
- miConsidered: mi.considered,
98
- miYieldRate: mi.yieldRate,
99
- miCacheHits: mi.cacheHits,
100
92
  geEntities: ge.entities,
101
93
  geRelations: ge.relations,
102
- geCacheHitRate: ge.cacheHitRate,
103
- geFailures: ge.failures,
104
- distillSkipped: r.actions.distill.skipped,
105
- distillQueued: r.actions.distill.queued,
106
- distillLlmFailed: r.actions.distill.llmFailed,
107
94
  distillByReason: r.actions.distill.skippedByReason,
108
95
  reflectOk: r.actions.reflect.ok,
109
96
  reflectFailed: r.actions.reflect.failed,
@@ -111,8 +98,6 @@ function reshapeRun(r) {
111
98
  eligible: r.memorySummary.eligible,
112
99
  lintFlagged: r.lintFlagged,
113
100
  lintFixed: r.lintFixed,
114
- reflectsWithErrorContext: r.reflectsWithErrorContext,
115
- orphansPurged: r.orphansPurged,
116
101
  };
117
102
  }
118
103
  function compareRuns(a, b) {
@@ -135,11 +120,7 @@ function classify(deltas, metricKeys, lowerIsBetter = false) {
135
120
  }
136
121
  function buildTrend(deltas) {
137
122
  const decisionQuality = classify(deltas, ["improve.memoryInference.yieldRate", "improve.consolidation.promoted"]);
138
- const outputVolume = classify(deltas, [
139
- "improve.consolidation.promoted",
140
- "improve.memoryInference.written",
141
- "improve.sessionExtraction.proposalsCreated",
142
- ]);
123
+ const outputVolume = classify(deltas, ["improve.consolidation.promoted", "improve.memoryInference.written"]);
143
124
  const failures = classify(deltas, ["improve.graphExtraction.failures"], true);
144
125
  const latency = classify(deltas, ["improve.wallTime.medianMs", "improve.wallTime.p95Ms"], true);
145
126
  const score = [decisionQuality, outputVolume, failures, latency].reduce((acc, d) => acc + (d === "up" ? 1 : d === "down" ? -1 : 0), 0);
@@ -205,19 +186,7 @@ function buildAggregatesPhase(result, runsPhase) {
205
186
  const mi = improve.memoryInference;
206
187
  const ge = improve.graphExtraction;
207
188
  const wallTime = improve.wallTime;
208
- // WS-5: perf telemetry, coverage, and degradation metrics.
209
- const perf = improve.perfTelemetry ?? {
210
- dedupPoolSize: 0,
211
- llmPoolSize: 0,
212
- embedMs: 0,
213
- embedCacheHits: 0,
214
- embedCacheMisses: 0,
215
- overBudgetRuns: 0,
216
- runsWithTelemetry: 0,
217
- };
218
189
  const coverage = improve.coverage;
219
- const degradation = improve.degradation;
220
- const minting = improve.enrichmentMinting;
221
190
  // #576: real per-stage LLM token/time accounting (replaces the GPU-time
222
191
  // proxy). Optional-guarded so reports built from older health JSON without
223
192
  // the aggregate still render.
@@ -242,10 +211,7 @@ function buildAggregatesPhase(result, runsPhase) {
242
211
  memoryInference: mi,
243
212
  graphExtraction: ge,
244
213
  wallTime,
245
- perf,
246
214
  coverage,
247
- degradation,
248
- minting,
249
215
  llm,
250
216
  memorySummary: improve.memorySummary,
251
217
  includedResultRows: improve.resultRows?.included ?? totalRuns,
@@ -343,7 +309,7 @@ function groupProposalsBySource(proposals) {
343
309
  }
344
310
  /** Summary table rows: the base metric set + the WS-5 coverage/minting/perf/degradation extensions, when present. */
345
311
  function buildSummaryRows(aggregates, trend) {
346
- const { consolidation: cons, graphExtraction: ge, wallTime, llm, coverage, minting, perf, degradation } = aggregates;
312
+ const { consolidation: cons, graphExtraction: ge, wallTime, llm, coverage } = aggregates;
347
313
  const summaryRows = [
348
314
  ["Task fail rate", aggregates.taskFailRate, "flat"],
349
315
  ["Agent fail rate", aggregates.agentFailRate, "flat"],
@@ -388,80 +354,19 @@ function buildSummaryRows(aggregates, trend) {
388
354
  "Tokens spent on model reasoning/thinking, billed separately from prompt and completion.",
389
355
  ],
390
356
  ["LLM wall time", fmtMs(llm.totalDurationMs), trend.latency],
391
- ];
392
- // WS-5: denominator-fixed coverage rows (only when we have real data).
393
- if (coverage && !Number.isNaN(coverage.rate)) {
394
- summaryRows.push([
395
- "Coverage rate",
396
- pct(coverage.rate, 1),
397
- "flat",
398
- "Distinct accepted refs / total stash assets (denominator-fixed). Shows what fraction of the corpus has been touched.",
399
- ], [
400
- "Eligible fraction",
401
- pct(coverage.eligibleFraction, 1),
402
- "flat",
403
- "Eligible assets / total stash assets. Fraction the improve pipeline actively considers.",
404
- ], [
357
+ [
405
358
  "Coverage accepted",
406
359
  num(coverage.acceptedProposals),
407
360
  "flat",
408
- "Total accepted proposals in the window (raw volume — includes repeated rewrites of the same asset).",
409
- ], [
410
- "Churn ratio",
411
- Number.isFinite(coverage.churnRatio) ? num(coverage.churnRatio) : "—",
412
- Number.isFinite(coverage.churnRatio) && coverage.churnRatio > 1.5 ? "down" : "flat",
413
- "Accepted proposals / distinct refs touched. >1.5 = the loop is repeatedly rewriting the same assets (churn, not coverage).",
414
- ]);
415
- }
416
- // Enrichment-vs-minting policy rollup (reporting-only).
417
- if (minting && Number.isFinite(minting.share)) {
418
- summaryRows.push([
419
- "Enrichment-lane minted share",
420
- pct(minting.share, 1),
421
- minting.share > 0.05 ? "down" : "flat",
422
- `New assets minted by enrichment lanes / their accepted total (${minting.minted} minted vs ${minting.updated} updated). Enrichment lanes are ratified to edit existing assets only; WARN >5%, FAIL >15%.`,
423
- ]);
424
- }
425
- // WS-5: perf telemetry rows (only when at least one run reported telemetry).
426
- if (perf.runsWithTelemetry > 0) {
427
- const embedCacheTotal = perf.embedCacheHits + perf.embedCacheMisses;
428
- const embedCacheHitRate = embedCacheTotal > 0 ? pct(perf.embedCacheHits / embedCacheTotal, 1) : "—";
429
- summaryRows.push([
430
- "Embed cache hit rate",
431
- embedCacheHitRate,
432
- "flat",
433
- "Fraction of embedding lookups served from cache (>95% is healthy). Aggregated across WS-5 runs.",
434
- ], [
435
- "Embed wall time",
436
- fmtMs(perf.embedMs),
437
- "flat",
438
- "Cumulative embedding wall-clock time across consolidation runs in the window.",
439
- ], [
440
- "Dedup pool size",
441
- num(perf.dedupPoolSize),
442
- "flat",
443
- "Memory pool size after incremental narrowing, before the limit cap. WS-5 perf telemetry.",
444
- ], [
445
- "Over-budget consolidation runs",
446
- String(perf.overBudgetRuns),
447
- perf.overBudgetRuns > 0 ? "down" : "flat",
448
- "Runs where consolidation alone exceeded the total run budget (estimatedBudgetFractionUsed > 1.0).",
449
- ]);
450
- }
451
- // WS-5: degradation metrics rows.
452
- if (degradation) {
453
- summaryRows.push([
454
- "Corpus diversity (Gini)",
455
- num(degradation.corpusCentroidDistance),
456
- degradation.entrenchmentFlagged || degradation.salienceUniformityFlagged ? "down" : "flat",
457
- `Gini coefficient of positive retrieval_salience values across ${degradation.retrievalSalienceSampleSize} resolvable assets. Two-tailed: >0.35 = entrenchment risk; <0.08 = collapsed toward uniform.`,
458
- ], [
459
- "Merge fidelity contradiction rate",
460
- pct(degradation.mergeFidelityContradictionRate, 1),
361
+ "Accepted proposals in the window (raw volume — includes repeated rewrites of the same asset).",
362
+ ],
363
+ [
364
+ "Distinct assets touched",
365
+ num(coverage.distinctRefs),
461
366
  "flat",
462
- "Fraction of consolidated proposals that involved a contradiction, from consolidation result envelopes.",
463
- ]);
464
- }
367
+ "Distinct assets touched by accepted proposals in the window.",
368
+ ],
369
+ ];
465
370
  return summaryRows;
466
371
  }
467
372
  /**
@@ -2,28 +2,13 @@
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  /**
5
- * Lanes ratified as ENRICHMENT-ONLY: they may propose edits to existing
6
- * assets (metadata, relations, content refresh) but must not mint new ones.
7
- * New-asset generation belongs to the signal-gated minting lanes
8
- * (extract/distill/memory-inference).
9
- */
10
- export const ENRICHMENT_LANES = ["proactive", "high-salience", "signal-delta"];
11
- /** Minted share of enrichment-lane accepts that triggers a WARN advisory. */
12
- export const ENRICHMENT_MINTED_WARN_SHARE = 0.05;
13
- /** Minted share of enrichment-lane accepts that triggers a FAIL advisory. */
14
- export const ENRICHMENT_MINTED_FAIL_SHARE = 0.15;
15
- /**
16
- * Cron task failure rate at or above which the `task-fail-rate` health advisory
17
- * warns. 0.05 (5%) is the SAME threshold the HTML report already applies as its
18
- * fail-rate pass/warn cutoff (`failOk = taskFailRate < 0.05` in
19
- * src/commands/health/html-report.ts) — this constant makes it the single
20
- * source so the advisory and the rendered badge cannot drift.
5
+ * Cron task failure rate at or above which the `task-fail-rate` advisory
6
+ * warns; also the HTML report's fail-rate badge cutoff, so the two cannot drift.
21
7
  */
22
8
  export const TASK_FAIL_RATE_WARN = 0.05;
23
9
  /**
24
10
  * Minimum task_history rows a single task_id needs in the window before its
25
- * per-task fail rate feeds the `task-fail-rate` advisory's "worst single
26
- * task" signal. Guards against a rarely-run task (e.g. 1 failure out of 1
27
- * run) reading as a 100% fail rate off a single noisy sample.
11
+ * own fail rate feeds `task-fail-rate` — a task run once and failed once is
12
+ * not a 100% signal.
28
13
  */
29
14
  export const MIN_ROWS_FOR_WORST_TASK_FAIL_RATE = 5;
@@ -3,17 +3,14 @@
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  /**
5
5
  * Window handling for `akm health`: `--window-compare` / `--windows` parsing,
6
- * per-window metric assembly, log-backing partition, and delta computation.
6
+ * per-window metric assembly, and delta computation between two windows.
7
7
  */
8
- import fs from "node:fs";
9
8
  import { UsageError } from "../../core/errors.js";
10
9
  import { readEvents } from "../../core/events.js";
11
- import { buildTaskRunId, getLoggedRunIds } from "../../core/logs-db.js";
12
10
  import { DURATION_UNITS, parseDuration } from "../../core/time.js";
13
11
  import { queryTaskHistory } from "../../storage/repositories/task-history-repository.js";
14
- import { buildImproveSkipSummary, computeWallTimeStats, countAgentFailureReasons, isAgentTaskHistoryRow, roundRate, summarizeImproveCompleted, summarizeImproveRuns, taskFailureDetail, } from "./improve-metrics.js";
12
+ import { buildImproveSkipSummary, computeWallTimeStats, computeWindowProposalCoverage, countAgentFailureReasons, isAgentTaskHistoryRow, roundRate, summarizeImproveRuns, taskFailureDetail, } from "./improve-metrics.js";
15
13
  import { readLlmUsageAggregate } from "./llm-usage.js";
16
- import { computeDegradationMetrics, computeDenominatorFixedCoverage } from "./metrics.js";
17
14
  import { buildPerRunSummaries } from "./task-runs.js";
18
15
  import { ACTIVE_RUN_WARN_MS, IMPROVE_COMPLETED_EVENT, } from "./types.js";
19
16
  /**
@@ -71,28 +68,30 @@ export function parseWindowSpec(raw) {
71
68
  ...(fields.until ? { until: fields.until } : {}),
72
69
  };
73
70
  }
74
- /** Hard-coded list of "interesting" metric paths for window-compare deltas. */
71
+ /**
72
+ * Metric paths diffed between the earliest and latest window (`deltas` in the
73
+ * JSON result; the delta column of `--format md`). Paths are relative to a
74
+ * {@link WindowResult}.
75
+ */
75
76
  export const INTERESTING_DELTA_PATHS = [
76
77
  "improve.actions.reflect.failed",
77
- "improve.actions.reflect.guardRejected",
78
- "improve.actions.distill.llmFailed",
79
78
  "improve.actions.distill.queued",
80
- "improve.actions.distill.deferred",
79
+ "improve.actions.distill.llmFailed",
81
80
  "improve.consolidation.promoted",
82
81
  "improve.memoryInference.written",
83
82
  "improve.memoryInference.yieldRate",
84
83
  "improve.memoryInference.skippedNoFacts",
85
- "improve.memoryInference.htmlErrorCount",
86
- "improve.graphExtraction.cacheHitRate",
87
84
  "improve.graphExtraction.failures",
88
- "improve.graphExtraction.htmlErrors",
89
- "improve.graphExtraction.nonArrayBatchFailures",
90
- "improve.sessionExtraction.sessionsScanned",
91
- "improve.sessionExtraction.proposalsCreated",
92
85
  "improve.autoAccept.promoted",
93
86
  "improve.autoAccept.validationFailed",
87
+ "improve.coverage.acceptedProposals",
88
+ "improve.coverage.distinctRefs",
94
89
  "improve.wallTime.medianMs",
95
90
  "improve.wallTime.p95Ms",
91
+ "metrics.llmUsage.calls",
92
+ "metrics.llmUsage.totalTokens",
93
+ "metrics.llmUsage.totalDurationMs",
94
+ "metrics.llmUsage.failures",
96
95
  ];
97
96
  export function readNumericPath(obj, path) {
98
97
  const parts = path.split(".");
@@ -123,74 +122,66 @@ export function computeDeltas(first, last) {
123
122
  return out;
124
123
  }
125
124
  /**
126
- * Partition task_history rows into "should have a log" (non-null log_path) and
127
- * "log is actually backed". A run counts as backed when logs.db holds rows for
128
- * its run_id (#579 — the DB is the primary record); rows written before logs.db
129
- * existed fall back to the transitional on-disk file check. `logsDb` may be
130
- * undefined when logs.db could not be opened — then only the file check runs.
125
+ * Task-history-derived rates, shared by the main health path
126
+ * (`health.ts`'s `gatherTaskHistoryPhase`) and {@link buildWindowMetrics}
127
+ * below — previously computed twice with the only difference being whether
128
+ * the caller had already bounded `taskRows` by an `until`. Pure projection of
129
+ * already-queried rows; callers own the `since`/`until` query.
131
130
  */
132
- export function partitionLogBackedRows(taskRows, logsDb) {
133
- const withLogs = taskRows.filter((row) => row.log_path !== null);
134
- const loggedRunIds = logsDb
135
- ? getLoggedRunIds(logsDb, withLogs.map((row) => buildTaskRunId(row.task_id, row.started_at)))
136
- : new Set();
137
- const backed = withLogs.filter((row) => loggedRunIds.has(buildTaskRunId(row.task_id, row.started_at)) ||
138
- (row.log_path !== null && fs.existsSync(row.log_path)));
139
- return { withLogs, backed };
140
- }
141
- export function buildWindowMetrics(db, stateDbPath, since, until, now = () => Date.now(), logsDb) {
142
- const taskRows = queryTaskHistory(db, { since }).filter((row) => {
143
- const startMs = new Date(row.started_at).getTime();
144
- const untilMs = new Date(until).getTime();
145
- return !Number.isFinite(untilMs) || startMs < untilMs;
146
- });
147
- const { withLogs: taskRowsWithLogs, backed: existingLogRows } = partitionLogBackedRows(taskRows, logsDb);
131
+ export function computeTaskWindowRates(taskRows, now) {
148
132
  const failedTaskRows = taskRows.filter((row) => row.status === "failed");
149
133
  const activeRows = taskRows.filter((row) => row.status === "active" && row.completed_at === null);
150
- const stuckActiveRuns = activeRows.filter((row) => now() - new Date(row.started_at).getTime() > ACTIVE_RUN_WARN_MS).length;
134
+ const stuckActiveRows = activeRows.filter((row) => now() - new Date(row.started_at).getTime() > ACTIVE_RUN_WARN_MS);
151
135
  const agentRows = taskRows.filter((row) => isAgentTaskHistoryRow(row));
152
136
  const agentFailures = agentRows.filter((row) => {
153
137
  const detail = taskFailureDetail(row);
154
138
  return typeof detail?.reason === "string" && detail.reason.length > 0;
155
139
  });
156
- const logBackingRate = taskRowsWithLogs.length === 0 ? 1 : existingLogRows.length / taskRowsWithLogs.length;
157
- const taskFailRate = taskRows.length === 0 ? 0 : failedTaskRows.length / taskRows.length;
158
- const agentFailureRate = agentRows.length === 0 ? 0 : agentFailures.length / agentRows.length;
159
- const agentFailureReasonCounts = countAgentFailureReasons(agentFailures);
160
- const improveInvoked = readEvents({ since, type: "improve_invoked" }, { dbPath: stateDbPath }).events.filter((event) => new Date(event.ts ?? since).getTime() < new Date(until).getTime()).length;
161
- const improveCompletedEvents = readEvents({ since, type: IMPROVE_COMPLETED_EVENT }, { dbPath: stateDbPath }).events.filter((event) => new Date(event.ts ?? since).getTime() < new Date(until).getTime());
162
- const improveSkippedEvents = readEvents({ since, type: "improve_skipped" }, { dbPath: stateDbPath }).events.filter((event) => new Date(event.ts ?? since).getTime() < new Date(until).getTime());
163
- const eventsMetrics = summarizeImproveCompleted(improveCompletedEvents);
164
- const { metrics: improveSummary, runCount } = summarizeImproveRuns(db, since, until);
165
- improveSummary.invoked = improveInvoked;
166
- improveSummary.completed = eventsMetrics.completed;
167
- const skipSummary = buildImproveSkipSummary(improveSkippedEvents);
168
- improveSummary.skipped = skipSummary.skipped;
169
- improveSummary.skipReasons = skipSummary.skipReasons;
170
- // Preserve the per-phase aggregation computed by summarizeImproveRuns and
171
- // derive top-level wall times from the same improve-runs window so counts
172
- // and percentiles stay aligned with per-run reporting.
140
+ return {
141
+ taskFailRate: taskRows.length === 0 ? 0 : failedTaskRows.length / taskRows.length,
142
+ agentFailureRate: agentRows.length === 0 ? 0 : agentFailures.length / agentRows.length,
143
+ agentFailureReasonCounts: countAgentFailureReasons(agentFailures),
144
+ stuckActiveRuns: stuckActiveRows.length,
145
+ stuckActiveRows,
146
+ };
147
+ }
148
+ /**
149
+ * Improve-pipeline window summary, shared by the main health path
150
+ * (`health.ts`) and {@link buildWindowMetrics} below: events-derived
151
+ * invoked/completed/skipped counts, the `result_json` aggregate, wall-time
152
+ * stats from the same per-run rows, and accepted-proposal coverage, all over
153
+ * one `[since, until)`.
154
+ */
155
+ export function buildImproveWindowSummary(db, stateDbPath, since, until) {
156
+ const untilMs = new Date(until).getTime();
157
+ const eventsBeforeUntil = (type) => readEvents({ since, type }, { dbPath: stateDbPath }).events.filter((event) => new Date(event.ts ?? since).getTime() < untilMs);
158
+ const { metrics: improve, runCount } = summarizeImproveRuns(db, since, until);
159
+ improve.invoked = eventsBeforeUntil("improve_invoked").length;
160
+ improve.completed = eventsBeforeUntil(IMPROVE_COMPLETED_EVENT).length;
161
+ const skipSummary = buildImproveSkipSummary(eventsBeforeUntil("improve_skipped"));
162
+ improve.skipped = skipSummary.skipped;
163
+ improve.skipReasons = skipSummary.skipReasons;
164
+ // Wall times come from the same improve-runs window as the per-run
165
+ // reporting so counts and percentiles stay aligned with it.
173
166
  const perRunSummaries = buildPerRunSummaries(db, since, until);
174
- const wallTimes = perRunSummaries.map((run) => run.wallTimeMs).filter((ms) => Number.isFinite(ms) && ms > 0);
175
- improveSummary.wallTime = computeWallTimeStats(wallTimes, improveSummary.wallTime.byPhase);
176
- // WS-5: Compute denominator-fixed coverage from the most recent run's
177
- // memorySummary (totalAssets = eligible + derived — the fixed denominator).
178
- const totalAssets = improveSummary.memorySummary.eligible + improveSummary.memorySummary.derived;
179
- improveSummary.coverage = computeDenominatorFixedCoverage(db, totalAssets, improveSummary.memorySummary.eligible, since, until);
180
- // WS-5: Compute per-run degradation metrics (corpus diversity, merge fidelity,
181
- // generation distribution, oracle spot-check). Health VIEWS only.
182
- const degradation = computeDegradationMetrics(db, since, until);
183
- if (degradation) {
184
- improveSummary.degradation = degradation;
185
- }
167
+ improve.wallTime = computeWallTimeStats(perRunSummaries.map((run) => run.wallTimeMs).filter((ms) => Number.isFinite(ms) && ms > 0));
168
+ improve.coverage = computeWindowProposalCoverage(db, since, until);
169
+ return { improve, runCount, perRunSummaries };
170
+ }
171
+ export function buildWindowMetrics(db, stateDbPath, since, until, now = () => Date.now()) {
172
+ const untilMs = new Date(until).getTime();
173
+ const taskRows = queryTaskHistory(db, { since }).filter((row) => {
174
+ const startMs = new Date(row.started_at).getTime();
175
+ return !Number.isFinite(untilMs) || startMs < untilMs;
176
+ });
177
+ const rates = computeTaskWindowRates(taskRows, now);
178
+ const { improve, runCount } = buildImproveWindowSummary(db, stateDbPath, since, until);
186
179
  const metrics = {
187
- taskFailRate: roundRate(taskFailRate),
188
- agentFailureRate: roundRate(agentFailureRate),
189
- agentFailureReasonCounts,
190
- stuckActiveRuns,
191
- logBackingRate: roundRate(logBackingRate),
192
- probeRoundTripMs: null,
180
+ taskFailRate: roundRate(rates.taskFailRate),
181
+ agentFailureRate: roundRate(rates.agentFailureRate),
182
+ agentFailureReasonCounts: rates.agentFailureReasonCounts,
183
+ stuckActiveRuns: rates.stuckActiveRuns,
193
184
  llmUsage: readLlmUsageAggregate(stateDbPath, since, until),
194
185
  };
195
- return { improve: improveSummary, metrics, runs: runCount };
186
+ return { improve, metrics, runs: runCount };
196
187
  }