akm-cli 0.9.0-rc.0 → 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 (598) hide show
  1. package/CHANGELOG.md +1283 -22
  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/prompts/workflow-unit-preamble.md +26 -0
  26. package/dist/assets/stash-skeleton/README.md +38 -10
  27. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +8 -0
  28. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +8 -0
  29. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +14 -1
  30. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +13 -1
  31. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +9 -1
  32. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +11 -0
  33. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +9 -0
  34. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +9 -0
  35. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +8 -0
  36. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +100 -0
  37. package/dist/assets/stash-skeleton/facts/conventions/domains.md +64 -0
  38. package/dist/assets/stash-skeleton/facts/conventions/organization.md +136 -0
  39. package/dist/assets/tasks/core/extract.yml +3 -2
  40. package/dist/assets/tasks/core/improve.yml +2 -1
  41. package/dist/assets/tasks/core/index-refresh.yml +1 -0
  42. package/dist/assets/tasks/core/sync.yml +1 -0
  43. package/dist/assets/tasks/core/version-check.yml +2 -1
  44. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  45. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  46. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  47. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  48. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  49. package/dist/assets/templates/html/health.html +5 -4
  50. package/dist/assets/workflows/workflow-template.md +31 -15
  51. package/dist/cli/invocation.js +279 -0
  52. package/dist/cli/parse-args.js +5 -90
  53. package/dist/cli/retired-commands.js +78 -0
  54. package/dist/cli/shared.js +158 -48
  55. package/dist/cli-node.mjs +2 -1
  56. package/dist/cli.js +747 -293
  57. package/dist/commands/agent/agent-dispatch.js +19 -18
  58. package/dist/commands/agent/agent-support.js +0 -24
  59. package/dist/commands/agent/contribute-cli.js +43 -97
  60. package/dist/commands/completions.js +80 -23
  61. package/dist/commands/config-cli.js +44 -281
  62. package/dist/commands/env/env-binding.js +99 -0
  63. package/dist/commands/env/env-cli.js +84 -224
  64. package/dist/commands/env/env.js +12 -163
  65. package/dist/commands/env/marker-path.js +6 -0
  66. package/dist/commands/env/secret-cli.js +45 -61
  67. package/dist/commands/env/secret.js +32 -62
  68. package/dist/commands/feedback-cli.js +179 -85
  69. package/dist/commands/health/accept-rate.js +58 -0
  70. package/dist/commands/health/advisories.js +7 -8
  71. package/dist/commands/health/checks.js +279 -94
  72. package/dist/commands/health/html-report.js +197 -578
  73. package/dist/commands/health/improve-metrics.js +277 -246
  74. package/dist/commands/health/llm-usage.js +19 -19
  75. package/dist/commands/health/md-report.js +16 -7
  76. package/dist/commands/health/metrics.js +67 -32
  77. package/dist/commands/health/renderers.js +47 -0
  78. package/dist/commands/health/report-view-model.js +508 -0
  79. package/dist/commands/health/stash-exposure.js +1 -1
  80. package/dist/commands/health/surfaces.js +16 -56
  81. package/dist/commands/health/task-runs.js +3 -67
  82. package/dist/{migrate-storage-node.mjs → commands/health/types-checks.js} +1 -5
  83. package/dist/commands/health/types-improve.js +29 -0
  84. package/dist/{output/text/save.js → commands/health/types-metrics.js} +1 -2
  85. package/dist/commands/health/types-result.js +7 -0
  86. package/dist/commands/health/types-runs.js +4 -0
  87. package/dist/commands/health/types-session-log.js +4 -0
  88. package/dist/commands/health/types-windows.js +4 -0
  89. package/dist/commands/health/types.js +26 -21
  90. package/dist/commands/health/windows.js +2 -3
  91. package/dist/commands/health.js +296 -167
  92. package/dist/commands/improve/anti-collapse.js +5 -5
  93. package/dist/commands/improve/autonomy-gate.js +68 -0
  94. package/dist/commands/improve/collapse-detector.js +65 -52
  95. package/dist/commands/improve/consolidate/chunking.js +9 -7
  96. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  97. package/dist/commands/improve/consolidate/merge.js +4 -0
  98. package/dist/commands/improve/consolidate.js +454 -1354
  99. package/dist/commands/improve/content-hash.js +39 -0
  100. package/dist/commands/improve/distill/content-repair.js +4 -10
  101. package/dist/commands/improve/distill/promote-memory.js +89 -64
  102. package/dist/commands/improve/distill/quality-gate.js +118 -42
  103. package/dist/commands/improve/distill-guards.js +1 -1
  104. package/dist/commands/improve/distill-promotion-policy.js +33 -888
  105. package/dist/commands/improve/distill.js +607 -363
  106. package/dist/commands/improve/eligibility.js +165 -79
  107. package/dist/commands/improve/extract-cli.js +35 -126
  108. package/dist/commands/improve/extract-prompt.js +6 -35
  109. package/dist/commands/improve/extract.js +640 -391
  110. package/dist/commands/improve/feedback-valence.js +2 -12
  111. package/dist/commands/improve/improve-cli.js +134 -135
  112. package/dist/commands/improve/improve-result-file.js +30 -50
  113. package/dist/commands/improve/improve-run-types.js +4 -0
  114. package/dist/commands/improve/improve-strategies.js +135 -0
  115. package/dist/commands/improve/improve.js +904 -701
  116. package/dist/commands/improve/locks.js +64 -111
  117. package/dist/commands/improve/loop-stages.js +1110 -923
  118. package/dist/commands/improve/memory/derived-ref.js +124 -0
  119. package/dist/commands/improve/memory/memory-belief.js +79 -7
  120. package/dist/commands/improve/memory/memory-contradiction-detect.js +49 -52
  121. package/dist/commands/improve/memory/memory-improve.js +25 -37
  122. package/dist/commands/improve/outcome-loop.js +25 -88
  123. package/dist/commands/improve/preparation.js +1034 -813
  124. package/dist/commands/improve/proactive-maintenance.js +34 -9
  125. package/dist/commands/improve/proposal-envelope.js +31 -0
  126. package/dist/commands/improve/reflect.js +983 -794
  127. package/dist/commands/improve/run-context.js +119 -0
  128. package/dist/commands/improve/salience.js +24 -127
  129. package/dist/commands/improve/session-asset.js +7 -3
  130. package/dist/commands/improve/shared.js +14 -34
  131. package/dist/commands/improve/source-identity.js +28 -0
  132. package/dist/commands/improve/triage.js +20 -17
  133. package/dist/commands/lint/base-linter.js +340 -313
  134. package/dist/commands/lint/env-key-rules.js +31 -47
  135. package/dist/commands/lint/index.js +185 -30
  136. package/dist/commands/{events.js → log.js} +28 -38
  137. package/dist/commands/migrate-cli.js +54 -0
  138. package/dist/commands/migration-tool.js +55 -0
  139. package/dist/commands/observability-cli.js +70 -208
  140. package/dist/commands/proposal/diff-format.js +50 -0
  141. package/dist/commands/proposal/drain-policies.js +0 -6
  142. package/dist/commands/proposal/drain.js +91 -40
  143. package/dist/commands/proposal/proposal-cli.js +134 -132
  144. package/dist/commands/proposal/proposal-types.js +56 -0
  145. package/dist/commands/proposal/proposal.js +83 -65
  146. package/dist/commands/proposal/propose-cli.js +88 -0
  147. package/dist/commands/proposal/propose.js +105 -88
  148. package/dist/commands/proposal/repository.js +1303 -278
  149. package/dist/commands/proposal/validators/proposal-quality-validators.js +16 -6
  150. package/dist/commands/proposal/validators/proposal-validators.js +61 -12
  151. package/dist/commands/proposal/validators/proposals.js +6 -8
  152. package/dist/commands/read/curate.js +78 -73
  153. package/dist/commands/read/knowledge.js +510 -13
  154. package/dist/commands/read/registry-search.js +2 -2
  155. package/dist/commands/read/remember-cli.js +84 -15
  156. package/dist/commands/read/search-cli.js +203 -96
  157. package/dist/commands/read/search.js +126 -94
  158. package/dist/commands/read/show.js +226 -250
  159. package/dist/commands/registry-cli.js +34 -60
  160. package/dist/commands/remember.js +18 -57
  161. package/dist/commands/sources/add-cli.js +104 -49
  162. package/dist/commands/sources/bundle-cli.js +166 -0
  163. package/dist/commands/sources/bundle-config-ops.js +63 -0
  164. package/dist/commands/sources/info.js +27 -15
  165. package/dist/commands/sources/init.js +30 -40
  166. package/dist/commands/sources/installed-stashes.js +469 -172
  167. package/dist/commands/sources/migration-help.js +7 -4
  168. package/dist/commands/sources/schema-repair.js +10 -9
  169. package/dist/commands/sources/self-update.js +182 -121
  170. package/dist/commands/sources/source-add.js +169 -178
  171. package/dist/commands/sources/source-clone.js +144 -41
  172. package/dist/commands/sources/source-manage.js +94 -59
  173. package/dist/commands/sources/sources-cli.js +64 -205
  174. package/dist/commands/sources/stash-cli.js +91 -54
  175. package/dist/commands/sources/stash-skeleton.js +1 -1
  176. package/dist/commands/tasks/tasks-cli.js +106 -104
  177. package/dist/commands/tasks/tasks.js +445 -262
  178. package/dist/commands/workflow-cli.js +232 -121
  179. package/dist/core/action-contributors.js +1 -1
  180. package/dist/core/activation-policy.js +49 -0
  181. package/dist/core/adapter/adapters/agent-skills-adapter.js +181 -0
  182. package/dist/core/adapter/adapters/akm-adapter.js +528 -0
  183. package/dist/core/adapter/adapters/akm-lint.js +392 -0
  184. package/dist/core/adapter/adapters/akm-metadata.js +387 -0
  185. package/dist/core/adapter/adapters/akm-task-adapter.js +149 -0
  186. package/dist/core/adapter/adapters/akm-workflow-adapter.js +180 -0
  187. package/dist/core/adapter/adapters/claude-adapter.js +61 -0
  188. package/dist/core/adapter/adapters/dotenv-adapter.js +187 -0
  189. package/dist/core/adapter/adapters/generic-files-adapter.js +119 -0
  190. package/dist/core/adapter/adapters/index.js +80 -0
  191. package/dist/core/adapter/adapters/llm-wiki-adapter.js +419 -0
  192. package/dist/core/adapter/adapters/okf-adapter.js +391 -0
  193. package/dist/core/adapter/adapters/opencode-adapter.js +68 -0
  194. package/dist/core/adapter/adapters/shared.js +286 -0
  195. package/dist/core/adapter/adapters/tool-dir-shared.js +217 -0
  196. package/dist/core/adapter/adapters/website-snapshot-adapter.js +155 -0
  197. package/dist/core/adapter/bundle-adapter.js +4 -0
  198. package/dist/core/adapter/detect-adapter.js +17 -0
  199. package/dist/core/adapter/recognize-match.js +44 -0
  200. package/dist/core/adapter/registry.js +56 -0
  201. package/dist/core/adapter/types.js +4 -0
  202. package/dist/core/asset/akm-markdown.js +30 -0
  203. package/dist/core/asset/asset-placement.js +243 -0
  204. package/dist/core/asset/asset-ref.js +110 -79
  205. package/dist/core/asset/asset-serialize.js +20 -0
  206. package/dist/core/asset/frontmatter.js +28 -12
  207. package/dist/core/asset/markdown.js +40 -51
  208. package/dist/core/asset/resolve-ref.js +274 -0
  209. package/dist/core/asset/stash-meta.js +2 -2
  210. package/dist/core/bundle-id.js +51 -0
  211. package/dist/core/common.js +281 -86
  212. package/dist/core/config/config-io.js +42 -128
  213. package/dist/core/config/config-schema.js +233 -834
  214. package/dist/core/config/config-sources.js +162 -39
  215. package/dist/core/config/config-types.js +16 -11
  216. package/dist/core/config/config-version.js +29 -0
  217. package/dist/core/config/config-walker.js +126 -37
  218. package/dist/core/config/config.js +154 -331
  219. package/dist/core/config/deep-merge.js +41 -0
  220. package/dist/core/config/engine-semantics.js +28 -0
  221. package/dist/core/config/experimental.js +21 -0
  222. package/dist/core/config/schema/embedding.js +38 -0
  223. package/dist/core/config/schema/engines.js +116 -0
  224. package/dist/core/config/schema/experimental.js +47 -0
  225. package/dist/core/config/schema/feedback.js +31 -0
  226. package/dist/core/config/schema/improve-processes.js +389 -0
  227. package/dist/core/config/schema/improve.js +94 -0
  228. package/dist/core/config/schema/index-config.js +176 -0
  229. package/dist/core/config/schema/output.js +18 -0
  230. package/dist/core/config/schema/primitives.js +94 -0
  231. package/dist/core/config/schema/search.js +30 -0
  232. package/dist/core/config/schema/setup.js +18 -0
  233. package/dist/core/config/schema/sources-bundles.js +169 -0
  234. package/dist/core/config/schema/workflow.js +29 -0
  235. package/dist/core/env-secret-ref.js +155 -20
  236. package/dist/core/errors.js +17 -15
  237. package/dist/core/events-types.js +4 -0
  238. package/dist/core/events.js +46 -128
  239. package/dist/core/extra-params.js +62 -0
  240. package/dist/core/file-change.js +17 -0
  241. package/dist/core/file-lock.js +202 -57
  242. package/dist/core/fs-txn.js +392 -0
  243. package/dist/core/git-message.js +59 -0
  244. package/dist/core/improve-result.js +167 -0
  245. package/dist/core/json-schema.js +142 -0
  246. package/dist/core/lesson-lint.js +1 -17
  247. package/dist/core/logs-db.js +1 -1
  248. package/dist/core/maintenance-barrier.js +135 -0
  249. package/dist/core/migration-operation.js +44 -0
  250. package/dist/core/mutation-target.js +78 -0
  251. package/dist/core/paths.js +22 -25
  252. package/dist/core/platform.js +10 -0
  253. package/dist/core/recognition-util.js +128 -0
  254. package/dist/core/redaction.js +392 -0
  255. package/dist/core/standards/resolve-standards-context.js +36 -65
  256. package/dist/core/standards/resolve-stash-standards.js +2 -2
  257. package/dist/core/standards/resolve-type-conventions.js +5 -5
  258. package/dist/core/state/migrations.js +242 -11
  259. package/dist/core/state-db.js +98 -10
  260. package/dist/core/structured.js +1 -1
  261. package/dist/core/subprocess.js +303 -0
  262. package/dist/core/text-truncation.js +9 -5
  263. package/dist/core/time.js +20 -0
  264. package/dist/core/type-presentation.js +130 -0
  265. package/dist/core/warn.js +0 -3
  266. package/dist/core/write-source.js +834 -118
  267. package/dist/indexer/bundle-identity-guard.js +92 -0
  268. package/dist/indexer/db/graph-db.js +1 -25
  269. package/dist/indexer/db/llm-cache.js +1 -1
  270. package/dist/indexer/ensure-index.js +30 -9
  271. package/dist/indexer/graph/graph-boost.js +9 -30
  272. package/dist/indexer/graph/graph-extraction.js +41 -27
  273. package/dist/indexer/graph/graph-types.js +4 -0
  274. package/dist/indexer/index-writer-lock.js +93 -49
  275. package/dist/indexer/index-written-assets.js +100 -53
  276. package/dist/indexer/indexer.js +746 -329
  277. package/dist/indexer/init.js +18 -25
  278. package/dist/indexer/installations.js +142 -0
  279. package/dist/indexer/passes/dir-staleness.js +18 -10
  280. package/dist/indexer/passes/memory-inference.js +25 -15
  281. package/dist/indexer/passes/metadata.js +412 -243
  282. package/dist/indexer/scan/doc-to-entry.js +160 -0
  283. package/dist/indexer/scan/drain-dir.js +134 -0
  284. package/dist/indexer/search/db-search.js +292 -108
  285. package/dist/indexer/search/fts-query.js +64 -0
  286. package/dist/indexer/search/ranking-contributors.js +145 -25
  287. package/dist/indexer/search/ranking-types.js +4 -0
  288. package/dist/indexer/search/ranking.js +28 -71
  289. package/dist/indexer/search/search-attribution.js +67 -0
  290. package/dist/indexer/search/search-fields.js +18 -3
  291. package/dist/indexer/search/search-hit-enrichers.js +30 -40
  292. package/dist/indexer/search/search-source.js +157 -111
  293. package/dist/indexer/search/semantic-status.js +4 -1
  294. package/dist/indexer/usage/usage-events.js +10 -30
  295. package/dist/indexer/walk/file-context.js +3 -45
  296. package/dist/indexer/walk/matchers.js +42 -34
  297. package/dist/indexer/walk/path-resolver.js +11 -5
  298. package/dist/indexer/walk/walker.js +42 -14
  299. package/dist/integrations/agent/builder-shared.js +7 -0
  300. package/dist/integrations/agent/builders.js +5 -56
  301. package/dist/integrations/agent/config.js +3 -143
  302. package/dist/integrations/agent/detect.js +17 -2
  303. package/dist/integrations/agent/engine-resolution.js +231 -0
  304. package/dist/integrations/agent/index.js +1 -2
  305. package/dist/integrations/agent/model-aliases.js +16 -2
  306. package/dist/integrations/agent/profiles.js +36 -62
  307. package/dist/integrations/agent/prompts.js +46 -18
  308. package/dist/integrations/agent/runner-dispatch.js +93 -4
  309. package/dist/integrations/agent/runner.js +76 -208
  310. package/dist/integrations/agent/spawn.js +88 -196
  311. package/dist/integrations/harnesses/aider/agent-builder.js +114 -0
  312. package/dist/integrations/harnesses/aider/index.js +48 -0
  313. package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
  314. package/dist/integrations/harnesses/amazonq/agent-builder.js +147 -0
  315. package/dist/integrations/harnesses/amazonq/index.js +45 -0
  316. package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
  317. package/dist/integrations/harnesses/claude/agent-builder.js +46 -8
  318. package/dist/integrations/harnesses/claude/config-import.js +1 -3
  319. package/dist/integrations/harnesses/claude/index.js +24 -35
  320. package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
  321. package/dist/integrations/harnesses/claude/session-log.js +27 -75
  322. package/dist/integrations/harnesses/codex/agent-builder.js +138 -0
  323. package/dist/integrations/harnesses/codex/index.js +52 -0
  324. package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
  325. package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
  326. package/dist/integrations/harnesses/copilot/index.js +48 -0
  327. package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
  328. package/dist/integrations/harnesses/gemini/agent-builder.js +120 -0
  329. package/dist/integrations/harnesses/gemini/index.js +48 -0
  330. package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
  331. package/dist/integrations/harnesses/ids.js +24 -0
  332. package/dist/integrations/harnesses/index.js +54 -34
  333. package/dist/integrations/harnesses/opencode/agent-builder.js +23 -5
  334. package/dist/integrations/harnesses/opencode/config-import.js +1 -3
  335. package/dist/integrations/harnesses/opencode/index.js +14 -32
  336. package/dist/integrations/harnesses/opencode/session-log.js +67 -125
  337. package/dist/integrations/harnesses/opencode-sdk/harness.js +51 -0
  338. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +681 -108
  339. package/dist/integrations/harnesses/openhands/agent-builder.js +128 -0
  340. package/dist/integrations/harnesses/openhands/index.js +48 -0
  341. package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
  342. package/dist/integrations/harnesses/pi/agent-builder.js +97 -0
  343. package/dist/integrations/harnesses/pi/index.js +45 -0
  344. package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
  345. package/dist/integrations/harnesses/shared.js +17 -0
  346. package/dist/integrations/harnesses/types.js +43 -32
  347. package/dist/integrations/lockfile.js +211 -24
  348. package/dist/integrations/session-logs/index.js +36 -39
  349. package/dist/integrations/session-logs/provider-base.js +113 -0
  350. package/dist/llm/client.js +182 -110
  351. package/dist/llm/embedders/deterministic.js +2 -2
  352. package/dist/llm/embedders/remote.js +21 -9
  353. package/dist/llm/feature-gate.js +17 -57
  354. package/dist/llm/graph-extract.js +12 -13
  355. package/dist/llm/index-passes.js +8 -42
  356. package/dist/llm/memory-infer.js +144 -1
  357. package/dist/llm/metadata-enhance.js +45 -30
  358. package/dist/llm/structured-call.js +16 -8
  359. package/dist/llm/usage-persist.js +30 -5
  360. package/dist/llm/usage-telemetry.js +59 -6
  361. package/dist/output/cli-hints.js +1 -2
  362. package/dist/output/command-registry.js +27 -0
  363. package/dist/output/context.js +22 -7
  364. package/dist/output/format-exempt.js +80 -0
  365. package/dist/output/generic-render.js +251 -0
  366. package/dist/output/html-render.js +11 -16
  367. package/dist/output/render-registry.js +57 -0
  368. package/dist/output/renderers.js +14 -279
  369. package/dist/output/shapes/curate.js +10 -1
  370. package/dist/output/shapes/events.js +12 -7
  371. package/dist/output/shapes/helpers.js +58 -84
  372. package/dist/output/shapes/passthrough.js +11 -39
  373. package/dist/output/shapes/proposal/producer.js +15 -7
  374. package/dist/output/shapes/registry.js +12 -6
  375. package/dist/output/shapes.js +0 -9
  376. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  377. package/dist/output/text/bundle-show.js +7 -0
  378. package/dist/output/text/command-format.js +562 -0
  379. package/dist/output/text/env.js +1 -3
  380. package/dist/output/text/events.js +8 -7
  381. package/dist/output/text/helpers.js +15 -1164
  382. package/dist/output/text/proposal/producer.js +4 -2
  383. package/dist/output/text/proposal-format.js +202 -0
  384. package/dist/output/text/registry-commands.js +1 -2
  385. package/dist/output/text/registry.js +12 -6
  386. package/dist/output/text/show-directives.js +117 -0
  387. package/dist/output/text/show-format.js +103 -0
  388. package/dist/output/text/sync.js +5 -0
  389. package/dist/output/text/workflow-format.js +332 -0
  390. package/dist/output/text/workflow.js +3 -2
  391. package/dist/output/text.js +10 -19
  392. package/dist/registry/factory.js +4 -6
  393. package/dist/registry/origin-resolve.js +16 -27
  394. package/dist/registry/providers/skills-sh.js +3 -3
  395. package/dist/registry/providers/static-index.js +15 -25
  396. package/dist/registry/resolve.js +43 -94
  397. package/dist/registry/semver.js +43 -0
  398. package/dist/runtime.js +81 -12
  399. package/dist/scripts/akm-migrate.js +35529 -0
  400. package/dist/setup/detect.js +5 -7
  401. package/dist/setup/detected-engines.js +136 -0
  402. package/dist/setup/engine-config.js +100 -0
  403. package/dist/setup/registry-stash-loader.js +3 -3
  404. package/dist/setup/semantic-assets.js +12 -9
  405. package/dist/setup/setup.js +444 -208
  406. package/dist/setup/steps/connection-shared.js +120 -0
  407. package/dist/setup/steps/connection.js +108 -305
  408. package/dist/setup/steps/platforms.js +13 -12
  409. package/dist/setup/steps/semantic.js +15 -3
  410. package/dist/setup/steps/sources.js +21 -15
  411. package/dist/setup/steps/stashdir.js +6 -4
  412. package/dist/setup/steps/tasks.js +236 -119
  413. package/dist/setup/steps.js +3 -2
  414. package/dist/sources/freshness.js +39 -0
  415. package/dist/sources/provider-factory.js +11 -17
  416. package/dist/sources/providers/filesystem.js +2 -3
  417. package/dist/sources/providers/git-install.js +278 -34
  418. package/dist/sources/providers/git-provider.js +54 -56
  419. package/dist/sources/providers/git-stash.js +420 -91
  420. package/dist/sources/providers/git.js +2 -2
  421. package/dist/sources/providers/npm.js +16 -19
  422. package/dist/sources/providers/provider-utils.js +47 -22
  423. package/dist/sources/providers/sync-from-ref.js +3 -9
  424. package/dist/sources/providers/website.js +2 -2
  425. package/dist/sources/resolve.js +11 -10
  426. package/dist/sources/snapshot-fetchers/types.js +4 -0
  427. package/dist/sources/{website-ingest.js → snapshot-fetchers/website-ingest.js} +110 -41
  428. package/dist/storage/database.js +60 -4
  429. package/dist/storage/engines/sqlite-migrations.js +156 -5
  430. package/dist/storage/locations.js +1 -2
  431. package/dist/storage/repositories/canaries-repository.js +1 -1
  432. package/dist/storage/repositories/events-repository.js +51 -11
  433. package/dist/storage/repositories/improve-runs-repository.js +6 -32
  434. package/dist/storage/repositories/index-connection.js +79 -0
  435. package/dist/storage/repositories/index-db.js +4 -3
  436. package/dist/storage/repositories/index-entries-repository.js +863 -0
  437. package/dist/{indexer/db/entry-mapper.js → storage/repositories/index-entry-mapper.js} +19 -2
  438. package/dist/storage/repositories/index-entry-types.js +4 -0
  439. package/dist/storage/repositories/index-fts-repository.js +167 -0
  440. package/dist/storage/repositories/index-llm-cache-repository.js +108 -0
  441. package/dist/storage/repositories/index-meta-repository.js +49 -0
  442. package/dist/{indexer/db/schema.js → storage/repositories/index-schema.js} +226 -100
  443. package/dist/storage/repositories/index-sql.js +12 -0
  444. package/dist/storage/repositories/index-utility-repository.js +356 -0
  445. package/dist/storage/repositories/index-vec-repository.js +250 -0
  446. package/dist/storage/repositories/outcome-repository.js +119 -0
  447. package/dist/storage/repositories/proposals-repository.js +317 -75
  448. package/dist/storage/repositories/registry-cache.js +1 -1
  449. package/dist/storage/repositories/salience-repository.js +172 -0
  450. package/dist/storage/repositories/task-history-repository.js +110 -3
  451. package/dist/storage/repositories/workflow-runs-repository.js +240 -19
  452. package/dist/tasks/backends/cron.js +169 -46
  453. package/dist/tasks/backends/exec-utils.js +76 -3
  454. package/dist/tasks/backends/index.js +6 -9
  455. package/dist/tasks/backends/launchd.js +292 -55
  456. package/dist/tasks/backends/schtasks.js +557 -70
  457. package/dist/tasks/backends/types.js +4 -0
  458. package/dist/tasks/command-executable.js +93 -0
  459. package/dist/tasks/embedded.js +56 -38
  460. package/dist/tasks/parser.js +156 -64
  461. package/dist/tasks/resolve-akm-bin.js +144 -51
  462. package/dist/tasks/runner.js +377 -209
  463. package/dist/tasks/schedule.js +108 -19
  464. package/dist/tasks/scheduler-invocation.js +296 -0
  465. package/dist/tasks/schema.js +1 -1
  466. package/dist/tasks/task-id.js +35 -0
  467. package/dist/tasks/validator.js +30 -16
  468. package/dist/text-import-hook.mjs +1 -1
  469. package/dist/workflows/authoring/authoring.js +104 -43
  470. package/dist/workflows/authoring/scope-key.js +1 -1
  471. package/dist/workflows/cli.js +0 -16
  472. package/dist/workflows/concurrency-policy.js +15 -0
  473. package/dist/workflows/exec/brief.js +450 -0
  474. package/dist/workflows/exec/frozen-judge.js +47 -0
  475. package/dist/workflows/exec/native-executor.js +1038 -0
  476. package/dist/workflows/exec/param-secrets.js +115 -0
  477. package/dist/workflows/exec/report.js +1460 -0
  478. package/dist/workflows/exec/run-workflow.js +602 -0
  479. package/dist/workflows/exec/scheduler.js +71 -0
  480. package/dist/workflows/exec/step-work.js +1190 -0
  481. package/dist/workflows/exec/unit-writer.js +23 -0
  482. package/dist/workflows/exec/workflow-engine-gate.js +67 -0
  483. package/dist/workflows/exec/worktree.js +171 -0
  484. package/dist/workflows/ir/compile.js +246 -0
  485. package/dist/workflows/ir/freeze.js +233 -0
  486. package/dist/workflows/ir/params.js +54 -0
  487. package/dist/workflows/ir/plan-hash.js +68 -0
  488. package/dist/workflows/ir/schema.js +540 -0
  489. package/dist/workflows/parser.js +878 -304
  490. package/dist/workflows/program/expressions.js +181 -0
  491. package/dist/workflows/program/schema.js +51 -0
  492. package/dist/workflows/renderer.js +100 -45
  493. package/dist/workflows/resource-limits.js +22 -0
  494. package/dist/workflows/runtime/agent-identity.js +59 -14
  495. package/dist/workflows/runtime/checkin.js +1 -1
  496. package/dist/workflows/runtime/plan-classifier.js +131 -0
  497. package/dist/workflows/runtime/runs.js +376 -119
  498. package/dist/workflows/runtime/unit-checkin.js +45 -0
  499. package/dist/workflows/runtime/unit-phases.js +20 -0
  500. package/dist/workflows/runtime/workflow-asset-loader.js +241 -40
  501. package/dist/workflows/schema.js +1 -11
  502. package/dist/workflows/validate-summary.js +2 -3
  503. package/dist/workflows/validator.js +52 -30
  504. package/docs/README.md +42 -78
  505. package/docs/migration/README.md +8 -0
  506. package/docs/migration/release-notes/0.6.0.md +1 -1
  507. package/docs/migration/release-notes/0.7.0.md +9 -8
  508. package/docs/migration/release-notes/0.9.0.md +158 -14
  509. package/docs/migration/v0.7-to-v0.8.md +46 -47
  510. package/docs/migration/v0.8-to-v0.9.md +844 -0
  511. package/docs/reference/README.md +12 -0
  512. package/docs/reference/data-and-telemetry.md +333 -0
  513. package/package.json +21 -17
  514. package/schemas/akm-asset-envelope.json +93 -0
  515. package/schemas/akm-config.json +4636 -0
  516. package/schemas/akm-task.json +87 -0
  517. package/schemas/akm-workflow.json +373 -0
  518. package/dist/akm-migrate-storage +0 -38
  519. package/dist/assets/help/help-accept.md +0 -12
  520. package/dist/assets/help/help-improve.md +0 -84
  521. package/dist/assets/help/help-proposals.md +0 -17
  522. package/dist/assets/help/help-propose.md +0 -17
  523. package/dist/assets/help/help-reject.md +0 -11
  524. package/dist/assets/profiles/frequent.json +0 -13
  525. package/dist/assets/profiles/recombine-only.json +0 -21
  526. package/dist/assets/profiles/reflect-distill.json +0 -30
  527. package/dist/assets/profiles/synthesize.json +0 -15
  528. package/dist/assets/prompts/procedural-system.md +0 -44
  529. package/dist/assets/prompts/recombine-system.md +0 -40
  530. package/dist/assets/prompts/staleness-detect-system.md +0 -6
  531. package/dist/assets/tasks/core/backup.yml +0 -4
  532. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  533. package/dist/assets/templates/html/default.html +0 -78
  534. package/dist/assets/templates/html/vendor/echarts.min.js +0 -45
  535. package/dist/assets/wiki/index-template.md +0 -12
  536. package/dist/assets/wiki/ingest-workflow-template.md +0 -83
  537. package/dist/assets/wiki/log-template.md +0 -8
  538. package/dist/assets/wiki/schema-template.md +0 -61
  539. package/dist/cli/config-migrate.js +0 -150
  540. package/dist/cli/config-validate.js +0 -39
  541. package/dist/commands/graph/graph-cli.js +0 -124
  542. package/dist/commands/graph/graph.js +0 -487
  543. package/dist/commands/improve/calibration.js +0 -161
  544. package/dist/commands/improve/dedup.js +0 -482
  545. package/dist/commands/improve/extract-watch.js +0 -140
  546. package/dist/commands/improve/hot-probation.js +0 -45
  547. package/dist/commands/improve/improve-auto-accept.js +0 -276
  548. package/dist/commands/improve/improve-profiles.js +0 -168
  549. package/dist/commands/improve/procedural.js +0 -398
  550. package/dist/commands/improve/recombine.js +0 -818
  551. package/dist/commands/improve/schema-similarity-gate.js +0 -168
  552. package/dist/commands/lint/agent-linter.js +0 -44
  553. package/dist/commands/lint/command-linter.js +0 -44
  554. package/dist/commands/lint/default-linter.js +0 -16
  555. package/dist/commands/lint/fact-linter.js +0 -39
  556. package/dist/commands/lint/knowledge-linter.js +0 -16
  557. package/dist/commands/lint/memory-linter.js +0 -61
  558. package/dist/commands/lint/registry.js +0 -41
  559. package/dist/commands/lint/skill-linter.js +0 -45
  560. package/dist/commands/lint/task-linter.js +0 -50
  561. package/dist/commands/lint/workflow-linter.js +0 -81
  562. package/dist/commands/proposal/legacy-import.js +0 -115
  563. package/dist/commands/sources/history.js +0 -196
  564. package/dist/commands/tasks/default-tasks.js +0 -186
  565. package/dist/commands/wiki-cli.js +0 -292
  566. package/dist/core/asset/asset-registry.js +0 -76
  567. package/dist/core/asset/asset-spec.js +0 -259
  568. package/dist/core/config/config-migration.js +0 -602
  569. package/dist/core/deep-merge.js +0 -38
  570. package/dist/core/eval/rank-metrics.js +0 -113
  571. package/dist/core/ripgrep/install.js +0 -163
  572. package/dist/core/ripgrep/resolve.js +0 -81
  573. package/dist/indexer/db/db.js +0 -1413
  574. package/dist/indexer/manifest.js +0 -170
  575. package/dist/indexer/passes/metadata-contributors.js +0 -31
  576. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -94
  577. package/dist/integrations/harnesses/opencode-sdk/index.js +0 -49
  578. package/dist/llm/call-ai.js +0 -62
  579. package/dist/llm/memory-infer-impl.js +0 -138
  580. package/dist/output/shapes/distill.js +0 -14
  581. package/dist/output/shapes/history.js +0 -11
  582. package/dist/output/text/distill.js +0 -6
  583. package/dist/output/text/enable-disable.js +0 -8
  584. package/dist/output/text/history.js +0 -6
  585. package/dist/output/text/wiki.js +0 -16
  586. package/dist/registry/build-index.js +0 -386
  587. package/dist/scripts/migrate-storage.js +0 -19108
  588. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +0 -9411
  589. package/dist/scripts/migrations/v16-to-v17.js +0 -141
  590. package/dist/setup/legacy-config.js +0 -106
  591. package/dist/storage/repositories/consolidation-repository.js +0 -38
  592. package/dist/storage/repositories/recombine-repository.js +0 -213
  593. package/dist/wiki/wiki-templates.js +0 -15
  594. package/dist/wiki/wiki.js +0 -1012
  595. package/dist/workflows/db.js +0 -215
  596. package/docs/data-and-telemetry.md +0 -226
  597. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/registry.js +0 -0
  598. /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,891 +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
