akm-cli 0.9.16 → 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 (403) hide show
  1. package/CHANGELOG.md +2101 -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/consolidate-system.md +4 -11
  15. package/dist/assets/prompts/retrieval-relevance-judge.md +6 -0
  16. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +20 -20
  17. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -2
  18. package/dist/assets/templates/html/health.html +3 -5
  19. package/dist/cli/retired-commands.js +1 -1
  20. package/dist/cli/shared.js +6 -2
  21. package/dist/cli/unknown-flags.js +24 -1
  22. package/dist/cli.js +68 -10
  23. package/dist/commands/agent/agent-dispatch.js +1 -1
  24. package/dist/commands/command/command-execution.js +24 -62
  25. package/dist/commands/feedback-cli.js +0 -1
  26. package/dist/commands/health/accept-rate.js +6 -0
  27. package/dist/commands/health/archive-usage.js +92 -0
  28. package/dist/commands/health/checks.js +83 -74
  29. package/dist/commands/health/config-skew.js +38 -0
  30. package/dist/commands/health/data-dir-usage.js +25 -13
  31. package/dist/commands/health/egress.js +54 -0
  32. package/dist/commands/health/html-report.js +1 -42
  33. package/dist/commands/health/improve-metrics.js +136 -591
  34. package/dist/commands/health/md-report.js +1 -6
  35. package/dist/commands/health/plugin-staleness.js +53 -3
  36. package/dist/commands/health/renderers.js +12 -4
  37. package/dist/commands/health/report-view-model.js +14 -120
  38. package/dist/commands/health/types-improve.js +4 -19
  39. package/dist/commands/health/windows.js +64 -74
  40. package/dist/commands/health.js +145 -143
  41. package/dist/commands/improve/consolidate/chunking.js +26 -117
  42. package/dist/commands/improve/consolidate/continuity-check.js +137 -0
  43. package/dist/commands/improve/consolidate/pair-pass.js +791 -0
  44. package/dist/commands/improve/consolidate/sanitize.js +54 -149
  45. package/dist/commands/improve/consolidate.js +589 -1127
  46. package/dist/commands/improve/content-hash.js +16 -24
  47. package/dist/commands/improve/distill/content-repair.js +18 -100
  48. package/dist/commands/improve/distill-guards.js +20 -81
  49. package/dist/commands/improve/distill-promotion-policy.js +23 -243
  50. package/dist/commands/improve/distill.js +608 -1041
  51. package/dist/commands/improve/eligibility.js +126 -390
  52. package/dist/commands/improve/execution.js +8 -10
  53. package/dist/commands/improve/extract-prompt.js +1 -2
  54. package/dist/commands/improve/extract.js +487 -1046
  55. package/dist/commands/improve/feedback-valence.js +0 -25
  56. package/dist/commands/improve/improve-cli.js +75 -169
  57. package/dist/commands/improve/improve-result-file.js +10 -66
  58. package/dist/commands/improve/improve-strategies.js +52 -4
  59. package/dist/commands/improve/improve-usage-report.js +18 -64
  60. package/dist/commands/improve/improve.js +480 -1074
  61. package/dist/commands/improve/ledger.js +119 -0
  62. package/dist/commands/improve/locks.js +2 -8
  63. package/dist/commands/improve/loop-stages.js +415 -1073
  64. package/dist/commands/improve/memory/derived-ref.js +12 -77
  65. package/dist/commands/improve/memory/memory-belief.js +16 -118
  66. package/dist/commands/improve/memory/memory-improve.js +266 -14
  67. package/dist/commands/improve/outcome-loop.js +28 -156
  68. package/dist/commands/improve/planner.js +5 -15
  69. package/dist/commands/improve/preparation.js +779 -2319
  70. package/dist/commands/improve/proactive-maintenance.js +34 -101
  71. package/dist/commands/improve/reflect-noise.js +104 -280
  72. package/dist/commands/improve/reflect.js +642 -1353
  73. package/dist/commands/improve/retrieval-gate.js +127 -0
  74. package/dist/commands/improve/retrieval-scope.js +92 -0
  75. package/dist/commands/improve/salience.js +41 -240
  76. package/dist/commands/improve/session-asset.js +19 -100
  77. package/dist/commands/improve/stage.js +322 -0
  78. package/dist/commands/lint/base-linter.js +37 -15
  79. package/dist/commands/proposal/drain.js +261 -578
  80. package/dist/commands/proposal/proposal-cli.js +19 -20
  81. package/dist/commands/proposal/proposal-types.js +31 -24
  82. package/dist/commands/proposal/proposal.js +38 -8
  83. package/dist/commands/proposal/propose.js +134 -160
  84. package/dist/commands/proposal/repository.js +1097 -1394
  85. package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
  86. package/dist/commands/proposal/validators/proposal-validators.js +1 -1
  87. package/dist/commands/proposal/validators/proposals.js +22 -89
  88. package/dist/commands/read/curate.js +105 -462
  89. package/dist/commands/read/knowledge.js +3 -2
  90. package/dist/commands/read/search-cli.js +16 -33
  91. package/dist/commands/read/search.js +17 -23
  92. package/dist/commands/read/show.js +57 -108
  93. package/dist/commands/sources/bundle-cli.js +25 -2
  94. package/dist/commands/sources/bundle-config-ops.js +4 -0
  95. package/dist/commands/sources/dangerous-env-audit.js +1 -2
  96. package/dist/commands/sources/info.js +127 -29
  97. package/dist/commands/sources/installed-stashes.js +197 -746
  98. package/dist/commands/sources/schema-repair.js +98 -129
  99. package/dist/commands/sources/source-add.js +62 -12
  100. package/dist/commands/sources/source-manage.js +9 -2
  101. package/dist/commands/sources/stash-cli.js +24 -4
  102. package/dist/commands/tasks/explain.js +10 -13
  103. package/dist/commands/tasks/tasks-cli.js +12 -13
  104. package/dist/commands/tasks/tasks.js +350 -936
  105. package/dist/commands/tasks/validate.js +26 -24
  106. package/dist/commands/workflow/plan.js +22 -29
  107. package/dist/commands/workflow-cli.js +4 -4
  108. package/dist/core/adapter/adapters/akm-adapter.js +2 -1
  109. package/dist/core/adapter/adapters/akm-lint.js +2 -3
  110. package/dist/core/adapter/adapters/akm-metadata.js +42 -12
  111. package/dist/core/adapter/adapters/akm-task-adapter.js +29 -8
  112. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  113. package/dist/core/adapter/execution-source.js +17 -29
  114. package/dist/core/asset/asset-placement.js +4 -13
  115. package/dist/core/asset/frontmatter.js +106 -1
  116. package/dist/core/asset/resolve-ref.js +1 -1
  117. package/dist/core/bundle-id.js +42 -5
  118. package/dist/core/bundle-rename.js +285 -0
  119. package/dist/core/config/config-io.js +1 -2
  120. package/dist/core/config/config-schema.js +9 -34
  121. package/dist/core/config/config-walker.js +1 -1
  122. package/dist/core/config/config.js +184 -111
  123. package/dist/core/config/engine-semantics.js +0 -2
  124. package/dist/core/config/legacy-source-shape-shim.js +38 -9
  125. package/dist/core/config/schema/embedding.js +20 -5
  126. package/dist/core/config/schema/engines.js +5 -0
  127. package/dist/core/config/schema/execution.js +1 -1
  128. package/dist/core/config/schema/experimental.js +1 -1
  129. package/dist/core/config/schema/improve-processes.js +54 -125
  130. package/dist/core/config/schema/improve.js +4 -42
  131. package/dist/core/config/schema/index-config.js +9 -48
  132. package/dist/core/config/schema/scheduler.js +12 -12
  133. package/dist/core/config/schema/search.js +6 -22
  134. package/dist/core/env-secret-ref.js +0 -1
  135. package/dist/core/errors.js +8 -9
  136. package/dist/core/file-change.js +13 -5
  137. package/dist/core/file-lock.js +76 -173
  138. package/dist/core/improve-result.js +35 -7
  139. package/dist/core/improve-types.js +0 -1
  140. package/dist/core/logs-db.js +2 -2
  141. package/dist/core/loopback.js +7 -12
  142. package/dist/core/non-task-input.js +20 -0
  143. package/dist/core/parse.js +13 -16
  144. package/dist/core/paths.js +0 -24
  145. package/dist/core/redaction.js +109 -2
  146. package/dist/core/run-lock.js +2 -5
  147. package/dist/core/spawn-env.js +1 -1
  148. package/dist/core/state/migrations.js +123 -61
  149. package/dist/core/state-db-scope.js +2 -4
  150. package/dist/core/state-db.js +126 -692
  151. package/dist/core/time.js +0 -20
  152. package/dist/core/type-presentation.js +1 -9
  153. package/dist/core/write-source.js +294 -1005
  154. package/dist/execution/input-contract.js +1 -1
  155. package/dist/execution/resolved-request.js +135 -689
  156. package/dist/execution/source.js +63 -257
  157. package/dist/execution/target-ref.js +1 -1
  158. package/dist/indexer/bundle-identity-guard.js +2 -2
  159. package/dist/indexer/db/llm-cache.js +2 -2
  160. package/dist/indexer/ensure-index.js +77 -73
  161. package/dist/indexer/index-rebuild-lock.js +3 -11
  162. package/dist/indexer/index-writer-lock.js +8 -17
  163. package/dist/indexer/index-written-assets.js +141 -154
  164. package/dist/indexer/indexer.js +400 -1124
  165. package/dist/indexer/links/declared-links.js +90 -0
  166. package/dist/indexer/materialize-embeddings.js +60 -397
  167. package/dist/indexer/passes/memory-inference.js +96 -90
  168. package/dist/indexer/passes/metadata.js +132 -219
  169. package/dist/indexer/read-preflight.js +0 -7
  170. package/dist/indexer/scan/doc-to-entry.js +2 -3
  171. package/dist/indexer/scan/drain-dir.js +1 -1
  172. package/dist/indexer/search/db-search.js +190 -590
  173. package/dist/indexer/search/fts-query.js +30 -41
  174. package/dist/indexer/search/ranking.js +28 -154
  175. package/dist/indexer/search/search-attribution.js +12 -32
  176. package/dist/indexer/search/search-fields.js +11 -15
  177. package/dist/indexer/search/search-hit-enrichers.js +54 -85
  178. package/dist/indexer/search/search-source.js +1 -4
  179. package/dist/indexer/usage/usage-events.js +36 -7
  180. package/dist/indexer/walk/walker.js +3 -4
  181. package/dist/integrations/agent/engine-fallback.js +23 -40
  182. package/dist/integrations/agent/engine-resolution.js +93 -183
  183. package/dist/integrations/agent/execution.js +507 -0
  184. package/dist/integrations/agent/model-map.js +28 -156
  185. package/dist/integrations/agent/request-lowering.js +66 -141
  186. package/dist/integrations/agent/runner-dispatch.js +143 -321
  187. package/dist/integrations/agent/runner.js +54 -14
  188. package/dist/integrations/lockfile.js +53 -101
  189. package/dist/llm/client.js +18 -6
  190. package/dist/llm/embedders/deterministic.js +2 -3
  191. package/dist/llm/embedders/profile.js +71 -0
  192. package/dist/llm/embedders/remote.js +11 -17
  193. package/dist/llm/feature-gate.js +0 -8
  194. package/dist/llm/index-passes.js +3 -5
  195. package/dist/llm/memory-infer.js +1 -2
  196. package/dist/llm/structured-call.js +5 -24
  197. package/dist/output/generic-render.js +23 -11
  198. package/dist/output/html-render.js +13 -10
  199. package/dist/output/render-registry.js +3 -32
  200. package/dist/output/shapes/helpers.js +25 -38
  201. package/dist/output/shapes/passthrough.js +1 -9
  202. package/dist/{indexer/graph/graph-types.js → output/text/bundle-rename.js} +4 -1
  203. package/dist/output/text/command-format.js +69 -31
  204. package/dist/output/text/helpers.js +1 -1
  205. package/dist/output/text/migrate.js +5 -14
  206. package/dist/output/text/proposal-format.js +48 -3
  207. package/dist/output/text/show-format.js +13 -17
  208. package/dist/output/text/workflow-format.js +0 -32
  209. package/dist/output/text.js +2 -0
  210. package/dist/registry/factory.js +4 -19
  211. package/dist/registry/network.js +66 -220
  212. package/dist/registry/providers/index.js +0 -2
  213. package/dist/registry/providers/skills-sh.js +3 -14
  214. package/dist/registry/providers/static-index.js +24 -26
  215. package/dist/registry/resolve.js +55 -131
  216. package/dist/scripts/akm-migrate-node.js +42948 -92369
  217. package/dist/scripts/akm-migrate.js +42935 -92354
  218. package/dist/setup/registry-stash-loader.js +4 -13
  219. package/dist/setup/semantic-assets.js +3 -44
  220. package/dist/setup/setup.js +1 -1
  221. package/dist/setup/steps/connection.js +5 -6
  222. package/dist/setup/steps/platforms.js +2 -2
  223. package/dist/setup/steps/tasks.js +25 -15
  224. package/dist/sources/provider-factory.js +17 -18
  225. package/dist/sources/providers/filesystem.js +2 -3
  226. package/dist/sources/providers/git-install.js +7 -1
  227. package/dist/sources/providers/git-provider.js +0 -3
  228. package/dist/sources/providers/git-stash.js +83 -21
  229. package/dist/sources/providers/npm.js +2 -4
  230. package/dist/sources/providers/provider-utils.js +5 -10
  231. package/dist/sources/providers/website.js +0 -2
  232. package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
  233. package/dist/sources/website-url.js +2 -2
  234. package/dist/storage/database.js +9 -35
  235. package/dist/storage/repositories/improve-ledger-repository.js +209 -0
  236. package/dist/storage/repositories/index-connection.js +39 -72
  237. package/dist/storage/repositories/index-entries-repository.js +131 -129
  238. package/dist/storage/repositories/index-entry-mapper.js +1 -2
  239. package/dist/storage/repositories/index-entry-schema.js +101 -268
  240. package/dist/storage/repositories/index-fts-repository.js +86 -256
  241. package/dist/storage/repositories/index-links-repository.js +143 -0
  242. package/dist/storage/repositories/index-llm-cache-repository.js +7 -9
  243. package/dist/storage/repositories/index-meta-repository.js +6 -4
  244. package/dist/storage/repositories/index-schema.js +257 -325
  245. package/dist/storage/repositories/index-utility-repository.js +8 -29
  246. package/dist/storage/repositories/index-vec-repository.js +133 -414
  247. package/dist/storage/repositories/outcome-repository.js +2 -1
  248. package/dist/storage/repositories/proposals-repository.js +104 -1
  249. package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
  250. package/dist/storage/repositories/salience-repository.js +1 -19
  251. package/dist/storage/repositories/task-history-repository.js +26 -4
  252. package/dist/storage/repositories/workflow-runs-repository.js +53 -244
  253. package/dist/storage/sqlite-migrations.js +136 -0
  254. package/dist/storage/sqlite-pragmas.js +11 -9
  255. package/dist/storage/sqlite-transaction.js +170 -0
  256. package/dist/storage/state-db-integrity.js +130 -0
  257. package/dist/tasks/activation-config.js +134 -62
  258. package/dist/tasks/backends/cron.js +191 -302
  259. package/dist/tasks/backends/exec-utils.js +2 -5
  260. package/dist/tasks/backends/launchd.js +141 -748
  261. package/dist/tasks/backends/schtasks.js +119 -623
  262. package/dist/tasks/prepare/prepare-support.js +5 -15
  263. package/dist/tasks/prepare/prepare.js +0 -2
  264. package/dist/tasks/resolve-akm-bin.js +20 -79
  265. package/dist/tasks/run/attempt-lifecycle.js +0 -1
  266. package/dist/tasks/run/load-task.js +1 -1
  267. package/dist/tasks/scheduler-binding.js +20 -238
  268. package/dist/tasks/scheduler-invocation.js +136 -244
  269. package/dist/tasks/scheduler-lock.js +53 -0
  270. package/dist/tasks/scheduler-sync.js +368 -679
  271. package/dist/tasks/source/parse-task-source.js +55 -9
  272. package/dist/tasks/source/task-source-v3-frozen.js +3 -4
  273. package/dist/tasks/source/task-to-v4.js +464 -88
  274. package/dist/workflows/authoring/authoring.js +3 -12
  275. package/dist/workflows/compile.js +211 -0
  276. package/dist/workflows/concurrency-policy.js +13 -74
  277. package/dist/workflows/exec/child-invocation.js +3 -17
  278. package/dist/workflows/exec/child-workflow.js +32 -141
  279. package/dist/workflows/exec/dispatch-redaction.js +13 -53
  280. package/dist/workflows/exec/environment.js +98 -0
  281. package/dist/workflows/exec/exec-unit.js +33 -140
  282. package/dist/workflows/exec/frozen-judge.js +7 -59
  283. package/dist/workflows/exec/native-executor.js +82 -341
  284. package/dist/workflows/exec/param-secrets.js +29 -47
  285. package/dist/workflows/exec/run-workflow.js +154 -387
  286. package/dist/workflows/exec/scheduler.js +9 -36
  287. package/dist/workflows/exec/step-work.js +127 -430
  288. package/dist/workflows/exec/unit-dispatch.js +11 -63
  289. package/dist/workflows/exec/unit-writer.js +8 -52
  290. package/dist/workflows/exec/worktree.js +39 -273
  291. package/dist/workflows/freeze/child-output-references.js +4 -15
  292. package/dist/workflows/freeze/environment.js +99 -92
  293. package/dist/workflows/freeze/freeze.js +172 -0
  294. package/dist/workflows/freeze/step-values.js +19 -21
  295. package/dist/workflows/freeze/targets/child-workflow.js +23 -92
  296. package/dist/workflows/freeze/targets/command.js +10 -33
  297. package/dist/workflows/freeze/targets/script.js +5 -12
  298. package/dist/workflows/freeze/targets/shell.js +3 -6
  299. package/dist/workflows/freeze/targets/task.js +25 -80
  300. package/dist/workflows/freeze/task-bindings.js +20 -67
  301. package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
  302. package/dist/workflows/ir/params.js +6 -51
  303. package/dist/workflows/ir/plan-hash.js +2 -34
  304. package/dist/workflows/parser.js +140 -43
  305. package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
  306. package/dist/workflows/renderer.js +36 -69
  307. package/dist/workflows/resource-limits.js +12 -120
  308. package/dist/workflows/runtime/agent-identity.js +8 -40
  309. package/dist/workflows/runtime/run-outputs.js +3 -6
  310. package/dist/workflows/runtime/run-plan.js +316 -0
  311. package/dist/workflows/runtime/runs.js +48 -200
  312. package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
  313. package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
  314. package/dist/workflows/validate-summary.js +2 -7
  315. package/docs/integration/bundling-akm.md +49 -42
  316. package/docs/migration/README.md +1 -0
  317. package/docs/migration/release-notes/0.9.17.md +43 -0
  318. package/docs/migration/v0.9.1-to-v0.9.2.md +23 -7
  319. package/docs/reference/cli.md +232 -135
  320. package/docs/reference/configuration.md +71 -57
  321. package/docs/reference/data-and-telemetry.md +20 -21
  322. package/docs/reference/tasks.md +105 -39
  323. package/docs/reference/workflow-schema.md +14 -18
  324. package/docs/reference/workflows.md +6 -9
  325. package/package.json +1 -1
  326. package/schemas/akm-config.json +115 -738
  327. package/schemas/akm-workflow.json +1 -0
  328. package/dist/assets/improve-strategies/graph-refresh.json +0 -15
  329. package/dist/assets/prompts/contradiction-judge.md +0 -33
  330. package/dist/assets/prompts/graph-extract-system.md +0 -1
  331. package/dist/assets/prompts/graph-extract-user-prompt.md +0 -35
  332. package/dist/assets/prompts/metadata-enhance-system.md +0 -1
  333. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +0 -4
  334. package/dist/commands/health/advisories.js +0 -150
  335. package/dist/commands/health/metrics.js +0 -329
  336. package/dist/commands/health/surfaces.js +0 -102
  337. package/dist/commands/improve/anti-collapse.js +0 -83
  338. package/dist/commands/improve/collapse-detector.js +0 -432
  339. package/dist/commands/improve/consolidate/eligibility.js +0 -48
  340. package/dist/commands/improve/consolidate/merge.js +0 -149
  341. package/dist/commands/improve/distill/promote-memory.js +0 -291
  342. package/dist/commands/improve/distill/quality-gate.js +0 -337
  343. package/dist/commands/improve/eval-cases.js +0 -52
  344. package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
  345. package/dist/commands/improve/proposal-envelope.js +0 -31
  346. package/dist/commands/improve/run-context.js +0 -123
  347. package/dist/commands/improve/shared.js +0 -31
  348. package/dist/commands/improve/source-identity.js +0 -28
  349. package/dist/commands/improve/triage.js +0 -96
  350. package/dist/commands/proposal/drain-policies.js +0 -151
  351. package/dist/commands/sources/update-transaction.js +0 -220
  352. package/dist/core/action-contributors.js +0 -28
  353. package/dist/core/config/config-version-shim.js +0 -101
  354. package/dist/core/fs-txn.js +0 -405
  355. package/dist/core/lexical-score.js +0 -25
  356. package/dist/core/maintenance-barrier.js +0 -167
  357. package/dist/execution/executable-identity.js +0 -105
  358. package/dist/execution/guarded-source.js +0 -427
  359. package/dist/indexer/db/graph-db.js +0 -444
  360. package/dist/indexer/graph/graph-boost.js +0 -427
  361. package/dist/indexer/graph/graph-dedup.js +0 -95
  362. package/dist/indexer/graph/graph-extraction.js +0 -1108
  363. package/dist/indexer/search/name-match.js +0 -35
  364. package/dist/indexer/search/ranking-contributors.js +0 -515
  365. package/dist/indexer/search/ranking-types.js +0 -4
  366. package/dist/indexer/walk/project-context.js +0 -192
  367. package/dist/integrations/agent/execution-cascade.js +0 -566
  368. package/dist/integrations/agent/execution-definitions.js +0 -202
  369. package/dist/integrations/agent/execution-lowering.js +0 -841
  370. package/dist/integrations/agent/execution-preparation.js +0 -98
  371. package/dist/integrations/agent/inline-execution.js +0 -74
  372. package/dist/llm/graph-extract.js +0 -728
  373. package/dist/llm/metadata-enhance.js +0 -96
  374. package/dist/registry/create-provider-registry.js +0 -29
  375. package/dist/registry/pinned-request-helper.js +0 -247
  376. package/dist/registry/pinned-transport.js +0 -717
  377. package/dist/sources/providers/index.js +0 -14
  378. package/dist/storage/engines/sqlite-migrations.js +0 -271
  379. package/dist/storage/repositories/canaries-repository.js +0 -107
  380. package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
  381. package/dist/storage/repositories/registry-cache.js +0 -113
  382. package/dist/tasks/scheduler-sync-preview.js +0 -52
  383. package/dist/tasks/source/task-to-v3.js +0 -507
  384. package/dist/workflows/freeze/resolve-steps.js +0 -86
  385. package/dist/workflows/freeze/source-freeze.js +0 -64
  386. package/dist/workflows/ir/compile.js +0 -321
  387. package/dist/workflows/ir/environment-v4.js +0 -330
  388. package/dist/workflows/ir/freeze-v4.js +0 -153
  389. package/dist/workflows/ir/schema-v4.js +0 -745
  390. package/dist/workflows/ir/schema.js +0 -354
  391. package/dist/workflows/program/schema.js +0 -77
  392. package/dist/workflows/runtime/checkin.js +0 -57
  393. package/dist/workflows/runtime/plan-classifier.js +0 -196
  394. package/dist/workflows/runtime/unit-checkin.js +0 -45
  395. package/dist/workflows/runtime/unit-phases.js +0 -20
  396. package/dist/workflows/schema.js +0 -4
  397. package/dist/workflows/source-ir/compile.js +0 -200
  398. package/dist/workflows/source-ir/program.js +0 -50
  399. package/dist/workflows/source-ir/result.js +0 -26
  400. package/dist/workflows/source-ir/schema.js +0 -786
  401. package/dist/workflows/source-ir/triggers.js +0 -79
  402. package/dist/workflows/source-ir/uses.js +0 -40
  403. package/dist/workflows/validator.js +0 -60
