akm-cli 0.9.17-alpha.1 → 0.9.17-alpha.10

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 (399) hide show
  1. package/CHANGELOG.md +1623 -0
  2. package/STABILITY.md +11 -10
  3. package/dist/akm +124 -193
  4. package/dist/akm-migrate +38 -19
  5. package/dist/assets/hints/cli-hints-full.md +6 -7
  6. package/dist/assets/improve-strategies/catchup.json +0 -3
  7. package/dist/assets/improve-strategies/consolidate.json +0 -1
  8. package/dist/assets/improve-strategies/default.json +1 -2
  9. package/dist/assets/improve-strategies/proactive-maintenance.json +1 -2
  10. package/dist/assets/improve-strategies/quick.json +1 -2
  11. package/dist/assets/improve-strategies/reflect-distill.json +1 -2
  12. package/dist/assets/improve-strategies/thorough.json +0 -3
  13. package/dist/assets/prompts/consolidate-pair.md +20 -0
  14. package/dist/assets/prompts/retrieval-relevance-judge.md +6 -0
  15. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +20 -20
  16. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -2
  17. package/dist/assets/templates/html/health.html +3 -5
  18. package/dist/cli/retired-commands.js +1 -1
  19. package/dist/cli/shared.js +6 -2
  20. package/dist/cli/unknown-flags.js +24 -1
  21. package/dist/cli.js +68 -10
  22. package/dist/commands/agent/agent-dispatch.js +1 -1
  23. package/dist/commands/command/command-execution.js +24 -62
  24. package/dist/commands/feedback-cli.js +0 -1
  25. package/dist/commands/health/accept-rate.js +2 -2
  26. package/dist/commands/health/archive-usage.js +92 -0
  27. package/dist/commands/health/checks.js +30 -75
  28. package/dist/commands/health/config-skew.js +38 -0
  29. package/dist/commands/health/data-dir-usage.js +25 -13
  30. package/dist/commands/health/egress.js +54 -0
  31. package/dist/commands/health/html-report.js +1 -42
  32. package/dist/commands/health/improve-metrics.js +136 -587
  33. package/dist/commands/health/md-report.js +1 -6
  34. package/dist/commands/health/plugin-staleness.js +53 -3
  35. package/dist/commands/health/renderers.js +12 -4
  36. package/dist/commands/health/report-view-model.js +14 -119
  37. package/dist/commands/health/types-improve.js +4 -19
  38. package/dist/commands/health/windows.js +64 -74
  39. package/dist/commands/health.js +135 -143
  40. package/dist/commands/improve/consolidate/chunking.js +25 -100
  41. package/dist/commands/improve/consolidate/continuity-check.js +137 -0
  42. package/dist/commands/improve/consolidate/pair-pass.js +791 -0
  43. package/dist/commands/improve/consolidate/sanitize.js +54 -149
  44. package/dist/commands/improve/consolidate.js +573 -1124
  45. package/dist/commands/improve/content-hash.js +16 -24
  46. package/dist/commands/improve/distill/content-repair.js +18 -100
  47. package/dist/commands/improve/distill-guards.js +20 -81
  48. package/dist/commands/improve/distill-promotion-policy.js +23 -243
  49. package/dist/commands/improve/distill.js +608 -1075
  50. package/dist/commands/improve/eligibility.js +126 -400
  51. package/dist/commands/improve/execution.js +8 -10
  52. package/dist/commands/improve/extract-prompt.js +1 -2
  53. package/dist/commands/improve/extract.js +487 -1046
  54. package/dist/commands/improve/feedback-valence.js +0 -25
  55. package/dist/commands/improve/improve-cli.js +54 -171
  56. package/dist/commands/improve/improve-result-file.js +10 -66
  57. package/dist/commands/improve/improve-strategies.js +35 -9
  58. package/dist/commands/improve/improve-usage-report.js +18 -64
  59. package/dist/commands/improve/improve.js +451 -1082
  60. package/dist/commands/improve/ledger.js +119 -0
  61. package/dist/commands/improve/locks.js +2 -8
  62. package/dist/commands/improve/loop-stages.js +401 -1192
  63. package/dist/commands/improve/memory/derived-ref.js +12 -77
  64. package/dist/commands/improve/memory/memory-belief.js +16 -118
  65. package/dist/commands/improve/memory/memory-improve.js +266 -14
  66. package/dist/commands/improve/outcome-loop.js +28 -156
  67. package/dist/commands/improve/planner.js +5 -15
  68. package/dist/commands/improve/preparation.js +776 -2349
  69. package/dist/commands/improve/proactive-maintenance.js +34 -101
  70. package/dist/commands/improve/reflect-noise.js +104 -280
  71. package/dist/commands/improve/reflect.js +642 -1364
  72. package/dist/commands/improve/retrieval-gate.js +127 -0
  73. package/dist/commands/improve/retrieval-scope.js +92 -0
  74. package/dist/commands/improve/salience.js +41 -240
  75. package/dist/commands/improve/session-asset.js +19 -100
  76. package/dist/commands/improve/stage.js +322 -0
  77. package/dist/commands/lint/base-linter.js +37 -15
  78. package/dist/commands/proposal/drain.js +258 -644
  79. package/dist/commands/proposal/proposal-cli.js +19 -20
  80. package/dist/commands/proposal/proposal-types.js +27 -41
  81. package/dist/commands/proposal/proposal.js +38 -8
  82. package/dist/commands/proposal/propose.js +134 -160
  83. package/dist/commands/proposal/repository.js +1099 -1475
  84. package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
  85. package/dist/commands/proposal/validators/proposal-validators.js +1 -1
  86. package/dist/commands/proposal/validators/proposals.js +22 -89
  87. package/dist/commands/read/curate.js +105 -462
  88. package/dist/commands/read/knowledge.js +3 -2
  89. package/dist/commands/read/search-cli.js +16 -33
  90. package/dist/commands/read/search.js +17 -23
  91. package/dist/commands/read/show.js +57 -108
  92. package/dist/commands/sources/bundle-cli.js +25 -2
  93. package/dist/commands/sources/bundle-config-ops.js +4 -0
  94. package/dist/commands/sources/dangerous-env-audit.js +1 -2
  95. package/dist/commands/sources/info.js +127 -29
  96. package/dist/commands/sources/installed-stashes.js +197 -746
  97. package/dist/commands/sources/schema-repair.js +98 -129
  98. package/dist/commands/sources/source-add.js +62 -12
  99. package/dist/commands/sources/source-manage.js +9 -2
  100. package/dist/commands/sources/stash-cli.js +24 -4
  101. package/dist/commands/tasks/explain.js +10 -13
  102. package/dist/commands/tasks/tasks-cli.js +12 -13
  103. package/dist/commands/tasks/tasks.js +350 -936
  104. package/dist/commands/tasks/validate.js +26 -24
  105. package/dist/commands/workflow/plan.js +22 -29
  106. package/dist/commands/workflow-cli.js +4 -4
  107. package/dist/core/adapter/adapters/akm-adapter.js +2 -1
  108. package/dist/core/adapter/adapters/akm-lint.js +2 -3
  109. package/dist/core/adapter/adapters/akm-metadata.js +42 -12
  110. package/dist/core/adapter/adapters/akm-task-adapter.js +29 -8
  111. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  112. package/dist/core/adapter/execution-source.js +17 -29
  113. package/dist/core/asset/asset-placement.js +4 -13
  114. package/dist/core/asset/resolve-ref.js +1 -1
  115. package/dist/core/bundle-id.js +42 -5
  116. package/dist/core/bundle-rename.js +285 -0
  117. package/dist/core/config/config-io.js +1 -2
  118. package/dist/core/config/config-schema.js +9 -34
  119. package/dist/core/config/config-walker.js +1 -1
  120. package/dist/core/config/config.js +184 -111
  121. package/dist/core/config/engine-semantics.js +0 -2
  122. package/dist/core/config/legacy-source-shape-shim.js +38 -9
  123. package/dist/core/config/schema/embedding.js +20 -5
  124. package/dist/core/config/schema/engines.js +5 -0
  125. package/dist/core/config/schema/execution.js +1 -1
  126. package/dist/core/config/schema/experimental.js +1 -1
  127. package/dist/core/config/schema/improve-processes.js +37 -135
  128. package/dist/core/config/schema/improve.js +4 -42
  129. package/dist/core/config/schema/index-config.js +9 -48
  130. package/dist/core/config/schema/scheduler.js +12 -12
  131. package/dist/core/config/schema/search.js +6 -22
  132. package/dist/core/env-secret-ref.js +0 -1
  133. package/dist/core/errors.js +8 -9
  134. package/dist/core/file-change.js +13 -5
  135. package/dist/core/file-lock.js +76 -173
  136. package/dist/core/improve-result.js +26 -7
  137. package/dist/core/improve-types.js +0 -1
  138. package/dist/core/logs-db.js +2 -2
  139. package/dist/core/loopback.js +7 -12
  140. package/dist/core/non-task-input.js +20 -0
  141. package/dist/core/parse.js +13 -16
  142. package/dist/core/paths.js +0 -31
  143. package/dist/core/redaction.js +109 -2
  144. package/dist/core/run-lock.js +2 -5
  145. package/dist/core/spawn-env.js +1 -1
  146. package/dist/core/state/migrations.js +123 -61
  147. package/dist/core/state-db-scope.js +2 -4
  148. package/dist/core/state-db.js +126 -692
  149. package/dist/core/time.js +0 -20
  150. package/dist/core/type-presentation.js +1 -9
  151. package/dist/core/write-source.js +294 -1005
  152. package/dist/execution/input-contract.js +1 -1
  153. package/dist/execution/resolved-request.js +135 -689
  154. package/dist/execution/source.js +63 -257
  155. package/dist/execution/target-ref.js +1 -1
  156. package/dist/indexer/bundle-identity-guard.js +2 -2
  157. package/dist/indexer/db/llm-cache.js +2 -2
  158. package/dist/indexer/ensure-index.js +46 -87
  159. package/dist/indexer/index-rebuild-lock.js +3 -11
  160. package/dist/indexer/index-writer-lock.js +8 -17
  161. package/dist/indexer/index-written-assets.js +141 -154
  162. package/dist/indexer/indexer.js +400 -1124
  163. package/dist/indexer/links/declared-links.js +90 -0
  164. package/dist/indexer/materialize-embeddings.js +60 -397
  165. package/dist/indexer/passes/memory-inference.js +81 -90
  166. package/dist/indexer/passes/metadata.js +132 -219
  167. package/dist/indexer/read-preflight.js +0 -7
  168. package/dist/indexer/scan/doc-to-entry.js +2 -3
  169. package/dist/indexer/scan/drain-dir.js +1 -1
  170. package/dist/indexer/search/db-search.js +190 -590
  171. package/dist/indexer/search/fts-query.js +30 -41
  172. package/dist/indexer/search/ranking.js +28 -154
  173. package/dist/indexer/search/search-attribution.js +12 -32
  174. package/dist/indexer/search/search-fields.js +11 -15
  175. package/dist/indexer/search/search-hit-enrichers.js +54 -85
  176. package/dist/indexer/search/search-source.js +1 -4
  177. package/dist/indexer/usage/usage-events.js +36 -7
  178. package/dist/indexer/walk/walker.js +3 -4
  179. package/dist/integrations/agent/engine-fallback.js +23 -40
  180. package/dist/integrations/agent/engine-resolution.js +93 -183
  181. package/dist/integrations/agent/execution.js +507 -0
  182. package/dist/integrations/agent/model-map.js +28 -156
  183. package/dist/integrations/agent/request-lowering.js +66 -141
  184. package/dist/integrations/agent/runner-dispatch.js +143 -321
  185. package/dist/integrations/agent/runner.js +54 -14
  186. package/dist/integrations/lockfile.js +53 -101
  187. package/dist/llm/client.js +8 -10
  188. package/dist/llm/embedders/deterministic.js +2 -3
  189. package/dist/llm/embedders/profile.js +71 -0
  190. package/dist/llm/embedders/remote.js +11 -17
  191. package/dist/llm/feature-gate.js +0 -8
  192. package/dist/llm/index-passes.js +3 -5
  193. package/dist/llm/memory-infer.js +1 -2
  194. package/dist/llm/structured-call.js +5 -24
  195. package/dist/output/generic-render.js +23 -11
  196. package/dist/output/html-render.js +13 -10
  197. package/dist/output/render-registry.js +3 -32
  198. package/dist/output/shapes/helpers.js +25 -38
  199. package/dist/output/shapes/passthrough.js +1 -9
  200. package/dist/{indexer/graph/graph-types.js → output/text/bundle-rename.js} +4 -1
  201. package/dist/output/text/command-format.js +69 -31
  202. package/dist/output/text/helpers.js +1 -1
  203. package/dist/output/text/migrate.js +5 -14
  204. package/dist/output/text/proposal-format.js +48 -3
  205. package/dist/output/text/show-format.js +13 -17
  206. package/dist/output/text/workflow-format.js +0 -32
  207. package/dist/output/text.js +2 -0
  208. package/dist/registry/factory.js +4 -19
  209. package/dist/registry/network.js +66 -220
  210. package/dist/registry/providers/index.js +0 -2
  211. package/dist/registry/providers/skills-sh.js +3 -14
  212. package/dist/registry/providers/static-index.js +24 -26
  213. package/dist/registry/resolve.js +55 -131
  214. package/dist/scripts/akm-migrate-node.js +42938 -92375
  215. package/dist/scripts/akm-migrate.js +42930 -92365
  216. package/dist/setup/registry-stash-loader.js +4 -13
  217. package/dist/setup/semantic-assets.js +3 -44
  218. package/dist/setup/setup.js +1 -1
  219. package/dist/setup/steps/connection.js +5 -6
  220. package/dist/setup/steps/platforms.js +2 -2
  221. package/dist/setup/steps/tasks.js +25 -15
  222. package/dist/sources/provider-factory.js +17 -18
  223. package/dist/sources/providers/filesystem.js +2 -3
  224. package/dist/sources/providers/git-install.js +7 -1
  225. package/dist/sources/providers/git-provider.js +0 -3
  226. package/dist/sources/providers/git-stash.js +83 -21
  227. package/dist/sources/providers/npm.js +2 -4
  228. package/dist/sources/providers/provider-utils.js +5 -10
  229. package/dist/sources/providers/website.js +0 -2
  230. package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
  231. package/dist/sources/website-url.js +2 -2
  232. package/dist/storage/database.js +9 -35
  233. package/dist/storage/repositories/improve-ledger-repository.js +209 -0
  234. package/dist/storage/repositories/index-connection.js +39 -72
  235. package/dist/storage/repositories/index-entries-repository.js +88 -129
  236. package/dist/storage/repositories/index-entry-mapper.js +1 -2
  237. package/dist/storage/repositories/index-entry-schema.js +101 -268
  238. package/dist/storage/repositories/index-fts-repository.js +86 -256
  239. package/dist/storage/repositories/index-links-repository.js +143 -0
  240. package/dist/storage/repositories/index-llm-cache-repository.js +7 -9
  241. package/dist/storage/repositories/index-meta-repository.js +6 -4
  242. package/dist/storage/repositories/index-schema.js +257 -325
  243. package/dist/storage/repositories/index-utility-repository.js +8 -29
  244. package/dist/storage/repositories/index-vec-repository.js +133 -414
  245. package/dist/storage/repositories/outcome-repository.js +2 -1
  246. package/dist/storage/repositories/proposals-repository.js +100 -0
  247. package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
  248. package/dist/storage/repositories/salience-repository.js +1 -19
  249. package/dist/storage/repositories/task-history-repository.js +26 -4
  250. package/dist/storage/repositories/workflow-runs-repository.js +53 -244
  251. package/dist/storage/sqlite-migrations.js +136 -0
  252. package/dist/storage/sqlite-pragmas.js +11 -9
  253. package/dist/storage/sqlite-transaction.js +170 -0
  254. package/dist/storage/state-db-integrity.js +34 -27
  255. package/dist/tasks/activation-config.js +134 -62
  256. package/dist/tasks/backends/cron.js +191 -302
  257. package/dist/tasks/backends/exec-utils.js +2 -5
  258. package/dist/tasks/backends/launchd.js +141 -748
  259. package/dist/tasks/backends/schtasks.js +119 -623
  260. package/dist/tasks/prepare/prepare-support.js +5 -15
  261. package/dist/tasks/prepare/prepare.js +0 -2
  262. package/dist/tasks/resolve-akm-bin.js +20 -79
  263. package/dist/tasks/run/attempt-lifecycle.js +0 -1
  264. package/dist/tasks/run/load-task.js +1 -1
  265. package/dist/tasks/scheduler-binding.js +20 -238
  266. package/dist/tasks/scheduler-invocation.js +136 -244
  267. package/dist/tasks/scheduler-lock.js +53 -0
  268. package/dist/tasks/scheduler-sync.js +368 -679
  269. package/dist/tasks/source/parse-task-source.js +55 -9
  270. package/dist/tasks/source/task-source-v3-frozen.js +3 -4
  271. package/dist/tasks/source/task-to-v4.js +464 -88
  272. package/dist/workflows/authoring/authoring.js +3 -12
  273. package/dist/workflows/compile.js +211 -0
  274. package/dist/workflows/concurrency-policy.js +13 -74
  275. package/dist/workflows/exec/child-invocation.js +3 -17
  276. package/dist/workflows/exec/child-workflow.js +32 -141
  277. package/dist/workflows/exec/dispatch-redaction.js +13 -53
  278. package/dist/workflows/exec/environment.js +98 -0
  279. package/dist/workflows/exec/exec-unit.js +33 -140
  280. package/dist/workflows/exec/frozen-judge.js +7 -59
  281. package/dist/workflows/exec/native-executor.js +82 -341
  282. package/dist/workflows/exec/param-secrets.js +29 -47
  283. package/dist/workflows/exec/run-workflow.js +154 -387
  284. package/dist/workflows/exec/scheduler.js +9 -36
  285. package/dist/workflows/exec/step-work.js +127 -430
  286. package/dist/workflows/exec/unit-dispatch.js +11 -63
  287. package/dist/workflows/exec/unit-writer.js +8 -52
  288. package/dist/workflows/exec/worktree.js +39 -273
  289. package/dist/workflows/freeze/child-output-references.js +4 -15
  290. package/dist/workflows/freeze/environment.js +99 -92
  291. package/dist/workflows/freeze/freeze.js +172 -0
  292. package/dist/workflows/freeze/step-values.js +19 -21
  293. package/dist/workflows/freeze/targets/child-workflow.js +23 -92
  294. package/dist/workflows/freeze/targets/command.js +10 -33
  295. package/dist/workflows/freeze/targets/script.js +5 -12
  296. package/dist/workflows/freeze/targets/shell.js +3 -6
  297. package/dist/workflows/freeze/targets/task.js +25 -80
  298. package/dist/workflows/freeze/task-bindings.js +20 -67
  299. package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
  300. package/dist/workflows/ir/params.js +6 -51
  301. package/dist/workflows/ir/plan-hash.js +2 -34
  302. package/dist/workflows/parser.js +140 -43
  303. package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
  304. package/dist/workflows/renderer.js +36 -69
  305. package/dist/workflows/resource-limits.js +12 -120
  306. package/dist/workflows/runtime/agent-identity.js +8 -40
  307. package/dist/workflows/runtime/run-outputs.js +3 -6
  308. package/dist/workflows/runtime/run-plan.js +316 -0
  309. package/dist/workflows/runtime/runs.js +48 -200
  310. package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
  311. package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
  312. package/dist/workflows/validate-summary.js +2 -7
  313. package/docs/integration/bundling-akm.md +49 -42
  314. package/docs/migration/README.md +1 -0
  315. package/docs/migration/release-notes/0.9.17.md +43 -0
  316. package/docs/migration/v0.9.1-to-v0.9.2.md +23 -7
  317. package/docs/reference/cli.md +229 -133
  318. package/docs/reference/configuration.md +71 -57
  319. package/docs/reference/data-and-telemetry.md +19 -21
  320. package/docs/reference/tasks.md +105 -39
  321. package/docs/reference/workflow-schema.md +14 -18
  322. package/docs/reference/workflows.md +6 -9
  323. package/package.json +1 -1
  324. package/schemas/akm-config.json +87 -754
  325. package/dist/assets/improve-strategies/graph-refresh.json +0 -15
  326. package/dist/assets/prompts/contradiction-judge.md +0 -33
  327. package/dist/assets/prompts/graph-extract-system.md +0 -1
  328. package/dist/assets/prompts/graph-extract-user-prompt.md +0 -35
  329. package/dist/assets/prompts/metadata-enhance-system.md +0 -1
  330. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +0 -4
  331. package/dist/commands/health/advisories.js +0 -150
  332. package/dist/commands/health/metrics.js +0 -329
  333. package/dist/commands/health/surfaces.js +0 -102
  334. package/dist/commands/improve/anti-collapse.js +0 -83
  335. package/dist/commands/improve/collapse-detector.js +0 -432
  336. package/dist/commands/improve/consolidate/eligibility.js +0 -48
  337. package/dist/commands/improve/consolidate/merge.js +0 -146
  338. package/dist/commands/improve/distill/promote-memory.js +0 -329
  339. package/dist/commands/improve/distill/quality-gate.js +0 -500
  340. package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
  341. package/dist/commands/improve/proposal-envelope.js +0 -31
  342. package/dist/commands/improve/run-context.js +0 -123
  343. package/dist/commands/improve/shared.js +0 -21
  344. package/dist/commands/improve/source-identity.js +0 -28
  345. package/dist/commands/improve/triage.js +0 -96
  346. package/dist/commands/proposal/drain-policies.js +0 -151
  347. package/dist/commands/sources/update-transaction.js +0 -220
  348. package/dist/core/action-contributors.js +0 -28
  349. package/dist/core/config/config-version-shim.js +0 -101
  350. package/dist/core/fs-txn.js +0 -405
  351. package/dist/core/lexical-score.js +0 -25
  352. package/dist/core/maintenance-barrier.js +0 -167
  353. package/dist/execution/executable-identity.js +0 -105
  354. package/dist/execution/guarded-source.js +0 -427
  355. package/dist/indexer/db/graph-db.js +0 -444
  356. package/dist/indexer/graph/graph-boost.js +0 -427
  357. package/dist/indexer/graph/graph-dedup.js +0 -95
  358. package/dist/indexer/graph/graph-extraction.js +0 -1182
  359. package/dist/indexer/search/name-match.js +0 -35
  360. package/dist/indexer/search/ranking-contributors.js +0 -515
  361. package/dist/indexer/search/ranking-types.js +0 -4
  362. package/dist/indexer/walk/project-context.js +0 -192
  363. package/dist/integrations/agent/execution-cascade.js +0 -566
  364. package/dist/integrations/agent/execution-definitions.js +0 -202
  365. package/dist/integrations/agent/execution-lowering.js +0 -841
  366. package/dist/integrations/agent/execution-preparation.js +0 -98
  367. package/dist/integrations/agent/inline-execution.js +0 -74
  368. package/dist/llm/graph-extract.js +0 -872
  369. package/dist/llm/metadata-enhance.js +0 -96
  370. package/dist/registry/create-provider-registry.js +0 -29
  371. package/dist/registry/pinned-request-helper.js +0 -247
  372. package/dist/registry/pinned-transport.js +0 -717
  373. package/dist/sources/providers/index.js +0 -14
  374. package/dist/storage/engines/sqlite-migrations.js +0 -271
  375. package/dist/storage/repositories/canaries-repository.js +0 -107
  376. package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
  377. package/dist/storage/repositories/registry-cache.js +0 -113
  378. package/dist/tasks/scheduler-sync-preview.js +0 -52
  379. package/dist/tasks/source/task-to-v3.js +0 -507
  380. package/dist/workflows/freeze/resolve-steps.js +0 -86
  381. package/dist/workflows/freeze/source-freeze.js +0 -64
  382. package/dist/workflows/ir/compile.js +0 -321
  383. package/dist/workflows/ir/environment-v4.js +0 -330
  384. package/dist/workflows/ir/freeze-v4.js +0 -153
  385. package/dist/workflows/ir/schema-v4.js +0 -745
  386. package/dist/workflows/ir/schema.js +0 -354
  387. package/dist/workflows/program/schema.js +0 -78
  388. package/dist/workflows/runtime/checkin.js +0 -57
  389. package/dist/workflows/runtime/plan-classifier.js +0 -196
  390. package/dist/workflows/runtime/unit-checkin.js +0 -45
  391. package/dist/workflows/runtime/unit-phases.js +0 -20
  392. package/dist/workflows/schema.js +0 -4
  393. package/dist/workflows/source-ir/compile.js +0 -200
  394. package/dist/workflows/source-ir/program.js +0 -50
  395. package/dist/workflows/source-ir/result.js +0 -26
  396. package/dist/workflows/source-ir/schema.js +0 -786
  397. package/dist/workflows/source-ir/triggers.js +0 -79
  398. package/dist/workflows/source-ir/uses.js +0 -40
  399. package/dist/workflows/validator.js +0 -60