- // ── Setup-derived recommendations ──────────────────────────────────────────
836
- /**
837
- * Cron-style schedule hints derived by `akm setup --reset-recommended`.
838
- *
839
- * These record the *recommended* cadence for the improve and index background
840
- * tasks. They are advisory metadata persisted into config so the value
841
- * survives a re-run; actual task scheduling lives in the tasks subsystem.
842
- */
843
- export const SetupTaskSchedulesSchema = z
844
- .object({
845
- improve: z.string().min(1).optional(),
846
- index: z.string().min(1).optional(),
847
- })
848
- .passthrough();
849
- export const SetupConfigSchema = z
850
- .object({
851
- taskSchedules: SetupTaskSchedulesSchema.optional(),
852
- })
853
- .passthrough();
854
- // ── Top-level AkmConfig ────────────────────────────────────────────────────
855
- /**
856
- * Base object schema used both as the top-level shape and as the source of
857
- * truth for {@link listTopLevelConfigKeys}. {@link AkmConfigSchema} wraps this
858
- * with cross-field refinements (`.superRefine()`).
859
- *
860
- * All fields validate loudly — typos and shape errors throw at load time. The
861
- * legacy parser's warn-and-drop tolerance was a frequent source of silent
862
- * configuration loss; the migration module ({@link migrateConfigShape}) handles
863
- * one-time 0.7→0.8 input transforms before the schema sees the value.
864
- */
865
- export const AkmConfigShape = {
866
- configVersion: z.union([z.string().min(1), z.number()]).optional(),
867
- profiles: ProfilesSchema.optional(),
868
- defaults: DefaultsSchema.optional(),
869
- // Global model-alias tiers: alias → platform → exact model string, with a
870
- // reserved `"*"` platform key as fallback. Lets workflows/callers name a
871
- // semantic tier ("fast", "deep") that resolves per-harness at dispatch
872
- // time. Values are literal model strings, never other aliases (one
873
- // resolution level). Platform keys match the platform string a command
874
- // builder resolves against ("claude", "opencode", "opencode-sdk", or a
875
- // custom profile's name for the default builder) — unknown keys are inert.
876
- // Precedence: profile modelAliases > this table > built-in aliases.
877
- modelAliases: z.record(z.string().min(1), z.record(z.string().min(1), z.string().min(1))).optional(),
878
- stashDir: nonEmptyString.optional(),
879
- semanticSearchMode: z.enum(["off", "auto"]).default("auto"),
880
- embedding: EmbeddingConnectionConfigSchema.optional(),
881
- index: IndexConfigSchema.optional(),
882
- // The `installed[]` shape is OWNED by the registry (`InstalledStashEntry`):
883
- // its `source` is the 4-value `InstallKind` produced by the registry ref
884
- // parser, and installed entries never carry the extra passthrough keys. The
885
- // schema still validates entries at runtime, but its OUTPUT type is pinned to
886
- // the domain type so config consumers get the registry `InstalledStashEntry`
887
- // (not a looser schema-local mirror) — the single-source-of-truth boundary.
888
- installed: z.array(InstalledStashEntrySchema).optional(),
889
- registries: z.array(RegistryConfigEntrySchema).optional(),
890
- sources: z.array(SourceConfigEntrySchema).optional(),
891
- output: OutputConfigSchema.optional(),
892
- writable: z.boolean().optional(),
893
- defaultWriteTarget: nonEmptyString.optional(),
894
- search: SearchConfigSchema.optional(),
895
- feedback: FeedbackConfigSchema.optional(),
896
- archiveRetentionDays: nonNegativeNumber.optional(),
897
- improve: ImproveConfigSchema.optional(),
898
- setup: SetupConfigSchema.optional(),
899
- };
900
- export const AkmConfigBaseSchema = z.object(AkmConfigShape).passthrough();
901
- export const AkmConfigSchema = AkmConfigBaseSchema.superRefine((config, ctx) => {
902
- // #464.a: defaultWriteTarget must name a configured source when sources
903
- // are present. With no sources configured, error out instead of silently
904
- // 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).
905
306
  if (config.defaultWriteTarget !== undefined) {
906
- const knownNames = (config.sources ?? [])
907
- .map((s) => s.name)
908
- .filter((n) => typeof n === "string" && n.length > 0);
307
+ const knownNames = Object.keys(config.bundles ?? {});
909
308
  if (knownNames.length === 0) {
910
309
  ctx.addIssue({
911
310
  code: z.ZodIssueCode.custom,
912
311
  path: ["defaultWriteTarget"],
913
- message: `defaultWriteTarget "${config.defaultWriteTarget}" cannot be resolved: no sources configured. ` +
914
- "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.",
915
314
  });
916
315
  }
917
316
  else if (!knownNames.includes(config.defaultWriteTarget)) {
918
317
  ctx.addIssue({
919
318
  code: z.ZodIssueCode.custom,
920
319
  path: ["defaultWriteTarget"],
921
- 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(", ")}.`,
922
321
  });
923
322
  }
924
323
  }