akm-cli 0.9.0-rc.1 → 0.9.0-rc.13

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 (587) hide show
  1. package/CHANGELOG.md +1190 -52
  2. package/README.md +62 -37
  3. package/SECURITY.md +46 -31
  4. package/dist/akm +162 -38
  5. package/dist/akm-migrate +44 -0
  6. package/dist/assets/backends/schtasks-template.xml +2 -1
  7. package/dist/assets/hints/cli-hints-full.md +268 -118
  8. package/dist/assets/hints/cli-hints-short.md +87 -24
  9. package/dist/assets/{profiles → improve-strategies}/catchup.json +3 -1
  10. package/dist/assets/{profiles → improve-strategies}/consolidate.json +3 -1
  11. package/dist/assets/{profiles → improve-strategies}/default.json +6 -7
  12. package/dist/assets/improve-strategies/frequent.json +15 -0
  13. package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +4 -2
  14. package/dist/assets/{profiles → improve-strategies}/memory-focus.json +4 -1
  15. package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +5 -5
  16. package/dist/assets/{profiles → improve-strategies}/quick.json +4 -2
  17. package/dist/assets/improve-strategies/reflect-distill.json +30 -0
  18. package/dist/assets/{profiles → improve-strategies}/thorough.json +1 -1
  19. package/dist/assets/prompts/consolidate-system.md +5 -5
  20. package/dist/assets/prompts/extract-session.md +2 -6
  21. package/dist/assets/prompts/memory-infer-user.md +2 -3
  22. package/dist/assets/prompts/reflect-llm-framed-contract.md +11 -0
  23. package/dist/assets/prompts/reflect-llm-schema-contract.md +3 -0
  24. package/dist/assets/prompts/reflect-output-repair.md +3 -0
  25. package/dist/assets/stash-skeleton/README.md +38 -10
  26. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +8 -0
  27. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +8 -0
  28. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +14 -1
  29. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +13 -1
  30. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +9 -1
  31. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +11 -0
  32. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +9 -0
  33. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +9 -0
  34. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +8 -0
  35. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +100 -0
  36. package/dist/assets/stash-skeleton/facts/conventions/domains.md +64 -0
  37. package/dist/assets/stash-skeleton/facts/conventions/organization.md +136 -0
  38. package/dist/assets/tasks/core/extract.yml +3 -2
  39. package/dist/assets/tasks/core/improve.yml +2 -1
  40. package/dist/assets/tasks/core/index-refresh.yml +1 -0
  41. package/dist/assets/tasks/core/sync.yml +1 -0
  42. package/dist/assets/tasks/core/version-check.yml +2 -1
  43. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  44. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  45. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  46. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  47. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  48. package/dist/assets/templates/html/health.html +5 -4
  49. package/dist/assets/workflows/workflow-template.md +31 -15
  50. package/dist/cli/invocation.js +279 -0
  51. package/dist/cli/parse-args.js +5 -90
  52. package/dist/cli/retired-commands.js +78 -0
  53. package/dist/cli/shared.js +158 -48
  54. package/dist/cli-node.mjs +2 -1
  55. package/dist/cli.js +747 -293
  56. package/dist/commands/agent/agent-dispatch.js +19 -18
  57. package/dist/commands/agent/agent-support.js +0 -24
  58. package/dist/commands/agent/contribute-cli.js +43 -97
  59. package/dist/commands/completions.js +80 -23
  60. package/dist/commands/config-cli.js +44 -281
  61. package/dist/commands/env/env-binding.js +13 -9
  62. package/dist/commands/env/env-cli.js +76 -159
  63. package/dist/commands/env/env.js +12 -163
  64. package/dist/commands/env/marker-path.js +6 -0
  65. package/dist/commands/env/secret-cli.js +45 -61
  66. package/dist/commands/env/secret.js +32 -62
  67. package/dist/commands/feedback-cli.js +179 -85
  68. package/dist/commands/health/accept-rate.js +58 -0
  69. package/dist/commands/health/advisories.js +7 -8
  70. package/dist/commands/health/checks.js +279 -94
  71. package/dist/commands/health/html-report.js +197 -578
  72. package/dist/commands/health/improve-metrics.js +277 -246
  73. package/dist/commands/health/llm-usage.js +19 -19
  74. package/dist/commands/health/md-report.js +16 -7
  75. package/dist/commands/health/metrics.js +67 -32
  76. package/dist/commands/health/renderers.js +47 -0
  77. package/dist/commands/health/report-view-model.js +508 -0
  78. package/dist/commands/health/stash-exposure.js +1 -1
  79. package/dist/commands/health/surfaces.js +16 -56
  80. package/dist/commands/health/task-runs.js +3 -67
  81. package/dist/{migrate-storage-node.mjs → commands/health/types-checks.js} +1 -5
  82. package/dist/commands/health/types-improve.js +29 -0
  83. package/dist/{output/text/save.js → commands/health/types-metrics.js} +1 -2
  84. package/dist/commands/health/types-result.js +7 -0
  85. package/dist/commands/health/types-runs.js +4 -0
  86. package/dist/commands/health/types-session-log.js +4 -0
  87. package/dist/commands/health/types-windows.js +4 -0
  88. package/dist/commands/health/types.js +26 -21
  89. package/dist/commands/health/windows.js +2 -3
  90. package/dist/commands/health.js +296 -167
  91. package/dist/commands/improve/anti-collapse.js +5 -5
  92. package/dist/commands/improve/autonomy-gate.js +68 -0
  93. package/dist/commands/improve/collapse-detector.js +65 -52
  94. package/dist/commands/improve/consolidate/chunking.js +9 -7
  95. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  96. package/dist/commands/improve/consolidate/merge.js +4 -0
  97. package/dist/commands/improve/consolidate.js +454 -1354
  98. package/dist/commands/improve/content-hash.js +39 -0
  99. package/dist/commands/improve/distill/content-repair.js +4 -10
  100. package/dist/commands/improve/distill/promote-memory.js +89 -64
  101. package/dist/commands/improve/distill/quality-gate.js +118 -42
  102. package/dist/commands/improve/distill-guards.js +1 -1
  103. package/dist/commands/improve/distill-promotion-policy.js +33 -888
  104. package/dist/commands/improve/distill.js +607 -363
  105. package/dist/commands/improve/eligibility.js +165 -79
  106. package/dist/commands/improve/extract-cli.js +35 -126
  107. package/dist/commands/improve/extract-prompt.js +6 -35
  108. package/dist/commands/improve/extract.js +640 -391
  109. package/dist/commands/improve/feedback-valence.js +2 -12
  110. package/dist/commands/improve/improve-cli.js +134 -135
  111. package/dist/commands/improve/improve-result-file.js +30 -50
  112. package/dist/commands/improve/improve-run-types.js +4 -0
  113. package/dist/commands/improve/improve-strategies.js +135 -0
  114. package/dist/commands/improve/improve.js +904 -701
  115. package/dist/commands/improve/locks.js +64 -111
  116. package/dist/commands/improve/loop-stages.js +1110 -923
  117. package/dist/commands/improve/memory/derived-ref.js +124 -0
  118. package/dist/commands/improve/memory/memory-belief.js +79 -7
  119. package/dist/commands/improve/memory/memory-contradiction-detect.js +49 -52
  120. package/dist/commands/improve/memory/memory-improve.js +25 -37
  121. package/dist/commands/improve/outcome-loop.js +25 -88
  122. package/dist/commands/improve/preparation.js +1034 -813
  123. package/dist/commands/improve/proactive-maintenance.js +34 -9
  124. package/dist/commands/improve/proposal-envelope.js +31 -0
  125. package/dist/commands/improve/reflect.js +983 -794
  126. package/dist/commands/improve/run-context.js +119 -0
  127. package/dist/commands/improve/salience.js +24 -127
  128. package/dist/commands/improve/session-asset.js +7 -3
  129. package/dist/commands/improve/shared.js +14 -34
  130. package/dist/commands/improve/source-identity.js +28 -0
  131. package/dist/commands/improve/triage.js +20 -17
  132. package/dist/commands/lint/base-linter.js +340 -313
  133. package/dist/commands/lint/env-key-rules.js +31 -47
  134. package/dist/commands/lint/index.js +185 -30
  135. package/dist/commands/{events.js → log.js} +28 -38
  136. package/dist/commands/migrate-cli.js +54 -0
  137. package/dist/commands/migration-tool.js +55 -0
  138. package/dist/commands/observability-cli.js +70 -208
  139. package/dist/commands/proposal/diff-format.js +50 -0
  140. package/dist/commands/proposal/drain-policies.js +0 -6
  141. package/dist/commands/proposal/drain.js +91 -40
  142. package/dist/commands/proposal/proposal-cli.js +134 -132
  143. package/dist/commands/proposal/proposal-types.js +56 -0
  144. package/dist/commands/proposal/proposal.js +83 -65
  145. package/dist/commands/proposal/propose-cli.js +88 -0
  146. package/dist/commands/proposal/propose.js +105 -88
  147. package/dist/commands/proposal/repository.js +1303 -278
  148. package/dist/commands/proposal/validators/proposal-quality-validators.js +16 -6
  149. package/dist/commands/proposal/validators/proposal-validators.js +61 -12
  150. package/dist/commands/proposal/validators/proposals.js +6 -8
  151. package/dist/commands/read/curate.js +78 -73
  152. package/dist/commands/read/knowledge.js +510 -13
  153. package/dist/commands/read/registry-search.js +2 -2
  154. package/dist/commands/read/remember-cli.js +84 -15
  155. package/dist/commands/read/search-cli.js +203 -96
  156. package/dist/commands/read/search.js +126 -94
  157. package/dist/commands/read/show.js +226 -250
  158. package/dist/commands/registry-cli.js +34 -60
  159. package/dist/commands/remember.js +18 -57
  160. package/dist/commands/sources/add-cli.js +104 -49
  161. package/dist/commands/sources/bundle-cli.js +166 -0
  162. package/dist/commands/sources/bundle-config-ops.js +63 -0
  163. package/dist/commands/sources/info.js +27 -15
  164. package/dist/commands/sources/init.js +30 -40
  165. package/dist/commands/sources/installed-stashes.js +469 -172
  166. package/dist/commands/sources/schema-repair.js +10 -9
  167. package/dist/commands/sources/self-update.js +182 -121
  168. package/dist/commands/sources/source-add.js +169 -178
  169. package/dist/commands/sources/source-clone.js +144 -41
  170. package/dist/commands/sources/source-manage.js +94 -59
  171. package/dist/commands/sources/sources-cli.js +64 -205
  172. package/dist/commands/sources/stash-cli.js +91 -54
  173. package/dist/commands/sources/stash-skeleton.js +1 -1
  174. package/dist/commands/tasks/tasks-cli.js +106 -104
  175. package/dist/commands/tasks/tasks.js +445 -262
  176. package/dist/commands/workflow-cli.js +75 -228
  177. package/dist/core/action-contributors.js +1 -1
  178. package/dist/core/activation-policy.js +49 -0
  179. package/dist/core/adapter/adapters/agent-skills-adapter.js +181 -0
  180. package/dist/core/adapter/adapters/akm-adapter.js +528 -0
  181. package/dist/core/adapter/adapters/akm-lint.js +392 -0
  182. package/dist/core/adapter/adapters/akm-metadata.js +387 -0
  183. package/dist/core/adapter/adapters/akm-task-adapter.js +149 -0
  184. package/dist/core/adapter/adapters/akm-workflow-adapter.js +180 -0
  185. package/dist/core/adapter/adapters/claude-adapter.js +61 -0
  186. package/dist/core/adapter/adapters/dotenv-adapter.js +187 -0
  187. package/dist/core/adapter/adapters/generic-files-adapter.js +119 -0
  188. package/dist/core/adapter/adapters/index.js +80 -0
  189. package/dist/core/adapter/adapters/llm-wiki-adapter.js +419 -0
  190. package/dist/core/adapter/adapters/okf-adapter.js +391 -0
  191. package/dist/core/adapter/adapters/opencode-adapter.js +68 -0
  192. package/dist/core/adapter/adapters/shared.js +286 -0
  193. package/dist/core/adapter/adapters/tool-dir-shared.js +217 -0
  194. package/dist/core/adapter/adapters/website-snapshot-adapter.js +155 -0
  195. package/dist/core/adapter/bundle-adapter.js +4 -0
  196. package/dist/core/adapter/detect-adapter.js +17 -0
  197. package/dist/core/adapter/recognize-match.js +44 -0
  198. package/dist/core/adapter/registry.js +56 -0
  199. package/dist/core/adapter/types.js +4 -0
  200. package/dist/core/asset/akm-markdown.js +30 -0
  201. package/dist/core/asset/asset-placement.js +243 -0
  202. package/dist/core/asset/asset-ref.js +110 -79
  203. package/dist/core/asset/asset-serialize.js +20 -0
  204. package/dist/core/asset/frontmatter.js +28 -12
  205. package/dist/core/asset/markdown.js +40 -51
  206. package/dist/core/asset/resolve-ref.js +274 -0
  207. package/dist/core/asset/stash-meta.js +2 -2
  208. package/dist/core/bundle-id.js +51 -0
  209. package/dist/core/common.js +281 -86
  210. package/dist/core/config/config-io.js +42 -128
  211. package/dist/core/config/config-schema.js +233 -855
  212. package/dist/core/config/config-sources.js +162 -39
  213. package/dist/core/config/config-types.js +16 -11
  214. package/dist/core/config/config-version.js +29 -0
  215. package/dist/core/config/config-walker.js +126 -37
  216. package/dist/core/config/config.js +154 -331
  217. package/dist/core/config/deep-merge.js +41 -0
  218. package/dist/core/config/engine-semantics.js +28 -0
  219. package/dist/core/config/experimental.js +21 -0
  220. package/dist/core/config/schema/embedding.js +38 -0
  221. package/dist/core/config/schema/engines.js +116 -0
  222. package/dist/core/config/schema/experimental.js +47 -0
  223. package/dist/core/config/schema/feedback.js +31 -0
  224. package/dist/core/config/schema/improve-processes.js +389 -0
  225. package/dist/core/config/schema/improve.js +94 -0
  226. package/dist/core/config/schema/index-config.js +176 -0
  227. package/dist/core/config/schema/output.js +18 -0
  228. package/dist/core/config/schema/primitives.js +94 -0
  229. package/dist/core/config/schema/search.js +30 -0
  230. package/dist/core/config/schema/setup.js +18 -0
  231. package/dist/core/config/schema/sources-bundles.js +169 -0
  232. package/dist/core/config/schema/workflow.js +29 -0
  233. package/dist/core/env-secret-ref.js +155 -20
  234. package/dist/core/errors.js +17 -15
  235. package/dist/core/events-types.js +4 -0
  236. package/dist/core/events.js +46 -128
  237. package/dist/core/extra-params.js +62 -0
  238. package/dist/core/file-change.js +17 -0
  239. package/dist/core/file-lock.js +202 -57
  240. package/dist/core/fs-txn.js +392 -0
  241. package/dist/core/git-message.js +59 -0
  242. package/dist/core/improve-result.js +167 -0
  243. package/dist/core/lesson-lint.js +1 -17
  244. package/dist/core/logs-db.js +1 -1
  245. package/dist/core/maintenance-barrier.js +135 -0
  246. package/dist/core/migration-operation.js +44 -0
  247. package/dist/core/mutation-target.js +78 -0
  248. package/dist/core/paths.js +22 -25
  249. package/dist/core/platform.js +10 -0
  250. package/dist/core/recognition-util.js +128 -0
  251. package/dist/core/redaction.js +392 -0
  252. package/dist/core/standards/resolve-standards-context.js +36 -65
  253. package/dist/core/standards/resolve-stash-standards.js +2 -2
  254. package/dist/core/standards/resolve-type-conventions.js +5 -5
  255. package/dist/core/state/migrations.js +242 -11
  256. package/dist/core/state-db.js +98 -10
  257. package/dist/core/structured.js +1 -1
  258. package/dist/core/subprocess.js +303 -0
  259. package/dist/core/text-truncation.js +9 -5
  260. package/dist/core/time.js +20 -0
  261. package/dist/core/type-presentation.js +130 -0
  262. package/dist/core/warn.js +0 -3
  263. package/dist/core/write-source.js +834 -118
  264. package/dist/indexer/bundle-identity-guard.js +92 -0
  265. package/dist/indexer/db/graph-db.js +1 -25
  266. package/dist/indexer/db/llm-cache.js +1 -1
  267. package/dist/indexer/ensure-index.js +30 -9
  268. package/dist/indexer/graph/graph-boost.js +9 -30
  269. package/dist/indexer/graph/graph-extraction.js +41 -27
  270. package/dist/indexer/graph/graph-types.js +4 -0
  271. package/dist/indexer/index-writer-lock.js +93 -49
  272. package/dist/indexer/index-written-assets.js +100 -53
  273. package/dist/indexer/indexer.js +746 -329
  274. package/dist/indexer/init.js +18 -25
  275. package/dist/indexer/installations.js +142 -0
  276. package/dist/indexer/passes/dir-staleness.js +18 -10
  277. package/dist/indexer/passes/memory-inference.js +25 -15
  278. package/dist/indexer/passes/metadata.js +412 -243
  279. package/dist/indexer/scan/doc-to-entry.js +160 -0
  280. package/dist/indexer/scan/drain-dir.js +134 -0
  281. package/dist/indexer/search/db-search.js +292 -108
  282. package/dist/indexer/search/fts-query.js +64 -0
  283. package/dist/indexer/search/ranking-contributors.js +145 -25
  284. package/dist/indexer/search/ranking-types.js +4 -0
  285. package/dist/indexer/search/ranking.js +28 -71
  286. package/dist/indexer/search/search-attribution.js +67 -0
  287. package/dist/indexer/search/search-fields.js +18 -3
  288. package/dist/indexer/search/search-hit-enrichers.js +30 -40
  289. package/dist/indexer/search/search-source.js +157 -111
  290. package/dist/indexer/search/semantic-status.js +4 -1
  291. package/dist/indexer/usage/usage-events.js +10 -30
  292. package/dist/indexer/walk/file-context.js +3 -45
  293. package/dist/indexer/walk/matchers.js +42 -73
  294. package/dist/indexer/walk/path-resolver.js +11 -5
  295. package/dist/indexer/walk/walker.js +42 -14
  296. package/dist/integrations/agent/builder-shared.js +7 -0
  297. package/dist/integrations/agent/builders.js +5 -58
  298. package/dist/integrations/agent/config.js +3 -143
  299. package/dist/integrations/agent/detect.js +17 -2
  300. package/dist/integrations/agent/engine-resolution.js +231 -0
  301. package/dist/integrations/agent/index.js +1 -2
  302. package/dist/integrations/agent/model-aliases.js +8 -3
  303. package/dist/integrations/agent/profiles.js +6 -99
  304. package/dist/integrations/agent/prompts.js +46 -18
  305. package/dist/integrations/agent/runner-dispatch.js +78 -13
  306. package/dist/integrations/agent/runner.js +76 -208
  307. package/dist/integrations/agent/spawn.js +48 -279
  308. package/dist/integrations/harnesses/aider/agent-builder.js +9 -8
  309. package/dist/integrations/harnesses/aider/index.js +2 -12
  310. package/dist/integrations/harnesses/amazonq/agent-builder.js +10 -16
  311. package/dist/integrations/harnesses/amazonq/index.js +3 -17
  312. package/dist/integrations/harnesses/claude/agent-builder.js +2 -3
  313. package/dist/integrations/harnesses/claude/config-import.js +1 -3
  314. package/dist/integrations/harnesses/claude/index.js +1 -14
  315. package/dist/integrations/harnesses/claude/session-log.js +27 -75
  316. package/dist/integrations/harnesses/codex/agent-builder.js +8 -7
  317. package/dist/integrations/harnesses/codex/index.js +2 -13
  318. package/dist/integrations/harnesses/copilot/agent-builder.js +9 -9
  319. package/dist/integrations/harnesses/copilot/index.js +1 -13
  320. package/dist/integrations/harnesses/gemini/agent-builder.js +8 -9
  321. package/dist/integrations/harnesses/gemini/index.js +1 -13
  322. package/dist/integrations/harnesses/ids.js +24 -0
  323. package/dist/integrations/harnesses/index.js +31 -33
  324. package/dist/integrations/harnesses/opencode/agent-builder.js +23 -5
  325. package/dist/integrations/harnesses/opencode/config-import.js +1 -3
  326. package/dist/integrations/harnesses/opencode/index.js +1 -18
  327. package/dist/integrations/harnesses/opencode/session-log.js +67 -125
  328. package/dist/integrations/harnesses/opencode-sdk/harness.js +3 -17
  329. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +274 -191
  330. package/dist/integrations/harnesses/openhands/agent-builder.js +12 -10
  331. package/dist/integrations/harnesses/openhands/index.js +2 -12
  332. package/dist/integrations/harnesses/pi/agent-builder.js +11 -18
  333. package/dist/integrations/harnesses/pi/index.js +3 -16
  334. package/dist/integrations/harnesses/shared.js +17 -0
  335. package/dist/integrations/harnesses/types.js +38 -34
  336. package/dist/integrations/lockfile.js +211 -24
  337. package/dist/integrations/session-logs/index.js +24 -40
  338. package/dist/integrations/session-logs/provider-base.js +113 -0
  339. package/dist/llm/client.js +182 -110
  340. package/dist/llm/embedders/deterministic.js +2 -2
  341. package/dist/llm/embedders/remote.js +21 -9
  342. package/dist/llm/feature-gate.js +17 -57
  343. package/dist/llm/graph-extract.js +12 -13
  344. package/dist/llm/index-passes.js +8 -42
  345. package/dist/llm/memory-infer.js +144 -1
  346. package/dist/llm/metadata-enhance.js +45 -30
  347. package/dist/llm/structured-call.js +16 -8
  348. package/dist/llm/usage-persist.js +30 -5
  349. package/dist/llm/usage-telemetry.js +59 -6
  350. package/dist/output/cli-hints.js +1 -2
  351. package/dist/output/command-registry.js +27 -0
  352. package/dist/output/context.js +22 -7
  353. package/dist/output/format-exempt.js +80 -0
  354. package/dist/output/generic-render.js +251 -0
  355. package/dist/output/html-render.js +11 -16
  356. package/dist/output/render-registry.js +57 -0
  357. package/dist/output/renderers.js +15 -281
  358. package/dist/output/shapes/curate.js +10 -1
  359. package/dist/output/shapes/events.js +12 -7
  360. package/dist/output/shapes/helpers.js +58 -84
  361. package/dist/output/shapes/passthrough.js +8 -40
  362. package/dist/output/shapes/proposal/producer.js +15 -7
  363. package/dist/output/shapes/registry.js +12 -6
  364. package/dist/output/shapes.js +0 -9
  365. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  366. package/dist/output/text/bundle-show.js +7 -0
  367. package/dist/output/text/command-format.js +562 -0
  368. package/dist/output/text/env.js +1 -3
  369. package/dist/output/text/events.js +8 -7
  370. package/dist/output/text/helpers.js +15 -1375
  371. package/dist/output/text/proposal/producer.js +4 -2
  372. package/dist/output/text/proposal-format.js +202 -0
  373. package/dist/output/text/registry-commands.js +1 -2
  374. package/dist/output/text/registry.js +12 -6
  375. package/dist/output/text/show-directives.js +117 -0
  376. package/dist/output/text/show-format.js +103 -0
  377. package/dist/output/text/sync.js +5 -0
  378. package/dist/output/text/workflow-format.js +332 -0
  379. package/dist/output/text/workflow.js +1 -2
  380. package/dist/output/text.js +10 -19
  381. package/dist/registry/factory.js +4 -6
  382. package/dist/registry/origin-resolve.js +16 -27
  383. package/dist/registry/providers/skills-sh.js +3 -3
  384. package/dist/registry/providers/static-index.js +15 -25
  385. package/dist/registry/resolve.js +43 -94
  386. package/dist/registry/semver.js +43 -0
  387. package/dist/runtime.js +81 -12
  388. package/dist/scripts/akm-migrate.js +35529 -0
  389. package/dist/setup/detect.js +5 -7
  390. package/dist/setup/detected-engines.js +136 -0
  391. package/dist/setup/engine-config.js +100 -0
  392. package/dist/setup/registry-stash-loader.js +3 -3
  393. package/dist/setup/semantic-assets.js +12 -9
  394. package/dist/setup/setup.js +444 -208
  395. package/dist/setup/steps/connection-shared.js +120 -0
  396. package/dist/setup/steps/connection.js +108 -305
  397. package/dist/setup/steps/platforms.js +13 -12
  398. package/dist/setup/steps/semantic.js +15 -3
  399. package/dist/setup/steps/sources.js +21 -15
  400. package/dist/setup/steps/stashdir.js +6 -4
  401. package/dist/setup/steps/tasks.js +236 -119
  402. package/dist/setup/steps.js +3 -2
  403. package/dist/sources/freshness.js +39 -0
  404. package/dist/sources/provider-factory.js +11 -17
  405. package/dist/sources/providers/filesystem.js +2 -3
  406. package/dist/sources/providers/git-install.js +278 -34
  407. package/dist/sources/providers/git-provider.js +54 -56
  408. package/dist/sources/providers/git-stash.js +420 -91
  409. package/dist/sources/providers/git.js +2 -2
  410. package/dist/sources/providers/npm.js +16 -19
  411. package/dist/sources/providers/provider-utils.js +47 -22
  412. package/dist/sources/providers/sync-from-ref.js +3 -9
  413. package/dist/sources/providers/website.js +2 -2
  414. package/dist/sources/resolve.js +11 -10
  415. package/dist/sources/snapshot-fetchers/types.js +4 -0
  416. package/dist/sources/{website-ingest.js → snapshot-fetchers/website-ingest.js} +110 -41
  417. package/dist/storage/database.js +60 -4
  418. package/dist/storage/engines/sqlite-migrations.js +156 -5
  419. package/dist/storage/locations.js +1 -2
  420. package/dist/storage/repositories/canaries-repository.js +1 -1
  421. package/dist/storage/repositories/events-repository.js +51 -11
  422. package/dist/storage/repositories/improve-runs-repository.js +6 -32
  423. package/dist/storage/repositories/index-connection.js +79 -0
  424. package/dist/storage/repositories/index-db.js +4 -3
  425. package/dist/storage/repositories/index-entries-repository.js +863 -0
  426. package/dist/{indexer/db/entry-mapper.js → storage/repositories/index-entry-mapper.js} +19 -2
  427. package/dist/storage/repositories/index-entry-types.js +4 -0
  428. package/dist/storage/repositories/index-fts-repository.js +167 -0
  429. package/dist/storage/repositories/index-llm-cache-repository.js +108 -0
  430. package/dist/storage/repositories/index-meta-repository.js +49 -0
  431. package/dist/{indexer/db/schema.js → storage/repositories/index-schema.js} +226 -100
  432. package/dist/storage/repositories/index-sql.js +12 -0
  433. package/dist/storage/repositories/index-utility-repository.js +356 -0
  434. package/dist/storage/repositories/index-vec-repository.js +250 -0
  435. package/dist/storage/repositories/outcome-repository.js +119 -0
  436. package/dist/storage/repositories/proposals-repository.js +317 -75
  437. package/dist/storage/repositories/registry-cache.js +1 -1
  438. package/dist/storage/repositories/salience-repository.js +172 -0
  439. package/dist/storage/repositories/task-history-repository.js +110 -3
  440. package/dist/storage/repositories/workflow-runs-repository.js +68 -35
  441. package/dist/tasks/backends/cron.js +169 -46
  442. package/dist/tasks/backends/exec-utils.js +76 -3
  443. package/dist/tasks/backends/index.js +6 -9
  444. package/dist/tasks/backends/launchd.js +292 -55
  445. package/dist/tasks/backends/schtasks.js +557 -70
  446. package/dist/tasks/backends/types.js +4 -0
  447. package/dist/tasks/command-executable.js +93 -0
  448. package/dist/tasks/embedded.js +56 -38
  449. package/dist/tasks/parser.js +156 -64
  450. package/dist/tasks/resolve-akm-bin.js +144 -51
  451. package/dist/tasks/runner.js +377 -209
  452. package/dist/tasks/schedule.js +108 -19
  453. package/dist/tasks/scheduler-invocation.js +296 -0
  454. package/dist/tasks/schema.js +1 -1
  455. package/dist/tasks/task-id.js +35 -0
  456. package/dist/tasks/validator.js +30 -16
  457. package/dist/workflows/authoring/authoring.js +96 -148
  458. package/dist/workflows/authoring/scope-key.js +1 -1
  459. package/dist/workflows/cli.js +0 -20
  460. package/dist/workflows/concurrency-policy.js +15 -0
  461. package/dist/workflows/exec/brief.js +25 -59
  462. package/dist/workflows/exec/frozen-judge.js +47 -0
  463. package/dist/workflows/exec/native-executor.js +157 -94
  464. package/dist/workflows/exec/report.js +365 -200
  465. package/dist/workflows/exec/run-workflow.js +46 -40
  466. package/dist/workflows/exec/scheduler.js +12 -41
  467. package/dist/workflows/exec/step-work.js +239 -205
  468. package/dist/workflows/exec/workflow-engine-gate.js +67 -0
  469. package/dist/workflows/ir/compile.js +141 -283
  470. package/dist/workflows/ir/freeze.js +233 -0
  471. package/dist/workflows/ir/plan-hash.js +40 -5
  472. package/dist/workflows/ir/schema.js +537 -1
  473. package/dist/workflows/parser.js +878 -306
  474. package/dist/workflows/program/expressions.js +20 -208
  475. package/dist/workflows/program/schema.js +7 -10
  476. package/dist/workflows/renderer.js +99 -121
  477. package/dist/workflows/resource-limits.js +22 -0
  478. package/dist/workflows/runtime/checkin.js +1 -1
  479. package/dist/workflows/runtime/plan-classifier.js +131 -0
  480. package/dist/workflows/runtime/runs.js +200 -113
  481. package/dist/workflows/runtime/unit-checkin.js +1 -1
  482. package/dist/workflows/runtime/unit-phases.js +20 -0
  483. package/dist/workflows/runtime/workflow-asset-loader.js +235 -97
  484. package/dist/workflows/schema.js +1 -11
  485. package/dist/workflows/validate-summary.js +4 -26
  486. package/dist/workflows/validator.js +52 -30
  487. package/docs/README.md +42 -78
  488. package/docs/migration/README.md +8 -0
  489. package/docs/migration/release-notes/0.6.0.md +1 -1
  490. package/docs/migration/release-notes/0.7.0.md +9 -8
  491. package/docs/migration/release-notes/0.9.0.md +158 -14
  492. package/docs/migration/v0.7-to-v0.8.md +46 -47
  493. package/docs/migration/v0.8-to-v0.9.md +844 -0
  494. package/docs/reference/README.md +12 -0
  495. package/docs/reference/data-and-telemetry.md +333 -0
  496. package/package.json +21 -17
  497. package/schemas/akm-asset-envelope.json +93 -0
  498. package/schemas/akm-config.json +4636 -0
  499. package/schemas/akm-task.json +87 -0
  500. package/{dist/schemas → schemas}/akm-workflow.json +127 -82
  501. package/dist/akm-migrate-storage +0 -38
  502. package/dist/assets/help/help-accept.md +0 -12
  503. package/dist/assets/help/help-improve.md +0 -84
  504. package/dist/assets/help/help-proposals.md +0 -17
  505. package/dist/assets/help/help-propose.md +0 -17
  506. package/dist/assets/help/help-reject.md +0 -11
  507. package/dist/assets/profiles/frequent.json +0 -13
  508. package/dist/assets/profiles/recombine-only.json +0 -21
  509. package/dist/assets/profiles/reflect-distill.json +0 -30
  510. package/dist/assets/profiles/synthesize.json +0 -15
  511. package/dist/assets/prompts/procedural-system.md +0 -44
  512. package/dist/assets/prompts/recombine-system.md +0 -40
  513. package/dist/assets/prompts/staleness-detect-system.md +0 -6
  514. package/dist/assets/tasks/core/backup.yml +0 -4
  515. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  516. package/dist/assets/templates/html/default.html +0 -78
  517. package/dist/assets/templates/html/vendor/echarts.min.js +0 -45
  518. package/dist/assets/wiki/index-template.md +0 -12
  519. package/dist/assets/wiki/ingest-workflow-template.md +0 -83
  520. package/dist/assets/wiki/log-template.md +0 -8
  521. package/dist/assets/wiki/schema-template.md +0 -61
  522. package/dist/cli/config-migrate.js +0 -150
  523. package/dist/cli/config-validate.js +0 -39
  524. package/dist/commands/graph/graph-cli.js +0 -124
  525. package/dist/commands/graph/graph.js +0 -487
  526. package/dist/commands/improve/calibration.js +0 -161
  527. package/dist/commands/improve/dedup.js +0 -482
  528. package/dist/commands/improve/extract-watch.js +0 -140
  529. package/dist/commands/improve/hot-probation.js +0 -45
  530. package/dist/commands/improve/improve-auto-accept.js +0 -276
  531. package/dist/commands/improve/improve-profiles.js +0 -168
  532. package/dist/commands/improve/procedural.js +0 -398
  533. package/dist/commands/improve/recombine.js +0 -818
  534. package/dist/commands/improve/schema-similarity-gate.js +0 -168
  535. package/dist/commands/lint/agent-linter.js +0 -44
  536. package/dist/commands/lint/command-linter.js +0 -44
  537. package/dist/commands/lint/default-linter.js +0 -16
  538. package/dist/commands/lint/fact-linter.js +0 -39
  539. package/dist/commands/lint/knowledge-linter.js +0 -16
  540. package/dist/commands/lint/memory-linter.js +0 -61
  541. package/dist/commands/lint/registry.js +0 -41
  542. package/dist/commands/lint/skill-linter.js +0 -45
  543. package/dist/commands/lint/task-linter.js +0 -50
  544. package/dist/commands/lint/workflow-linter.js +0 -81
  545. package/dist/commands/proposal/legacy-import.js +0 -115
  546. package/dist/commands/sources/history.js +0 -196
  547. package/dist/commands/tasks/default-tasks.js +0 -186
  548. package/dist/commands/wiki-cli.js +0 -292
  549. package/dist/core/asset/asset-registry.js +0 -76
  550. package/dist/core/asset/asset-spec.js +0 -316
  551. package/dist/core/config/config-migration.js +0 -602
  552. package/dist/core/deep-merge.js +0 -38
  553. package/dist/core/eval/rank-metrics.js +0 -113
  554. package/dist/core/ripgrep/install.js +0 -163
  555. package/dist/core/ripgrep/resolve.js +0 -81
  556. package/dist/indexer/db/db.js +0 -1414
  557. package/dist/indexer/manifest.js +0 -170
  558. package/dist/indexer/passes/metadata-contributors.js +0 -31
  559. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -94
  560. package/dist/integrations/harnesses/opencode-sdk/index.js +0 -25
  561. package/dist/llm/call-ai.js +0 -62
  562. package/dist/llm/memory-infer-impl.js +0 -138
  563. package/dist/output/shapes/distill.js +0 -14
  564. package/dist/output/shapes/history.js +0 -11
  565. package/dist/output/text/distill.js +0 -6
  566. package/dist/output/text/enable-disable.js +0 -8
  567. package/dist/output/text/history.js +0 -6
  568. package/dist/output/text/wiki.js +0 -16
  569. package/dist/registry/build-index.js +0 -386
  570. package/dist/schemas/akm-config.json +0 -14225
  571. package/dist/scripts/migrate-storage.js +0 -13169
  572. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +0 -10169
  573. package/dist/scripts/migrations/v16-to-v17.js +0 -141
  574. package/dist/setup/legacy-config.js +0 -106
  575. package/dist/storage/repositories/consolidation-repository.js +0 -38
  576. package/dist/storage/repositories/recombine-repository.js +0 -213
  577. package/dist/wiki/wiki-templates.js +0 -15
  578. package/dist/wiki/wiki.js +0 -1012
  579. package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
  580. package/dist/workflows/db.js +0 -350
  581. package/dist/workflows/exec/watch.js +0 -116
  582. package/dist/workflows/program/parser.js +0 -760
  583. package/dist/workflows/program/project.js +0 -105
  584. package/docs/data-and-telemetry.md +0 -227
  585. package/docs/migration/release-notes/0.9.0-beta.60.md +0 -19
  586. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/registry.js +0 -0
  587. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/youtube.js +0 -0