@@ -5,14 +5,13 @@ import fs from "node:fs";
5
5
  import path from "node:path";
6
6
  import { daysToMs, resolveStashDir } from "../core/common.js";
7
7
  import { loadConfig } from "../core/config/config.js";
8
- import { ConfigError, UsageError } from "../core/errors.js";
9
- import { readEvents } from "../core/events.js";
10
- import { openLogsDatabase } from "../core/logs-db.js";
8
+ import { ConfigError, rethrowIfTestIsolationError, UsageError } from "../core/errors.js";
11
9
  import { classifyPathAccess, describeInaccessiblePath } from "../core/path-access.js";
12
10
  import { getConfigPath, getDataDir, getDbPath, getStateDbPathInDataDir } from "../core/paths.js";
13
- import { listExistingTableNames, listPendingStateMigrations, openStateDatabase } from "../core/state-db.js";
11
+ import { listExistingTableNames, listPendingStateMigrations, openStateDatabaseWithReport, withStateDb, } from "../core/state-db.js";
14
12
  import { DURATION_UNITS, parseDuration, parseSinceToIso } from "../core/time.js";
15
13
  import { probeLlmEndpoint } from "../llm/client.js";
14
+ import { insertEvent } from "../storage/repositories/events-repository.js";
16
15
  import { getExtractOutcomeCountsSince } from "../storage/repositories/extract-sessions-repository.js";
