akm-cli 0.10.261012-beta → 0.10.261014-beta

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 (518) hide show
  1. package/CHANGELOG.md +710 -3
  2. package/README.md +15 -0
  3. package/SECURITY.md +11 -10
  4. package/STABILITY.md +47 -51
  5. package/dist/assets/hints/cli-hints-full.md +21 -19
  6. package/dist/assets/hints/cli-hints-short.md +3 -4
  7. package/dist/assets/opencode/akm-plugin.js +177 -0
  8. package/dist/assets/stash-skeleton/README.md +1 -1
  9. package/dist/assets/tasks/core/improve.yml +1 -1
  10. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +1 -1
  11. package/dist/assets/ui/config.js +27 -10
  12. package/dist/assets/workflows/workflow-template.md +2 -2
  13. package/dist/cli/clack.js +3 -1
  14. package/dist/cli/selection.js +33 -0
  15. package/dist/cli/shared.js +14 -1
  16. package/dist/cli/unknown-flags.js +6 -37
  17. package/dist/cli.js +69 -48
  18. package/dist/commands/agent/agent-dispatch.js +17 -25
  19. package/dist/commands/agent/contribute-cli.js +9 -8
  20. package/dist/commands/command/command-cli.js +4 -8
  21. package/dist/commands/command/command-execution.js +25 -39
  22. package/dist/commands/command/execution-source-loader.js +10 -14
  23. package/dist/commands/completions.js +1 -1
  24. package/dist/commands/config-cli.js +14 -5
  25. package/dist/commands/env/child-env.js +12 -14
  26. package/dist/commands/env/env-binding.js +47 -58
  27. package/dist/commands/env/env-cli.js +22 -24
  28. package/dist/commands/env/env.js +10 -58
  29. package/dist/commands/env/secret-cli.js +11 -29
  30. package/dist/commands/env/secret.js +3 -10
  31. package/dist/commands/health/checks.js +10 -849
  32. package/dist/commands/health/connections-and-harnesses-check.js +109 -0
  33. package/dist/commands/health/egress.js +3 -3
  34. package/dist/commands/health/html-report.js +9 -10
  35. package/dist/commands/health/plugin-staleness.js +18 -11
  36. package/dist/commands/health/report-view-model.js +16 -12
  37. package/dist/commands/health/scheduler-binary.js +1 -1
  38. package/dist/commands/health/semantic-search-check.js +85 -0
  39. package/dist/commands/health/type-directory-check.js +0 -3
  40. package/dist/commands/health.js +30 -42
  41. package/dist/commands/improve/consolidate/pair-pass.js +10 -25
  42. package/dist/commands/improve/consolidate.js +10 -15
  43. package/dist/commands/improve/distill.js +5 -17
  44. package/dist/commands/improve/eligibility.js +1 -3
  45. package/dist/commands/improve/execution.js +37 -40
  46. package/dist/commands/improve/extract-cli.js +6 -11
  47. package/dist/commands/improve/extract-prompt.js +2 -2
  48. package/dist/commands/improve/extract.js +67 -97
  49. package/dist/commands/improve/improve-cli.js +22 -128
  50. package/dist/commands/improve/improve-report.js +1 -1
  51. package/dist/commands/improve/improve-strategies.js +25 -108
  52. package/dist/commands/improve/improve.js +10 -14
  53. package/dist/commands/improve/loop-stages.js +3 -0
  54. package/dist/commands/improve/memory/memory-improve.js +1 -1
  55. package/dist/commands/improve/preparation.js +8 -13
  56. package/dist/commands/improve/reflect.js +203 -332
  57. package/dist/commands/improve/retrieval-scope.js +1 -1
  58. package/dist/commands/improve/session-asset.js +3 -2
  59. package/dist/commands/improve/stage.js +87 -115
  60. package/dist/commands/lint/base-linter.js +3 -3
  61. package/dist/commands/lint/env-key-rules.js +7 -6
  62. package/dist/commands/lint/index.js +3 -3
  63. package/dist/commands/models-cli.js +103 -63
  64. package/dist/commands/proposal/drain.js +26 -39
  65. package/dist/commands/proposal/proposal-cli.js +14 -18
  66. package/dist/commands/proposal/propose-cli.js +4 -2
  67. package/dist/commands/proposal/propose.js +37 -64
  68. package/dist/commands/proposal/repository.js +17 -17
  69. package/dist/commands/proposal/validators/proposal-validators.js +3 -6
  70. package/dist/commands/read/curate.js +10 -6
  71. package/dist/commands/read/registry-search.js +2 -48
  72. package/dist/commands/read/search-cli.js +1 -8
  73. package/dist/commands/read/search.js +5 -6
  74. package/dist/commands/read/show.js +6 -29
  75. package/dist/commands/remember.js +25 -40
  76. package/dist/commands/sources/add-cli.js +8 -63
  77. package/dist/commands/sources/bundle-cli.js +8 -9
  78. package/dist/commands/sources/bundle-config-ops.js +2 -2
  79. package/dist/commands/sources/info.js +12 -1
  80. package/dist/commands/sources/init.js +1 -1
  81. package/dist/commands/sources/installed-stashes.js +119 -63
  82. package/dist/commands/sources/migration-help.js +1 -1
  83. package/dist/commands/sources/plugin-upgrade.js +8 -15
  84. package/dist/commands/sources/schema-repair.js +4 -10
  85. package/dist/commands/sources/source-add.js +2 -2
  86. package/dist/commands/sources/source-clone.js +31 -7
  87. package/dist/commands/sources/source-manage.js +4 -4
  88. package/dist/commands/targets.js +175 -0
  89. package/dist/commands/tasks/explain.js +104 -69
  90. package/dist/commands/tasks/tasks-cli.js +9 -11
  91. package/dist/commands/tasks/tasks.js +75 -97
  92. package/dist/commands/ui/api.js +11 -3
  93. package/dist/commands/ui/redact.js +5 -31
  94. package/dist/commands/ui/workspace-actions.js +1 -1
  95. package/dist/commands/workflow/plan.js +48 -9
  96. package/dist/commands/workflow-cli.js +40 -45
  97. package/dist/core/adapter/adapters/agent-skills-adapter.js +10 -11
  98. package/dist/core/adapter/adapters/akm-adapter.js +36 -41
  99. package/dist/core/adapter/adapters/akm-lint.js +10 -12
  100. package/dist/core/adapter/adapters/akm-metadata.js +2 -2
  101. package/dist/core/adapter/adapters/akm-task-adapter.js +8 -9
  102. package/dist/core/adapter/adapters/akm-workflow-adapter.js +5 -5
  103. package/dist/core/adapter/adapters/claude-adapter.js +1 -1
  104. package/dist/core/adapter/adapters/dotenv-adapter.js +7 -6
  105. package/dist/core/adapter/adapters/generic-files-adapter.js +5 -5
  106. package/dist/core/adapter/adapters/index.js +3 -4
  107. package/dist/core/adapter/adapters/llm-wiki-adapter.js +12 -14
  108. package/dist/core/adapter/adapters/okf-adapter.js +8 -10
  109. package/dist/core/adapter/adapters/opencode-adapter.js +1 -1
  110. package/dist/core/adapter/adapters/shared.js +3 -3
  111. package/dist/core/adapter/adapters/tool-dir-shared.js +7 -25
  112. package/dist/core/adapter/adapters/website-snapshot-adapter.js +3 -3
  113. package/dist/core/adapter/detect-adapter.js +1 -1
  114. package/dist/core/adapter/execution-source.js +13 -35
  115. package/dist/core/adapter/registry.js +1 -1
  116. package/dist/core/adapter/validate-context.js +2 -4
  117. package/dist/core/asset/asset-placement.js +1 -1
  118. package/dist/core/asset/asset-ref.js +3 -3
  119. package/dist/core/asset/resolve-ref.js +5 -0
  120. package/dist/core/bundle-rename.js +12 -4
  121. package/dist/core/closest-match.js +37 -0
  122. package/dist/core/common.js +37 -57
  123. package/dist/core/config/config-io.js +26 -18
  124. package/dist/core/config/config-schema.js +19 -106
  125. package/dist/core/config/config-sources.js +3 -3
  126. package/dist/core/config/config-types.js +4 -4
  127. package/dist/core/config/config-walker.js +50 -77
  128. package/dist/core/config/config.js +438 -109
  129. package/dist/core/config/convert-engine-references.js +114 -0
  130. package/dist/core/config/convert-engines-shared.js +40 -0
  131. package/dist/core/config/convert-engines.js +300 -0
  132. package/dist/core/config/convert-models-overlay.js +137 -0
  133. package/dist/core/config/credentials.js +304 -0
  134. package/dist/core/config/engine-semantics.js +3 -3
  135. package/dist/core/config/migrate-keys.js +222 -0
  136. package/dist/core/config/model-aliases.js +37 -0
  137. package/dist/core/config/models-file.js +34 -0
  138. package/dist/core/config/retired-engines.js +53 -0
  139. package/dist/core/config/schema/connections.js +41 -0
  140. package/dist/core/config/schema/embedding.js +2 -2
  141. package/dist/core/config/schema/harnesses.js +41 -0
  142. package/dist/core/config/schema/improve-processes.js +9 -28
  143. package/dist/core/config/schema/improve.js +36 -1
  144. package/dist/core/config/schema/index-config.js +18 -20
  145. package/dist/core/config/schema/primitives.js +42 -17
  146. package/dist/core/config/schema/search.js +6 -11
  147. package/dist/core/config/schema/sources-bundles.js +9 -9
  148. package/dist/core/config/schema/workflow.js +2 -6
  149. package/dist/core/config/unusable-entries.js +66 -0
  150. package/dist/core/content-safety.js +3 -6
  151. package/dist/core/endpoint-url.js +47 -0
  152. package/dist/core/env-secret-ref.js +84 -73
  153. package/dist/core/errors.js +10 -16
  154. package/dist/core/extra-params.js +3 -3
  155. package/dist/core/improve-result.js +8 -9
  156. package/dist/core/json-schema.js +1 -1
  157. package/dist/core/lesson-lint.js +1 -1
  158. package/dist/core/llm-inference.js +80 -0
  159. package/dist/core/parse.js +2 -2
  160. package/dist/core/paths.js +33 -10
  161. package/dist/core/recognition-util.js +8 -10
  162. package/dist/core/redaction.js +94 -55
  163. package/dist/core/ref-token.js +82 -0
  164. package/dist/core/secret-value.js +14 -0
  165. package/dist/core/state/migrations.js +17 -22
  166. package/dist/core/structured.js +2 -68
  167. package/dist/core/subprocess.js +63 -22
  168. package/dist/core/type-presentation.js +10 -14
  169. package/dist/core/write-source.js +20 -4
  170. package/dist/execution/input-contract.js +3 -6
  171. package/dist/execution/notices.js +49 -0
  172. package/dist/execution/resolved-request.js +24 -40
  173. package/dist/execution/source.js +21 -45
  174. package/dist/execution/target-ref.js +2 -2
  175. package/dist/indexer/bundle-identity-guard.js +6 -5
  176. package/dist/indexer/ensure-index.js +1 -1
  177. package/dist/indexer/index-writer-lock.js +1 -1
  178. package/dist/indexer/indexer.js +46 -42
  179. package/dist/indexer/installations.js +12 -22
  180. package/dist/indexer/lookup/adapter-concept-owner.js +40 -14
  181. package/dist/indexer/materialize-embeddings.js +20 -16
  182. package/dist/indexer/passes/memory-inference.js +21 -50
  183. package/dist/indexer/passes/metadata.js +5 -5
  184. package/dist/indexer/scan/doc-to-entry.js +1 -1
  185. package/dist/indexer/scan/drain-dir.js +9 -11
  186. package/dist/indexer/search/db-search.js +9 -7
  187. package/dist/indexer/search/search-source.js +42 -22
  188. package/dist/indexer/usage/usage-events.js +2 -3
  189. package/dist/indexer/walk/index-context.js +2 -1
  190. package/dist/indexer/walk/matchers.js +3 -11
  191. package/dist/indexer/walk/path-resolver.js +13 -68
  192. package/dist/indexer/walk/walker.js +32 -26
  193. package/dist/integrations/agent/akm-read-tools.js +81 -0
  194. package/dist/integrations/agent/config.js +1 -1
  195. package/dist/integrations/agent/detect.js +32 -36
  196. package/dist/integrations/agent/execution.js +130 -428
  197. package/dist/integrations/agent/index.js +1 -1
  198. package/dist/integrations/agent/profiles.js +24 -28
  199. package/dist/integrations/agent/prompts.js +17 -30
  200. package/dist/integrations/agent/request-lowering.js +67 -101
  201. package/dist/integrations/agent/resolution.js +332 -0
  202. package/dist/integrations/agent/runner-dispatch.js +156 -181
  203. package/dist/integrations/agent/runner.js +20 -70
  204. package/dist/integrations/agent/runtime-port.js +4 -0
  205. package/dist/integrations/agent/runtime.js +10 -0
  206. package/dist/integrations/agent/spawn.js +65 -190
  207. package/dist/integrations/harnesses/aider/index.js +19 -39
  208. package/dist/integrations/harnesses/amazonq/index.js +17 -32
  209. package/dist/integrations/harnesses/claude/index.js +59 -28
  210. package/dist/integrations/harnesses/claude/session-log.js +7 -5
  211. package/dist/integrations/harnesses/codex/index.js +19 -31
  212. package/dist/integrations/harnesses/copilot/index.js +17 -35
  213. package/dist/integrations/harnesses/gemini/index.js +18 -34
  214. package/dist/integrations/harnesses/harness.js +36 -0
  215. package/dist/integrations/harnesses/index.js +20 -51
  216. package/dist/integrations/harnesses/opencode/agent-builder.js +132 -100
  217. package/dist/integrations/harnesses/opencode/index.js +11 -33
  218. package/dist/integrations/harnesses/opencode/roots.js +50 -0
  219. package/dist/integrations/harnesses/opencode/runtime/binary.js +206 -0
  220. package/dist/integrations/harnesses/opencode/runtime/command.js +152 -0
  221. package/dist/integrations/harnesses/opencode/runtime/index.js +177 -0
  222. package/dist/integrations/harnesses/opencode/runtime/tar.js +107 -0
  223. package/dist/integrations/harnesses/opencode/session-log-v1.js +15 -7
  224. package/dist/integrations/harnesses/opencode/session-log-v2.js +72 -48
  225. package/dist/integrations/harnesses/opencode/session-log.js +58 -137
  226. package/dist/integrations/harnesses/opencode/version.js +34 -53
  227. package/dist/integrations/harnesses/openhands/index.js +13 -37
  228. package/dist/integrations/harnesses/pi/index.js +21 -32
  229. package/dist/integrations/lockfile.js +1 -1
  230. package/dist/integrations/session-logs/index.js +11 -31
  231. package/dist/llm/client.js +93 -111
  232. package/dist/llm/embedder.js +19 -34
  233. package/dist/llm/embedders/connection.js +31 -0
  234. package/dist/llm/embedders/local-model-files.js +56 -0
  235. package/dist/llm/embedders/local.js +146 -115
  236. package/dist/llm/embedders/remote.js +26 -23
  237. package/dist/llm/embedders/types.js +1 -1
  238. package/dist/llm/index-passes.js +33 -39
  239. package/dist/llm/memory-infer.js +67 -77
  240. package/dist/llm/model-list.js +35 -0
  241. package/dist/llm/rerank-client.js +8 -14
  242. package/dist/llm/structured-call.js +45 -99
  243. package/dist/llm/structured.js +60 -0
  244. package/dist/output/renderers.js +2 -8
  245. package/dist/output/shapes/helpers.js +18 -44
  246. package/dist/output/shapes/passthrough.js +1 -0
  247. package/dist/output/shapes.js +1 -7
  248. package/dist/output/text/command-format.js +41 -39
  249. package/dist/output/text/health-format.js +8 -2
  250. package/dist/output/text/helpers.js +1 -1
  251. package/dist/output/text/migrate.js +12 -0
  252. package/dist/output/text/models.js +3 -2
  253. package/dist/output/text/registry-commands.js +1 -2
  254. package/dist/output/text/show-directives.js +16 -28
  255. package/dist/output/text/show-format.js +0 -2
  256. package/dist/output/text/targets-table.js +70 -0
  257. package/dist/output/text/workflow-format.js +44 -15
  258. package/dist/output/text.js +1 -1
  259. package/dist/registry/providers/skills-sh.js +1 -28
  260. package/dist/registry/providers/static-index.js +1 -71
  261. package/dist/runtime.js +6 -2
  262. package/dist/scripts/akm-migrate-node.js +47095 -44905
  263. package/dist/scripts/akm-migrate.js +46946 -44755
  264. package/dist/setup/detect.js +28 -273
  265. package/dist/setup/endpoint.js +36 -0
  266. package/dist/setup/semantic-assets.js +13 -19
  267. package/dist/setup/setup-flags.js +77 -0
  268. package/dist/setup/setup-plan.js +144 -0
  269. package/dist/setup/setup.js +134 -358
  270. package/dist/setup/steps/connection-shared.js +49 -101
  271. package/dist/setup/steps/connection.js +121 -524
  272. package/dist/setup/steps/embeddings.js +151 -0
  273. package/dist/setup/steps/harnesses.js +118 -0
  274. package/dist/setup/steps/platforms.js +2 -81
  275. package/dist/setup/steps/tasks.js +1 -2
  276. package/dist/setup/steps.js +10 -0
  277. package/dist/setup/summary.js +67 -0
  278. package/dist/sources/freshness.js +16 -21
  279. package/dist/sources/providers/git-provider.js +2 -9
  280. package/dist/sources/providers/provider-utils.js +13 -5
  281. package/dist/sources/resolve.js +12 -16
  282. package/dist/sources/snapshot-fetchers/robots.js +11 -12
  283. package/dist/sources/snapshot-fetchers/secret-seam.js +2 -4
  284. package/dist/sources/snapshot-fetchers/website-ingest.js +8 -15
  285. package/dist/sources/snapshot-fetchers/x.js +1 -1
  286. package/dist/standalone/onnxruntime-library.js +52 -0
  287. package/dist/storage/repositories/improve-ledger-repository.js +1 -1
  288. package/dist/storage/repositories/improve-runs-repository.js +0 -13
  289. package/dist/storage/repositories/index-entries-repository.js +2 -2
  290. package/dist/storage/repositories/proposals-repository.js +2 -2
  291. package/dist/storage/repositories/task-history-repository.js +2 -2
  292. package/dist/storage/repositories/workflow-runs-repository.js +44 -129
  293. package/dist/storage/sqlite-read-snapshot.js +33 -29
  294. package/dist/tasks/embedded.js +1 -1
  295. package/dist/tasks/log-redaction.js +7 -119
  296. package/dist/tasks/prepare/prepare-support.js +8 -7
  297. package/dist/tasks/prepare/prepare.js +8 -9
  298. package/dist/tasks/prepare/script-capture.js +9 -25
  299. package/dist/tasks/run/attempt-lifecycle.js +17 -35
  300. package/dist/tasks/run/load-task.js +54 -27
  301. package/dist/tasks/run/run-command-task.js +15 -10
  302. package/dist/tasks/run/run-native-task.js +8 -21
  303. package/dist/tasks/run/run-task.js +5 -8
  304. package/dist/tasks/run/run-workflow-task.js +14 -21
  305. package/dist/tasks/run/task-history.js +9 -17
  306. package/dist/tasks/run/task-log.js +1 -5
  307. package/dist/tasks/scheduler-invocation.js +13 -18
  308. package/dist/tasks/scheduler-sync.js +0 -1
  309. package/dist/tasks/source/bounded-document.js +31 -117
  310. package/dist/tasks/source/parse-task-source.js +0 -1
  311. package/dist/tasks/source/project-v4.js +5 -3
  312. package/dist/tasks/source/task-input-diagnostics.js +3 -4
  313. package/dist/tasks/source/task-source-v4.js +48 -72
  314. package/dist/tasks/source-v3.js +2 -5
  315. package/dist/tasks/task-run-reserved-flags.js +1 -2
  316. package/dist/workflows/authoring/authoring.js +2 -7
  317. package/dist/workflows/compile.js +1 -15
  318. package/dist/workflows/concurrency-policy.js +4 -4
  319. package/dist/workflows/exec/child-workflow.js +1 -4
  320. package/dist/workflows/exec/dispatch-redaction.js +35 -86
  321. package/dist/workflows/exec/environment.js +13 -77
  322. package/dist/workflows/exec/exec-unit.js +14 -55
  323. package/dist/workflows/exec/frozen-judge.js +26 -28
  324. package/dist/workflows/exec/native-executor.js +126 -71
  325. package/dist/workflows/exec/run-workflow.js +38 -42
  326. package/dist/workflows/exec/step-work.js +13 -17
  327. package/dist/workflows/exec/unit-dispatch.js +7 -21
  328. package/dist/workflows/freeze/environment.js +14 -119
  329. package/dist/workflows/freeze/freeze.js +14 -33
  330. package/dist/workflows/freeze/step-values.js +6 -13
  331. package/dist/workflows/freeze/targets/child-workflow.js +3 -5
  332. package/dist/workflows/freeze/targets/command.js +9 -15
  333. package/dist/workflows/freeze/targets/shell.js +0 -3
  334. package/dist/workflows/freeze/task-bindings.js +0 -1
  335. package/dist/workflows/parser.js +50 -59
  336. package/dist/workflows/plan.js +8 -2
  337. package/dist/workflows/renderer.js +83 -9
  338. package/dist/workflows/resource-limits.js +2 -2
  339. package/dist/workflows/runtime/run-plan.js +38 -23
  340. package/dist/workflows/runtime/runs.js +55 -93
  341. package/dist/workflows/runtime/workflow-asset-loader.js +55 -86
  342. package/dist/workflows/source-files.js +7 -19
  343. package/docs/README.md +10 -16
  344. package/docs/architecture/README.md +2 -37
  345. package/docs/architecture/adapters.md +2 -2
  346. package/docs/architecture/akm-core-principles.md +256 -118
  347. package/docs/architecture/architecture.md +283 -99
  348. package/docs/architecture/improvement.md +8 -2
  349. package/docs/architecture/internals/classification.md +1 -3
  350. package/docs/architecture/internals/fresh-host-rebuild-runbook.md +7 -6
  351. package/docs/architecture/internals/health-advisories.md +35 -74
  352. package/docs/architecture/internals/improve-workflow.md +27 -26
  353. package/docs/architecture/internals/indexing.md +3 -3
  354. package/docs/architecture/internals/storage-locations.md +48 -15
  355. package/docs/architecture/persisted-data-compat.md +10 -6
  356. package/docs/architecture/specs/okf-support.md +9 -11
  357. package/docs/architecture/specs/ref.md +7 -9
  358. package/docs/architecture/testing/testing-workflow.md +172 -52
  359. package/docs/architecture/workflow-engine.md +51 -34
  360. package/docs/guides/README.md +0 -1
  361. package/docs/guides/author-bundles.md +15 -20
  362. package/docs/guides/author-workflows.md +93 -71
  363. package/docs/guides/bundles.md +20 -16
  364. package/docs/guides/discover-and-load.md +1 -1
  365. package/docs/guides/getting-started.md +49 -3
  366. package/docs/guides/improve-the-library.md +10 -4
  367. package/docs/guides/recipes/headless-install.md +11 -16
  368. package/docs/guides/run-workflows.md +29 -20
  369. package/docs/guides/scheduling.md +2 -2
  370. package/docs/guides/use-with-any-agent.md +20 -22
  371. package/docs/integration/bundling-akm.md +19 -9
  372. package/docs/maintainers/README.md +2 -6
  373. package/docs/maintainers/release-checklist.md +73 -126
  374. package/docs/migration/README.md +10 -3
  375. package/docs/migration/release-notes/0.10.0.md +81 -16
  376. package/docs/migration/release-notes/0.6.0.md +1 -1
  377. package/docs/migration/release-notes/0.7.0.md +1 -1
  378. package/docs/migration/release-notes/0.9.0.md +1 -1
  379. package/docs/migration/release-notes/README.md +1 -1
  380. package/docs/migration/v0.9-to-v0.10.md +300 -60
  381. package/docs/migration/v0.9.1-to-v0.9.2.md +6 -5
  382. package/docs/posts/config-v2-lessons-16.md +1 -1
  383. package/docs/posts/llm-mode-reflect-13.md +1 -1
  384. package/docs/posts/release-0.6.0.md +11 -11
  385. package/docs/posts/release-0.8.0.md +4 -4
  386. package/docs/posts/workflows-vaults-09.md +1 -1
  387. package/docs/reference/README.md +1 -1
  388. package/docs/reference/asset-types.md +2 -2
  389. package/docs/reference/cli.md +477 -388
  390. package/docs/reference/configuration.md +569 -550
  391. package/docs/reference/data-and-telemetry.md +9 -6
  392. package/docs/reference/env-and-secrets.md +72 -15
  393. package/docs/reference/refs.md +3 -20
  394. package/docs/reference/registry.md +12 -38
  395. package/docs/reference/supported-formats.md +1 -1
  396. package/docs/reference/tasks.md +22 -11
  397. package/docs/reference/ui-api.md +14 -9
  398. package/docs/reference/workflow-schema.md +199 -154
  399. package/docs/reference/workflows.md +4 -6
  400. package/package.json +9 -11
  401. package/schemas/akm-config.json +1696 -2922
  402. package/schemas/akm-task.json +4 -4
  403. package/schemas/akm-workflow.json +36 -9
  404. package/ROADMAP.md +0 -112
  405. package/dist/assets/models.json +0 -35
  406. package/dist/commands/health/engine-usage.js +0 -45
  407. package/dist/commands/sources/dangerous-env-audit.js +0 -134
  408. package/dist/core/activation-policy.js +0 -67
  409. package/dist/core/config/schema/engines.js +0 -160
  410. package/dist/core/config/schema/execution.js +0 -23
  411. package/dist/core/spawn-env.js +0 -268
  412. package/dist/execution/directory-identity.js +0 -78
  413. package/dist/integrations/agent/builder-shared.js +0 -33
  414. package/dist/integrations/agent/builders.js +0 -43
  415. package/dist/integrations/agent/engine-fallback.js +0 -59
  416. package/dist/integrations/agent/engine-resolution.js +0 -333
  417. package/dist/integrations/agent/model-map.js +0 -475
  418. package/dist/integrations/harnesses/aider/agent-builder.js +0 -107
  419. package/dist/integrations/harnesses/aider/result-extractor.js +0 -53
  420. package/dist/integrations/harnesses/amazonq/agent-builder.js +0 -138
  421. package/dist/integrations/harnesses/amazonq/result-extractor.js +0 -48
  422. package/dist/integrations/harnesses/claude/agent-builder.js +0 -106
  423. package/dist/integrations/harnesses/claude/config-import.js +0 -67
  424. package/dist/integrations/harnesses/claude/result-extractor.js +0 -67
  425. package/dist/integrations/harnesses/codex/agent-builder.js +0 -150
  426. package/dist/integrations/harnesses/codex/result-extractor.js +0 -110
  427. package/dist/integrations/harnesses/copilot/agent-builder.js +0 -117
  428. package/dist/integrations/harnesses/copilot/result-extractor.js +0 -178
  429. package/dist/integrations/harnesses/gemini/agent-builder.js +0 -114
  430. package/dist/integrations/harnesses/gemini/result-extractor.js +0 -149
  431. package/dist/integrations/harnesses/opencode/config-import.js +0 -149
  432. package/dist/integrations/harnesses/opencode/model-config.js +0 -33
  433. package/dist/integrations/harnesses/opencode/model-work-agent.js +0 -115
  434. package/dist/integrations/harnesses/opencode/session-log-types.js +0 -12
  435. package/dist/integrations/harnesses/opencode-sdk/index.js +0 -44
  436. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +0 -1052
  437. package/dist/integrations/harnesses/opencode-sdk/v1-adapter.js +0 -199
  438. package/dist/integrations/harnesses/opencode-sdk/v2-adapter.js +0 -254
  439. package/dist/integrations/harnesses/opencode-sdk/wire.js +0 -66
  440. package/dist/integrations/harnesses/openhands/agent-builder.js +0 -120
  441. package/dist/integrations/harnesses/openhands/result-extractor.js +0 -135
  442. package/dist/integrations/harnesses/pi/agent-builder.js +0 -89
  443. package/dist/integrations/harnesses/pi/result-extractor.js +0 -168
  444. package/dist/integrations/harnesses/shared.js +0 -18
  445. package/dist/integrations/harnesses/types.js +0 -55
  446. package/dist/llm/feature-gate.js +0 -115
  447. package/dist/output/shapes/models-list.js +0 -16
  448. package/dist/output/shapes/registry-search.js +0 -10
  449. package/dist/setup/detected-engines.js +0 -136
  450. package/dist/setup/engine-config.js +0 -97
  451. package/dist/setup/harness-config-import.js +0 -47
  452. package/dist/setup/providers.js +0 -14
  453. package/dist/setup/steps/semantic.js +0 -39
  454. package/dist/workflows/authoring/scope-key.js +0 -79
  455. package/dist/workflows/exec/lowering-notices.js +0 -23
  456. package/dist/workflows/source-semantics.js +0 -110
  457. package/docs/architecture/0.10-process-rules.md +0 -832
  458. package/docs/architecture/akm-architecture-decision-history.md +0 -2055
  459. package/docs/architecture/comparisons/claude-code-vs-akm-workflows-full.md +0 -550
  460. package/docs/architecture/decisions/0001-no-interpolation-attached-structured-context.md +0 -72
  461. package/docs/architecture/decisions/0002-unit-reuse-and-input-hash-scope.md +0 -142
  462. package/docs/architecture/decisions/0003-child-env-allowlist-and-provenance.md +0 -189
  463. package/docs/architecture/decisions/0004-task-input-contract-and-flag-coercion.md +0 -97
  464. package/docs/architecture/decisions/0005-task-result-vocabulary-and-legacy-read-mapping.md +0 -108
  465. package/docs/architecture/decisions/0006-task-source-version-routing.md +0 -124
  466. package/docs/architecture/decisions/0007-workflow-composition-bounds.md +0 -83
  467. package/docs/architecture/decisions/0008-task-binding-normalization.md +0 -88
  468. package/docs/architecture/decisions/0009-child-run-publication-column-parity.md +0 -80
  469. package/docs/architecture/decisions/0010-driver-protocol-cut.md +0 -36
  470. package/docs/architecture/decisions/0011-engine-run-loop-invariants.md +0 -125
  471. package/docs/architecture/decisions/README.md +0 -62
  472. package/docs/architecture/internals/functional-contract-patterns.md +0 -340
  473. package/docs/architecture/internals/registry-network-boundary.md +0 -57
  474. package/docs/architecture/internals/search.md +0 -248
  475. package/docs/architecture/reviews/env-secret-access.md +0 -319
  476. package/docs/architecture/runtime-boundary-design.md +0 -153
  477. package/docs/architecture/specs/0.9.0-close-out-plan.md +0 -361
  478. package/docs/architecture/specs/0.9.0-decisions.md +0 -532
  479. package/docs/architecture/specs/0.9.0-docs-code-drift-register.md +0 -745
  480. package/docs/architecture/specs/0.9.0-open-items-register.md +0 -135
  481. package/docs/architecture/specs/0.9.0-public-api-issue-backlog.md +0 -740
  482. package/docs/architecture/specs/0.9.0-release-surface-review.md +0 -636
  483. package/docs/architecture/specs/agent-command-engine-model-design.md +0 -513
  484. package/docs/architecture/specs/akm-0.9.0-bundle-adapter-spec.md +0 -501
  485. package/docs/architecture/specs/akm-0.9.0-ref-grammar-decision.md +0 -291
  486. package/docs/architecture/specs/akm-format-neutral-bundle-workspace-spec.md +0 -2059
  487. package/docs/architecture/specs/di-seams-plan.md +0 -539
  488. package/docs/architecture/specs/driver-protocol-keep-or-cut.md +0 -275
  489. package/docs/architecture/specs/fact-asset-type.md +0 -142
  490. package/docs/architecture/specs/improve-collapse-churn-detector-design.md +0 -634
  491. package/docs/architecture/specs/reviews/task-workflow-unification/README.md +0 -72
  492. package/docs/architecture/specs/reviews/task-workflow-unification/findings-consolidated.md +0 -152
  493. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-alternatives.md +0 -352
  494. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-bunqueue.md +0 -300
  495. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-openworkflow.md +0 -402
  496. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-1.md +0 -758
  497. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-2.md +0 -645
  498. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-3.md +0 -602
  499. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-adversarial.md +0 -27
  500. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-architecture.md +0 -27
  501. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-coverage.md +0 -74
  502. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-security.md +0 -15
  503. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-adversarial.md +0 -132
  504. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-verification.md +0 -147
  505. package/docs/architecture/specs/stash-conventions-code-spec.md +0 -275
  506. package/docs/architecture/specs/stash-organization-conventions.md +0 -377
  507. package/docs/architecture/specs/task-workflow-format-unification.md +0 -721
  508. package/docs/architecture/specs/workflow-engine-buy-vs-build.md +0 -231
  509. package/docs/architecture/specs/workflow-format-unification.md +0 -432
  510. package/docs/architecture/testing/manual-testing-checklist.md +0 -3411
  511. package/docs/architecture/testing/okf-v0.2-conformance-runbook.md +0 -924
  512. package/docs/architecture/testing/task-workflow-manual-tests.md +0 -1720
  513. package/docs/guides/claude-code-vs-akm-workflows.md +0 -29
  514. package/docs/guides/knowledge-management.md +0 -18
  515. package/docs/maintainers/curate-workmap.md +0 -335
  516. package/docs/migration/v0.5-to-v0.6.md +0 -759
  517. package/docs/migration/v0.7-to-v0.8.md +0 -1378
  518. package/docs/plans/0.9.2-agent-command-workflow-plan.md +0 -423