@@ -5,36 +5,35 @@ 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";
19
18
  import { getAllEntries } from "../storage/repositories/index-entries-repository.js";
20
19
  import { queryTaskHistory } from "../storage/repositories/task-history-repository.js";
20
+ import { getStateDbFreelistInfo, runStateDbQuickCheck } from "../storage/state-db-integrity.js";
21
21
  import { pkgVersion } from "../version.js";
22
- import { collectImproveAdvisories } from "./health/advisories.js";
22
+ import { collectArchiveUsageAdvisory } from "./health/archive-usage.js";
23
23
  import { HEALTH_CHECKS, probeActiveImproveStrategy, runHealthEngineProbes, runPendingStateMigrationsCheck, SESSION_EXTRACTION_LEDGER_WINDOW_DAYS, } from "./health/checks.js";
24
+ import { collectConfigSkewAdvisory } from "./health/config-skew.js";
24
25
  import { collectDataDirUsageAdvisory } from "./health/data-dir-usage.js";
26
+ import { collectEgressAdvisory } from "./health/egress.js";
25
27
  import { engineLastUsedSince, readLastEngineUsage } from "./health/engine-usage.js";
26
- import { buildImproveSkipSummary, computeWallTimeStats, countAgentFailureReasons, isAgentTaskHistoryRow, roundRate, summarizeImproveCompleted, summarizeImproveRuns, taskFailureDetail, } from "./health/improve-metrics.js";
28
+ import { emptyImproveMetrics, roundRate } from "./health/improve-metrics.js";
27
29
  import { emptyLlmUsageAggregate, readLlmUsageAggregate } from "./health/llm-usage.js";