@@ -4,6 +4,15 @@
4
4
  /**
5
5
  * Zod schema for AkmConfig — the single source of truth for the on-disk shape.
6
6
  *
7
+ * ASSEMBLY BARREL: the individual section schemas live under `./schema/*` (one
8
+ * cohesive module per config area — engines, embedding, improve, sources/
9
+ * bundles, index, search, output, feedback, workflow, setup, plus shared
10
+ * `primitives`). This file assembles them into the top-level {@link AkmConfigShape}
11
+ * / {@link AkmConfigSchema}, owns the cross-field {@link AkmConfigSchema.superRefine},
12
+ * and RE-EXPORTS every section schema so all existing `./config-schema` imports
13
+ * keep working unchanged. The decomposition is a pure structural refactor — the
14
+ * generated `schemas/akm-config.json` is byte-identical.
15
+ *
7
16
  * Two responsibilities:
8
17
  * 1. **Validate + transform** the raw JSON-parsed config object into the runtime
9
18
  * `AkmConfig` shape consumed by the rest of the codebase. Replaces the
@@ -34,912 +43,281 @@
34
43
  * enforced at save time via `superRefine` on the top-level schema.
35
44
  */
36
45
  import { z } from "zod";
37
- import { VALID_HARNESS_IDS } from "./config-types.js";
38
- // ── Reusable atomic schemas ─────────────────────────────────────────────────
39
- /** Positive integer (used for tokens, timeouts, batch sizes). */
40
- const positiveInt = z.number().int().positive();
41
- /** Non-negative finite number (used for scores, weights, days). */
42
- const nonNegativeNumber = z.number().finite().min(0);
43
- /** Non-empty string (rejects "" and whitespace-only). */
44
- const nonEmptyString = z
45
- .string()
46
- .min(1)
47
- .refine((v) => v.trim().length > 0, { message: "expected a non-empty string" });
48
- /** HTTP(S) URL string. */
49
- const httpUrl = z.string().refine((v) => v.startsWith("http://") || v.startsWith("https://"), {
50
- message: "endpoint must start with http:// or https://",
51
- });
52
- // ── Feedback failure modes (F-3 / #384) ─────────────────────────────────────
53
- /**
54
- * Curated taxonomy of failure modes for negative feedback.
55
- *
56
- * Structured failure modes enable aggregation across feedback events so the
57
- * distill pipeline can detect that "5 assets failed for the same reason" and
58
- * act on it — free-text strings about the same issue are not aggregatable.
59
- */
60
- export const FEEDBACK_FAILURE_MODES = [
61
- "incorrect", // Factually wrong or logically flawed content
62
- "outdated", // Correct at some point but now stale
63
- "dangerous", // Could cause harm if followed (security, safety)
64
- "incomplete", // Missing key steps, context, or caveats
65
- "redundant", // Duplicates another asset without adding value
66
- ];
67
- // ── Connection configs (LLM / embedding) ────────────────────────────────────
68
- const LlmCapabilitiesSchema = z
46
+ import { BUILTIN_IMPROVE_STRATEGY_NAMES, IMPROVE_PROCESS_ENGINE_CAPABILITIES } from "./engine-semantics.js";
47
+ import { EmbeddingConnectionConfigSchema } from "./schema/embedding.js";
48
+ import { EnginesSchema } from "./schema/engines.js";
49
+ import { ExperimentalConfigSchema } from "./schema/experimental.js";
50
+ import { FeedbackConfigSchema } from "./schema/feedback.js";
51
+ import { ImproveConfigSchema } from "./schema/improve.js";
52
+ import { IndexConfigSchema } from "./schema/index-config.js";
53
+ import { OutputConfigSchema } from "./schema/output.js";
54
+ import { CURRENT_CONFIG_VERSION, engineName, GlobalModelAliasesSchema, nonEmptyString, nonNegativeNumber, } from "./schema/primitives.js";
55
+ import { SearchConfigSchema } from "./schema/search.js";
56
+ import { SetupConfigSchema } from "./schema/setup.js";
57
+ import { BundlesConfigSchema, RegistryConfigEntrySchema } from "./schema/sources-bundles.js";
58
+ import { WorkflowConfigSchema } from "./schema/workflow.js";
59
+ // ── Section re-exports (keep every `./config-schema` import path working) ────
60
+ export { EmbeddingConnectionConfigSchema } from "./schema/embedding.js";
61
+ export { EngineConfigSchema, EnginesSchema, LlmConnectionConfigSchema, LlmProfileConfigSchema } from "./schema/engines.js";
62
+ export { ExperimentalConfigSchema } from "./schema/experimental.js";
63
+ export { FEEDBACK_FAILURE_MODES, FeedbackConfigSchema } from "./schema/feedback.js";
64
+ export { ImproveConfigSchema } from "./schema/improve.js";
65
+ export { ConsolidateProcessConfigSchema, DistillProcessConfigSchema, ExtractProcessConfigSchema, GraphExtractionProcessConfigSchema, ImproveProcessConfigSchema, ImproveProfileConfigSchema, MemoryInferenceProcessConfigSchema, ProactiveMaintenanceProcessConfigSchema, ReflectProcessConfigSchema, TriageProcessConfigSchema, ValidationProcessConfigSchema, } from "./schema/improve-processes.js";
66
+ export { IndexConfigSchema, IndexPassConfigSchema } from "./schema/index-config.js";
67
+ export { OutputConfigSchema } from "./schema/output.js";
68
+ export { CURRENT_CONFIG_VERSION, LlmInvocationOverridesSchema } from "./schema/primitives.js";
69
+ export { SearchConfigSchema } from "./schema/search.js";
70
+ export { SetupConfigSchema } from "./schema/setup.js";
71
+ export { BundleConfigEntrySchema, BundlesConfigSchema, RegistryConfigEntrySchema, SourceConfigEntrySchema, } from "./schema/sources-bundles.js";
72
+ export { WorkflowConfigSchema } from "./schema/workflow.js";
73
+ // ── Defaults ───────────────────────────────────────────────────────────────
74
+ export const DefaultsSchema = z
69
75
  .object({
70
- structuredOutput: z.boolean().optional(),
76
+ engine: engineName.optional(),
77
+ llmEngine: engineName.optional(),
78
+ improveStrategy: engineName.optional(),
71
79
  })