@@ -25,58 +25,46 @@ migrate` / `akm upgrade`; ordinary runtime code reads only the current shape.
25
25
  ```jsonc
26
26
  {
27
27
  "configVersion": "0.9.0",
28
- "$schema": "https://itlackey.github.io/akm/schemas/akm-config.json",
29
- "engines": {
30
- "fast": {
31
- "kind": "llm",
32
- "endpoint": "http://localhost:11434/v1/chat/completions",
33
- "model": "qwen3",
34
- "apiKey": "${LOCAL_LLM_API_KEY}"
35
- },
36
- "reviewer": {
37
- "kind": "agent",
38
- "platform": "opencode",
39
- "model": "anthropic/claude-sonnet-4-6"
28
+ "connections": {
29
+ "default": {
30
+ "endpoint": "http://localhost:11434/v1",
31
+ "models": { "fast": "qwen3-8b", "balanced": "qwen3-32b", "reasoning": "qwen3-32b" }
40
32
  }
41
33
  },
42
- "defaults": {
43
- "engine": "reviewer",
44
- "llmEngine": "fast",
45
- "improveStrategy": "default"
46
- },
47
- "workflow": {
48
- "maxConcurrency": 8,
49
- "judgeEngine": "reviewer"
50
- },
51
- "execution": {
52
- "allowedTools": ["read_file", "search"]
53
- },
34
+ "harnesses": { "claude": {} },
35
+ "embedding": { "model": "nomic-embed-text" },
36
+ "workflow": { "maxConcurrency": 8 },
54
37
  "improve": {
38
+ "strategy": "nightly",
39
+ "processes": {
40
+ "memoryInference": { "model": "qwen3-8b", "llm": { "temperature": 0.1 } }
41
+ },
55
42
  "strategies": {
56
43
  "nightly": {
57
- "engine": "fast",
58
- "processes": {
59
- "reflect": {},
60
- "memoryInference": { "model": "qwen3-small", "llm": { "temperature": 0.1 } }
61
- }
44
+ "processes": { "reflect": { "limit": 10 } }
62
45
  }
63
46
  }
64
47
  }
65
48
  }
66
49
  ```
67
50
 
51
+ A config with no connection and no harness is valid: everything that needs no
52
+ model works. [Connections](#connections), [Harnesses](#harnesses) and [Where
53
+ each process runs](#where-each-process-runs) say how models are added.
54
+
68
55
  ## Scheduler activation
69
56
 
70
57
  `scheduler.enabled` is this host's list of scheduled refs, such as
71
- `stash//tasks/nightly`. A ref that is not listed is disabled. On disk each
72
- entry is still written as the `{kind, ref, sourceId}` object 0.9.16 reads,
73
- so that release keeps working against a config this one wrote; in memory it
74
- is the ref.
58
+ `stash//tasks/nightly`, held and written as plain refs. A ref that is not
59
+ listed is disabled, and removing a bundle drops its refs. The older
60
+ `{kind, ref, sourceId}` entries are read as their `ref` and written back
61
+ plain.
75
62
  A config without the list (written before 0.9.17) means "keep what is
76
- installed": the first `akm task sync` fills it from the akm-written native
77
- scheduler rows. The 0.9.17-alpha `{kind, ref, sourceId}` entries are read as
78
- their `ref`. Authored task/workflow files may describe schedules but cannot
79
- put themselves on the list.
63
+ installed": the first `akm task sync`, `akm setup`, `akm task enable`, `akm task
64
+ disable` or `akm task add` fills it from the akm-written native scheduler rows.
65
+ There are no grants, source identities or fire-time checks. Authored
66
+ task/workflow files may describe schedules but cannot put themselves on the
67
+ list.
80
68
 
81
69
  This key is deliberately local: if a config uses `extends`, any `scheduler`
82
70
  section in the base is ignored with a warning. Only the top-level local config
@@ -85,25 +73,129 @@ can activate schedules. Prefer `akm task enable <ref>` and `akm task disable
85
73
  affected bundle. An unscoped `akm task sync` reconciles enabled refs across all
86
74
  enabled configured bundles.
87
75
 
88
- ## Engines
76
+ ## Connections
89
77
 
90
- `engines` is the only public execution map. An engine name is lowercase
91
- kebab-case, at most 63 characters, and cannot start with `akm-`.
78
+ A connection is an OpenAI-compatible endpoint akm sends model requests to, and
79
+ the models it serves. `connections` maps a name to one. A name is lowercase
80
+ kebab-case, at most 63 characters, and cannot start with `akm-`. The
81
+ connection named `default` is the default connection; a lone connection is the
82
+ default whatever it is called. `akm setup` names the first one `default`.
92
83
 
93
- | Kind | Required fields | Use |
94
- | --- | --- | --- |
95
- | `llm` | `endpoint`, `model` | OpenAI-compatible chat completions |
96
- | `agent` | `platform` | A registered dispatch-capable harness |
84
+ ```jsonc
85
+ {
86
+ "connections": {
87
+ "default": {
88
+ "endpoint": "http://localhost:11434/v1",
89
+ "models": {
90
+ "fast": "qwen3-8b",
91
+ "balanced": "qwen3-32b",
92
+ "reasoning": { "model": "glm-5", "reasoning_effort": "high" }
93
+ },
94
+ "top_p": 0.9
95
+ }
96
+ }
97
+ }
98
+ ```
97
99
 
98
- The registered harnesses (generated from the harness registry; do not edit):
100
+ akm reads these keys of a connection:
101
+
102
+ | Key | Meaning |
103
+ | --- | --- |
104
+ | `endpoint` | Required. The OpenAI-compatible base URL (`http://localhost:11434/v1`) or the chat-completions URL itself, starting with `http://` or `https://`. Requests go to `<base>/chat/completions` and the model list is read from `<base>/models`; a query string on the URL (a gateway's `api-version`, say) stays after the path on every request, and a `#fragment` is dropped. A username or password in the URL is warned about; put the key in `apiKey` or `credentials.json`. |
105
+ | `apiKey` | A `$VAR` or `${VAR}` reference to an environment variable. Absent, the key is the one stored in `credentials.json` for the connection and its endpoint. See [Credentials](#credentials). |
106
+ | `apiKeyFile` | Retired. `akm migrate apply` leaves an engine's key file here, as written, when it cannot read the file yet; akm ignores it (never reads it or sends it) and a later `akm migrate apply` moves the key it names into `credentials.json`. |
107
+ | `models` | Alias to model. See [Aliases](#aliases). |
108
+ | `contextLength` | The model's context window in tokens. `akm improve` sizes its prompts from it (consolidate's chunks, reflect's asset content); unset, it assumes 4096. |
109
+ | `timeoutMs` | How long one call may take, in milliseconds; `null` runs unbounded. Unset, a model call stops after 600 000 (ten minutes) and agent work runs until it finishes. The ceiling is 2 147 483 647. |
110
+ | `concurrency` | How many requests a workflow `map` step may have in flight on this connection, clamped to 1–64. Unset, 1 on a loopback endpoint (a local model server runs one inference at a time) and 4 on a remote one. akm never sends it. |
111
+
112
+ Every other key, on the connection or in a model entry, is a request field in
113
+ the endpoint's own spelling and is sent with each request as written (`top_p`,
114
+ `reasoning_effort`, `chat_template_kwargs`, ...). A connection's fields apply to
115
+ all its models and a model entry's override them; an object field on both sides
116
+ (`chat_template_kwargs`) merges key by key. `model`, `messages`, `tools`,
117
+ `stream` and `response_format` are akm's, on a direct call and in akm's own
118
+ runtime alike: the resolved model, the conversation and tool state, whether the
119
+ reply streams and the response format a caller asks for are never replaced. A
120
+ connection or model entry, or an `extraParams` of a call, a process or a
121
+ workflow step, that writes one of them is ignored for that field, with one
122
+ warning (the final fields are checked, whichever layer brought it).
123
+
124
+ A transient failure (an HTTP 5xx, a dropped connection) is retried once, within
125
+ what is left of the call's timeout. A 4xx is not.
126
+
127
+ ### Settings a call asks for
128
+
129
+ A command, agent, workflow step or task can run against any endpoint, so it asks
130
+ for these in akm's words, and akm writes them as the endpoint's fields over the
131
+ connection's own:
132
+
133
+ | Setting | Request field |
134
+ | --- | --- |
135
+ | `temperature` | `temperature` |
136
+ | `maxTokens` | `max_tokens` |
137
+ | `reasoningEffort` (an asset's `effort` is the same setting) | `reasoning_effort` |
138
+ | `enableThinking` | `chat_template_kwargs.enable_thinking` and `enable_thinking` |
139
+
140
+ They are written `akm.inference` (and top-level `temperature` and `effort`) in
141
+ an asset's frontmatter, `llm:` on a workflow step (`max_tokens`,
142
+ `enable_thinking`, `reasoning_effort`), `inference` in a task, and `llm` on an
143
+ improve process, the quality gate or an index pass. Nearest wins: the asset,
144
+ then the process, then the model entry, then the connection. A connection and
145
+ akm's own runtime take all of them. A harness takes none: set them in the
146
+ program's own configuration, and akm reports each as an `untranslated-field`
147
+ notice while the call goes on.
148
+
149
+ akm sends no `max_tokens` unless one is written, on a connection, on a model
150
+ entry or by the call: the model and its endpoint know their own limits, and a
151
+ default cap would truncate replies silently. A direct chat call (model work on a
152
+ connection) does send `temperature: 0.3` when nothing writes one; the
153
+ connection's, a model entry's or the call's own `temperature` wins. akm's own
154
+ runtime sends none and leaves it to the endpoint.
155
+
156
+ `enableThinking: false` turns thinking off and `reasoningEffort` takes a value
157
+ such as `"none"`, `"low"` or `"high"`. akm sends **both** thinking fields,
158
+ because which one an endpoint honors is a fact about it and any gateway in front
159
+ of it. llama.cpp direct honors both (`reasoning_effort` from build b10644); vLLM
160
+ honors `chat_template_kwargs`; Bifrost drops `chat_template_kwargs` and passes
161
+ `reasoning_effort` through, so also set `reasoningEffort: "none"` behind it. A
162
+ strict hosted API that rejects the two fields (OpenAI answers 400 `Unknown
163
+ parameter: 'chat_template_kwargs'`) gets one retry without both, and akm stops
164
+ sending them to that endpoint and model for the rest of the process. A response
165
+ with reasoning tokens despite `enableThinking: false` triggers a runtime
166
+ warning.
167
+
168
+ Improve's `consolidate` and `reflect` always ask for thinking off, for a
169
+ machine-readable payload, and the quality-gate and triage judges do too unless
170
+ the judge's connection turns thinking on (`enable_thinking: true` as a request
171
+ field, or `llm.enableThinking: true`).
172
+
173
+ When a call asks for JSON that matches a schema, a connection receives the
174
+ schema as `response_format` (`json_schema`, strict). If the endpoint rejects it
175
+ with a 4xx other than 429, or with a 5xx that names the response format, akm
176
+ retries once without `response_format` and stops sending it to that endpoint and
177
+ model for the rest of the process. A harness receives the schema as one
178
+ instruction at the end of its prompt (`Respond with ONLY a JSON value matching
179
+ this JSON Schema (no prose, no code fences):` followed by the schema).
180
+
181
+ ## Harnesses
182
+
183
+ A harness is an agent program you already have. `harnesses` maps a name to an
184
+ entry; an entry named for a platform enables it, so `{ "claude": {} }` enables
185
+ Claude Code. A second install of the same program sets `platform`: `{
186
+ "claude-work": { "platform": "claude", "bin": "/opt/claude-work/bin/claude" }
187
+ }`. The harness named `default` is the default harness; a lone harness is the
188
+ default whatever it is called. With several and none named `default`, there is no
189
+ default harness. `akm setup` names the default one `default`.
190
+
191
+ The platforms (generated from the harness registry; do not edit):
99
192
 
100
193
  <!-- harness-table:start (generated by scripts/gen-config-schema.ts) -->
101
- | `platform` | Harness | Binary | Session logs | Model work | Native `agent` |
194
+ | `platform` | Harness | Binary | Session logs | Restricted model work | Native `agent` |
102
195
  | --- | --- | --- | --- | --- | --- |
103
- | `opencode` | OpenCode | `opencode` | yes | yes | yes |
196
+ | `opencode` | OpenCode | `opencode` | yes | depends on the install | yes |
104
197
  | `claude` | Claude Code | `claude` | yes | yes | yes |
105
- | `opencode-sdk` | OpenCode SDK | in-process | no | yes | yes |
106
- | `codex` | OpenAI Codex CLI | `codex` | yes | no | no |
198
+ | `codex` | OpenAI Codex CLI | `codex` | yes | yes | no |
107
199
  | `copilot` | GitHub Copilot CLI | `copilot` | no | no | no |
108
200
  | `pi` | Pi | `pi` | no | no | no |
109
201
  | `gemini` | Gemini CLI | `gemini` | no | no | no |
@@ -112,373 +204,321 @@ The registered harnesses (generated from the harness registry; do not edit):
112
204
  | `openhands` | OpenHands | `openhands` | no | no | no |
113
205
  <!-- harness-table:end -->
114
206
 
115
- LLM endpoints must be complete `http://` or `https://` chat-completions URLs
116
- ending in `/chat/completions`, without userinfo, query, or fragment. API keys
117
- are symbolic only: `$VAR` or `${VAR}`. AKM resolves them only at dispatch.
118
-
119
- An LLM engine may set `enableThinking: false` to turn thinking off and
120
- `reasoningEffort` to a value such as `"none"`, `"low"`, or `"high"`. AKM sends
121
- **both** wire forms — `chat_template_kwargs.enable_thinking` and top-level
122
- `enable_thinking` — whenever `enableThinking` resolves, from engine config
123
- or a calling process (improve's `consolidate`/`reflect` always request
124
- `enableThinking: false` for a machine-readable payload, and the reflect and
125
- distill quality-gate judges do too unless the judge's engine sets
126
- `enableThinking: true`); `reasoningEffort` is always sent as top-level
127
- `reasoning_effort` when set. Backend support: llama.cpp direct honors both forms
128
- (`reasoning_effort` from build ≥ b10644); vLLM honors
129
- `chat_template_kwargs`; Bifrost drops `chat_template_kwargs` and passes
130
- `reasoning_effort` through, so also set `reasoningEffort: "none"` behind it. A
131
- strict hosted API that rejects the two fields (OpenAI answers 400 `Unknown
132
- parameter: 'chat_template_kwargs'`) gets one retry without both, and AKM stops
133
- sending them to that endpoint and model for the rest of the process, as it
134
- does for `response_format` below. Both fields are AKM-owned, not
135
- settable via `extraParams`. A response with reasoning tokens despite
136
- `enableThinking: false` triggers a runtime warning and the `akm health`
137
- `thinking-control` advisory.
138
-
139
- When a call asks for JSON that matches a schema, an LLM engine sends the schema
140
- as `response_format` (`json_schema`, strict) unless the engine sets
141
- `supportsJsonSchema: false`. If the endpoint rejects it with a 4xx other than
142
- 429, AKM retries once without `response_format` and stops sending it to that
143
- endpoint and model for the rest of the process. An agent engine receives the
144
- schema as one instruction at the end of its prompt (`Respond with ONLY a JSON
145
- value matching this JSON Schema (no prose, no code fences):` followed by the
146
- schema), plus the harness's own schema channel where it has one (codex
147
- `--output-schema`).
148
-
149
- An agent engine may set `bin`, `args`, `workspace`, `model`, `agent`, and `timeoutMs`;
150
- it takes no inference of its own (see
151
- [Inference on an agent engine](#inference-on-an-agent-engine)). `agent` names the
152
- engine's default native agent (`opencode`, `opencode-sdk` and `claude` only): it
153
- runs when a request names no agent, e.g. `{"kind":"agent","platform":"opencode-sdk","model":"openai/gpt-5.6-terra","agent":"akm-workflow"}`.
154
- A request's own agent wins, an agent asset's persona is kept, and model work keeps its confined
155
- agent. `opencode-sdk` takes no `args`, so `agent` is how an SDK engine names one. Only `platform: "opencode-sdk"` may set `llmEngine`; it names
156
- the LLM engine used as that SDK engine's fallback connection. With no
157
- `llmEngine`, an SDK engine has no fallback connection and opencode resolves
158
- provider, model and auth from its own configuration. `defaults.llmEngine` is
159
- not a substitute.
160
-
161
- An `opencode` or `opencode-sdk` engine has no setting for the OpenCode major: akm
162
- detects it. With no `bin`, the engine runs the most recent OpenCode on `PATH`
163
- (`opencode2`, OpenCode 2's own alias, when present, else `opencode`); with a
164
- `bin`, it runs that binary. akm runs `<bin> --version` once per binary per
165
- process and uses the OpenCode 2 or OpenCode 1 adapters to match. OpenCode 1 keeps
166
- working and logs a warning recommending OpenCode 2. A `--version` akm cannot read
167
- (or a major it does not know) uses the OpenCode 2 adapters and logs a warning
168
- with the raw output. To run a particular OpenCode, set the engine's `bin`, e.g.
169
- `{"kind":"agent","platform":"opencode-sdk","bin":"/opt/opencode1/bin/opencode"}`.
170
-
171
- **Permissions in a dispatch.** akm answers every OpenCode permission request of a
172
- dispatch with "reject" (there is no one to approve it), and the rejection reaches
173
- the model as a tool error. akm changes no OpenCode permission default, so what
174
- gets asked is OpenCode's own setting:
175
-
176
- - **OpenCode 1** asks before reading or writing outside the project directory
177
- (`external_directory`), so akm rejects it.
178
- - **OpenCode 2** (2.0.26) does not ask: a read or write outside the project
179
- directory just runs, and akm's rejection never happens, unless your OpenCode
180
- config says to ask. Add a rule to the global `opencode.json`, e.g.
181
- `"permissions": [{"action": "external_directory", "resource": "*", "effect": "ask"}]`
182
- (`"read"` and `"shell"` are actions too).
183
-
184
- On OpenCode 2 a rejection ends the turn: the dispatch returns `ok: false` with
185
- `reason: "aborted"` and the declined tool call in `error`. On OpenCode 1 the model
186
- carries on.
187
-
188
- On an engine without `timeoutMs`, model work (an improve process, a quality or
189
- triage judge, or an index pass) stops after 600 seconds, whatever the engine's
190
- kind; other work on an agent engine runs until it finishes. An improve stage's
191
- reply that the stage cannot read gets one corrective retry. Reflect holds its
192
- reply to its own contract the same way on every engine kind: a reply that is not
193
- the JSON object of reflect's schema gets one repair turn, and one still invalid
194
- fails with `parse_error`.
195
-
196
- Executable assets may request tools, but the request is not authority. Configure
197
- the host-local `execution.allowedTools` list to define the ceiling; `"*"` is an
198
- explicit allow-all. The default is an empty list. Asset frontmatter cannot set
199
- `workspace`, `environment`, or opaque `runtime` values; those belong to local
200
- engine configuration or workflow environment bindings.
201
-
202
- `platform: "opencode-sdk"` needs the **`opencode` binary** on PATH (or a `bin`
203
- pointing at it). akm bundles `@opencode-ai/sdk`, but that package is an HTTP
204
- client with no dependencies — it spawns `opencode serve` and talks to it — so
205
- the npm dependency alone does not make the platform usable. Install the binary
206
- with `npm i -g opencode-ai` or opencode's own installer.
207
-
208
- ### Inference on an agent engine
209
-
210
- An agent engine sets no inference of its own. For `opencode`, set it on the
211
- model in your own opencode config, which akm leaves as it is. akm carries
212
- inference (`temperature`, `reasoningEffort`, `enableThinking`, `maxTokens`,
213
- `contextLength`) into opencode only where it writes opencode's config itself:
214
-
215
- - **Model work on `opencode` and `opencode-sdk`:** an improve process's `llm`
216
- overlay becomes the options of the `akm-model-work` agent that runs the
217
- dispatch, so opencode's own title call on the same model keeps its defaults.
218
- - **An `opencode-sdk` engine's `llmEngine` fallback:** the model akm declares for
219
- it under `akm-custom` carries the fallback's inference and the request's.
220
- `maxTokens` and `contextLength` become `limit.output` and `limit.context`, and
221
- only together: opencode refuses half a `limit`.
222
-
223
- Inference from an asset, a workflow's `llm:` or a `models.json` alias reaches an
224
- LLM engine. On an agent engine it is reported as an `untranslated-field`
225
- notice and dispatch continues; model work's agent carries `temperature`,
226
- `reasoningEffort` and `enableThinking` without one.
227
-
228
- Reasoning effort has one word in a request, `reasoningEffort`. `effort`, as a
229
- `models.json` alias or an asset's `effort:` frontmatter spells it, is read as
230
- `reasoningEffort` wherever layers are merged, so an LLM engine sends it as
231
- `reasoning_effort`.
232
-
233
- ### Engines for unattended model work
234
-
235
- Unattended model work is the work akm hands a model with no one watching:
236
- - the improve processes (reflect, distill, consolidate, memory inference,
237
- extract, and validation's repair);
238
- - the quality and triage judges;
239
- - index passes;
240
- - `akm remember --enrich`.
241
-
242
- It runs on any engine kind under one tool policy. The model may read, edit
243
- files only inside a scratch working directory that akm creates for the
244
- dispatch and removes after it, and run `akm search` and `akm show`. The
245
- stash stays read-only to it: what model work changes reaches the stash only
246
- as a proposal, through the review queue.
247
-
248
- Each engine enforces as much of the policy as it can, and grants nothing it
249
- cannot enforce:
250
-
251
- | Engine | What the model gets |
252
- |---|---|
253
- | LLM | No tools. |
254
- | `claude` | Read and Edit inside the working directory, and Bash for `akm search` and `akm show` only. akm runs it with `--restricted`, so your user, project and local settings cannot widen that. |
255
- | `opencode`, `opencode-sdk` | Read, grep and glob in the working directory and the stash, edit in the working directory only, and `akm_search` and `akm_show`, through an injected `akm-model-work` agent. No bash, because opencode cannot stop a redirect such as `akm show x > file` from writing elsewhere. |
256
- | Every other `agent` harness | Cannot run model work: akm refuses the request before it starts. |
257
-
258
- **The one rule.** Every key model work reads its engine from must name an
259
- LLM engine or a `claude`, `opencode` or `opencode-sdk` agent engine. Those
260
- keys are:
261
- - `defaults.llmEngine`;
262
- - `index.defaults.engine` and `index.<pass>.engine`;
263
- - `improve.strategies.<name>.engine`;
264
- - `improve.strategies.<name>.processes.<process>.engine`;
265
- - an enabled `processes.triage.judgment.engine`;
266
- - `processes.<process>.qualityGate.engine`.
267
-
268
- A config that breaks the rule fails to load, with an error that names the
269
- key, the engine and its platform. Other engine keys, `defaults.engine` and
270
- `workflow.judgeEngine`, may name any configured engine.
271
-
272
- **Further details:**
273
- - A model-work dispatch builds its own command, so the engine's `args` do not
274
- apply to it, except a `--model` they name, and neither does its `workspace`.
275
- - An improve process's `llm` overlay reaches the `akm-model-work` agent on
276
- `opencode` and `opencode-sdk` (see
277
- [Inference on an agent engine](#inference-on-an-agent-engine)); on any other
278
- agent engine it is reported as `untranslated-field` notices, not errors.
279
- - opencode model work may read the stash and write only its working directory.
280
- `akm_search` and `akm_show` come from the akm-opencode plugin (0.9.21 or
281
- later), which akm does not load: put it in your opencode config
282
- (`"plugin": ["akm-opencode"]` for OpenCode 1; for OpenCode 2 install
283
- `akm-opencode-v2` with `opencode plugin add akm-opencode-v2`, see
284
- [Use AKM With Any Agent](https://github.com/itlackey/akm/blob/main/docs/guides/use-with-any-agent.md#opencode-plugins-one-package-per-opencode-major)). akm turns off the plugin's curation, learning
285
- and write gate for these dispatches and keeps its state in akm's state
286
- directory. The stash is protected from edits only while the temporary
287
- directory is outside a git repository.
288
-
289
- ### Model-map files
290
-
291
- AKM ships an immutable `models.json` package asset with three intent aliases:
292
- `fast`, `balanced`, and `reasoning`. The installed starter has separate
293
- columns for Claude Code, OpenCode, and OpenCode SDK only. A provider-specific
294
- identifier is not pretended to work on unrelated engines. Add mappings for
295
- Gemini, Codex, named direct LLM engines, or other harnesses in your user file.
296
- Config-root and per-engine `modelAliases` are rejected; this file is the only
297
- alias definition surface.
298
-
299
- An optional operator-owned file lives beside `config.json` at
300
- `$XDG_CONFIG_HOME/akm/models.json` (or `<AKM_CONFIG_DIR>/models.json`). It uses
301
- the same version-1 schema as the installed file:
302
-
303
- ```json
207
+ An entry sets any of:
208
+
209
+ | Key | Meaning |
210
+ | --- | --- |
211
+ | `platform` | One of the platforms above. Absent, the entry's name must be one. |
212
+ | `bin` | The program to run. Default: the platform's binary above, looked up on `PATH`. Set it when that is not the one you mean, for example `opencode2` when OpenCode 2 installed it beside an older `opencode` that comes first on your `PATH`. |
213
+ | `args` | Arguments placed before the ones akm adds. |
214
+ | `workspace` | The directory agent work runs in. Default: the directory akm was started in. |
215
+ | `agent` | The harness's native agent to run when a request names none (`claude` and `opencode` only). A request's own agent wins, an agent asset's persona is kept, and model work runs no native agent. |
216
+ | `timeoutMs` | How long a run may take, in milliseconds; `null` runs unbounded. Unset, agent work runs until it finishes and model work stops after 600 000. |
217
+ | `models` | Alias to model name for this harness, over the map akm ships for its platform. See [Aliases](#aliases). |
218
+ | `modelWork` | `false` keeps akm's own model calls (improve, judging, extraction) off this harness when it is the default for them: with no connection to run them on, they are off. A call that names the harness (`--harness`, `improve.harness`) still runs there. Absent or `true`, the default harness takes model calls when no connection does. `akm info` and `akm health` list a harness set this way. |
219
+
220
+ Asset frontmatter cannot set `workspace`, `environment` or opaque `runtime`
221
+ values; those belong to this configuration or to a workflow's environment
222
+ bindings.
223
+
224
+ ### How akm runs a harness
225
+
226
+ What asks for a harness decides how akm runs it.
227
+
228
+ **Agent work** (`akm agent`, `akm command run`, `akm proposal new`, agent tasks and workflow agent steps) runs your program the way you would run it yourself. It gets your environment as it is, with nothing filtered out, and your configuration, plugins, settings and logins as they are. akm sets only its own variables for the run: the directories this akm resolved (`AKM_CONFIG_DIR`, `AKM_DATA_DIR`, `AKM_CACHE_DIR`, `AKM_STATE_DIR`, and `AKM_BUNDLE_DIR` when you set one), so a program that runs `akm` uses the same installation, and, when a task runs it, `AKM_EVENT_SOURCE` (unless already set), so the akm commands the program runs count as machine use, not yours. A workflow unit's or task's own `env:` bindings are set over them. On the command line, after your `args` as you wrote them, akm adds the model or agent the asset or flag names, the agent asset's persona where the program takes one there, and what the program needs to run without a person at the terminal: its print or exec mode and its approve-all switch where it asks before acting. Codex runs with the sandbox your Codex configuration sets, or the one your `args` name; model work runs it with `--sandbox read-only` (below). The prompt goes on standard input where the program reads it there and on the command line where it does not; standard output is the reply. `akm agent` with no `--prompt` starts the program interactively instead. What akm scrubs from the output it captures is in [Architecture: Connections and Harnesses](../architecture/architecture.md#connections-and-harnesses) and, for a workflow, [Workflow Schema](workflow-schema.md#the-childs-environment).
229
+
230
+ **Model work** (see [Where each process runs](#where-each-process-runs)) is akm's own process, so akm sets the call up as it needs it. The program has the same environment. akm runs it in a scratch directory that it creates for the call and removes after it, stamps the akm commands it runs `improve`, and runs no native agent of yours. Where the program can be restricted, akm also limits its tools. The "Restricted model work" column of the table says on which platforms (OpenCode: on version 2):
231
+
232
+ - Claude Code runs with `--restricted`, so none of your settings files can widen the tools; with `Read` and `Bash` only for `akm search`, `akm show` and `akm curate`; with no MCP servers; and with no transcript saved.
233
+ - Codex runs with `--sandbox read-only`.
234
+ - OpenCode 2 runs as the `akm-model-work` agent, which akm passes inline with the run, merged into your `OPENCODE_CONFIG_CONTENT` and keeping the rest of it; akm reads that variable as OpenCode does, as JSONC (comments and trailing commas), with its `{env:NAME}` references replaced as OpenCode replaces them when the content reads only that way (a reference written outside a string, such as a number), and leaves it out, with one warning, when it is not valid JSONC holding an object. akm writes it in OpenCode 2's native `agents` map, which OpenCode reads over the legacy `agent` map, so an entry of that name in your config, in either map, inline or in a file, does not change it (a same-named entry in your inline `agent` map is left out). Its tools are limited to reading and searching: every tool is denied except `read`, `glob` and `grep`, and `akm_search`, `akm_show` and `akm_curate` when you have the akm OpenCode plugin installed. A tool that a plugin of yours adds is denied too. The limit is on tools: your plugins' own setup and hooks still run in the session. The run is `--standalone`, one process that ends with it, with no background service left behind, and `--title akm`, which names the session and spares the second model request that would generate a title.
235
+ - On Claude Code, Codex and OpenCode, `args` do not apply to model work, except a `--model` they name.
236
+ - OpenCode 1 and every other platform cannot be restricted: the run is unrestricted, in the scratch directory, and akm reports that. The other platforms get the agent-work command line, `args` included.
237
+ - OpenCode model work sets `OPENCODE_DISABLE_FREE_MODELS=1`, which OpenCode 2.1.2 and later honor: a call with no usable provider fails instead of sending the prompt to OpenCode's hosted free model, and so does a call whose model (named, or your OpenCode's default) is one of its free ones. Agent work does not set it: your OpenCode's models are as you would get running it by hand.
238
+
239
+ Where model work is restricted, its tools cannot change the library, so what it changes there reaches it only as a proposal, through the review queue. Where it cannot be restricted, nothing but its scratch working directory stands between the run and your files: the program keeps its own tools and runs with the file and command authority of the user running akm, in your environment, so it can edit files and run commands, in or outside the library, and akm does not check what it did. Any harness may be enabled for model calls; a connection is the way to keep them from touching anything. `akm setup` says so when you enable such a harness for akm's model calls, and saves `modelWork: false` on the harness when you decline ([setup](cli.md#setup)).
240
+
241
+ **History.** A harness run leaves a session in the program's own history, and `akm proposal extract` and improve read that history like any other. The exceptions are the model-work sessions akm marked: Claude Code keeps no transcript of them, and OpenCode 2 records them under the `akm-model-work` agent, which the reader skips. Codex and OpenCode 1 cannot be marked, so their model-work sessions are read like any other. akm's own runtime (below) keeps its sessions in `<akm data dir>/opencode-runtime/data/opencode/opencode.db`, read the same way.
242
+
243
+ **Stopping.** When akm stops a run (a timeout, an abort or a signal to akm) it sends the program's process group SIGINT, as Ctrl-C would. If anything in the group still runs after 5 seconds, or as soon as the program itself has exited and left members behind, it sends SIGTERM, and 5 seconds after that SIGKILL if anything still runs. Workflow `exec` units and task commands are stopped the same way. Windows has no process groups, and a signal sent to a process there ends it at once. A program that records how a run ended acts on the SIGINT: OpenCode 2 marks its session interrupted and releases it, and only a run that had to be terminated or killed stays suspended in your OpenCode history, where your own OpenCode's background service may resume it when it next starts (up to ten attempts, OpenCode's own policy). Through OpenCode 2's background service (measured on 2.1.4) the interrupt ends the `opencode` command and the service's turn alike: the service abandons the turn's model request and marks the session interrupted, and keeps running itself.
244
+
245
+ **OpenCode.** The `opencode` platform runs your installed OpenCode, either major version, as `opencode run`; the reply is its standard output. Agent work is that command with the prompt on stdin, your `args` as written, `--model` when akm names a model and `--agent` when the asset names an agent. Your OpenCode owns its configuration, logins, token refresh and sessions: akm reads, copies and embeds nothing of yours.
246
+ - OpenCode 2 runs agent work through its own background service, which stays up after the command. Add `--standalone` to the harness's `args` for a run that is one process.
247
+ - A model name is OpenCode's `provider/model`, from the request, else the `--model` in the harness's `args`; without one, your OpenCode's default decides.
248
+
249
+ Nobody can answer an akm run. OpenCode's headless `run` cancels every question
250
+ and rejects every permission request that would ask, and the model carries on
251
+ without it; that holds for akm's own runtime and for the `opencode` harness
252
+ alike.
253
+
254
+ ### Without a harness
255
+
256
+ Agent work runs on akm's own runtime when no harness is configured: a pinned
257
+ OpenCode 2 binary (2.1.4) that akm runs as a process for each run, driven by the
258
+ default connection. The npm package and the standalone binary do the same.
259
+
260
+ - **The first run fetches OpenCode.** akm downloads the binary for your platform
261
+ from the npm registry, one package of about 114 MB (240 MB on disk), checks it
262
+ against the registry's integrity hash, and keeps it in
263
+ `<akm cache dir>/opencode/2.1.4/`; later runs, and later versions of akm that
264
+ pin the same OpenCode, fetch nothing. Nothing else is downloaded: the binary
265
+ carries the package it reaches the endpoint with. If the fetch fails the run
266
+ fails as not started, naming the package, its URL and the cache path. With no
267
+ network access for this step, enable the `opencode` harness with an installed
268
+ OpenCode 2 instead: no setting points akm's runtime at a binary of your own.
269
+ On Alpine (musl) the binary needs `libstdc++` and `libgcc`: the Alpine Node
270
+ image has them, a bare Alpine needs `apk add libstdc++ libgcc`. akm's cache
271
+ and data directories must not be mounted `noexec`: the binary runs from the
272
+ first and unpacks its own native pieces into the second.
273
+ - It uses your connections. The connection a run is dispatched with (its
274
+ endpoint, key, model and context window, as they were when the run was planned)
275
+ is the process's only provider, so a connection edited or removed afterwards
276
+ cannot redirect a run that was already planned. Your OpenCode's configuration is
277
+ not read: its config, data, cache, state and temp directories are akm's, under
278
+ `<akm data dir>/opencode-runtime/`, so none of your plugins, MCP servers or
279
+ agents load, and no `OPENCODE_*` variable of your environment reaches it.
280
+ OpenCode still reads `~/.agents` and its system config (`/etc/opencode`, or
281
+ macOS managed preferences); a project's `AGENTS.md` and `opencode.json` and
282
+ your Claude Code skills (`~/.claude/skills`) are not read; OpenCode's free
283
+ models and its model-catalog fetch are off.
284
+ - It has OpenCode's default agent and tools, editing files and running commands
285
+ included, and akm's `akm_search`, `akm_show` and `akm_curate`, which run the
286
+ `akm` that started the run. A request's `persona` goes in the prompt, as for
287
+ the `opencode` harness.
288
+ - The connection's request fields (`reasoning_effort`, `top_p`, a `max_tokens`
289
+ and the rest) are sent with each model request as written. OpenCode's own
290
+ output cap is not: a request carries the cap the connection or the call writes,
291
+ and none otherwise, as a direct request does.
292
+ - Its sessions are in `<akm data dir>/opencode-runtime/data/opencode/opencode.db`,
293
+ and all of its files under `<akm data dir>/opencode-runtime/`. Nothing is
294
+ written under `~/.local/share/opencode`.
295
+ - The connection's key is in no process's environment: akm writes it for the
296
+ run to a file under `<akm data dir>/opencode-runtime/keys/` (readable by you
297
+ alone), names that file in the runtime's config, and removes it when the run
298
+ ends.
299
+ - The commands the agent runs get the environment akm runs in, with the
300
+ directories this akm resolved and the run's variables set over it, less your
301
+ `OPENCODE_*` variables. What akm sets for the OpenCode process itself (its
302
+ directories and its OpenCode settings) is not in it: akm's plugin puts those
303
+ variables back to what akm runs in, or removes them, for each command.
304
+ - A run reports its session id and the tokens its turn used, read from that
305
+ session once the process has ended; a workflow's `budget.max_tokens` counts
306
+ them. A failed run is a non-zero exit with OpenCode's own message
307
+ (`non_zero_exit`), as for a harness.
308
+ - Runs that start together, in one akm process or several, wait while the first
309
+ run of this OpenCode version prepares its database alone.
310
+
311
+ ## Credentials
312
+
313
+ akm reads an API key from one of two places, and nowhere else:
314
+
315
+ - **A `$VAR` reference.** `"apiKey": "$LOCAL_LLM_KEY"` (or `"${LOCAL_LLM_KEY}"`)
316
+ names an environment variable, read when the call is made. An unset or empty
317
+ variable is warned about once and the request goes without a key.
318
+ - **`credentials.json`.** With no `apiKey`, the key is the entry stored for the
319
+ block in `credentials.json`, next to `config.json`, written with mode 0600:
320
+
321
+ ```json
322
+ { "connections": { "<name>": { "key": "…", "endpoint": "…" } },
323
+ "services": { "embedding": { "key": "…", "endpoint": "…" } } }
324
+ ```
325
+
326
+ A key is stored for one endpoint and read for that endpoint alone. Change a
327
+ block's endpoint and the old key is not sent to the new one (it is named once,
328
+ without its value, and the block goes without until a key is stored for the new
329
+ endpoint). A connection's name and a service (`embedding`, `curateRerank`) are
330
+ different namespaces, so a connection called `embedding` and the embedding
331
+ service hold different keys. An `embedding` that runs on the default connection
332
+ uses that connection's entry. A connection that only an `extends` base defines
333
+ gets its endpoint from that base, so it never uses a stored key: stored keys
334
+ are for the connections your own `config.json` names (name it there, even with
335
+ only its `models`, and a key stored for the endpoint it ends up with is used).
336
+ A key in the older flat `{ "<name>": "<key>" }` form names no endpoint and is
337
+ not used; `akm migrate apply` files it under the endpoint your own config gives
338
+ that block, after copying `credentials.json` to `<cache dir>/config-backups/`.
339
+ A block that already has a stored key is never overwritten by it: the same key
340
+ for the same endpoint just drops the older entry, and any other leaves both in
341
+ the file, with a note. A flat key called `connections` or `services` (a
342
+ connection of that name) is told from the namespace of that name by its shape and
343
+ filed first; until it is, a write that would replace it stops with an error
344
+ instead.
345
+
346
+ `akm setup` asks for a key at a masked prompt and stores it in
347
+ `credentials.json` for the endpoint it was typed for; `--api-key-env NAME` sets
348
+ `apiKey: "$NAME"` instead. Setup an endpoint change that comes with no key does
349
+ not carry the old key to the new endpoint (a `$VAR` is dropped from the entry, a
350
+ stored key stays for the old endpoint), and says so. A key
351
+ is never written to `config.json` or passed on a command line. A literal
352
+ `apiKey` in the file, or a `${secrets/...}` reference, is warned about once and
353
+ ignored (never sent), and `akm config set` refuses to write one. `akm migrate
354
+ apply` moves such a key into `credentials.json`. The key is sent as
355
+ `Authorization: Bearer <key>` and redacted from everything akm prints and logs.
356
+
357
+ The bundle's `secrets/` store, `akm secret` and `akm env` are for the secrets
358
+ your own agents and tasks need, and have nothing to do with these keys.
359
+
360
+ ## Aliases
361
+
362
+ A model alias names "a model of this kind", so the same bundle means the same
363
+ thing on every machine. `fast`, `balanced` and `reasoning` are the standard
364
+ ones; add any other you like. A connection's `models` and a harness's `models`
365
+ say which of their models answers each:
366
+
367
+ ```jsonc
304
368
  {
305
- "version": 1,
306
- "aliases": {
307
- "fast": {
308
- "gemini": "gemini-2.5-flash"
309
- },
310
- "reasoning": {
311
- "claude": {
312
- "inference": {
313
- "effort": "medium"
314
- }
315
- },
316
- "local-reasoner": {
317
- "model": "qwen3:30b",
318
- "inference": {
319
- "effort": "high"
320
- }
321
- }
369
+ "connections": {
370
+ "default": {
371
+ "endpoint": "http://localhost:11434/v1",
372
+ "models": { "fast": "qwen3-8b", "balanced": "qwen3-32b", "reasoning": "qwen3-32b" }
322
373
  }
323
- }
374
+ },
375
+ "harnesses": { "claude": { "models": { "fast": "claude-haiku-4-5-20251001" } } }
324
376
  }
325
377
  ```
326
378
 
327
- Each engine mapping is either a non-empty exact model string or a structured
328
- profile with the documented fields `model`, `inference`, and `engine`. A user
329
- profile may omit `model` when the installed layer already supplies it, as the
330
- partial Claude override above does. After overlay, every alias/engine entry
331
- must have a usable model. Unknown profile fields are rejected; JSON-safe
332
- fields inside `inference` are preserved for engine adapters to lower
333
- optimistically. An `inference.effort` is read as `reasoningEffort`
334
- (see [Inference on an agent engine](#inference-on-an-agent-engine)).
335
-
336
- A profile's `engine` field (0.9.15, #946) borrows a column's `model` (and, for
337
- an `llm`-kind engine, its inference defaults) from a configured
338
- `engines.<name>` connection instead of hand-typing a literal model a second
339
- time:
340
-
341
- ```json
379
+ A model is asked for by `model:` in an asset's frontmatter, `--model` on a
380
+ command, or `model` on an improve process, and it is an alias or an exact model
381
+ name:
382
+ - An alias is looked up in the selected connection's `models` (or harness's).
383
+ - A name that is not an alias is used as written.
384
+ - An alias the connection does not map answers with its first model. A
385
+ connection with no `models` at all cannot answer an alias; the error names
386
+ `akm models set`.
387
+ - A harness passes no model for an alias it has none for, and the program uses
388
+ its own default; when the harness maps other aliases, akm says so once. Claude
389
+ Code ships a map (`fast` Haiku 4.5, `balanced` Sonnet 4.6, `reasoning` Opus
390
+ 4.7), which a `models` entry overrides. No other harness ships one. A
391
+ harness is passed the model name only: an alias written as an object
392
+ (`{ "model": "...", "effort": "high" }`) gives its `model`, and akm says once
393
+ that its other fields are ignored.
394
+
395
+ A kind of work that names no model asks for a default alias: improve's
396
+ processes, the index passes, extraction and `akm remember --enrich` ask for
397
+ `fast`; the quality and triage judges and a workflow gate ask for `balanced`;
398
+ agent work asks for `balanced`.
399
+
400
+ `akm models` lists a connection's aliases, then the models its endpoint lists,
401
+ and `akm models set <alias> <model>` changes one; see the [CLI
402
+ reference](cli.md#models). A harness's `models` is edited with `akm config set`.
403
+
404
+ ## Where each process runs
405
+
406
+ akm's work is of two kinds, and each has a default place to run.
407
+
408
+ - **Model work** asks a model for an answer with nobody watching: the improve
409
+ processes (reflect, distill, consolidate, memory inference, extract, and
410
+ validation's repair), the quality and triage judges, the judge of a workflow
411
+ `### gate`, the index passes, and `akm remember --enrich`. It runs on the
412
+ default connection, as a direct request to the endpoint. With no connection it
413
+ runs on the default harness, unless that harness has `modelWork: false`. On a
414
+ harness it is akm's own process, not your use of the program ([How akm runs a
415
+ harness](#how-akm-runs-a-harness)).
416
+ - **Agent work** runs an agent: `akm agent`, `akm command run`, `akm proposal
417
+ new`, agent tasks and workflow agent steps. It runs on the default harness.
418
+ With no harness it runs on akm's own runtime, over the default connection.
419
+
420
+ With neither a connection nor a harness, model features and agent work are off:
421
+ each command that needs one says so and points at `akm setup`, and everything
422
+ that needs no model keeps working.
423
+
424
+ Anywhere a connection or harness can be named (config, a flag, an asset's
425
+ `connection:` or `harness:`) and none is, the default is used. A name that is
426
+ not configured is an error (exit 78), never a reason to use the default: the
427
+ message names the selector, says it is not configured, lists the configured
428
+ connections (or harnesses) and the nearest name when one is close, and the same
429
+ holds for the retired `engine:` and `--engine`. Defaults apply only when nothing
430
+ was named, so a call for a private connection is never sent to a remote service,
431
+ nor a call that must not touch files to a harness that can, by a typo, a removed
432
+ entry, or an entry akm ignored when it loaded the config (`akm health` lists
433
+ what is configured).
434
+
435
+ A harness is named by its name or by its platform, so a shared asset that says
436
+ `harness: claude` runs wherever Claude Code is set up, whatever the entry is
437
+ called. The name comes first. Failing that, the one harness that runs the
438
+ platform (its `platform`, else its key) is chosen; when several do, the one
439
+ named `default` is, and with none named `default` the call fails like any
440
+ unconfigured name, naming them. akm never picks the first.
441
+
442
+ These settings, all optional, choose another place for one piece of work:
443
+
444
+ | Key | Chooses where |
445
+ | --- | --- |
446
+ | `improve.connection`, `improve.harness` | All of `akm improve`'s model work |
447
+ | `improve.processes.<process>` | One improve process: `reflect`, `distill`, `consolidate`, `memoryInference`, `extract`, `validation`, or `triage` (its judgment) |
448
+ | `improve.qualityGate` | The judge of the reflect and distill quality gates |
449
+ | `index.connection`, `index.<pass>` | The index passes (`index.memory`) |
450
+
451
+ Each takes `connection` or `harness`, `model` (an alias or a name), `llm` (the
452
+ [settings a call asks for](#settings-a-call-asks-for)) and `timeoutMs`. A pass
453
+ under `index` takes `connection`, not `harness` (one that names nothing runs
454
+ where model work does: the default connection, else the default harness), and
455
+ `enabled: false` turns it off:
456
+
457
+ ```jsonc
342
458
  {
343
- "version": 1,
344
- "aliases": {
345
- "fast": {
346
- "opencode": { "engine": "local-fast" }
347
- }
348
- }
459
+ "improve": {
460
+ "connection": "default",
461
+ "processes": {
462
+ "reflect": { "connection": "cloud", "model": "reasoning" },
463
+ "memoryInference": { "model": "qwen3-8b", "llm": { "temperature": 0.1 } }
464
+ },
465
+ "qualityGate": { "connection": "judge", "llm": { "enableThinking": true } }
466
+ },
467
+ "index": { "connection": "default", "memory": { "enabled": false } }
349
468
  }
350
469
  ```
351
470
 
352
- With `engines.local-fast` configured (agent-kind or llm-kind), this column
353
- resolves to that engine's own `model` string. `model` and `engine` are
354
- mutually exclusive on the same profile — `engine` is an indirection for the
355
- model value, never an engine-selection override; which engine `akm agent`
356
- dispatches to is still decided entirely by `--engine`/`defaults.engine` (see
357
- [Engine selection](#engines)). The referenced engine's `model` must itself be
358
- literal, not another alias, and akm copies it verbatim: it does not translate
359
- between an engine's connection and an agent platform's own provider registry,
360
- so the value must already be meaningful for the column's platform (e.g. a
361
- `kind: "agent", platform: "opencode"` engine's `model` should already be a
362
- string opencode itself understands, such as `krang/qwen3.5-9b`). Run
363
- `akm models list` to see, for every alias/column, the resolved model and
364
- whether it came from the installed defaults, the user overlay, and a literal
365
- value or an `engine` reference.
366
-
367
- The user file overlays the installed file by alias, engine, and nested object
368
- field. Objects merge recursively. Arrays, scalars, and explicit `null` replace
369
- the lower value; omitted fields preserve it. A layer setting a literal `model`
370
- clears any `engine` inherited from a farther layer, and vice versa — the
371
- nearer layer's choice of literal-vs-engine always wins outright rather than
372
- merging. Alias and engine keys are case-normalized, and case-colliding
373
- definitions are rejected. Unknown model inputs still pass through
374
- byte-for-byte as exact identifiers. Once a name is a known merged alias,
375
- selecting an engine with no mapping is an actionable configuration error
376
- rather than silently sending the alias as a model ID.
377
-
378
- The common execution cascade reads these files for current direct command and
379
- non-interactive agent calls, task source v4 runs, and improve/proposal/index
380
- model work routed through that resolver. A structured alias expands as
381
- defaults at the layer that selected it; explicit sibling fields and nearer
382
- layers still win. The resulting request carries the exact model ID and merged
383
- inference object. Engine lowerers consume that exact selection and never run
384
- alias resolution again. New workflow starts persist the exact request and
385
- symbolic runner selection in the durable plan v4 family's executable
386
- `irVersion: 5`; resume consumes that frozen material without resolving aliases
387
- again.
388
-
389
- Copy the complete installed starter into the user configuration directory when
390
- you want to customize all fields:
471
+ The nearest setting wins, farthest to nearest: the shipped alias of the kind of
472
+ work, `improve.connection` and `improve.harness`, the process's entry in
473
+ `improve.processes`, and `--connection`, `--harness` and `--model` on the run,
474
+ which reach every call in it, the judges included. The quality-gate judge is the
475
+ exception: a process's own entry is the writer's, so `improve.qualityGate`
476
+ replaces it, and a `reasoning` model chosen for reflect is not the one that
477
+ judges it. An index pass reads `index.*`, then `index.<pass>`. A strategy adds no
478
+ layer: it says which processes run, never where.
391
479
 
392
- ```sh
393
- akm models copy-defaults
394
- akm models copy-defaults --overwrite # explicit replacement confirmation
395
- ```
480
+ `akm health` and `akm info` print this as a table, resolved by the same code the
481
+ calls use: for each kind of work, the connection or harness and the model.
396
482
 
397
- The command validates the installed asset, creates the config directory, and
398
- writes a fully synced sibling before publication. Without `--overwrite`, a
399
- hard-link/no-replace operation makes publication atomic: a racing creator wins
400
- without losing its bytes. A filesystem that cannot provide that operation
401
- fails safely instead of falling back to a clobbering rename.
402
-
403
- With `--overwrite`, the portable guarantee is an atomic pathname replacement
404
- that never follows the target when it is a symlink. AKM verifies the observed
405
- regular-file identity again immediately before rename, but the portable
406
- filesystem APIs do not provide a conditional compare-and-swap rename. Another
407
- process can still change the directory entry after that check; AKM replaces
408
- the entry at the pathname without dereferencing it. Consequently,
409
- `overwritten: true` means overwrite was requested for an entry AKM observed,
410
- not that an inode identity was transactionally locked. Symlinks and other
411
- non-regular targets observed at either check are refused.
412
-
413
- AKM does not auto-create or sync this file, and authoritative defaults never
414
- live in the cache. npm/Node and normal Bun installs read the packaged
415
- `dist/assets/models.json` lazily, so `akm health` can report a missing or
416
- malformed package asset as a `model-map-files` failure. A standalone binary has
417
- the same authoritative bytes embedded at compile time and therefore has no
418
- external model-map asset that can later disappear; its health check validates
419
- the embedded copy, and release tests pin copied bytes to `src/assets/models.json`.
420
- The health check passes when the optional user file is absent and warns with
421
- its path and JSON location when the user file is unreadable or invalid.
422
-
423
- `defaults.engine` names an LLM or agent engine. `defaults.llmEngine` names the
424
- default engine for unattended model work, so it follows
425
- [the one rule](#engines-for-unattended-model-work). There is no first-engine
426
- fallback: an unset `defaults.engine`
427
- never resolves to some arbitrary entry in `engines`. It resolves instead to a
428
- synthesized, config-free `opencode-sdk` engine when the `opencode` binary is on
429
- PATH — announced once per run, and preempted by any `opencode-sdk` engine you
430
- configure yourself. Naming an engine that is not configured is always an error
431
- and is never rescued by that fallback.
432
-
433
- `defaults.llmEngine` is not an `opencode-sdk` engine's fallback connection. An
434
- SDK engine gets an LLM fallback only from its own `llmEngine`, so the
435
- synthesized engine, which sets none, runs on opencode's own provider, model and
436
- auth.
437
-
438
- Index passes select engines through `index.defaults.engine` or
439
- `index.<pass>.engine`, which follow
440
- [the one rule](#engines-for-unattended-model-work). Per-pass `model`, `timeoutMs`, and `llm` fields are
441
- invocation overrides; `enabled: false` disables that pass. Connection fields
442
- such as `endpoint`, `provider`, `apiKey`, and `apiKeyFile` belong only on
443
- named engines.
444
-
445
- `workflow.maxConcurrency` is the native workflow engine ceiling. An explicit
446
- value is clamped to `1..64`. When absent, AKM derives the cap once from the CPU
447
- count (`min(16, max(1, cores - 2))`) and freezes it into the run plan, so resume
448
- does not change policy on a different host or after config edits.
483
+ ## Workflows
484
+
485
+ `workflow.maxConcurrency` is the ceiling on units a workflow run executes at
486
+ once. An explicit value is clamped to `1..64`. When absent, akm derives it once
487
+ from the CPU count (`min(16, max(1, cores - 2))`) and freezes it into the run
488
+ plan, so resuming on a different host or after a config edit does not change the
489
+ policy.
449
490
 
450
491
  `workflow.defaultMapConcurrency` is the width a `map` step freezes when it
451
- declares no `concurrency:` of its own. Unset means **4** — map steps are
452
- parallel by default as of 0.9.1. An explicit value is clamped to `1..64`; set
453
- it to `1` to restore the pre-0.9.1 serial-by-default fan-out for every workflow
454
- on this machine. It is only a default: an authored `map.concurrency` always
455
- wins, and it never raises a step past `workflow.maxConcurrency`, the selected
456
- engine's `concurrency`, or the host CPU cap. An LLM engine that declares no
457
- `engines.<name>.concurrency` gets **1** on a loopback endpoint (a local model
458
- server holds one loaded model) and **4** on a remote one.
459
-
460
- `workflow.judgeEngine` names the LLM or agent engine used to verify every
461
- non-empty workflow `### gate` rubric. It is required when a workflow declares
462
- completion criteria and is frozen into each new run, so later config edits do
463
- not change an in-flight run's verifier. Missing, failed, or malformed verifier
464
- results reject the gate; criteria are never silently bypassed.
492
+ declares no `concurrency:` of its own. Unset means 4. An explicit value is
493
+ clamped to `1..64`; set it to `1` for serial fan-out. It is only a default: an
494
+ authored `map.concurrency` always wins, and a step never runs wider than
495
+ `workflow.maxConcurrency`, the selected connection's `concurrency`, or the host
496
+ CPU cap.
497
+
498
+ The judge of a `### gate` rubric is model work, so a workflow with completion
499
+ criteria needs a connection or a harness before a new run can be frozen. The
500
+ choice is frozen into each run, so later config edits do not change an
501
+ in-flight run's verifier. A missing, failed or malformed verdict rejects the
502
+ gate; criteria are never silently bypassed.
465
503
 
466
504
  ## Strategies
467
505
 
468
506
  Improve presets live under `improve.strategies`; invoke one with
469
507
  `akm improve --strategy <name>`. The selection order is `--strategy`,
470
- `defaults.improveStrategy`, then built-in `default`. A strategy and each process
471
- can select `engine`, `model`, `timeoutMs`, and LLM request overrides:
508
+ `improve.strategy`, then built-in `default`. A strategy says which processes run
509
+ and how each is tuned (`enabled`, limits, gates), never where: where each process
510
+ runs is [Where each process runs](#where-each-process-runs), whichever strategy
511
+ is selected.
472
512
 
473
513
  ```jsonc
474
514
  {
475
515
  "improve": {
516
+ "strategy": "nightly",
476
517
  "strategies": {
477
518
  "nightly": {
478
- "engine": "fast",
479
519
  "processes": {
480
- "reflect": { "llm": { "temperature": 0.2 } },
481
- "memoryInference": { "model": "qwen3-small" }
520
+ "reflect": { "limit": 10 },
521
+ "memoryInference": { "enabled": false }
482
522
  }
483
523
  }
484
524
  }
@@ -486,32 +526,24 @@ can select `engine`, `model`, `timeoutMs`, and LLM request overrides:
486
526
  }
487
527
  ```
488
528
 
489
- An improve process's engine follows
490
- [the one rule](#engines-for-unattended-model-work); an explicit invalid or
491
- incompatible engine never falls back to another engine. The built-in strategies
492
- are `default` and `consolidate`. `consolidate` is a patch that
493
- is deep-merged onto `default`, and a user-defined strategy of the same name is
494
- merged on top of that; so every strategy, built-in or user-defined, inherits
495
- omitted fields from `default`. `quick`, `reflect-distill`, `thorough`,
496
- `catchup` were removed in 0.10 (#1130): measured, none beat `default` (`thorough`
497
- and `catchup` added only a judged triage drain, and `catchup` was identical to
498
- `consolidate`). `proactive-maintenance` was removed too (#1129): its lane now
499
- lives in `default`. A `improve.strategies.<name>` block with one of those names is
500
- now an ordinary user-defined strategy that inherits `default`, so it no longer
501
- carries the old built-in's settings; `--strategy` or `defaults.improveStrategy`
502
- naming one with no such block fails with an error that names `default`. To keep
503
- the old judged drain, set `processes.triage` (`enabled`, `applyMode: "promote"`,
504
- `judgment: true`) in your own strategy. The built-in strategies other than
505
- `default` and `consolidate` are Experimental (see [STABILITY.md](../../STABILITY.md)).
506
-
507
- `processes.triage.judgment` explicitly controls the optional judgment tier.
508
- Use `true` to enable it, `false` to disable it, or an object with `enabled`,
509
- `engine`, `model`, `timeoutMs`, and/or `llm` overrides. Existing object values
510
- such as `{}` and `{ "engine": "reviewer" }` remain enabled by default. Unknown
511
- object keys are rejected so misspellings cannot silently change execution;
512
- the retired `mode` and `profile` keys continue to report their engine migration
513
- guidance. When enabled, engine selection is judgment → triage → strategy →
514
- `defaults.llmEngine`, and resolution fails closed if none is available.
529
+ The built-in strategies are `default` and `consolidate`. `consolidate` is a
530
+ patch that is deep-merged onto `default`, and a user-defined strategy of the
531
+ same name is merged on top of that; so every strategy, built-in or user-defined,
532
+ inherits omitted fields from `default`. A name that is neither built-in nor
533
+ defined under `improve.strategies` is refused: `improve.strategy` when the config
534
+ loads, `--strategy` when the command runs. A block named `quick`, `thorough`,
535
+ `catchup`, `reflect-distill` or `proactive-maintenance` is an ordinary
536
+ user-defined strategy that inherits `default`, and `--strategy` naming one with
537
+ no such block fails with an error that names `default`. For a judged triage
538
+ drain, set `processes.triage` (`enabled`, `applyMode: "promote"`,
539
+ `judgment: true`) in your own strategy.
540
+
541
+ `processes.triage.judgment` controls the optional judgment tier: `true` enables
542
+ it, `false` disables it, and an object holds `enabled` (default `true`). The
543
+ judgment is a model call; it runs where `improve.processes.triage` says
544
+ (`connection` or `harness`, `model`, `llm`, `timeoutMs`), over `improve.connection`
545
+ and `improve.harness`, and over the defaults. Enabling it with no connection or
546
+ harness configured fails before the run starts, naming `akm setup`.
515
547
 
516
548
  The `default` strategy runs triage in `promote` mode with judgment off and
517
549
  `maxAcceptsPerRun: 200` (#1143), so the proposal backlog drains through the
@@ -532,14 +564,10 @@ gpt-5.6-terra it was very cautious (precision 1.0, recall 0.17).
532
564
  ```jsonc
533
565
  {
534
566
  "improve": {
567
+ "processes": { "triage": { "harness": "claude" } },
535
568
  "strategies": {
536
569
  "nightly": {
537
- "processes": {
538
- "triage": {
539
- "enabled": true,
540
- "judgment": { "enabled": true, "engine": "reviewer" }
541
- }
542
- }
570
+ "processes": { "triage": { "enabled": true, "judgment": true } }
543
571
  }
544
572
  }
545
573
  }
@@ -552,38 +580,30 @@ each process's LLM-as-judge quality gate. Each is on unless it sets
552
580
  judge passes is staged for the triage drain to accept; a distill lesson it
553
581
  passes is deferred for a person (reason `distill-review`), which the drain and
554
582
  its judgment tier leave alone. With the distill gate off nothing is judged, and
555
- the drain decides. The judge is the
556
- process's own engine when that is an LLM engine, or the `defaults.llmEngine`
557
- engine when an agent generates. `engine`, `model`, `timeoutMs` and `llm` give the gate a judge of its own,
558
- resolved over the process's settings the way `triage.judgment` resolves over
559
- triage's. The judge may be any engine that follows
560
- [the one rule](#engines-for-unattended-model-work). A gate whose settings
561
- resolve to no engine fails before anything is generated; it never falls back
562
- to another engine. The judge runs at
563
- temperature 0 with thinking off unless its engine sets `enableThinking: true`.
583
+ the drain decides. The judge is a model call that
584
+ runs where `improve.qualityGate` says (`connection` or `harness`, `model`, `llm`,
585
+ `timeoutMs`), for both gates, over `improve.connection` and `improve.harness`
586
+ and the defaults: with no `qualityGate`, the default connection answers with its
587
+ `balanced` model. The process's own entry in `improve.processes` is the writer's
588
+ and does not reach the judge. The judge may be a connection or a harness; a
589
+ harness that cannot be restricted runs it unrestricted, with a notice ([How akm
590
+ runs a harness](#how-akm-runs-a-harness)). It runs
591
+ at temperature 0 with thinking off unless its connection turns thinking on.
564
592
  Thinking is slow: on a 27B llama.cpp server, a thinking judgment took a median
565
593
  of 30–67 s and up to about 3 minutes, against about 5 s without.
566
594
  Behind a gateway that drops `chat_template_kwargs` (Bifrost), point a thinking
567
- judge's engine at the server directly.
595
+ judge's connection at the server directly.
568
596
 
569
597
  ```jsonc
570
598
  {
571
- "engines": {
599
+ "connections": {
572
600
  "judge": {
573
- "kind": "llm",
574
- "endpoint": "http://127.0.0.1:8080/v1/chat/completions",
575
- "model": "qwen3-27b",
576
- "enableThinking": true
601
+ "endpoint": "http://127.0.0.1:8080/v1",
602
+ "models": { "balanced": "qwen3-27b" }
577
603
  }
578
604
  },
579
605
  "improve": {
580
- "strategies": {
581
- "nightly": {
582
- "processes": {
583
- "reflect": { "qualityGate": { "engine": "judge" } }
584
- }
585
- }
586
- }
606
+ "qualityGate": { "connection": "judge", "llm": { "enableThinking": true } }
587
607
  }
588
608
  }
589
609
  ```
@@ -658,7 +678,7 @@ metadata placed before body content.
658
678
 
659
679
  ## Semantic search
660
680
 
661
- `semanticSearchMode` (top-level, `"off" | "auto"`, default `"off"`) gates
681
+ `semanticSearchMode` (top-level, `"off" | "auto"`, default `"off"`, or `"auto"` when `embedding` is set) gates
662
682
  embedding-based search. `"auto"` lets AKM set up embeddings (which downloads
663
683
  a local model unless you point `embedding` at a remote provider) and falls
664
684
  back to keyword-only FTS if the embedding runtime is unavailable; `"off"`
@@ -669,14 +689,26 @@ This is distinct from `searchMode: "keyword"`, which is the normal result when
669
689
  semantic search is disabled or has not been built. A read-only sandbox that
670
690
  cannot record best-effort usage telemetry does not by itself mark search as
671
691
  degraded.
672
- The npm/Bun package declares `@huggingface/transformers` as a normal dependency.
673
- AKM imports that external package directly; it does not carry a copied runtime
674
- under `src/` or `dist/`. If the dependency is unavailable, reinstall `akm-cli`
675
- or configure a remote `embedding.endpoint`. Setup does not mutate a global
676
- installation to add runtime packages.
692
+ The local embedder runs its model (`Xenova/bge-small-en-v1.5` unless
693
+ `embedding.localModel` names another) on `onnxruntime-node`, with
694
+ `@huggingface/tokenizers` for the token ids. Both are normal dependencies of the
695
+ package, imported directly; AKM does not carry a copied runtime under `src/` or
696
+ `dist/`. The model's four files (about 127 MB) are fetched from the Hugging Face
697
+ Hub on first use into `$HF_HOME/<repo>/` (default `<cache dir>/models`), and
698
+ `HF_HUB_OFFLINE=1` forbids the fetch. `akm index --format json` reports the
699
+ runtime it embedded with as `verification.embeddingRuntime`.
700
+ If the runtime cannot load, because the dependency is missing or the platform
701
+ has no native ONNX Runtime library (Alpine/musl, Intel macOS), the index reports
702
+ semantic search `blocked` with the reason and search stays keyword-only:
703
+ reinstall `akm-cli` for a missing dependency, or configure a remote
704
+ `embedding.endpoint`. A standalone binary copies the library it embeds once into
705
+ `<cache dir>/onnxruntime/` (see
706
+ [Getting Started](../guides/getting-started.md#standalone-binary-and-semantic-search)).
707
+ Setup does not mutate a global installation to add runtime packages.
677
708
  The default is `"off"` so a bare or headless install (`akm bundle create`, `--yes`,
678
709
  `--config`) never silently downloads the local embedding model on first
679
- index.
710
+ index. A config that has an `embedding` block and sets no `semanticSearchMode`
711
+ reads as `"auto"`: writing the block says semantic search is wanted.
680
712
  The interactive `akm setup` wizard pre-selects semantic search **on**
681
713
  regardless of this default, and warns that choosing it downloads the model
682
714
  unless a remote `embedding` config is provided.
@@ -687,11 +719,15 @@ unless a remote `embedding` config is provided.
687
719
 
688
720
  `embedding` configures the connection used for semantic search and
689
721
  `akm improve`'s memory-inference/consolidate passes when they call an
690
- embedding model: `provider`, `endpoint`, `model`, `apiKey` (symbolic
691
- reference, same rules as engine `apiKey`), `dimension`, `localModel`,
722
+ embedding model: `provider`, `endpoint`, `model`, `apiKey` (a `$VAR` reference,
723
+ or the `embedding` service's entry in `credentials.json`; see
724
+ [Credentials](#credentials)), `dimension`, `localModel`,
692
725
  `maxInputTokens`, `maxTokens`, `batchSize`, `contextLength`, `timeoutMs`,
693
726
  `queryTimeoutMs`, `queryTemplate`, `documentTemplate`, `concurrency`, and
694
- `ollamaOptions.num_ctx`.
727
+ `ollamaOptions.num_ctx`. An `embedding` with a `model` and no `endpoint` names a
728
+ model the default connection serves, at its endpoint and with its key; with no
729
+ default connection there is nothing to serve it, and the built-in local embedder
730
+ runs, with a warning.
695
731
 
696
732
  Retrieval models expect a prompt around queries and documents. akm picks it by
697
733
  model name (`src/llm/embedders/profile.ts`): Qwen3-Embedding gets
@@ -818,8 +854,8 @@ taking about the same wall time as a single one against a healthy endpoint.
818
854
  ### Curate rerank (#951)
819
855
 
820
856
  An optional cross-encoder rerank pass over the top fused search candidates
821
- `akm curate` fetches, via a standalone `/rerank`-style HTTP endpoint (NOT one
822
- of the `engines.*` `"llm"`/`"agent"` kinds). Each candidate is sent as its
857
+ `akm curate` fetches, via a standalone `/rerank`-style HTTP endpoint (not a
858
+ connection or a harness). Each candidate is sent as its
823
859
  name, description and the start of its indexed content (2,000 characters in
824
860
  all). Disabled by default; a misconfigured endpoint, network failure, timeout,
825
861
  or malformed response keeps the fused order.
@@ -829,7 +865,7 @@ or malformed response keeps the fused order.
829
865
  | `search.curateRerank.enabled` | Turn the rerank pass on (default `false`) |
830
866
  | `search.curateRerank.endpoint` | Full URL of the reranker's rerank endpoint |
831
867
  | `search.curateRerank.model` | Model name sent to the endpoint (optional) |
832
- | `search.curateRerank.apiKey` | `$VAR`/`secret://<name>` credential reference (optional) |
868
+ | `search.curateRerank.apiKey` | `$VAR` reference (optional); absent, the `curateRerank` service's entry in `credentials.json` |
833
869
  | `search.curateRerank.timeoutMs` | Request timeout (default `10000`) |
834
870
  | `search.curateRerank.topN` | How many of the top fused candidates to rerank (default `30`, max `50`) |
835
871
 
@@ -943,16 +979,18 @@ section, an absent key, and an explicit `false` all read identically as off.
943
979
 
944
980
  ```sh
945
981
  akm config list
946
- akm config get engines.fast
947
- akm config set engines.fast '{"kind":"llm","endpoint":"http://localhost:11434/v1/chat/completions","model":"qwen3"}'
948
- akm config set engines.fast.apiKey '$LOCAL_LLM_API_KEY'
949
- akm config unset engines.old
982
+ akm config get connections.default
983
+ akm config set connections.fast '{"endpoint":"http://localhost:11434/v1","models":{"fast":"qwen3-8b"}}'
984
+ akm config set connections.fast.apiKey '$LOCAL_LLM_API_KEY'
985
+ akm config unset connections.old
950
986
  ```
951
987
 
952
988
  Object values passed to `config set` deep-merge with their current value.
953
989
  Arrays replace, `null` is only valid for nullable fields, and `config unset` is
954
- the only deletion operation. `configVersion` cannot be set or unset with the
955
- generic walker.
990
+ the only deletion operation. It removes the named key and nothing else: a
991
+ parent object left empty stays, because `harnesses.opencode: {}` is how a
992
+ harness is enabled. `configVersion` cannot be set or unset with the generic
993
+ walker.
956
994
 
957
995
  `config get <key> --show-source` wraps the (redacted) value as
958
996
  `{ value, source }`, where `source` is `"local"` when the local file's own
@@ -962,20 +1000,19 @@ that sets it, or `"default"` when neither does. It is opt-in — plain
962
1000
 
963
1001
  ### Sharing configuration across installs
964
1002
 
965
- Five hosts running the same fleet often carry an identical `engines` map and
966
- `improve.strategies` block, differing only in credential delivery (`apiKey`
967
- vs `apiKeyFile`), bundle paths, and cron offsets. Hand-syncing that block
968
- across hosts drifts silently. `extends` fixes this: put the shared block in
1003
+ Five hosts running the same fleet often carry an identical `connections` map and
1004
+ `improve.strategies` block, differing only in credential delivery, bundle paths,
1005
+ and cron offsets. Hand-syncing that block across hosts drifts silently. `extends` fixes this: put the shared block in
969
1006
  one file, and have each host's local config extend it.
970
1007
 
971
1008
  ```jsonc
972
1009
  // bundles/fleet/config/shared.json — versioned with the bundle, shared by every host
973
1010
  {
974
1011
  "configVersion": "0.9.0",
975
- "engines": {
976
- "fast": { "kind": "llm", "endpoint": "https://api.example.test/v1/chat/completions", "model": "qwen3" }
1012
+ "connections": {
1013
+ "fast": { "endpoint": "https://api.example.test/v1", "models": { "fast": "qwen3", "balanced": "qwen3" } }
977
1014
  },
978
- "improve": { "strategies": { "nightly": { "engine": "fast" } } }
1015
+ "improve": { "connection": "fast", "strategies": { "nightly": { "processes": { "reflect": { "limit": 10 } } } } }
979
1016
  }
980
1017
  ```
981
1018
 
@@ -989,7 +1026,7 @@ one file, and have each host's local config extend it.
989
1026
  "stash": { "path": "~/akm-stash", "writable": true }
990
1027
  },
991
1028
  "defaultBundle": "stash",
992
- "engines": { "fast": { "apiKeyFile": "/run/secrets/fast-api-key" } }
1029
+ "connections": { "fast": { "apiKey": "$FAST_API_KEY" } }
993
1030
  }
994
1031
  ```
995
1032
 
@@ -1010,10 +1047,11 @@ one file, and have each host's local config extend it.
1010
1047
 
1011
1048
  Shared layers carry portable policy, not host authority. `bundles`, source and
1012
1049
  write defaults, registries, embedding connections, scheduler activation,
1013
- `execution`, `experimental`, and setup state are ignored when inherited.
1014
- Engine definitions may be shared, but credentials and executable authority
1015
- (`apiKey`, `apiKeyFile`, `bin`, `args`, and `workspace`) must be supplied by
1016
- the local file. Improve publication (`strategies.*.sync`) and reranker network
1050
+ `experimental`, and setup state are ignored when inherited.
1051
+ Connection and harness definitions may be shared, but credentials and
1052
+ executable authority (`apiKey`, `bin`, `args`, and `workspace`) are ignored
1053
+ when inherited and must be supplied by the local file; a key in the host's
1054
+ `credentials.json` needs no entry at all. Improve publication (`strategies.*.sync`) and reranker network
1017
1055
  configuration are local as well. Bundle-relative chains stay physically inside
1018
1056
  the bundle root for every hop; lexical `..` paths and symlink escapes are both
1019
1057
  rejected before a referenced file is read.
@@ -1057,9 +1095,6 @@ akm config diff fleet//config/shared.json
1057
1095
  | Variable | Purpose |
1058
1096
  | --- | --- |
1059
1097
  | `AKM_CONFIG_DIR` | Override the user config directory (or set `XDG_CONFIG_HOME`) |
1060
- | `AKM_ENGINE_<NAME>_API_KEY` | Fallback credential for LLM engine `<name>` |
1061
- | `AKM_LLM_API_KEY` | Fallback only for the selected `defaults.llmEngine` |
1062
- | `AKM_EMBED_API_KEY` | Embedding credential |
1063
1098
  | `AKM_BUNDLE_DIR` | Override the bundle directory |
1064
1099
  | `AKM_DATA_DIR` | Override the data directory — `index.db`, durable `state.db`, and `akm.lock` (or set `XDG_DATA_HOME`) |
1065
1100
  | `AKM_CACHE_DIR` | Override the cache directory — regenerable caches (or set `XDG_CACHE_HOME`) |
@@ -1068,80 +1103,64 @@ akm config diff fleet//config/shared.json
1068
1103
  | `AKM_VERBOSE` | Truthy value enables the same diagnostics as `--verbose` |
1069
1104
  | `AKM_DEBUG` | `1` prints a stack trace on unexpected internal errors |
1070
1105
 
1071
- For an engine named `fast`, its fallback variable is
1072
- `AKM_ENGINE_FAST_API_KEY`. An explicit `apiKey` symbolic reference is
1073
- authoritative and does not fall through to another variable.
1074
-
1075
- `engines.<name>.apiKeyFile` is a file-backed alternative to `apiKey`, for a
1076
- host that refuses to put secrets in the process environment (a container
1077
- runtime's mounted secret, for example). It is a plain filesystem path — `~`
1078
- expands to the home directory — read at dispatch time and trimmed of one
1079
- trailing newline; the raw path is safe to keep in `config.json` since it is
1080
- not itself a secret. Setting both `apiKey` and `apiKeyFile` on the same
1081
- engine is rejected. A missing, unreadable, or empty file fails the call
1082
- closed, naming the engine and path but never the file's content.
1083
-
1084
- `engines.<name>.apiKey` also accepts `secret://<name>`, a reference into
1085
- AKM's own secret store (`akm secret set <name> --from-file <file>`), for a
1086
- launch context where the credential's environment variable is deliberately
1087
- not sourced into the process — a scheduled task's crontab preamble, or a
1088
- container entrypoint that keeps the user's env out on purpose — and a
1089
- file-backed credential is not an option. Like `apiKeyFile`, only the
1090
- reference is kept in `config.json`; the store lookup happens at dispatch
1091
- time, and an unresolved reference fails the call closed, naming the
1092
- reference but never the value. `akm improve`, workflow LLM steps, and `akm
1093
- health`'s engine probes all resolve `secret://` the same way direct LLM and
1094
- embedding calls have since 0.9.13 (#917); resolution order for a single
1095
- `apiKey` field is: an env reference (`$VAR`/`${VAR}`) first, then
1096
- `apiKeyFile`, then `secret://<name>` — though in practice a config sets only
1097
- one of the three per engine.
1098
-
1099
- `embedding.apiKey` accepts the same three forms and resolves `secret://` the
1100
- same way, on every path that sends an embedding request: `akm index`
1101
- (including the reindex `akm bundle update` runs and the targeted re-embed a
1102
- write command like `akm remember` triggers), `akm improve`'s
1103
- consolidate pass (memory dedup and similarity clustering). All of them build the
1104
- provider request through the same `RemoteEmbedder`/`resolveSecret` boundary,
1105
- so a `secret://` reference resolves identically regardless of which command
1106
- triggered the request (#953).
1107
-
1108
1106
  Use `AKM_SQLITE_JOURNAL_MODE=DELETE` or `TRUNCATE` when WAL is unavailable,
1109
1107
  such as on some NFS/SMB mounts. With the default `WAL` setting, AKM detects a
1110
1108
  network filesystem for the data directory and falls back to `DELETE`.
1111
1109
 
1112
1110
  ## Retired Configuration
1113
1111
 
1114
- `profiles`, `llm`, `agent`, `features`, and `stashes` are rejected in 0.9.
1115
- Recreate the configuration using `engines`, `defaults.engine`,
1116
- `defaults.llmEngine`, and `improve.strategies`; AKM deliberately does not infer
1117
- or rename ambiguous profile identities. `defaults.llm`, `defaults.agent`,
1118
- `defaults.improve`, `index.stalenessDetection`, the engine settings that used to
1119
- sit under `index.<pass>` (`endpoint`, `provider`, `apiKey`, `baseUrl`,
1120
- `temperature`, `maxTokens`, `capabilities`) and the `feedbackDistillation`
1121
- process (use `processes.distill.enabled`) load like any other unknown key: named
1122
- once by the unknown-key warning, ignored, and dropped by `akm migrate apply`.
1123
-
1124
- `embedding.chunkSize` was never read by anything under `src/` (#954), so a
1125
- config that still sets it is simply ignored — it still loads, unvalidated
1126
- and without warning.
1127
-
1128
- `index.graph.*` and every strategy's `processes.graphExtraction.*` are retired
1129
- in 0.9.17-alpha.9: the LLM entity graph they configured is gone —
1130
- `akm show`'s links come from declared links instead (see `## Strategies`
1131
- above). A config that still sets them loads; each key is named once as
1132
- unknown, and `akm migrate apply` removes it. The built-in `graph-refresh`
1133
- strategy is retired too, but not the same way as an ordinary unknown name:
1134
- naming it via `--strategy` or a task always fails with a message pointing at
1135
- the retirement, even when `improve.strategies["graph-refresh"]` still has a
1136
- leftover override from customizing the built-in (the message names it;
1137
- `akm migrate apply` drops it — a leftover override is never resolved as a new
1138
- custom strategy, which would silently run a full, unplanned improve pass).
1139
- `defaults.improveStrategy: "graph-refresh"` still loads config successfully;
1140
- the refusal happens lazily, when the strategy is actually resolved.
1141
-
1142
- `improve.strategies.<name>.processes.consolidate.incrementalSince` and
1143
- `.neighborsPerChanged` are retired in 0.9.17-alpha.9: the consolidate pair
1144
- pass is now the candidate generator, narrowing per initiator through the
1145
- improve ledger rather than a global time window. A config that still sets
1146
- either key loads; each is named once as unknown, and `akm migrate apply`
1147
- removes it.
1112
+ A key akm no longer reads is named once, ignored, and dropped by `akm migrate
1113
+ apply`. That covers `profiles`, `llm`, `agent`, `features`, `stashes`,
1114
+ `defaults.llm`, `defaults.agent`, `defaults.improve`, the settings that sat
1115
+ under `index.<pass>` (`endpoint`, `provider`, `apiKey`, `baseUrl`,
1116
+ `temperature`, `maxTokens`, `capabilities`), `index.graph.*`, every strategy's
1117
+ `processes.graphExtraction.*`, a strategy's `processes.feedbackDistillation`
1118
+ (use `processes.distill.enabled`), `processes.consolidate.incrementalSince`
1119
+ and `neighborsPerChanged`, and `embedding.chunkSize`. The strategy name
1120
+ `graph-refresh` is always refused, even when `improve.strategies["graph-refresh"]`
1121
+ holds an override; `akm migrate apply` drops the override.
1122
+
1123
+ ### The `engines` form
1124
+
1125
+ A config that holds `engines` or `defaults` loads: akm reads it in memory as
1126
+ `connections` and `harnesses`, with one warning that names `akm migrate apply`.
1127
+ `akm migrate apply` makes the same conversion permanent, under a backup, and
1128
+ prints a note for everything it cannot carry. Credentials, `models.json` and
1129
+ asset keys move as the table says.
1130
+
1131
+ | In the file | What `akm migrate apply` does |
1132
+ | --- | --- |
1133
+ | `engines.<name>` with `kind: "llm"` | `connections.<name>`. `endpoint`, `contextLength`, `timeoutMs`, `concurrency` and an `apiKey` that is a `$VAR` are kept. `model` becomes `models`, with `fast`, `balanced` and `reasoning` all naming it. `temperature`, `maxTokens`, `enableThinking`, `reasoningEffort` and `extraParams` become request fields (`temperature`, `max_tokens`, `enable_thinking` with `chat_template_kwargs.enable_thinking`, `reasoning_effort`, and the extras as written). `kind`, `provider` and `supportsJsonSchema` are dropped, with a note. |
1134
+ | `engines.<name>` with `kind: "agent"` and `platform: "opencode-sdk"` | Removed: akm's own runtime has no entry. Its `llmEngine` connection becomes `default`. |
1135
+ | `engines.<name>` with `kind: "agent"`, any other platform | `harnesses.<name>`, keeping `platform`, `bin`, `args`, `workspace`, `agent` and `timeoutMs`. `model` becomes `models`, naming it for all three aliases. |
1136
+ | `defaults.llmEngine`, else `defaults.engine` when it names an LLM engine | That connection is renamed `default`, and every reference to the old name follows. An entry already called `default` moves aside to `legacy-default`, or the first `legacy-default-<n>` nothing else has: the renaming is one name to one name, so no two entries share a name and none is lost. |
1137
+ | `defaults.engine` naming an agent engine, when there are several | That harness is renamed `default`. |
1138
+ | `defaults.improveStrategy` | `improve.strategy`. |
1139
+ | `models.json` | Folded into the `models` of the connections and harnesses it names; what it cannot place is listed in the notes. The file moves to `config-backups/` in the cache directory. |
1140
+ | `apiKeyFile`, a literal `apiKey`, a `${secrets/...}` `apiKey` | The key moves to `credentials.json` for the connection and its endpoint; a note names where it was, never its value. A key that cannot be read stays on the connection as it was (`apiKey`, or `apiKeyFile` for a key file), with a note that it is still to do; a later run moves it once it can be read. |
1141
+ | `AKM_ENGINE_<NAME>_API_KEY` or `AKM_LLM_API_KEY` set in the environment | `apiKey: "$AKM_ENGINE_<NAME>_API_KEY"` (or `$AKM_LLM_API_KEY`) on that connection. Set in no variable, an engine that names no key gets none, and a note names the variables 0.9 read. |
1142
+ | `improve.strategies.<name>.engine`, `model`, `llm`, `timeoutMs` | Dropped, with a note: a strategy says which processes run, not where. |
1143
+ | `engine`, `model`, `llm`, `timeoutMs` on a strategy's process, its `qualityGate` or its `triage.judgment` | Moved to `improve.processes.<process>` or `improve.qualityGate`, with `engine` becoming `connection` or `harness` by what it named. When that place already says, the first one is kept, with a note. |
1144
+ | `index.defaults.*`, `index.<pass>.engine` | `index.*`, and `connection`. An index pass cannot name a harness, so an engine that named one is dropped, with a note; a pass that names nothing runs where model work does: on the default connection, else the default harness. |
1145
+ | `workflow.judgeEngine` | Dropped, with a note. |
1146
+ | `engine:` in an asset's frontmatter, a workflow step or a task source | `connection:` or `harness:`, by what the configured target is; a name it cannot place becomes `harness:`, with a note. A bundle akm may not change, and an asset that is a symbolic link, are skipped. |
1147
+
1148
+ An `engine:` key on an asset that has not been migrated is read the same way,
1149
+ with a one-time notice, and so is `--engine NAME` (hidden from `--help`): both
1150
+ name a connection by that name, else a harness. While the config file is still
1151
+ in the `engines` form they name the connection or harness that engine became, so
1152
+ `engine: lab` still means the engine called `lab` after the conversion has
1153
+ renamed it `default`. Once `akm migrate apply` has rewritten the file, the names
1154
+ the engines had are kept in akm's data directory
1155
+ (`migrate/engine-targets.json`), and the key and the flag go on meaning the same
1156
+ engine until the migrator has rewritten every `engine:` key it can reach; an
1157
+ asset in a read-only bundle, or a block it could not splice, is rewritten under
1158
+ the same names by a later run, and an asset that is a symbolic link is read but
1159
+ never rewritten, so it is listed as skipped and keeps the names until you change
1160
+ the file it leads to. Then the names are forgotten, an old name that is
1161
+ neither a connection nor a harness fails like any unconfigured name (exit 78),
1162
+ and the run that forgets them prints one note listing each renamed engine and
1163
+ the flag that replaces it. A script or scheduled command that passes `--engine`
1164
+ is not one the migrator can reach: change it to `--connection` or `--harness`. A
1165
+ remembered name whose connection is no longer configured fails like any
1166
+ selection of an unconfigured name.