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
@@ -6,11 +6,14 @@ import path from "node:path";
6
6
  import { recognizeMatch } from "../core/adapter/recognize-match.js";
7
7
  import { assetPathForName, deriveCanonicalAssetNameFromStashRoot, isRelevantAssetFile, stashDirFor, } from "../core/asset/asset-placement.js";
8
8
  import { conceptIdFromTypeName } from "../core/asset/resolve-ref.js";
9
- import { hasErrnoCode, isWithin } from "../core/common.js";
9
+ import { hasErrnoCode, isContainedResolvedPath } from "../core/common.js";
10
10
  import { NotFoundError, UsageError } from "../core/errors.js";
11
11
  import { walkStashFlat } from "../indexer/walk/walker.js";
12
12
  /**
13
- * Resolve an asset path from a stash directory, type, and name.
13
+ * Resolve an asset path from a stash directory, type, and name. The path keeps
14
+ * the authored spelling, not the symlink target's: an asset that links to a
15
+ * file with another name (or none) is still the kind its own name says, and a
16
+ * read through it reaches the same bytes.
14
17
  */
15
18
  export async function resolveAssetPath(stashDir, type, name) {
16
19
  try {
@@ -31,25 +34,24 @@ export async function resolveAssetPath(stashDir, type, name) {
31
34
  function resolveInTypeDir(stashDir, typeDir, type, name) {
32
35
  const root = path.join(stashDir, typeDir);
33
36
  const target = assetPathForName(type, root, name);
34
- const resolvedRoot = resolveAndValidateTypeRoot(root, type, name);
37
+ resolveAndValidateTypeRoot(root, type, name);
35
38
  const resolvedTarget = path.resolve(target);
36
- if (!isWithin(resolvedTarget, resolvedRoot)) {
39
+ // Name grammar: the name must not climb out of the type directory (`..` or
40
+ // an absolute name). Lexical only: an asset that is a symlink to a file
41
+ // elsewhere resolves, because installing a bundle is the trust decision.
42
+ if (!isContainedResolvedPath(resolvedTarget, path.resolve(root))) {
37
43
  throw new UsageError("Ref resolves outside the stash root.", "PATH_ESCAPE_VIOLATION");
38
44
  }
39
45
  if (!fs.existsSync(resolvedTarget) || !fs.statSync(resolvedTarget).isFile()) {
40
46
  throw new NotFoundError(`Stash asset not found for ref: ${conceptIdFromTypeName(type, name)}`, "ASSET_NOT_FOUND");
41
47
  }
42
- const realTarget = fs.realpathSync(resolvedTarget);
43
- if (!isWithin(realTarget, resolvedRoot)) {
44
- throw new UsageError("Ref resolves outside the stash root.", "PATH_ESCAPE_VIOLATION");
45
- }
46
48
  if (!isRelevantAssetFile(type, path.basename(resolvedTarget))) {
47
49
  if (type === "script") {
48
50
  throw new NotFoundError("Script ref must resolve to a file with a supported script extension. Refer to the akm documentation for the complete list of supported script extensions.");
49
51
  }
50
52
  throw new NotFoundError(`Stash asset not found for ref: ${conceptIdFromTypeName(type, name)}`, "ASSET_NOT_FOUND");
51
53
  }
52
- return realTarget;
54
+ return resolvedTarget;
53
55
  }
54
56
  function resolveAndValidateTypeRoot(root, type, name) {
55
57
  const rootStat = readTypeRootStat(root, type, name);
@@ -57,7 +59,6 @@ function resolveAndValidateTypeRoot(root, type, name) {
57
59
  throw new NotFoundError(`Asset directory for ${type} assets is not accessible — got a file where a directory was expected for ref: ${conceptIdFromTypeName(type, name)}. ` +
58
60
  "Run `akm index` to rebuild the index, or check your source configuration.", "ASSET_NOT_FOUND", "Run `akm bundle list` to see your configured sources and verify the source path exists.");
59
61
  }
60
- return fs.realpathSync(root);
61
62
  }
62
63
  function readTypeRootStat(root, type, name) {
63
64
  try {
@@ -79,12 +80,7 @@ async function resolveByCanonicalName(stashDir, type, name) {
79
80
  const canonicalName = deriveCanonicalAssetNameFromStashRoot(type, stashDir, ctx.absPath);
80
81
  if (canonicalName !== normalizedName)
81
82
  continue;
82
- const realTarget = fs.realpathSync(ctx.absPath);
83
- const resolvedRoot = fs.realpathSync(stashDir);
84
- if (!isWithin(realTarget, resolvedRoot)) {
85
- throw new UsageError("Ref resolves outside the stash root.", "PATH_ESCAPE_VIOLATION");
86
- }
87
- return realTarget;
83
+ return ctx.absPath;
88
84
  }
89
85
  return undefined;
90
86
  }
@@ -4,8 +4,7 @@
4
4
  /**
5
5
  * robots.txt parsing, matching, and a crawl-scoped fetch/cache policy.
6
6
  *
7
- * Behavioral reference: docs/plans/specs/p1-robots.md (authoritative). This
8
- * module is a clean rewrite against akm's contracts, not a port of any
7
+ * This module is a clean rewrite against akm's contracts, not a port of any
9
8
  * upstream source file.
10
9
  *
11
10
  * Pure by design: no fetching happens here (see `RobotsTxtLoader`/
@@ -14,7 +13,7 @@
14
13
  * `createRobotsPolicy` returns so it dies with the crawl that created it —
15
14
  * a module-level `origin -> RobotsRuleSet` map would leak `disallowAll`
16
15
  * results across crawls and across the test suite's process-wide `bun test`
17
- * run (see spec §6.1). The module-level `WeakMap` below is a different
16
+ * run. The module-level `WeakMap` below is a different
18
17
  * category: it memoizes a pure function of an individual rule object's own
19
18
  * `pattern` field, keyed by that object's identity. It cannot leak meaning
20
19
  * between origins or tests — distinct `parseRobotsTxt` calls (even for
@@ -40,7 +39,7 @@ export const ROBOTS_USER_AGENT_HEADER = "akm-cli website provider";
40
39
  /**
41
40
  * Frozen so a stray mutation (e.g. an accidental `.rules.push(...)` somewhere
42
41
  * that borrows one of these as a starting point) can never poison every other
43
- * crawl sharing this instance (spec §10, Review log 2026-08-01 finding 1).
42
+ * crawl sharing this instance.
44
43
  */
45
44
  const EMPTY_ROBOTS_RULES = Object.freeze([]);
46
45
  export const ALLOW_ALL_RULES = Object.freeze({
@@ -128,8 +127,8 @@ export function parseRobotsTxt(text) {
128
127
  pattern: value,
129
128
  specificity: value.length,
130
129
  };
131
- // Compile once, here, at parse time — not on every URL check (spec
132
- // §6.4, review finding robots.ts:238). See `compiledPatternCache`.
130
+ // Compile once, here, at parse time — not on every URL check. See
131
+ // `compiledPatternCache`.
133
132
  compiledPatternCache.set(rule, compilePatternSegments(value));
134
133
  current.rules.push(rule);
135
134
  continue;
@@ -185,7 +184,7 @@ function isUnreservedByte(byte) {
185
184
  * fallback) and match targets (`isPathAllowedByRobots`), so a percent-encoded
186
185
  * alias of a disallowed path — e.g. a link written as `/%73ecret/` — cannot
187
186
  * bypass `Disallow: /secret/` simply because `URL.pathname` preserves
188
- * %-escapes verbatim. Reserved-octet patterns like spec §4.3 M-31's
187
+ * %-escapes verbatim. Reserved-octet patterns like
189
188
  * `/caf%C3%A9` are untouched on both sides (0xC3/0xA9 are not unreserved),
190
189
  * so they keep matching themselves exactly as before.
191
190
  */
@@ -211,10 +210,10 @@ function compilePatternSegments(pattern) {
211
210
  }
212
211
  /**
213
212
  * Per-rule-object cache of the compiled pattern (see the file-level doc
214
- * comment for why this is not "module-level state" in the sense spec §6.1
215
- * forbids). `parseRobotsTxt` populates this eagerly, once, when it creates
216
- * each `RobotsPathRule` — the compile-once requirement from spec §6.4 /
217
- * review finding robots.ts:238. `compiledPatternFor` below is a defensive
213
+ * comment for why this is not "module-level state" in the sense that comment
214
+ * rules out). `parseRobotsTxt` populates this eagerly, once, when it creates
215
+ * each `RobotsPathRule` — the compile-once requirement.
216
+ * `compiledPatternFor` below is a defensive
218
217
  * fallback for `RobotsRuleSet`s assembled by hand (e.g. test fixtures)
219
218
  * rather than via `parseRobotsTxt`; it still compiles at most once per rule
220
219
  * object, just lazily on first use instead of at parse time.
@@ -324,7 +323,7 @@ export function createRobotsPolicy(load) {
324
323
  const pending = load(robotsUrl).then(outcomeToRuleSet);
325
324
  // A rejected fetch must not poison the cache: the next lookup for this
326
325
  // origin should retry the loader rather than replaying the same failure
327
- // forever (spec §4.4 L-04).
326
+ // forever.
328
327
  pending.catch(() => {
329
328
  if (cache.get(origin) === pending)
330
329
  cache.delete(origin);
@@ -1,8 +1,8 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- import fs from "node:fs";
5
4
  import { resolveSecretPath } from "../../core/env-secret-ref.js";
5
+ import { readValue } from "../../core/secret-value.js";
6
6
  /**
7
7
  * Reads a secret out of akm's secret store for the fetcher `resolveSecret`
8
8
  * seam (see `FetcherContext.resolveSecret`).
@@ -21,9 +21,7 @@ import { resolveSecretPath } from "../../core/env-secret-ref.js";
21
21
  export function resolveSecretFromStore(ref) {
22
22
  try {
23
23
  const { absPath } = resolveSecretPath(ref);
24
- if (!fs.existsSync(absPath))
25
- return null;
26
- return fs.readFileSync(absPath, "utf8").trim() || null;
24
+ return readValue(absPath) || null;
27
25
  }
28
26
  catch {
29
27
  return null;
@@ -15,10 +15,6 @@ import { escapeMarkdownStructure } from "./fetcher-util.js";
15
15
  import { assertResolvedHostAllowed, assertWebsiteRequestUrl } from "./host-guard.js";
16
16
  import { loadWikiSnapshotFetchers } from "./registry.js";
17
17
  import { createAllowAllRobotsPolicy, createRobotsPolicy, isPathAllowedByRobots, ROBOTS_BODY_TIMEOUT_MS, ROBOTS_BYTE_CAP, } from "./robots.js";
18
- /** Refresh website snapshots every 12 hours to balance freshness with scraping load. */
19
- const CACHE_REFRESH_INTERVAL_MS = 12 * 60 * 60 * 1000;
20
- /** Allow up to 7 days of stale snapshots when refresh fails so search remains available during outages. */
21
- const CACHE_STALE_MS = 7 * 24 * 60 * 60 * 1000;
22
18
  /** Allow limited breadth-first expansion without letting the crawl queue grow unbounded. */
23
19
  const QUEUE_EXPANSION_FACTOR = 5;
24
20
  const MAX_PAGES_DEFAULT = 50;
@@ -80,8 +76,6 @@ export async function ensureWebsiteMirror(config, options) {
80
76
  const requireStashDir = options?.requireStashDir === true;
81
77
  await withFreshnessCache({
82
78
  markerPath: cachePaths.manifestPath,
83
- ttlMs: CACHE_REFRESH_INTERVAL_MS,
84
- staleMs: CACHE_STALE_MS,
85
79
  force: options?.force === true,
86
80
  isUsable: () => !requireStashDir || hasExtractedSite(cachePaths.stashDir),
87
81
  refresh: async () => {
@@ -465,7 +459,7 @@ async function resolveCrawlRobotsDecision(robots, normalizedUrl, rawUrl) {
465
459
  return decideRobotsAllowance(rules, normalizedUrl, rawUrl);
466
460
  }
467
461
  /**
468
- * C-02/C-03: fail fast, before any page fetch, when the start URL itself is
462
+ * Fail fast, before any page fetch, when the start URL itself is
469
463
  * off-limits per an actually-parsed robots.txt rule. A 5xx on robots.txt
470
464
  * itself no longer reaches this as `disallowAll`: `loadRobotsTxt` reports it the same way a
471
465
  * 4xx is reported (`ALLOW_ALL_RULES`), with its own warning, rather than
@@ -478,7 +472,7 @@ async function resolveCrawlRobotsDecision(robots, normalizedUrl, rawUrl) {
478
472
  * `normalizeSiteUrl` stripped any trailing slash — via
479
473
  * `decideRobotsAllowance`. Without this, a start URL typed as `.../secret/`
480
474
  * under `Disallow: /secret/` would never match that rule and would be
481
- * crawled instead of rejected with the spec §4.6 C-02 UsageError; conversely,
475
+ * crawled instead of rejected with a UsageError; conversely,
482
476
  * a start URL typed as `.../docs/` under `Disallow: / \n Allow: /docs/`
483
477
  * would be normalized to `.../docs`, fail to match `Allow: /docs/`, and be
484
478
  * rejected even though the site owner explicitly opened `/docs/` to
@@ -748,7 +742,7 @@ async function websitePageFromResponse(fetched, pageUrl, options) {
748
742
  // `Disallow: /secret/` correctly lets `/secret` through that gate; if the
749
743
  // server then redirects to `/secret/` (a common trailing-slash
750
744
  // canonicalization), the disallowed page would otherwise be fetched and
751
- // stored without ever being weighed against robots.txt. See spec §4.6 C-04.
745
+ // stored without ever being weighed against robots.txt.
752
746
  // Checks `rawFinalUrl` (the un-normalized `response.url`, slash intact) as
753
747
  // well as `finalUrl`, per `isCrawlUrlAllowedByRobots` — `normalizeCrawlUrl`
754
748
  // would otherwise strip the very trailing slash a `Disallow: /secret/`
@@ -821,8 +815,7 @@ async function fetchWebsiteResponse(pageUrl, redirectCount = 0, options) {
821
815
  // `/secret/` even when robots.txt disallows it, because that hop is
822
816
  // never the queue URL and never the final URL. Only gated when a
823
817
  // `RobotsPolicy` was actually threaded in (`crawlWebsite`); single-URL
824
- // `fetchWebsiteMarkdownSnapshot` fetches stay deliberately ungated per
825
- // spec §1.
818
+ // `fetchWebsiteMarkdownSnapshot` fetches stay deliberately ungated.
826
819
  if (options?.robots) {
827
820
  const normalizedNext = normalizeCrawlUrl(nextUrl);
828
821
  if (!normalizedNext) {
@@ -850,15 +843,15 @@ async function fetchWebsiteResponse(pageUrl, redirectCount = 0, options) {
850
843
  }
851
844
  /**
852
845
  * Fetches and classifies `<origin>/robots.txt`. Reuses `fetchWebsiteResponse`
853
- * (spec §6.2: no second fetch path) so robots.txt gets the exact same SSRF
846
+ * (no second fetch path) so robots.txt gets the exact same SSRF
854
847
  * guards, retry, and redirect handling as a page fetch.
855
848
  *
856
849
  * Steps 1–2 (the guard on the INITIAL URL) run OUTSIDE the try/catch on
857
850
  * purpose: a guard rejection there must propagate as-is, never be downgraded
858
- * to "unavailable" (spec §4.5 F-12, §6.2). Guard rejections on a LATER
851
+ * to "unavailable". Guard rejections on a LATER
859
852
  * redirect hop happen inside `fetchWebsiteResponse`, which the try/catch
860
853
  * below does cover — the guard has already refused to fetch that host, so
861
- * only the error *reporting* is downgraded (F-11).
854
+ * only the error *reporting* is downgraded.
862
855
  */
863
856
  export async function loadRobotsTxt(robotsUrl, options) {
864
857
  assertWebsiteRequestUrl(robotsUrl, UsageError, options);
@@ -883,7 +876,7 @@ export async function loadRobotsTxt(robotsUrl, options) {
883
876
  }
884
877
  if (response.status >= 500 && response.status < 600) {
885
878
  await response.body?.cancel().catch(() => undefined);
886
- // Guard-audit finding 12: RFC 9309 §2.3.1.4 recommends treating an
879
+ // RFC 9309 §2.3.1.4 recommends treating an
887
880
  // unreachable robots.txt as a full disallow, but for a personal tool
888
881
  // crawling a site the operator explicitly chose, a transient 5xx on
889
882
  // the site's OWN robots.txt is not a site owner's Disallow — it is the
@@ -89,7 +89,7 @@ export function resolveXBearerToken(context) {
89
89
  if (fromEnv)
90
90
  return fromEnv;
91
91
  try {
92
- return context?.resolveSecret?.(X_BEARER_TOKEN_SECRET_REF)?.trim() || null;
92
+ return context?.resolveSecret?.(X_BEARER_TOKEN_SECRET_REF) || null;
93
93
  }
94
94
  catch {
95
95
  return null;
@@ -0,0 +1,52 @@
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 ONNX Runtime shared library for a compiled standalone binary.
6
+ *
7
+ * `onnxruntime-node`'s addon links a shared library that sits beside it in the
8
+ * package. A Bun executable extracts an embedded addon at load but not the
9
+ * library it links, so the addon cannot start. The standalone build embeds the
10
+ * library for its target (`src/standalone/native/`, chosen by
11
+ * `scripts/build-standalone.ts`); this copies it once into akm's cache and
12
+ * loads it with `bun:ffi`, after which the addon's own link to it is already
13
+ * satisfied.
14
+ *
15
+ * Outside a standalone binary (`bun src/cli.ts`, the Node build) nothing here
16
+ * is defined and nothing is evaluated: the package's own files are used.
17
+ */
18
+ import fs from "node:fs";
19
+ import path from "node:path";
20
+ import { getCacheDir } from "../core/paths.js";
21
+ /**
22
+ * Put the embedded library on disk and load it, before `onnxruntime-node` is
23
+ * imported. Throws when the library cannot be loaded; the caller reports that
24
+ * as the embedding runtime being unavailable.
25
+ */
26
+ export async function preloadOnnxRuntimeLibrary() {
27
+ if (typeof AKM_STANDALONE_NATIVE_MODULE === "undefined")
28
+ return;
29
+ // The specifier is a build constant, so the standalone build bundles this one module and no other build bundles any.
30
+ const { embeddedPath, fileName } = (await import(AKM_STANDALONE_NATIVE_MODULE));
31
+ if (!embeddedPath || !fileName)
32
+ return;
33
+ const directory = path.join(getCacheDir(), "onnxruntime", `${AKM_ONNXRUNTIME_VERSION}-${process.platform}-${process.arch}`);
34
+ const library = path.join(directory, fileName);
35
+ const size = fs.statSync(embeddedPath).size;
36
+ if (!fs.existsSync(library) || fs.statSync(library).size !== size) {
37
+ fs.mkdirSync(directory, { recursive: true });
38
+ const partial = `${library}.${process.pid}.tmp`;
39
+ fs.writeFileSync(partial, fs.readFileSync(embeddedPath));
40
+ try {
41
+ fs.renameSync(partial, library);
42
+ }
43
+ catch (error) {
44
+ // Another akm process copied it first and has it loaded, which Windows will not replace: use that copy.
45
+ fs.rmSync(partial, { force: true });
46
+ if (!fs.existsSync(library) || fs.statSync(library).size !== size)
47
+ throw error;
48
+ }
49
+ }
50
+ const { dlopen } = await import("bun:ffi");
51
+ dlopen(library, { OrtGetApiBase: { args: [], returns: "ptr" } });
52
+ }
@@ -233,7 +233,7 @@ export function forgetImproveLedgerDecision(db, stashDir, proposalId) {
233
233
  db.prepare("DELETE FROM improve_ledger WHERE stash_dir = ? AND proposal_id = ?").run(stashDir, proposalId);
234
234
  }
235
235
  /**
236
- * Should-fix 6 (second review round): a read-only or dry-run open never
236
+ * A read-only or dry-run open never
237
237
  * migrates, so it can land on a state.db from before migration 029 added
238
238
  * `content_hash` — reading it there threw "no such column", which
239
239
  * `loadRetrievalScope`'s own catch then reported as "usage history
@@ -127,19 +127,6 @@ export function listRecentImproveRuns(db, limit) {
127
127
  .prepare("SELECT id, started_at, completed_at, ok, scope_mode, scope_value, strategy, result_json FROM improve_runs WHERE dry_run = 0 ORDER BY started_at DESC LIMIT ?")
128
128
  .all(limit);
129
129
  }
130
- /**
131
- * #950: count real (non-dry-run) improve_runs rows whose `started_at` falls
132
- * in `[since, now)` — same filter as {@link queryImproveRuns}, but a `SELECT
133
- * COUNT(*)` for a caller (the `engine-last-used` gate) that only needs to
134
- * know whether any run happened, not the rows themselves (which would pull
135
- * every `result_json` blob in the window just to test for zero).
136
- */
137
- export function countImproveRunsSince(db, since) {
138
- const row = db
139
- .prepare("SELECT COUNT(*) AS cnt FROM improve_runs WHERE started_at >= ? AND dry_run = 0")
140
- .get(since);
141
- return row.cnt;
142
- }
143
130
  /**
144
131
  * Delete improve_runs rows older than `retentionDays` (default: 90). Mirrors
145
132
  * {@link purgeOldEvents} — same default, same return shape (number of rows
@@ -760,10 +760,10 @@ export function isRefLiveInSnapshot(snapshot, ref) {
760
760
  }
761
761
  /**
762
762
  * The fully-qualified `item_ref` (`<bundle>//<conceptId>`, the durable stored
763
- * spelling — spec §11.1 D-R3) for an entry `id`, or `null` when the row is gone
763
+ * spelling) for an entry `id`, or `null` when the row is gone.
764
764
  * The usage-event / salience / feedback writers derive the durable
765
765
  * key from this so a stored key is always the resolved entry's canonical ref,
766
- * never raw input (D-R3: durable keys are never derived from input).
766
+ * never raw input (durable keys are never derived from input).
767
767
  */
768
768
  export function getItemRefById(db, id) {
769
769
  const row = db.prepare("SELECT item_ref FROM entries WHERE id = ?").get(id);
@@ -525,8 +525,8 @@ export function renameProposalsBundleRef(db, oldBundleId, newBundleId) {
525
525
  const prefix = `${escapeLikePattern(oldBundleId)}//`;
526
526
  return db.transaction(() => {
527
527
  const refs = Number(db
528
- .prepare(`UPDATE proposals SET ref = ? || substr(ref, ?) WHERE ref LIKE ? ESCAPE '\\'`)
529
- .run(newBundleId, oldBundleId.length + 1, `${prefix}%`).changes);
528
+ .prepare(`UPDATE proposals SET ref = ? || substr(ref, length(?) + 1) WHERE ref LIKE ? ESCAPE '\\'`)
529
+ .run(newBundleId, oldBundleId, `${prefix}%`).changes);
530
530
  const targets = Number(db
531
531
  .prepare(`UPDATE proposals SET metadata_json = json_set(metadata_json, '$.proposedTarget.source', ?)
532
532
  WHERE json_extract(metadata_json, '$.proposedTarget.source') = ?`)
@@ -221,6 +221,6 @@ export function countTaskHistoryTargetRefs(db, oldBundleId) {
221
221
  export function renameTaskHistoryTargetRefs(db, oldBundleId, newBundleId) {
222
222
  const prefix = `${escapeLikePattern(oldBundleId)}//`;
223
223
  return Number(db
224
- .prepare(`UPDATE task_history SET target_ref = ? || substr(target_ref, ?) WHERE target_ref LIKE ? ESCAPE '\\'`)
225
- .run(newBundleId, oldBundleId.length + 1, `${prefix}%`).changes);
224
+ .prepare(`UPDATE task_history SET target_ref = ? || substr(target_ref, length(?) + 1) WHERE target_ref LIKE ? ESCAPE '\\'`)
225
+ .run(newBundleId, oldBundleId, `${prefix}%`).changes);
226
226
  }
@@ -42,75 +42,10 @@ export class WorkflowRunsRepository {
42
42
  return withImmediateTransaction(this.db, () => fn(this.db));
43
43
  }
44
44
  // ── reads (fully materialised) ─────────────────────────────────────────────
45
- /**
46
- * The top-level start guard `publishWorkflowRunV4` uses to refuse starting
47
- * a SECOND active run of the same ref in the same scope. `AND
48
- * parent_run_id IS NULL` (B-N10's FOURTH site, code-review round 4 finding
49
- * 2 / Review log R2): a child run carries the PARENT's `scope_key` (P3a
50
- * §5.2), so once a parent publishes a child of some ref, starting a
51
- * TOP-LEVEL run of that same ref in that scope must never resolve to the
52
- * child — the child is not "already an active run in this scope" from a
53
- * fresh top-level invocation's point of view; the PARENT, if anything, is.
54
- * Before this filter, `publishWorkflowRunV4` refused with
55
- * `RESOURCE_ALREADY_EXISTS` naming the CHILD's own run id, instructing the
56
- * operator to `akm workflow abandon` a child a parent is actively driving.
57
- * For any database with no child rows the result is byte-identical, same
58
- * as the other three B-N10 sites below.
59
- *
60
- * Always scope-local: `scopeKey` is a real scope, never "every scope" — see
61
- * {@link findActiveRunOutsideScope} for the cross-scope warning's query.
62
- */
63
- findActiveRunForScope(workflowRefs, scopeKey) {
64
- const refs = typeof workflowRefs === "string" ? [workflowRefs] : [...workflowRefs];
65
- if (refs.length === 0)
66
- return undefined;
67
- return this.db
68
- .prepare(`SELECT id, current_step_id FROM workflow_runs WHERE workflow_ref IN (${refs.map(() => "?").join(", ")}) AND scope_key = ? AND status = 'active' AND parent_run_id IS NULL ORDER BY updated_at DESC, created_at DESC LIMIT 1`)
69
- .get(...refs, scopeKey);
70
- }
71
- /**
72
- * The cross-scope start warning's query (#942): the most recently active
73
- * run of these refs OUTSIDE `scopeKey` — i.e. `scope_key IS NULL OR
74
- * scope_key != scopeKey`, not merely "the most recent active run of these
75
- * refs anywhere". A same-scope active row must never win the `LIMIT 1` and
76
- * mask a DIFFERENT scope's run: with `--new`/`--force`, the caller's own
77
- * active run could otherwise sort first (most recently updated) and hide a
78
- * third scope's run entirely, so `startWorkflowRun` silently warned about
79
- * nothing while a genuinely stale run in another scope went unreported.
80
- * `findActiveRunForScope` deliberately stays scope-local and is never used
81
- * for this purpose.
82
- */
83
- findActiveRunOutsideScope(workflowRefs, scopeKey) {
84
- const refs = typeof workflowRefs === "string" ? [workflowRefs] : [...workflowRefs];
85
- if (refs.length === 0)
86
- return undefined;
87
- return (this.db
88
- .prepare(`SELECT * FROM workflow_runs WHERE workflow_ref IN (${refs.map(() => "?").join(", ")}) AND (scope_key IS NULL OR scope_key != ?) AND status = 'active' AND parent_run_id IS NULL ORDER BY updated_at DESC, created_at DESC LIMIT 1`)
89
- .get(...refs, scopeKey) ?? undefined);
90
- }
91
45
  getRunById(runId) {
92
46
  return (this.db.prepare("SELECT * FROM workflow_runs WHERE id = ?").get(runId) ??
93
47
  undefined);
94
48
  }
95
- /**
96
- * The scope-attach lookup `akm workflow run <ref>` uses to find an
97
- * in-progress top-level run to resume instead of starting a new one.
98
- * `AND parent_run_id IS NULL` (B-N10): a child run must never be attached
99
- * to directly through this path — a parent-driven child would then have
100
- * TWO drivers. For any database with no child rows the result is
101
- * byte-identical.
102
- *
103
- * Always scope-local: `scopeKey` is a real scope, never "every scope" — see
104
- * {@link findActiveRunOutsideScope} for the cross-scope warning's query.
105
- */
106
- getActiveRunRowForScope(workflowRefs, scopeKey) {
107
- const refs = typeof workflowRefs === "string" ? [workflowRefs] : [...workflowRefs];
108
- if (refs.length === 0)
109
- return undefined;
110
- return (this.db
111
- .prepare(`SELECT * FROM workflow_runs WHERE workflow_ref IN (${refs.map(() => "?").join(", ")}) AND scope_key = ? AND status = 'active' AND parent_run_id IS NULL ORDER BY updated_at DESC, created_at DESC LIMIT 1`)
112
- .get(...refs, scopeKey) ?? undefined);
113
- }
114
49
  hasRun(runId) {
115
50
  const row = this.db.prepare("SELECT 1 FROM workflow_runs WHERE id = ? LIMIT 1").get(runId);
116
51
  return !!row;
@@ -128,16 +63,9 @@ export class WorkflowRunsRepository {
128
63
  }
129
64
  throw new NotFoundError(`Workflow run "${prefix}" not found.`, "WORKFLOW_NOT_FOUND");
130
65
  }
131
- listRuns(filter) {
66
+ listRuns(filter = {}) {
132
67
  const filters = [];
133
68
  const params = [];
134
- // `scopeKey: null` (#942) is "every scope" — the predicate is omitted
135
- // rather than bound as SQL NULL (which would match nothing, since a real
136
- // `scope_key` column value is never NULL for a fresh run).
137
- if (filter.scopeKey !== null) {
138
- filters.push("scope_key = ?");
139
- params.push(filter.scopeKey);
140
- }
141
69
  if (filter.workflowRef) {
142
70
  filters.push("workflow_ref = ?");
143
71
  params.push(filter.workflowRef);
@@ -152,11 +80,7 @@ export class WorkflowRunsRepository {
152
80
  // human `resume`), so it must never appear under `--active`, or a script
153
81
  // treating `--active` output as executable work would pick up a blocked
154
82
  // run (owner manual-validation finding 1). Blocked runs stay visible in
155
- // plain `list` (all statuses) with their `blocked` status. The
156
- // active-OR-blocked scope semantics some call sites want (the `akm show`
157
- // scope guard, which surfaces a blocked run as the scope's occupant) live
158
- // in the SEPARATE {@link findActiveOrBlockedRunForScope} — never folded
159
- // into this shared list filter.
83
+ // plain `list` (all statuses) with their `blocked` status.
160
84
  filters.push("status = 'active'");
161
85
  }
162
86
  // B-N10: a child run is invisible to this scope query unless the caller
@@ -178,32 +102,20 @@ export class WorkflowRunsRepository {
178
102
  getStep(runId, stepId) {
179
103
  return this.db.prepare("SELECT * FROM workflow_run_steps WHERE run_id = ? AND step_id = ?").get(runId, stepId);
180
104
  }
181
- /**
182
- * The `akm show` active-run guard: which run (if any) currently occupies
183
- * this scope. `AND parent_run_id IS NULL` (B-N10) — a child a parent is
184
- * driving must never be reported as "the" active run; the PARENT is. For
185
- * any database with no child rows the result is byte-identical.
186
- */
187
- findActiveOrBlockedRunForScope(scopeKey) {
188
- return (this.db
189
- .prepare("SELECT id, current_step_id, workflow_ref FROM workflow_runs WHERE scope_key = ? AND status IN ('active', 'blocked') AND parent_run_id IS NULL ORDER BY updated_at DESC LIMIT 1")
190
- .get(scopeKey) ?? null);
191
- }
192
105
  // ── writes ─────────────────────────────────────────────────────────────────
193
106
  insertRun(input) {
194
- // R-R3 (P3a Review log; docs/plans/specs/p4-deletions-closeout.md §8):
195
- // this 12-column list is hand-duplicated by publishChildWorkflowRun's own
107
+ // This column list is hand-duplicated by publishChildWorkflowRun's own
196
108
  // INSERT below, which extends it with parent_run_id/parent_unit_id/
197
- // invocation_key. A signature refactor to share one INSERT builder was
198
- // considered and deliberately deferred — see
199
- // docs/architecture/decisions/0009-child-run-publication-column-parity.md.
200
- // Keep both column lists in sync by hand.
109
+ // invocation_key; one shared INSERT builder was deliberately not introduced.
110
+ // Keep both column lists in sync by hand. `scope_key` and
111
+ // `workflow_entry_id` are legacy columns: read tolerantly, never written
112
+ // (#1239), so a fresh row carries NULL in both.
201
113
  this.db
202
114
  .prepare(`INSERT INTO workflow_runs (
203
- id, workflow_ref, scope_key, workflow_entry_id, workflow_title, status, params_json, current_step_id, created_at, updated_at,
115
+ id, workflow_ref, workflow_title, status, params_json, current_step_id, created_at, updated_at,
204
116
  agent_harness, agent_session_id
205
- ) VALUES (?, ?, ?, ?, ?, 'active', ?, ?, ?, ?, ?, ?)`)
206
- .run(input.id, input.workflowRef, input.scopeKey, input.workflowEntryId, input.workflowTitle, input.paramsJson, input.currentStepId, input.createdAt, input.updatedAt, input.agentHarness, input.agentSessionId);
117
+ ) VALUES (?, ?, ?, 'active', ?, ?, ?, ?, ?, ?)`)
118
+ .run(input.id, input.workflowRef, input.workflowTitle, input.paramsJson, input.currentStepId, input.createdAt, input.updatedAt, input.agentHarness, input.agentSessionId);
207
119
  }
208
120
  insertSteps(steps) {
209
121
  const insertStep = this.db.prepare(`INSERT INTO workflow_run_steps (
@@ -252,22 +164,6 @@ export class WorkflowRunsRepository {
252
164
  */
253
165
  publishWorkflowRunV4(input) {
254
166
  this.immediateTransaction((db) => {
255
- if (!input.force) {
256
- // The uniqueness guard must never silently skip its scope predicate
257
- // (#942) — every real caller (`startWorkflowRun`) stamps a concrete
258
- // scope key, so a null one here means the caller is misusing this
259
- // top-level guard, not that scoping should be waived.
260
- if (input.run.scopeKey === null) {
261
- throw new Error("publishWorkflowRunV4: run.scopeKey must not be null for the scope uniqueness guard.");
262
- }
263
- const existing = this.findActiveRunForScope(input.workflowRefs, input.run.scopeKey);
264
- if (existing) {
265
- throw new UsageError(`Workflow ${input.run.workflowRef} already has an active run in this scope ` +
266
- `(id=${existing.id}, scope=${input.run.scopeKey}, step=${existing.current_step_id ?? "—"}). ` +
267
- `Use 'akm workflow run ${input.run.workflowRef}' to resume it or ` +
268
- `'akm workflow abandon ${existing.id}' to give up on it.`, "RESOURCE_ALREADY_EXISTS");
269
- }
270
- }
271
167
  this.insertRun(input.run);
272
168
  this.insertSteps(input.steps);
273
169
  db.prepare("UPDATE workflow_runs SET plan_json = ?, plan_hash = ?, plan_ir_version = ? WHERE id = ?").run(input.planJson, input.planHash, WORKFLOW_PLAN_VERSION, input.run.id);
@@ -294,13 +190,10 @@ export class WorkflowRunsRepository {
294
190
  * (`plan_ir_version` = the current plan version), and append its
295
191
  * `workflow_started` event — then return the freshly-inserted row.
296
192
  *
297
- * Deliberately does NOT call {@link findActiveRunForScope} or raise
298
- * `RESOURCE_ALREADY_EXISTS` (top-level scope-conflict rules do not apply to
299
- * a child), and reads no source: the child plan was frozen into the parent's.
193
+ * Reads no source: the child plan was frozen into the parent's.
300
194
  *
301
195
  * This method's serialization guarantee holds only when it is the
302
- * OUTERMOST transaction on the connection (spec Review log R10,
303
- * docs/plans/specs/p3a-plan-v5-child-freeze.md). `withImmediateTransaction`
196
+ * OUTERMOST transaction on the connection. `withImmediateTransaction`
304
197
  * (src/core/state-db.ts) has a re-entrancy guard: if a transaction is
305
198
  * already open on the connection, it SILENTLY JOINS that transaction
306
199
  * instead of issuing its own `BEGIN IMMEDIATE`.
@@ -318,11 +211,10 @@ export class WorkflowRunsRepository {
318
211
  * same `(parentRunId, invocationKey)` serialize on SQLite's write lock: the
319
212
  * first to acquire it inserts and commits, and the second's own SELECT —
320
213
  * which can only run once it has acquired the lock in turn — finds and
321
- * returns the first's row rather than inserting a duplicate or throwing
322
- * (C-09). Calling this twice with the same key, including across a crash
214
+ * returns the first's row rather than inserting a duplicate or throwing.
215
+ * Calling this twice with the same key, including across a crash
323
216
  * between publish and parent-side recording, is therefore safe and returns
324
- * the same child both times, with exactly one event and one step set
325
- * (C-08).
217
+ * the same child both times, with exactly one event and one step set.
326
218
  */
327
219
  publishChildWorkflowRun(input) {
328
220
  return this.immediateTransaction((db) => {
@@ -331,15 +223,13 @@ export class WorkflowRunsRepository {
331
223
  .get(input.parentRunId, input.invocationKey);
332
224
  if (existing)
333
225
  return existing;
334
- // R-R3 (P3a Review log; docs/plans/specs/p4-deletions-closeout.md §8):
335
- // the first 12 columns here must stay byte-identical to insertRun's own
336
- // column list above (hand-duplicated, not shared, by deliberate choice
337
- // — see docs/architecture/decisions/0009-child-run-publication-column-parity.md).
226
+ // The first 10 columns here must stay byte-identical to insertRun's own
227
+ // column list above (hand-duplicated, not shared, by deliberate choice).
338
228
  // Keep both column lists in sync by hand.
339
229
  db.prepare(`INSERT INTO workflow_runs (
340
- id, workflow_ref, scope_key, workflow_entry_id, workflow_title, status, params_json, current_step_id, created_at, updated_at,
230
+ id, workflow_ref, workflow_title, status, params_json, current_step_id, created_at, updated_at,
341
231
  agent_harness, agent_session_id, parent_run_id, parent_unit_id, invocation_key
342
- ) VALUES (?, ?, ?, ?, ?, 'active', ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(input.run.id, input.run.workflowRef, input.run.scopeKey, input.run.workflowEntryId, input.run.workflowTitle, input.run.paramsJson, input.run.currentStepId, input.run.createdAt, input.run.updatedAt, input.run.agentHarness, input.run.agentSessionId, input.parentRunId, input.spawnedByUnitId, input.invocationKey);
232
+ ) VALUES (?, ?, ?, 'active', ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(input.run.id, input.run.workflowRef, input.run.workflowTitle, input.run.paramsJson, input.run.currentStepId, input.run.createdAt, input.run.updatedAt, input.run.agentHarness, input.run.agentSessionId, input.parentRunId, input.spawnedByUnitId, input.invocationKey);
343
233
  this.insertSteps(input.steps);
344
234
  db.prepare("UPDATE workflow_runs SET plan_json = ?, plan_hash = ?, plan_ir_version = ? WHERE id = ?").run(input.planJson, input.planHash, WORKFLOW_PLAN_VERSION, input.run.id);
345
235
  insertEventOnce(db, {
@@ -543,6 +433,31 @@ export class WorkflowRunsRepository {
543
433
  return this.db.prepare("SELECT * FROM workflow_run_units WHERE run_id = ? AND unit_id = ?").get(runId, unitId);
544
434
  }
545
435
  }
436
+ /**
437
+ * Preview counterpart of {@link renameWorkflowRunRefs} for `akm bundle rename`:
438
+ * how many run rows name `oldBundleId`, and which of them are still `active`
439
+ * or `blocked` (their frozen `plan_json` keeps the old qualified refs, so the operator
440
+ * finishes or abandons them — the rename lists them rather than touching them).
441
+ */
442
+ export function countWorkflowRunRefsForRename(db, oldBundleId) {
443
+ const like = `${escapeLikePattern(oldBundleId)}//%`;
444
+ const refs = db.prepare(`SELECT COUNT(*) AS n FROM workflow_runs WHERE workflow_ref LIKE ? ESCAPE '\\'`).get(like).n;
445
+ const active = db
446
+ .prepare(`SELECT id, workflow_ref AS workflowRef, status FROM workflow_runs WHERE workflow_ref LIKE ? ESCAPE '\\' AND status IN ('active', 'blocked') ORDER BY updated_at DESC, created_at DESC`)
447
+ .all(like);
448
+ return { refs, active };
449
+ }
450
+ /**
451
+ * Rewrite every `workflow_runs.workflow_ref` naming `oldBundleId` to
452
+ * `newBundleId` (`akm bundle rename`). A legacy unqualified ref is not matched
453
+ * by the `LIKE` prefix and is left alone. Returns the number of rows rewritten.
454
+ */
455
+ export function renameWorkflowRunRefs(db, oldBundleId, newBundleId) {
456
+ const prefix = `${escapeLikePattern(oldBundleId)}//`;
457
+ return Number(db
458
+ .prepare(`UPDATE workflow_runs SET workflow_ref = ? || substr(workflow_ref, length(?) + 1) WHERE workflow_ref LIKE ? ESCAPE '\\'`)
459
+ .run(newBundleId, oldBundleId, `${prefix}%`).changes);
460
+ }
546
461
  /**
547
462
  * Run `fn` against a {@link WorkflowRunsRepository} bound to state.db
548
463
  * ({@link StorageLocations.stateDb}, the post-cutover home of the