akm-cli 0.9.17-alpha.2 → 0.9.17-alpha.4

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 (343) hide show
  1. package/CHANGELOG.md +756 -0
  2. package/dist/akm +94 -196
  3. package/dist/cli/shared.js +6 -2
  4. package/dist/cli.js +22 -9
  5. package/dist/commands/agent/agent-dispatch.js +1 -1
  6. package/dist/commands/command/command-execution.js +24 -62
  7. package/dist/commands/feedback-cli.js +0 -1
  8. package/dist/commands/health/accept-rate.js +2 -2
  9. package/dist/commands/health/checks.js +30 -75
  10. package/dist/commands/health/config-skew.js +38 -0
  11. package/dist/commands/health/egress.js +54 -0
  12. package/dist/commands/health/html-report.js +0 -38
  13. package/dist/commands/health/improve-metrics.js +123 -562
  14. package/dist/commands/health/plugin-staleness.js +53 -3
  15. package/dist/commands/health/renderers.js +12 -4
  16. package/dist/commands/health/report-view-model.js +11 -106
  17. package/dist/commands/health/types-improve.js +4 -19
  18. package/dist/commands/health/windows.js +64 -73
  19. package/dist/commands/health.js +122 -143
  20. package/dist/commands/improve/consolidate/chunking.js +25 -100
  21. package/dist/commands/improve/consolidate/sanitize.js +54 -149
  22. package/dist/commands/improve/consolidate.js +538 -1075
  23. package/dist/commands/improve/content-hash.js +16 -24
  24. package/dist/commands/improve/distill/content-repair.js +18 -100
  25. package/dist/commands/improve/distill-guards.js +20 -81
  26. package/dist/commands/improve/distill-promotion-policy.js +23 -243
  27. package/dist/commands/improve/distill.js +608 -1075
  28. package/dist/commands/improve/eligibility.js +126 -400
  29. package/dist/commands/improve/execution.js +3 -5
  30. package/dist/commands/improve/extract.js +487 -1046
  31. package/dist/commands/improve/feedback-valence.js +0 -25
  32. package/dist/commands/improve/improve-cli.js +29 -166
  33. package/dist/commands/improve/improve-result-file.js +10 -66
  34. package/dist/commands/improve/improve-strategies.js +12 -7
  35. package/dist/commands/improve/improve-usage-report.js +18 -64
  36. package/dist/commands/improve/improve.js +443 -1063
  37. package/dist/commands/improve/ledger.js +114 -0
  38. package/dist/commands/improve/locks.js +2 -8
  39. package/dist/commands/improve/loop-stages.js +459 -1172
  40. package/dist/commands/improve/memory/derived-ref.js +12 -77
  41. package/dist/commands/improve/memory/memory-belief.js +14 -118
  42. package/dist/commands/improve/memory/memory-improve.js +4 -3
  43. package/dist/commands/improve/outcome-loop.js +28 -156
  44. package/dist/commands/improve/planner.js +5 -10
  45. package/dist/commands/improve/preparation.js +851 -2339
  46. package/dist/commands/improve/proactive-maintenance.js +34 -101
  47. package/dist/commands/improve/reflect-noise.js +104 -280
  48. package/dist/commands/improve/reflect.js +621 -1367
  49. package/dist/commands/improve/salience.js +46 -232
  50. package/dist/commands/improve/session-asset.js +19 -100
  51. package/dist/commands/improve/stage.js +323 -0
  52. package/dist/commands/proposal/drain.js +251 -644
  53. package/dist/commands/proposal/proposal-cli.js +3 -18
  54. package/dist/commands/proposal/proposal-types.js +20 -41
  55. package/dist/commands/proposal/proposal.js +1 -2
  56. package/dist/commands/proposal/propose.js +134 -160
  57. package/dist/commands/proposal/repository.js +502 -1487
  58. package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
  59. package/dist/commands/proposal/validators/proposal-validators.js +1 -1
  60. package/dist/commands/proposal/validators/proposals.js +13 -89
  61. package/dist/commands/read/curate.js +63 -413
  62. package/dist/commands/read/search-cli.js +16 -33
  63. package/dist/commands/read/search.js +17 -23
  64. package/dist/commands/read/show.js +2 -13
  65. package/dist/commands/sources/bundle-cli.js +25 -2
  66. package/dist/commands/sources/bundle-config-ops.js +7 -0
  67. package/dist/commands/sources/dangerous-env-audit.js +1 -2
  68. package/dist/commands/sources/info.js +2 -11
  69. package/dist/commands/sources/installed-stashes.js +197 -746
  70. package/dist/commands/sources/schema-repair.js +98 -129
  71. package/dist/commands/sources/source-add.js +62 -12
  72. package/dist/commands/sources/stash-cli.js +1 -1
  73. package/dist/commands/tasks/explain.js +10 -13
  74. package/dist/commands/tasks/tasks-cli.js +9 -8
  75. package/dist/commands/tasks/tasks.js +326 -930
  76. package/dist/commands/tasks/validate.js +42 -21
  77. package/dist/commands/workflow/plan.js +22 -29
  78. package/dist/commands/workflow-cli.js +4 -4
  79. package/dist/core/adapter/adapters/akm-adapter.js +0 -1
  80. package/dist/core/adapter/adapters/akm-lint.js +2 -3
  81. package/dist/core/adapter/adapters/akm-metadata.js +11 -12
  82. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  83. package/dist/core/adapter/execution-source.js +17 -29
  84. package/dist/core/asset/resolve-ref.js +1 -1
  85. package/dist/core/bundle-id.js +42 -5
  86. package/dist/core/bundle-rename.js +291 -0
  87. package/dist/core/config/config-io.js +1 -2
  88. package/dist/core/config/config-schema.js +1 -33
  89. package/dist/core/config/config-walker.js +1 -1
  90. package/dist/core/config/config.js +163 -68
  91. package/dist/core/config/legacy-source-shape-shim.js +38 -9
  92. package/dist/core/config/schema/embedding.js +20 -5
  93. package/dist/core/config/schema/engines.js +5 -0
  94. package/dist/core/config/schema/execution.js +1 -1
  95. package/dist/core/config/schema/experimental.js +1 -1
  96. package/dist/core/config/schema/improve-processes.js +21 -95
  97. package/dist/core/config/schema/improve.js +4 -42
  98. package/dist/core/config/schema/scheduler.js +12 -12
  99. package/dist/core/config/schema/search.js +6 -22
  100. package/dist/core/env-secret-ref.js +0 -1
  101. package/dist/core/errors.js +8 -9
  102. package/dist/core/file-lock.js +76 -173
  103. package/dist/core/logs-db.js +2 -2
  104. package/dist/core/paths.js +0 -27
  105. package/dist/core/redaction.js +109 -2
  106. package/dist/core/run-lock.js +2 -5
  107. package/dist/core/spawn-env.js +1 -1
  108. package/dist/core/state/migrations.js +108 -61
  109. package/dist/core/state-db-scope.js +2 -4
  110. package/dist/core/state-db.js +126 -692
  111. package/dist/core/type-presentation.js +1 -9
  112. package/dist/core/write-source.js +293 -1012
  113. package/dist/execution/input-contract.js +1 -1
  114. package/dist/execution/resolved-request.js +135 -689
  115. package/dist/execution/source.js +63 -257
  116. package/dist/execution/target-ref.js +1 -1
  117. package/dist/indexer/bundle-identity-guard.js +2 -2
  118. package/dist/indexer/db/graph-db.js +106 -46
  119. package/dist/indexer/ensure-index.js +44 -85
  120. package/dist/indexer/graph/graph-extraction.js +340 -562
  121. package/dist/indexer/graph/graph-related.js +130 -0
  122. package/dist/indexer/index-rebuild-lock.js +3 -11
  123. package/dist/indexer/index-writer-lock.js +8 -17
  124. package/dist/indexer/index-written-assets.js +139 -151
  125. package/dist/indexer/indexer.js +524 -846
  126. package/dist/indexer/materialize-embeddings.js +60 -397
  127. package/dist/indexer/passes/memory-inference.js +81 -90
  128. package/dist/indexer/passes/metadata.js +132 -200
  129. package/dist/indexer/read-preflight.js +0 -7
  130. package/dist/indexer/scan/doc-to-entry.js +1 -3
  131. package/dist/indexer/scan/drain-dir.js +1 -1
  132. package/dist/indexer/search/db-search.js +181 -590
  133. package/dist/indexer/search/fts-query.js +30 -41
  134. package/dist/indexer/search/ranking.js +28 -154
  135. package/dist/indexer/search/search-attribution.js +12 -32
  136. package/dist/indexer/search/search-fields.js +11 -15
  137. package/dist/indexer/search/search-hit-enrichers.js +54 -85
  138. package/dist/indexer/search/search-source.js +1 -4
  139. package/dist/indexer/usage/usage-events.js +2 -7
  140. package/dist/integrations/agent/engine-fallback.js +23 -40
  141. package/dist/integrations/agent/engine-resolution.js +93 -183
  142. package/dist/integrations/agent/execution.js +507 -0
  143. package/dist/integrations/agent/model-map.js +28 -156
  144. package/dist/integrations/agent/request-lowering.js +66 -141
  145. package/dist/integrations/agent/runner-dispatch.js +143 -321
  146. package/dist/integrations/agent/runner.js +54 -14
  147. package/dist/integrations/lockfile.js +53 -101
  148. package/dist/llm/embedders/deterministic.js +2 -3
  149. package/dist/llm/embedders/profile.js +71 -0
  150. package/dist/llm/embedders/remote.js +10 -15
  151. package/dist/llm/graph-extract.js +3 -12
  152. package/dist/llm/index-passes.js +3 -5
  153. package/dist/llm/memory-infer.js +1 -2
  154. package/dist/llm/metadata-enhance.js +1 -2
  155. package/dist/llm/structured-call.js +5 -24
  156. package/dist/output/generic-render.js +23 -11
  157. package/dist/output/html-render.js +13 -10
  158. package/dist/output/render-registry.js +3 -32
  159. package/dist/output/shapes/helpers.js +2 -34
  160. package/dist/output/shapes/passthrough.js +1 -9
  161. package/dist/{indexer/search/ranking-types.js → output/text/bundle-rename.js} +4 -1
  162. package/dist/output/text/command-format.js +60 -23
  163. package/dist/output/text/helpers.js +1 -1
  164. package/dist/output/text/migrate.js +5 -14
  165. package/dist/output/text/proposal-format.js +1 -2
  166. package/dist/output/text/workflow-format.js +0 -32
  167. package/dist/output/text.js +2 -0
  168. package/dist/registry/factory.js +4 -19
  169. package/dist/registry/network.js +66 -220
  170. package/dist/registry/providers/index.js +0 -2
  171. package/dist/registry/providers/skills-sh.js +3 -14
  172. package/dist/registry/providers/static-index.js +24 -26
  173. package/dist/registry/resolve.js +55 -131
  174. package/dist/scripts/akm-migrate-node.js +43937 -93313
  175. package/dist/scripts/akm-migrate.js +43697 -93071
  176. package/dist/setup/registry-stash-loader.js +4 -13
  177. package/dist/setup/semantic-assets.js +3 -44
  178. package/dist/setup/setup.js +1 -1
  179. package/dist/setup/steps/tasks.js +25 -15
  180. package/dist/sources/provider-factory.js +17 -18
  181. package/dist/sources/providers/filesystem.js +2 -3
  182. package/dist/sources/providers/git-install.js +7 -1
  183. package/dist/sources/providers/git-provider.js +0 -3
  184. package/dist/sources/providers/git-stash.js +0 -17
  185. package/dist/sources/providers/npm.js +2 -4
  186. package/dist/sources/providers/provider-utils.js +5 -10
  187. package/dist/sources/providers/website.js +0 -2
  188. package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
  189. package/dist/sources/website-url.js +2 -2
  190. package/dist/storage/database.js +9 -35
  191. package/dist/storage/repositories/improve-ledger-repository.js +168 -0
  192. package/dist/storage/repositories/index-connection.js +34 -70
  193. package/dist/storage/repositories/index-entries-repository.js +69 -111
  194. package/dist/storage/repositories/index-entry-mapper.js +1 -2
  195. package/dist/storage/repositories/index-entry-schema.js +83 -269
  196. package/dist/storage/repositories/index-fts-repository.js +86 -256
  197. package/dist/storage/repositories/index-llm-cache-repository.js +17 -0
  198. package/dist/storage/repositories/index-meta-repository.js +6 -4
  199. package/dist/storage/repositories/index-schema.js +192 -220
  200. package/dist/storage/repositories/index-utility-repository.js +8 -29
  201. package/dist/storage/repositories/index-vec-repository.js +133 -414
  202. package/dist/storage/repositories/outcome-repository.js +2 -1
  203. package/dist/storage/repositories/proposals-repository.js +35 -0
  204. package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
  205. package/dist/storage/repositories/task-history-repository.js +26 -4
  206. package/dist/storage/repositories/workflow-runs-repository.js +53 -244
  207. package/dist/storage/sqlite-migrations.js +136 -0
  208. package/dist/storage/sqlite-pragmas.js +11 -9
  209. package/dist/storage/sqlite-transaction.js +170 -0
  210. package/dist/storage/state-db-integrity.js +34 -27
  211. package/dist/tasks/activation-config.js +134 -62
  212. package/dist/tasks/backends/cron.js +129 -277
  213. package/dist/tasks/backends/exec-utils.js +2 -5
  214. package/dist/tasks/backends/launchd.js +125 -745
  215. package/dist/tasks/backends/schtasks.js +101 -620
  216. package/dist/tasks/prepare/prepare-support.js +5 -15
  217. package/dist/tasks/prepare/prepare.js +0 -2
  218. package/dist/tasks/resolve-akm-bin.js +20 -79
  219. package/dist/tasks/run/attempt-lifecycle.js +0 -1
  220. package/dist/tasks/scheduler-binding.js +18 -238
  221. package/dist/tasks/scheduler-invocation.js +52 -52
  222. package/dist/tasks/scheduler-lock.js +53 -0
  223. package/dist/tasks/scheduler-sync.js +363 -679
  224. package/dist/tasks/source/parse-task-source.js +160 -10
  225. package/dist/tasks/source/task-source-v3-frozen.js +3 -4
  226. package/dist/tasks/source/task-to-v4.js +2 -2
  227. package/dist/workflows/authoring/authoring.js +3 -12
  228. package/dist/workflows/compile.js +211 -0
  229. package/dist/workflows/concurrency-policy.js +13 -74
  230. package/dist/workflows/exec/child-invocation.js +3 -17
  231. package/dist/workflows/exec/child-workflow.js +32 -141
  232. package/dist/workflows/exec/dispatch-redaction.js +13 -53
  233. package/dist/workflows/exec/environment.js +98 -0
  234. package/dist/workflows/exec/exec-unit.js +33 -140
  235. package/dist/workflows/exec/frozen-judge.js +7 -59
  236. package/dist/workflows/exec/native-executor.js +82 -341
  237. package/dist/workflows/exec/param-secrets.js +29 -47
  238. package/dist/workflows/exec/run-workflow.js +154 -387
  239. package/dist/workflows/exec/scheduler.js +9 -36
  240. package/dist/workflows/exec/step-work.js +127 -430
  241. package/dist/workflows/exec/unit-dispatch.js +11 -63
  242. package/dist/workflows/exec/unit-writer.js +8 -52
  243. package/dist/workflows/exec/worktree.js +39 -273
  244. package/dist/workflows/freeze/child-output-references.js +4 -15
  245. package/dist/workflows/freeze/environment.js +99 -92
  246. package/dist/workflows/freeze/freeze.js +172 -0
  247. package/dist/workflows/freeze/step-values.js +19 -21
  248. package/dist/workflows/freeze/targets/child-workflow.js +23 -92
  249. package/dist/workflows/freeze/targets/command.js +10 -33
  250. package/dist/workflows/freeze/targets/script.js +5 -12
  251. package/dist/workflows/freeze/targets/shell.js +3 -6
  252. package/dist/workflows/freeze/targets/task.js +25 -80
  253. package/dist/workflows/freeze/task-bindings.js +20 -67
  254. package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
  255. package/dist/workflows/ir/params.js +6 -51
  256. package/dist/workflows/ir/plan-hash.js +2 -34
  257. package/dist/workflows/parser.js +140 -43
  258. package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
  259. package/dist/workflows/renderer.js +36 -69
  260. package/dist/workflows/resource-limits.js +12 -120
  261. package/dist/workflows/runtime/agent-identity.js +8 -40
  262. package/dist/workflows/runtime/run-outputs.js +3 -6
  263. package/dist/workflows/runtime/run-plan.js +316 -0
  264. package/dist/workflows/runtime/runs.js +48 -200
  265. package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
  266. package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
  267. package/dist/workflows/validate-summary.js +2 -7
  268. package/docs/integration/bundling-akm.md +49 -42
  269. package/docs/migration/README.md +1 -0
  270. package/docs/migration/release-notes/0.9.17.md +41 -0
  271. package/docs/migration/v0.9.1-to-v0.9.2.md +19 -7
  272. package/docs/reference/cli.md +182 -125
  273. package/docs/reference/configuration.md +49 -56
  274. package/docs/reference/data-and-telemetry.md +19 -20
  275. package/docs/reference/tasks.md +86 -38
  276. package/docs/reference/workflow-schema.md +14 -18
  277. package/docs/reference/workflows.md +6 -9
  278. package/package.json +1 -1
  279. package/schemas/akm-config.json +87 -406
  280. package/dist/commands/health/advisories.js +0 -150
  281. package/dist/commands/health/metrics.js +0 -329
  282. package/dist/commands/health/surfaces.js +0 -102
  283. package/dist/commands/improve/anti-collapse.js +0 -83
  284. package/dist/commands/improve/collapse-detector.js +0 -432
  285. package/dist/commands/improve/consolidate/eligibility.js +0 -48
  286. package/dist/commands/improve/consolidate/merge.js +0 -146
  287. package/dist/commands/improve/distill/promote-memory.js +0 -329
  288. package/dist/commands/improve/distill/quality-gate.js +0 -500
  289. package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
  290. package/dist/commands/improve/proposal-envelope.js +0 -31
  291. package/dist/commands/improve/run-context.js +0 -123
  292. package/dist/commands/improve/shared.js +0 -21
  293. package/dist/commands/improve/source-identity.js +0 -28
  294. package/dist/commands/improve/triage.js +0 -96
  295. package/dist/commands/proposal/drain-policies.js +0 -151
  296. package/dist/commands/sources/update-transaction.js +0 -220
  297. package/dist/core/action-contributors.js +0 -28
  298. package/dist/core/config/config-version-shim.js +0 -101
  299. package/dist/core/config/retired-experimental-keys-shim.js +0 -62
  300. package/dist/core/fs-txn.js +0 -405
  301. package/dist/core/lexical-score.js +0 -25
  302. package/dist/core/maintenance-barrier.js +0 -167
  303. package/dist/execution/executable-identity.js +0 -105
  304. package/dist/execution/guarded-source.js +0 -427
  305. package/dist/indexer/graph/graph-boost.js +0 -427
  306. package/dist/indexer/graph/graph-dedup.js +0 -95
  307. package/dist/indexer/search/name-match.js +0 -35
  308. package/dist/indexer/search/ranking-contributors.js +0 -515
  309. package/dist/indexer/walk/project-context.js +0 -192
  310. package/dist/integrations/agent/execution-cascade.js +0 -566
  311. package/dist/integrations/agent/execution-definitions.js +0 -202
  312. package/dist/integrations/agent/execution-lowering.js +0 -841
  313. package/dist/integrations/agent/execution-preparation.js +0 -98
  314. package/dist/integrations/agent/inline-execution.js +0 -74
  315. package/dist/registry/create-provider-registry.js +0 -29
  316. package/dist/registry/pinned-request-helper.js +0 -247
  317. package/dist/registry/pinned-transport.js +0 -717
  318. package/dist/sources/providers/index.js +0 -14
  319. package/dist/storage/engines/sqlite-migrations.js +0 -271
  320. package/dist/storage/repositories/canaries-repository.js +0 -107
  321. package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
  322. package/dist/storage/repositories/registry-cache.js +0 -113
  323. package/dist/tasks/scheduler-sync-preview.js +0 -52
  324. package/dist/workflows/freeze/resolve-steps.js +0 -86
  325. package/dist/workflows/freeze/source-freeze.js +0 -64
  326. package/dist/workflows/ir/compile.js +0 -321
  327. package/dist/workflows/ir/environment-v4.js +0 -330
  328. package/dist/workflows/ir/freeze-v4.js +0 -153
  329. package/dist/workflows/ir/schema-v4.js +0 -745
  330. package/dist/workflows/ir/schema.js +0 -354
  331. package/dist/workflows/program/schema.js +0 -78
  332. package/dist/workflows/runtime/checkin.js +0 -57
  333. package/dist/workflows/runtime/plan-classifier.js +0 -196
  334. package/dist/workflows/runtime/unit-checkin.js +0 -45
  335. package/dist/workflows/runtime/unit-phases.js +0 -20
  336. package/dist/workflows/schema.js +0 -4
  337. package/dist/workflows/source-ir/compile.js +0 -200
  338. package/dist/workflows/source-ir/program.js +0 -50
  339. package/dist/workflows/source-ir/result.js +0 -26
  340. package/dist/workflows/source-ir/schema.js +0 -786
  341. package/dist/workflows/source-ir/triggers.js +0 -79
  342. package/dist/workflows/source-ir/uses.js +0 -40
  343. package/dist/workflows/validator.js +0 -60