28
- import { computeDegradationMetrics, computeDenominatorFixedCoverage, computeEnrichmentMintingRollup, probeStateDbRoundTrip, } from "./health/metrics.js";
29
30
  import { collectPluginStalenessAdvisories } from "./health/plugin-staleness.js";
30
31
  import { collectSchedulerBinaryAdvisory } from "./health/scheduler-binary.js";
31
32
  import { collectStashExposureAdvisory } from "./health/stash-exposure.js";
32
- import { collectSurfacesAdvisories } from "./health/surfaces.js";
33
- import { buildPerRunSummaries } from "./health/task-runs.js";
34
33
  import { buildTypeDirectoryAdvisory } from "./health/type-directory-check.js";
35
- 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";
36
35
  import { collectVersionDriftAdvisory } from "./health/version-drift.js";
37
- import { buildWindowMetrics, computeDeltas, partitionLogBackedRows, resolveWindowCompare } from "./health/windows.js";
36
+ import { buildImproveWindowSummary, buildWindowMetrics, computeDeltas, computeTaskWindowRates, resolveWindowCompare, } from "./health/windows.js";
38
37
  const DEFAULT_SINCE_MS = 24 * 60 * 60 * 1000;
39
38
  export function parseHealthSince(since) {
40
39
  if (since === undefined || since.trim() === "") {
@@ -71,6 +70,65 @@ function validateAkmHealthOptions(options) {
71
70
  }
72
71
  }
73
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
+ }
74
132
  /**
75
133
  * Item 7: dedupe stuck-active rows by task_id (keeping the oldest/largest age
76
134
  * per id) so the `active-runs` check can name WHICH tasks are stuck instead of
@@ -118,43 +176,37 @@ function computeWorstTaskFailRate(rows) {
118
176
  return worst;
119
177
  }
120
178
  /** Table presence, the state.db round-trip probe, and task_history-derived rates. */
