akm-cli 0.9.17-alpha.3 → 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 +731 -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 +361 -751
  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 -441
  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,500 +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 { warn } from "../../../core/warn.js";
21
- import { recordWrittenPath } from "../../../core/write-provenance.js";
22
- import { callStructured } from "../../../llm/structured-call.js";
23
- import { archiveProposal, isProposalSkipped, recordGateDecision, } from "../../proposal/repository.js";
24
- import { akmSearch } from "../../read/search.js";
25
- import { scoreEncodingSalience } from "../encoding-salience.js";
26
- import { resolveImproveLlmExecution } from "../execution.js";
27
- import { emitProposal } from "../proposal-envelope.js";
28
- import { computeSalience, upsertAssetSalience } from "../salience.js";
29
- // ── D-4 / #390: Top-3 similar lessons retrieval ──────────────────────────────
30
- /**
31
- * Default implementation: use akmSearch to find top-N similar lesson assets.
32
- * Returns empty array when search fails or returns no results.
33
- * Requires embedding configured for semantic similarity; degrades gracefully.
34
- */
35
- export async function fetchTopSimilarLessons(query, n, _stashDir) {
36
- try {
37
- const result = await akmSearch({
38
- query,
39
- type: "lesson",
40
- limit: n,
41
- skipLogging: true,
42
- eventSource: "improve",
43
- });
44
- const hits = result?.hits ?? [];
45
- return hits
46
- .filter((h) => "path" in h && typeof h.path === "string")
47
- .slice(0, n)
48
- .map((h) => {
49
- let content = "";
50
- try {
51
- if (h.path && fs.existsSync(h.path)) {
52
- content = fs.readFileSync(h.path, "utf8");
53
- }
54
- }
55
- catch {
56
- /* best-effort */
57
- }
58
- return { ref: h.ref, content };
59
- });
60
- }
61
- catch {
62
- return [];
63
- }
64
- }
65
- // ── LLM-as-judge quality gate (P2-B) ────────────────────────────────────────
66
- /**
67
- * D-4 / #390: Build the LLM-as-judge prompt.
68
- *
69
- * When similarLessons are provided (top-3 by embedding similarity), they are
70
- * included in the context so the judge can lower the score for near-duplicates.
71
- * Voyager arXiv:2305.16291 — skill library admission requires similarity check
72
- * against the existing library. A-MEM arXiv:2502.12110 — new notes are checked
73
- * against existing notes before linking.
74
- */
75
- export function buildJudgePrompt(lessonContent, sourceContent, similarLessons) {
76
- const lines = [
77
- "You are evaluating a proposed lesson asset for an akm knowledge base.",
78
- "",
79
- "Score this lesson on each criterion from 1 (poor) to 5 (excellent):",
80
- "1. NOVELTY: Does the lesson add information not already present in the source asset?",
81
- "2. NON-REDUNDANCY: Is this lesson meaningfully different from what the source already says?",
82
- "",
83
- "Source asset content:",
84
- "```",
85
- sourceContent.slice(0, 2000),
86
- "```",
87
- ];
88
- if (similarLessons && similarLessons.length > 0) {
89
- lines.push("");
90
- lines.push("Existing similar lessons (top-3 by similarity). Rate lower if the proposed lesson is substantially similar to any of these:");
91
- for (const sl of similarLessons) {
92
- lines.push(`\nExisting lesson ref: ${sl.ref}`);
93
- lines.push("```");
94
- lines.push(sl.content.slice(0, 500));
95
- lines.push("```");
96
- }
97
- }
98
- lines.push("");
99
- lines.push("Proposed lesson content:");
100
- lines.push("```");
101
- lines.push(lessonContent.slice(0, 1000));
102
- lines.push("```");
103
- lines.push("");
104
- lines.push('Return ONLY valid JSON, no prose: {"scores": {"novelty": <1-5 integer>, "nonRedundancy": <1-5 integer>}, "reason": "<one sentence>"}');
105
- return lines.join("\n");
106
- }
107
- function boundedDocument(content, maxChars = 6000) {
108
- if (content.length <= maxChars)
109
- return content;
110
- const half = Math.floor((maxChars - 80) / 2);
111
- return `${content.slice(0, half)}\n\n[... middle omitted for bounded judge context ...]\n\n${content.slice(-half)}`;
112
- }
113
- function buildChangedRegion(sourceContent, candidateContent) {
114
- const source = sourceContent.split("\n");
115
- const candidate = candidateContent.split("\n");
116
- let prefix = 0;
117
- while (prefix < source.length && prefix < candidate.length && source[prefix] === candidate[prefix])
118
- prefix++;
119
- let suffix = 0;
120
- while (suffix < source.length - prefix &&
121
- suffix < candidate.length - prefix &&
122
- source[source.length - 1 - suffix] === candidate[candidate.length - 1 - suffix]) {
123
- suffix++;
124
- }
125
- const removed = source.slice(prefix, source.length - suffix).join("\n");
126
- const added = candidate.slice(prefix, candidate.length - suffix).join("\n");
127
- return boundedDocument(`Removed or replaced:\n${removed || "(none)"}\n\nAdded or replacement:\n${added || "(none)"}`);
128
- }
129
- /** Build quality criteria for revising an existing asset in place. */
130
- export function buildReflectJudgePrompt(candidateContent, sourceContent, feedback) {
131
- return [
132
- "You are evaluating a proposed revision to an existing akm asset.",
133
- "",
134
- "Score this revision on each criterion from 1 (poor) to 5 (excellent):",
135
- "1. FEEDBACK ALIGNMENT: Does the revision address the supplied feedback or improve retrieval and clarity?",
136
- "2. PRESERVATION: Does it retain the source's concrete facts, code, commands, examples, and structure without truncation?",
137
- "3. QUALITY: Is the revision coherent, actionable, complete, and free of unsupported claims?",
138
- "",
139
- "Overlap with the source is expected and must not lower the score by itself; this is an in-place revision, not a new lesson.",
140
- "",
141
- "Feedback:",
142
- "```",
143
- (feedback.length > 0 ? feedback.join("\n") : "No explicit feedback supplied.").slice(0, 1000),
144
- "```",
145
- "",
146
- "Source asset content:",
147
- "```",
148
- boundedDocument(sourceContent),
149
- "```",
150
- "",
151
- "Proposed revision:",
152
- "```",
153
- boundedDocument(candidateContent),
154
- "```",
155
- "",
156
- "Changed region:",
157
- "```",
158
- buildChangedRegion(sourceContent, candidateContent),
159
- "```",
160
- "",
161
- 'Return ONLY valid JSON, no prose: {"scores": {"feedbackAlignment": <1-5 integer>, "preservation": <1-5 integer>, "quality": <1-5 integer>}, "reason": "<one sentence>"}',
162
- ].join("\n");
163
- }
164
- /**
165
- * Criterion keys `buildJudgePrompt` asks the lesson judge to score.
166
- * R16: ACTIONABILITY dropped (splinter measured AUC 0.46 against accept/reject
167
- * outcomes — no signal — and averaging it pulled scores toward the review band).
168
- */
169
- const LESSON_JUDGE_CRITERIA_KEYS = ["novelty", "nonRedundancy"];
170
- /** Criterion keys `buildReflectJudgePrompt` asks the reflect judge to score. */
171
- const REFLECT_JUDGE_CRITERIA_KEYS = ["feedbackAlignment", "preservation", "quality"];
172
- /**
173
- * R16 / r2-2 / JUDGE2: parse the judge's JSON response, accepting either the
174
- * current per-criterion shape (`{"scores": {...}, "reason"}`, averaged in
175
- * code) or the old averaged-float shape (`{"score": 1-5, "reason"}`) a model
176
- * may still return. `expectedCriteriaKeys` names the criteria this judge's
177
- * prompt asked for; only those keys are read, validated, and averaged — a
178
- * `scores` object missing any of them is a parse failure (a truncated or
179
- * partial response can't auto-pass on whatever keys happened to arrive), and
180
- * any OTHER key present (e.g. a model spelling a key differently, or echoing
181
- * a criterion the prompt didn't ask for) is silently ignored rather than
182
- * changing the score or failing the parse. Each expected criterion (or the
183
- * bare score) must be a finite number in 1..5; anything else — an
184
- * out-of-range or non-finite value, a non-string `reason` — is a parse
185
- * failure so the caller routes to review exactly as before.
186
- */
187
- function parseJudgeResponse(raw, expectedCriteriaKeys) {
188
- const parsed = parseEmbeddedJsonResponse(raw);
189
- if (!parsed || typeof parsed.reason !== "string")
190
- return undefined;
191
- const reason = parsed.reason;
192
- if (parsed.scores !== undefined) {
193
- if (typeof parsed.scores !== "object" || parsed.scores === null || Array.isArray(parsed.scores))
194
- return undefined;
195
- const scores = parsed.scores;
196
- const criteria = {};
197
- let sum = 0;
198
- for (const key of expectedCriteriaKeys) {
199
- const value = scores[key];
200
- if (typeof value !== "number" || !Number.isFinite(value) || value < 1 || value > 5)
201
- return undefined;
202
- criteria[key] = value;
203
- sum += value;
204
- }
205
- const score = sum / expectedCriteriaKeys.length;
206
- return { score, reason, criteria };
207
- }
208
- if (typeof parsed.score === "number" && Number.isFinite(parsed.score) && parsed.score >= 1 && parsed.score <= 5) {
209
- return { score: parsed.score, reason };
210
- }
211
- return undefined;
212
- }
213
- /**
214
- * JUDGE2: strict JSON Schema for a judge response, sent through the same
215
- * `supportsJsonSchema`-gated `request.responseSchema` path
216
- * `src/llm/graph-extract.ts` (`GRAPH_EXTRACTION_JSON_SCHEMA`) uses — a
217
- * provider that doesn't opt in (`runner.connection.supportsJsonSchema`) sees
218
- * no change. Built from `expectedCriteriaKeys` so each judge's schema matches
219
- * exactly the criteria its own prompt asks for; `additionalProperties: false`
220
- * at both levels means a model that spells a key differently is rejected by
221
- * a schema-enforcing provider rather than silently producing a parse failure.
222
- */
223
- function buildJudgeResponseSchema(expectedCriteriaKeys) {
224
- const properties = {};
225
- for (const key of expectedCriteriaKeys) {
226
- properties[key] = { type: "integer", minimum: 1, maximum: 5 };
227
- }
228
- return {
229
- type: "object",
230
- required: ["scores", "reason"],
231
- additionalProperties: false,
232
- properties: {
233
- scores: {
234
- type: "object",
235
- required: [...expectedCriteriaKeys],
236
- additionalProperties: false,
237
- properties,
238
- },
239
- reason: { type: "string" },
240
- },
241
- };
242
- }
243
- async function runQualityJudge(feature, config, prompt, expectedCriteriaKeys, chat, options = {}) {
244
- const resolvedDefault = !options.runnerSelectionFrozen && !options.llmRunner
245
- ? resolveImproveLlmExecution({ config, processName: `${feature}-judge` })
246
- : null;
247
- if (resolvedDefault)
248
- options.onNotices?.(resolvedDefault.notices);
249
- const runner = options.llmRunner ?? resolvedDefault?.runner;
250
- if (!runner) {
251
- return { pass: false, score: -1, reason: "no LLM configured — cannot judge, failing closed" };
252
- }
253
- try {
254
- // UNGATED at the seam (no akmConfig): the quality gates' enablement is
255
- // resolved by the caller before this function runs, and a transport throw
256
- // propagates into the fail-closed catch below. `feature` labels the call.
257
- const raw = await callStructured({
258
- feature,
259
- runner,
260
- ...(options.lease ? { lease: options.lease } : {}),
261
- messages: [
262
- { role: "system", content: "Return only valid JSON. No prose." },
263
- { role: "user", content: prompt },
264
- ],
265
- request: {
266
- enableThinking: false,
267
- // R13: the judge must not inherit the generation runner's temperature
268
- // (measured: 10/16 verdict flips at 0.3, 0/16 at 0). Pinned regardless
269
- // of what `engines.<name>.temperature` the runner resolves.
270
- temperature: 0,
271
- // JUDGE2: bounds the response to exactly this judge's criteria on
272
- // providers that opt into structured output; a no-op otherwise.
273
- responseSchema: buildJudgeResponseSchema(expectedCriteriaKeys),
274
- ...(Object.hasOwn(options, "timeoutMs") ? { timeoutMs: options.timeoutMs } : {}),
275
- ...(options.signal ? { signal: options.signal } : {}),
276
- ...(chat ? { chat } : {}),
277
- },
278
- parse: (rawResponse) => rawResponse ?? "",
279
- // Unreachable on the ungated path (errors propagate); fail closed anyway.
280
- onError: () => "",
281
- fallback: "",
282
- ...(options.onNotices ? { onNotices: options.onNotices } : {}),
283
- });
284
- const parsed = parseJudgeResponse(raw, expectedCriteriaKeys);
285
- if (!parsed) {
286
- return { pass: false, score: -1, reason: "judge parse failed — routed to review", reviewNeeded: true };
287
- }
288
- // D-5 / #388: Three-band system (MT-Bench arXiv:2306.05685 — ~±0.5 judge variance).
289
- // >= 3.5: auto-queue as pending (pass: true)
290
- // 2.5–3.5: review-needed band — uncertain, escalate to human (reviewNeeded: true)
291
- // < 2.5: auto-reject (pass: false)
292
- const { score, reason, criteria } = parsed;
293
- if (score >= 3.5)
294
- return { pass: true, score, reason, ...(criteria ? { criteria } : {}) };
295
- if (score >= 2.5)
296
- return { pass: false, score, reason, reviewNeeded: true, ...(criteria ? { criteria } : {}) };
297
- return { pass: false, score, reason, ...(criteria ? { criteria } : {}) };
298
- }
299
- catch (error) {
300
- // Invalid symbolic credentials are configuration failures, not a negative
301
- // content verdict. Provider/runtime failures retain the fail-closed result.
302
- if (error instanceof ConfigError)
303
- throw error;
304
- return { pass: false, score: -1, reason: "judge timeout/error — routed to review", reviewNeeded: true };
305
- }
306
- }
307
- /**
308
- * Run the LLM-as-judge quality gate on a proposal's content.
309
- *
310
- * Exported so reflect.ts can apply the same gate to reflect proposals (R-5 / #374).
311
- * The selected strategy's distill/reflect quality-gate setting is resolved by
312
- * the caller before this function runs.
313
- *
314
- * Fail-CLOSED (07 P0-2): returns `pass: false` (score -1) on timeout, parse
315
- * failure, or missing LLM. Minted content that cannot be judged is rejected,
316
- * not passed through — an unverifiable judge must never wave content into the
317
- * stash. The rejection is `quality_rejected`, not `review_needed`.
318
- */
319
- export async function runLessonQualityJudge(config, lessonContent, sourceContent, chat, options = {}) {
320
- return runQualityJudge("lesson_quality_gate", config, buildJudgePrompt(lessonContent, sourceContent, options.similarLessons), LESSON_JUDGE_CRITERIA_KEYS, chat, options);
321
- }
322
- /** Judge an in-place reflect revision without applying new-lesson novelty criteria. */
323
- export async function runReflectQualityJudge(config, candidateContent, sourceContent, feedback, chat, options = {}) {
324
- return runQualityJudge("proposal_quality_gate", config, buildReflectJudgePrompt(candidateContent, sourceContent, feedback), REFLECT_JUDGE_CRITERIA_KEYS, chat, options);
325
- }
326
- // ── Quality-rejection helper ─────────────────────────────────────────────────
327
- /**
328
- * Write a rejected lesson to `$STATE/improve/distill-rejected/<stash>/`
329
- * (itlackey/akm#890), persist it as a real `proposals` row, append a
330
- * `distill_invoked` quality-rejected event, and return the `quality_rejected`
331
- * envelope.
332
- *
333
- * R10: the proposal row is minted through the same `createProposal`
334
- * (`emitProposal`) path every other distill proposal takes, so `source:
335
- * "distill"` fingerprint/backoff bookkeeping (proposal/repository.ts
336
- * `checkFingerprintAndBackoff`) and the Reflexion "previously rejected"
337
- * context (distill.ts's `buildDistillMessages`, reflect.ts's
338
- * `readRejectedProposals`) can see it — before this, a quality rejection
339
- * left only an event and a `$STATE`-side file nothing read, so the same ref
340
- * was re-selected and re-rejected on every run. `review_needed` stays
341
- * `pending` for a human to triage in the normal queue (matching what
342
- * promote-memory.ts's comment always claimed) and is stamped with a
343
- * `quality-gate` gate decision so the triage drain's `classifyPendingProposals`
344
- * (proposal/drain.ts) leaves it pending instead of deferring it to the
345
- * judgment tier, which could auto-accept it with no human in the loop;
346
- * `quality_rejected` is minted pending, then immediately archived to
347
- * `rejected` with the judge's reason.
348
- * A fingerprint/backoff guard hit here (rare pre-R9; the pre-generation
349
- * guard is item R9) just means no new row — the envelope + event below are
350
- * written either way.
351
- *
352
- * @param stash - Root stash directory.
353
- * @param inputRef - The original input ref (for the event).
354
- * @param proposalRef - The proposed lesson/knowledge ref.
355
- * @param content - The raw content that failed the quality gate.
356
- * @param score - Quality score from the judge.
357
- * @param reason - Human-readable rejection reason.
358
- * @param extraMeta - Optional additional metadata for the event.
359
- * @param eventsCtx - Events context so the emit takes appendEvent's fast path (R25).
360
- * @param proposalOpts - Test seam / attribution passthrough for the minted proposal row.
361
- */
362
- export function writeQualityRejection(stash, inputRef, proposalRef, content, score, reason, extraMeta = {}, eligibilitySource, eventsCtx, proposalOpts = {}) {
363
- // D-5 / #388: reviewNeeded flag selects "review_needed" vs "quality_rejected" outcome.
364
- const outcome = extraMeta.reviewNeeded ? "review_needed" : "quality_rejected";
365
- // r2-1: the mint-time canonical validator inside createProposal (via
366
- // emitProposal) throws UsageError for structurally-invalid content (e.g. a
367
- // lessons/ ref missing description/when_to_use). The proposal row here is
368
- // bookkeeping for backoff/Reflexion, never the authoritative record of the
369
- // rejection, so a validator throw degrades to "no row minted" — the same
370
- // bucket as the fingerprint/backoff skip below, not a caller-visible error.
371
- // r3-1: the archiveProposal call below is guarded the same way, for the
372
- // same reason.
373
- let mintedProposal;
374
- try {
375
- mintedProposal = emitProposal({ stashDir: stash, ...(proposalOpts.proposalsCtx ? { proposalsCtx: proposalOpts.proposalsCtx } : {}) }, {
376
- ref: proposalRef,
377
- source: "distill",
378
- ...(proposalOpts.sourceRun !== undefined ? { sourceRun: proposalOpts.sourceRun } : {}),
379
- ...(proposalOpts.modelId !== undefined ? { modelId: proposalOpts.modelId } : {}),
380
- payload: { content },
381
- ...(eligibilitySource ? { eligibilitySource } : {}),
382
- });
383
- }
384
- catch {
385
- mintedProposal = undefined;
386
- }
387
- let proposal;
388
- if (mintedProposal && !isProposalSkipped(mintedProposal)) {
389
- if (outcome === "quality_rejected") {
390
- try {
391
- proposal = archiveProposal(stash, mintedProposal.id, "rejected", reason, proposalOpts.proposalsCtx);
392
- }
393
- catch (error) {
394
- warn(`[akm] writeQualityRejection: failed to archive proposal ${mintedProposal.id} as rejected: ${error instanceof Error ? error.message : String(error)}`);
395
- }
396
- }
397
- else {
398
- proposal = mintedProposal;
399
- // REVIEW: stamp the mint so the triage drain's `classifyPendingProposals`
400
- // skips it instead of deferring it to the judgment tier, which could
401
- // auto-accept it under `applyMode: promote` with no human ever seeing
402
- // the review-band content the gate explicitly refused to auto-queue.
403
- // Best-effort like the mint/archive tolerance above: a stamp failure
404
- // warns and continues rather than blocking the rejection envelope.
405
- try {
406
- proposal =
407
- recordGateDecision(stash, mintedProposal.id, { outcome: "deferred", reason: "quality-review", gate: "quality-gate" }, proposalOpts.proposalsCtx) ?? proposal;
408
- }
409
- catch (error) {
410
- warn(`[akm] writeQualityRejection: failed to stamp gate decision for ${mintedProposal.id}: ${error instanceof Error ? error.message : String(error)}`);
411
- }
412
- }
413
- }
414
- const rejectDir = getDistillRejectedDir(stash);
415
- fs.mkdirSync(rejectDir, { recursive: true });
416
- const ts = timestampForFilename();
417
- const rejectPath = path.join(rejectDir, `${ts}-${proposalRef.replace(/[:/\\]/g, "-")}.md`);
418
- // R16: surface the judge's per-criterion scores in the envelope frontmatter
419
- // when the caller supplied them (a judge-based rejection), same as the event.
420
- const criteria = extraMeta.criteria && typeof extraMeta.criteria === "object" && !Array.isArray(extraMeta.criteria)
421
- ? extraMeta.criteria
422
- : undefined;
423
- const criteriaFrontmatter = criteria
424
- ? `criteria:\n${Object.entries(criteria)
425
- .map(([key, value]) => ` ${key}: ${value}`)
426
- .join("\n")}\n`
427
- : "";
428
- fs.writeFileSync(rejectPath, `---\nscore: ${score}\nreason: ${reason}\noutcome: ${outcome}\n${criteriaFrontmatter}---\n\n${content}`, "utf8");
429
- // #652 / itlackey/akm#890: journal it even though it now lands under
430
- // `$STATE`, outside the stash's git repo — `result.writtenPaths` reports
431
- // every path a run touched, in or out of the stash (describeRunWrittenPaths
432
- // in improve.ts falls back to the absolute path for anything outside the
433
- // stash root), and the auto-sync commit's own containment check
434
- // (resolveSyncPathSet's `relativeWrittenPath`) already drops anything
435
- // outside `repoDir` from what gets staged — recording it here cannot cause
436
- // it to be committed.
437
- recordWrittenPath(rejectPath);
438
- appendEvent({
439
- eventType: "distill_invoked",
440
- ref: inputRef,
441
- metadata: {
442
- outcome,
443
- proposalRef,
444
- score,
445
- reason,
446
- ...extraMeta,
447
- // Attribution tagging: stamp the eligibility lane so distill_invoked can be
448
- // sliced by lane downstream. See EligibilitySource.
449
- ...(eligibilitySource ? { eligibilitySource } : {}),
450
- },
451
- }, eventsCtx);
452
- return {
453
- schemaVersion: 1,
454
- ok: true,
455
- outcome,
456
- inputRef,
457
- proposalRef,
458
- score,
459
- reason,
460
- ...(proposal ? { proposalId: proposal.id, proposal } : {}),
461
- ...extraMeta,
462
- };
463
- }
464
- /**
465
- * G4 — content-score a distilled OUTPUT (lesson/knowledge proposal body) and
466
- * persist it to state.db :: asset_salience with `encoding_source: "content"`.
467
- *
468
- * Lessons are refused as distill INPUTS (`DISTILL_REFUSED_INPUT_TYPES`), so
469
- * this creation-time write is their only chance to earn a real content-derived
470
- * encoding score instead of sitting on the type-weight stub forever. Best-effort:
471
- * never blocks or fails the proposal flow.
472
- */
473
- export function persistOutputEncodingSalience(ref, body, existingRefVocabulary,
474
- // Operator opt-out (improve.salience.outcomeWeightEnabled: false) must apply
475
- // here too, or distill-written rank_score rows would use WS-2 weights while
476
- // preparation uses parity weights — inconsistent salience semantics.
477
- outcomeWeightEnabled) {
478
- try {
479
- const parsedRef = parseRefInput(ref);
480
- const salienceResult = scoreEncodingSalience({
481
- body,
482
- type: parsedRef.type,
483
- existingRefVocabulary,
484
- revisionCount: 0, // a freshly distilled output IS a first encounter
485
- });
486
- withStateDb((stateDb) => {
487
- const vector = computeSalience({
488
- ref,
489
- type: parsedRef.type,
490
- retrievalFreq: 0,
491
- encodingSalience: salienceResult.score,
492
- outcomeWeightEnabled,
493
- });
494
- upsertAssetSalience(stateDb, ref, vector);
495
- });
496
- }
497
- catch {
498
- // Best-effort — scoring must never block proposal creation.
499
- }
500
- }