akm-cli 0.9.1 → 0.9.2-alpha.2

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 (350) hide show
  1. package/CHANGELOG.md +103 -28
  2. package/README.md +3 -1
  3. package/SECURITY.md +1 -1
  4. package/STABILITY.md +1 -1
  5. package/dist/akm +2 -2
  6. package/dist/akm-migrate +2 -2
  7. package/dist/assets/hints/cli-hints-full.md +14 -9
  8. package/dist/assets/improve-strategies/proactive-maintenance.json +1 -1
  9. package/dist/assets/improve-strategies/reflect-distill.json +1 -1
  10. package/dist/assets/models.json +35 -0
  11. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +3 -4
  12. package/dist/assets/stash-skeleton/facts/conventions/organization.md +1 -3
  13. package/dist/assets/tasks/core/extract.yml +6 -5
  14. package/dist/assets/tasks/core/improve.yml +6 -5
  15. package/dist/assets/tasks/core/index-refresh.yml +6 -5
  16. package/dist/assets/tasks/core/sync.yml +6 -5
  17. package/dist/assets/tasks/core/version-check.yml +6 -5
  18. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +6 -5
  19. package/dist/assets/tasks/improve/akm-improve-catchup.yml +6 -5
  20. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +6 -5
  21. package/dist/assets/tasks/improve/akm-improve-frequent.yml +6 -5
  22. package/dist/assets/tasks/improve/akm-improve-nightly.yml +6 -5
  23. package/dist/cli/confirm.js +2 -2
  24. package/dist/cli/parse-args.js +3 -24
  25. package/dist/cli/retired-commands.js +1 -1
  26. package/dist/cli/shared.js +2 -2
  27. package/dist/cli.js +11 -9
  28. package/dist/commands/agent/agent-dispatch.js +55 -89
  29. package/dist/commands/agent/contribute-cli.js +12 -45
  30. package/dist/commands/command/builtin-action.js +32 -0
  31. package/dist/commands/command/command-cli.js +99 -0
  32. package/dist/commands/command/command-execution.js +308 -0
  33. package/dist/commands/command/execution-source-loader.js +176 -0
  34. package/dist/commands/command/portable-template.js +60 -0
  35. package/dist/commands/config-cli.js +10 -4
  36. package/dist/commands/env/env.js +4 -2
  37. package/dist/commands/feedback-cli.js +1 -1
  38. package/dist/commands/health/checks.js +241 -29
  39. package/dist/commands/health/html-report.js +0 -14
  40. package/dist/commands/health/report-view-model.js +0 -1
  41. package/dist/commands/health/surfaces.js +6 -7
  42. package/dist/commands/health/types.js +0 -2
  43. package/dist/commands/health.js +63 -18
  44. package/dist/commands/improve/collapse-detector.js +5 -6
  45. package/dist/commands/improve/consolidate.js +251 -214
  46. package/dist/commands/improve/distill/promote-memory.js +71 -34
  47. package/dist/commands/improve/distill/quality-gate.js +17 -5
  48. package/dist/commands/improve/distill.js +232 -155
  49. package/dist/commands/improve/eligibility.js +112 -79
  50. package/dist/commands/improve/execution.js +57 -0
  51. package/dist/commands/improve/extract-cli.js +5 -5
  52. package/dist/commands/improve/extract-prompt.js +64 -22
  53. package/dist/commands/improve/extract.js +608 -360
  54. package/dist/commands/improve/improve-strategies.js +43 -14
  55. package/dist/commands/improve/improve.js +249 -29
  56. package/dist/commands/improve/loop-stages.js +11 -17
  57. package/dist/commands/improve/memory/memory-contradiction-detect.js +90 -66
  58. package/dist/commands/improve/outcome-loop.js +22 -38
  59. package/dist/commands/improve/planner.js +134 -0
  60. package/dist/commands/improve/preparation.js +730 -409
  61. package/dist/commands/improve/reflect.js +386 -223
  62. package/dist/commands/improve/run-context.js +3 -4
  63. package/dist/commands/improve/salience.js +6 -58
  64. package/dist/commands/improve/session-asset.js +12 -12
  65. package/dist/commands/lint/index.js +101 -29
  66. package/dist/commands/migrate-cli.js +11 -69
  67. package/dist/commands/migration-tool.js +6 -9
  68. package/dist/commands/models-cli.js +27 -0
  69. package/dist/commands/proposal/drain.js +258 -186
  70. package/dist/commands/proposal/proposal-cli.js +32 -10
  71. package/dist/commands/proposal/proposal.js +2 -5
  72. package/dist/commands/proposal/propose.js +192 -172
  73. package/dist/commands/proposal/repository.js +54 -91
  74. package/dist/commands/proposal/validators/proposal-validators.js +9 -7
  75. package/dist/commands/read/curate.js +53 -22
  76. package/dist/commands/read/registry-search.js +25 -9
  77. package/dist/commands/read/remember-cli.js +14 -2
  78. package/dist/commands/read/search.js +10 -4
  79. package/dist/commands/read/show.js +139 -153
  80. package/dist/commands/registry-cli.js +16 -7
  81. package/dist/commands/remember.js +33 -18
  82. package/dist/commands/sources/add-cli.js +19 -178
  83. package/dist/commands/sources/bundle-cli.js +15 -3
  84. package/dist/commands/sources/dangerous-env-audit.js +135 -0
  85. package/dist/commands/sources/info.js +2 -1
  86. package/dist/commands/sources/installed-stashes.js +901 -177
  87. package/dist/commands/sources/schema-repair.js +174 -95
  88. package/dist/commands/sources/self-update.js +30 -74
  89. package/dist/commands/sources/source-add.js +3 -5
  90. package/dist/commands/sources/sources-cli.js +2 -15
  91. package/dist/commands/sources/update-transaction.js +220 -0
  92. package/dist/commands/tasks/tasks-cli.js +3 -3
  93. package/dist/commands/tasks/tasks.js +736 -317
  94. package/dist/commands/workflow-cli.js +2 -2
  95. package/dist/core/adapter/adapters/agent-skills-adapter.js +3 -0
  96. package/dist/core/adapter/adapters/akm-adapter.js +85 -35
  97. package/dist/core/adapter/adapters/akm-lint.js +54 -39
  98. package/dist/core/adapter/adapters/akm-metadata.js +45 -45
  99. package/dist/core/adapter/adapters/akm-task-adapter.js +32 -49
  100. package/dist/core/adapter/adapters/akm-workflow-adapter.js +38 -23
  101. package/dist/core/adapter/adapters/dotenv-adapter.js +30 -1
  102. package/dist/core/adapter/adapters/generic-files-adapter.js +11 -0
  103. package/dist/core/adapter/adapters/index.js +0 -9
  104. package/dist/core/adapter/adapters/llm-wiki-adapter.js +4 -0
  105. package/dist/core/adapter/adapters/okf-adapter.js +4 -0
  106. package/dist/core/adapter/adapters/opencode-adapter.js +5 -8
  107. package/dist/core/adapter/adapters/tool-dir-shared.js +63 -6
  108. package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -0
  109. package/dist/core/adapter/execution-source.js +308 -0
  110. package/dist/core/adapter/recognize-match.js +36 -13
  111. package/dist/core/adapter/registry.js +0 -9
  112. package/dist/core/asset/stash-meta.js +94 -4
  113. package/dist/core/common.js +6 -11
  114. package/dist/core/config/config-io.js +3 -3
  115. package/dist/core/config/config-schema.js +18 -40
  116. package/dist/core/config/config-sources.js +11 -21
  117. package/dist/core/config/config-walker.js +31 -13
  118. package/dist/core/config/config.js +23 -26
  119. package/dist/core/config/schema/engines.js +8 -7
  120. package/dist/core/config/schema/improve-processes.js +29 -5
  121. package/dist/core/config/schema/index-config.js +0 -27
  122. package/dist/core/config/schema/primitives.js +1 -23
  123. package/dist/core/config/schema/sources-bundles.js +13 -16
  124. package/dist/core/errors.js +2 -0
  125. package/dist/core/events.js +68 -32
  126. package/dist/core/extra-params.js +1 -0
  127. package/dist/core/improve-result.js +315 -0
  128. package/dist/core/lesson-lint.js +0 -6
  129. package/dist/core/maintenance-barrier.js +4 -4
  130. package/dist/core/network-policy.js +152 -0
  131. package/dist/core/paths.js +1 -1
  132. package/dist/core/recognition-util.js +4 -4
  133. package/dist/core/registry-url.js +456 -0
  134. package/dist/core/state/migrations.js +161 -47
  135. package/dist/core/state-db.js +453 -80
  136. package/dist/core/system-error.js +32 -0
  137. package/dist/core/time.js +2 -12
  138. package/dist/core/write-source.js +0 -18
  139. package/dist/execution/directory-identity.js +52 -0
  140. package/dist/execution/executable-identity.js +107 -0
  141. package/dist/execution/guarded-source.js +398 -0
  142. package/dist/execution/json.js +95 -0
  143. package/dist/{commands/health/types-session-log.js → execution/limits.js} +2 -1
  144. package/dist/execution/record.js +55 -0
  145. package/dist/execution/resolved-request.js +730 -0
  146. package/dist/execution/source.js +320 -0
  147. package/dist/indexer/bundle-identity-guard.js +5 -4
  148. package/dist/indexer/db/graph-db.js +33 -0
  149. package/dist/indexer/graph/graph-boost.js +3 -4
  150. package/dist/indexer/graph/graph-extraction.js +562 -373
  151. package/dist/indexer/index-written-assets.js +78 -39
  152. package/dist/indexer/indexer.js +471 -432
  153. package/dist/indexer/installations.js +6 -0
  154. package/dist/indexer/lookup/adapter-concept-owner.js +283 -0
  155. package/dist/indexer/materialize-embeddings.js +155 -0
  156. package/dist/indexer/passes/memory-inference.js +227 -174
  157. package/dist/indexer/passes/metadata.js +263 -118
  158. package/dist/indexer/scan/doc-to-entry.js +7 -10
  159. package/dist/indexer/scan/drain-dir.js +51 -23
  160. package/dist/indexer/search/db-search.js +156 -50
  161. package/dist/indexer/search/fts-query.js +40 -40
  162. package/dist/indexer/search/ranking.js +36 -1
  163. package/dist/indexer/search/search-attribution.js +3 -1
  164. package/dist/indexer/search/search-fields.js +23 -14
  165. package/dist/indexer/search/search-hit-enrichers.js +1 -1
  166. package/dist/indexer/search/search-source.js +7 -16
  167. package/dist/indexer/search/semantic-status.js +10 -1
  168. package/dist/indexer/usage/show-usage.js +105 -0
  169. package/dist/indexer/usage/usage-events.js +7 -2
  170. package/dist/indexer/walk/matchers.js +40 -10
  171. package/dist/indexer/walk/path-resolver.js +5 -2
  172. package/dist/indexer/walk/walker.js +20 -2
  173. package/dist/integrations/agent/builder-shared.js +3 -6
  174. package/dist/integrations/agent/conversation-fallback.js +16 -0
  175. package/dist/integrations/agent/engine-resolution.js +87 -87
  176. package/dist/integrations/agent/execution-cascade.js +566 -0
  177. package/dist/integrations/agent/execution-definitions.js +211 -0
  178. package/dist/integrations/agent/execution-lowering.js +811 -0
  179. package/dist/integrations/agent/execution-preparation.js +67 -0
  180. package/dist/integrations/agent/index.js +0 -2
  181. package/dist/integrations/agent/inline-execution.js +74 -0
  182. package/dist/integrations/agent/model-map.js +515 -0
  183. package/dist/integrations/agent/persona-fallback.js +30 -0
  184. package/dist/integrations/agent/request-lowering.js +186 -0
  185. package/dist/integrations/agent/runner-dispatch.js +230 -37
  186. package/dist/integrations/agent/runner.js +12 -83
  187. package/dist/integrations/harnesses/aider/agent-builder.js +8 -0
  188. package/dist/integrations/harnesses/aider/index.js +0 -1
  189. package/dist/integrations/harnesses/amazonq/agent-builder.js +8 -0
  190. package/dist/integrations/harnesses/amazonq/index.js +0 -1
  191. package/dist/integrations/harnesses/claude/agent-builder.js +14 -1
  192. package/dist/integrations/harnesses/claude/index.js +1 -5
  193. package/dist/integrations/harnesses/claude/session-log.js +3 -33
  194. package/dist/integrations/harnesses/codex/agent-builder.js +8 -0
  195. package/dist/integrations/harnesses/codex/index.js +0 -1
  196. package/dist/integrations/harnesses/copilot/agent-builder.js +8 -0
  197. package/dist/integrations/harnesses/copilot/index.js +0 -1
  198. package/dist/integrations/harnesses/gemini/agent-builder.js +8 -0
  199. package/dist/integrations/harnesses/gemini/index.js +0 -1
  200. package/dist/integrations/harnesses/index.js +4 -44
  201. package/dist/integrations/harnesses/opencode/agent-builder.js +16 -9
  202. package/dist/integrations/harnesses/opencode/index.js +0 -2
  203. package/dist/integrations/harnesses/opencode/session-log.js +14 -204
  204. package/dist/integrations/harnesses/opencode-sdk/harness.js +12 -1
  205. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +40 -42
  206. package/dist/integrations/harnesses/openhands/agent-builder.js +8 -0
  207. package/dist/integrations/harnesses/openhands/index.js +0 -1
  208. package/dist/integrations/harnesses/pi/agent-builder.js +8 -0
  209. package/dist/integrations/harnesses/pi/index.js +0 -1
  210. package/dist/integrations/harnesses/shared.js +0 -1
  211. package/dist/integrations/harnesses/types.js +1 -3
  212. package/dist/integrations/lockfile.js +82 -79
  213. package/dist/integrations/session-logs/index.js +6 -17
  214. package/dist/integrations/session-logs/provider-base.js +1 -29
  215. package/dist/llm/client.js +10 -5
  216. package/dist/llm/embedder.js +6 -7
  217. package/dist/llm/embedders/local.js +37 -88
  218. package/dist/llm/embedders/types.js +1 -1
  219. package/dist/llm/graph-extract.js +75 -50
  220. package/dist/llm/index-passes.js +43 -5
  221. package/dist/llm/memory-infer.js +8 -6
  222. package/dist/llm/metadata-enhance.js +5 -3
  223. package/dist/llm/structured-call.js +122 -25
  224. package/dist/output/format-exempt.js +1 -1
  225. package/dist/output/render-registry.js +0 -16
  226. package/dist/output/renderers.js +12 -7
  227. package/dist/output/shapes/curate.js +1 -0
  228. package/dist/output/shapes/helpers.js +10 -2
  229. package/dist/output/shapes/passthrough.js +2 -0
  230. package/dist/output/text/command-format.js +31 -33
  231. package/dist/output/text/health-format.js +1 -29
  232. package/dist/output/text/migrate.js +6 -56
  233. package/dist/output/text/proposal-format.js +16 -1
  234. package/dist/output/text/workflow-format.js +16 -0
  235. package/dist/registry/network.js +279 -0
  236. package/dist/registry/pinned-request-helper.js +247 -0
  237. package/dist/registry/pinned-transport.js +717 -0
  238. package/dist/registry/providers/skills-sh.js +18 -6
  239. package/dist/registry/providers/static-index.js +20 -7
  240. package/dist/registry/resolve.js +53 -28
  241. package/dist/scripts/akm-migrate-node.js +19334 -52269
  242. package/dist/scripts/akm-migrate.js +19270 -51612
  243. package/dist/setup/registry-stash-loader.js +64 -20
  244. package/dist/setup/semantic-assets.js +9 -34
  245. package/dist/setup/setup.js +12 -30
  246. package/dist/setup/source-identity.js +17 -0
  247. package/dist/setup/steps/sources.js +36 -15
  248. package/dist/setup/steps/tasks.js +39 -11
  249. package/dist/sources/providers/git-provider.js +3 -3
  250. package/dist/sources/providers/npm.js +2 -2
  251. package/dist/sources/providers/provider-utils.js +4 -3
  252. package/dist/sources/providers/website.js +11 -7
  253. package/dist/sources/snapshot-fetchers/host-guard.js +9 -136
  254. package/dist/sources/snapshot-fetchers/website-ingest.js +25 -109
  255. package/dist/sources/website-url.js +73 -0
  256. package/dist/storage/engines/sqlite-migrations.js +81 -26
  257. package/dist/storage/managed-db.js +27 -24
  258. package/dist/storage/repositories/events-repository.js +3 -0
  259. package/dist/storage/repositories/index-connection.js +42 -10
  260. package/dist/storage/repositories/index-entries-repository.js +203 -229
  261. package/dist/storage/repositories/index-entry-mapper.js +8 -12
  262. package/dist/storage/repositories/index-entry-schema.js +255 -0
  263. package/dist/storage/repositories/index-fts-repository.js +64 -71
  264. package/dist/storage/repositories/index-llm-cache-repository.js +8 -13
  265. package/dist/storage/repositories/index-meta-repository.js +0 -11
  266. package/dist/storage/repositories/index-schema.js +74 -350
  267. package/dist/storage/repositories/index-utility-repository.js +12 -17
  268. package/dist/storage/repositories/index-vec-repository.js +56 -7
  269. package/dist/storage/repositories/proposals-repository.js +4 -127
  270. package/dist/storage/repositories/registry-cache.js +2 -1
  271. package/dist/storage/repositories/task-history-repository.js +20 -40
  272. package/dist/storage/repositories/workflow-runs-repository.js +228 -129
  273. package/dist/storage/sqlite-read-snapshot.js +148 -0
  274. package/dist/tasks/backends/cron.js +170 -42
  275. package/dist/tasks/backends/index.js +1 -1
  276. package/dist/tasks/backends/launchd.js +787 -202
  277. package/dist/tasks/backends/schtasks.js +282 -83
  278. package/dist/tasks/embedded.js +7 -7
  279. package/dist/tasks/frozen-script.js +50 -0
  280. package/dist/tasks/resolve-akm-bin.js +5 -1
  281. package/dist/tasks/runner.js +239 -251
  282. package/dist/tasks/runtime-v3.js +281 -0
  283. package/dist/tasks/scheduler-binding.js +272 -0
  284. package/dist/tasks/scheduler-invocation.js +57 -43
  285. package/dist/tasks/scheduler-sync.js +654 -0
  286. package/dist/tasks/source-v3.js +752 -0
  287. package/dist/tasks/standalone-script-entry.js +5 -0
  288. package/dist/tasks/task-id.js +29 -0
  289. package/dist/workflows/authoring/authoring.js +15 -32
  290. package/dist/workflows/exec/dispatch-redaction.js +14 -8
  291. package/dist/workflows/exec/exec-unit.js +7 -28
  292. package/dist/workflows/exec/frozen-judge.js +57 -89
  293. package/dist/workflows/exec/lowering-notices.js +23 -0
  294. package/dist/workflows/exec/native-executor.js +301 -458
  295. package/dist/workflows/exec/param-secrets.js +4 -3
  296. package/dist/workflows/exec/run-workflow.js +26 -32
  297. package/dist/workflows/exec/step-work.js +105 -109
  298. package/dist/workflows/exec/unit-dispatch.js +103 -27
  299. package/dist/workflows/exec/unit-writer.js +3 -3
  300. package/dist/workflows/exec/worktree.js +2 -2
  301. package/dist/workflows/ir/compile.js +86 -72
  302. package/dist/workflows/ir/environment-v4.js +328 -0
  303. package/dist/workflows/ir/freeze-v4.js +122 -0
  304. package/dist/workflows/ir/plan-hash.js +13 -7
  305. package/dist/workflows/ir/schema-v4.js +525 -0
  306. package/dist/workflows/ir/schema.js +25 -284
  307. package/dist/workflows/ir/source-freeze-v4.js +506 -0
  308. package/dist/workflows/parser.js +27 -24
  309. package/dist/workflows/program/schema.js +1 -2
  310. package/dist/workflows/renderer.js +42 -29
  311. package/dist/workflows/resource-limits.js +4 -5
  312. package/dist/workflows/runtime/agent-identity.js +11 -13
  313. package/dist/workflows/runtime/plan-classifier.js +8 -8
  314. package/dist/workflows/runtime/runs.js +27 -43
  315. package/dist/workflows/runtime/workflow-asset-loader.js +45 -205
  316. package/dist/workflows/source-files.js +373 -0
  317. package/dist/workflows/source-ir/compile.js +196 -0
  318. package/dist/workflows/source-ir/github-yaml.js +577 -0
  319. package/dist/workflows/source-ir/ordering.js +38 -0
  320. package/dist/workflows/source-ir/program.js +50 -0
  321. package/dist/workflows/source-ir/result.js +26 -0
  322. package/dist/workflows/source-ir/schema.js +772 -0
  323. package/dist/workflows/source-ir/semantics.js +242 -0
  324. package/dist/workflows/source-ir/uses.js +14 -0
  325. package/docs/README.md +2 -0
  326. package/docs/migration/README.md +3 -1
  327. package/docs/migration/release-notes/0.9.2.md +55 -0
  328. package/docs/migration/release-notes/README.md +5 -0
  329. package/docs/migration/v0.8-to-v0.9.md +76 -1077
  330. package/docs/migration/v0.9.0-troubleshooting.md +104 -516
  331. package/docs/migration/v0.9.1-to-v0.9.2.md +150 -0
  332. package/docs/reference/README.md +1 -0
  333. package/docs/reference/cli.md +230 -98
  334. package/docs/reference/configuration.md +159 -36
  335. package/docs/reference/data-and-telemetry.md +19 -1
  336. package/docs/reference/supported-formats.md +23 -3
  337. package/docs/reference/tasks.md +182 -0
  338. package/docs/reference/workflow-schema.md +91 -40
  339. package/docs/reference/workflows.md +33 -6
  340. package/package.json +10 -6
  341. package/schemas/akm-config.json +372 -224
  342. package/schemas/akm-task.json +324 -80
  343. package/schemas/akm-workflow.json +6 -9
  344. package/dist/core/migration-operation.js +0 -75
  345. package/dist/integrations/agent/model-aliases.js +0 -74
  346. package/dist/tasks/parser.js +0 -380
  347. package/dist/tasks/schema.js +0 -123
  348. package/dist/tasks/validator.js +0 -80
  349. package/dist/workflows/ir/freeze.js +0 -320
  350. package/dist/workflows/runtime/document-cache.js +0 -13