17
16
  import { countImproveRunsSince } from "../storage/repositories/improve-runs-repository.js";
18
17
  import { closeDatabase, openReadonlyExistingDatabase } from "../storage/repositories/index-connection.js";
@@ -20,22 +19,21 @@ import { getAllEntries } from "../storage/repositories/index-entries-repository.
20
19
  import { queryTaskHistory } from "../storage/repositories/task-history-repository.js";
21
20
  import { getStateDbFreelistInfo, runStateDbQuickCheck } from "../storage/state-db-integrity.js";
22
21
  import { pkgVersion } from "../version.js";
23
- import { collectImproveAdvisories } from "./health/advisories.js";
22
+ import { collectArchiveUsageAdvisory } from "./health/archive-usage.js";
24
23
  import { HEALTH_CHECKS, probeActiveImproveStrategy, runHealthEngineProbes, runPendingStateMigrationsCheck, SESSION_EXTRACTION_LEDGER_WINDOW_DAYS, } from "./health/checks.js";
24
+ import { collectConfigSkewAdvisory } from "./health/config-skew.js";
25
25
  import { collectDataDirUsageAdvisory } from "./health/data-dir-usage.js";
26
+ import { collectEgressAdvisory } from "./health/egress.js";
26
27
  import { engineLastUsedSince, readLastEngineUsage } from "./health/engine-usage.js";