@@ -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
- }
@@ -1,31 +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
- * `emitProposal` — the proposal-envelope facade (D10 / R7).
6
- *
7
- * Every improve verb that raises a proposal (revise = reflect, learn =
8
- * distill/promote/extract, consolidate = the merge/promote ops) routes it
9
- * through this single seam instead of calling {@link createProposal} directly.
10
- *
11
- * Since WI-6.2 the envelope carries `changes: FileChange[]` + `beforeHash`:
12
- * `createProposal` derives a single-entry change set (whose `after` IS
13
- * `payload.content`) and the mint-time before-state hash for every proposal
14
- * emitted through this seam — producers stay payload-shaped and get the
15
- * envelope for free. The facade remains a pass-through: it does NOT synthesise
16
- * `sourceRun` or mutate the input — each emit site owns its own
17
- * `source`/`sourceRun`/attribution.
18
- *
19
- * Pinned by `tests/commands/improve/proposal-envelope.test.ts`.
20
- */
21
- import { createProposal, } from "../proposal/repository.js";
22
- /**
23
- * Emit a proposal through the single improve envelope seam. Equivalent to
24
- * `createProposal(ctx.stashDir, input, ctx.proposalsCtx)`; returns either the
25
- * persisted {@link import("../proposal/repository.js").Proposal} or a
26
- * {@link import("../proposal/repository.js").CreateProposalSkipped} record when a
27
- * dedup/cooldown guard fires (detect with `isProposalSkipped`).
28
- */
29
- export function emitProposal(ctx, input) {
30
- return createProposal(ctx.stashDir, input, ctx.proposalsCtx);
31
- }
@@ -1,123 +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
- * `RunContext` — the shared, run-scoped carrier for the three improve verbs
6
- * (revise = reflect, learn = extract/distill/inference, consolidate = the
7
- * memory-tier ops; D17). WI-7.5–7.8 decompose the improve god-functions into
8
- * named passes that thread a `RunContext` instead of long positional argument
9
- * lists.
10
- *
11
- * ## The D6 read-once seam
12
- *
13
- * `readAsset` is a memoizing file-read seam whose memo is **NEVER run-wide**
14
- * (D6 / top-risk #7: a run-wide memo would serve prep-time bytes after a mid-run
15
- * write). The guarantee is structural here, not merely conventional:
16
- *
17
- * - The **base** context returned by {@link createRunContext} has NO active
18
- * memo. `readAsset` on it always reads through to disk, so it can never go
19
- * stale — the safe default.
20
- * - {@link RunContext.withFreshAssetMemo} mints a sibling context that shares
21
- * every run-scoped carrier but owns a FRESH, empty memo. Memoization is
22
- * therefore opt-in per scope:
23
- * * prep's stage-body passes share ONE memo, forked only AFTER the
24
- * mutating pre-loop passes have written their ops to disk, then
25
- * discarded at stage end;
26
- * * reflect and distill each fork their own memo per verb invocation, so
27
- * their reads still happen at invoke time (not prep time).
28
- * - Within a memo scope, {@link RunContext.writeAsset} writes through to disk
29
- * AND refreshes the memo, and {@link RunContext.noteAssetWrite} drops a memo
30
- * entry for a path written out-of-band, so a subsequent `readAsset` of the
31
- * same path returns POST-write bytes (e.g. the distill in-loop salience
32
- * stamp at `distill.ts:816`).
33
- *
34
- * The contract is pinned by `tests/commands/improve/run-context.test.ts`.
35
- *
36
- * This module deliberately mints only the carrier + seam. Adoption at the verb
37
- * call sites lands in WI-7.5 (reflect/distill), WI-7.6 (preparation), and
38
- * WI-7.7 (akmImprove/extract). It does NOT change `EventsContext` shape and does
39
- * NOT thread a db handle into `ProposalsContext` (D14).
40
- */
41
- import fs from "node:fs";
42
- import path from "node:path";
43
- import { resolveStashDir } from "../../core/common.js";
44
- import { recordWrittenPath } from "../../core/write-provenance.js";
45
- /**
46
- * Build a `RunContext` over the given carriers with the supplied memo (or `null`
47
- * for the non-memoizing base context). Kept private so the only way to obtain a
48
- * memoizing context is {@link RunContext.withFreshAssetMemo}.
49
- */
50
- function buildRunContext(carriers, memo) {
51
- const memoKey = (filePath) => path.resolve(filePath);
52
- return {
53
- stashDir: carriers.stashDir,
54
- config: carriers.config,
55
- eventsCtx: carriers.eventsCtx,
56
- proposalsCtx: carriers.proposalsCtx,
57
- chat: carriers.chat,
58
- getLlmRunner: carriers.getLlmRunner,
59
- sourceRun: carriers.sourceRun,
60
- dryRun: carriers.dryRun,
61
- signal: carriers.signal,
62
- now: carriers.now,
63
- readAsset(filePath) {
64
- if (!memo)
65
- return carriers.readFile(filePath);
66
- const key = memoKey(filePath);
67
- const cached = memo.get(key);
68
- if (cached !== undefined)
69
- return cached;
70
- const bytes = carriers.readFile(filePath);
71
- memo.set(key, bytes);
72
- return bytes;
73
- },
74
- writeAsset(filePath, content) {
75
- carriers.writeFile(filePath, content);
76
- // #652: journal the write for the run's write-provenance set. Recorded
77
- // here (not in the default `writeFile` carrier) so an injected IO seam is
78
- // attributed identically to a real `fs` write.
79
- recordWrittenPath(filePath);
80
- memo?.set(memoKey(filePath), content);
81
- },
82
- noteAssetWrite(filePath) {
83
- memo?.delete(memoKey(filePath));
84
- },
85
- withFreshAssetMemo() {
86
- return buildRunContext(carriers, new Map());
87
- },
88
- };
89
- }
90
- /**
91
- * Create the base `RunContext` for an improve run. Its `readAsset` is
92
- * non-memoizing (always fresh); call {@link RunContext.withFreshAssetMemo} to
93
- * enter a memoized pass/invocation scope.
94
- */
95
- export function createRunContext(init) {
96
- const carriers = {
97
- stashDir: init.stashDir,
98
- config: init.config,
99
- eventsCtx: init.eventsCtx,
100
- proposalsCtx: init.proposalsCtx,
101
- chat: init.chat,
102
- getLlmRunner: init.getLlmRunner ?? (() => null),
103
- sourceRun: init.sourceRun,
104
- dryRun: init.dryRun,
105
- signal: init.signal,
106
- now: init.now ?? Date.now,
107
- readFile: init.io?.readFile ?? ((filePath) => fs.readFileSync(filePath, "utf8")),
108
- writeFile: init.io?.writeFile ?? ((filePath, content) => fs.writeFileSync(filePath, content, "utf8")),
109
- };
110
- return buildRunContext(carriers, null);
111
- }
112
- /**
113
- * Resolve the run's stash directory at the improve builder boundary: the
114
- * explicit `options.stashDir` when the caller passed one, else the ambient
115
- * resolveStashDir() fallback. WI-9.10d: this is the SINGLE resolveStashDir
116
- * call site on the improve path — the verb entries call THIS instead of
117
- * resolveStashDir directly, so the ambient read lives only in the builder
118
- * module. (The CLI-wide resolveStashDir sweep for non-improve commands is a
119
- * deferred follow-on — see the chunk-9 ledger WI-9.10d entry.)
120
- */
121
- export function resolveRunStashDir(explicitStashDir) {
122
- return explicitStashDir ?? resolveStashDir();
123
- }
@@ -1,21 +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
- * Small pure helpers shared across the improve command family. Extracted to
6
- * delete byte-identical duplication that previously lived inline in the
7
- * per-process passes (the whole-corpus synthesis pass removed in 0.9.0 and
8
- * the procedural-compilation pass removed in 0.9.0). Keep this file free of
9
- * I/O and of any improve-specific state — these are leaf utilities.
10
- */
11
- /**
12
- * Cap on how many prior rejected proposals for a ref are injected as
13
- * Reflexion-style verbal-RL context so the LLM avoids regenerating refusals.
14
- * Shared by both the revise (reflect) and learn (distill) verbs, which
15
- * previously declared this constant independently.
16
- */
17
- export const MAX_REJECTED_PROPOSALS = 3;
18
- /** Normalize an unknown thrown value to a human-readable message string. */
19
- export function errMessage(e) {
20
- return e instanceof Error ? e.message : String(e);
21
- }
@@ -1,28 +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
- // ── Durable improve-state keys ───────────────────────────────────────────────
5
- //
6
- // Every improve ref uses the current grammar (a short conceptId), and the durable
7
- // state key is the resolved index entry's `item_ref` when the planner supplied
8
- // one, else the conceptId `ref` itself (`preparation.ts`
9
- // `salienceWriteKey`/`outcomeWriteKey` = `itemRef ?? ref`).
10
- /**
11
- * The durable improve-state key for a ref with no resolved `item_ref` — the
12
- * conceptId `ref` itself.
13
- */
14
- export function durableImproveRef(ref) {
15
- return ref;
16
- }
17
- /** Return the conceptId portion of a durable improve-state key. */
18
- export function bareImproveRef(ref) {
19
- const boundary = ref.indexOf("//");
20
- return boundary >= 0 ? ref.slice(boundary + 2) : ref;
21
- }
22
- /**
23
- * The single durable improve-state key used by both readers and writers.
24
- * Indexed entries use item_ref; direct or provenance-free refs use conceptId.
25
- */
26
- export function improveStateReadRefs(ref, itemRef) {
27
- return [itemRef ?? ref];
28
- }
@@ -1,96 +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
- * Default minimum total score a session must reach to PASS the gate. Tuned
6
- * conservatively: a single real narrative-lesson session, or a procedurally
7
- * dense session, clears it — but pure read-only Q&A does not. Config-overridable
8
- * via `processes.extract.triage.minScore`.
9
- */
10
- export const DEFAULT_TRIAGE_MIN_SCORE = 2;
11
- // Decision / outcome / error narrative markers. Case-insensitive, word-bounded.
12
- const MARKER_RE = /\b(error|failed|fix(?:ed)?|root cause|turns out|because|decided|instead|gotcha|workaround|regress(?:ed)?|broke|TIL)\b/i;
13
- // Edit / commit / file-write markers in tool event text.
14
- const EDIT_COMMIT_RE = /\b(Edit|Write|MultiEdit|git commit|diff)\b/i;
15
- // A substantive turn is an assistant or tool turn whose text is non-trivial.
16
- const SUBSTANTIVE_MIN_CHARS = 40;
17
- /**
18
- * Pure heuristic scorer. Operates only on `data.events`. Each sub-signal is
19
- * normalized to a small bounded contribution; `score` is their sum and
20
- * `pass = score >= minScore`.
21
- *
22
- * Sub-signals:
23
- * - markers: presence of decision/outcome/error keywords across event text
24
- * (capped, narrative-lesson signal).
25
- * - toolDensity: bounded contribution from tool-use events.
26
- * - editCommit: bounded contribution from edit/write/commit events.
27
- * - substantiveRatio: scaled fraction of non-trivial assistant+tool turns,
28
- * filtering pure short Q&A.
29
- *
30
- * toolDensity + editCommit alone can clear DEFAULT_TRIAGE_MIN_SCORE so
31
- * high-action / no-narrative sessions are KEPT.
32
- */
33
- export function scoreSessionTriage(data, minScore) {
34
- const events = data.events;
35
- const total = events.length;
36
- // (a) markers — count word-bounded marker hits across all event text, capped.
37
- let markerHits = 0;
38
- for (const e of events) {
39
- if (MARKER_RE.test(e.text))
40
- markerHits += 1;
41
- }
42
- // Each marker-bearing event contributes 1 point, capped at 2 (a single real
43
- // narrative session clears the bar on markers alone).
44
- const markers = Math.min(markerHits, 2);
45
- // (b) toolDensity — bounded contribution from tool-use events.
46
- let toolEvents = 0;
47
- for (const e of events) {
48
- if (e.role === "tool")
49
- toolEvents += 1;
50
- }
51
- // 0.25 per tool event, capped at 1.5.
52
- const toolDensity = Math.min(toolEvents * 0.25, 1.5);
53
- // (c) editCommit — bounded contribution from edit/write/commit markers on
54
- // events (filePath present, or text matching the edit/commit regex).
55
- let editCommitEvents = 0;
56
- for (const e of events) {
57
- if (e.filePath || EDIT_COMMIT_RE.test(e.text))
58
- editCommitEvents += 1;
59
- }
60
- // 0.25 per edit/commit event, capped at 1.5. Together with toolDensity a
61
- // procedurally dense session reaches well above the default threshold.
62
- const editCommit = Math.min(editCommitEvents * 0.25, 1.5);
63
- // (d) substantiveRatio — scaled fraction of non-trivial assistant+tool turns.
64
- let substantive = 0;
65
- for (const e of events) {
66
- if ((e.role === "assistant" || e.role === "tool") && e.text.length >= SUBSTANTIVE_MIN_CHARS) {
67
- substantive += 1;
68
- }
69
- }
70
- const ratio = total > 0 ? substantive / total : 0;
71
- // Scale to a bounded [0,1] contribution.
72
- const substantiveRatio = Math.min(ratio, 1);
73
- const subscores = { markers, toolDensity, editCommit, substantiveRatio };
74
- const score = markers + toolDensity + editCommit + substantiveRatio;
75
- // Base gate: score must clear the floor.
76
- const pass = score >= minScore;
77
- return {
78
- pass,
79
- score,
80
- subscores,
81
- ...(pass ? {} : { reason: "low_signal" }),
82
- };
83
- }
84
- /**
85
- * Resolve the effective triage config from the extract process config. Mirrors
86
- * the minContentChars resolution style in akmExtract.
87
- *
88
- * Default-off: `enabled` is FALSE unless `triage.enabled === true`. `minScore`
89
- * defaults to {@link DEFAULT_TRIAGE_MIN_SCORE}.
90
- */
91
- export function resolveTriageConfig(extractProcess) {
92
- const triage = extractProcess?.triage;
93
- const enabled = triage?.enabled === true;
94
- const minScore = typeof triage?.minScore === "number" ? triage.minScore : DEFAULT_TRIAGE_MIN_SCORE;
95
- return { enabled, minScore };
96
- }