@@ -38,10 +38,9 @@ import path from "node:path";
38
38
  import contradictionJudgeTemplate from "../../../assets/prompts/contradiction-judge.md" with { type: "text" };
39
39
  import { mutateFrontmatter, parseFrontmatter } from "../../../core/asset/frontmatter.js";
40
40
  import { parseEmbeddedJsonResponse } from "../../../core/parse.js";
41
- import { getDefaultLlmConfig } from "../../../integrations/agent/engine-resolution.js";
42
- import { materializeLlmRunnerConnection, resolveImproveProcessRunner } from "../../../integrations/agent/runner.js";
43
- import { chatCompletion } from "../../../llm/client.js";
44
- import { callStructured } from "../../../llm/structured-call.js";
41
+ import { disposeLoweredExecutionDispatchLease } from "../../../integrations/agent/execution-lowering.js";
42
+ import { callStructured, preflightStructuredLlmRunner } from "../../../llm/structured-call.js";
43
+ import { resolveImproveLlmExecution } from "../execution.js";
45
44
  import { isDerivedMemory, memoryIdentityRef, resolveParentRef } from "./derived-ref.js";
46
45
  // ── Constants ────────────────────────────────────────────────────────────────
47
46
  /**
@@ -153,23 +152,30 @@ function pickContradictionLoser(a, b) {
153
152
  *
154
153
  * @param stashDir - Root stash directory.
155
154
  * @param config - Loaded AKM config (used to access LLM settings).
156
- * @param chat - Optional chat seam for testing (defaults to chatCompletion).
155
+ * @param chat - Optional test-only chat seam.
157
156
  */