121
- function gatherTaskHistoryPhase(db, logsDb, since, stateDbPath, now) {
179
+ function gatherTaskHistoryPhase(db, since, stateDbPath, now) {
122
180
  const tables = listExistingTableNames(db, ["events", "task_history", "proposals", "schema_migrations"]);
123
181
  const tableNames = tables.map((row) => row.name).sort();
124
182
  const requiredTables = ["events", "proposals", "schema_migrations", "task_history"];
125
183
  const missingTables = requiredTables.filter((name) => !tableNames.includes(name));
126
184
  const probe = probeStateDbRoundTrip(stateDbPath);
127
- const taskRows = queryTaskHistory(db, { since });
128
- const { withLogs: taskRowsWithLogs, backed: existingLogRows } = partitionLogBackedRows(taskRows, logsDb);
129
- const failedTaskRows = taskRows.filter((row) => row.status === "failed");
130
- const activeRows = taskRows.filter((row) => row.status === "active" && row.completed_at === null);
131
- const stuckActiveRows = activeRows.filter((row) => now() - new Date(row.started_at).getTime() > ACTIVE_RUN_WARN_MS);
185
+ // R0: read-only, independent of the round-trip probe above — quick_check
186
+ // catches corruption a successful append/read cannot (out-of-order rowids,
187
+ // bad index entry counts), and the freelist reading is purely informational.
188
+ const stateDbIntegrity = runStateDbQuickCheck(stateDbPath);
189
+ const stateDbFreelist = getStateDbFreelistInfo(stateDbPath);
132
190
  // D8 (spec §5.3): a marked "command" row or a legacy (unmarked) "prompt"
133
191
  // row is the agent/LLM arm; an unmarked "command" row is the legacy
134
192
  // native shell/script arm and must not be counted here (see
135
- // isAgentTaskHistoryRow's header comment for the full mapping).
136
- const agentRows = taskRows.filter((row) => isAgentTaskHistoryRow(row));
137
- const agentFailures = agentRows.filter((row) => {
138
- const detail = taskFailureDetail(row);
139
- return typeof detail?.reason === "string" && detail.reason.length > 0;
140
- });
141
- const logBackingRate = taskRowsWithLogs.length === 0 ? 1 : existingLogRows.length / taskRowsWithLogs.length;
142
- const taskFailRate = taskRows.length === 0 ? 0 : failedTaskRows.length / taskRows.length;
143
- 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);
144
197
  return {
145
198
  tableNames,
146
199
  missingTables,
147
200
  probe,
201
+ stateDbIntegrity,
202
+ stateDbFreelist,
148
203
  taskRowCount: taskRows.length,
149
- taskRowsWithLogsCount: taskRowsWithLogs.length,
150
- existingLogRowsCount: existingLogRows.length,
151
- stuckActiveRuns: stuckActiveRows.length,
152
- stuckActiveTasks: dedupeStuckActiveTasks(stuckActiveRows, now),
153
- logBackingRate,
154
- taskFailRate,
204
+ stuckActiveRuns: rates.stuckActiveRuns,
205
+ stuckActiveTasks: dedupeStuckActiveTasks(rates.stuckActiveRows, now),
206
+ taskFailRate: rates.taskFailRate,
155
207
  worstTaskFailRate: computeWorstTaskFailRate(taskRows),
156
- agentFailureRate,
157
- agentFailureReasonCounts: countAgentFailureReasons(agentFailures),
208
+ agentFailureRate: rates.agentFailureRate,
209
+ agentFailureReasonCounts: rates.agentFailureReasonCounts,
158
210
  };
159
211
  }