27
- import { buildImproveSkipSummary, computeWallTimeStats, countAgentFailureReasons, isAgentTaskHistoryRow, roundRate, summarizeImproveCompleted, summarizeImproveRuns, taskFailureDetail, } from "./health/improve-metrics.js";
28
+ import { emptyImproveMetrics, roundRate } from "./health/improve-metrics.js";
28
29
  import { emptyLlmUsageAggregate, readLlmUsageAggregate } from "./health/llm-usage.js";
29
- import { computeDegradationMetrics, computeDenominatorFixedCoverage, computeEnrichmentMintingRollup, probeStateDbRoundTrip, } from "./health/metrics.js";
30
30
  import { collectPluginStalenessAdvisories } from "./health/plugin-staleness.js";
31
31
  import { collectSchedulerBinaryAdvisory } from "./health/scheduler-binary.js";
32
32
  import { collectStashExposureAdvisory } from "./health/stash-exposure.js";
33
- import { collectSurfacesAdvisories } from "./health/surfaces.js";
34
- import { buildPerRunSummaries } from "./health/task-runs.js";
35
33
  import { buildTypeDirectoryAdvisory } from "./health/type-directory-check.js";
36
- import { ACTIVE_RUN_WARN_MS, IMPROVE_COMPLETED_EVENT, MIN_ROWS_FOR_WORST_TASK_FAIL_RATE, } from "./health/types.js";
34
+ import { MIN_ROWS_FOR_WORST_TASK_FAIL_RATE, } from "./health/types.js";
37
35
  import { collectVersionDriftAdvisory } from "./health/version-drift.js";
38
- import { buildWindowMetrics, computeDeltas, partitionLogBackedRows, resolveWindowCompare } from "./health/windows.js";
36
+ import { buildImproveWindowSummary, buildWindowMetrics, computeDeltas, computeTaskWindowRates, resolveWindowCompare, } from "./health/windows.js";
39
37
  const DEFAULT_SINCE_MS = 24 * 60 * 60 * 1000;
40
38
  export function parseHealthSince(since) {
41
39
  if (since === undefined || since.trim() === "") {
@@ -72,6 +70,65 @@ function validateAkmHealthOptions(options) {
72
70
  }
73
71
  }
74
72
  }