158
- export async function detectAndWriteContradictions(stashDir, config, chat = chatCompletion, strategy, resolvedLlmConfig) {
157
+ export async function detectAndWriteContradictions(stashDir, config, chat, strategy, resolvedRunner) {
159
158
  const result = {
160
159
  familiesExamined: 0,
161
160
  pairsChecked: 0,
162
161
  edgesWritten: 0,
163
162
  warnings: [],
164
163
  };
165
- const contradictionLlm = resolvedLlmConfig === null
166
- ? undefined
167
- : (resolvedLlmConfig ??
168
- (() => {
169
- const runner = resolveImproveProcessRunner(strategy, "consolidate", config);
170
- return runner ? materializeLlmRunnerConnection(runner) : getDefaultLlmConfig(config);
171
- })());
172
- if (!contradictionLlm)
164
+ if (!(strategy?.processes?.consolidate?.contradictionDetection?.enabled ?? false))
165
+ return result;
166
+ const noticesByKey = new Map();
167
+ const resolvedExecution = resolvedRunner === undefined
168
+ ? resolveImproveLlmExecution({
169
+ config,
170
+ profile: strategy,
171
+ process: strategy?.processes?.consolidate,
172
+ processName: "memory-contradiction-detection",
173
+ })
174
+ : null;
175
+ for (const notice of resolvedExecution?.notices ?? [])
176
+ noticesByKey.set(JSON.stringify(notice), notice);
177
+ const contradictionRunner = resolvedRunner === null ? undefined : (resolvedRunner ?? resolvedExecution?.runner);
178
+ if (!contradictionRunner)
173
179
  return result;
174
180
  // Collect derived memories grouped by parent.
175
181
  const memoriesDir = path.join(stashDir, "memories");
@@ -208,7 +214,7 @@ export async function detectAndWriteContradictions(stashDir, config, chat = chat
208
214
  family.push(entry);
209
215
  byParent.set(parentRef, family);
210
216
  }
211
- let totalPairsChecked = 0;
217
+ const candidatePairs = [];
212
218
  for (const [, family] of byParent) {
213
219
  if (family.length < 2)
214
220
  continue;
@@ -219,7 +225,7 @@ export async function detectAndWriteContradictions(stashDir, config, chat = chat
219
225
  result.familiesExamined++;
220
226
  for (let i = 0; i < family.length - 1; i++) {
221
227
  for (let j = i + 1; j < family.length; j++) {
222
- if (totalPairsChecked >= MAX_PAIRS_PER_RUN)
228
+ if (candidatePairs.length >= MAX_PAIRS_PER_RUN)
223
229
  break;
224
230
  const a = family[i];
225
231
  const b = family[j];
@@ -237,58 +243,76 @@ export async function detectAndWriteContradictions(stashDir, config, chat = chat
237
243
  const loserCB = Array.isArray(loserData.contradictedBy) ? loserData.contradictedBy : [];
238
244
  if (loserCB.includes(winnerRef))
239
245
  continue;
240
- const prompt = buildContradictionJudgePrompt(a, b);
241
- const judgeResult = await callStructured({
242
- feature: "memory_contradiction_detection",
243
- akmConfig: config,
244
- // Resolver-less key: the strategy decision IS the gate (default-off).
245
- enabled: strategy?.processes?.consolidate?.contradictionDetection?.enabled ?? false,
246
- config: contradictionLlm,
247
- messages: [
248
- { role: "system", content: "Return only valid JSON. No prose." },
249
- { role: "user", content: prompt },
250
- ],
251
- request: { chat },
252
- parse: (raw) => raw ?? null,
253
- // A transport throw used to escape the gated fn into the gate's
254
- // catch and take the null fallback ("skip"); onError reproduces it.
255
- onError: () => null,
256
- fallback: null, // null means "skip" — gate disabled or LLM call failed.
257
- });
258
- totalPairsChecked++;
259
- result.pairsChecked++;
260
- if (!judgeResult)
261
- continue; // Feature gate disabled or LLM call failed.
262
- let parsed = null;
263
- try {
264
- parsed = parseEmbeddedJsonResponse(judgeResult);
265
- }
266
- catch {
267
- result.warnings.push(`Could not parse contradiction judge response for pair ${a.ref} / ${b.ref}`);
268
- continue;
269
- }
270
- if (!parsed?.contradicts)
271
- continue;
272
- const confidence = typeof parsed.confidence === "number" ? parsed.confidence : 0;
273
- if (confidence < CONTRADICT_CONFIDENCE_THRESHOLD) {
274
- result.warnings.push(`Pair ${a.ref} / ${b.ref}: confidence ${confidence.toFixed(2)} below ${CONTRADICT_CONFIDENCE_THRESHOLD} threshold — skipped.`);
275
- continue;
276
- }
277
- // Write a SINGLE directed contradiction edge: the losing (older) memory
278
- // gets `contradictedBy` pointing to the winner. A mutual A↔B pair forms
279
- // a 2-cycle that the SCC resolver refreshes back to active, erasing the
280
- // contradiction every run (see pickContradictionLoser).
281
- try {
282
- const wrote = writeContradictedByEdge(loser.filePath, winnerRef);
283
- result.edgesWritten += wrote ? 1 : 0;
284
- }
285
- catch (err) {
286
- result.warnings.push(`Failed to write contradiction edge ${loser.ref} -> ${winnerRef}: ${err instanceof Error ? err.message : String(err)}`);
287
- }
246
+ candidatePairs.push({ a, b, loser, winnerRef });
288
247
  }
289
- if (totalPairsChecked >= MAX_PAIRS_PER_RUN)
248
+ if (candidatePairs.length >= MAX_PAIRS_PER_RUN)
290
249
  break;
291
250
  }
292
251
  }
293
- return result;
252
+ if (candidatePairs.length === 0) {
253
+ const notices = Object.freeze([...noticesByKey.values()]);
254
+ return notices.length > 0 ? { ...result, notices } : result;
255
+ }
256
+ const dispatchLease = await preflightStructuredLlmRunner(contradictionRunner);
257
+ try {
258
+ for (const { a, b, loser, winnerRef } of candidatePairs) {
259
+ const prompt = buildContradictionJudgePrompt(a, b);
260
+ const judgeResult = await callStructured({
261
+ feature: "memory_contradiction_detection",
262
+ akmConfig: config,
263
+ // Resolver-less key: the strategy decision IS the gate (default-off).
264
+ enabled: true,
265
+ runner: contradictionRunner,
266
+ lease: dispatchLease,
267
+ messages: [
268
+ { role: "system", content: "Return only valid JSON. No prose." },
269
+ { role: "user", content: prompt },
270
+ ],
271
+ ...(chat ? { request: { chat } } : {}),
272
+ onNotices: (notices) => {
273
+ for (const notice of notices)
274
+ noticesByKey.set(JSON.stringify(notice), notice);
275
+ },
276
+ parse: (raw) => raw ?? null,
277
+ // A transport throw used to escape the gated fn into the gate's
278
+ // catch and take the null fallback ("skip"); onError reproduces it.
279
+ onError: () => null,
280
+ fallback: null, // null means "skip" — gate disabled or LLM call failed.
281
+ });
282
+ result.pairsChecked++;
283
+ if (!judgeResult)
284
+ continue; // Feature gate disabled or LLM call failed.
285
+ let parsed = null;
286
+ try {
287
+ parsed = parseEmbeddedJsonResponse(judgeResult);
288
+ }
289
+ catch {
290
+ result.warnings.push(`Could not parse contradiction judge response for pair ${a.ref} / ${b.ref}`);
291
+ continue;
292
+ }
293
+ if (!parsed?.contradicts)
294
+ continue;
295
+ const confidence = typeof parsed.confidence === "number" ? parsed.confidence : 0;
296
+ if (confidence < CONTRADICT_CONFIDENCE_THRESHOLD) {
297
+ result.warnings.push(`Pair ${a.ref} / ${b.ref}: confidence ${confidence.toFixed(2)} below ${CONTRADICT_CONFIDENCE_THRESHOLD} threshold — skipped.`);
298
+ continue;
299
+ }
300
+ // Write a SINGLE directed contradiction edge: the losing (older) memory
301
+ // gets `contradictedBy` pointing to the winner. A mutual A↔B pair forms
302
+ // a 2-cycle that the SCC resolver refreshes back to active, erasing the
303
+ // contradiction every run (see pickContradictionLoser).
304
+ try {
305
+ const wrote = writeContradictedByEdge(loser.filePath, winnerRef);
306
+ result.edgesWritten += wrote ? 1 : 0;
307
+ }
308
+ catch (err) {
309
+ result.warnings.push(`Failed to write contradiction edge ${loser.ref} -> ${winnerRef}: ${err instanceof Error ? err.message : String(err)}`);
310
+ }
311
+ }
312
+ }
313
+ finally {
314
+ disposeLoweredExecutionDispatchLease(dispatchLease);
315
+ }
316
+ const notices = Object.freeze([...noticesByKey.values()]);
317
+ return notices.length > 0 ? { ...result, notices } : result;
294
318
  }
@@ -44,61 +44,45 @@ export const OUTCOME_SCORE_MAX = 1.5;
44
44
  */
45
45
  export const DIVERSITY_FLOOR_FRACTION = 0.1;
46
46
  /**
47
- * Upsert one asset's outcome row.
48
- *
49
- * On first call for a ref (no prior row): warm-starts from `utilityScore`.
50
- * On subsequent calls: applies the differential update formula.
51
- *
52
- * Returns the resulting `outcome_score` so the caller can pass it to
53
- * `computeSalience` without a second read.
47
+ * Project the next outcome values without touching storage. Dry planning and
48
+ * the live writer both call this function, so rank inputs cannot drift merely
49
+ * because one caller persists the projection and the other does not.
54
50
  */
55
- export function updateAssetOutcome(db, inputs) {
56
- const now = inputs.now ?? Date.now();
51
+ export function projectAssetOutcome(existing, inputs) {
57
52
  const valence = inputs.valence ?? 0;
58
- // Fetch existing row (or undefined for first insert).
59
- const existing = getAssetOutcome(db, inputs.ref);
60
53
  const isNewRow = existing === undefined;
61
54
  let outcomeScore;
62
55
  let expectedRetrievalRate;
63
56
  if (isNewRow) {
64
- // ── Warm-start ─────────────────────────────────────────────────────────
65
- // Seed from utility EMA, clipped to [0, WARM_START_CAP].
66
- // The warm-start is a non-negative seed; the first real differential update
67
- // will adjust it up or down from there.
68
57
  const seedScore = Math.min(WARM_START_CAP, Math.max(0, inputs.utilityScore ?? 0));
69
58
  outcomeScore = seedScore;
70
- // Seed expected_retrieval_rate = 0 (no delta history yet).
71
- // Seeding with currentRetrievalCount would produce a large spurious negative
72
- // prediction error on the first real cycle (delta ≪ cumulative count).
73
59
  expectedRetrievalRate = 0;
74
60
  }
75
61
  else {
76
- // ── Differential update ────────────────────────────────────────────────
77
- //
78
- // retrieval_delta = current − stored (non-negative — we never go backwards)
79
62
  const retrievalDelta = Math.max(0, inputs.currentRetrievalCount - existing.retrieval_count);
80
- // Differential prediction-error term:
81
- // outcome = (retrieval_delta − expected_delta) + valence
82
- //
83
- // Prediction error is computed against the PRIOR stored EMA (before folding
84
- // in this cycle's observation), so the current delta cannot leak into its own
85
- // expectation. Negative values are intentional — they signal below-average cycles.
86
63
  const expectedDelta = existing.expected_retrieval_rate;
87
64
  const predictionError = retrievalDelta - expectedDelta;
88
- // Advance the EMA over the OBSERVED delta (not the cumulative count).
89
- // expected' = α × delta + (1−α) × prior_expected
90
65
  expectedRetrievalRate =
91
66
  OUTCOME_EMA_ALPHA * retrievalDelta + (1 - OUTCOME_EMA_ALPHA) * existing.expected_retrieval_rate;
92
- // Running sum (EMA approach): new score = α × update + (1−α) × old
93
- // so the score tracks the moving signal, not the cumulative sum.
94
67
  const rawUpdate = predictionError + valence;
95
68
  const newScore = OUTCOME_EMA_ALPHA * rawUpdate + (1 - OUTCOME_EMA_ALPHA) * existing.outcome_score;
96
- // Clip to [OUTCOME_SCORE_MIN, OUTCOME_SCORE_MAX] — the ceiling is the RPE
97
- // saturation analog (G2): without it, long-lived popular assets accumulate
98
- // unbounded positive mass and would dominate rank_score when outcome weight
99
- // is enabled.
100
69
  outcomeScore = Math.min(OUTCOME_SCORE_MAX, Math.max(OUTCOME_SCORE_MIN, newScore));
101
70
  }
71
+ return { outcomeScore, expectedRetrievalRate, isNewRow };
72
+ }
73
+ /**
74
+ * Upsert one asset's outcome row.
75
+ *
76
+ * On first call for a ref (no prior row): warm-starts from `utilityScore`.
77
+ * On subsequent calls: applies the differential update formula.
78
+ *
79
+ * Returns the resulting `outcome_score` so the caller can pass it to
80
+ * `computeSalience` without a second read.
81
+ */
82
+ export function updateAssetOutcome(db, inputs) {
83
+ const now = inputs.now ?? Date.now();
84
+ const existing = getAssetOutcome(db, inputs.ref);
85
+ const projection = projectAssetOutcome(existing, inputs);
102
86
  // Upsert the row. See `upsertAssetOutcome` in
103
87
  // storage/repositories/outcome-repository.ts (#672 part 2) for the SQL text
104
88
  // and the `review_pressure` omission rationale — this call site is
@@ -107,13 +91,13 @@ export function updateAssetOutcome(db, inputs) {
107
91
  ref: inputs.ref,
108
92
  lastRetrievedAt: inputs.lastRetrievedAt,
109
93
  retrievalCount: inputs.currentRetrievalCount,
110
- expectedRetrievalRate,
94
+ expectedRetrievalRate: projection.expectedRetrievalRate,
111
95
  negativeFeedbackCount: inputs.negativeFeedbackCount,
112
96
  acceptedChangeCount: inputs.acceptedChangeCount,
113
- outcomeScore,
97
+ outcomeScore: projection.outcomeScore,
114
98
  updatedAt: now,
115
99
  });
116
- return { outcomeScore, isNewRow };
100
+ return { outcomeScore: projection.outcomeScore, isNewRow: projection.isNewRow };
117
101
  }
118
102
  // ── Reader ────────────────────────────────────────────────────────────────────
119
103
  //
@@ -0,0 +1,134 @@
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
+ * Pure improve planning projections.
6
+ *
7
+ * Filesystem/database snapshot collection stays in `preparation.ts`; this leaf
8
+ * receives immutable values and is shared by dry preview and live execution.
9
+ * Keeping the limit/lane projection here prevents the two entry paths from
10
+ * independently reconstructing "what would run".
11
+ */
12
+ import { conceptIdFromTypeName } from "../../core/asset/resolve-ref.js";
13
+ import { parseMemoryName } from "./memory/derived-ref.js";
14
+ function canonicalMemoryConceptRef(ref) {
15
+ const name = parseMemoryName(ref);
16
+ return name === undefined ? undefined : conceptIdFromTypeName("memory", name);
17
+ }
18
+ /**
19
+ * Project the refs that survive memory cleanup from either the planned archive
20
+ * candidates (dry estimate) or the archives that actually succeeded (live
21
+ * execution). Cleanup records use the legacy `memory:<name>` identity channel,
22
+ * while improve candidates use canonical `memories/<name>` concept refs, so
23
+ * both sides are normalized before comparison.
24
+ */
25
+ export function projectMemoryCleanup(input) {
26
+ const cleanupRefs = input.allowApply ? (input.mode === "estimate" ? input.candidateRefs : input.archivedRefs) : [];
27
+ const removed = new Set(cleanupRefs.map(canonicalMemoryConceptRef).filter((ref) => ref !== undefined));
28
+ const postCleanupRefs = input.plannedRefs.filter((entry) => {
29
+ const canonical = canonicalMemoryConceptRef(entry.ref);
30
+ return canonical === undefined || !removed.has(canonical);
31
+ });
32
+ return {
33
+ postCleanupRefs,
34
+ gate: {
35
+ name: "cleanup",
36
+ removed: input.plannedRefs.length - postCleanupRefs.length,
37
+ reason: !input.allowApply
38
+ ? "memory cleanup archive application is autonomy-gated"
39
+ : input.mode === "estimate"
40
+ ? "would be archived by memory cleanup"
41
+ : "archived by memory cleanup",
42
+ },
43
+ };
44
+ }
45
+ /**
46
+ * Apply the final global cap to an already-ranked candidate snapshot.
47
+ * Replay remains additive to the ordinary cap, matching the live #610 rule.
48
+ */
49
+ export function selectEffectiveImproveRefs(args) {
50
+ const distillOnlySet = new Set(args.distillOnlyRefs.map((entry) => entry.ref));
51
+ const reflectAndDistill = args.rankedRefs.filter((entry) => !distillOnlySet.has(entry.ref));
52
+ const distillOnly = args.rankedRefs.filter((entry) => distillOnlySet.has(entry.ref));
53
+ // Preserve the established live ordering: ordinary reflect-path refs first,
54
+ // then distill-only refs, with the rank order stable inside each partition.
55
+ const allLoopRefs = [...reflectAndDistill, ...distillOnly];
56
+ const replay = allLoopRefs.filter((entry) => entry.eligibilitySource === "replay");
57
+ const ordinary = allLoopRefs.filter((entry) => entry.eligibilitySource !== "replay");
58
+ const selectedOrdinary = args.limit === undefined ? ordinary : ordinary.slice(0, args.limit);
59
+ const loopRefs = [...selectedOrdinary, ...replay.slice(0, args.replayBudget)];
60
+ return {
61
+ loopRefs,
62
+ distillOnlyRefs: distillOnly,
63
+ limitRemoved: allLoopRefs.length - loopRefs.length,
64
+ };
65
+ }
66
+ /** Build the stable public plan DTO from one invocation's selector observation. */
67
+ export function buildImproveExecutionPlan(input) {
68
+ const effectiveRefs = input.effectiveRefs.map((entry) => ({
69
+ ref: entry.ref,
70
+ lane: input.distillOnlyRefs.has(entry.ref) ? "distill-only" : (entry.eligibilitySource ?? "unknown"),
71
+ reason: entry.reason,
72
+ }));
73
+ return {
74
+ mode: input.dryRun ? "estimate" : "execution",
75
+ dispatch: !input.dryRun,
76
+ snapshot: { ...input.snapshot },
77
+ candidates: {
78
+ rawInScope: input.rawInScope,
79
+ selected: input.selectedRefs.length,
80
+ effective: effectiveRefs.length,
81
+ },
82
+ limits: {
83
+ configured: { ...input.configuredLimits },
84
+ ...(input.effectiveLimit !== undefined ? { effective: input.effectiveLimit } : {}),
85
+ additiveReplayAllowance: input.replayBudget,
86
+ ...(input.effectiveLimit !== undefined ? { totalCeiling: input.effectiveLimit + input.replayBudget } : {}),
87
+ },
88
+ gates: input.gates.map((gate) => ({ ...gate })),
89
+ effectiveRefs,
90
+ ...(input.proactive
91
+ ? {
92
+ proactive: {
93
+ ...input.proactive,
94
+ configured: { ...input.proactive.configured },
95
+ effective: { ...input.proactive.effective },
96
+ selectedRefs: [...input.proactive.selectedRefs],
97
+ },
98
+ }
99
+ : {}),
100
+ consolidation: {
101
+ ...input.consolidation,
102
+ configured: { ...input.consolidation.configured },
103
+ effective: { ...input.consolidation.effective },
104
+ gates: {
105
+ profile: { ...input.consolidation.gates.profile },
106
+ minimumPool: { ...input.consolidation.gates.minimumPool },
107
+ delta: { ...input.consolidation.gates.delta },
108
+ },
109
+ },
110
+ stages: [
111
+ {
112
+ name: "consolidation",
113
+ wouldRun: input.consolidation.wouldRun,
114
+ reason: input.consolidation.reason,
115
+ },
116
+ {
117
+ name: "extract",
118
+ wouldRun: input.stageConfig.extract.enabled,
119
+ reason: input.stageConfig.extract.reason,
120
+ },
121
+ {
122
+ name: "graph-extraction",
123
+ wouldRun: input.stageConfig.graphExtraction.enabled,
124
+ reason: input.stageConfig.graphExtraction.reason,
125
+ },
126
+ {
127
+ name: "memory-inference",
128
+ wouldRun: input.stageConfig.memoryInference.enabled,
129
+ reason: input.stageConfig.memoryInference.reason,
130
+ },
131
+ ],
132
+ triage: { ...input.triage },
133
+ };
134
+ }