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
@@ -1,337 +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
- * Distill quality-gate cluster — LLM-as-judge, quality-rejection envelope
6
- * writer, and output-salience persistence. Extracted verbatim from
7
- * `distill.ts` so the main `akmDistill` orchestrator and the memory→knowledge
8
- * promotion branch (`promote-memory.ts`) can share the same helpers without a
9
- * circular import. Logic is byte-identical to the pre-extraction inline code.
10
- */
11
- import fs from "node:fs";
12
- import path from "node:path";
13
- import { parseRefInput } from "../../../core/asset/resolve-ref.js";
14
- import { timestampForFilename } from "../../../core/common.js";
15
- import { ConfigError } from "../../../core/errors.js";
16
- import { appendEvent } from "../../../core/events.js";
17
- import { parseEmbeddedJsonResponse } from "../../../core/parse.js";
18
- import { getDistillRejectedDir } from "../../../core/paths.js";
19
- import { withStateDb } from "../../../core/state-db.js";
20
- import { recordWrittenPath } from "../../../core/write-provenance.js";
21
- import { callStructured } from "../../../llm/structured-call.js";
22
- import { akmSearch } from "../../read/search.js";
23
- import { scoreEncodingSalience } from "../encoding-salience.js";
24
- import { resolveImproveLlmExecution } from "../execution.js";
25
- import { computeSalience, upsertAssetSalience } from "../salience.js";
26
- // ── D-4 / #390: Top-3 similar lessons retrieval ──────────────────────────────
27
- /**
28
- * Default implementation: use akmSearch to find top-N similar lesson assets.
29
- * Returns empty array when search fails or returns no results.
30
- * Requires embedding configured for semantic similarity; degrades gracefully.
31
- */
32
- export async function fetchTopSimilarLessons(query, n, _stashDir) {
33
- try {
34
- const result = await akmSearch({
35
- query,
36
- type: "lesson",
37
- limit: n,
38
- skipLogging: true,
39
- eventSource: "improve",
40
- });
41
- const hits = result?.hits ?? [];
42
- return hits
43
- .filter((h) => "path" in h && typeof h.path === "string")
44
- .slice(0, n)
45
- .map((h) => {
46
- let content = "";
47
- try {
48
- if (h.path && fs.existsSync(h.path)) {
49
- content = fs.readFileSync(h.path, "utf8");
50
- }
51
- }
52
- catch {
53
- /* best-effort */
54
- }
55
- return { ref: h.ref, content };
56
- });
57
- }
58
- catch {
59
- return [];
60
- }
61
- }
62
- // ── LLM-as-judge quality gate (P2-B) ────────────────────────────────────────
63
- /**
64
- * D-4 / #390: Build the LLM-as-judge prompt.
65
- *
66
- * When similarLessons are provided (top-3 by embedding similarity), they are
67
- * included in the context so the judge can lower the score for near-duplicates.
68
- * Voyager arXiv:2305.16291 — skill library admission requires similarity check
69
- * against the existing library. A-MEM arXiv:2502.12110 — new notes are checked
70
- * against existing notes before linking.
71
- */
72
- export function buildJudgePrompt(lessonContent, sourceContent, similarLessons) {
73
- const lines = [
74
- "You are evaluating a proposed lesson asset for an akm knowledge base.",
75
- "",
76
- "Score this lesson on each criterion from 1 (poor) to 5 (excellent):",
77
- "1. NOVELTY: Does the lesson add information not already present in the source asset?",
78
- "2. ACTIONABILITY: Can an agent follow this lesson without additional context?",
79
- "3. NON-REDUNDANCY: Is this lesson meaningfully different from what the source already says?",
80
- "",
81
- "Source asset content:",
82
- "```",
83
- sourceContent.slice(0, 2000),
84
- "```",
85
- ];
86
- if (similarLessons && similarLessons.length > 0) {
87
- lines.push("");
88
- lines.push("Existing similar lessons (top-3 by similarity). Rate lower if the proposed lesson is substantially similar to any of these:");
89
- for (const sl of similarLessons) {
90
- lines.push(`\nExisting lesson ref: ${sl.ref}`);
91
- lines.push("```");
92
- lines.push(sl.content.slice(0, 500));
93
- lines.push("```");
94
- }
95
- }
96
- lines.push("");
97
- lines.push("Proposed lesson content:");
98
- lines.push("```");
99
- lines.push(lessonContent.slice(0, 1000));
100
- lines.push("```");
101
- lines.push("");
102
- lines.push('Return ONLY valid JSON, no prose: {"score": <average score 1-5 as float>, "reason": "<one sentence>"}');
103
- return lines.join("\n");
104
- }
105
- function boundedDocument(content, maxChars = 6000) {
106
- if (content.length <= maxChars)
107
- return content;
108
- const half = Math.floor((maxChars - 80) / 2);
109
- return `${content.slice(0, half)}\n\n[... middle omitted for bounded judge context ...]\n\n${content.slice(-half)}`;
110
- }
111
- function buildChangedRegion(sourceContent, candidateContent) {
112
- const source = sourceContent.split("\n");
113
- const candidate = candidateContent.split("\n");
114
- let prefix = 0;
115
- while (prefix < source.length && prefix < candidate.length && source[prefix] === candidate[prefix])
116
- prefix++;
117
- let suffix = 0;
118
- while (suffix < source.length - prefix &&
119
- suffix < candidate.length - prefix &&
120
- source[source.length - 1 - suffix] === candidate[candidate.length - 1 - suffix]) {
121
- suffix++;
122
- }
123
- const removed = source.slice(prefix, source.length - suffix).join("\n");
124
- const added = candidate.slice(prefix, candidate.length - suffix).join("\n");
125
- return boundedDocument(`Removed or replaced:\n${removed || "(none)"}\n\nAdded or replacement:\n${added || "(none)"}`);
126
- }
127
- /** Build quality criteria for revising an existing asset in place. */
128
- export function buildReflectJudgePrompt(candidateContent, sourceContent, feedback) {
129
- return [
130
- "You are evaluating a proposed revision to an existing akm asset.",
131
- "",
132
- "Score this revision on each criterion from 1 (poor) to 5 (excellent):",
133
- "1. FEEDBACK ALIGNMENT: Does the revision address the supplied feedback or improve retrieval and clarity?",
134
- "2. PRESERVATION: Does it retain the source's concrete facts, code, commands, examples, and structure without truncation?",
135
- "3. QUALITY: Is the revision coherent, actionable, complete, and free of unsupported claims?",
136
- "",
137
- "Overlap with the source is expected and must not lower the score by itself; this is an in-place revision, not a new lesson.",
138
- "",
139
- "Feedback:",
140
- "```",
141
- (feedback.length > 0 ? feedback.join("\n") : "No explicit feedback supplied.").slice(0, 1000),
142
- "```",
143
- "",
144
- "Source asset content:",
145
- "```",
146
- boundedDocument(sourceContent),
147
- "```",
148
- "",
149
- "Proposed revision:",
150
- "```",
151
- boundedDocument(candidateContent),
152
- "```",
153
- "",
154
- "Changed region:",
155
- "```",
156
- buildChangedRegion(sourceContent, candidateContent),
157
- "```",
158
- "",
159
- 'Return ONLY valid JSON, no prose: {"score": <average score 1-5 as float>, "reason": "<one sentence>"}',
160
- ].join("\n");
161
- }
162
- async function runQualityJudge(feature, config, prompt, chat, options = {}) {
163
- const resolvedDefault = !options.runnerSelectionFrozen && !options.llmRunner
164
- ? resolveImproveLlmExecution({ config, processName: `${feature}-judge` })
165
- : null;
166
- if (resolvedDefault)
167
- options.onNotices?.(resolvedDefault.notices);
168
- const runner = options.llmRunner ?? resolvedDefault?.runner;
169
- if (!runner) {
170
- return { pass: false, score: -1, reason: "no LLM configured — cannot judge, failing closed" };
171
- }
172
- try {
173
- // UNGATED at the seam (no akmConfig): the quality gates' enablement is
174
- // resolved by the caller before this function runs, and a transport throw
175
- // propagates into the fail-closed catch below. `feature` labels the call.
176
- const raw = await callStructured({
177
- feature,
178
- runner,
179
- ...(options.lease ? { lease: options.lease } : {}),
180
- messages: [
181
- { role: "system", content: "Return only valid JSON. No prose." },
182
- { role: "user", content: prompt },
183
- ],
184
- request: {
185
- enableThinking: false,
186
- ...(Object.hasOwn(options, "timeoutMs") ? { timeoutMs: options.timeoutMs } : {}),
187
- ...(options.signal ? { signal: options.signal } : {}),
188
- ...(chat ? { chat } : {}),
189
- },
190
- parse: (rawResponse) => rawResponse ?? "",
191
- // Unreachable on the ungated path (errors propagate); fail closed anyway.
192
- onError: () => "",
193
- fallback: "",
194
- ...(options.onNotices ? { onNotices: options.onNotices } : {}),
195
- });
196
- const parsed = parseEmbeddedJsonResponse(raw);
197
- if (!parsed ||
198
- typeof parsed.score !== "number" ||
199
- !Number.isFinite(parsed.score) ||
200
- parsed.score < 1 ||
201
- parsed.score > 5 ||
202
- typeof parsed.reason !== "string") {
203
- return { pass: false, score: -1, reason: "judge parse failed — routed to review", reviewNeeded: true };
204
- }
205
- // D-5 / #388: Three-band system (MT-Bench arXiv:2306.05685 — ~±0.5 judge variance).
206
- // >= 3.5: auto-queue as pending (pass: true)
207
- // 2.5–3.5: review-needed band — uncertain, escalate to human (reviewNeeded: true)
208
- // < 2.5: auto-reject (pass: false)
209
- const score = parsed.score;
210
- const reason = parsed.reason ?? "";
211
- if (score >= 3.5)
212
- return { pass: true, score, reason };
213
- if (score >= 2.5)
214
- return { pass: false, score, reason, reviewNeeded: true };
215
- return { pass: false, score, reason };
216
- }
217
- catch (error) {
218
- // Invalid symbolic credentials are configuration failures, not a negative
219
- // content verdict. Provider/runtime failures retain the fail-closed result.
220
- if (error instanceof ConfigError)
221
- throw error;
222
- return { pass: false, score: -1, reason: "judge timeout/error — routed to review", reviewNeeded: true };
223
- }
224
- }
225
- /**
226
- * Run the LLM-as-judge quality gate on a proposal's content.
227
- *
228
- * Exported so reflect.ts can apply the same gate to reflect proposals (R-5 / #374).
229
- * The selected strategy's distill/reflect quality-gate setting is resolved by
230
- * the caller before this function runs.
231
- *
232
- * Fail-CLOSED (07 P0-2): returns `pass: false` (score -1) on timeout, parse
233
- * failure, or missing LLM. Minted content that cannot be judged is rejected,
234
- * not passed through — an unverifiable judge must never wave content into the
235
- * stash. The rejection is `quality_rejected`, not `review_needed`.
236
- */
237
- export async function runLessonQualityJudge(config, lessonContent, sourceContent, chat, options = {}) {
238
- return runQualityJudge("lesson_quality_gate", config, buildJudgePrompt(lessonContent, sourceContent, options.similarLessons), chat, options);
239
- }
240
- /** Judge an in-place reflect revision without applying new-lesson novelty criteria. */
241
- export async function runReflectQualityJudge(config, candidateContent, sourceContent, feedback, chat, options = {}) {
242
- return runQualityJudge("proposal_quality_gate", config, buildReflectJudgePrompt(candidateContent, sourceContent, feedback), chat, options);
243
- }
244
- // ── Quality-rejection helper ─────────────────────────────────────────────────
245
- /**
246
- * Write a rejected lesson to `$STATE/improve/distill-rejected/<stash>/`
247
- * (itlackey/akm#890), append a `distill_invoked` quality-rejected event, and
248
- * return the `quality_rejected` envelope.
249
- *
250
- * @param stash - Root stash directory.
251
- * @param inputRef - The original input ref (for the event).
252
- * @param proposalRef - The proposed lesson/knowledge ref.
253
- * @param content - The raw content that failed the quality gate.
254
- * @param score - Quality score from the judge.
255
- * @param reason - Human-readable rejection reason.
256
- * @param extraMeta - Optional additional metadata for the event.
257
- * @param eventsCtx - Events context so the emit takes appendEvent's fast path (R25).
258
- */
259
- export function writeQualityRejection(stash, inputRef, proposalRef, content, score, reason, extraMeta = {}, eligibilitySource, eventsCtx) {
260
- // D-5 / #388: reviewNeeded flag selects "review_needed" vs "quality_rejected" outcome.
261
- const outcome = extraMeta.reviewNeeded ? "review_needed" : "quality_rejected";
262
- const rejectDir = getDistillRejectedDir(stash);
263
- fs.mkdirSync(rejectDir, { recursive: true });
264
- const ts = timestampForFilename();
265
- const rejectPath = path.join(rejectDir, `${ts}-${proposalRef.replace(/[:/\\]/g, "-")}.md`);
266
- fs.writeFileSync(rejectPath, `---\nscore: ${score}\nreason: ${reason}\noutcome: ${outcome}\n---\n\n${content}`, "utf8");
267
- // #652 / itlackey/akm#890: journal it even though it now lands under
268
- // `$STATE`, outside the stash's git repo — `result.writtenPaths` reports
269
- // every path a run touched, in or out of the stash (describeRunWrittenPaths
270
- // in improve.ts falls back to the absolute path for anything outside the
271
- // stash root), and the auto-sync commit's own containment check
272
- // (resolveSyncPathSet's `relativeWrittenPath`) already drops anything
273
- // outside `repoDir` from what gets staged — recording it here cannot cause
274
- // it to be committed.
275
- recordWrittenPath(rejectPath);
276
- appendEvent({
277
- eventType: "distill_invoked",
278
- ref: inputRef,
279
- metadata: {
280
- outcome,
281
- proposalRef,
282
- score,
283
- reason,
284
- ...extraMeta,
285
- // Attribution tagging: stamp the eligibility lane so distill_invoked can be
286
- // sliced by lane downstream. See EligibilitySource.
287
- ...(eligibilitySource ? { eligibilitySource } : {}),
288
- },
289
- }, eventsCtx);
290
- return {
291
- schemaVersion: 1,
292
- ok: true,
293
- outcome,
294
- inputRef,
295
- proposalRef,
296
- score,
297
- reason,
298
- ...extraMeta,
299
- };
300
- }
301
- /**
302
- * G4 — content-score a distilled OUTPUT (lesson/knowledge proposal body) and
303
- * persist it to state.db :: asset_salience with `encoding_source: "content"`.
304
- *
305
- * Lessons are refused as distill INPUTS (`DISTILL_REFUSED_INPUT_TYPES`), so
306
- * this creation-time write is their only chance to earn a real content-derived
307
- * encoding score instead of sitting on the type-weight stub forever. Best-effort:
308
- * never blocks or fails the proposal flow.
309
- */
310
- export function persistOutputEncodingSalience(ref, body, existingRefVocabulary,
311
- // Operator opt-out (improve.salience.outcomeWeightEnabled: false) must apply
312
- // here too, or distill-written rank_score rows would use WS-2 weights while
313
- // preparation uses parity weights — inconsistent salience semantics.
314
- outcomeWeightEnabled) {
315
- try {
316
- const parsedRef = parseRefInput(ref);
317
- const salienceResult = scoreEncodingSalience({
318
- body,
319
- type: parsedRef.type,
320
- existingRefVocabulary,
321
- revisionCount: 0, // a freshly distilled output IS a first encounter
322
- });
323
- withStateDb((stateDb) => {
324
- const vector = computeSalience({
325
- ref,
326
- type: parsedRef.type,
327
- retrievalFreq: 0,
328
- encodingSalience: salienceResult.score,
329
- outcomeWeightEnabled,
330
- });
331
- upsertAssetSalience(stateDb, ref, vector);
332
- });
333
- }
334
- catch {
335
- // Best-effort — scoring must never block proposal creation.
336
- }
337
- }
@@ -1,52 +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
- import fs from "node:fs";
5
- import path from "node:path";
6
- import { writeFileAtomic } from "../../core/common.js";
7
- import { getEvalCasesDir } from "../../core/paths.js";
8
- import { recordWrittenPath } from "../../core/write-provenance.js";
9
- export function writeEvalCase(stashDir, evalCase) {
10
- const evalDir = getEvalCasesDir(stashDir);
11
- fs.mkdirSync(evalDir, { recursive: true });
12
- const fileName = `${evalCase.slug}.md`;
13
- const filePath = path.join(evalDir, fileName);
14
- const content = `---
15
- ref: ${evalCase.ref}
16
- failureReason: ${evalCase.failureReason}
17
- assetType: ${evalCase.assetType}
18
- rejectedAt: ${evalCase.rejectedAt}
19
- source: ${evalCase.source}
20
- ---
21
-
22
- # Eval Case: ${evalCase.ref}
23
-
24
- **Failure reason:** ${evalCase.failureReason}
25
- **Source:** ${evalCase.source}
26
- **Asset type:** ${evalCase.assetType}
27
-
28
- This case was automatically captured when a distillation or proposal was rejected.
29
- Use it as a regression test: future improve runs on this ref should not produce
30
- output that would be rejected for the same reason.
31
- `;
32
- writeFileAtomic(filePath, content);
33
- // itlackey/akm#890: journal it even though it now lands under `$STATE`,
34
- // outside the stash's git repo — `result.writtenPaths` still reports it
35
- // (as an absolute path; see describeRunWrittenPaths in improve.ts), and
36
- // the auto-sync commit's own containment check already excludes anything
37
- // outside the stash from what gets staged, so recording it here cannot
38
- // cause it to be committed.
39
- recordWrittenPath(filePath);
40
- return filePath;
41
- }
42
- export function countEvalCases(stashDir) {
43
- const evalDir = getEvalCasesDir(stashDir);
44
- if (!fs.existsSync(evalDir))
45
- return 0;
46
- try {
47
- return fs.readdirSync(evalDir).filter((f) => f.endsWith(".md")).length;
48
- }
49
- catch {
50
- return 0;
51
- }
52
- }
@@ -1,291 +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
- * LLM-based contradiction-detection pass for derived memories (M-1 / #367).
6
- *
7
- * Runs BEFORE `analyzeMemoryCleanup` to populate `contradictedBy` frontmatter
8
- * edges so the existing `resolveFamilyContradictions` SCC resolver has real
9
- * input to work on. Without this pass the SCC resolver operates on a nearly
10
- * empty edge graph because no automated subsystem was previously generating
11
- * contradiction edges — the elegant Tarjan implementation in memory-improve.ts
12
- * had no input.
13
- *
14
- * # Algorithm
15
- *
16
- * 1. Collect all derived memories grouped by `parentRef` family.
17
- * 2. For each family, enumerate candidate pairs.
18
- * 3. For each pair, call the LLM to judge whether the two memories are in
19
- * direct factual conflict.
20
- * 4. For confirmed contradictions, append a `contradictedBy` edge to the
21
- * losing memory's frontmatter via `writeContradictEdge`
22
- * (`./memory-belief.ts`).
23
- *
24
- * That last step used to call a private near-copy of `writeContradictEdge`
25
- * living in this file. The copy had drifted (#885): it read `contradictedBy`
26
- * with `Array.isArray` only, so a SCALAR edge — live data the indexer accepts
27
- * and lint never flags — read as "no edges" and was overwritten out of
28
- * existence; and it set `beliefState: "contradicted"` unconditionally,
29
- * promoting an `archived` memory back up (archived ranks BELOW contradicted).
30
- * Both behaviors had tests, but the tests exercised the shared primitive,
31
- * which nothing called — so they guarded dead code while the live path
32
- * carried the bugs.
33
- *
34
- * # LLM Feature Gate
35
- *
36
- * The pass is gated by the selected strategy's consolidate contradiction setting.
37
- * When the gate is disabled or no LLM is configured,
38
- * the pass is a no-op and `analyzeMemoryCleanup` proceeds with only manually
39
- * annotated edges.
40
- *
41
- * # References
42
- *
43
- * - Zep / Graphiti (arXiv:2501.13956): writes contradiction edges at detection time.
44
- * - ATMS (de Kleer 1986): assumption-based truth maintenance via edge propagation.
45
- * - mem0 contradiction probe (arXiv:2504.19413): pairwise LLM-judge pattern.
46
- */
47
- import fs from "node:fs";
48
- import path from "node:path";
49
- import contradictionJudgeTemplate from "../../../assets/prompts/contradiction-judge.md" with { type: "text" };
50
- import { parseFrontmatter } from "../../../core/asset/frontmatter.js";
51
- import { parseEmbeddedJsonResponse } from "../../../core/parse.js";
52
- import { disposeLoweredExecutionDispatchLease } from "../../../integrations/agent/execution-lowering.js";
53
- import { callStructured, preflightStructuredLlmRunner } from "../../../llm/structured-call.js";
54
- import { resolveImproveLlmExecution } from "../execution.js";
55
- import { isDerivedMemory, memoryIdentityRef, resolveParentRef } from "./derived-ref.js";
56
- import { writeContradictEdge } from "./memory-belief.js";
57
- // ── Constants ────────────────────────────────────────────────────────────────
58
- /**
59
- * Minimum confidence required to write a contradiction edge. Below this
60
- * threshold the LLM may be flagging topic-overlap rather than genuine logical
61
- * exclusivity (investigation 2026-06-18).
62
- */
63
- const CONTRADICT_CONFIDENCE_THRESHOLD = 0.92;
64
- /**
65
- * Truncation limit for memory body content sent to the LLM judge.
66
- * Keeps prompts compact while preserving the key factual claims.
67
- */
68
- const BODY_TRUNCATION = 800;
69
- // ── Prompt builder ────────────────────────────────────────────────────────────
70
- function buildContradictionJudgePrompt(a, b) {
71
- return contradictionJudgeTemplate
72
- .replace("{{A_REF}}", a.ref)
73
- .replace("{{A_DESCRIPTION}}", a.description || "(none)")
74
- .replace("{{A_BODY}}", a.body.slice(0, BODY_TRUNCATION))
75
- .replace("{{B_REF}}", b.ref)
76
- .replace("{{B_DESCRIPTION}}", b.description || "(none)")
77
- .replace("{{B_BODY}}", b.body.slice(0, BODY_TRUNCATION));
78
- }
79
- // ── Filesystem helpers ────────────────────────────────────────────────────────
80
- function* walkMarkdownFilesLocal(root) {
81
- if (!fs.existsSync(root))
82
- return;
83
- for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
84
- const full = path.join(root, entry.name);
85
- if (entry.isDirectory())
86
- yield* walkMarkdownFilesLocal(full);
87
- else if (entry.isFile() && entry.name.endsWith(".md"))
88
- yield full;
89
- }
90
- }
91
- // Build the derived memory's own belief-edge IDENTITY ref from its file path.
92
- // Emits through the shared {@link memoryIdentityRef} so this — the former THIRD
93
- // hand-rolled copy of the identity-channel spelling — no longer diverges from
94
- // memory-improve's `refArray` (ref-grammar decision D-R3 identity-channel
95
- // exception, documented at `memoryIdentityRef`).
96
- function toMemoryRef(memoriesDir, filePath) {
97
- const rel = path.relative(memoriesDir, filePath);
98
- if (!rel || rel.startsWith(".."))
99
- return undefined;
100
- const name = rel.replace(/\\/g, "/").replace(/\.md$/i, "");
101
- return memoryIdentityRef(name);
102
- }
103
- // ── Edge writing ─────────────────────────────────────────────────────────────
104
- /**
105
- * Deterministically pick, for a confirmed-contradiction pair, the LOSER memory
106
- * that receives the single directed `contradictedBy` edge (SCC-resolved to
107
- * `contradicted`) and the WINNER ref that survives as the current belief.
108
- *
109
- * A SINGLE directed edge is essential. Writing mutual A↔B edges forms a 2-cycle
110
- * that {@link resolveFamilyContradictions} collapses into one strongly-connected
111
- * SINK component and refreshes BOTH members back to active — erasing the
112
- * contradiction on every run (the self-erasing bug this fix removes).
113
- *
114
- * Direction = lexicographic ref order: the ref that sorts LATER is the loser.
115
- * This is a **total order** over the family's (distinct) refs, so the induced
116
- * edges are always acyclic — a family of any size resolves to a DAG with a
117
- * single sink, never a cycle that the resolver would refresh back to active.
118
- * It is also immutable across runs (unlike file mtime, which the resolver
119
- * bumps when it rewrites loser files), so detection is idempotent. Ref order
120
- * carries no recency meaning — no derived-memory writer sets a `createdAt`/
121
- * timestamp today — but the mechanism only needs a stable, acyclic direction;
122
- * eliminating worst-case self-erasure, not ranking by recency, is the goal.
123
- */
124
- function pickContradictionLoser(a, b) {
125
- return a.ref < b.ref ? { loser: b, winnerRef: a.ref } : { loser: a, winnerRef: b.ref };
126
- }
127
- // ── Main entry point ──────────────────────────────────────────────────────────
128
- /**
129
- * Run the LLM-based contradiction-detection pass on derived memories in
130
- * `<stashDir>/memories/`. Writes `contradictedBy` frontmatter edges for
131
- * confirmed contradiction pairs so the subsequent `resolveFamilyContradictions`
132
- * SCC pass has edges to work on.
133
- *
134
- * @param stashDir - Root stash directory.
135
- * @param config - Loaded AKM config (used to access LLM settings).
136
- * @param chat - Optional test-only chat seam.
137
- */
138
- export async function detectAndWriteContradictions(stashDir, config, chat, strategy, resolvedRunner) {
139
- const result = {
140
- familiesExamined: 0,
141
- pairsChecked: 0,
142
- edgesWritten: 0,
143
- warnings: [],
144
- };
145
- if (!(strategy?.processes?.consolidate?.contradictionDetection?.enabled ?? false))
146
- return result;
147
- const noticesByKey = new Map();
148
- const resolvedExecution = resolvedRunner === undefined
149
- ? resolveImproveLlmExecution({
150
- config,
151
- profile: strategy,
152
- process: strategy?.processes?.consolidate,
153
- processName: "memory-contradiction-detection",
154
- })
155
- : null;
156
- for (const notice of resolvedExecution?.notices ?? [])
157
- noticesByKey.set(JSON.stringify(notice), notice);
158
- const contradictionRunner = resolvedRunner === null ? undefined : (resolvedRunner ?? resolvedExecution?.runner);
159
- if (!contradictionRunner)
160
- return result;
161
- // Collect derived memories grouped by parent.
162
- const memoriesDir = path.join(stashDir, "memories");
163
- const byParent = new Map();
164
- for (const filePath of walkMarkdownFilesLocal(memoriesDir)) {
165
- let raw;
166
- try {
167
- raw = fs.readFileSync(filePath, "utf8");
168
- }
169
- catch {
170
- continue;
171
- }
172
- const parsed = parseFrontmatter(raw);
173
- const ref = toMemoryRef(memoriesDir, filePath);
174
- if (!ref)
175
- continue;
176
- // Key the shared derived-ref helpers on the memory NAME (stash-relative, no
177
- // extension) — the same key the consumer uses — so producer and consumer
178
- // resolve the identical parent (R12). This intentionally widens the producer
179
- // to honour `derivedFrom` and normalised `source:` values it previously
180
- // dropped (pinned by derived-ref.test.ts).
181
- const name = ref.slice("memory:".length);
182
- if (!isDerivedMemory(name, parsed.data))
183
- continue;
184
- const parentRef = resolveParentRef(name, parsed.data);
185
- if (!parentRef)
186
- continue;
187
- const entry = {
188
- filePath,
189
- ref,
190
- parentRef,
191
- body: parsed.content.trim(),
192
- description: typeof parsed.data.description === "string" ? parsed.data.description : "",
193
- };
194
- const family = byParent.get(parentRef) ?? [];
195
- family.push(entry);
196
- byParent.set(parentRef, family);
197
- }
198
- const candidatePairs = [];
199
- for (const [, family] of byParent) {
200
- if (family.length < 2)
201
- continue;
202
- result.familiesExamined++;
203
- for (let i = 0; i < family.length - 1; i++) {
204
- for (let j = i + 1; j < family.length; j++) {
205
- const a = family[i];
206
- const b = family[j];
207
- if (!a || !b)
208
- continue;
209
- // Resolve the directed edge up front (independent of the judge — it is
210
- // decided by lexicographic ref order). Skip when that single loser→winner
211
- // edge already exists (no new information; avoids re-judging resolved
212
- // pairs across runs).
213
- //
214
- const aParsed = parseFrontmatter(fs.readFileSync(a.filePath, "utf8"));
215
- const bParsed = parseFrontmatter(fs.readFileSync(b.filePath, "utf8"));
216
- const { loser, winnerRef } = pickContradictionLoser(a, b);
217
- const loserData = loser === a ? aParsed.data : bParsed.data;
218
- const loserCB = Array.isArray(loserData.contradictedBy) ? loserData.contradictedBy : [];
219
- if (loserCB.includes(winnerRef))
220
- continue;
221
- candidatePairs.push({ a, b, loser, winnerRef });
222
- }
223
- }
224
- }
225
- if (candidatePairs.length === 0) {
226
- const notices = Object.freeze([...noticesByKey.values()]);
227
- return notices.length > 0 ? { ...result, notices } : result;
228
- }
229
- const dispatchLease = await preflightStructuredLlmRunner(contradictionRunner);
230
- try {
231
- for (const { a, b, loser, winnerRef } of candidatePairs) {
232
- const prompt = buildContradictionJudgePrompt(a, b);
233
- const judgeResult = await callStructured({
234
- feature: "memory_contradiction_detection",
235
- akmConfig: config,
236
- // Resolver-less key: the strategy decision IS the gate (default-off).
237
- enabled: true,
238
- runner: contradictionRunner,
239
- lease: dispatchLease,
240
- messages: [
241
- { role: "system", content: "Return only valid JSON. No prose." },
242
- { role: "user", content: prompt },
243
- ],
244
- ...(chat ? { request: { chat } } : {}),
245
- onNotices: (notices) => {
246
- for (const notice of notices)
247
- noticesByKey.set(JSON.stringify(notice), notice);
248
- },
249
- parse: (raw) => raw ?? null,
250
- // A transport throw used to escape the gated fn into the gate's
251
- // catch and take the null fallback ("skip"); onError reproduces it.
252
- onError: () => null,
253
- fallback: null, // null means "skip" — gate disabled or LLM call failed.
254
- });
255
- result.pairsChecked++;
256
- if (!judgeResult)
257
- continue; // Feature gate disabled or LLM call failed.
258
- let parsed = null;
259
- try {
260
- parsed = parseEmbeddedJsonResponse(judgeResult);
261
- }
262
- catch {
263
- result.warnings.push(`Could not parse contradiction judge response for pair ${a.ref} / ${b.ref}`);
264
- continue;
265
- }
266
- if (!parsed?.contradicts)
267
- continue;
268
- const confidence = typeof parsed.confidence === "number" ? parsed.confidence : 0;
269
- if (confidence < CONTRADICT_CONFIDENCE_THRESHOLD) {
270
- result.warnings.push(`Pair ${a.ref} / ${b.ref}: confidence ${confidence.toFixed(2)} below ${CONTRADICT_CONFIDENCE_THRESHOLD} threshold — skipped.`);
271
- continue;
272
- }
273
- // Write a SINGLE directed contradiction edge: the losing (older) memory
274
- // gets `contradictedBy` pointing to the winner. A mutual A↔B pair forms
275
- // a 2-cycle that the SCC resolver refreshes back to active, erasing the
276
- // contradiction every run (see pickContradictionLoser).
277
- try {
278
- const wrote = writeContradictEdge(loser.filePath, winnerRef);
279
- result.edgesWritten += wrote ? 1 : 0;
280
- }
281
- catch (err) {
282
- result.warnings.push(`Failed to write contradiction edge ${loser.ref} -> ${winnerRef}: ${err instanceof Error ? err.message : String(err)}`);
283
- }
284
- }
285
- }
286
- finally {
287
- disposeLoweredExecutionDispatchLease(dispatchLease);
288
- }
289
- const notices = Object.freeze([...noticesByKey.values()]);
290
- return notices.length > 0 ? { ...result, notices } : result;
291
- }