72
80
  .passthrough();
81
+ // ── Top-level AkmConfig ────────────────────────────────────────────────────
73
82
  /**
74
- * Connection config used for both top-level `llm` (after migration) and
75
- * `profiles.llm[*]`. `model` is required at schema level — partial entries
76
- * created by `akm config set llm.endpoint <url>` (where model is left absent)
77
- * are normalized to `model: ""` *before* Zod sees them by the load-time
78
- * pre-Zod migrator hook, so this strict shape gates CLI writes without
79
- * breaking legacy load-time partial configs.
83
+ * Base object schema used both as the top-level shape and as the source of
84
+ * truth for {@link listTopLevelConfigKeys}. {@link AkmConfigSchema} wraps this
85
+ * with cross-field refinements (`.superRefine()`).
86
+ *
87
+ * All fields validate loudly — typos and shape errors throw at load time. The
88
+ * legacy parser's warn-and-drop tolerance was a frequent source of silent
89
+ * configuration loss; the migration module ({@link migrateConfigShape}) handles
90
+ * one-time 0.7→0.8 input transforms before the schema sees the value.
80
91
  */
81
- export const LlmConnectionConfigSchema = z
82
- .object({
83
- provider: z.string().optional(),
84
- endpoint: z.string(),
85
- model: z.string(),
86
- apiKey: z.string().optional(),
87
- temperature: z.number().finite().optional(),
88
- maxTokens: positiveInt.optional(),
89
- timeoutMs: positiveInt.optional(),
90
- concurrency: positiveInt.optional(),
91
- capabilities: LlmCapabilitiesSchema.optional(),
92
- extraParams: z.record(z.unknown()).optional(),
93
- contextLength: positiveInt.optional(),
94
- judgeModel: z.string().min(1).optional(),
95
- enableThinking: z.boolean().optional(),
96
- })
97
- .passthrough();
98
- export const LlmProfileConfigSchema = LlmConnectionConfigSchema.extend({
99
- supportsJsonSchema: z.boolean().optional(),
100
- }).passthrough();
101
- const EmbeddingOllamaOptionsSchema = z
102
- .object({
103
- num_ctx: positiveInt.optional(),
104
- })
105
- .passthrough();
92
+ export const AkmConfigShape = {
93
+ configVersion: z.literal(CURRENT_CONFIG_VERSION),
94
+ engines: EnginesSchema.optional(),
95
+ defaults: DefaultsSchema.optional(),
96
+ // Global model-alias tiers: alias → platform → exact model string, with a
97
+ // reserved `"*"` platform key as fallback. Lets workflows/callers name a
98
+ // semantic tier ("fast", "deep") that resolves per-harness at dispatch
99
+ // time. Values are literal model strings, never other aliases (one
100
+ // resolution level). Platform keys match the platform string a command
101
+ // builder resolves against ("claude", "opencode", "opencode-sdk", or a
102
+ // custom profile's name for the default builder) — unknown keys are inert.
103
+ // Precedence: profile modelAliases > this table > built-in aliases.
104
+ modelAliases: GlobalModelAliasesSchema.optional(),
105
+ semanticSearchMode: z.enum(["off", "auto"]).default("off"),
106
+ embedding: EmbeddingConnectionConfigSchema.optional(),
107
+ index: IndexConfigSchema.optional(),
108
+ registries: z.array(RegistryConfigEntrySchema).optional(),
109
+ // 0.9.0 config-shape cutover (spec §10.1 / D-R5). `bundles` + `defaultBundle`
110
+ // are the ONLY source shape — the retired `stashDir`/`sources[]`/`installed[]`
111
+ // trio is hard-rejected at load (see the top-level superRefine). The migrator
112
+ // ({@link migrateConfigSourcesToBundles}) converts a pre-cutover config to this
113
+ // shape before validation. `defaultBundle` names the primary bundle (spec
114
+ // §11.1 short-ref resolution / D-R4).
115
+ bundles: BundlesConfigSchema.optional(),
116
+ defaultBundle: nonEmptyString.optional(),
117
+ output: OutputConfigSchema.optional(),
118
+ defaultWriteTarget: nonEmptyString.optional(),
119
+ search: SearchConfigSchema.optional(),
120
+ feedback: FeedbackConfigSchema.optional(),
121
+ archiveRetentionDays: nonNegativeNumber.optional(),
122
+ improve: ImproveConfigSchema.optional(),
123
+ workflow: WorkflowConfigSchema.optional(),
124
+ setup: SetupConfigSchema.optional(),
125
+ // D8 — explicit opt-ins for behaviour outside the stability contract. Every
126
+ // key defaults to OFF; see `src/core/config/experimental.ts` for the readers.
127
+ experimental: ExperimentalConfigSchema.optional(),
128
+ };
129
+ export const AkmConfigBaseSchema = z.object(AkmConfigShape).passthrough();
106
130
  /**
107
- * Embedding connection config. Both `endpoint` and `model` are optional:
108
- * - Remote: provide `endpoint` (http/https URL) + `model`.
109
- * - Local-only: omit `endpoint`/`model`; set `localModel` (or fall back to
110
- * {@link DEFAULT_LOCAL_MODEL}).
111
- *
112
- * Consumers route via `hasRemoteEndpoint()` which checks for an http(s)
113
- * endpoint — absent fields take the local path naturally, no sentinels needed.
131
+ * Per-key overrides for the retired `stashDir`/`sources`/`installed` source-
132
+ * shape rejection below. Keys without an entry fall back to the shared
133
+ * "retired pre-cutover source shape" message, which already names the
134
+ * replacement (bundles) and the migration command.
114
135
  */
