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
@@ -0,0 +1,304 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /**
5
+ * API keys akm holds for the user: `<configDir>/credentials.json` (0600).
6
+ *
7
+ * { "connections": { "<name>": { "key": "…", "endpoint": "…" } },
8
+ * "services": { "embedding": { "key": "…", "endpoint": "…" },
9
+ * "curateRerank": { … } } }
10
+ *
11
+ * A key is stored for one endpoint and is read for that endpoint alone: the
12
+ * entry records the endpoint it was stored for, and a block whose endpoint is
13
+ * now another gets no key, with a word saying so. Only the connections this
14
+ * host's own config file defines have a stored key: one that only an `extends`
15
+ * base defines gets none, whatever the store holds under its name. A connection's name and a
16
+ * service's slot are different namespaces, so a connection called `embedding`
17
+ * and the embedding service hold different keys. A block whose `apiKey` is a
18
+ * `$VAR` reference reads that variable and never touches this file. The bundle's
19
+ * `secrets/` store is for secrets the user's own agents need and has nothing to
20
+ * do with these.
21
+ */
22
+ import fs from "node:fs";
23
+ import path from "node:path";
24
+ import { isRecord, writeFileAtomic } from "../common.js";
25
+ import { sameEndpoint } from "../endpoint-url.js";
26
+ import { ConfigError } from "../errors.js";
27
+ import { getConfigDir } from "../paths.js";
28
+ import { collectSensitiveValues, redactUrlCredentials } from "../redaction.js";
29
+ import { warnOnce } from "../warn.js";
30
+ import { isInheritedConnection } from "./config.js";
31
+ import { copyToConfigBackups } from "./config-io.js";
32
+ import { envReferenceName, isEnvReference } from "./schema/primitives.js";
33
+ const NAMESPACES = ["connections", "services"];
34
+ function storePath() {
35
+ return path.join(getConfigDir(), "credentials.json");
36
+ }
37
+ function namespaceOf(slot) {
38
+ return "connection" in slot
39
+ ? { group: "connections", name: slot.connection }
40
+ : { group: "services", name: slot.service };
41
+ }
42
+ function describeSlot(slot) {
43
+ return "connection" in slot ? `connection "${slot.connection}"` : `the ${slot.service} service`;
44
+ }
45
+ /** The stored entries; an absent file holds none. */
46
+ function readStore() {
47
+ let text;
48
+ try {
49
+ text = fs.readFileSync(storePath(), "utf8");
50
+ }
51
+ catch (err) {
52
+ const code = err.code;
53
+ return code === "ENOENT"
54
+ ? { entries: {} }
55
+ : { problem: `${storePath()} cannot be read (${code ?? "unknown error"})` };
56
+ }
57
+ try {
58
+ const parsed = JSON.parse(text);
59
+ if (isRecord(parsed))
60
+ return { entries: parsed };
61
+ }
62
+ catch {
63
+ // Falls through: not JSON is not a JSON object.
64
+ }
65
+ return { problem: `${storePath()} is not a JSON object` };
66
+ }
67
+ function writeStore(entries) {
68
+ fs.mkdirSync(path.dirname(storePath()), { recursive: true, mode: 0o700 });
69
+ writeFileAtomic(storePath(), `${JSON.stringify(entries, null, 2)}\n`, 0o600);
70
+ }
71
+ /** The group of entries (`connections` or `services`) a store holds; anything else there is not one. */
72
+ function groupOf(entries, group) {
73
+ const held = entries[group];
74
+ return isRecord(held) ? held : {};
75
+ }
76
+ /** Whether a stored value is the current shape of an entry. */
77
+ function isStoredCredential(value) {
78
+ return isRecord(value) && typeof value.key === "string" && typeof value.endpoint === "string";
79
+ }
80
+ /**
81
+ * The stored entry for `slot`, if the store has one in the current shape. One
82
+ * the store has in any other shape (the flat `{ "<name>": "<key>" }` an earlier
83
+ * build wrote, which names no endpoint) is named once and ignored: `akm migrate
84
+ * apply` files it under the endpoint it belongs to.
85
+ */
86
+ export function storedCredential(slot) {
87
+ const store = readStore();
88
+ if ("problem" in store) {
89
+ warnOnce(`credentials:unusable:${storePath()}`, `${store.problem}; ignoring it.`);
90
+ return undefined;
91
+ }
92
+ const { group, name } = namespaceOf(slot);
93
+ const held = groupOf(store.entries, group)[name];
94
+ if (held === undefined) {
95
+ const flat = store.entries[name];
96
+ if (typeof flat === "string") {
97
+ warnOnce(`credentials:flat:${storePath()}:${name}`, `${storePath()}: the key "${name}" is in the older form, which names no endpoint, so it is not used; \`akm migrate apply\` files it under the endpoint it belongs to.`);
98
+ }
99
+ return undefined;
100
+ }
101
+ if (!isStoredCredential(held)) {
102
+ warnOnce(`credentials:malformed:${storePath()}:${group}:${name}`, `${storePath()}: the entry for ${describeSlot(slot)} is not { "key": "…", "endpoint": "…" }; ignoring it.`);
103
+ return undefined;
104
+ }
105
+ return held;
106
+ }
107
+ /**
108
+ * The key stored for `slot` and `endpoint`. A key stored for another endpoint is
109
+ * not read: it is named once, without its value, and the block goes without.
110
+ * `storedKey` is a connection's eligibility already decided (a runner carries it);
111
+ * absent, the config on this host decides now.
112
+ */
113
+ export function readCredential(slot, endpoint, storedKey) {
114
+ if ("connection" in slot) {
115
+ const eligible = storedKey ?? !isInheritedConnection(slot.connection);
116
+ if (!eligible)
117
+ return undefined;
118
+ }
119
+ const held = storedCredential(slot);
120
+ if (!held)
121
+ return undefined;
122
+ if (!sameEndpoint(held.endpoint, endpoint)) {
123
+ const shownHeld = redactUrlCredentials(held.endpoint);
124
+ const shownNow = redactUrlCredentials(endpoint);
125
+ warnOnce(`credentials:endpoint:${storePath()}:${describeSlot(slot)}:${shownHeld}:${shownNow}`, `The key stored for ${describeSlot(slot)} belongs to ${shownHeld}, not ${shownNow}, so it is not sent there. Store a key for the new endpoint with \`akm setup\`.`);
126
+ return undefined;
127
+ }
128
+ return held.key || undefined;
129
+ }
130
+ function writableStore() {
131
+ const store = readStore();
132
+ if ("problem" in store)
133
+ throw new ConfigError(`${store.problem}; refusing to overwrite it.`, "INVALID_CONFIG_FILE");
134
+ return store.entries;
135
+ }
136
+ /**
137
+ * Store `key` for `slot` and `endpoint`, keeping every other entry as it is. A root
138
+ * `connections` or `services` that is not an object is a key of the older flat form
139
+ * (a connection of that name) in the namespace's place: it is kept, and the write stops,
140
+ * because the write would replace it. `akm migrate apply` files it under its endpoint.
141
+ */
142
+ export function writeCredential(slot, key, endpoint) {
143
+ const entries = writableStore();
144
+ const { group, name } = namespaceOf(slot);
145
+ const root = entries[group];
146
+ if (root !== undefined && !isRecord(root)) {
147
+ throw new ConfigError(`${storePath()}: "${group}" holds ${typeof root === "string" ? "a key in the older form" : "something that is not an object"}, which is where the ${group} are stored, so ${describeSlot(slot)} cannot be stored beside it; refusing to overwrite it. ${typeof root === "string" ? "`akm migrate apply` files it under its endpoint; then store this key again." : "Fix the file, then store this key again."}`, "INVALID_CONFIG_FILE");
148
+ }
149
+ writeStore({ ...entries, [group]: { ...groupOf(entries, group), [name]: { key, endpoint } } });
150
+ }
151
+ /** The entries the store holds in the current shape, by slot, whatever endpoint each is stored for. */
152
+ export function storedCredentials() {
153
+ const store = readStore();
154
+ if ("problem" in store)
155
+ return [];
156
+ return NAMESPACES.flatMap((group) => Object.entries(groupOf(store.entries, group)).flatMap(([name, held]) => isStoredCredential(held)
157
+ ? [
158
+ {
159
+ slot: group === "connections" ? { connection: name } : { service: name },
160
+ credential: held,
161
+ },
162
+ ]
163
+ : []));
164
+ }
165
+ /**
166
+ * Every migration into a credential slot asks this one question. An empty slot is
167
+ * `free`; one holding exactly this key for this endpoint is `same` (filing it again
168
+ * changes nothing); anything else, a malformed entry included, is `different`, and
169
+ * is never replaced: the migration leaves both and says so. `ahead` is the entry the
170
+ * same migration has already planned for the slot, which stands in for the store's.
171
+ */
172
+ export function slotStanding(slot, key, endpoint, ahead) {
173
+ let held = ahead;
174
+ if (held === undefined) {
175
+ const store = readStore();
176
+ // An unusable store is not a conflict: it refuses every write, which stops the migration with nothing lost.
177
+ if ("problem" in store)
178
+ return "free";
179
+ const { group, name } = namespaceOf(slot);
180
+ held = groupOf(store.entries, group)[name];
181
+ }
182
+ if (held === undefined)
183
+ return "free";
184
+ return isStoredCredential(held) && held.key === key && sameEndpoint(held.endpoint, endpoint) ? "same" : "different";
185
+ }
186
+ /**
187
+ * File every key the store holds in the older flat `{ "<name>": "<key>" }` form under
188
+ * the endpoint of the block it belongs to, as `endpointOf` says: a name that is a
189
+ * connection's, or `curateRerank`, or `embedding` (the service's, or a connection
190
+ * called that: one of the two must have an endpoint, or it is not filed). `pending` is
191
+ * what the same migration is about to file in the current form, which a flat key is
192
+ * measured against like the store's own entries ({@link slotStanding}): a slot that
193
+ * already holds the same key for the same endpoint just drops the older entry, and one
194
+ * that holds anything else keeps the older entry, with a note. An entry that cannot be
195
+ * filed stays as it is, and a note says why. Writes only when `apply`, after copying
196
+ * the store to the config-backups directory (`backupPath`).
197
+ */
198
+ export function fileFlatCredentials(endpointOf, apply, pending = []) {
199
+ const store = readStore();
200
+ if ("problem" in store)
201
+ return { filed: [], notes: [`${store.problem}; its keys were not filed.`] };
202
+ const entries = { ...store.entries };
203
+ const filed = [];
204
+ const notes = [];
205
+ // A key called `connections` or `services` is told from the namespace of that name by its shape: a string. It sits
206
+ // where the namespace goes, so it is filed first, and until it is, no other key can be filed into that namespace.
207
+ const rank = (name) => {
208
+ const index = NAMESPACES.indexOf(name);
209
+ return index < 0 ? NAMESPACES.length : index;
210
+ };
211
+ for (const name of Object.keys(store.entries).sort((a, b) => rank(a) - rank(b))) {
212
+ const value = store.entries[name];
213
+ if (typeof value !== "string")
214
+ continue;
215
+ const slots = name === "curateRerank"
216
+ ? [{ service: "curateRerank" }]
217
+ : name === "embedding"
218
+ ? [{ service: "embedding" }, { connection: name }]
219
+ : [{ connection: name }];
220
+ const homes = slots.flatMap((slot) => {
221
+ const endpoint = endpointOf(slot);
222
+ return endpoint === undefined ? [] : [{ slot, endpoint }];
223
+ });
224
+ const home = homes[0];
225
+ if (homes.length !== 1 || home === undefined) {
226
+ notes.push(homes.length > 1
227
+ ? `credentials.json: the key "${name}" could belong to the embedding service or to the connection of that name; store it again with \`akm setup\`. It stays as it is.`
228
+ : `credentials.json: the key "${name}" belongs to no block this config gives an endpoint, so it cannot be bound to one. It stays as it is.`);
229
+ continue;
230
+ }
231
+ const { group, name: slotName } = namespaceOf(home.slot);
232
+ if (name !== group && entries[group] !== undefined && !isRecord(entries[group])) {
233
+ notes.push(`credentials.json: the key "${name}" cannot be filed while the key "${group}" in the older form holds the place the ${group} go. It stays as it is; give the block called "${group}" an endpoint so it can be filed first, or remove it.`);
234
+ continue;
235
+ }
236
+ const ahead = pending.find((move) => JSON.stringify(move.slot) === JSON.stringify(home.slot));
237
+ const standing = slotStanding(home.slot, value, home.endpoint, ahead && { key: ahead.value, endpoint: ahead.endpoint });
238
+ if (standing === "different") {
239
+ notes.push(`credentials.json: ${describeSlot(home.slot)} already holds a different key or endpoint than the older entry "${name}"; neither is replaced. Remove the older entry when the stored one is the key you want.`);
240
+ continue;
241
+ }
242
+ if (standing === "free" || name === group) {
243
+ entries[group] = { ...groupOf(entries, group), [slotName]: { key: value, endpoint: home.endpoint } };
244
+ }
245
+ // The key called like its own namespace has just become that namespace's child, in the same place.
246
+ if (name !== group)
247
+ delete entries[name];
248
+ filed.push(name);
249
+ notes.push(standing === "free"
250
+ ? `credentials.json: the key "${name}" is filed for ${describeSlot(home.slot)} at ${redactUrlCredentials(home.endpoint)}.`
251
+ : `credentials.json: the key "${name}" is already stored for ${describeSlot(home.slot)} at ${redactUrlCredentials(home.endpoint)}; the older entry is removed.`);
252
+ }
253
+ if (!apply || filed.length === 0)
254
+ return { filed, notes };
255
+ const backupPath = copyToConfigBackups(storePath(), (stamp) => `credentials.json.${stamp}`);
256
+ writeStore(entries);
257
+ return { filed, notes, backupPath };
258
+ }
259
+ /**
260
+ * The key of the block stored in `slot`, whose config `apiKey` is `apiKey` and
261
+ * whose endpoint is `endpoint`: the variable it references, else the key stored
262
+ * for that endpoint. An unset variable is named once and sends no key. `quiet`
263
+ * is for callers that only collect values to redact; `storedKey` is as for
264
+ * {@link readCredential}.
265
+ */
266
+ export function resolveApiKey(apiKey, slot, endpoint, env = process.env, quiet = false, storedKey) {
267
+ const variable = apiKey === undefined ? undefined : envReferenceName(apiKey);
268
+ if (variable === undefined)
269
+ return readCredential(slot, endpoint, storedKey);
270
+ const value = env[variable]?.trim();
271
+ if (!value && !quiet) {
272
+ warnOnce(`credentials:env-unset:${variable}`, `Environment variable ${variable} referenced by an apiKey is unset or empty; requests are sent without a key.`);
273
+ }
274
+ return value || undefined;
275
+ }
276
+ /**
277
+ * The `$VAR` reference an `apiKey` holds. Anything else in that field is a key
278
+ * the user left in the file, which akm never reads or sends and never passes on.
279
+ */
280
+ export function apiKeyReference(apiKey) {
281
+ return apiKey !== undefined && isEnvReference(apiKey) ? apiKey : undefined;
282
+ }
283
+ /**
284
+ * Every key akm could send or has been given, for redaction: whatever the store
285
+ * holds, the variables the config's blocks reference, and any key left in the
286
+ * file as written.
287
+ */
288
+ export function collectCredentialValues(config, env = process.env) {
289
+ const blocks = [
290
+ ...Object.values(config.connections ?? {}),
291
+ config.embedding,
292
+ config.search?.curateRerank,
293
+ ];
294
+ return collectSensitiveValues([
295
+ ...storedCredentials().map(({ credential }) => credential.key),
296
+ ...blocks.flatMap((block) => {
297
+ const variable = block?.apiKey === undefined ? undefined : envReferenceName(block.apiKey);
298
+ return [
299
+ variable === undefined ? undefined : env[variable]?.trim(),
300
+ apiKeyReference(block?.apiKey) === undefined ? block?.apiKey : undefined,
301
+ ];
302
+ }),
303
+ ]);
304
+ }
@@ -16,8 +16,8 @@ export const REMOVED_IMPROVE_STRATEGY_NAMES = [
16
16
  "proactive-maintenance",
17
17
  ];