73
+ // ── akmHealth phase helpers (chunk-9 WI-9.5b; file-level decompose following
74
+ // the function's natural gather/advise/check/assemble phases) ───────────────
75
+ /** Event type appended + read back by the state.db round-trip probe. */
76
+ const HEALTH_PROBE_EVENT = "health_probe";
77
+ /** Synthetic sentinel ref (ref-grammar decision D-R3): a colon-free
78
+ * `<subsystem>/_<marker>` label. `health` has no asset stash-subdir, so
79
+ * `health/_probe` names the subsystem. */
80
+ const HEALTH_PROBE_REF = "health/_probe";
81
+ /**
82
+ * Verify state.db can accept a write and read it back — WITHOUT leaving any
83
+ * permanent trace (R-030). Earlier versions appended a `health_probe` event
84
+ * on every `akm health` invocation and never removed it: a read-only health
85
+ * check ran on a cron would grow state.db without bound (the only purge is
86
+ * `improve`'s retention pass, which a health-only user never runs). The probe
87
+ * row inserted here is deleted again inside the SAME connection once the
88
+ * round trip is confirmed, so the net effect on the `events` table is always
89
+ * zero rows — the round trip still genuinely exercises append + read against
90
+ * the real table, it just doesn't accumulate.
91
+ */
92
+ export function probeStateDbRoundTrip(stateDbPath) {
93
+ const started = Date.now();
94
+ try {
95
+ return withStateDb((db) => {
96
+ const ts = new Date().toISOString();
97
+ const insertedId = insertEvent(db, {
98
+ eventType: HEALTH_PROBE_EVENT,
99
+ ts,
100
+ ref: HEALTH_PROBE_REF,
101
+ metadata: { source: "akm health" },
102
+ });
103
+ const durationMs = Date.now() - started;
104
+ if (insertedId === undefined) {
105
+ return { ok: false, durationMs, error: "probe event insert did not return a row id" };
106
+ }
107
+ // The round-trip matches on the exact (id, eventType, ref) triple
108
+ // written above, then removes the row regardless of outcome — a
109
+ // failed round trip must not leak a row any more than a successful
110
+ // one should.
111
+ let roundTripOk = false;
112
+ try {
113
+ const row = db
114
+ .prepare("SELECT 1 AS present FROM events WHERE id = ? AND event_type = ? AND ref = ?")
115
+ .get(insertedId, HEALTH_PROBE_EVENT, HEALTH_PROBE_REF);
116
+ roundTripOk = row !== undefined;
117
+ }
118
+ finally {
119
+ db.prepare("DELETE FROM events WHERE id = ?").run(insertedId);
120
+ }
121
+ if (!roundTripOk) {
122
+ return { ok: false, durationMs, error: "probe event was not readable after append" };
123
+ }
124
+ return { ok: true, durationMs };
125
+ }, { path: stateDbPath });
126
+ }
127
+ catch (err) {
128
+ rethrowIfTestIsolationError(err);
129
+ return { ok: false, durationMs: Date.now() - started, error: err instanceof Error ? err.message : String(err) };
130
+ }
131
+ }
75
132
  /**
76
133
  * Item 7: dedupe stuck-active rows by task_id (keeping the oldest/largest age
77
134
  * per id) so the `active-runs` check can name WHICH tasks are stuck instead of
@@ -119,7 +176,7 @@ function computeWorstTaskFailRate(rows) {
119
176
  return worst;
120
177
  }
121
178
  /** Table presence, the state.db round-trip probe, and task_history-derived rates. */
