akm-cli 0.9.0-beta.9 → 0.9.0-rc.1

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 (381) hide show
  1. package/CHANGELOG.md +715 -0
  2. package/README.md +12 -4
  3. package/dist/akm +38 -0
  4. package/dist/akm-migrate-storage +38 -0
  5. package/dist/assets/help/help-improve.md +9 -6
  6. package/dist/assets/hints/cli-hints-full.md +6 -5
  7. package/dist/assets/profiles/default.json +9 -4
  8. package/dist/assets/profiles/frequent.json +1 -1
  9. package/dist/assets/profiles/memory-focus.json +1 -1
  10. package/dist/assets/profiles/proactive-maintenance.json +25 -0
  11. package/dist/assets/profiles/quick.json +1 -1
  12. package/dist/assets/profiles/recombine-only.json +21 -0
  13. package/dist/assets/profiles/reflect-distill.json +30 -0
  14. package/dist/assets/profiles/synthesize.json +15 -0
  15. package/dist/assets/profiles/thorough.json +1 -1
  16. package/dist/assets/prompts/consolidate-system.md +23 -0
  17. package/dist/assets/prompts/contradiction-judge.md +33 -0
  18. package/dist/assets/prompts/distill-knowledge-system.md +22 -0
  19. package/dist/assets/prompts/distill-lesson-system.md +36 -0
  20. package/dist/assets/prompts/extract-session.md +11 -3
  21. package/dist/assets/prompts/graph-extract-system.md +1 -0
  22. package/dist/assets/prompts/graph-extract-user-prompt.md +1 -1
  23. package/dist/assets/prompts/memory-infer-system.md +1 -0
  24. package/dist/assets/prompts/memory-infer-user.md +5 -0
  25. package/dist/assets/prompts/metadata-enhance-system.md +1 -0
  26. package/dist/assets/prompts/procedural-system.md +44 -0
  27. package/dist/assets/prompts/recombine-system.md +40 -0
  28. package/dist/assets/prompts/staleness-detect-system.md +6 -0
  29. package/dist/assets/prompts/validate-summary-judge.md +1 -0
  30. package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
  31. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +38 -0
  32. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +38 -0
  33. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +39 -0
  34. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +40 -0
  35. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +43 -0
  36. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +38 -0
  37. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +43 -0
  38. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +40 -0
  39. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +43 -0
  40. package/dist/assets/templates/html/health.html +281 -111
  41. package/dist/assets/wiki/ingest-workflow-template.md +45 -16
  42. package/dist/assets/wiki/schema-template.md +4 -4
  43. package/dist/cli/clack.js +56 -0
  44. package/dist/cli/config-migrate.js +7 -1
  45. package/dist/cli/confirm.js +1 -1
  46. package/dist/cli/parse-args.js +46 -1
  47. package/dist/cli/shared.js +28 -0
  48. package/dist/cli.js +25 -21
  49. package/dist/commands/agent/agent-dispatch.js +3 -2
  50. package/dist/commands/agent/agent-support.js +0 -7
  51. package/dist/commands/agent/contribute-cli.js +26 -7
  52. package/dist/commands/config-cli.js +26 -13
  53. package/dist/commands/env/child-env.js +47 -0
  54. package/dist/commands/env/env-binding.js +95 -0
  55. package/dist/commands/env/env-cli.js +228 -292
  56. package/dist/commands/env/env.js +14 -67
  57. package/dist/commands/env/secret-cli.js +140 -138
  58. package/dist/commands/feedback-cli.js +156 -155
  59. package/dist/commands/graph/graph-cli.js +5 -13
  60. package/dist/commands/graph/graph.js +3 -3
  61. package/dist/commands/health/advisories.js +151 -0
  62. package/dist/commands/health/checks.js +103 -16
  63. package/dist/commands/health/html-report.js +447 -81
  64. package/dist/commands/health/improve-metrics.js +771 -0
  65. package/dist/commands/health/llm-usage.js +65 -0
  66. package/dist/commands/health/md-report.js +103 -0
  67. package/dist/commands/health/metrics.js +278 -0
  68. package/dist/commands/health/stash-exposure.js +46 -0
  69. package/dist/commands/health/surfaces.js +216 -0
  70. package/dist/commands/health/task-runs.js +135 -0
  71. package/dist/commands/health/types.js +26 -0
  72. package/dist/commands/health/windows.js +195 -0
  73. package/dist/commands/health.js +91 -1091
  74. package/dist/commands/improve/anti-collapse.js +170 -0
  75. package/dist/commands/improve/calibration.js +161 -0
  76. package/dist/commands/improve/collapse-detector.js +421 -0
  77. package/dist/commands/improve/consolidate/chunking.js +141 -0
  78. package/dist/commands/improve/consolidate/eligibility.js +64 -0
  79. package/dist/commands/improve/consolidate/merge.js +145 -0
  80. package/dist/commands/improve/consolidate/sanitize.js +231 -0
  81. package/dist/commands/{lint.js → improve/consolidate/types.js} +1 -1
  82. package/dist/commands/improve/consolidate.js +1295 -1277
  83. package/dist/commands/improve/dedup.js +482 -0
  84. package/dist/commands/improve/distill/content-repair.js +202 -0
  85. package/dist/commands/improve/distill/promote-memory.js +229 -0
  86. package/dist/commands/improve/distill/quality-gate.js +236 -0
  87. package/dist/commands/improve/distill-guards.js +127 -0
  88. package/dist/commands/improve/distill-promotion-policy.js +826 -167
  89. package/dist/commands/improve/distill.js +228 -605
  90. package/dist/commands/improve/eligibility.js +434 -0
  91. package/dist/commands/improve/encoding-salience.js +205 -0
  92. package/dist/commands/improve/extract-cli.js +179 -59
  93. package/dist/commands/improve/extract-prompt.js +54 -3
  94. package/dist/commands/improve/extract-watch.js +140 -0
  95. package/dist/commands/improve/extract.js +409 -43
  96. package/dist/commands/improve/feedback-valence.js +54 -0
  97. package/dist/commands/improve/hot-probation.js +45 -0
  98. package/dist/commands/improve/improve-auto-accept.js +157 -10
  99. package/dist/commands/improve/improve-cli.js +115 -73
  100. package/dist/commands/improve/improve-profiles.js +28 -8
  101. package/dist/commands/improve/improve-result-file.js +15 -25
  102. package/dist/commands/improve/improve-session.js +58 -0
  103. package/dist/commands/improve/improve.js +485 -2764
  104. package/dist/commands/improve/locks.js +154 -0
  105. package/dist/commands/improve/loop-stages.js +1100 -0
  106. package/dist/commands/improve/memory/memory-belief.js +14 -15
  107. package/dist/commands/improve/memory/memory-contradiction-detect.js +83 -60
  108. package/dist/commands/improve/memory/memory-improve.js +27 -27
  109. package/dist/commands/improve/outcome-loop.js +270 -0
  110. package/dist/commands/improve/preparation.js +2002 -0
  111. package/dist/commands/improve/proactive-maintenance.js +37 -35
  112. package/dist/commands/improve/procedural.js +398 -0
  113. package/dist/commands/improve/recombine.js +818 -0
  114. package/dist/commands/improve/reflect-noise.js +0 -0
  115. package/dist/commands/improve/reflect.js +206 -45
  116. package/dist/commands/improve/salience.js +455 -0
  117. package/dist/commands/improve/schema-similarity-gate.js +168 -0
  118. package/dist/commands/improve/shared.js +51 -0
  119. package/dist/commands/improve/triage.js +93 -0
  120. package/dist/commands/lint/agent-linter.js +19 -24
  121. package/dist/commands/lint/base-linter.js +173 -60
  122. package/dist/commands/lint/command-linter.js +19 -24
  123. package/dist/commands/lint/env-key-rules.js +38 -1
  124. package/dist/commands/lint/fact-linter.js +39 -0
  125. package/dist/commands/lint/index.js +31 -13
  126. package/dist/commands/lint/memory-linter.js +1 -1
  127. package/dist/commands/lint/registry.js +7 -2
  128. package/dist/commands/lint/task-linter.js +3 -3
  129. package/dist/commands/lint/workflow-linter.js +26 -1
  130. package/dist/commands/observability-cli.js +4 -4
  131. package/dist/commands/proposal/drain-policies.js +13 -4
  132. package/dist/commands/proposal/drain.js +45 -51
  133. package/dist/commands/proposal/legacy-import.js +115 -0
  134. package/dist/commands/proposal/proposal-cli.js +24 -34
  135. package/dist/commands/proposal/proposal.js +2 -1
  136. package/dist/commands/proposal/propose.js +8 -3
  137. package/dist/commands/proposal/repository.js +829 -0
  138. package/dist/commands/proposal/validators/proposal-quality-validators.js +9 -8
  139. package/dist/commands/proposal/validators/proposals.js +93 -895
  140. package/dist/commands/read/curate.js +410 -111
  141. package/dist/commands/read/knowledge.js +10 -3
  142. package/dist/commands/read/remember-cli.js +133 -138
  143. package/dist/commands/read/search-cli.js +15 -8
  144. package/dist/commands/read/search.js +22 -11
  145. package/dist/commands/read/show.js +106 -14
  146. package/dist/commands/registry-cli.js +76 -87
  147. package/dist/commands/remember.js +11 -12
  148. package/dist/commands/sources/add-cli.js +91 -95
  149. package/dist/commands/sources/history.js +1 -1
  150. package/dist/commands/sources/init.js +66 -18
  151. package/dist/commands/sources/installed-stashes.js +11 -3
  152. package/dist/commands/sources/migration-help.js +7 -4
  153. package/dist/commands/sources/schema-repair.js +44 -46
  154. package/dist/commands/sources/self-update.js +2 -2
  155. package/dist/commands/sources/source-add.js +7 -3
  156. package/dist/commands/sources/sources-cli.js +3 -3
  157. package/dist/commands/sources/stash-cli.js +19 -39
  158. package/dist/commands/sources/stash-skeleton.js +57 -8
  159. package/dist/commands/tasks/default-tasks.js +15 -2
  160. package/dist/commands/tasks/tasks-cli.js +20 -29
  161. package/dist/commands/tasks/tasks.js +39 -11
  162. package/dist/commands/wiki-cli.js +23 -38
  163. package/dist/commands/workflow-cli.js +291 -13
  164. package/dist/core/asset/asset-registry.js +3 -1
  165. package/dist/core/asset/asset-spec.js +79 -5
  166. package/dist/core/asset/frontmatter.js +188 -167
  167. package/dist/core/asset/markdown.js +8 -0
  168. package/dist/core/authoring-rules.js +92 -0
  169. package/dist/core/common.js +4 -23
  170. package/dist/core/concurrent.js +10 -1
  171. package/dist/core/config/config-io.js +10 -1
  172. package/dist/core/config/config-migration.js +18 -40
  173. package/dist/core/config/config-schema.js +403 -62
  174. package/dist/core/config/config-types.js +3 -3
  175. package/dist/core/config/config.js +67 -22
  176. package/dist/core/deep-merge.js +38 -0
  177. package/dist/core/errors.js +1 -0
  178. package/dist/core/eval/rank-metrics.js +113 -0
  179. package/dist/core/events.js +4 -7
  180. package/dist/core/improve-types.js +47 -8
  181. package/dist/core/json-schema.js +142 -0
  182. package/dist/core/logs-db.js +14 -75
  183. package/dist/core/parse.js +36 -16
  184. package/dist/core/paths.js +18 -18
  185. package/dist/core/standards/resolve-standards-context.js +87 -0
  186. package/dist/core/standards/resolve-stash-standards.js +99 -0
  187. package/dist/core/standards/resolve-type-conventions.js +66 -0
  188. package/dist/core/state/migrations.js +770 -0
  189. package/dist/core/state-db.js +132 -1126
  190. package/dist/core/structured.js +69 -0
  191. package/dist/core/time.js +53 -0
  192. package/dist/core/warn.js +21 -0
  193. package/dist/core/write-source.js +37 -0
  194. package/dist/indexer/db/db.js +261 -770
  195. package/dist/indexer/db/entry-mapper.js +41 -0
  196. package/dist/indexer/db/graph-db.js +129 -86
  197. package/dist/indexer/db/llm-cache.js +2 -2
  198. package/dist/indexer/db/schema.js +516 -0
  199. package/dist/indexer/ensure-index.js +36 -92
  200. package/dist/indexer/feedback/utility-policy.js +75 -0
  201. package/dist/indexer/graph/graph-boost.js +51 -41
  202. package/dist/indexer/graph/graph-extraction.js +207 -4
  203. package/dist/indexer/index-writer-lock.js +18 -11
  204. package/dist/indexer/index-written-assets.js +105 -0
  205. package/dist/indexer/indexer.js +182 -204
  206. package/dist/indexer/passes/dir-staleness.js +114 -0
  207. package/dist/indexer/passes/memory-inference.js +13 -5
  208. package/dist/indexer/passes/metadata.js +20 -0
  209. package/dist/indexer/read-preflight.js +23 -0
  210. package/dist/indexer/search/db-search.js +89 -13
  211. package/dist/indexer/search/fts-query.js +51 -0
  212. package/dist/indexer/search/ranking-contributors.js +95 -9
  213. package/dist/indexer/search/ranking.js +79 -3
  214. package/dist/indexer/search/search-fields.js +6 -0
  215. package/dist/indexer/search/search-source.js +32 -21
  216. package/dist/indexer/search/semantic-status.js +4 -0
  217. package/dist/indexer/walk/matchers.js +48 -0
  218. package/dist/indexer/walk/walker.js +21 -13
  219. package/dist/integrations/agent/builders.js +41 -13
  220. package/dist/integrations/agent/config.js +20 -59
  221. package/dist/integrations/agent/detect.js +9 -0
  222. package/dist/integrations/agent/index.js +3 -19
  223. package/dist/integrations/agent/model-aliases.js +16 -2
  224. package/dist/integrations/agent/profiles.js +79 -6
  225. package/dist/integrations/agent/prompts.js +75 -9
  226. package/dist/integrations/agent/runner-dispatch.js +83 -0
  227. package/dist/integrations/agent/runner.js +13 -9
  228. package/dist/integrations/agent/spawn.js +206 -81
  229. package/dist/integrations/harnesses/aider/agent-builder.js +113 -0
  230. package/dist/integrations/harnesses/aider/index.js +58 -0
  231. package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
  232. package/dist/integrations/harnesses/amazonq/agent-builder.js +153 -0
  233. package/dist/integrations/harnesses/amazonq/index.js +59 -0
  234. package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
  235. package/dist/integrations/harnesses/claude/agent-builder.js +46 -7
  236. package/dist/integrations/harnesses/claude/index.js +27 -23
  237. package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
  238. package/dist/integrations/harnesses/claude/session-log.js +10 -0
  239. package/dist/integrations/harnesses/codex/agent-builder.js +137 -0
  240. package/dist/integrations/harnesses/codex/index.js +63 -0
  241. package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
  242. package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
  243. package/dist/integrations/harnesses/copilot/index.js +60 -0
  244. package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
  245. package/dist/integrations/harnesses/gemini/agent-builder.js +121 -0
  246. package/dist/integrations/harnesses/gemini/index.js +60 -0
  247. package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
  248. package/dist/integrations/harnesses/index.js +28 -7
  249. package/dist/integrations/harnesses/opencode/agent-builder.js +1 -1
  250. package/dist/integrations/harnesses/opencode/index.js +17 -16
  251. package/dist/integrations/harnesses/opencode/session-log.js +173 -3
  252. package/dist/integrations/harnesses/opencode-sdk/harness.js +65 -0
  253. package/dist/integrations/harnesses/opencode-sdk/index.js +10 -34
  254. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +642 -71
  255. package/dist/integrations/harnesses/openhands/agent-builder.js +126 -0
  256. package/dist/integrations/harnesses/openhands/index.js +58 -0
  257. package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
  258. package/dist/integrations/harnesses/pi/agent-builder.js +104 -0
  259. package/dist/integrations/harnesses/pi/index.js +58 -0
  260. package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
  261. package/dist/integrations/harnesses/types.js +8 -0
  262. package/dist/integrations/session-logs/index.js +40 -11
  263. package/dist/llm/call-ai.js +2 -2
  264. package/dist/llm/client.js +34 -11
  265. package/dist/llm/embedder.js +67 -4
  266. package/dist/llm/embedders/cache.js +3 -1
  267. package/dist/llm/embedders/deterministic.js +66 -0
  268. package/dist/llm/embedders/local.js +73 -3
  269. package/dist/llm/feature-gate.js +16 -15
  270. package/dist/llm/graph-extract.js +67 -44
  271. package/dist/llm/memory-infer-impl.js +138 -0
  272. package/dist/llm/memory-infer.js +1 -127
  273. package/dist/llm/metadata-enhance.js +44 -31
  274. package/dist/llm/structured-call.js +49 -0
  275. package/dist/migrate-storage-node.mjs +8 -0
  276. package/dist/output/context.js +5 -5
  277. package/dist/output/renderers.js +87 -15
  278. package/dist/output/shapes/curate.js +14 -2
  279. package/dist/output/shapes/helpers.js +0 -3
  280. package/dist/output/shapes/passthrough.js +6 -1
  281. package/dist/output/text/helpers.js +241 -2
  282. package/dist/output/text/workflow.js +4 -1
  283. package/dist/registry/providers/skills-sh.js +21 -147
  284. package/dist/registry/providers/static-index.js +15 -157
  285. package/dist/registry/resolve.js +27 -9
  286. package/dist/runtime.js +25 -1
  287. package/dist/schemas/akm-config.json +14225 -0
  288. package/dist/schemas/akm-workflow.json +328 -0
  289. package/dist/scripts/migrate-storage.js +2743 -8390
  290. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +1652 -607
  291. package/dist/setup/detect.js +9 -0
  292. package/dist/setup/legacy-config.js +106 -0
  293. package/dist/setup/prompt.js +57 -0
  294. package/dist/setup/providers.js +14 -0
  295. package/dist/setup/registry-stash-loader.js +12 -0
  296. package/dist/setup/semantic-assets.js +124 -0
  297. package/dist/setup/setup.js +52 -1614
  298. package/dist/setup/steps/connection.js +734 -0
  299. package/dist/setup/steps/output.js +31 -0
  300. package/dist/setup/steps/platforms.js +124 -0
  301. package/dist/setup/steps/semantic.js +27 -0
  302. package/dist/setup/steps/sources.js +222 -0
  303. package/dist/setup/steps/stashdir.js +42 -0
  304. package/dist/setup/steps/tasks.js +152 -0
  305. package/dist/sources/include.js +6 -2
  306. package/dist/sources/providers/filesystem.js +0 -1
  307. package/dist/sources/providers/git-install.js +210 -0
  308. package/dist/sources/providers/git-provider.js +234 -0
  309. package/dist/sources/providers/git-stash.js +248 -0
  310. package/dist/sources/providers/git.js +10 -661
  311. package/dist/sources/providers/npm.js +2 -6
  312. package/dist/sources/providers/provider-utils.js +13 -7
  313. package/dist/sources/providers/sync-from-ref.js +9 -1
  314. package/dist/sources/providers/website.js +9 -5
  315. package/dist/sources/website-ingest.js +187 -29
  316. package/dist/sources/wiki-fetchers/registry.js +53 -0
  317. package/dist/sources/wiki-fetchers/youtube.js +239 -0
  318. package/dist/storage/database.js +45 -10
  319. package/dist/storage/managed-db.js +82 -0
  320. package/dist/storage/repositories/canaries-repository.js +107 -0
  321. package/dist/storage/repositories/consolidation-repository.js +38 -0
  322. package/dist/storage/repositories/embeddings-repository.js +72 -0
  323. package/dist/storage/repositories/events-repository.js +187 -0
  324. package/dist/storage/repositories/extract-sessions-repository.js +96 -0
  325. package/dist/storage/repositories/improve-runs-repository.js +146 -0
  326. package/dist/storage/repositories/index-db.js +14 -8
  327. package/dist/storage/repositories/proposals-repository.js +220 -0
  328. package/dist/storage/repositories/recombine-repository.js +213 -0
  329. package/dist/storage/repositories/registry-cache.js +93 -0
  330. package/dist/storage/repositories/registry-index-cache-repository.js +46 -0
  331. package/dist/storage/repositories/task-history-repository.js +93 -0
  332. package/dist/storage/repositories/workflow-runs-repository.js +189 -1
  333. package/dist/storage/sqlite-pragmas.js +146 -0
  334. package/dist/tasks/backends/cron.js +1 -1
  335. package/dist/tasks/backends/index.js +9 -0
  336. package/dist/tasks/backends/launchd.js +1 -1
  337. package/dist/tasks/backends/schtasks.js +1 -1
  338. package/dist/tasks/{resolveAkmBin.js → resolve-akm-bin.js} +2 -2
  339. package/dist/tasks/runner.js +15 -13
  340. package/dist/text-import-hook.mjs +1 -1
  341. package/dist/wiki/wiki.js +52 -11
  342. package/dist/workflows/authoring/authoring.js +123 -10
  343. package/dist/workflows/authoring/workflow-program-template.yaml +31 -0
  344. package/dist/workflows/cli.js +5 -0
  345. package/dist/workflows/db.js +138 -4
  346. package/dist/workflows/exec/brief.js +484 -0
  347. package/dist/workflows/exec/native-executor.js +975 -0
  348. package/dist/workflows/exec/param-secrets.js +115 -0
  349. package/dist/workflows/exec/report.js +1295 -0
  350. package/dist/workflows/exec/run-workflow.js +596 -0
  351. package/dist/workflows/exec/scheduler.js +100 -0
  352. package/dist/workflows/exec/step-work.js +1156 -0
  353. package/dist/workflows/exec/unit-writer.js +23 -0
  354. package/dist/workflows/exec/watch.js +116 -0
  355. package/dist/workflows/exec/worktree.js +171 -0
  356. package/dist/workflows/ir/compile.js +388 -0
  357. package/dist/workflows/ir/params.js +54 -0
  358. package/dist/workflows/ir/plan-hash.js +33 -0
  359. package/dist/workflows/ir/schema.js +4 -0
  360. package/dist/workflows/parser.js +3 -1
  361. package/dist/workflows/program/expressions.js +369 -0
  362. package/dist/workflows/program/parser.js +760 -0
  363. package/dist/workflows/program/project.js +105 -0
  364. package/dist/workflows/program/schema.js +54 -0
  365. package/dist/workflows/renderer.js +82 -5
  366. package/dist/workflows/runtime/agent-identity.js +59 -14
  367. package/dist/workflows/runtime/runs.js +248 -153
  368. package/dist/workflows/runtime/unit-checkin.js +45 -0
  369. package/dist/workflows/runtime/workflow-asset-loader.js +188 -0
  370. package/dist/workflows/validate-summary.js +26 -10
  371. package/dist/workflows/validator.js +1 -1
  372. package/docs/README.md +69 -18
  373. package/docs/data-and-telemetry.md +7 -5
  374. package/docs/migration/release-notes/0.7.0.md +1 -1
  375. package/docs/migration/release-notes/0.9.0-beta.60.md +19 -0
  376. package/docs/migration/release-notes/0.9.0.md +39 -0
  377. package/package.json +10 -10
  378. package/dist/assets/tasks/core/update-stashes.yml +0 -4
  379. package/dist/commands/db-cli.js +0 -23
  380. package/dist/indexer/db/db-backup.js +0 -376
  381. package/dist/indexer/passes/staleness-detect.js +0 -488