160
212
  /**
@@ -183,47 +235,16 @@ function gatherSessionExtractionLedgerPhase(db, now) {
183
235
  return { since, rows: getExtractOutcomeCountsSince(db, since) };
184
236
  }
185
237
  /**
186
- * Assemble the window's improve-pipeline summary: invoked/completed/skipped
187
- * counts from events, the per-run result_json aggregate, wall-time stats, and
188
- * the WS-5 coverage/degradation/enrichment-minting rollups.
189
- */
190
- function gatherImproveSummaryPhase(db, stateDbPath, since, now) {
191
- const improveInvoked = readEvents({ since, type: "improve_invoked" }, { dbPath: stateDbPath }).events.length;
192
- const improveCompletedEvents = readEvents({ since, type: IMPROVE_COMPLETED_EVENT }, { dbPath: stateDbPath }).events;
193
- const improveSkippedEvents = readEvents({ since, type: "improve_skipped" }, { dbPath: stateDbPath }).events;
194
- const eventsMetrics = summarizeImproveCompleted(improveCompletedEvents);
195
- const { metrics: improveSummary } = summarizeImproveRuns(db, since);
196
- improveSummary.invoked = improveInvoked;
197
- improveSummary.completed = eventsMetrics.completed;
198
- const skipSummary = buildImproveSkipSummary(improveSkippedEvents);
199
- improveSummary.skipped = skipSummary.skipped;
200
- improveSummary.skipReasons = skipSummary.skipReasons;
201
- const perRunSummaries = buildPerRunSummaries(db, since);
202
- const wallTimes = perRunSummaries.map((run) => run.wallTimeMs).filter((ms) => Number.isFinite(ms) && ms > 0);
203
- improveSummary.wallTime = computeWallTimeStats(wallTimes, improveSummary.wallTime.byPhase);
204
- // WS-5: Compute denominator-fixed coverage and per-run degradation metrics
205
- // for the main health path (not just window-compare mode).
206
- const until = new Date(now()).toISOString();
207
- const totalAssetsMain = improveSummary.memorySummary.eligible + improveSummary.memorySummary.derived;
208
- improveSummary.coverage = computeDenominatorFixedCoverage(db, totalAssetsMain, improveSummary.memorySummary.eligible, since, until);
209
- const degradationMain = computeDegradationMetrics(db, since, until);
210
- if (degradationMain) {
211
- improveSummary.degradation = degradationMain;
212
- }
213
- improveSummary.enrichmentMinting = computeEnrichmentMintingRollup(db, since, until);
214
- return { improveSummary, perRunSummaries };
215
- }
216
- /**
217
- * The best-effort advisory groups beyond the health-check registry: improve
218
- * advisories, the `stash-git-exposure` probe, the 08 surfaces group
219
- * (binary-config-skew, egress-endpoints), `type-directory-disagreement`
220
- * (#831), `data-dir-usage` (#896), and `plugin-version` (itlackey/akm#832).
221
- * Order matches emission order in the returned array. A probe/filesystem
222
- * failure in any try/catch must not abort the health report — each group
223
- * 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.
224
245
  */