122
- function gatherTaskHistoryPhase(db, logsDb, since, stateDbPath, now) {
179
+ function gatherTaskHistoryPhase(db, since, stateDbPath, now) {
123
180
  const tables = listExistingTableNames(db, ["events", "task_history", "proposals", "schema_migrations"]);
124
181
  const tableNames = tables.map((row) => row.name).sort();
125
182
  const requiredTables = ["events", "proposals", "schema_migrations", "task_history"];
@@ -130,23 +187,13 @@ function gatherTaskHistoryPhase(db, logsDb, since, stateDbPath, now) {
130
187
  // bad index entry counts), and the freelist reading is purely informational.
131
188
  const stateDbIntegrity = runStateDbQuickCheck(stateDbPath);
132
189
  const stateDbFreelist = getStateDbFreelistInfo(stateDbPath);
133
- const taskRows = queryTaskHistory(db, { since });
134
- const { withLogs: taskRowsWithLogs, backed: existingLogRows } = partitionLogBackedRows(taskRows, logsDb);
135
- const failedTaskRows = taskRows.filter((row) => row.status === "failed");
136
- const activeRows = taskRows.filter((row) => row.status === "active" && row.completed_at === null);
137
- const stuckActiveRows = activeRows.filter((row) => now() - new Date(row.started_at).getTime() > ACTIVE_RUN_WARN_MS);
138
190
  // D8 (spec §5.3): a marked "command" row or a legacy (unmarked) "prompt"
139
191
  // row is the agent/LLM arm; an unmarked "command" row is the legacy
140
192
  // native shell/script arm and must not be counted here (see
141
- // isAgentTaskHistoryRow's header comment for the full mapping).
142
- const agentRows = taskRows.filter((row) => isAgentTaskHistoryRow(row));
143
- const agentFailures = agentRows.filter((row) => {
144
- const detail = taskFailureDetail(row);
145
- return typeof detail?.reason === "string" && detail.reason.length > 0;
146
- });
147
- const logBackingRate = taskRowsWithLogs.length === 0 ? 1 : existingLogRows.length / taskRowsWithLogs.length;
148
- const taskFailRate = taskRows.length === 0 ? 0 : failedTaskRows.length / taskRows.length;
149
- const agentFailureRate = agentRows.length === 0 ? 0 : agentFailures.length / agentRows.length;
193
+ // isAgentTaskHistoryRow's header comment, referenced from
194
+ // computeTaskWindowRates, for the full mapping).
195
+ const taskRows = queryTaskHistory(db, { since });
196
+ const rates = computeTaskWindowRates(taskRows, now);
150
197
  return {
151
198
  tableNames,
152
199
  missingTables,
@@ -154,15 +201,12 @@ function gatherTaskHistoryPhase(db, logsDb, since, stateDbPath, now) {
154
201
  stateDbIntegrity,
155
202
  stateDbFreelist,
156
203
  taskRowCount: taskRows.length,
157
- taskRowsWithLogsCount: taskRowsWithLogs.length,
158
- existingLogRowsCount: existingLogRows.length,
159
- stuckActiveRuns: stuckActiveRows.length,
160
- stuckActiveTasks: dedupeStuckActiveTasks(stuckActiveRows, now),
161
- logBackingRate,
162
- taskFailRate,
204
+ stuckActiveRuns: rates.stuckActiveRuns,
205
+ stuckActiveTasks: dedupeStuckActiveTasks(rates.stuckActiveRows, now),
206
+ taskFailRate: rates.taskFailRate,
163
207
  worstTaskFailRate: computeWorstTaskFailRate(taskRows),
164
- agentFailureRate,
165
- agentFailureReasonCounts: countAgentFailureReasons(agentFailures),
208
+ agentFailureRate: rates.agentFailureRate,
209
+ agentFailureReasonCounts: rates.agentFailureReasonCounts,
166
210
  };
167
211
  }
168
212
  /**
@@ -191,47 +235,16 @@ function gatherSessionExtractionLedgerPhase(db, now) {
191
235
  return { since, rows: getExtractOutcomeCountsSince(db, since) };
192
236
  }
193
237
  /**
194
- * Assemble the window's improve-pipeline summary: invoked/completed/skipped
195
- * counts from events, the per-run result_json aggregate, wall-time stats, and
196
- * the WS-5 coverage/degradation/enrichment-minting rollups.
197
- */
198
- function gatherImproveSummaryPhase(db, stateDbPath, since, now) {
199
- const improveInvoked = readEvents({ since, type: "improve_invoked" }, { dbPath: stateDbPath }).events.length;
200
- const improveCompletedEvents = readEvents({ since, type: IMPROVE_COMPLETED_EVENT }, { dbPath: stateDbPath }).events;
201
- const improveSkippedEvents = readEvents({ since, type: "improve_skipped" }, { dbPath: stateDbPath }).events;
202
- const eventsMetrics = summarizeImproveCompleted(improveCompletedEvents);
203
- const { metrics: improveSummary } = summarizeImproveRuns(db, since);
204
- improveSummary.invoked = improveInvoked;
205
- improveSummary.completed = eventsMetrics.completed;
206
- const skipSummary = buildImproveSkipSummary(improveSkippedEvents);
207
- improveSummary.skipped = skipSummary.skipped;
208
- improveSummary.skipReasons = skipSummary.skipReasons;
209
- const perRunSummaries = buildPerRunSummaries(db, since);
210
- const wallTimes = perRunSummaries.map((run) => run.wallTimeMs).filter((ms) => Number.isFinite(ms) && ms > 0);
211
- improveSummary.wallTime = computeWallTimeStats(wallTimes, improveSummary.wallTime.byPhase);
212
- // WS-5: Compute denominator-fixed coverage and per-run degradation metrics
213
- // for the main health path (not just window-compare mode).
214
- const until = new Date(now()).toISOString();
215
- const totalAssetsMain = improveSummary.memorySummary.eligible + improveSummary.memorySummary.derived;
216
- improveSummary.coverage = computeDenominatorFixedCoverage(db, totalAssetsMain, improveSummary.memorySummary.eligible, since, until);
217
- const degradationMain = computeDegradationMetrics(db, since, until);
218
- if (degradationMain) {
219
- improveSummary.degradation = degradationMain;
220
- }
221
- improveSummary.enrichmentMinting = computeEnrichmentMintingRollup(db, since, until);
222
- return { improveSummary, perRunSummaries };
223
- }
224
- /**
225
- * The best-effort advisory groups beyond the health-check registry: improve
226
- * advisories, the `stash-git-exposure` probe, the 08 surfaces group
227
- * (binary-config-skew, egress-endpoints), `type-directory-disagreement`
228
- * (#831), `data-dir-usage` (#896), and `plugin-version` (itlackey/akm#832).
229
- * Order matches emission order in the returned array. A probe/filesystem
230
- * failure in any try/catch must not abort the health report — each group
231
- * degrades to "no advisory" independently.
238
+ * The best-effort advisory groups beyond the health-check registry: the
239
+ * `stash-git-exposure` probe, the 08 surfaces group (binary-config-skew,
240
+ * egress-endpoints), `type-directory-disagreement` (#831), `data-dir-usage`
241
+ * (#896), and `plugin-version` (itlackey/akm#832). Order matches emission
242
+ * order in the returned array. A probe/filesystem failure in any try/catch
243
+ * must not abort the health report — each group degrades to "no advisory"
244
+ * independently.
232
245
  */
233
- function gatherAncillaryAdvisories(db, stateDbPath, since, improveSummary, options, egressConfigView) {
234
- const advisories = [...collectImproveAdvisories(db, stateDbPath, since, improveSummary)];
246
+ function gatherAncillaryAdvisories(db, options, egressConfigView) {
247
+ const advisories = [];
235
248
  const indexStateMismatch = detectIndexStateGenerationMismatch(db);
236
249
  if (indexStateMismatch)
237
250
  advisories.push(indexStateMismatch);
@@ -258,10 +271,12 @@ function gatherAncillaryAdvisories(db, stateDbPath, since, improveSummary, optio
258
271
  // egress-endpoints). Best-effort — a filesystem probe failure must not abort
259
272
  // the health report.
260
273
  try {
261
- advisories.push(...collectSurfacesAdvisories({
262
- configPath: getConfigPath(),
263
- config: egressConfigView,
264
- }));
274
+ const configSkew = collectConfigSkewAdvisory(getConfigPath());
275
+ if (configSkew)
276
+ advisories.push(configSkew);
277
+ const egress = collectEgressAdvisory(egressConfigView);
278
+ if (egress)
279
+ advisories.push(egress);
265
280
  }
266
281
  catch {
267
282
  // Non-fatal.
@@ -277,6 +292,18 @@ function gatherAncillaryAdvisories(db, stateDbPath, since, improveSummary, optio
277
292
  catch {
278
293
  // Non-fatal.
279
294
  }
295
+ // Item 4 (alpha.9 plan §5.4/§8 step 8): a bundle with no git history of its
296
+ // own never gets the purge sweep, so its memory-cleanup archive only ever
297
+ // grows — report its size and file count instead. Best-effort — an
298
+ // unreadable archive must not abort the health report.
299
+ try {
300
+ const archiveUsage = collectArchiveUsageAdvisory(options.stashDir ?? resolveStashDir());
301
+ if (archiveUsage)
302
+ advisories.push(archiveUsage);
303
+ }
304
+ catch {
305
+ // Non-fatal.
306
+ }
280
307
  // #896: report the data dir's total size and its largest top-level
281
308
  // subdirectory, so a disk-usage blowup (e.g. unpruned migration snapshot
282
309
  // backups, #897) is self-diagnosing instead of requiring `du` archaeology.
@@ -399,7 +426,7 @@ function detectIndexStateGenerationMismatch(stateDb) {
399
426
  * per-window bundles and computes deltas between the earliest and latest
400
427
  * window. Top-level metrics retain the primary `--since` query.
401
428
  */
402
- function resolveWindowComparePhase(options, db, stateDbPath, now, logsDb) {
429
+ function resolveWindowComparePhase(options, db, stateDbPath, now) {
403
430
  let windowSpecs;
404
431
  if (options.windowCompare) {
405
432
  windowSpecs = resolveWindowCompare(options.windowCompare, now);
@@ -413,7 +440,7 @@ function resolveWindowComparePhase(options, db, stateDbPath, now, logsDb) {
413
440
  windowResults = windowSpecs.map((spec) => {
414
441
  const winSince = parseHealthSince(spec.since);
415
442
  const winUntil = spec.until ? parseHealthSince(spec.until) : new Date(now()).toISOString();
416
- const bundle = buildWindowMetrics(db, stateDbPath, winSince, winUntil, now, logsDb);
443
+ const bundle = buildWindowMetrics(db, stateDbPath, winSince, winUntil, now);
417
444
  return {
418
445
  name: spec.name,
419
446
  since: winSince,
@@ -438,19 +465,13 @@ function resolveWindowComparePhase(options, db, stateDbPath, now, logsDb) {
438
465
  return { windowResults, deltas };
439
466
  }
440
467
  /**
441
- * The health report for a state.db a managed open cannot reach at all: either
442
- * the file is not readable (#791), or — the same shape, a different cause —
443
- * it holds a pending migration the managed open refuses to apply without
444
- * deliberate consent (`akm upgrade` / `akm migrate apply` are the
445
- * only two callers allowed to admit a historical-destructive migration; see
446
- * `beforeMigrationLocked` in `src/core/state/migrations.ts`).
468
+ * The health report for a state.db the open cannot reach at all: either the
469
+ * file is not readable (#791), or — the same shape, a different cause — the
470
+ * open failed while applying a pending migration, which is left pending.
447
471
  *
448
472
  * `akm health` is what an operator (or a bundler's boot check) runs when
449
473
  * something else is misbehaving, so it must survive either problem long
450
- * enough to NAME it. Previously an unreadable file was already handled this
451
- * way, but a pending migration was not: the managed open's refusal escaped as
452
- * a thrown `ConfigError` (exit 78) and crashed the whole command before any
453
- * check — including this one — could report anything.
474
+ * enough to NAME it rather than exit 78 before any check could report.
454
475
  *
455
476
  * Reported as a single hard-channel `fail` check — the run genuinely could
456
477
  * not open state.db, so every check that depends on it is skipped rather than
@@ -470,13 +491,9 @@ function degradedStateDbReport(hardCheck, options) {
470
491
  agentFailureRate: 0,
471
492
  agentFailureReasonCounts: {},
472
493
  stuckActiveRuns: 0,
473
- logBackingRate: 0,
474
- // `null`, not 0: the round-trip probe did not run, which is not the same
475
- // as it running instantly.
476
- probeRoundTripMs: null,
477
494
  llmUsage: emptyLlmUsageAggregate(),
478
495
  },
479
- improve: summarizeImproveCompleted([]),
496
+ improve: emptyImproveMetrics(),
480
497
  };
481
498
  }
482
499
  function unreadableStateDbCheck(detail) {
@@ -498,29 +515,25 @@ export async function akmHealth(options = {}) {
498
515
  const stateDbPath = options.stateDbPath ?? getStateDbPathInDataDir();
499
516
  const hardChecks = [];
500
517
  const advisories = [];
501
- // #791: an UNREADABLE state.db, or one with a pending migration the
502
- // managed open refuses to apply, are the two failures `akm health` most
503
- // needs to be able to report, because this is the command an operator (or a
504
- // bundler's boot check) runs to find out why everything else is behaving
505
- // oddly. Dying here meant health could not diagnose that state at all — not
506
- // even the checks that never touch state.db got to run. Report it as a
507
- // finding instead.
508
- let db;
518
+ // #791: an UNREADABLE state.db, or one whose pending migration failed to
519
+ // apply, are the two failures `akm health` most needs to be able to report,
520
+ // because this is the command an operator (or a bundler's boot check) runs
521
+ // to find out why everything else is behaving oddly. Report it as a finding
522
+ // instead of dying before any check runs. The open itself applies every
523
+ // pending migration; `state-db-migrations` reports what it applied.
524
+ let opened;
509
525
  try {
510
- db = openStateDatabase(stateDbPath);
526
+ opened = openStateDatabaseWithReport(stateDbPath);
511
527
  }
512
528
  catch (error) {
513
529
  const { access, code } = classifyPathAccess(stateDbPath);
514
530
  if (access === "inaccessible") {
515
531
  return degradedStateDbReport(unreadableStateDbCheck(describeInaccessiblePath(stateDbPath, code)), options);
516
532
  }
517
- // The managed open's refusal of a pending historical-destructive
518
- // migration is a plain `Error`, not a distinguishable error class — so
519
- // confirm the cause via the read-only preflight (`listPendingStateMigrations`,
520
- // which never applies anything) rather than pattern-matching the message.
521
- // A ledger too broken to enumerate at all throws here too; in that case
522
- // fall through to the generic config-error report below, since it is a
523
- // genuinely different, rarer failure this check cannot explain.
533
+ // A migration that failed to apply rolled back and is still pending:
534
+ // name it from a read-only listing (which never applies anything) rather
535
+ // than pattern-matching the error text. A ledger too broken to enumerate
536
+ // throws here too and falls through to the generic config error below.
524
537
  let pendingMigrationsCheck;
525
538
  try {
526
539
  pendingMigrationsCheck = runPendingStateMigrationsCheck(stateDbPath, { listPendingStateMigrations });
@@ -533,16 +546,7 @@ export async function akmHealth(options = {}) {
533
546
  }
534
547
  throw new ConfigError(`Unable to open state.db: ${error instanceof Error ? error.message : String(error)}`, "INVALID_CONFIG_FILE");
535
548
  }
536
- // logs.db backs the log-backing metric (#579). Best-effort: when it cannot
537
- // be opened, partitionLogBackedRows falls back to the on-disk file check, so
538
- // health never hard-fails on a missing/locked logs database.
539
- let logsDb;
540
- try {
541
- logsDb = openLogsDatabase(options.logsDbPath);
542
- }
543
- catch {
544
- logsDb = undefined;
545
- }
549
+ const { db } = opened;
546
550
  try {
547
551
  // Network probes overlap the local database phases below; awaited where consumed.
548
552
  const engineProbesPromise = runHealthEngineProbes({ probeReachable: options.probe ? probeLlmEndpoint : undefined });
@@ -557,11 +561,15 @@ export async function akmHealth(options = {}) {
557
561
  cliVersion: pkgVersion,
558
562
  });
559
563
  schedulerBinaryDriftPromise.catch(() => undefined);
560
- const taskHistory = gatherTaskHistoryPhase(db, logsDb, since, stateDbPath, now);
564
+ const taskHistory = gatherTaskHistoryPhase(db, since, stateDbPath, now);
561
565
  const { tableNames, missingTables, probe } = taskHistory;
562
566
  const { egressConfigView, thinkingOffEngines } = gatherEgressConfigPhase();
563
- const { improveSummary } = gatherImproveSummaryPhase(db, stateDbPath, since, now);
564
- advisories.push(...gatherAncillaryAdvisories(db, stateDbPath, since, improveSummary, options, egressConfigView));
567
+ // Same window bundle `--window-compare` builds per-window (windows.ts) —
568
+ // reused here for the main `--since` window's improve summary and for
569
+ // `--group-by run`, so neither re-reads `improve_runs`.
570
+ const until = new Date(now()).toISOString();
571
+ const { improve: improveSummary, perRunSummaries } = buildImproveWindowSummary(db, stateDbPath, since, until);
572
+ advisories.push(...gatherAncillaryAdvisories(db, options, egressConfigView));
565
573
  const sessionExtractionLedger = gatherSessionExtractionLedgerPhase(db, now);
566
574
  // #950: computed once (no IO beyond config/env, same as gatherEgressConfigPhase)
567
575
  // so `active-improve-strategy` and `engine-last-used` project the same
@@ -590,16 +598,13 @@ export async function akmHealth(options = {}) {
590
598
  probe,
591
599
  stateDbIntegrity: taskHistory.stateDbIntegrity,
592
600
  stateDbFreelist: taskHistory.stateDbFreelist,
601
+ stateDbMigrations: { applied: opened.applied, ...(opened.backupPath ? { backupPath: opened.backupPath } : {}) },
593
602
  taskRowCount: taskHistory.taskRowCount,
594
603
  taskFailRate: taskHistory.taskFailRate,
595
- taskRowsWithLogsCount: taskHistory.taskRowsWithLogsCount,
596
- existingLogRowsCount: taskHistory.existingLogRowsCount,
597
- logBackingRate: taskHistory.logBackingRate,
598
604
  stuckActiveRuns: taskHistory.stuckActiveRuns,
599
605
  stuckActiveTasks: taskHistory.stuckActiveTasks,
600
606
  worstTaskFailRate: taskHistory.worstTaskFailRate,
601
607
  agentFailureReasonCounts: taskHistory.agentFailureReasonCounts,
602
- sessionExtraction: improveSummary.sessionExtraction,
603
608
  sessionExtractionLedger,
604
609
  autoAccept: improveSummary.autoAccept,
605
610
  engineProbes,
@@ -624,20 +629,15 @@ export async function akmHealth(options = {}) {
624
629
  agentFailureRate: roundRate(taskHistory.agentFailureRate),
625
630
  agentFailureReasonCounts: taskHistory.agentFailureReasonCounts,
626
631
  stuckActiveRuns: taskHistory.stuckActiveRuns,
627
- logBackingRate: roundRate(taskHistory.logBackingRate),
628
- probeRoundTripMs: probe.durationMs,
629
632
  llmUsage,
630
633
  };
631
634
  const hardFailure = hardChecks.some((check) => check.status === "fail");
632
635
  const deterministicWarnings = [...hardChecks, ...advisories].some((check) => check.status === "warn" && check.kind === "deterministic");
633
636
  const status = hardFailure ? "fail" : deterministicWarnings ? "warn" : "pass";
634
637
  // ── Window-compare mode (Phase 3) ─────────────────────────────────────
635
- const { windowResults, deltas } = resolveWindowComparePhase(options, db, stateDbPath, now, logsDb);
638
+ const { windowResults, deltas } = resolveWindowComparePhase(options, db, stateDbPath, now);
636
639
  // ── Per-run mode (Phase 2) ────────────────────────────────────────────
637
- let runs;
638
- if (options.groupBy === "run") {
639
- runs = buildPerRunSummaries(db, since);
640
- }
640
+ const runs = options.groupBy === "run" ? perRunSummaries : undefined;
641
641
  return {
642
642
  schemaVersion: 3,
643
643
  ok: !hardFailure,
@@ -654,14 +654,6 @@ export async function akmHealth(options = {}) {
654
654
  }
655
655
  finally {
656
656
  db.close();
657
- if (logsDb) {
658
- try {
659
- logsDb.close();
660
- }
661
- catch {
662
- // best-effort
663
- }
664
- }
665
657
  }
666
658
  }
667
659
  // Markdown renderers (renderRunsDetailMd / renderWindowCompareMd) live in
@@ -1,129 +1,54 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
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
- // Chunk sizing + per-chunk prompt assembly for consolidate. Pure token math and
5
- // prompt-string construction over MemoryEntry inputs — no LLM call, no embedder,
6
- // no orchestrator coupling.
4
+ /** Consolidate's chunk sizing and per-chunk prompt (pure token math and text). */
7
5
  import fs from "node:fs";
8
6
  import { parseFrontmatter } from "../../../core/asset/frontmatter.js";
9
- import { cacheHash, stripFrontmatterBody } from "../content-hash.js";
10
- /**
11
- * Conservative chars-per-token estimate used when computing prompt budgets.
12
- * English text averages roughly 4 chars/token for most LLM tokenizers. We use
13
- * 3 to stay conservative (shorter tokens = more tokens per char). Exported so
14
- * other context-length-driven budgets (e.g. reflect's direct-LLM content cap,
15
- * #952) share this single estimate instead of redeclaring it.
16
- */
7
+ import { contentHash, stripFrontmatterBody } from "../content-hash.js";
8
+ /** A conservative chars-per-token estimate, shared by every context-length budget. */
17
9
  export const CHARS_PER_TOKEN = 3;
18
- /**
19
- * Overhead budget reserved for the system prompt, chunk header lines, and per-
20
- * memory metadata lines (name, description, tags, separator). Measured at
21
- * roughly 600 chars for the system prompt + ~100 chars of header + ~50 chars
22
- * per memory × chunk size. We round up to 2 000 tokens to leave room for the
23
- * model's own output.
24
- */
10
+ /** System prompt, headers and per-memory metadata, with room for the output. */
25
11
  const PROMPT_OVERHEAD_TOKENS = 2_000;
26
12
  /**
27
- * Default effective token budget used when the default LLM profile's
28
- * `contextLength` is not set. This is intentionally conservative (4 096)
29
- * rather than being set to the model's actual context window, because:
30
- *
31
- * - When the agent path is used, the agent CLI (e.g. opencode)
32
- * prepends its own large system prompt + conversation history before
33
- * forwarding to the model. That overhead easily consumes 30K+ tokens on
34
- * a model with a 16K context window, leaving very little room for
35
- * chunk content.
36
- * - When the HTTP path is used (an LLM profile is selected), only the akm
37
- * system prompt and user prompt are sent, so the budget can be set to the
38
- * model's actual context length via the selected LLM engine's contextLength.
39
- *
40
- * Set the selected LLM engine's contextLength in your config file to the
41
- * model's actual context window to allow larger chunks on the HTTP path.
13
+ * The budget when the engine sets no `contextLength`. Conservative on purpose:
14
+ * an agent CLI prepends its own large prompt; set `contextLength` on a direct
15
+ * LLM engine to allow bigger chunks.
42
16
  */
43
17
  export const DEFAULT_CONTEXT_LENGTH_TOKENS = 4_096;
44
- /**
45
- * Given the model's context window and the per-memory body truncation limit,
46
- * return the maximum number of memories that can safely fit in one chunk
47
- * without the prompt overflowing the context window.
48
- *
49
- * The formula is:
50
- * usableTokens = contextLength - PROMPT_OVERHEAD_TOKENS
51
- * tokensPerMemory = ceil(bodyTruncation / CHARS_PER_TOKEN)
52
- * chunkSize = floor(usableTokens / tokensPerMemory)
53
- *
54
- * Result is clamped between 1 and 50 to avoid degenerate values.
55
- *
56
- * @param contextLength - Model context window in tokens.
57
- * @param bodyTruncation - Max chars per memory body included in the prompt.
58
- * @param maxChunkSize - Optional override for the hardcoded cap of 50 (1–50).
59
- */
18
+ /** Memories per chunk: `(contextLength − overhead) / ceil(bodyTruncation / CHARS_PER_TOKEN)`, within 1…cap (50). */
60
19
  export function computeSafeChunkSize(contextLength, bodyTruncation, maxChunkSize) {
61
20
  const usableTokens = Math.max(contextLength - PROMPT_OVERHEAD_TOKENS, 0);
62
21
  const tokensPerMemory = Math.max(Math.ceil(bodyTruncation / CHARS_PER_TOKEN), 1);
63
- const raw = Math.floor(usableTokens / tokensPerMemory);
64
- return Math.max(1, Math.min(maxChunkSize ?? 50, raw));
22
+ return Math.max(1, Math.min(maxChunkSize ?? 50, Math.floor(usableTokens / tokensPerMemory)));
65
23
  }
66
24
  /**
67
- * Build the per-chunk user prompt fed to the consolidate LLM.
68
- *
69
- * Each memory is annotated with two flags:
70
- * - `(captureMode: hot)` — user-explicit memory, surfaced to the model as
71
- * provenance context.
72
- * - `(already queued)` — the memory's body hash matches a pending
73
- * consolidate proposal; the system prompt's PROMOTE rule forbids
74
- * proposing promote for these. ~107/4h before this annotation.
75
- *
76
- * Both annotations are visible to the LLM. `pendingProposalBodyHashes`
77
- * is precomputed once per run by `loadPendingConsolidateProposalHashes`
78
- * so the cost stays O(memories) inside the chunk loop.
25
+ * One chunk's user prompt. Each memory shows its body only (frontmatter can
26
+ * outrun the truncation and leave the model judging metadata), marked
27
+ * `captureMode: hot` when the user captured it explicitly and `already queued`
28
+ * when a pending consolidate proposal carries its body hash — the system
29
+ * prompt forbids promoting those.
79
30
  */
80
31
  export function buildChunkPrompt(sourceName, memories, chunkIndex, totalChunks, bodyTruncation, pendingProposalBodyHashes = new Set()) {
81
- const annotationsByIndex = [];
82
- for (const m of memories) {
83
- let body = "";
32
+ const lines = [
33
+ `Source: ${sourceName}`,
34
+ `Chunk ${chunkIndex + 1} of ${totalChunks} (${memories.length} memories):`,
35
+ "",
36
+ ];
37
+ memories.forEach((m, i) => {
38
+ let body;
84
39
  try {
85
40
  body = fs.readFileSync(m.filePath, "utf8");
86
41
  }
87
42
  catch {
88
43
  body = "(unreadable)";
89
44
  }
90
- // Hot/queued detection stays on the raw body — frontmatter is exactly
91
- // what parseFrontmatter and the pending-proposal hash domain need.
92
- const parsed = parseFrontmatter(body);
93
- const isHot = parsed.data.captureMode === "hot";
94
- // Use cacheHash (case-preserving stripped body) to match the domain used
95
- // by loadPendingConsolidateProposalHashes and the body-embedding cache.
96
- const bodyHash = cacheHash(body);
97
- const isAlreadyQueued = pendingProposalBodyHashes.has(bodyHash);
98
- // The excerpt shown to the LLM is the body ONLY — frontmatter can run
99
- // longer than bodyTruncation (~21% of memories), which used to leave the
100
- // model judging metadata instead of content. stripFrontmatterBody falls
101
- // back to raw.trim() on unparseable/absent frontmatter, so this is a
102
- // no-op for the "(unreadable)" placeholder.
103
- const excerpt = stripFrontmatterBody(body);
104
- annotationsByIndex.push({ isHot, isAlreadyQueued, excerpt });
105
- }
106
- const lines = [
107
- `Source: ${sourceName}`,
108
- `Chunk ${chunkIndex + 1} of ${totalChunks} (${memories.length} memories):`,
109
- "",
110
- ];
111
- for (let i = 0; i < memories.length; i++) {
112
- const m = memories[i];
113
- // `annotationsByIndex` has exactly one entry per memory (built in the loop above).
114
- const { isHot, isAlreadyQueued, excerpt } = annotationsByIndex[i];
115
45
  const annotations = [];
116
- if (isHot)
46
+ if (parseFrontmatter(body).data.captureMode === "hot")
117
47
  annotations.push("captureMode: hot");
118
- if (isAlreadyQueued)
48
+ if (pendingProposalBodyHashes.has(contentHash(body, "body")))
119
49
  annotations.push("already queued");
120
50
  const annotationSuffix = annotations.length > 0 ? ` (${annotations.join("; ")})` : "";
121
- lines.push(`[${i + 1}] memories/${m.name}${annotationSuffix}`);
122
- lines.push(`Description: ${m.description || "(none)"}`);
123
- lines.push(`Tags: ${m.tags.length > 0 ? m.tags.join(", ") : "(none)"}`);
124
- lines.push("---");
125
- lines.push(excerpt.slice(0, bodyTruncation));
126
- lines.push("");
127
- }
51
+ lines.push(`[${i + 1}] memories/${m.name}${annotationSuffix}`, `Description: ${m.description || "(none)"}`, `Tags: ${m.tags.length > 0 ? m.tags.join(", ") : "(none)"}`, "---", stripFrontmatterBody(body).slice(0, bodyTruncation), "");
52
+ });
128
53
  return lines.join("\n");
129
54
  }