115
- export const EmbeddingConnectionConfigSchema = z
116
- .object({
117
- provider: z.string().optional(),
118
- endpoint: z.string().optional(),
119
- model: z.string().optional(),
120
- apiKey: z.string().optional(),
121
- dimension: positiveInt.optional(),
122
- localModel: z.string().min(1).optional(),
123
- maxTokens: positiveInt.optional(),
124
- batchSize: positiveInt.optional(),
125
- chunkSize: positiveInt.optional(),
126
- contextLength: positiveInt.optional(),
127
- ollamaOptions: EmbeddingOllamaOptionsSchema.optional(),
128
- })
129
- .passthrough();
130
- // ── Agent profiles ──────────────────────────────────────────────────────────
131
- // Derives from the canonical VALID_HARNESS_IDS (#565) so the Zod gate cannot
132
- // drift from the TS union / parse check / setup detection.
133
- const AgentPlatformSchema = z.enum(VALID_HARNESS_IDS);
134
- export const AgentProfileConfigSchema = z
135
- .object({
136
- platform: AgentPlatformSchema,
137
- bin: z.string().min(1).optional(),
138
- args: z.array(z.string()).optional(),
139
- workspace: z.string().min(1).optional(),
140
- model: z.string().min(1).optional(),
141
- // Per-call timeout (ms) used when a command does not pass an explicit
142
- // timeout (e.g. `akm wiki ingest` without `--timeout-ms`). null = no
143
- // timeout. Honored by resolveAgentProfile (integrations/agent/config.ts).
144
- timeoutMs: z.union([positiveInt, z.null()]).optional(),
145
- // Per-profile model aliases: lowercase alias → exact model string for
146
- // this profile's CLI. Highest-precedence tier in resolveModel
147
- // (integrations/agent/model-aliases.ts) — beats the config-root
148
- // `modelAliases` table and the built-in opus/sonnet/haiku entries.
149
- modelAliases: z.record(z.string().min(1), z.string().min(1)).optional(),
150
- })
151
- .passthrough();
152
- // ── Improve profile / process ──────────────────────────────────────────────
153
- export const ImproveProcessConfigSchema = z
154
- .object({
155
- enabled: z.boolean().optional(),
156
- mode: z.enum(["llm", "agent", "sdk"]).optional(),
157
- profile: z.string().min(1).optional(),
158
- timeoutMs: z.union([positiveInt, z.null()]).optional(),
159
- allowedTypes: z.array(z.string().min(1)).optional(),
160
- // Consolidate process: minimum eligible-memory pool size below which the
161
- // consolidation pass skips entirely (emits `pool_below_min_size`). 0 disables
162
- // the guard. Only meaningful on the `consolidate` process. Default 500.
163
- minPoolSize: z.number().int().min(0).optional(),
164
- // Consolidate process: deterministic near-duplicate dedup pre-pass (#617).
165
- // A cheap, no-LLM fast path that collapses obvious duplicates (`.derived`
166
- // origin pairs + content twins) before the LLM consolidation. Default OFF
167
- // — when absent the consolidate pass behaves byte-identically to today.
168
- // `cosineThreshold` is a strict floor in [0, 1] (default 0.97) for the
169
- // optional embedding-similarity match; exact normalized content-hash
170
- // equality always collapses regardless of the threshold. Only meaningful
171
- // on the `consolidate` process.
172
- dedup: z
173
- .object({
174
- enabled: z.boolean().optional(),
175
- cosineThreshold: z.number().min(0).max(1).optional(),
176
- // WS-3a: maximum pool size for the O(n²) cosine-similarity twin compare.
177
- // Only the first `cosineCandidateLimit` memories are cosine-compared;
178
- // exact-hash matches still run over the full pool. Default 500. Raise
179
- // with care — cost is O(n²).
180
- cosineCandidateLimit: z.number().int().positive().optional(),
181
- })
182
- .passthrough()
183
- .optional(),
184
- // Consolidate process: judged-state cache (#581). When enabled, a memory
185
- // whose current content hash equals its cached judged hash is SKIPPED from
186
- // the LLM pool (judged-unchanged → no re-judge), letting one run sweep the
187
- // whole corpus at O(changed/new) cost instead of narrowing to a recent
188
- // time-window slice. Default OFF — when absent the consolidate pass behaves
189
- // byte-identically to today (the incrementalSince path is unaffected). Only
190
- // meaningful on the `consolidate` process.
191
- judgedCache: z.object({ enabled: z.boolean().optional() }).passthrough().optional(),
192
- // Distill process: LLM-as-judge lesson quality gate. Default ON (R3);
193
- // fail-open — judge failure/timeout/parse errors pass through. Set
194
- // `enabled: false` on the distill process to opt out.
195
- qualityGate: z.object({ enabled: z.boolean().optional() }).passthrough().optional(),
196
- contradictionDetection: z.object({ enabled: z.boolean().optional() }).passthrough().optional(),
197
- // Extract process config (only meaningful for extract process)
198
- defaultSince: z.string().min(1).optional(),
199
- maxTotalChars: positiveInt.optional(),
200
- // Extract process: minimum raw session size (pre-filter inputCount) below
201
- // which the extract LLM call is skipped (#595/#596). 0 disables the gate.
202
- // Absent = default 10 (skip only truly empty sessions). Only meaningful
203
- // on the `extract` process.
204
- minContentChars: z.number().int().min(0).optional(),
205
- maxChunkSize: z.number().int().min(1).max(50).optional(),
206
- // Consolidate process: narrow candidate pool to memories modified within
207
- // this duration window plus their graph neighbours. Only meaningful on
208
- // the `consolidate` process. Absent = full-pool sweep.
209
- incrementalSince: z.string().optional(),
210
- // Consolidate process: hard cap on memories processed per pass.
211
- // Reflect/distill: max refs processed (same as profile-level `limit`).
212
- limit: positiveInt.optional(),
213
- // Consolidate process: graph neighbours per changed memory during
214
- // incremental consolidation. Default 5. Only meaningful with incrementalSince.
215
- neighborsPerChanged: z.number().int().min(1).optional(),
216
- // Distill process: skip distill entirely when reflect produced zero planned refs.
217
- requirePlannedRefs: z.boolean().optional(),
218
- // proactiveMaintenance process (Layer 2): staleness gate + rotation cooldown
219
- // in days (default 30). Only meaningful on `proactiveMaintenance`.
220
- dueDays: z.number().int().min(0).optional(),
221
- // proactiveMaintenance process: top-N bound per run (default 25). Alias for
222
- // `limit`; `maxPerRun` wins when both are set.
223
- maxPerRun: positiveInt.optional(),
224
- // graphExtraction process (#624 P2): when set, rank eligible files by
225
- // utility_scores DESC and process only the top-N per run (incremental
226
- // high-signal-first sweep). Unset = process all eligible (current
227
- // behavior). Only meaningful on `graphExtraction`.
228
- topN: positiveInt.optional(),
229
- // graphExtraction process: full-corpus scan. When true, graph extraction
230
- // runs on ALL stash files instead of only files touched by actionable refs
231
- // in the current run. Used by the `graph-refresh` built-in profile / a
232
- // scheduled weekly task. Only meaningful on `graphExtraction`.
233
- fullScan: z.boolean().optional(),
234
- // #626 — extract process: pre-LLM heuristic triage gate. When enabled, a
235
- // deterministic scorer decides BEFORE the extraction LLM call whether a
236
- // session carries enough signal to be worth extracting; low-signal sessions
237
- // are skipped at zero LLM cost. Default OFF. Only meaningful on `extract`.
238
- // `minScore` is the minimum total heuristic score to PASS (default 2).
239
- triage: z
240
- .object({
241
- enabled: z.boolean().optional(),
242
- minScore: z.number().min(0).optional(),
243
- })
244
- .passthrough()
245
- .optional(),
246
- // MemoryInference process: minimum pending memory count to run the pass.
247
- minPendingCount: z.number().int().min(0).optional(),
248
- // Extract process: minimum number of new (unseen, in-window) candidate
249
- // sessions below which the extract pass skips entirely (emits an
250
- // `improve_skipped` event with `reason: "below_min_new_sessions"`). 0
251
- // disables the guard. Only meaningful on the `extract` process. Default 0
252
- // (disabled) so existing behaviour is preserved; only opted-in profiles set it.
253
- minNewSessions: z.number().int().min(0).optional(),
254
- // Extract process: cap on NEW sessions processed (LLM-called) per run; the
255
- // rest roll to the next run (still unseen). 0 disables. Absent = default 25.
256
- maxSessionsPerRun: z.number().int().min(0).optional(),
257
- // #561 — index agent sessions as a searchable `session` asset (extract
258
- // process). Absent = on-when-an-LLM-is-available (fail-open when offline).
259
- // COST: when on, each processed session makes a SECOND LLM call (the session
260
- // summary) on top of the extraction call — i.e. ~2 LLM calls/session. Set to
261
- // false to halve per-session extract cost at the price of unsearchable
262
- // sessions. (Unchanged/skip sessions still cost zero — the content-hash
263
- // ledger gates both calls upstream.)
264
- indexSessions: z.boolean().optional(),
265
- // #561 — minimum session duration in minutes for session indexing. 0
266
- // disables the gate. Absent = default 5. Only meaningful on `extract`.
267
- minSessionDuration: z.number().min(0).optional(),
268
- // Consolidate process: fallback p90 wall-clock time per consolidation chunk
269
- // in seconds, used for cold-start budget estimation when no telemetry
270
- // history exists. The actual p90 is derived from observed run durations
271
- // once sufficient history accumulates; this value is only used on the very
272
- // first run. Default 30 s. Only meaningful on the `consolidate` process.
273
- p90ChunkSecondsDefault: z.number().finite().positive().optional(),
274
- // (WS-3b step 0a `homeostaticDemotion` was removed — R4. The key is
275
- // tolerated via passthrough if an old config still carries it; continuous
276
- // decay is now part of the always-applied salience recency term.)
277
- // WS-3b: Schema-similarity gate (step 0b). At intake, if a new candidate's
278
- // body embedding is within epsilon of an existing derived-layer lesson/knowledge
279
- // node, mark it schema-consistent and lower its priority. Default ON for
280
- // the `extract` process since R3 (fail-open; set `enabled: false` to opt out).
281
- // Only meaningful on the `consolidate` and `extract` processes.
282
- schemaSimilarity: z
283
- .object({
284
- enabled: z.boolean().optional(),
285
- // Epsilon: cosine similarity threshold above which a candidate is schema-consistent
286
- // (default 0.85 — looser than dedup's 0.97 since we want to catch conceptual overlap).
287
- epsilon: z.number().min(0).max(1).optional(),
288
- // Multiplicative factor applied to candidate confidence when schema-consistent.
289
- // Default 0.5 — halves the confidence so schema-consistent candidates are less likely
290
- // to pass the quality gate and create redundant stash entries.
291
- confidencePenalty: z.number().min(0).max(1).optional(),
292
- })
293
- .passthrough()
294
- .optional(),
295
- // WS-3b: Hot-probation intake buffer (step 0c, #604). New system-generated
296
- // extractions enter captureMode: hot-probation and spend ONE consolidation
297
- // cycle in probation. Dedup + quality second-pass runs before promotion.
298
- // Default OFF. Only meaningful on the `extract` process.
299
- hotProbation: z
300
- .object({
301
- enabled: z.boolean().optional(),
302
- })
303
- .passthrough()
304
- .optional(),
305
- // WS-3b: Anti-collapse guards (step 8). Prevents the consolidation pipeline
306
- // from collapsing too aggressively and losing diversity.
307
- // - maxGeneration: refuse to merge two assets both above this generation (default 2).
308
- // - lexicalDiversityCheck: low n-gram diversity ⇒ raise merge threshold.
309
- // - randomClusterFraction: occasional random (non-similar) cluster in pool (default 0.05).
310
- // - mergeInformationFloor: measure that merges keep provenance + specificity
311
- // (R5 §4.2; ADVISORY in v1 — counted, never refused).
312
- // - minSpecificityRetention: distinct-token retention floor for merges (default 0.6).
313
- // Default ON since R5 (opt out via enabled: false). Only meaningful on the
314
- // `consolidate` process.
315
- antiCollapse: z
316
- .object({
317
- enabled: z.boolean().optional(),
318
- maxGeneration: z.number().int().min(1).optional(),
319
- lexicalDiversityCheck: z.boolean().optional(),
320
- randomClusterFraction: z.number().min(0).max(1).optional(),
321
- mergeInformationFloor: z.boolean().optional(),
322
- minSpecificityRetention: z.number().min(0).max(1).optional(),
323
- })
324
- .passthrough()
325
- .optional(),
326
- // WS-3b: CLS (Complementary Learning System) interleaving (step 9).
327
- // distill/memoryInference prompts include embedding-retrieved existing adjacent
328
- // lessons/knowledge to prevent catastrophic interference with prior generalizations.
329
- // Default OFF. Only meaningful on `distill` and `memoryInference` processes.
330
- cls: z
331
- .object({
332
- enabled: z.boolean().optional(),
333
- // Number of adjacent lessons/knowledge to include in prompts (default 3).
334
- adjacentCount: z.number().int().min(1).optional(),
335
- })
336
- .passthrough()
337
- .optional(),
338
- // WS-3b: Distill→source fidelity check (step 10). After a distill proposal,
339
- // check it against its cited source memories; a contradiction flag forces
340
- // human review. Default OFF. Only meaningful on `distill` process.
341
- fidelityCheck: z
342
- .object({
343
- enabled: z.boolean().optional(),
344
- })
345
- .passthrough()
346
- .optional(),
347
- // #609 — recombine process: minimum related-memory cluster size before an
348
- // LLM generalization call. Default 3. Only meaningful on `recombine`.
349
- minClusterSize: z.number().int().min(2).optional(),
350
- // #609 — recombine process: hard cap on clusters processed per run (one
351
- // bounded LLM call each). Default 5. Only meaningful on `recombine`.
352
- maxClustersPerRun: positiveInt.optional(),
353
- // #632 — recombine process: max members a cluster may contain before it is
354
- // SKIPPED (drops bland over-broad buckets). When set, largest-first ranking
355
- // no longer starves tighter clusters. Default UNSET = no cap. Only
356
- // meaningful on `recombine`.
357
- maxClusterSize: positiveInt.optional(),
358
- // #632 — recombine process: tag values that must never form a tag cluster
359
- // (generic project-wide tags). Default UNSET/[]. Only meaningful on
360
- // `recombine`.
361
- excludeTags: z.array(z.string().min(1)).optional(),
362
- // #632 — recombine process: entity_norm values that must never form an
363
- // entity cluster (user counterpart to the built-in generic-entity filter).
364
- // Default UNSET/[]. Only meaningful on `recombine`.
365
- excludeEntities: z.array(z.string().min(1)).optional(),
366
- // #609 — recombine process: relatedness signal used to form clusters
367
- // (tags | graph | both). Clustering is by relatedness, never embedding
368
- // similarity. Default "both" (#632). Only meaningful on `recombine`.
369
- relatednessSource: z.enum(["tags", "graph", "both"]).optional(),
370
- // #609 — recombine process: consecutive re-inductions required before a
371
- // hypothesis is promoted to a lesson. Default 2. Only meaningful on
372
- // `recombine`.
373
- confirmThreshold: z.number().int().min(1).optional(),
374
- // #615 — procedural process: minimum number of distinct assets sharing the
375
- // same successful normalized ordered-action sequence before it is compiled
376
- // into a workflow proposal. Default 3. Only meaningful on `procedural`.
377
- minRecurrence: z.number().int().min(2).optional(),
378
- // #615 — procedural process: hard cap on workflow proposals emitted per run
379
- // (one bounded LLM call each). Default 3. Only meaningful on `procedural`.
380
- maxProposalsPerRun: positiveInt.optional(),
381
- // #615 — procedural process: asset type a compiled sequence is emitted as.
382
- // Reserved; v1 always emits "workflow". Only meaningful on `procedural`.
383
- emitAs: z.enum(["workflow", "skill"]).optional(),
384
- // #639 — semantic value-floor filter for the `reflect` process. When
385
- // enabled, proposals classified as "low-value" by the deterministic noise
386
- // gate are deferred. DEFAULT OFF (absent / { enabled: false } = pre-#639
387
- // byte-identical behaviour). Only meaningful on the `reflect` process.
388
- lowValueFilter: z.object({ enabled: z.boolean().optional() }).passthrough().optional(),
389
- // #641 — procedural-aware floor for the `extract` process triage gate.
390
- // When true, a session must have markers>=1 OR editCommit>=0.5 to pass, even
391
- // if score>=minScore. DEFAULT OFF (absent/false = pre-#641 byte-identical).
392
- // Only meaningful on the `extract` process when triage is also enabled.
393
- proceduralAwareFloor: z.boolean().optional(),
394
- // Triage process config (only meaningful for the `triage` process)
395
- applyMode: z.enum(["queue", "promote"]).optional(),
396
- policy: z.string().min(1).optional(),
397
- maxAcceptsPerRun: positiveInt.optional(),
398
- maxDiffLines: positiveInt.optional(),
399
- rejectEmpty: z.boolean().optional(),
400
- judgment: z
401
- .object({
402
- mode: z.enum(["llm", "agent", "sdk"]).optional(),
403
- profile: z.string().min(1).optional(),
404
- timeoutMs: z.union([positiveInt, z.null()]).optional(),
405
- })
406
- .passthrough()
407
- .optional(),
408
- })
409
- .passthrough();
410
- const ImproveProfileProcessesSchema = z
411
- .object({
412
- reflect: ImproveProcessConfigSchema.optional(),
413
- distill: ImproveProcessConfigSchema.optional(),
414
- consolidate: ImproveProcessConfigSchema.optional(),
415
- memoryInference: ImproveProcessConfigSchema.optional(),
416
- graphExtraction: ImproveProcessConfigSchema.optional(),
417
- extract: ImproveProcessConfigSchema.optional(),
418
- validation: ImproveProcessConfigSchema.optional(),
419
- triage: ImproveProcessConfigSchema.optional(),
420
- proactiveMaintenance: ImproveProcessConfigSchema.optional(),
421
- recombine: ImproveProcessConfigSchema.optional(),
422
- procedural: ImproveProcessConfigSchema.optional(),
423
- })
424
- .passthrough()
425
- .superRefine((val, ctx) => {
426
- // 0.8.0 removed the duplicated `feedbackDistillation` process key — it was
427
- // a thin wrapper around `processes.distill.enabled`. Keep the migration
428
- // hint so a stale config gets an actionable message rather than silently
429
- // doing nothing. All OTHER unknown process keys are tolerated (passthrough)
430
- // — see the unknown-key policy in this file's header. Hard-rejecting them
431
- // turned benign cross-version skew into INVALID_CONFIG_FILE failures.
432
- if ("feedbackDistillation" in val) {
433
- ctx.addIssue({
434
- code: z.ZodIssueCode.custom,
435
- message: "feedbackDistillation was removed in 0.8.0 — use processes.distill.enabled instead. " +
436
- "It now controls both the orchestration gate and the LLM-call gate.",
437
- });
136
+ const RETIRED_SOURCE_SHAPE_KEY_MESSAGES = {
137
+ stashDir: "stashDir is retired in 0.9; the stash path now comes from `bundles`. Run `akm-migrate apply` to convert a pre-0.9 config, or see `akm config path --all` / `akm info`.",
138
+ };
139
+ export const AkmConfigSchema = AkmConfigBaseSchema.superRefine((config, ctx) => {
140
+ const raw = config;
141
+ for (const key of ["profiles", "llm", "agent", "features", "stashes"]) {
142
+ if (key in raw) {
143
+ ctx.addIssue({
144
+ code: z.ZodIssueCode.custom,
145
+ path: [key],
146
+ message: `${key} is retired in 0.9; configure engines and improve.strategies instead`,
147
+ });
148
+ }
438
149
  }
439
- });
440
- export const ImproveProfileConfigSchema = z
441
- .object({
442
- description: z.string().min(1).optional(),
443
- processes: ImproveProfileProcessesSchema.optional(),
444
- autoAccept: nonNegativeNumber.optional(),
445
- limit: positiveInt.optional(),
446
- // #616 — bounded multi-cycle phasing. Number of prep->loop->post-loop
447
- // cycles per run. positiveInt forbids 0/negative. DEFAULT 1 => byte-identical
448
- // single-pass behavior.
449
- maxCycles: positiveInt.optional(),
450
- // #614 — symmetric valence weighting in the eligibility sort. When true,
451
- // the attention term becomes |valence| MAGNITUDE so BOTH strong positive
452
- // and strong negative feedback drive attention (utility stays dominant) and
453
- // strong-signed assets are routed to a fix/reinforce lane. DEFAULT OFF —
454
- // false/absent preserves the legacy negative-only ranking byte-for-byte.
455
- symmetricValence: z.boolean().optional(),
456
- sync: z
457
- .object({
458
- enabled: z.boolean().optional(),
459
- push: z.boolean().optional(),
460
- message: z.string().min(1).optional(),
461
- })
462
- .passthrough()
463
- .optional(),
464
- })
465
- .passthrough();
466
- // ── Profiles / defaults ────────────────────────────────────────────────────
467
- export const ProfilesSchema = z
468
- .object({
469
- llm: z.record(z.string(), LlmProfileConfigSchema).optional(),
470
- agent: z.record(z.string(), AgentProfileConfigSchema).optional(),
471
- improve: z.record(z.string(), ImproveProfileConfigSchema).optional(),
472
- })
473
- .passthrough();
474
- export const DefaultsSchema = z
475
- .object({
476
- llm: z.string().min(1).optional(),
477
- agent: z.string().min(1).optional(),
478
- improve: z.string().min(1).optional(),
479
- })
480
- .passthrough();
481
- // ── Sources / registries / installed ────────────────────────────────────────
482
- const SourceConfigEntryOptionsSchema = z
483
- .object({
484
- /**
485
- * @deprecated 0.9.0 (issue #507). Retired per-asset push-on-commit. Kept so
486
- * old configs still parse; its intent maps onto the batch push gate and
487
- * encountering it emits a one-time deprecation warning.
488
- */
489
- pushOnCommit: z.boolean().optional(),
490
- })
491
- .passthrough();
492
- export const SourceConfigEntrySchema = z
493
- .object({
494
- type: nonEmptyString,
495
- path: z.string().min(1).optional(),
496
- url: z.string().min(1).optional(),
497
- name: z.string().min(1).optional(),
498
- enabled: z.boolean().optional(),
499
- writable: z.boolean().optional(),
500
- primary: z.boolean().optional(),
501
- options: SourceConfigEntryOptionsSchema.optional(),
502
- wikiName: z.string().min(1).optional(),
503
- })
504
- .passthrough()
505
- .superRefine((entry, ctx) => {
506
- if (entry.writable === true && (entry.type === "website" || entry.type === "npm")) {
150
+ // `bindings` (spec §10.1) is Tier B — never emitted, never accepted. The
151
+ // top-level schema is `.passthrough()`, so without this it would round-trip
152
+ // silently; reject it loudly so a stray/hand-written bindings block is caught.
153
+ if ("bindings" in raw) {
507
154
  ctx.addIssue({
508
155
  code: z.ZodIssueCode.custom,
509
- message: `writable: true is only supported on filesystem and git sources (got "${entry.type}"` +
510
- (entry.name ? ` on source "${entry.name}"` : "") +
511
- ").",
156
+ path: ["bindings"],
157
+ message: "bindings is not supported in 0.9.0 (Tier B); it is neither emitted nor accepted",
512
158
  });
513
159
  }
514
- });
515
- export const RegistryConfigEntrySchema = z
516
- .object({
517
- url: httpUrl,
518
- name: z.string().min(1).optional(),
519
- enabled: z.boolean().optional(),
520
- provider: z.string().min(1).optional(),
521
- options: z.record(z.unknown()).optional(),
522
- })
523
- .passthrough();
524
- const KitSourceSchema = z.enum(["filesystem", "git", "npm", "github", "website", "local"]);
525
- export const InstalledStashEntrySchema = z
526
- .object({
527
- id: nonEmptyString,
528
- source: KitSourceSchema,
529
- ref: nonEmptyString,
530
- artifactUrl: nonEmptyString,
531
- stashRoot: nonEmptyString,
532
- cacheDir: nonEmptyString,
533
- installedAt: nonEmptyString,
534
- writable: z.boolean().optional(),
535
- resolvedVersion: z.string().min(1).optional(),
536
- resolvedRevision: z.string().min(1).optional(),
537
- wikiName: z.string().min(1).optional(),
538
- })
539
- .passthrough()
540
- .superRefine((entry, ctx) => {
541
- if (entry.writable === true && entry.source !== "git" && entry.source !== "filesystem") {
160
+ // 0.9.0 config-shape cutover (spec §10.1): the retired `stashDir`/`sources`/
161
+ // `installed` trio is HARD-REJECTED at load whenever present — `bundles` +
162
+ // `defaultBundle` fully supersede it. A pre-cutover config never loads through
163
+ // this validated path; the migrator ({@link migrateConfigSourcesToBundles})
164
+ // normalizes old→bundles BEFORE validation, and `inspectConfig` classifies an
165
+ // old-shape-alone config "old" (migration-eligible) via that same normalize.
166
+ for (const key of ["stashDir", "sources", "installed"]) {
167
+ if (key in raw && raw[key] !== undefined) {
168
+ ctx.addIssue({
169
+ code: z.ZodIssueCode.custom,
170
+ path: [key],
171
+ message: RETIRED_SOURCE_SHAPE_KEY_MESSAGES[key] ??
172
+ `${key} is the retired pre-cutover source shape; run \`akm-migrate apply\` to convert it to bundles`,
173
+ });
174
+ }
175
+ }
176
+ if ("writable" in raw) {
542
177
  ctx.addIssue({
543
178
  code: z.ZodIssueCode.custom,
544
- message: `writable: true is only supported on filesystem and git sources (got "${entry.source}" on installed entry "${entry.id}").`,
179
+ path: ["writable"],
180
+ message: "top-level writable is not supported; configure bundles.<id>.writable instead",
545
181
  });
546
182
  }
547
- });
548
- // ── Output ──────────────────────────────────────────────────────────────────
549
- export const OutputConfigSchema = z
550
- .object({
551
- format: z.enum(["json", "yaml", "text"]).optional(),
552
- detail: z.enum(["brief", "normal", "full"]).optional(),
553
- })
554
- .passthrough();
555
- // ── Search ──────────────────────────────────────────────────────────────────
556
- const SearchGraphBoostSchema = z
557
- .object({
558
- directBoostPerEntity: nonNegativeNumber.optional(),
559
- directBoostCap: nonNegativeNumber.optional(),
560
- hopBoostPerEntity: nonNegativeNumber.optional(),
561
- hopBoostCap: nonNegativeNumber.optional(),
562
- /** Hard-capped at 3; values > 3 hard-error so users see the typo. */
563
- maxHops: positiveInt.max(3).optional(),
564
- confidenceMode: z.enum(["off", "blend", "multiply"]).default("blend").optional(),
565
- /** Range [0, 1]; values > 1 hard-error (no silent clamp). */
566
- confidenceWeight: z.number().finite().min(0).max(1).default(0.2).optional(),
567
- })
568
- .passthrough();
569
- export const SearchConfigSchema = z
570
- .object({
571
- minScore: nonNegativeNumber.optional(),
572
- defaultExcludeTypes: z.array(nonEmptyString).optional(),
573
- graphBoost: SearchGraphBoostSchema.optional(),
574
- })
575
- .passthrough();
576
- // ── Feedback ────────────────────────────────────────────────────────────────
577
- export const FeedbackConfigSchema = z
578
- .object({
579
- requireReason: z.boolean().optional(),
580
- allowedFailureModes: z.array(nonEmptyString).optional(),
581
- })
582
- .passthrough();
583
- // ── Improve top-level (utility decay, event retention) ─────────────────────
584
- const ImproveUtilityDecaySchema = z
585
- .object({
586
- halfLifeDays: z.number().finite().min(0.1).optional(),
587
- feedbackStabilityBoost: z.number().finite().min(1).optional(),
588
- })
589
- .passthrough();
590
- // #612 / WS-4 — auto-accept gate calibration + bounded, opt-in per-phase
591
- // threshold auto-tune. DEFAULT OFF: when absent (or `autoTune: false`) no
592
- // tuning occurs, so the gate behaves byte-identically to today.
593
- // WS-4 adds: per-phase persistence (state.db) + auto-tune ceiling default 85.
594
- const ImproveCalibrationSchema = z
595
- .object({
596
- /** Master switch for the bounded threshold auto-tune. Default false (parity). */
597
- autoTune: z.boolean().optional(),
598
- /** Lower bound (0-100) the tuned threshold may never drop below. */
599
- minThreshold: z.number().int().min(0).max(100).optional(),
600
- /**
601
- * Upper bound (0-100) the tuned threshold may never rise above.
602
- * WS-4 default: 85 (prevents gate converging to pure exploitation).
603
- */
604
- maxThreshold: z.number().int().min(0).max(100).optional(),
605
- /** Maximum adjustment magnitude (points) applied in one tune step. */
606
- maxStep: positiveInt.optional(),
607
- /** Minimum acted-on sample count required before any adjustment. */
608
- minSamples: nonNegativeNumber.optional(),
609
- /** Target realized accept rate in [0, 1]. Default 0.9. */
610
- targetAcceptRate: z.number().finite().min(0).max(1).optional(),
611
- })
612
- .passthrough();
613
- // WS-4 — exploration budget: a fixed fraction of proposals accepted per run
614
- // regardless of confidence. DEFAULT OFF.
615
- const ImproveExplorationSchema = z
616
- .object({
617
- /**
618
- * Enable the exploration budget lane. Default false (parity).
619
- * When true, a fraction of proposals are accepted regardless of confidence.
620
- */
621
- enabled: z.boolean().optional(),
622
- /**
623
- * Fraction of proposals per run to accept as exploration [0, 1].
624
- * Default 0.05 (5%). Clamped to [0, 1] at read time.
625
- */
626
- budgetFraction: z.number().finite().min(0).max(1).optional(),
627
- })
628
- .passthrough();
629
- const ImproveSalienceSchema = z
630
- .object({
631
- /**
632
- * WS-2 Part-V gate: enable the outcome-weight term in the salience projection.
633
- * Default false (parity — WS-1 weights w_e=0.30, w_r=0.70 until Part-V confirms
634
- * no regression). Set to true after running scripts/akm-eval + health report.
635
- */
636
- outcomeWeightEnabled: z.boolean().optional(),
637
- /**
638
- * Minimum encoding salience score [0, 1] for a zero-feedback asset to be
639
- * admitted to the high-salience improve lane (#608).
640
- * Default 0.75. Set to 1.0 to disable the lane entirely.
641
- */
642
- salienceThreshold: z.number().min(0).max(1).optional(),
643
- /**
644
- * Per-run additive replay budget (#610). Up to this many top-salience refs are
645
- * revisited even with no reactive signal and regardless of cooldown. Additive
646
- * on top of --limit. Default 0 = no replay.
647
- */
648
- replayBudget: z.number().int().min(0).optional(),
649
- })
650
- .passthrough();
651
- // R5 — longitudinal collapse/churn detector (observe-only in v1; deterministic,
652
- // fail-open, runs only on cycles where consolidate/recombine did work).
653
- // Default ON; opt out via `improve.collapseDetector.enabled: false`.
654
- // See docs/design/improve-collapse-churn-detector-design.md.
655
- const ImproveCollapseDetectorSchema = z
656
- .object({
657
- enabled: z.boolean().optional(),
658
- // Canary set size minted on first run (owner-approved 30–50 range; default 40).
659
- canaryCount: z.number().int().min(3).max(200).optional(),
660
- // Top-K cutoff for canary recall/nDCG (default 10).
661
- k: z.number().int().min(1).max(100).optional(),
662
- // Trend window in qualifying cycles (default 5).
663
- windowCycles: z.number().int().min(2).max(50).optional(),
664
- // Absolute mean-recall drop vs window median that fires collapse (default 0.15).
665
- recallDropThreshold: z.number().min(0).max(1).optional(),
666
- // distinct-content-ratio decline over the window that fires collapse (default 0.05).
667
- entropyDropThreshold: z.number().min(0).max(1).optional(),
668
- // Accepted-action volume over the window below which churn never fires (default 25).
669
- churnMinAcceptedActions: z.number().int().min(1).optional(),
670
- // improve_cycle_metrics retention (default 365 days, owner-approved).
671
- retentionDays: z.number().int().min(1).optional(),
672
- })
673
- .passthrough();
674
- export const ImproveConfigSchema = z
675
- .object({
676
- utilityDecay: ImproveUtilityDecaySchema.optional(),
677
- eventRetentionDays: nonNegativeNumber.optional(),
678
- calibration: ImproveCalibrationSchema.optional(),
679
- exploration: ImproveExplorationSchema.optional(),
680
- salience: ImproveSalienceSchema.optional(),
681
- collapseDetector: ImproveCollapseDetectorSchema.optional(),
682
- })
683
- .passthrough();
684
- // ── Index / per-pass ────────────────────────────────────────────────────────
685
- const GRAPH_EXTRACTION_INCLUDE_TYPES_ALLOWED = [
686
- "memory",
687
- "knowledge",
688
- "skill",
689
- "command",
690
- "agent",
691
- "workflow",
692
- "lesson",
693
- "task",
694
- "wiki",
695
- "fact",
696
- ];
697
- const INDEX_PASS_PROVIDER_KEYS = new Set([
698
- "endpoint",
699
- "model",
700
- "provider",
701
- "apiKey",
702
- "baseUrl",
703
- "temperature",
704
- "maxTokens",
705
- "capabilities",
706
- ]);
707
- const INDEX_PASS_KNOWN_KEYS = new Set([
708
- "llm",
709
- "graphExtractionBatchSize",
710
- "graphExtractionIncludeTypes",
711
- "memoryInferenceBatchSize",
712
- "lazyGraphExtraction",
713
- ]);
714
- /**
715
- * Per-pass `index.<pass>` entry. Uses preprocess + manual validation so we can
716
- * emit the legacy parser's targeted error messages ("Duplicate LLM provider
717
- * configuration", "Unknown key `index.<pass>.<key>`", "expected a boolean")
718
- * instead of Zod's generic `Unrecognized key` / `Expected boolean, received
719
- * string` strings — keeps `akm` startup errors actionable.
720
- */
721
- export const IndexPassConfigSchema = z.preprocess((raw, ctx) => {
722
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
723
- return raw; // let z.object below produce the type error
724
- }
725
- const obj = raw;
726
- for (const key of Object.keys(obj)) {
727
- if (INDEX_PASS_PROVIDER_KEYS.has(key)) {
183
+ // `defaultBundle`, when present, must name a configured bundle.
184
+ if (config.defaultBundle !== undefined) {
185
+ if (config.bundles === undefined) {
728
186
  ctx.addIssue({
729
187
  code: z.ZodIssueCode.custom,
730
- message: `Duplicate LLM provider configuration: \`${[...(ctx.path ?? []), key].join(".")}\` is not allowed. ` +
731
- "Configure provider/model/endpoint under `profiles.llm` only; per-pass entries support `{ llm: false }` opt-out.",
188
+ path: ["defaultBundle"],
189
+ message: "defaultBundle requires a bundles map",
732
190
  });
733
- return raw;
734
191
  }
735
- if (!INDEX_PASS_KNOWN_KEYS.has(key)) {
192
+ else if (!(config.defaultBundle in config.bundles)) {
736
193
  ctx.addIssue({
737
194
  code: z.ZodIssueCode.custom,
738
- message: `Unknown key \`${[...(ctx.path ?? []), key].join(".")}\`. Per-pass entries support \`llm\` ` +
739
- "(boolean opt-out), `graphExtractionBatchSize`, `graphExtractionIncludeTypes`, " +
740
- "`memoryInferenceBatchSize`, and `lazyGraphExtraction`.",
195
+ path: ["defaultBundle"],
196
+ message: `defaultBundle "${config.defaultBundle}" does not name a configured bundle`,
741
197
  });
742
- return raw;
743
198
  }
744
199
  }
745
- if ("llm" in obj && typeof obj.llm !== "boolean") {
200
+ for (const key of ["llm", "agent", "improve"]) {
201
+ if (config.defaults && key in config.defaults) {
202
+ ctx.addIssue({
203
+ code: z.ZodIssueCode.custom,
204
+ path: ["defaults", key],
205
+ message: `defaults.${key} is retired in 0.9`,
206
+ });
207
+ }
208
+ }
209
+ for (const [name, engine] of Object.entries(config.engines ?? {})) {
210
+ if (engine.kind === "agent" && engine.llmEngine) {
211
+ const fallback = config.engines?.[engine.llmEngine];
212
+ if (!fallback || fallback.kind !== "llm") {
213
+ ctx.addIssue({
214
+ code: z.ZodIssueCode.custom,
215
+ path: ["engines", name, "llmEngine"],
216
+ message: "llmEngine must name an LLM engine",
217
+ });
218
+ }
219
+ }
220
+ }
221
+ const defaultEngine = config.defaults?.engine;
222
+ if (defaultEngine && !config.engines?.[defaultEngine]) {
746
223
  ctx.addIssue({
747
224
  code: z.ZodIssueCode.custom,
748
- message: `Invalid \`${[...(ctx.path ?? []), "llm"].join(".")}\`: expected a boolean (true to use the default LLM profile, false to opt out). Got ${typeof obj.llm}.`,
225
+ path: ["defaults", "engine"],
226
+ message: "engine does not name a configured engine",
749
227
  });
750
- return raw;
751
228
  }
752
- return raw;
753
- }, z
754
- .object({
755
- llm: z.boolean().optional(),
756
- graphExtractionBatchSize: positiveInt.optional(),
757
- graphExtractionIncludeTypes: z.array(z.enum(GRAPH_EXTRACTION_INCLUDE_TYPES_ALLOWED)).nonempty().optional(),
758
- memoryInferenceBatchSize: positiveInt.optional(),
759
- lazyGraphExtraction: z.boolean().optional(),
760
- })
761
- .passthrough());
762
- const MetadataEnhanceSchema = z.object({ enabled: z.boolean().optional() }).passthrough();
763
- /**
764
- * RETIRED (meta-review 10-Q3): the staleness-detect pass was deleted; nothing
765
- * reads this section anymore. The key stays TOLERATED here so configs that
766
- * still carry `index.stalenessDetection` (written by 0.8.x migrations) do not
767
- * fail validation — deleting the key would route it into the per-pass
768
- * catchall, which rejects its `enabled`/`thresholdDays` fields.
769
- */
770
- const StalenessDetectionSchema = z
771
- .object({
772
- enabled: z.boolean().optional(),
773
- thresholdDays: positiveInt.optional(),
774
- })
775
- .passthrough();
776
- /**
777
- * Index config is a union of reserved feature sections and per-pass entries.
778
- * Passthrough so per-pass entries (keyed by arbitrary pass names like `graph`,
779
- * `enrichment`) can live next to the reserved keys.
780
- *
781
- * The outer preprocess emits the legacy parser's actionable error messages
782
- * for the two most common type-shape mistakes:
783
- * - An array at the `index` block.
784
- * - A non-object at `index.<passName>`.
785
- * Inner field validation (graphExtractionIncludeTypes enum, llm boolean,
786
- * provider-key rejection) is delegated to {@link IndexPassConfigSchema}.
787
- */
788
- export const IndexConfigSchema = z.preprocess((raw, ctx) => {
789
- if (raw === undefined || raw === null)
790
- return raw;
791
- if (Array.isArray(raw)) {
229
+ const defaultLlm = config.defaults?.llmEngine;
230
+ if (defaultLlm && config.engines?.[defaultLlm]?.kind !== "llm") {
792
231
  ctx.addIssue({
793
232
  code: z.ZodIssueCode.custom,
794
- message: 'Invalid `index` config: expected an object keyed by pass name (e.g. `{ "enrichment": { "llm": false } }`).',
233
+ path: ["defaults", "llmEngine"],
234
+ message: "llmEngine must name an LLM engine",
795
235
  });
796
- return raw;
797
236
  }
798
- if (typeof raw !== "object")
799
- return raw;
800
- for (const [passName, value] of Object.entries(raw)) {
801
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
802
- ctx.addIssue({
803
- code: z.ZodIssueCode.custom,
804
- message: `Invalid \`index.${passName}\` config: expected an object like \`{ "llm": false }\`.`,
805
- });
806
- return raw;
807
- }
808
- if (passName !== "metadataEnhance" &&
809
- passName !== "stalenessDetection" &&
810
- Array.isArray(value.graphExtractionIncludeTypes)) {
811
- const arr = value.graphExtractionIncludeTypes;
812
- const invalid = [];
813
- for (const t of arr) {
814
- if (typeof t === "string" &&
815
- !GRAPH_EXTRACTION_INCLUDE_TYPES_ALLOWED.includes(t.toLowerCase())) {
816
- invalid.push(t);
817
- }
237
+ const defaultStrategy = config.defaults?.improveStrategy;
238
+ if (defaultStrategy &&
239
+ !BUILTIN_IMPROVE_STRATEGY_NAMES.includes(defaultStrategy) &&
240
+ !config.improve?.strategies?.[defaultStrategy]) {
241
+ ctx.addIssue({
242
+ code: z.ZodIssueCode.custom,
243
+ path: ["defaults", "improveStrategy"],
244
+ message: "improveStrategy does not name a built-in or configured strategy",
245
+ });
246
+ }
247
+ for (const [strategyName, strategy] of Object.entries(config.improve?.strategies ?? {})) {
248
+ const strategyEngine = strategy.engine;
249
+ if (strategyEngine) {
250
+ const engine = config.engines?.[strategyEngine];
251
+ if (!engine || engine.kind !== "llm") {
252
+ ctx.addIssue({
253
+ code: z.ZodIssueCode.custom,
254
+ path: ["improve", "strategies", strategyName, "engine"],
255
+ message: engine ? "strategy engine must be an LLM engine" : "engine does not name a configured engine",
256
+ });
818
257
  }
819
- if (invalid.length > 0) {
258
+ }
259
+ for (const [processName, process] of Object.entries(strategy.processes ?? {})) {
260
+ const processConfig = process;
261
+ const capability = IMPROVE_PROCESS_ENGINE_CAPABILITIES[processName];
262
+ if (processConfig.engine && capability === null) {
820
263
  ctx.addIssue({
821
264
  code: z.ZodIssueCode.custom,
822
- message: `Invalid \`index.${passName}.graphExtractionIncludeTypes\`: unsupported type(s): ${invalid.join(", ")}.`,
265
+ path: ["improve", "strategies", strategyName, "processes", processName, "engine"],
266
+ message: `${processName} does not dispatch an engine`,
823
267
  });
824
- return raw;
268
+ }
269
+ else {
270
+ const processEngine = processConfig.engine ?? strategyEngine;
271
+ if (processEngine && capability === "llm") {
272
+ const engine = config.engines?.[processEngine];
273
+ if (!engine || engine.kind !== "llm") {
274
+ ctx.addIssue({
275
+ code: z.ZodIssueCode.custom,
276
+ path: ["improve", "strategies", strategyName, "processes", processName, "engine"],
277
+ message: engine ? `${processName} requires an LLM engine` : "engine does not name a configured engine",
278
+ });
279
+ }
280
+ }
281
+ else if (processConfig.engine && capability === "runner" && !config.engines?.[processConfig.engine]) {
282
+ ctx.addIssue({
283
+ code: z.ZodIssueCode.custom,
284
+ path: ["improve", "strategies", strategyName, "processes", processName, "engine"],
285
+ message: "engine does not name a configured engine",
286
+ });
287
+ }
288
+ }
289
+ const judgmentEngine = processConfig.judgment?.engine;
290
+ if (judgmentEngine) {
291
+ const engine = config.engines?.[judgmentEngine];
292
+ if (!engine) {
293
+ ctx.addIssue({
294
+ code: z.ZodIssueCode.custom,
295
+ path: ["improve", "strategies", strategyName, "processes", processName, "judgment", "engine"],
296
+ message: "engine does not name a configured engine",
297
+ });
298
+ }
825
299
  }
826
300
  }
827
301
  }
828
- return raw;
829
- }, z
830
- .object({
831
- metadataEnhance: MetadataEnhanceSchema.optional(),
832
- stalenessDetection: StalenessDetectionSchema.optional(),
833
- })
834
- .catchall(IndexPassConfigSchema));
835
- // ── Workflow engine ─────────────────────────────────────────────────────────
836
- /**
837
- * Workflow-engine settings (`workflow`).
838
- *
839
- * `maxConcurrency` is the engine-wide ceiling on concurrent units for native
840
- * fan-out (`akm workflow run`). It replaces the hard-coded `min(16, cores−2)`
841
- * cap (which matched Claude Code) with a user knob:
842
- * - UNSET → the CPU-derived default `min(16, max(1, cores−2))`.
843
- * - SET → the explicit positive integer, CLAMPED at read time to
844
- * `[1, WORKFLOW_MAX_CONCURRENCY_CEILING]` (64). Values above the ceiling
845
- * are clamped, not rejected, so a config shared across machines with wildly
846
- * different core counts never hard-fails validation.
847
- * The R3 brief/report driver surface does NOT consult this — drivers own their
848
- * own parallelism (the engine only caps native dispatch).
849
- */
850
- export const WorkflowConfigSchema = z
851
- .object({
852
- maxConcurrency: positiveInt.optional(),
853
- })
854
- .passthrough();
855
- // ── Setup-derived recommendations ──────────────────────────────────────────
856
- /**
857
- * Cron-style schedule hints derived by `akm setup --reset-recommended`.
858
- *
859
- * These record the *recommended* cadence for the improve and index background
860
- * tasks. They are advisory metadata persisted into config so the value
861
- * survives a re-run; actual task scheduling lives in the tasks subsystem.
862
- */
863
- export const SetupTaskSchedulesSchema = z
864
- .object({
865
- improve: z.string().min(1).optional(),
866
- index: z.string().min(1).optional(),
867
- })
868
- .passthrough();
869
- export const SetupConfigSchema = z
870
- .object({
871
- taskSchedules: SetupTaskSchedulesSchema.optional(),
872
- })
873
- .passthrough();
874
- // ── Top-level AkmConfig ────────────────────────────────────────────────────
875
- /**
876
- * Base object schema used both as the top-level shape and as the source of
877
- * truth for {@link listTopLevelConfigKeys}. {@link AkmConfigSchema} wraps this
878
- * with cross-field refinements (`.superRefine()`).
879
- *
880
- * All fields validate loudly — typos and shape errors throw at load time. The
881
- * legacy parser's warn-and-drop tolerance was a frequent source of silent
882
- * configuration loss; the migration module ({@link migrateConfigShape}) handles
883
- * one-time 0.7→0.8 input transforms before the schema sees the value.
884
- */
885
- export const AkmConfigShape = {
886
- configVersion: z.union([z.string().min(1), z.number()]).optional(),
887
- profiles: ProfilesSchema.optional(),
888
- defaults: DefaultsSchema.optional(),
889
- // Global model-alias tiers: alias → platform → exact model string, with a
890
- // reserved `"*"` platform key as fallback. Lets workflows/callers name a
891
- // semantic tier ("fast", "deep") that resolves per-harness at dispatch
892
- // time. Values are literal model strings, never other aliases (one
893
- // resolution level). Platform keys match the platform string a command
894
- // builder resolves against ("claude", "opencode", "opencode-sdk", or a
895
- // custom profile's name for the default builder) — unknown keys are inert.
896
- // Precedence: profile modelAliases > this table > built-in aliases.
897
- modelAliases: z.record(z.string().min(1), z.record(z.string().min(1), z.string().min(1))).optional(),
898
- stashDir: nonEmptyString.optional(),
899
- semanticSearchMode: z.enum(["off", "auto"]).default("auto"),
900
- embedding: EmbeddingConnectionConfigSchema.optional(),
901
- index: IndexConfigSchema.optional(),
902
- // The `installed[]` shape is OWNED by the registry (`InstalledStashEntry`):
903
- // its `source` is the 4-value `InstallKind` produced by the registry ref
904
- // parser, and installed entries never carry the extra passthrough keys. The
905
- // schema still validates entries at runtime, but its OUTPUT type is pinned to
906
- // the domain type so config consumers get the registry `InstalledStashEntry`
907
- // (not a looser schema-local mirror) — the single-source-of-truth boundary.
908
- installed: z.array(InstalledStashEntrySchema).optional(),
909
- registries: z.array(RegistryConfigEntrySchema).optional(),
910
- sources: z.array(SourceConfigEntrySchema).optional(),
911
- output: OutputConfigSchema.optional(),
912
- writable: z.boolean().optional(),
913
- defaultWriteTarget: nonEmptyString.optional(),
914
- search: SearchConfigSchema.optional(),
915
- feedback: FeedbackConfigSchema.optional(),
916
- archiveRetentionDays: nonNegativeNumber.optional(),
917
- improve: ImproveConfigSchema.optional(),
918
- workflow: WorkflowConfigSchema.optional(),
919
- setup: SetupConfigSchema.optional(),
920
- };
921
- export const AkmConfigBaseSchema = z.object(AkmConfigShape).passthrough();
922
- export const AkmConfigSchema = AkmConfigBaseSchema.superRefine((config, ctx) => {
923
- // #464.a: defaultWriteTarget must name a configured source when sources
924
- // are present. With no sources configured, error out instead of silently
925
- // accepting (no implicit "first writable" fallback — see locked decision 3).
302
+ // #464.a: defaultWriteTarget must name a configured source. 0.9.0 (spec
303
+ // §10.1): sources are `bundles` keys, so it must name a bundle. With no
304
+ // bundles configured, error out instead of silently accepting (no implicit
305
+ // "first writable" fallback — see locked decision 3).
926
306
  if (config.defaultWriteTarget !== undefined) {
927
- const knownNames = (config.sources ?? [])
928
- .map((s) => s.name)
929
- .filter((n) => typeof n === "string" && n.length > 0);
307
+ const knownNames = Object.keys(config.bundles ?? {});
930
308
  if (knownNames.length === 0) {
931
309
  ctx.addIssue({
932
310
  code: z.ZodIssueCode.custom,
933
311
  path: ["defaultWriteTarget"],
934
- message: `defaultWriteTarget "${config.defaultWriteTarget}" cannot be resolved: no sources configured. ` +
935
- "Add at least one entry to `sources` with a matching `name` first.",
312
+ message: `defaultWriteTarget "${config.defaultWriteTarget}" cannot be resolved: no bundles configured. ` +
313
+ "Add at least one entry to `bundles` first.",
936
314
  });
937
315
  }
938
316
  else if (!knownNames.includes(config.defaultWriteTarget)) {
939
317
  ctx.addIssue({
940
318
  code: z.ZodIssueCode.custom,
941
319
  path: ["defaultWriteTarget"],
942
- message: `defaultWriteTarget "${config.defaultWriteTarget}" does not match any configured source name: ${knownNames.map((n) => `"${n}"`).join(", ")}.`,
320
+ message: `defaultWriteTarget "${config.defaultWriteTarget}" does not match any configured bundle: ${knownNames.map((n) => `"${n}"`).join(", ")}.`,
943
321
  });
944
322
  }
945
323
  }