18
18
  /**
19
- * The improve processes that use an engine. Triage's engine is its judgment's;
20
- * each of the others makes the process's own model calls.
19
+ * The improve processes that make model calls, so each runs on a connection or
20
+ * harness. Triage's is its judgment's; each of the others makes the process's own calls.
21
21
  */
22
22
  export const IMPROVE_ENGINE_PROCESSES = [
23
23
  "reflect",
@@ -28,5 +28,5 @@ export const IMPROVE_ENGINE_PROCESSES = [
28
28
  "validation",
29
29
  "triage",
30
30
  ];
31
- /** Every improve process, in plan order: the engine processes and `proactiveMaintenance`, which uses none. */
31
+ /** Every improve process, in plan order: the model-calling processes and `proactiveMaintenance`, which makes no model call. */
32
32
  export const IMPROVE_PROCESS_NAMES = [...IMPROVE_ENGINE_PROCESSES, "proactiveMaintenance"];
@@ -0,0 +1,222 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /**
5
+ * `akm migrate apply`'s key step. akm reads a key from a `$VAR` reference or
6
+ * from `credentials.json`, nowhere else. The keys a config still holds in any
7
+ * other form are read once, filed in `credentials.json` for the block they
8
+ * belong to (a connection, or the `embedding` or `curateRerank` service) and the
9
+ * endpoint that block names, and removed from the document:
10
+ *
11
+ * - an engine's `apiKeyFile`, a literal `apiKey` or a `${secrets/...}` one,
12
+ * which the conversion to a connection cannot carry;
13
+ * - the same literal or secret-store `apiKey` on a connection, `embedding` or
14
+ * `search.curateRerank` that the file already says in the current form.
15
+ *
16
+ * A key that cannot be read, whose block names no endpoint to file it for, whose
17
+ * engine wrote it for another endpoint than the connection now names (the file's
18
+ * current-form entries overlay the converted engine), or whose slot
19
+ * `credentials.json` already holds a different key or endpoint for, is not moved. It stays in the file: on the block that held
20
+ * it, or, for an engine's `apiKey`, put back on the connection as `apiKey`
21
+ * exactly as it was, so the rewrite never leaves it in the backups alone (akm
22
+ * ignores a key held there, with a warning, until it is moved). An engine's
23
+ * `apiKeyFile` is the exception: it names a file, not a key, and a connection
24
+ * cannot hold it (an unowned key is sent as a request field), so a note names
25
+ * the path. A note names the key's place and its entry, never its value.
26
+ */
27
+ import fs from "node:fs";
28
+ import os from "node:os";
29
+ import path from "node:path";
30
+ import { isRecord } from "../common.js";
31
+ import { sameEndpoint } from "../endpoint-url.js";
32
+ import { redactUrlCredentials } from "../redaction.js";
33
+ import { isSecretRefToken } from "../ref-token.js";
34
+ import { slotStanding } from "./credentials.js";
35
+ import { isEnvReference } from "./schema/primitives.js";
36
+ /** An `apiKeyFile`'s key: the file's contents less one trailing newline, `~` expanded as it always was. */
37
+ function readKeyFile(file) {
38
+ try {
39
+ const expanded = file.startsWith("~") ? path.join(os.homedir(), file.slice(1)) : file;
40
+ return fs.readFileSync(expanded, "utf8").replace(/\r?\n$/, "") || undefined;
41
+ }
42
+ catch {
43
+ return undefined;
44
+ }
45
+ }
46
+ /** A key held in the config itself: the literal, or the secret it references. */
47
+ function heldValue(value, options) {
48
+ if (!isSecretRefToken(value))
49
+ return value;
50
+ try {
51
+ return options.resolveSecretRef?.(value) || undefined;
52
+ }
53
+ catch {
54
+ return undefined;
55
+ }
56
+ }
57
+ /** The keys of the converted document that are not `$VAR` references. */
58
+ function documentCandidates(config, options) {
59
+ const blocks = [];
60
+ if (isRecord(config.connections)) {
61
+ for (const [name, block] of Object.entries(config.connections)) {
62
+ if (isRecord(block))
63
+ blocks.push({ name, slot: { connection: name }, from: `connections.${name}.apiKey`, block });
64
+ }
65
+ }
66
+ const search = isRecord(config.search) ? config.search : {};
67
+ if (isRecord(config.embedding)) {
68
+ blocks.push({
69
+ name: "embedding",
70
+ slot: { service: "embedding" },
71
+ from: "embedding.apiKey",
72
+ block: config.embedding,
73
+ });
74
+ }
75
+ if (isRecord(search.curateRerank)) {
76
+ blocks.push({
77
+ name: "curateRerank",
78
+ slot: { service: "curateRerank" },
79
+ from: "search.curateRerank.apiKey",
80
+ block: search.curateRerank,
81
+ });
82
+ }
83
+ return blocks.flatMap(({ name, slot, from, block }) => {
84
+ const endpoint = typeof block.endpoint === "string" ? block.endpoint : undefined;
85
+ const found = [];
86
+ const apiKey = block.apiKey;
87
+ if (typeof apiKey === "string" && !isEnvReference(apiKey)) {
88
+ found.push({
89
+ name,
90
+ slot,
91
+ endpoint,
92
+ from,
93
+ read: () => heldValue(apiKey, options),
94
+ source: isSecretRefToken(apiKey) ? "the secret store reference" : "the literal key",
95
+ remove: () => {
96
+ delete block.apiKey;
97
+ },
98
+ stays: "in the file",
99
+ });
100
+ }
101
+ // A key file an earlier run could not read is still named here, so a later run can move its key.
102
+ const keyFile = block.apiKeyFile;
103
+ if (typeof keyFile === "string" && "connection" in slot) {
104
+ found.push({
105
+ name,
106
+ slot,
107
+ endpoint,
108
+ from: from.replace(/apiKey$/, "apiKeyFile"),
109
+ read: () => readKeyFile(keyFile),
110
+ source: `its key file ${keyFile}`,
111
+ remove: () => {
112
+ delete block.apiKeyFile;
113
+ },
114
+ stays: "in the file",
115
+ });
116
+ }
117
+ return found;
118
+ });
119
+ }
120
+ /** The endpoint the document gives the block that `slot` stands for, if it gives one. */
121
+ export function documentEndpoint(config, slot) {
122
+ const search = isRecord(config.search) ? config.search : {};
123
+ const block = "connection" in slot
124
+ ? isRecord(config.connections)
125
+ ? config.connections[slot.connection]
126
+ : undefined
127
+ : slot.service === "embedding"
128
+ ? config.embedding
129
+ : search.curateRerank;
130
+ return isRecord(block) && typeof block.endpoint === "string" ? block.endpoint : undefined;
131
+ }
132
+ /** The keys the conversion could not carry from an engine, read from the original document. */
133
+ function engineCandidates(original, converted, config, options) {
134
+ const engines = isRecord(original.engines) ? original.engines : {};
135
+ return converted.keys.flatMap(({ engine, connection, field }) => {
136
+ const source = isRecord(engines[engine]) ? engines[engine] : {};
137
+ const held = source[field];
138
+ if (typeof held !== "string")
139
+ return [];
140
+ const owner = typeof source.endpoint === "string" ? source.endpoint : undefined;
141
+ if (field === "apiKeyFile") {
142
+ return [
143
+ {
144
+ name: connection,
145
+ slot: { connection },
146
+ endpoint: documentEndpoint(config, { connection }),
147
+ owner,
148
+ from: `engines.${engine}.${field}`,
149
+ read: () => readKeyFile(held),
150
+ source: `its key file ${held}`,
151
+ restore: () => {
152
+ const block = isRecord(config.connections) ? config.connections[connection] : undefined;
153
+ if (isRecord(block))
154
+ block.apiKeyFile = held;
155
+ },
156
+ stays: "on the connection as apiKeyFile",
157
+ },
158
+ ];
159
+ }
160
+ return [
161
+ {
162
+ name: connection,
163
+ slot: { connection },
164
+ endpoint: documentEndpoint(config, { connection }),
165
+ owner,
166
+ from: `engines.${engine}.${field}`,
167
+ read: () => heldValue(held, options),
168
+ source: isSecretRefToken(held) ? `the secret store reference ${held}` : "the literal key",
169
+ restore: () => {
170
+ const block = isRecord(config.connections) ? config.connections[connection] : undefined;
171
+ if (isRecord(block))
172
+ block.apiKey = held;
173
+ },
174
+ stays: "on the connection as apiKey",
175
+ },
176
+ ];
177
+ });
178
+ }
179
+ export function planKeyMoves(original, converted, options = {}) {
180
+ const config = structuredClone(converted.config);
181
+ const planned = new Map();
182
+ const moves = [];
183
+ const notes = [];
184
+ for (const candidate of [
185
+ ...engineCandidates(original, converted, config, options),
186
+ ...documentCandidates(config, options),
187
+ ]) {
188
+ const stays = ` It stays ${candidate.stays}.`;
189
+ const { owner } = candidate;
190
+ if (owner !== undefined && candidate.endpoint !== undefined && !sameEndpoint(owner, candidate.endpoint)) {
191
+ candidate.restore?.();
192
+ notes.push(`${candidate.from}: this key was written for ${redactUrlCredentials(owner)}, and the file now gives "${candidate.name}" ${redactUrlCredentials(candidate.endpoint)}, so no key moves and it is not sent there.${stays} Store a key for the new endpoint with \`akm setup\`.`);
193
+ continue;
194
+ }
195
+ const value = candidate.read();
196
+ if (value === undefined) {
197
+ candidate.restore?.();
198
+ notes.push(`${candidate.from}: ${candidate.source} could not be read, so no key moves and this is still to do.${stays} A later \`akm migrate apply\` moves it once it can be read.`);
199
+ continue;
200
+ }
201
+ const { endpoint } = candidate;
202
+ if (endpoint === undefined) {
203
+ candidate.restore?.();
204
+ notes.push(`${candidate.from}: the block names no endpoint of its own, so its key cannot be filed for one and no key moves.${stays} Store it for the endpoint that serves it with \`akm setup\`.`);
205
+ continue;
206
+ }
207
+ const slotKey = JSON.stringify(candidate.slot);
208
+ const earlier = planned.get(slotKey);
209
+ const standing = slotStanding(candidate.slot, value, endpoint, earlier && { key: earlier.value, endpoint: earlier.endpoint });
210
+ if (standing === "different") {
211
+ candidate.restore?.();
212
+ notes.push(`${candidate.from}: credentials.json already holds a different key or endpoint for "${candidate.name}"; it is not replaced.${stays}`);
213
+ continue;
214
+ }
215
+ const move = { slot: candidate.slot, endpoint, name: candidate.name, value, from: candidate.from };
216
+ planned.set(slotKey, move);
217
+ moves.push(move);
218
+ notes.push(`${candidate.from}: its key moves to credentials.json for "${candidate.name}" at ${redactUrlCredentials(endpoint)}.`);
219
+ candidate.remove?.();
220
+ }
221
+ return { config, moves, notes };
222
+ }
@@ -0,0 +1,37 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /**
5
+ * The model aliases akm itself speaks: a call asks for a model by one of these
6
+ * (or by an exact name), and each connection or harness says which of its own
7
+ * models answers to them.
8
+ */
9
+ export const STANDARD_ALIASES = ["fast", "balanced", "reasoning"];
10
+ /**
11
+ * The alias each kind of call asks for when nothing nearer (an asset, a process,
12
+ * a flag) names a model. It is the farthest layer of the call.
13
+ */
14
+ export const SHIPPED_ALIAS = {
15
+ /** Improve's passes: reflect, distill, consolidate, memory inference, validation. */
16
+ improve: "fast",
17
+ /** Judges: the reflect and distill quality gates, the triage judgment, a workflow gate. */
18
+ judge: "balanced",
19
+ /** Agent work: `akm agent`, `command run`, `proposal new`, task dispatch, workflow agent units. */
20
+ agent: "balanced",
21
+ extract: "fast",
22
+ enrich: "fast",
23
+ /** The index passes. */
24
+ index: "fast",
25
+ };
26
+ /** One model answering to every standard alias: what a connection with a single model has. */
27
+ export function modelForEveryAlias(model) {
28
+ return { fast: model, balanced: model, reasoning: model };
29
+ }
30
+ /** The model name an alias entry holds. */
31
+ export function entryModel(entry) {
32
+ return typeof entry === "string" ? entry : entry.model;
33
+ }
34
+ /** The entry for `entry` once it names `model`: an entry with request fields keeps them. */
35
+ export function entryWithModel(entry, model) {
36
+ return typeof entry === "object" ? { ...entry, model } : model;
37
+ }
@@ -0,0 +1,34 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /**
5
+ * The user's `models.json` (retired): `<configDir>/models.json`, an alias to
6
+ * model table per engine. It is read, in memory, as part of the conversion of
7
+ * the `engines` form ({@link convertEngineConfig}); `akm migrate apply` moves it
8
+ * out of the config directory once its aliases are folded into the config.
9
+ */
10
+ import fs from "node:fs";
11
+ import path from "node:path";
12
+ import { getConfigDir } from "../paths.js";
13
+ import { copyToConfigBackups } from "./config-io.js";
14
+ function modelsFilePath() {
15
+ return path.join(getConfigDir(), "models.json");
16
+ }
17
+ /** The user's `models.json`, parsed; absent or unreadable is no overlay. */
18
+ export function readModelsFile() {
19
+ try {
20
+ return JSON.parse(fs.readFileSync(modelsFilePath(), "utf8"));
21
+ }
22
+ catch {
23
+ return undefined;
24
+ }
25
+ }
26
+ export function modelsFileExists() {
27
+ return fs.existsSync(modelsFilePath());
28
+ }
29
+ /** Move `models.json` to `<cacheDir>/config-backups/models.json.<timestamp>` and return where it went. */
30
+ export function retireModelsFile(now = new Date()) {
31
+ const target = copyToConfigBackups(modelsFilePath(), (stamp) => `models.json.${stamp}`, now);
32
+ fs.rmSync(modelsFilePath());
33
+ return target;
34
+ }
@@ -0,0 +1,53 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /**
5
+ * What the engine names of the retired `engines` form became, kept in the data
6
+ * directory (`migrate/engine-targets.json`) after `akm migrate apply` has rewritten
7
+ * the config. The conversion renames the engine `defaults` named to `default` and
8
+ * moves an old `default` aside, so an `engine:` selector that is still in a file the
9
+ * migration has not rewritten (a read-only bundle, a block it could not splice) keeps
10
+ * its meaning only if the old names outlive the config that had them. The migrator
11
+ * writes and removes this file; the resolver of `engine:` selectors reads it
12
+ * ({@link retiredEngineTargets}); nothing else does.
13
+ */
14
+ import fs from "node:fs";
15
+ import path from "node:path";
16
+ import { isRecord } from "../common.js";
17
+ import { getDataDir } from "../paths.js";
18
+ function rememberedPath() {
19
+ return path.join(getDataDir(), "migrate", "engine-targets.json");
20
+ }
21
+ /** An old engine name's entry, when it is `{ "connection": "<name>" }` or `{ "harness": "<name>" }`. */
22
+ function targetOf(value) {
23
+ if (!isRecord(value))
24
+ return undefined;
25
+ if (typeof value.connection === "string" && value.connection)
26
+ return { connection: value.connection };
27
+ if (typeof value.harness === "string" && value.harness)
28
+ return { harness: value.harness };
29
+ return undefined;
30
+ }
31
+ /** The names an earlier run's config step recorded; a file that is not a name map, or an entry that is not a target, holds none. */
32
+ export function rememberedEngineTargets() {
33
+ let parsed;
34
+ try {
35
+ parsed = JSON.parse(fs.readFileSync(rememberedPath(), "utf8"));
36
+ }
37
+ catch {
38
+ return {};
39
+ }
40
+ if (!isRecord(parsed))
41
+ return {};
42
+ return Object.fromEntries(Object.entries(parsed).flatMap(([engine, value]) => {
43
+ const target = targetOf(value);
44
+ return target ? [[engine, target]] : [];
45
+ }));
46
+ }
47
+ export function rememberEngineTargets(targets) {
48
+ fs.mkdirSync(path.dirname(rememberedPath()), { recursive: true });
49
+ fs.writeFileSync(rememberedPath(), `${JSON.stringify(targets, null, 2)}\n`);
50
+ }
51
+ export function forgetEngineTargets() {
52
+ fs.rmSync(rememberedPath(), { force: true });
53
+ }