225
- function gatherAncillaryAdvisories(db, stateDbPath, since, improveSummary, options, egressConfigView) {
226
- const advisories = [...collectImproveAdvisories(db, stateDbPath, since, improveSummary)];
246
+ function gatherAncillaryAdvisories(db, options, egressConfigView) {
247
+ const advisories = [];
227
248
  const indexStateMismatch = detectIndexStateGenerationMismatch(db);
228
249
  if (indexStateMismatch)
229
250
  advisories.push(indexStateMismatch);
@@ -250,10 +271,12 @@ function gatherAncillaryAdvisories(db, stateDbPath, since, improveSummary, optio
250
271
  // egress-endpoints). Best-effort — a filesystem probe failure must not abort
251
272
  // the health report.
252
273
  try {
253
- advisories.push(...collectSurfacesAdvisories({
254
- configPath: getConfigPath(),
255
- config: egressConfigView,
256
- }));
274
+ const configSkew = collectConfigSkewAdvisory(getConfigPath());
275
+ if (configSkew)
276
+ advisories.push(configSkew);
277
+ const egress = collectEgressAdvisory(egressConfigView);
278
+ if (egress)
279
+ advisories.push(egress);
257
280
  }
258
281
  catch {
259
282
  // Non-fatal.
@@ -269,6 +292,18 @@ function gatherAncillaryAdvisories(db, stateDbPath, since, improveSummary, optio
269
292
  catch {
270
293
  // Non-fatal.
271
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
+ }
272
307
  // #896: report the data dir's total size and its largest top-level
273
308
  // subdirectory, so a disk-usage blowup (e.g. unpruned migration snapshot
274
309
  // backups, #897) is self-diagnosing instead of requiring `du` archaeology.
@@ -391,7 +426,7 @@ function detectIndexStateGenerationMismatch(stateDb) {
391
426
  * per-window bundles and computes deltas between the earliest and latest
392
427
  * window. Top-level metrics retain the primary `--since` query.
393
428
  */
394
- function resolveWindowComparePhase(options, db, stateDbPath, now, logsDb) {
429
+ function resolveWindowComparePhase(options, db, stateDbPath, now) {
395
430
  let windowSpecs;
396
431
  if (options.windowCompare) {
397
432
  windowSpecs = resolveWindowCompare(options.windowCompare, now);
@@ -405,7 +440,7 @@ function resolveWindowComparePhase(options, db, stateDbPath, now, logsDb) {
405
440
  windowResults = windowSpecs.map((spec) => {
406
441
  const winSince = parseHealthSince(spec.since);
407
442
  const winUntil = spec.until ? parseHealthSince(spec.until) : new Date(now()).toISOString();
408
- const bundle = buildWindowMetrics(db, stateDbPath, winSince, winUntil, now, logsDb);
443
+ const bundle = buildWindowMetrics(db, stateDbPath, winSince, winUntil, now);
409
444
  return {
410
445
  name: spec.name,
411
446
  since: winSince,
@@ -430,19 +465,13 @@ function resolveWindowComparePhase(options, db, stateDbPath, now, logsDb) {
430
465
  return { windowResults, deltas };
431
466
  }
432
467
  /**
433
- * The health report for a state.db a managed open cannot reach at all: either
434
- * the file is not readable (#791), or — the same shape, a different cause —
435
- * it holds a pending migration the managed open refuses to apply without
436
- * deliberate consent (`akm upgrade` / `akm migrate apply` are the
437
- * only two callers allowed to admit a historical-destructive migration; see
438
- * `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.
439
471
  *
440
472
  * `akm health` is what an operator (or a bundler's boot check) runs when
441
473
  * something else is misbehaving, so it must survive either problem long
442
- * enough to NAME it. Previously an unreadable file was already handled this
443
- * way, but a pending migration was not: the managed open's refusal escaped as
444
- * a thrown `ConfigError` (exit 78) and crashed the whole command before any
445
- * check — including this one — could report anything.
474
+ * enough to NAME it rather than exit 78 before any check could report.
446
475
  *
447
476
  * Reported as a single hard-channel `fail` check — the run genuinely could
448
477
  * not open state.db, so every check that depends on it is skipped rather than
@@ -462,13 +491,9 @@ function degradedStateDbReport(hardCheck, options) {
462
491
  agentFailureRate: 0,
463
492
  agentFailureReasonCounts: {},
464
493
  stuckActiveRuns: 0,
465
- logBackingRate: 0,
466
- // `null`, not 0: the round-trip probe did not run, which is not the same
467
- // as it running instantly.
468
- probeRoundTripMs: null,
469
494
  llmUsage: emptyLlmUsageAggregate(),
470
495
  },
471
- improve: summarizeImproveCompleted([]),
496
+ improve: emptyImproveMetrics(),
472
497
  };
473
498
  }
474
499
  function unreadableStateDbCheck(detail) {
@@ -490,29 +515,25 @@ export async function akmHealth(options = {}) {
490
515
  const stateDbPath = options.stateDbPath ?? getStateDbPathInDataDir();
491
516
  const hardChecks = [];
492
517
  const advisories = [];
493
- // #791: an UNREADABLE state.db, or one with a pending migration the
494
- // managed open refuses to apply, are the two failures `akm health` most
495
- // needs to be able to report, because this is the command an operator (or a
496
- // bundler's boot check) runs to find out why everything else is behaving
497
- // oddly. Dying here meant health could not diagnose that state at all — not
498
- // even the checks that never touch state.db got to run. Report it as a
499
- // finding instead.
500
- 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;
501
525
  try {
502
- db = openStateDatabase(stateDbPath);
526
+ opened = openStateDatabaseWithReport(stateDbPath);
503
527
  }
504
528
  catch (error) {
505
529
  const { access, code } = classifyPathAccess(stateDbPath);
506
530
  if (access === "inaccessible") {
507
531
  return degradedStateDbReport(unreadableStateDbCheck(describeInaccessiblePath(stateDbPath, code)), options);
508
532
  }
509
- // The managed open's refusal of a pending historical-destructive
510
- // migration is a plain `Error`, not a distinguishable error class — so
511
- // confirm the cause via the read-only preflight (`listPendingStateMigrations`,
512
- // which never applies anything) rather than pattern-matching the message.
513
- // A ledger too broken to enumerate at all throws here too; in that case
514
- // fall through to the generic config-error report below, since it is a
515
- // 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.
516
537
  let pendingMigrationsCheck;
517
538
  try {
518
539
  pendingMigrationsCheck = runPendingStateMigrationsCheck(stateDbPath, { listPendingStateMigrations });
@@ -525,16 +546,7 @@ export async function akmHealth(options = {}) {
525
546
  }
526
547
  throw new ConfigError(`Unable to open state.db: ${error instanceof Error ? error.message : String(error)}`, "INVALID_CONFIG_FILE");
527
548
  }
528
- // logs.db backs the log-backing metric (#579). Best-effort: when it cannot
529
- // be opened, partitionLogBackedRows falls back to the on-disk file check, so
530
- // health never hard-fails on a missing/locked logs database.
531
- let logsDb;
532
- try {
533
- logsDb = openLogsDatabase(options.logsDbPath);
534
- }
535
- catch {
536
- logsDb = undefined;
537
- }
549
+ const { db } = opened;
538
550
  try {
539
551
  // Network probes overlap the local database phases below; awaited where consumed.
540
552
  const engineProbesPromise = runHealthEngineProbes({ probeReachable: options.probe ? probeLlmEndpoint : undefined });
@@ -549,11 +561,15 @@ export async function akmHealth(options = {}) {
549
561
  cliVersion: pkgVersion,
550
562
  });
551
563
  schedulerBinaryDriftPromise.catch(() => undefined);
552
- const taskHistory = gatherTaskHistoryPhase(db, logsDb, since, stateDbPath, now);
564
+ const taskHistory = gatherTaskHistoryPhase(db, since, stateDbPath, now);
553
565
  const { tableNames, missingTables, probe } = taskHistory;
554
566
  const { egressConfigView, thinkingOffEngines } = gatherEgressConfigPhase();
555
- const { improveSummary } = gatherImproveSummaryPhase(db, stateDbPath, since, now);
556
- 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));
557
573
  const sessionExtractionLedger = gatherSessionExtractionLedgerPhase(db, now);
558
574
  // #950: computed once (no IO beyond config/env, same as gatherEgressConfigPhase)
559
575
  // so `active-improve-strategy` and `engine-last-used` project the same
@@ -580,16 +596,15 @@ export async function akmHealth(options = {}) {
580
596
  tableNames,
581
597
  missingTables,
582
598
  probe,
599
+ stateDbIntegrity: taskHistory.stateDbIntegrity,
600
+ stateDbFreelist: taskHistory.stateDbFreelist,
601
+ stateDbMigrations: { applied: opened.applied, ...(opened.backupPath ? { backupPath: opened.backupPath } : {}) },
583
602
  taskRowCount: taskHistory.taskRowCount,
584
603
  taskFailRate: taskHistory.taskFailRate,
585
- taskRowsWithLogsCount: taskHistory.taskRowsWithLogsCount,
586
- existingLogRowsCount: taskHistory.existingLogRowsCount,
587
- logBackingRate: taskHistory.logBackingRate,
588
604
  stuckActiveRuns: taskHistory.stuckActiveRuns,
589
605
  stuckActiveTasks: taskHistory.stuckActiveTasks,
590
606
  worstTaskFailRate: taskHistory.worstTaskFailRate,
591
607
  agentFailureReasonCounts: taskHistory.agentFailureReasonCounts,
592
- sessionExtraction: improveSummary.sessionExtraction,
593
608
  sessionExtractionLedger,
594
609
  autoAccept: improveSummary.autoAccept,
595
610
  engineProbes,
@@ -614,20 +629,15 @@ export async function akmHealth(options = {}) {
614
629
  agentFailureRate: roundRate(taskHistory.agentFailureRate),
615
630
  agentFailureReasonCounts: taskHistory.agentFailureReasonCounts,
616
631
  stuckActiveRuns: taskHistory.stuckActiveRuns,
617
- logBackingRate: roundRate(taskHistory.logBackingRate),
618
- probeRoundTripMs: probe.durationMs,
619
632
  llmUsage,
620
633
  };
621
634
  const hardFailure = hardChecks.some((check) => check.status === "fail");
622
635
  const deterministicWarnings = [...hardChecks, ...advisories].some((check) => check.status === "warn" && check.kind === "deterministic");
623
636
  const status = hardFailure ? "fail" : deterministicWarnings ? "warn" : "pass";
624
637
  // ── Window-compare mode (Phase 3) ─────────────────────────────────────
625
- const { windowResults, deltas } = resolveWindowComparePhase(options, db, stateDbPath, now, logsDb);
638
+ const { windowResults, deltas } = resolveWindowComparePhase(options, db, stateDbPath, now);
626
639
  // ── Per-run mode (Phase 2) ────────────────────────────────────────────
627
- let runs;
628
- if (options.groupBy === "run") {
629
- runs = buildPerRunSummaries(db, since);
630
- }
640
+ const runs = options.groupBy === "run" ? perRunSummaries : undefined;
631
641
  return {
632
642
  schemaVersion: 3,
633
643
  ok: !hardFailure,
@@ -644,14 +654,6 @@ export async function akmHealth(options = {}) {
644
654
  }
645
655
  finally {
646
656
  db.close();
647
- if (logsDb) {
648
- try {
649
- logsDb.close();
650
- }
651
- catch {
652
- // best-effort
653
- }
654
- }
655
657
  }
656
658
  }
657
659
  // Markdown renderers (renderRunsDetailMd / renderWindowCompareMd) live in
@@ -1,145 +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 } 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 that drive the system-prompt
70
- * rules at lines 181-186:
71
- * - `(captureMode: hot)` — user-explicit memory; system prompt rule 2
72
- * forbids proposing delete. ~60 wasted LLM verdicts/4h on this user's
73
- * stack before this annotation.
74
- * - `(already queued)` — the memory's body hash matches a pending
75
- * consolidate proposal; system prompt rule 3 forbids proposing
76
- * promote/merge/contradict. ~107/4h before this annotation.
77
- *
78
- * Both annotations are visible to the LLM. `pendingProposalBodyHashes`
79
- * is precomputed once per run by `loadPendingConsolidateProposalHashes`
80
- * 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.
81
30
  */
82
- export function buildChunkPrompt(sourceName, memories, chunkIndex, totalChunks, bodyTruncation, pendingProposalBodyHashes = new Set(), standardsContext = "") {
83
- const start = memories[0] ? `memories/${memories[0].name}` : "";
84
- const lastMemory = memories[memories.length - 1];
85
- const end = lastMemory ? `memories/${lastMemory.name}` : "";
86
- const annotationsByIndex = [];
87
- const hotRefs = [];
88
- for (const m of memories) {
89
- let body = "";
31
+ export function buildChunkPrompt(sourceName, memories, chunkIndex, totalChunks, bodyTruncation, pendingProposalBodyHashes = new Set()) {
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;
90
39
  try {
91
40
  body = fs.readFileSync(m.filePath, "utf8");
92
41
  }
93
42
  catch {
94
43
  body = "(unreadable)";
95
44
  }
96
- const parsed = parseFrontmatter(body);
97
- const isHot = parsed.data.captureMode === "hot";
98
- // Use cacheHash (case-preserving stripped body) to match the domain used
99
- // by loadPendingConsolidateProposalHashes and the body-embedding cache.
100
- const bodyHash = cacheHash(body);
101
- const isAlreadyQueued = pendingProposalBodyHashes.has(bodyHash);
102
- annotationsByIndex.push({ isHot, isAlreadyQueued, body });
103
- if (isHot)
104
- hotRefs.push(`memories/${m.name}`);
105
- }
106
- const lines = [
107
- `Source: ${sourceName}`,
108
- `Chunk ${chunkIndex + 1} of ${totalChunks}, memories ${start}–${end}:`,
109
- "",
110
- ];
111
- if (standardsContext.trim()) {
112
- lines.push("Standards to follow (the rulebook for this target):");
113
- lines.push(standardsContext.trim());
114
- lines.push("");
115
- }
116
- // Top-of-prompt protection block for hot refs. Neutral phrasing — avoid
117
- // op-words like "promote", "merge", "contradict" so the model doesn't
118
- // accidentally treat the warning as a hint to use that op elsewhere
119
- // (variant B leaked the word "contradict" into the control sample
120
- // during the diagnostic).
121
- if (hotRefs.length > 0) {
122
- lines.push("⛔ DO NOT propose any `delete` operation for these refs — they are user-explicit (captureMode: hot) and the downstream guard refuses them regardless. Proposing delete for any of these only wastes tokens.");
123
- for (const ref of hotRefs)
124
- lines.push(` - ${ref}`);
125
- lines.push("");
126
- }
127
- for (let i = 0; i < memories.length; i++) {
128
- const m = memories[i];
129
- // `annotationsByIndex` has exactly one entry per memory (built in the loop above).
130
- const { isHot, isAlreadyQueued, body } = annotationsByIndex[i];
131
45
  const annotations = [];
132
- if (isHot)
46
+ if (parseFrontmatter(body).data.captureMode === "hot")
133
47
  annotations.push("captureMode: hot");
134
- if (isAlreadyQueued)
48
+ if (pendingProposalBodyHashes.has(contentHash(body, "body")))
135
49
  annotations.push("already queued");
136
50
  const annotationSuffix = annotations.length > 0 ? ` (${annotations.join("; ")})` : "";
137
- lines.push(`[${i + 1}] memories/${m.name}${annotationSuffix}`);
138
- lines.push(`Description: ${m.description || "(none)"}`);
139
- lines.push(`Tags: ${m.tags.length > 0 ? m.tags.join(", ") : "(none)"}`);
140
- lines.push("---");
141
- lines.push(body.slice(0, bodyTruncation));
142
- lines.push("");
143
- }
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
+ });
144
53
  return lines.join("\n");
145
54
  }