@@ -1,488 +0,0 @@
1
- // This Source Code Form is subject to the terms of the Mozilla Public
2
- // License, v. 2.0. If a copy of the MPL was not distributed with this
3
- // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- /**
5
- * Staleness-detection pass for `akm improve` (Phase 4A,
6
- * `.plans/0.8.0/self-improvement-enhancements-plan.md` lines 132-145).
7
- *
8
- * Activates the `deprecated` belief-state machinery shipped in Phase 1A
9
- * (commit 7b6fffe). Without this pass, nothing in the pipeline ever wrote
10
- * `beliefState: deprecated`, so the -0.15 ranking penalty and the
11
- * `matchBeliefFilter("historical")` inclusion were dormant.
12
- *
13
- * Pipeline
14
- * --------
15
- * 1. Walk every memory under `<stash>/memories/` and select candidates
16
- * whose belief state is NOT already excluded
17
- * ({contradicted, archived, deprecated}) AND whose `lastConfirmedAt`
18
- * is absent or older than the configured threshold (default 90 days).
19
- * Files without `lastConfirmedAt` fall back to file `mtime`.
20
- * 2. For each candidate, ask the configured validation-tier LLM
21
- * (`resolveValidationRunner`) whether the candidate is still current
22
- * given the top-K most-similar memories from the stash.
23
- * Strict response contract: `YES\nSUPERSEDED_BY: <ref>` or `NO`.
24
- * Anything else is treated as a parse error and the candidate is skipped.
25
- * 3. YES → write `beliefState: "deprecated"`, `supersededBy: [<ref>]`,
26
- * `lastConfirmedAt: <now>`. The supersededBy ref MUST exist in the
27
- * stash (DB lookup); if it doesn't, treat as NO.
28
- * NO → write only `lastConfirmedAt: <now>` (refreshes the staleness
29
- * window). All other frontmatter fields stay untouched.
30
- *
31
- * Caching
32
- * -------
33
- * Uses the standard `withLlmCache` wrapper with cacheVariant
34
- * `"staleness_detect"`, so re-running the pass on an unchanged file is a
35
- * no-op (no LLM call).
36
- *
37
- * Feature gate
38
- * ------------
39
- * Default OFF. Enable via `features.index.staleness_detection.enabled` (or
40
- * the boolean shorthand `features.index.staleness_detection = true`).
41
- * Threshold-days knob lives at
42
- * `features.index.staleness_detection.options.thresholdDays` (default 90).
43
- */
44
- import { createHash } from "node:crypto";
45
- import fs from "node:fs";
46
- import path from "node:path";
47
- import { assembleAsset } from "../../core/asset/asset-serialize.js";
48
- import { parseFrontmatter, parseFrontmatterBlock } from "../../core/asset/frontmatter.js";
49
- import { concurrentMap } from "../../core/concurrent.js";
50
- import { warn } from "../../core/warn.js";
51
- import { resolveValidationRunner, runnerIsLlm } from "../../integrations/agent/runner.js";
52
- import { chatCompletion } from "../../llm/client.js";
53
- import { isProcessEnabled } from "../../llm/feature-gate.js";
54
- import { findEntryIdByRef } from "../db/db.js";
55
- import { withLlmCache } from "../db/llm-cache.js";
56
- import { walkMarkdownFiles } from "../walk/walker.js";
57
- /** Frontmatter keys this pass touches. Constants so a future rename only needs to touch one site. */
58
- const FM_BELIEF_STATE = "beliefState";
59
- const FM_SUPERSEDED_BY = "supersededBy";
60
- const FM_LAST_CONFIRMED_AT = "lastConfirmedAt";
61
- /** Cache variant for `withLlmCache`. Keeps staleness results isolated from memory-inference cache rows. */
62
- const CACHE_VARIANT = "staleness_detect";
63
- /** Belief states excluded from staleness detection — already historical / archived. */
64
- const EXCLUDED_BELIEF_STATES = new Set(["contradicted", "archived", "deprecated"]);
65
- /** Default threshold in days before a memory is re-evaluated. */
66
- const DEFAULT_THRESHOLD_DAYS = 90;
67
- /** Top-K similar memories included in the LLM prompt. */
68
- const TOP_K_SIMILAR = 5;
69
- /**
70
- * Top-level entry point. Returns a zero-counters result when the feature is
71
- * disabled or no validation-tier runner is configured.
72
- */
73
- export async function runStalenessDetectionPass(ctx) {
74
- const { config, sources, signal, db } = ctx;
75
- const start = Date.now();
76
- const result = {
77
- considered: 0,
78
- deprecated: 0,
79
- confirmed: 0,
80
- skipped: 0,
81
- durationMs: 0,
82
- warnings: [],
83
- };
84
- // Feature gate — default OFF.
85
- if (!isProcessEnabled("index", "staleness_detection", config)) {
86
- result.durationMs = Date.now() - start;
87
- return result;
88
- }
89
- // The pass only writes to the primary (writable) stash. Read-only sources
90
- // would be clobbered by the next sync(), so we skip them entirely.
91
- const primary = sources[0];
92
- if (!primary) {
93
- result.durationMs = Date.now() - start;
94
- return result;
95
- }
96
- const runner = resolveValidationRunner(config);
97
- if (!runner) {
98
- result.warnings.push("staleness_detection: no validation runner configured; skipping pass");
99
- result.durationMs = Date.now() - start;
100
- return result;
101
- }
102
- if (!runnerIsLlm(runner)) {
103
- // MVP scope: only the LLM runner kind is supported. Agent/SDK runners
104
- // would require a different prompt-dispatch path that is out of scope
105
- // for the initial Phase 4A implementation.
106
- result.warnings.push(`staleness_detection: validation runner kind "${runner.kind}" not supported by MVP; configure an llm-kind validation profile`);
107
- result.durationMs = Date.now() - start;
108
- return result;
109
- }
110
- const configuredThreshold = config.index?.stalenessDetection?.thresholdDays;
111
- const thresholdDays = typeof configuredThreshold === "number" && configuredThreshold >= 0 ? configuredThreshold : DEFAULT_THRESHOLD_DAYS;
112
- const thresholdMs = thresholdDays * 24 * 60 * 60 * 1000;
113
- const now = Date.now();
114
- const candidates = collectStaleCandidates(primary.path, now, thresholdMs);
115
- result.considered = candidates.length;
116
- if (candidates.length === 0) {
117
- result.durationMs = Date.now() - start;
118
- return result;
119
- }
120
- const allMemories = collectAllMemoriesForSimilarity(primary.path);
121
- const nowIso = new Date(now).toISOString();
122
- const concurrency = runner.connection.concurrency ?? 1;
123
- const validate = (raw) => {
124
- if (!raw || typeof raw !== "object")
125
- return undefined;
126
- const r = raw;
127
- if (r.decision === "deprecated") {
128
- if (typeof r.supersededBy === "string" && r.supersededBy.trim().length > 0) {
129
- return { decision: "deprecated", supersededBy: r.supersededBy.trim() };
130
- }
131
- return undefined;
132
- }
133
- if (r.decision === "confirmed")
134
- return { decision: "confirmed" };
135
- return undefined;
136
- };
137
- const perResult = await concurrentMap(candidates, async (candidate) => {
138
- if (signal?.aborted)
139
- return undefined;
140
- const cacheKey = candidate.filePath;
141
- const cacheBody = `${candidate.filePath}\n${candidate.body}`;
142
- const decision = db
143
- ? await withLlmCache(db, cacheKey, cacheBody, false, () => askValidator(runner.connection, candidate, allMemories, signal, runner.timeoutMs), validate, undefined, CACHE_VARIANT)
144
- : await askValidator(runner.connection, candidate, allMemories, signal, runner.timeoutMs);
145
- return { candidate, decision };
146
- }, concurrency);
147
- for (const entry of perResult) {
148
- if (!entry)
149
- continue;
150
- const { candidate, decision } = entry;
151
- if (!decision) {
152
- result.skipped += 1;
153
- continue;
154
- }
155
- if (decision.decision === "deprecated") {
156
- const targetRef = decision.supersededBy ?? "";
157
- const validatedRef = validateSupersedingRef(targetRef, primary.path, db);
158
- if (!validatedRef) {
159
- // Spec line 153: never mark deprecated unless SUPERSEDED_BY exists.
160
- // Refresh lastConfirmedAt instead — the candidate is still our best
161
- // record until a real superseder shows up — and emit a warning.
162
- result.warnings.push(`staleness_detection: ${candidate.ref} reported superseded by "${targetRef}" but that ref does not exist; refreshing instead`);
163
- try {
164
- writeLastConfirmed(candidate, nowIso);
165
- result.confirmed += 1;
166
- }
167
- catch (err) {
168
- result.warnings.push(`staleness_detection: failed to refresh ${candidate.ref}: ${err instanceof Error ? err.message : String(err)}`);
169
- result.skipped += 1;
170
- }
171
- continue;
172
- }
173
- try {
174
- writeDeprecated(candidate, validatedRef, nowIso);
175
- result.deprecated += 1;
176
- }
177
- catch (err) {
178
- result.warnings.push(`staleness_detection: failed to deprecate ${candidate.ref}: ${err instanceof Error ? err.message : String(err)}`);
179
- result.skipped += 1;
180
- }
181
- }
182
- else {
183
- try {
184
- writeLastConfirmed(candidate, nowIso);
185
- result.confirmed += 1;
186
- }
187
- catch (err) {
188
- result.warnings.push(`staleness_detection: failed to refresh ${candidate.ref}: ${err instanceof Error ? err.message : String(err)}`);
189
- result.skipped += 1;
190
- }
191
- }
192
- }
193
- result.durationMs = Date.now() - start;
194
- return result;
195
- }
196
- function collectStaleCandidates(stashRoot, now, thresholdMs) {
197
- const memoriesDir = path.join(stashRoot, "memories");
198
- if (!fs.existsSync(memoriesDir))
199
- return [];
200
- const out = [];
201
- for (const filePath of walkMarkdownFiles(memoriesDir)) {
202
- let raw;
203
- let stat;
204
- try {
205
- raw = fs.readFileSync(filePath, "utf8");
206
- stat = fs.statSync(filePath);
207
- }
208
- catch {
209
- continue;
210
- }
211
- const parsed = parseFrontmatter(raw);
212
- const belief = typeof parsed.data[FM_BELIEF_STATE] === "string" ? parsed.data[FM_BELIEF_STATE] : "";
213
- if (EXCLUDED_BELIEF_STATES.has(belief))
214
- continue;
215
- const lastConfirmedMs = parseDateMs(parsed.data[FM_LAST_CONFIRMED_AT]);
216
- const signalMs = lastConfirmedMs ?? stat.mtimeMs;
217
- const ageMs = now - signalMs;
218
- if (ageMs < thresholdMs)
219
- continue;
220
- const name = toMemoryName(memoriesDir, filePath);
221
- if (!name)
222
- continue;
223
- out.push({
224
- filePath,
225
- ref: `memory:${name}`,
226
- name,
227
- data: parsed.data,
228
- body: parsed.content,
229
- lastSignalMs: signalMs,
230
- });
231
- }
232
- return out;
233
- }
234
- function collectAllMemoriesForSimilarity(stashRoot) {
235
- const memoriesDir = path.join(stashRoot, "memories");
236
- if (!fs.existsSync(memoriesDir))
237
- return [];
238
- const out = [];
239
- const now = Date.now();
240
- for (const filePath of walkMarkdownFiles(memoriesDir)) {
241
- let raw;
242
- let stat;
243
- try {
244
- raw = fs.readFileSync(filePath, "utf8");
245
- stat = fs.statSync(filePath);
246
- }
247
- catch {
248
- continue;
249
- }
250
- const parsed = parseFrontmatter(raw);
251
- const name = toMemoryName(memoriesDir, filePath);
252
- if (!name)
253
- continue;
254
- const title = typeof parsed.data.title === "string" ? parsed.data.title : "";
255
- const description = typeof parsed.data.description === "string" ? parsed.data.description : "";
256
- const body = parsed.content;
257
- out.push({
258
- ref: `memory:${name}`,
259
- name,
260
- filePath,
261
- body,
262
- title,
263
- description,
264
- tokens: tokenize(`${title} ${description} ${body}`),
265
- ageMs: now - stat.mtimeMs,
266
- });
267
- }
268
- return out;
269
- }
270
- function toMemoryName(memoriesDir, filePath) {
271
- const rel = path.relative(memoriesDir, filePath);
272
- if (!rel || rel.startsWith(".."))
273
- return undefined;
274
- return rel.replace(/\\/g, "/").replace(/\.md$/i, "");
275
- }
276
- function parseDateMs(value) {
277
- if (typeof value !== "string")
278
- return undefined;
279
- const ms = Date.parse(value);
280
- return Number.isFinite(ms) ? ms : undefined;
281
- }
282
- // ── Similarity (lightweight FTS-style fallback) ─────────────────────────────
283
- /**
284
- * Token-overlap similarity is the FTS fallback for the prompt's "similar
285
- * memories" payload. Embedding-aware nearest-neighbor lookup is a future
286
- * enhancement; the prompt only needs enough context to ground a YES/NO
287
- * decision, which token overlap of body + description provides.
288
- */
289
- function tokenize(text) {
290
- const out = new Set();
291
- const lower = text.toLowerCase();
292
- const tokens = lower.match(/[a-z0-9][a-z0-9_-]{2,}/g);
293
- if (!tokens)
294
- return out;
295
- for (const t of tokens)
296
- out.add(t);
297
- return out;
298
- }
299
- function pickSimilar(candidate, all) {
300
- const candTokens = tokenize(`${typeof candidate.data.title === "string" ? candidate.data.title : ""} ${typeof candidate.data.description === "string" ? candidate.data.description : ""} ${candidate.body}`);
301
- const candMs = candidate.lastSignalMs;
302
- const scored = [];
303
- for (const snap of all) {
304
- if (snap.ref === candidate.ref)
305
- continue;
306
- // Prefer memories more recent than the candidate so the validator can
307
- // see what may have superseded it.
308
- if (snap.ageMs >= Date.now() - candMs)
309
- continue;
310
- let overlap = 0;
311
- for (const t of candTokens)
312
- if (snap.tokens.has(t))
313
- overlap += 1;
314
- if (overlap === 0)
315
- continue;
316
- scored.push({ snap, score: overlap });
317
- }
318
- scored.sort((a, b) => b.score - a.score);
319
- return scored.slice(0, TOP_K_SIMILAR).map((s) => s.snap);
320
- }
321
- // ── LLM dispatch ────────────────────────────────────────────────────────────
322
- const SYSTEM_PROMPT = "You are a belief-state classifier for a memory store. Given a candidate memory and a list of more-recent similar memories from the same store, decide whether the candidate is still current or has been superseded.\n\n" +
323
- "Respond on the first line with exactly YES or NO.\n" +
324
- "If YES, the second line MUST be of the form `SUPERSEDED_BY: <ref>` where <ref> is the exact ref of the superseding memory from the list provided. Do NOT invent refs.\n" +
325
- "If NO, do not include any additional lines.\n" +
326
- "No prose, no preamble, no markdown.";
327
- async function askValidator(connection, candidate, allMemories, signal, timeoutMs) {
328
- const similar = pickSimilar(candidate, allMemories);
329
- if (similar.length === 0) {
330
- // No more-recent similar memories — there is nothing the candidate could
331
- // have been superseded by. Treat as confirmed without paying for an LLM call.
332
- return { decision: "confirmed" };
333
- }
334
- const messages = [
335
- { role: "system", content: SYSTEM_PROMPT },
336
- { role: "user", content: buildPrompt(candidate, similar) },
337
- ];
338
- let raw;
339
- try {
340
- raw = await chatCompletion(connection, messages, {
341
- ...(typeof timeoutMs === "number" ? { timeoutMs } : {}),
342
- ...(signal ? { signal } : {}),
343
- temperature: 0,
344
- });
345
- }
346
- catch (err) {
347
- warn(`[improve] staleness detection LLM call failed for ${candidate.ref}: ${err instanceof Error ? err.message : String(err)}`);
348
- return undefined;
349
- }
350
- return parseStalenessResponse(raw);
351
- }
352
- export function buildPrompt(candidate, similar) {
353
- const lines = [];
354
- lines.push(`Candidate memory: ${candidate.ref}`);
355
- if (typeof candidate.data.title === "string" && candidate.data.title.trim().length > 0) {
356
- lines.push(`Title: ${candidate.data.title.trim()}`);
357
- }
358
- if (typeof candidate.data.description === "string" && candidate.data.description.trim().length > 0) {
359
- lines.push(`Description: ${candidate.data.description.trim()}`);
360
- }
361
- lines.push("Body:");
362
- lines.push(candidate.body.trim());
363
- lines.push("");
364
- lines.push(`Similar more-recent memories (top ${similar.length}):`);
365
- for (const s of similar) {
366
- lines.push("---");
367
- lines.push(`Ref: ${s.ref}`);
368
- if (s.title)
369
- lines.push(`Title: ${s.title}`);
370
- if (s.description)
371
- lines.push(`Description: ${s.description}`);
372
- lines.push("Body:");
373
- lines.push(s.body.trim());
374
- }
375
- lines.push("");
376
- lines.push("Question: Given the more-recent similar memories above, has the candidate memory been superseded, or is it still current?");
377
- return lines.join("\n");
378
- }
379
- /** Exported for direct unit testing. */
380
- export function parseStalenessResponse(raw) {
381
- if (typeof raw !== "string")
382
- return undefined;
383
- const trimmed = raw.trim();
384
- if (trimmed.length === 0)
385
- return undefined;
386
- const lines = trimmed
387
- .split(/\r?\n/)
388
- .map((l) => l.trim())
389
- .filter((l) => l.length > 0);
390
- if (lines.length === 0)
391
- return undefined;
392
- const head = lines[0]?.toUpperCase();
393
- if (head === "NO")
394
- return { decision: "confirmed" };
395
- if (head === "YES") {
396
- // Find the SUPERSEDED_BY line; tolerate it being anywhere in the body
397
- // but require an exact prefix match.
398
- for (const line of lines.slice(1)) {
399
- const m = line.match(/^SUPERSEDED_BY:\s*(\S.*)$/i);
400
- if (m?.[1]) {
401
- const ref = m[1].trim();
402
- if (ref.length > 0)
403
- return { decision: "deprecated", supersededBy: ref };
404
- }
405
- }
406
- return undefined; // YES without a SUPERSEDED_BY line is a parse error.
407
- }
408
- return undefined;
409
- }
410
- // ── Ref validation ──────────────────────────────────────────────────────────
411
- /**
412
- * Validate that the proposed `supersededBy` ref actually exists. We first try
413
- * the indexed DB (canonical source of truth when available), then fall back to
414
- * an on-disk filesystem probe under `<stash>/memories/<name>.md` so the pass
415
- * works even on stashes that have never been indexed.
416
- */
417
- function validateSupersedingRef(refStr, stashRoot, db) {
418
- const trimmed = refStr.trim();
419
- if (!trimmed)
420
- return undefined;
421
- if (db) {
422
- try {
423
- const id = findEntryIdByRef(db, trimmed);
424
- if (typeof id === "number")
425
- return trimmed;
426
- }
427
- catch {
428
- // Fall through to filesystem probe.
429
- }
430
- }
431
- const m = trimmed.match(/^memory:(.+)$/);
432
- if (!m)
433
- return undefined;
434
- const filePath = path.join(stashRoot, "memories", `${m[1]}.md`);
435
- if (fs.existsSync(filePath))
436
- return trimmed;
437
- return undefined;
438
- }
439
- // ── Frontmatter writes ──────────────────────────────────────────────────────
440
- /**
441
- * Write `beliefState: deprecated`, `supersededBy`, and `lastConfirmedAt` to
442
- * the candidate's frontmatter. All other fields are preserved verbatim. Uses
443
- * the same atomic-write shape as `markParentProcessed()` in
444
- * `memory-inference.ts`: re-read the file from disk, parse the YAML block,
445
- * stitch a new block in front of the original body bytes.
446
- */
447
- function writeDeprecated(candidate, supersededByRef, nowIso) {
448
- const raw = fs.readFileSync(candidate.filePath, "utf8");
449
- const block = parseFrontmatterBlock(raw);
450
- const baseFm = block ? { ...parseFrontmatter(raw).data } : {};
451
- const nextFm = {
452
- ...baseFm,
453
- [FM_BELIEF_STATE]: "deprecated",
454
- [FM_SUPERSEDED_BY]: dedupeStringArray([...stringArrayOrEmpty(baseFm[FM_SUPERSEDED_BY]), supersededByRef]),
455
- [FM_LAST_CONFIRMED_AT]: nowIso,
456
- };
457
- writeFrontmatterAtomic(candidate.filePath, nextFm, block?.content ?? raw);
458
- }
459
- /**
460
- * Strict additive frontmatter write: ONLY `lastConfirmedAt` is touched.
461
- * Every other field in the file is preserved as-is.
462
- */
463
- function writeLastConfirmed(candidate, nowIso) {
464
- const raw = fs.readFileSync(candidate.filePath, "utf8");
465
- const block = parseFrontmatterBlock(raw);
466
- const baseFm = block ? { ...parseFrontmatter(raw).data } : {};
467
- const nextFm = {
468
- ...baseFm,
469
- [FM_LAST_CONFIRMED_AT]: nowIso,
470
- };
471
- writeFrontmatterAtomic(candidate.filePath, nextFm, block?.content ?? raw);
472
- }
473
- function writeFrontmatterAtomic(filePath, frontmatter, body) {
474
- fs.writeFileSync(filePath, assembleAsset(frontmatter, body), "utf8");
475
- }
476
- function stringArrayOrEmpty(value) {
477
- if (!Array.isArray(value))
478
- return [];
479
- return value.filter((v) => typeof v === "string" && v.trim().length > 0);
480
- }
481
- function dedupeStringArray(values) {
482
- return [...new Set(values.map((v) => v.trim()).filter((v) => v.length > 0))];
483
- }
484
- // ── Body hash helper (exported for tests) ───────────────────────────────────
485
- /** Internal helper exported only for testing — mirrors `computeBodyHash`. */
486
- export function _stalenessBodyHash(filePath, body) {
487
- return createHash("sha256").update(`${filePath}\n${body}`).digest("hex");
488
- }