akm-cli 0.9.0-rc.8 → 0.9.0

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 (450) hide show
  1. package/CHANGELOG.md +1063 -44
  2. package/README.md +51 -25
  3. package/SECURITY.md +14 -1
  4. package/STABILITY.md +497 -0
  5. package/dist/akm +148 -35
  6. package/dist/{akm-migrate-storage → akm-migrate} +6 -9
  7. package/dist/assets/hints/cli-hints-full.md +223 -95
  8. package/dist/assets/hints/cli-hints-short.md +85 -22
  9. package/dist/assets/improve-strategies/default.json +1 -1
  10. package/dist/assets/improve-strategies/reflect-distill.json +1 -1
  11. package/dist/assets/prompts/memory-infer-user.md +2 -3
  12. package/dist/assets/stash-skeleton/README.md +6 -5
  13. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +2 -0
  14. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +2 -0
  15. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +2 -0
  16. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +2 -0
  17. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +2 -0
  18. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +2 -0
  19. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +2 -0
  20. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +2 -0
  21. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +2 -0
  22. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +2 -0
  23. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -0
  24. package/dist/assets/stash-skeleton/facts/conventions/organization.md +20 -9
  25. package/dist/assets/tasks/core/extract.yml +1 -1
  26. package/dist/assets/tasks/core/version-check.yml +1 -1
  27. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  28. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  29. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  30. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  31. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  32. package/dist/assets/templates/html/health.html +1 -3
  33. package/dist/assets/workflows/workflow-template.md +32 -15
  34. package/dist/cli/invocation.js +40 -15
  35. package/dist/cli/parse-args.js +0 -22
  36. package/dist/cli/retired-commands.js +121 -0
  37. package/dist/cli/shared.js +154 -22
  38. package/dist/cli/unknown-flags.js +236 -0
  39. package/dist/cli-node.mjs +2 -1
  40. package/dist/cli.js +696 -258
  41. package/dist/commands/agent/agent-dispatch.js +14 -3
  42. package/dist/commands/agent/contribute-cli.js +73 -88
  43. package/dist/commands/completions.js +79 -22
  44. package/dist/commands/config-cli.js +17 -150
  45. package/dist/commands/env/env-cli.js +59 -143
  46. package/dist/commands/env/env.js +12 -163
  47. package/dist/commands/env/marker-path.js +6 -0
  48. package/dist/commands/env/secret-cli.js +36 -66
  49. package/dist/commands/env/secret.js +24 -57
  50. package/dist/commands/feedback-cli.js +141 -87
  51. package/dist/commands/health/accept-rate.js +58 -0
  52. package/dist/commands/health/advisories.js +3 -4
  53. package/dist/commands/health/checks.js +85 -23
  54. package/dist/commands/health/html-report.js +7 -10
  55. package/dist/commands/health/improve-metrics.js +25 -83
  56. package/dist/commands/health/md-report.js +5 -9
  57. package/dist/commands/health/metrics.js +62 -20
  58. package/dist/commands/health/renderers.js +47 -0
  59. package/dist/commands/health/report-view-model.js +4 -5
  60. package/dist/commands/health/stash-exposure.js +1 -1
  61. package/dist/commands/health/surfaces.js +3 -48
  62. package/dist/commands/health/task-runs.js +3 -67
  63. package/dist/commands/health/types-improve.js +7 -0
  64. package/dist/commands/health.js +99 -28
  65. package/dist/commands/improve/anti-collapse.js +2 -2
  66. package/dist/commands/improve/autonomy-gate.js +68 -0
  67. package/dist/commands/improve/collapse-detector.js +41 -40
  68. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  69. package/dist/commands/improve/consolidate/merge.js +4 -0
  70. package/dist/commands/improve/consolidate.js +140 -1000
  71. package/dist/commands/improve/distill/promote-memory.js +12 -12
  72. package/dist/commands/improve/distill/quality-gate.js +6 -6
  73. package/dist/commands/improve/distill.js +58 -69
  74. package/dist/commands/improve/eligibility.js +105 -57
  75. package/dist/commands/improve/extract-cli.js +14 -133
  76. package/dist/commands/improve/improve-cli.js +98 -114
  77. package/dist/commands/improve/improve-result-file.js +1 -28
  78. package/dist/commands/improve/improve-strategies.js +8 -5
  79. package/dist/commands/improve/improve.js +128 -91
  80. package/dist/commands/improve/loop-stages.js +182 -20
  81. package/dist/commands/improve/memory/derived-ref.js +45 -43
  82. package/dist/commands/improve/memory/memory-belief.js +1 -1
  83. package/dist/commands/improve/memory/memory-contradiction-detect.js +4 -12
  84. package/dist/commands/improve/memory/memory-improve.js +6 -5
  85. package/dist/commands/improve/outcome-loop.js +22 -65
  86. package/dist/commands/improve/preparation.js +114 -123
  87. package/dist/commands/improve/proactive-maintenance.js +2 -5
  88. package/dist/commands/improve/reflect.js +56 -160
  89. package/dist/commands/improve/salience.js +11 -122
  90. package/dist/commands/improve/source-identity.js +10 -38
  91. package/dist/commands/lint/base-linter.js +20 -124
  92. package/dist/commands/lint/env-key-rules.js +31 -47
  93. package/dist/commands/lint/index.js +249 -43
  94. package/dist/commands/{events.js → log.js} +33 -38
  95. package/dist/commands/migrate-cli.js +92 -12
  96. package/dist/commands/migration-tool.js +46 -0
  97. package/dist/commands/observability-cli.js +70 -209
  98. package/dist/commands/proposal/drain.js +101 -29
  99. package/dist/commands/proposal/proposal-cli.js +76 -48
  100. package/dist/commands/proposal/proposal.js +54 -18
  101. package/dist/commands/proposal/propose-cli.js +88 -0
  102. package/dist/commands/proposal/propose.js +23 -15
  103. package/dist/commands/proposal/repository.js +701 -278
  104. package/dist/commands/proposal/validators/proposal-quality-validators.js +2 -8
  105. package/dist/commands/proposal/validators/proposal-validators.js +55 -7
  106. package/dist/commands/proposal/validators/proposals.js +4 -7
  107. package/dist/commands/read/curate.js +34 -53
  108. package/dist/commands/read/knowledge.js +150 -95
  109. package/dist/commands/read/registry-search.js +2 -2
  110. package/dist/commands/read/remember-cli.js +42 -15
  111. package/dist/commands/read/search-cli.js +180 -78
  112. package/dist/commands/read/search.js +58 -43
  113. package/dist/commands/read/show.js +197 -141
  114. package/dist/commands/registry-cli.js +12 -51
  115. package/dist/commands/remember.js +14 -57
  116. package/dist/commands/sources/add-cli.js +100 -31
  117. package/dist/commands/sources/bundle-cli.js +166 -0
  118. package/dist/commands/sources/bundle-config-ops.js +7 -2
  119. package/dist/commands/sources/info.js +18 -5
  120. package/dist/commands/sources/init.js +12 -12
  121. package/dist/commands/sources/installed-stashes.js +382 -98
  122. package/dist/commands/sources/schema-repair.js +3 -2
  123. package/dist/commands/sources/self-update.js +131 -38
  124. package/dist/commands/sources/source-add.js +72 -17
  125. package/dist/commands/sources/source-clone.js +129 -45
  126. package/dist/commands/sources/source-manage.js +43 -23
  127. package/dist/commands/sources/sources-cli.js +57 -208
  128. package/dist/commands/sources/stash-cli.js +46 -53
  129. package/dist/commands/tasks/tasks-cli.js +91 -97
  130. package/dist/commands/tasks/tasks.js +276 -421
  131. package/dist/commands/workflow-cli.js +175 -450
  132. package/dist/core/adapter/adapters/akm-adapter.js +47 -28
  133. package/dist/core/adapter/adapters/akm-lint.js +42 -27
  134. package/dist/core/adapter/adapters/akm-metadata.js +15 -44
  135. package/dist/core/adapter/adapters/akm-task-adapter.js +15 -13
  136. package/dist/core/adapter/adapters/akm-workflow-adapter.js +55 -71
  137. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -1
  138. package/dist/core/adapter/adapters/generic-files-adapter.js +2 -0
  139. package/dist/core/adapter/adapters/index.js +6 -6
  140. package/dist/core/adapter/adapters/llm-wiki-adapter.js +14 -8
  141. package/dist/core/adapter/adapters/okf-adapter.js +187 -19
  142. package/dist/core/adapter/adapters/shared.js +3 -19
  143. package/dist/core/adapter/adapters/tool-dir-shared.js +8 -3
  144. package/dist/core/adapter/adapters/website-snapshot-adapter.js +1 -0
  145. package/dist/core/adapter/detect-adapter.js +17 -0
  146. package/dist/core/adapter/recognize-match.js +6 -4
  147. package/dist/core/adapter/validate-context.js +214 -0
  148. package/dist/core/asset/akm-markdown.js +63 -0
  149. package/dist/core/asset/asset-placement.js +20 -6
  150. package/dist/core/asset/asset-ref.js +11 -9
  151. package/dist/core/asset/frontmatter-lint.js +30 -0
  152. package/dist/core/asset/frontmatter.js +37 -9
  153. package/dist/core/asset/markdown.js +40 -51
  154. package/dist/core/asset/resolve-ref.js +89 -18
  155. package/dist/core/asset/stash-meta.js +1 -1
  156. package/dist/core/bundle-id.js +51 -0
  157. package/dist/core/common.js +152 -38
  158. package/dist/core/config/config-io.js +12 -1
  159. package/dist/core/config/config-schema.js +35 -8
  160. package/dist/core/config/config-sources.js +55 -11
  161. package/dist/core/config/config-walker.js +25 -9
  162. package/dist/core/config/config.js +9 -48
  163. package/dist/core/config/experimental.js +21 -0
  164. package/dist/core/config/schema/embedding.js +5 -1
  165. package/dist/core/config/schema/experimental.js +30 -0
  166. package/dist/core/config/schema/improve-processes.js +0 -6
  167. package/dist/core/config/schema/improve.js +21 -3
  168. package/dist/core/config/schema/index-config.js +8 -15
  169. package/dist/core/config/schema/output.js +4 -1
  170. package/dist/core/config/schema/setup.js +9 -18
  171. package/dist/core/config/schema/sources-bundles.js +49 -33
  172. package/dist/core/config/schema/workflow.js +3 -3
  173. package/dist/core/env-secret-ref.js +76 -46
  174. package/dist/core/errors.js +18 -12
  175. package/dist/core/events.js +46 -128
  176. package/dist/core/file-change.js +6 -5
  177. package/dist/core/fs-txn.js +83 -7
  178. package/dist/core/git-message.js +2 -2
  179. package/dist/core/improve-result.js +1 -100
  180. package/dist/core/lesson-lint.js +1 -17
  181. package/dist/core/logs-db.js +2 -1
  182. package/dist/core/migration-operation.js +16 -0
  183. package/dist/core/mutation-target.js +78 -0
  184. package/dist/core/parse.js +4 -1
  185. package/dist/core/paths.js +17 -20
  186. package/dist/core/recognition-util.js +12 -14
  187. package/dist/core/redaction.js +34 -0
  188. package/dist/core/standards/resolve-standards-context.js +2 -14
  189. package/dist/core/standards/resolve-stash-standards.js +2 -2
  190. package/dist/core/standards/resolve-type-conventions.js +2 -2
  191. package/dist/core/state/migrations.js +41 -18
  192. package/dist/core/state-db.js +5 -14
  193. package/dist/core/structured.js +1 -1
  194. package/dist/core/subprocess.js +6 -4
  195. package/dist/core/text-truncation.js +9 -5
  196. package/dist/core/type-presentation.js +3 -3
  197. package/dist/core/warn.js +0 -3
  198. package/dist/core/write-source.js +771 -95
  199. package/dist/indexer/bundle-identity-guard.js +3 -2
  200. package/dist/indexer/db/graph-db.js +0 -24
  201. package/dist/indexer/ensure-index.js +1 -0
  202. package/dist/indexer/graph/graph-boost.js +9 -34
  203. package/dist/indexer/graph/graph-extraction.js +8 -5
  204. package/dist/indexer/index-writer-lock.js +53 -17
  205. package/dist/indexer/index-written-assets.js +16 -22
  206. package/dist/indexer/indexer.js +497 -239
  207. package/dist/indexer/installations.js +14 -96
  208. package/dist/indexer/passes/dir-staleness.js +16 -9
  209. package/dist/indexer/passes/memory-inference.js +11 -9
  210. package/dist/indexer/passes/metadata.js +113 -47
  211. package/dist/indexer/scan/doc-to-entry.js +38 -1
  212. package/dist/indexer/scan/drain-dir.js +13 -23
  213. package/dist/indexer/search/db-search.js +99 -54
  214. package/dist/indexer/search/fts-query.js +47 -24
  215. package/dist/indexer/search/ranking-contributors.js +42 -20
  216. package/dist/indexer/search/ranking.js +18 -99
  217. package/dist/indexer/search/search-fields.js +7 -2
  218. package/dist/indexer/search/search-source.js +82 -93
  219. package/dist/indexer/usage/usage-events.js +0 -89
  220. package/dist/indexer/walk/file-context.js +2 -1
  221. package/dist/indexer/walk/matchers.js +30 -43
  222. package/dist/indexer/walk/path-resolver.js +7 -2
  223. package/dist/indexer/walk/walker.js +38 -12
  224. package/dist/integrations/agent/builders.js +0 -6
  225. package/dist/integrations/agent/config.js +2 -2
  226. package/dist/integrations/agent/detect.js +49 -19
  227. package/dist/integrations/agent/engine-fallback.js +76 -0
  228. package/dist/integrations/agent/profiles.js +14 -0
  229. package/dist/integrations/agent/prompts.js +12 -8
  230. package/dist/integrations/agent/runner-dispatch.js +4 -2
  231. package/dist/integrations/agent/runner.js +0 -1
  232. package/dist/integrations/agent/spawn.js +5 -6
  233. package/dist/integrations/github.js +1 -1
  234. package/dist/integrations/harnesses/aider/agent-builder.js +6 -4
  235. package/dist/integrations/harnesses/amazonq/agent-builder.js +7 -4
  236. package/dist/integrations/harnesses/claude/session-log.js +0 -10
  237. package/dist/integrations/harnesses/codex/agent-builder.js +5 -2
  238. package/dist/integrations/harnesses/copilot/agent-builder.js +5 -3
  239. package/dist/integrations/harnesses/gemini/agent-builder.js +5 -3
  240. package/dist/integrations/harnesses/index.js +3 -7
  241. package/dist/integrations/harnesses/opencode/agent-builder.js +21 -2
  242. package/dist/integrations/harnesses/opencode/session-log.js +0 -15
  243. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +13 -4
  244. package/dist/integrations/harnesses/openhands/agent-builder.js +9 -6
  245. package/dist/integrations/harnesses/pi/agent-builder.js +6 -4
  246. package/dist/integrations/lockfile.js +101 -6
  247. package/dist/integrations/session-logs/index.js +3 -28
  248. package/dist/llm/client.js +136 -100
  249. package/dist/llm/embedders/remote.js +13 -5
  250. package/dist/llm/feature-gate.js +4 -12
  251. package/dist/llm/graph-extract.js +5 -11
  252. package/dist/llm/memory-infer.js +144 -1
  253. package/dist/llm/metadata-enhance.js +5 -7
  254. package/dist/llm/structured-call.js +1 -1
  255. package/dist/llm/usage-persist.js +26 -5
  256. package/dist/llm/usage-telemetry.js +25 -2
  257. package/dist/output/cli-hints.js +1 -2
  258. package/dist/output/context.js +22 -7
  259. package/dist/output/format-exempt.js +80 -0
  260. package/dist/output/generic-render.js +259 -0
  261. package/dist/output/render-registry.js +57 -0
  262. package/dist/output/renderers.js +14 -36
  263. package/dist/output/shapes/curate.js +10 -1
  264. package/dist/output/shapes/events.js +12 -7
  265. package/dist/output/shapes/helpers.js +56 -83
  266. package/dist/output/shapes/migrate.js +8 -0
  267. package/dist/output/shapes/passthrough.js +7 -41
  268. package/dist/output/shapes/proposal/producer.js +15 -7
  269. package/dist/output/shapes.js +2 -9
  270. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  271. package/dist/output/text/bundle-show.js +7 -0
  272. package/dist/output/text/command-format.js +164 -96
  273. package/dist/output/text/env.js +1 -3
  274. package/dist/output/text/events.js +8 -7
  275. package/dist/output/text/health-format.js +103 -0
  276. package/dist/output/text/health.js +7 -0
  277. package/dist/output/text/helpers.js +10 -8
  278. package/dist/output/text/lint-format.js +43 -0
  279. package/dist/output/text/{save.js → lint.js} +2 -2
  280. package/dist/output/text/migrate.js +88 -0
  281. package/dist/output/text/proposal/producer.js +4 -2
  282. package/dist/output/text/proposal-format.js +44 -72
  283. package/dist/output/text/registry-commands.js +1 -2
  284. package/dist/output/text/show-directives.js +15 -7
  285. package/dist/output/text/status-list.js +32 -0
  286. package/dist/output/text/sync.js +5 -0
  287. package/dist/output/text/workflow-format.js +24 -203
  288. package/dist/output/text/workflow.js +1 -7
  289. package/dist/output/text.js +16 -17
  290. package/dist/registry/factory.js +4 -6
  291. package/dist/registry/origin-resolve.js +16 -27
  292. package/dist/registry/providers/skills-sh.js +3 -3
  293. package/dist/registry/providers/static-index.js +13 -23
  294. package/dist/registry/resolve.js +42 -7
  295. package/dist/registry/semver.js +34 -84
  296. package/dist/runtime.js +2 -23
  297. package/dist/scripts/akm-migrate-node.js +60290 -0
  298. package/dist/scripts/akm-migrate.js +59628 -0
  299. package/dist/setup/detect.js +42 -15
  300. package/dist/setup/registry-stash-loader.js +2 -2
  301. package/dist/setup/setup.js +236 -136
  302. package/dist/setup/steps/connection.js +7 -9
  303. package/dist/setup/steps/platforms.js +9 -9
  304. package/dist/setup/steps/semantic.js +15 -3
  305. package/dist/setup/steps/sources.js +12 -13
  306. package/dist/setup/steps/stashdir.js +2 -3
  307. package/dist/setup/steps/tasks.js +237 -120
  308. package/dist/sources/freshness.js +1 -1
  309. package/dist/sources/provider-factory.js +11 -17
  310. package/dist/sources/providers/filesystem.js +2 -3
  311. package/dist/sources/providers/git-install.js +278 -34
  312. package/dist/sources/providers/git-provider.js +25 -23
  313. package/dist/sources/providers/git-stash.js +395 -106
  314. package/dist/sources/providers/git.js +2 -2
  315. package/dist/sources/providers/npm.js +16 -19
  316. package/dist/sources/providers/provider-utils.js +7 -4
  317. package/dist/sources/providers/sync-from-ref.js +3 -9
  318. package/dist/sources/providers/website.js +6 -1
  319. package/dist/sources/resolve.js +6 -5
  320. package/dist/sources/snapshot-fetchers/bluesky.js +146 -0
  321. package/dist/sources/snapshot-fetchers/content-extract.js +566 -0
  322. package/dist/sources/snapshot-fetchers/fetcher-util.js +41 -0
  323. package/dist/sources/snapshot-fetchers/github.js +100 -0
  324. package/dist/sources/snapshot-fetchers/host-guard.js +291 -0
  325. package/dist/sources/snapshot-fetchers/registry.js +17 -1
  326. package/dist/sources/snapshot-fetchers/robots.js +348 -0
  327. package/dist/sources/snapshot-fetchers/rss.js +282 -0
  328. package/dist/sources/snapshot-fetchers/secret-seam.js +42 -0
  329. package/dist/sources/snapshot-fetchers/website-ingest.js +566 -268
  330. package/dist/sources/snapshot-fetchers/x.js +910 -0
  331. package/dist/storage/database.js +7 -0
  332. package/dist/storage/engines/sqlite-migrations.js +23 -111
  333. package/dist/storage/managed-db.js +2 -2
  334. package/dist/storage/repositories/canaries-repository.js +1 -1
  335. package/dist/storage/repositories/events-repository.js +27 -11
  336. package/dist/storage/repositories/improve-runs-repository.js +6 -12
  337. package/dist/storage/repositories/index-connection.js +17 -6
  338. package/dist/storage/repositories/index-entries-repository.js +151 -240
  339. package/dist/storage/repositories/index-entry-mapper.js +15 -11
  340. package/dist/storage/repositories/index-fts-repository.js +5 -2
  341. package/dist/storage/repositories/index-llm-cache-repository.js +0 -1
  342. package/dist/storage/repositories/index-meta-repository.js +2 -3
  343. package/dist/storage/repositories/index-schema.js +10 -25
  344. package/dist/storage/repositories/index-utility-repository.js +15 -28
  345. package/dist/storage/repositories/index-vec-repository.js +6 -1
  346. package/dist/storage/repositories/outcome-repository.js +119 -0
  347. package/dist/storage/repositories/proposals-repository.js +296 -59
  348. package/dist/storage/repositories/registry-cache.js +19 -0
  349. package/dist/storage/repositories/salience-repository.js +172 -0
  350. package/dist/storage/repositories/task-history-repository.js +15 -13
  351. package/dist/storage/repositories/workflow-runs-repository.js +52 -40
  352. package/dist/tasks/backends/cron.js +105 -15
  353. package/dist/tasks/backends/index.js +1 -1
  354. package/dist/tasks/backends/launchd.js +85 -38
  355. package/dist/tasks/backends/schtasks.js +135 -15
  356. package/dist/tasks/embedded.js +56 -40
  357. package/dist/tasks/parser.js +7 -157
  358. package/dist/tasks/resolve-akm-bin.js +137 -59
  359. package/dist/tasks/runner.js +79 -42
  360. package/dist/tasks/scheduler-invocation.js +220 -10
  361. package/dist/tasks/schema.js +24 -1
  362. package/dist/tasks/task-id.js +1 -3
  363. package/dist/tasks/validator.js +20 -6
  364. package/dist/workflows/authoring/authoring.js +94 -143
  365. package/dist/workflows/authoring/scope-key.js +1 -1
  366. package/dist/workflows/exec/frozen-judge.js +28 -2
  367. package/dist/workflows/exec/native-executor.js +77 -57
  368. package/dist/workflows/exec/param-secrets.js +9 -9
  369. package/dist/workflows/exec/run-workflow.js +133 -79
  370. package/dist/workflows/exec/step-work.js +219 -346
  371. package/dist/{migrate-storage-node.mjs → workflows/exec/unit-dispatch.js} +1 -5
  372. package/dist/workflows/ir/compile.js +141 -270
  373. package/dist/workflows/ir/freeze.js +40 -30
  374. package/dist/workflows/ir/params.js +135 -11
  375. package/dist/workflows/ir/plan-hash.js +1 -1
  376. package/dist/workflows/ir/schema.js +25 -26
  377. package/dist/workflows/parser.js +872 -307
  378. package/dist/workflows/program/expressions.js +20 -208
  379. package/dist/workflows/program/schema.js +7 -10
  380. package/dist/workflows/renderer.js +95 -68
  381. package/dist/workflows/resource-limits.js +2 -0
  382. package/dist/workflows/runtime/checkin.js +3 -3
  383. package/dist/workflows/runtime/plan-classifier.js +16 -75
  384. package/dist/workflows/runtime/runs.js +186 -127
  385. package/dist/workflows/runtime/unit-checkin.js +1 -1
  386. package/dist/workflows/runtime/unit-phases.js +2 -2
  387. package/dist/workflows/runtime/workflow-asset-loader.js +232 -83
  388. package/dist/workflows/schema.js +1 -11
  389. package/dist/workflows/validate-summary.js +30 -36
  390. package/dist/workflows/validator.js +21 -62
  391. package/docs/README.md +68 -0
  392. package/docs/migration/README.md +8 -0
  393. package/docs/migration/release-notes/0.7.0.md +11 -11
  394. package/docs/migration/release-notes/0.9.0.md +208 -27
  395. package/docs/migration/v0.7-to-v0.8.md +46 -47
  396. package/docs/migration/v0.8-to-v0.9.md +564 -208
  397. package/docs/migration/v0.9.0-troubleshooting.md +561 -0
  398. package/docs/reference/README.md +12 -0
  399. package/docs/reference/cli.md +2253 -0
  400. package/docs/reference/configuration.md +358 -0
  401. package/docs/reference/data-and-telemetry.md +105 -42
  402. package/docs/reference/workflows.md +647 -0
  403. package/package.json +22 -11
  404. package/schemas/akm-asset-envelope.json +93 -0
  405. package/schemas/akm-config.json +81 -128
  406. package/schemas/akm-workflow.json +74 -73
  407. package/dist/assets/tasks/core/backup.yml +0 -5
  408. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  409. package/dist/cli/config-migrate.js +0 -1806
  410. package/dist/cli/config-validate.js +0 -41
  411. package/dist/commands/backup-cli.js +0 -56
  412. package/dist/commands/bundle/bundle-cli.js +0 -68
  413. package/dist/commands/bundle/bundle.js +0 -219
  414. package/dist/commands/graph/graph-cli.js +0 -124
  415. package/dist/commands/graph/graph.js +0 -489
  416. package/dist/commands/improve/extract-watch.js +0 -140
  417. package/dist/commands/mv-cli.js +0 -1221
  418. package/dist/commands/sources/history.js +0 -201
  419. package/dist/commands/tasks/default-tasks.js +0 -186
  420. package/dist/core/migration-backup.js +0 -1234
  421. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -95
  422. package/dist/llm/memory-infer-impl.js +0 -138
  423. package/dist/migrate/legacy/config-source-migration.js +0 -223
  424. package/dist/migrate/legacy/content-migration.js +0 -305
  425. package/dist/migrate/legacy/legacy-layout.js +0 -779
  426. package/dist/migrate/legacy/legacy-paths.js +0 -25
  427. package/dist/migrate/legacy/legacy-stash-json.js +0 -72
  428. package/dist/migrate/legacy/proposal-fs-import.js +0 -168
  429. package/dist/migrate/legacy/task-target-ref-migration.js +0 -272
  430. package/dist/migrate/legacy/three-db-cutover.js +0 -841
  431. package/dist/migrate/legacy/workflow-migrations-bodies.js +0 -52
  432. package/dist/migrate/legacy/workflow-migrations-frozen.js +0 -21
  433. package/dist/migrate/legacy-ref-grammar.js +0 -214
  434. package/dist/output/shapes/distill.js +0 -14
  435. package/dist/output/shapes/history.js +0 -11
  436. package/dist/output/text/distill.js +0 -6
  437. package/dist/output/text/enable-disable.js +0 -8
  438. package/dist/output/text/history.js +0 -6
  439. package/dist/registry/build-index.js +0 -382
  440. package/dist/schemas/akm-config.json +0 -4704
  441. package/dist/schemas/akm-task.json +0 -87
  442. package/dist/schemas/akm-workflow.json +0 -372
  443. package/dist/scripts/migrate-storage.js +0 -3816
  444. package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
  445. package/dist/workflows/cli.js +0 -53
  446. package/dist/workflows/exec/brief.js +0 -481
  447. package/dist/workflows/exec/report.js +0 -1460
  448. package/dist/workflows/exec/watch.js +0 -116
  449. package/dist/workflows/program/parser.js +0 -813
  450. package/dist/workflows/program/project.js +0 -104
@@ -4,109 +4,78 @@
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
6
6
  import workflowTemplate from "../../assets/workflows/workflow-template.md" with { type: "text" };
7
- import { isWithin, resolveStashDir } from "../../core/common.js";
7
+ import { ensureAkmMarkdownType } from "../../core/asset/akm-markdown.js";
8
+ import { makeBundleRef } from "../../core/asset/asset-ref.js";
9
+ import { isWithin, writeFileAtomic } from "../../core/common.js";
10
+ import { loadConfig } from "../../core/config/config.js";
8
11
  import { UsageError } from "../../core/errors.js";
12
+ import { defaultBundleForTarget } from "../../core/mutation-target.js";
9
13
  import { canonicalizeWorkflowName, WORKFLOW_EXTENSIONS } from "../../core/recognition-util.js";
10
14
  import { warn } from "../../core/warn.js";
11
- import { compileWorkflowProgram } from "../ir/compile.js";
15
+ import { prepareWriteTargetForMutation, resolveWriteTarget, withWriteTargetMutation } from "../../core/write-source.js";
16
+ import { compileWorkflowPlan } from "../ir/compile.js";
12
17
  import { parseWorkflow } from "../parser.js";
13
- import { parseWorkflowProgram } from "../program/parser.js";
14
- import workflowProgramTemplate from "./workflow-program-template.yaml" with { type: "text" };
15
- const DEFAULT_WORKFLOW_TEMPLATE = renderWorkflowTemplate({
16
- title: "Example Workflow",
17
- firstStepTitle: "First Step",
18
- firstStepId: "first-step",
19
- });
18
+ const DEFAULT_WORKFLOW_TEMPLATE = renderWorkflowTemplate("New Workflow");
20
19
  export function getWorkflowTemplate() {
21
20
  return DEFAULT_WORKFLOW_TEMPLATE;
22
21
  }
23
- /**
24
- * Minimal valid YAML workflow *program* (redesign addendum, R1), printed by
25
- * `akm workflow template --yaml`. Kept as an external asset file per the repo
26
- * convention (see `workflow-program-template.yaml` next to this module);
27
- * `tests/workflows/program-assets.test.ts` pins that it parses AND compiles.
28
- */
29
- export function getWorkflowProgramTemplate() {
30
- return workflowProgramTemplate;
31
- }
32
22
  export function buildWorkflowTemplate(name) {
33
23
  if (!name)
34
24
  return DEFAULT_WORKFLOW_TEMPLATE;
35
- const title = humanizeWorkflowName(name);
36
- const stepId = slugifyWorkflowStepId(name);
37
- const customized = renderWorkflowTemplate({
38
- title,
39
- firstStepTitle: `${title} Setup`,
40
- firstStepId: `${stepId}-setup`,
41
- });
42
- const result = parseWorkflow(customized, { path: `<template:${name}>` });
43
- if (!result.ok) {
44
- throw new UsageError(formatWorkflowErrors(`<template:${name}>`, result.errors));
45
- }
25
+ // Only the H1 is customized. Step ids are STRUCTURAL under the unified format
26
+ // each appears in `steps[].id`, as a body `## <id>` heading, and possibly in
27
+ // another step's `inputs:` — so rewriting them from a name would have to patch
28
+ // three places consistently to stay parseable. Generic `first-step`/
29
+ // `second-step` are the author's to rename.
30
+ const customized = renderWorkflowTemplate(humanizeWorkflowName(name));
31
+ validateWorkflowContent(customized, `<template:${name}>`);
46
32
  return customized;
47
33
  }
48
- /**
49
- * Customize the shipped YAML program template ({@link getWorkflowProgramTemplate})
50
- * for a named workflow: swap the placeholder `name:` for the workflow's slug so
51
- * a freshly created `.yaml`/`.yml` asset round-trips through the program parser
52
- * (its `title` becomes the slug). Parses AND compiles the result — mirroring
53
- * {@link buildWorkflowTemplate} — so a create never writes an asset that
54
- * `show`/`start`/`validate` would then reject.
55
- */
56
- export function buildWorkflowProgramTemplate(name) {
57
- if (!name)
58
- return workflowProgramTemplate;
59
- const programName = slugifyWorkflowStepId(name);
60
- const customized = workflowProgramTemplate.replace(/^name:.*$/m, `name: ${programName}`);
61
- const parsed = parseWorkflowProgram(customized, { path: `<template:${name}>` });
62
- if (!parsed.ok) {
63
- throw new UsageError(formatWorkflowErrors(`<template:${name}>`, parsed.errors));
34
+ /** Parse AND compile `content`, so a create never writes an asset that `show`/`start`/`validate` would then reject. */
35
+ function validateWorkflowContent(content, sourcePath) {
36
+ const result = parseWorkflow(content, { path: sourcePath });
37
+ if (!result.ok) {
38
+ throw new UsageError(formatWorkflowErrors(sourcePath, result.errors));
64
39
  }
65
- const compiled = compileWorkflowProgram(parsed.program);
40
+ const compiled = compileWorkflowPlan(result.document, slugifyWorkflowStepId(sourcePath));
66
41
  if (!compiled.ok) {
67
- throw new UsageError(formatWorkflowErrors(`<template:${name}>`, compiled.errors));
42
+ throw new UsageError(formatWorkflowErrors(sourcePath, compiled.errors));
68
43
  }
69
- return customized;
70
44
  }
71
- /** Recognized YAML workflow-program suffixes (see {@link WORKFLOW_EXTENSIONS}). */
72
- const WORKFLOW_PROGRAM_SUFFIX_RE = /\.ya?ml$/i;
45
+ /** Recognized workflow-program suffixes creating one is a lint-time usage error (workflow-format-unification). */
46
+ const YAML_SUFFIX_RE = /\.ya?ml$/i;
47
+ export function assertWorkflowMarkdownName(name) {
48
+ if (!YAML_SUFFIX_RE.test(name.trim()))
49
+ return;
50
+ throw new UsageError(`Workflows are markdown-only now (workflow-format-unification) — "${name}" cannot be created. ` +
51
+ `Use a plain name (no ".yaml"/".yml" suffix); the orchestration graph lives in the ".md" file's frontmatter.`);
52
+ }
73
53
  export function createWorkflowAsset(input) {
74
- const stashDir = resolveStashDir();
75
- const typeRoot = path.join(stashDir, "workflows");
76
- fs.mkdirSync(typeRoot, { recursive: true });
77
- // A `.yaml`/`.yml` name selects the YAML *program* format (redesign
78
- // addendum, R1); capture the exact suffix the user typed so the written file
79
- // keeps it, then strip every workflow extension to get the canonical name.
80
- const suffixMatch = input.name.trim().replace(/\\/g, "/").match(WORKFLOW_PROGRAM_SUFFIX_RE);
81
- const programSuffix = suffixMatch ? suffixMatch[0].toLowerCase() : undefined;
82
- const isProgram = programSuffix !== undefined;
54
+ assertWorkflowMarkdownName(input.name);
55
+ const config = loadConfig();
56
+ const resolvedTarget = resolveWriteTarget(config);
57
+ const target = prepareWriteTargetForMutation(resolvedTarget, { allowedAdapters: ["akm", "akm-workflow"] });
58
+ const stashDir = target.source.path;
59
+ const standaloneWorkflowBundle = target.source.adapterId === "akm-workflow";
60
+ const typeRoot = standaloneWorkflowBundle ? stashDir : path.join(stashDir, "workflows");
83
61
  const normalizedName = normalizeWorkflowName(input.name);
84
- // The write target is DEFINITIVE the canonical name plus the chosen format's
85
- // extension (`.yaml`/`.yml` for a program, `.md` for markdown). We deliberately
86
- // do NOT go through `assetPathForName`, which PROBES existing files and
87
- // would redirect a markdown create onto an existing `foo.yaml` (writing
88
- // markdown into a `.yaml`). Computing the target directly makes a markdown
89
- // create always write `.md`, so the finding-C cross-extension check below sees
90
- // the real collision instead of a self-match.
91
- const targetSuffix = isProgram ? programSuffix : ".md";
92
- const assetPath = path.join(typeRoot, `${normalizedName}${targetSuffix}`);
93
- if (!isWithin(assetPath, typeRoot)) {
94
- throw new UsageError(`Resolved workflow path escapes the stash: "${normalizedName}"`, "PATH_ESCAPE_VIOLATION");
95
- }
96
- // Codex round-3 finding C: a `workflows/<name>` ref is canonical across every
97
- // recognized extension (`.md`/`.yaml`/`.yml`) and resolves `.md` BEFORE
98
- // `.yaml`. So creating `foo.yaml` while `foo.md` exists would return the ref
99
- // `workflows/foo` that still starts the OLD markdown workflow a silently
100
- // shadowed asset. Reject creation when ANY recognized extension already holds
101
- // the same canonical name, naming the existing file. A same-extension collision
102
- // (the target path itself exists) keeps the classic `--force` overwrite escape;
103
- // a DIFFERENT-extension collision cannot be force-overwritten (writing a new
104
- // file would leave the old one shadowing it) — remove the existing file first.
105
- const existingPaths = WORKFLOW_EXTENSIONS.map((ext) => path.join(typeRoot, `${normalizedName}${ext}`)).filter((candidate) => fs.existsSync(candidate));
106
- const conflicting = existingPaths.find((p) => p !== assetPath);
107
- if (conflicting !== undefined) {
108
- throw new UsageError(`Workflow "${normalizedName}" already exists as ${path.relative(stashDir, conflicting)} — the ` +
109
- `\`workflows/${normalizedName}\` ref resolves to that file, so creating this one would shadow it. ` +
62
+ const conceptId = standaloneWorkflowBundle ? normalizedName : `workflows/${normalizedName}`;
63
+ const assetPath = path.join(typeRoot, `${normalizedName}.md`);
64
+ const relativeAssetPath = path.relative(path.resolve(typeRoot), path.resolve(assetPath));
65
+ if (relativeAssetPath === ".." ||
66
+ relativeAssetPath.startsWith(`..${path.sep}`) ||
67
+ path.isAbsolute(relativeAssetPath)) {
68
+ throw new UsageError(`Resolved workflow path escapes the bundle: "${normalizedName}"`, "PATH_ESCAPE_VIOLATION");
69
+ }
70
+ // A file whose canonical name matches but whose PATH differs can only be a
71
+ // case variant now (`upper.MD` vs `upper.md`) — the cross-EXTENSION case this
72
+ // guard also covered is gone with the unified `.md`-only format. It still
73
+ // matters: on a case-insensitive filesystem the two spellings are one file,
74
+ // so writing the target would silently clobber or shadow the existing asset.
75
+ const shadowing = findExistingWorkflowPaths(typeRoot, normalizedName).find((p) => p !== assetPath);
76
+ if (shadowing !== undefined) {
77
+ throw new UsageError(`Workflow "${normalizedName}" already exists as ${path.relative(stashDir, shadowing)}the ` +
78
+ `\`${conceptId}\` ref resolves to that file, so creating this one would shadow it. ` +
110
79
  `Remove or rename the existing file first, or create the workflow under a different name.`, "RESOURCE_ALREADY_EXISTS");
111
80
  }
112
81
  if (fs.existsSync(assetPath) && !input.force) {
@@ -114,33 +83,19 @@ export function createWorkflowAsset(input) {
114
83
  }
115
84
  const content = input.from
116
85
  ? readWorkflowSource(input.from, stashDir)
117
- : (input.content ??
118
- (isProgram ? buildWorkflowProgramTemplate(normalizedName) : buildWorkflowTemplate(normalizedName)));
119
- const sourcePath = input.from ?? `workflows/${normalizedName}${isProgram ? programSuffix : ".md"}`;
120
- // Validate against the format the destination extension selects — a YAML
121
- // program parses+compiles as a program, markdown as a document — so the
122
- // created asset is guaranteed usable by show/start/validate, which pick
123
- // their parser by the same extension.
124
- if (isProgram) {
125
- const parsed = parseWorkflowProgram(content, { path: sourcePath });
126
- if (!parsed.ok) {
127
- throw new UsageError(formatWorkflowErrors(sourcePath, parsed.errors));
128
- }
129
- const compiled = compileWorkflowProgram(parsed.program);
130
- if (!compiled.ok) {
131
- throw new UsageError(formatWorkflowErrors(sourcePath, compiled.errors));
132
- }
133
- }
134
- else {
135
- const result = parseWorkflow(content, { path: sourcePath });
136
- if (!result.ok) {
137
- throw new UsageError(formatWorkflowErrors(sourcePath, result.errors));
138
- }
139
- }
140
- fs.mkdirSync(path.dirname(assetPath), { recursive: true });
141
- fs.writeFileSync(assetPath, content.endsWith("\n") ? content : `${content}\n`, "utf8");
86
+ : (input.content ?? buildWorkflowTemplate(normalizedName));
87
+ const sourcePath = input.from ?? `workflows/${normalizedName}.md`;
88
+ validateWorkflowContent(content, sourcePath);
89
+ const authoredContent = ensureAkmMarkdownType(content, "workflow");
90
+ const mode = fs.existsSync(assetPath) ? fs.lstatSync(assetPath).mode & 0o777 : 0o644;
91
+ const defaultBundle = defaultBundleForTarget(config);
92
+ const ref = makeBundleRef(target.source.name === defaultBundle ? undefined : target.source.name, conceptId);
93
+ withWriteTargetMutation(target, [assetPath], { ignored: "reject", purpose: "workflow-authoring", message: `Create ${ref}` }, () => {
94
+ fs.mkdirSync(path.dirname(assetPath), { recursive: true });
95
+ writeFileAtomic(assetPath, authoredContent.endsWith("\n") ? authoredContent : `${authoredContent}\n`, mode);
96
+ });
142
97
  return {
143
- ref: `workflows/${normalizedName}`,
98
+ ref,
144
99
  path: assetPath,
145
100
  stashDir,
146
101
  };
@@ -167,10 +122,9 @@ function readWorkflowSource(source, stashDir) {
167
122
  return fs.readFileSync(resolved, "utf8");
168
123
  }
169
124
  function normalizeWorkflowName(name) {
170
- // Strip any recognized workflow extension (.md/.yaml/.yml) so the canonical
171
- // name — and thus the `workflows/<name>` ref — is extension-free regardless of
172
- // how the user spelled it. The chosen format is recovered from the raw suffix
173
- // by the caller (createWorkflowAsset).
125
+ // Strip a recognized workflow extension (.md) so the canonical name — and
126
+ // thus the `workflows/<name>` ref — is extension-free regardless of how the
127
+ // user spelled it.
174
128
  const normalized = canonicalizeWorkflowName(name
175
129
  .trim()
176
130
  .replace(/\\/g, "/")
@@ -190,7 +144,7 @@ function humanizeWorkflowName(name) {
190
144
  .pop()
191
145
  ?.replace(/[-_]+/g, " ")
192
146
  .replace(/\b\w/g, (match) => match.toUpperCase())
193
- .trim() || "Example Workflow");
147
+ .trim() || "New Workflow");
194
148
  }
195
149
  function slugifyWorkflowStepId(name) {
196
150
  return (name
@@ -222,34 +176,31 @@ export function validateWorkflowSource(target) {
222
176
  return { path: target, parse: parseWorkflow(content, { path: target }) };
223
177
  }
224
178
  /**
225
- * Validate a YAML workflow *program* by filesystem path: parse via
226
- * `parseWorkflowProgram`, then only when the parse is clean compile via
227
- * `compileWorkflowProgram` so expression/reference errors surface too. Both
228
- * error lists carry line numbers and are returned in the same
229
- * `WorkflowError[]` shape, ready for `formatWorkflowErrors`. Throws
230
- * `UsageError` only when the target cannot be located on disk.
179
+ * Every file under `typeRoot` whose canonical workflow name equals
180
+ * `normalizedName`. Extension matching is case-INSENSITIVE, so `foo.MD` and
181
+ * `foo.md` both match the case-variant collision the create path must refuse
182
+ * rather than silently clobber on a case-insensitive filesystem.
231
183
  */
232
- export function validateWorkflowProgramSource(target) {
233
- const resolved = path.resolve(target);
234
- if (!fs.existsSync(resolved)) {
235
- throw new UsageError(`Workflow file not found: "${target}".`);
236
- }
237
- const content = fs.readFileSync(resolved, "utf8");
238
- const parse = parseWorkflowProgram(content, { path: target });
239
- if (!parse.ok) {
240
- return { path: target, result: { ok: false, errors: parse.errors } };
241
- }
242
- const compiled = compileWorkflowProgram(parse.program);
243
- if (!compiled.ok) {
244
- return { path: target, result: { ok: false, errors: compiled.errors } };
184
+ function findExistingWorkflowPaths(typeRoot, normalizedName) {
185
+ const parent = path.join(typeRoot, path.dirname(normalizedName));
186
+ let entries;
187
+ try {
188
+ entries = fs.readdirSync(parent, { withFileTypes: true });
245
189
  }
246
- // Non-fatal advisories ride along on a successful validation (additive) so
247
- // `workflow validate` can surface them without changing the ok verdict.
248
- return { path: target, result: { ok: true, program: parse.program, warnings: compiled.warnings } };
190
+ catch {
191
+ return [];
192
+ }
193
+ const basename = path.basename(normalizedName);
194
+ return WORKFLOW_EXTENSIONS.flatMap((extension) => entries
195
+ .filter((entry) => {
196
+ if (!entry.isFile() && !entry.isSymbolicLink())
197
+ return false;
198
+ const entryExtension = path.extname(entry.name);
199
+ return entryExtension.toLowerCase() === extension && entry.name.slice(0, -entryExtension.length) === basename;
200
+ })
201
+ .sort((a, b) => a.name.localeCompare(b.name))
202
+ .map((entry) => path.join(parent, entry.name)));
249
203
  }
250
- function renderWorkflowTemplate(input) {
251
- return workflowTemplate
252
- .replace("{{TITLE}}", input.title)
253
- .replace("{{FIRST_STEP_TITLE}}", input.firstStepTitle)
254
- .replace("{{FIRST_STEP_ID}}", input.firstStepId);
204
+ function renderWorkflowTemplate(title) {
205
+ return workflowTemplate.replace("{{TITLE}}", title);
255
206
  }
@@ -21,7 +21,7 @@ export function resolveWorkflowScopeAnchor(startDir) {
21
21
  if (gitRoot)
22
22
  return gitRoot;
23
23
  try {
24
- const stashDir = safeRealpath(resolveStashDir({ readOnly: true }));
24
+ const stashDir = safeRealpath(resolveStashDir());
25
25
  if (isWithin(cwd, stashDir))
26
26
  return stashDir;
27
27
  }
@@ -4,12 +4,37 @@
4
4
  import { deepMergeConfig } from "../../core/config/deep-merge.js";
5
5
  import { ConfigError } from "../../core/errors.js";
6
6
  /** Build a gate judge from a v3 catalog entry without consulting live config. */
7
- export function frozenSummaryJudge(plan, invocation) {
7
+ export function frozenSummaryJudge(plan, invocation, signal, dispatcher) {
8
8
  if (!invocation)
9
9
  return null;
10
10
  const engine = plan.execution?.engines[invocation.engine];
11
- if (!engine || engine.kind !== "llm")
11
+ if (!engine)
12
12
  throw new ConfigError(`Frozen gate engine "${invocation.engine}" is unavailable.`, "INVALID_CONFIG_FILE");
13
+ if (engine.kind === "agent") {
14
+ if (!dispatcher) {
15
+ throw new ConfigError(`Frozen agent gate engine "${invocation.engine}" has no unit dispatcher.`, "INVALID_CONFIG_FILE");
16
+ }
17
+ return async ({ system, user }) => {
18
+ const fallbackEngine = engine.fallbackLlmEngine ? plan.execution.engines[engine.fallbackLlmEngine] : undefined;
19
+ const result = await dispatcher({
20
+ runId: "gate",
21
+ stepId: "gate",
22
+ unitId: "gate",
23
+ nodeId: "gate",
24
+ prompt: user,
25
+ systemPrompt: system,
26
+ engine,
27
+ ...(fallbackEngine?.kind === "llm" ? { fallbackEngine } : {}),
28
+ invocation,
29
+ timeoutMs: invocation.timeoutMs,
30
+ ...(signal ? { signal } : {}),
31
+ });
32
+ if (!result.ok) {
33
+ throw new Error(result.error || `Verification engine "${invocation.engine}" failed.`);
34
+ }
35
+ return result.text;
36
+ };
37
+ }
13
38
  return async ({ system, user }) => {
14
39
  const { chatCompletion } = await import("../../llm/client.js");
15
40
  return chatCompletion(materialize(engine, invocation), [
@@ -17,6 +42,7 @@ export function frozenSummaryJudge(plan, invocation) {
17
42
  { role: "user", content: user },
18
43
  ], {
19
44
  timeoutMs: invocation.timeoutMs,
45
+ ...(signal ? { signal } : {}),
20
46
  });
21
47
  };
22
48
  }
@@ -2,7 +2,7 @@
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
4
  /**
5
- * Native executor — executes ONE step's IR v2 subgraph (`IrStepPlan.root`) on
5
+ * Native executor — executes one frozen step subgraph (`IrStepPlan.root`) on
6
6
  * the local machine: fan-out through the scheduler, schema-validated
7
7
  * structured output through `runStructured` (core/structured.ts), per-unit
8
8
  * persistence through the serialized writer queue, and `workflow_unit_*`
@@ -33,12 +33,11 @@
33
33
  *
34
34
  * Empty free-text outputs (peer review): a SUCCESSFUL schemaless unit that
35
35
  * returns the empty string is normalized to "no output" — {@link dispatchUnit}
36
- * drops the falsy `text`, `finishUnit` journals `result_json = NULL`, and both
37
- * durable-row reuse and the R3 report surface rehydrate the same absence
38
- * (`unitOutcomeFromRow`). This is the ONLY empty-output resolution: `''` never
39
- * survives on any surface, so the live artifact cannot diverge from the
40
- * resume/report artifact (the cross-surface parity cardinal rule; the
41
- * `EMPTY_OUTPUT` driver-parity golden pins it). Consequences that follow from
36
+ * drops the falsy `text`, `finishUnit` journals `result_json = NULL`, and
37
+ * durable-row reuse rehydrates the same absence (`unitOutcomeFromRow`). This is
38
+ * the ONLY empty-output resolution: `''` never survives into the journal, so the
39
+ * live artifact cannot diverge from the artifact a resume rebuilds from the same
40
+ * rows (the byte-identical-graph cardinal rule). Consequences that follow from
42
41
  * "empty == absent", not special-cased anywhere:
43
42
  * - a SOLO empty step promotes `output = null` (the unit's absent text ??
44
43
  * null); a `collect` fan-out promotes `null` in that item's slot.
@@ -74,9 +73,7 @@
74
73
  * never a silent re-dispatch — under a frozen plan the same identity must
75
74
  * reproduce the same inputs, so a mismatch means the journal (or params
76
75
  * row) was tampered with. Failed/running/missing rows dispatch live.
77
- * - Pre-release R1 journals used positional ids (`node.unit[3]`). There is
78
- * no back-compat shim: those rows simply never match a content-derived id
79
- * and are ignored (the step re-runs cleanly on top of them).
76
+ * - Rows with unrelated ids never match a content-derived id and are ignored.
80
77
  *
81
78
  * Gate loops (addendum, R2 `gate.max_loops`): when the engine re-executes a
82
79
  * step subgraph after a gate rejection, it threads the judge's feedback in as
@@ -136,17 +133,15 @@ import { validateJsonSchemaSubset } from "../../core/json-schema.js";
136
133
  import { collectSensitiveValues, isEnvPassthroughValueSafeToExpose, redactSensitiveValue } from "../../core/redaction.js";
137
134
  import { runStructured } from "../../core/structured.js";
138
135
  import { warn } from "../../core/warn.js";
136
+ import { insertEventStrict } from "../../storage/repositories/events-repository.js";
139
137
  import { withWorkflowRunsRepo } from "../../storage/repositories/workflow-runs-repository.js";
140
138
  import { LIFETIME_UNIT_CAP, scheduleUnits, UnitCapExceededError } from "./scheduler.js";
141
- // Shared step semantics — the ONE implementation consumed by both the engine
142
- // (this module + run-workflow.ts) and, from R3, the brief/report driver
143
- // protocol. This module dispatches; step-work.ts owns the pure decisions.
144
- import { buildArtifactSummary, computeStepWorkList, DEFAULT_UNIT_TIMEOUT_MS, projectStepOutput, reduceEmptyStep, reduceStepOutcomes, stepOutputsFromEvidence, unitOutcomeFromRow, } from "./step-work.js";
139
+ // Shared step semantics — the ONE implementation consumed by the engine
140
+ // (this module + run-workflow.ts) on both the fresh-execution and the resume
141
+ // path. This module dispatches; step-work.ts owns the pure decisions.
142
+ import { computeStepWorkList, reduceEmptyStep, reduceStepOutcomes, stepOutputsFromEvidence, unitOutcomeFromRow, } from "./step-work.js";
145
143
  import { enqueueUnitWrite } from "./unit-writer.js";
146
144
  import { assertGitWorkTree, cleanupUnitWorktree, createUnitWorktree } from "./worktree.js";
147
- // Re-exported for existing consumers (run-workflow.ts, tests) that import these
148
- // from native-executor; they now live in the shared step-work module.
149
- export { buildArtifactSummary, DEFAULT_UNIT_TIMEOUT_MS, projectStepOutput };
150
145
  /**
151
146
  * Mutable per-step dispatch budget: the lifetime unit cap PLUS the declared
152
147
  * run-level budget ceilings (`budget.max_units` / `budget.max_tokens`,
@@ -248,10 +243,11 @@ export async function executeStepPlan(plan, ctx) {
248
243
  const dispatched = ctx.unitsDispatched ?? 0;
249
244
  // Work-list computation is the SHARED, PURE decision (step-work.ts): resolve
250
245
  // the fan-out list, derive content-derived unit ids, assemble each unit's
251
- // prompt, and hash its resolved input. `brief` (R3) computes the identical
252
- // list — that shared implementation is the anti-drift guarantee. This module
253
- // owns only the impure remainder: env/worktree preflight, durable-row reuse,
254
- // dispatch, journaling, budget.
246
+ // prompt, and hash its resolved input. A resume recomputes the identical list
247
+ // from the same frozen plan — that shared pure implementation is what lets
248
+ // journaled rows be matched instead of re-executed. This module owns only the
249
+ // impure remainder: env/worktree preflight, durable-row reuse, dispatch,
250
+ // journaling, budget.
255
251
  const workList = computeStepWorkList(plan, {
256
252
  runId: ctx.runId,
257
253
  params: ctx.params,
@@ -266,10 +262,9 @@ export async function executeStepPlan(plan, ctx) {
266
262
  const { template, reducer, isFanOut, items, units: workUnits } = workList.list;
267
263
  if (items.length === 0) {
268
264
  // Empty fan-out: the promoted artifact is the degenerate empty value, honored
269
- // against the step's declared output schema. `reduceEmptyStep` is the SHARED
270
- // decision (step-work.ts) the R3 report surface also uses to auto-complete an
271
- // empty step the spine reaches, so both surfaces promote the identical
272
- // artifact + schema verdict.
265
+ // against the step's declared output schema. `reduceEmptyStep` (step-work.ts)
266
+ // owns that decision so a zero-unit step promotes the identical artifact +
267
+ // schema verdict every time the spine reaches it.
273
268
  return { ...reduceEmptyStep(plan, reducer), unitsDispatched: dispatched };
274
269
  }
275
270
  const dispatcher = ctx.dispatcher ?? defaultUnitDispatcher;
@@ -466,7 +461,7 @@ async function runUnit(input) {
466
461
  ...(sensitiveValues.length > 0 ? { sensitiveValues } : {}),
467
462
  ...(input.signal ? { signal: input.signal } : {}),
468
463
  };
469
- // Bounded retry (IR v2 failure policy): attempt 0 journals under the base
464
+ // Bounded retry: attempt 0 journals under the base
470
465
  // journal id (`<unitId>`, or `<unitId>~l<loop>` in a gate loop — computed by
471
466
  // the shared work-list), retry attempt N under `<baseId>~r<N>`. Every attempt
472
467
  // keeps its own row. Retries only fire when the failure reason is in
@@ -489,8 +484,8 @@ async function runUnit(input) {
489
484
  const reuse = classifyUnitReuse(workUnit, input.existingUnits, gateLoop);
490
485
  if (reuse.kind === "reuse") {
491
486
  // Identity in the durable step evidence is the CONTENT-derived base id, not
492
- // the `~r<n>` attempt row it was reused from — the report surface reduces
493
- // from the base id too, so both surfaces' evidence.units[].unitId agree.
487
+ // the `~r<n>` attempt row it was reused from — the work list only ever knows
488
+ // base ids, so evidence.units[].unitId stays stable across retries+resumes.
494
489
  return reuseCompletedUnit(unitId, reuse.row, workUnit.schema !== undefined);
495
490
  }
496
491
  if (reuse.kind === "diverge") {
@@ -504,6 +499,14 @@ async function runUnit(input) {
504
499
  }
505
500
  let outcome;
506
501
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
502
+ if (input.signal?.aborted) {
503
+ return (outcome ?? {
504
+ unitId,
505
+ ok: false,
506
+ failureReason: "aborted",
507
+ error: "unit was not dispatched because the workflow invocation was interrupted",
508
+ });
509
+ }
507
510
  const attemptId = attemptIdFor(attempt);
508
511
  // Lifetime cap + declared budget ceilings, consumed per ACTUAL dispatch
509
512
  // (reuses above returned before reaching here). Refusal fails this unit
@@ -542,6 +545,8 @@ async function runUnit(input) {
542
545
  input.budget.addTokens(outcome.tokens);
543
546
  if (outcome.ok)
544
547
  return outcome;
548
+ if (input.signal?.aborted)
549
+ return outcome;
545
550
  const reason = outcome.failureReason;
546
551
  if (!retry || reason === undefined || !retry.on.includes(reason))
547
552
  return outcome;
@@ -596,8 +601,14 @@ async function dispatchJournaledAttempt(input) {
596
601
  metadata: { runId: ctx.runId, stepId: plan.stepId, unitId: attemptId },
597
602
  });
598
603
  const outcome = redactUnitOutcome(await dispatchUnit(request, dispatcher), request.sensitiveValues ?? []);
599
- await enqueueUnitWrite(async () => {
600
- await withWorkflowRunsRepo((repo) => repo.finishUnit({
604
+ const finishedAt = new Date().toISOString();
605
+ await enqueueUnitWrite(() => withWorkflowRunsRepo((repo) => repo.immediateTransaction((db) => {
606
+ const run = repo.getRunById(ctx.runId);
607
+ if (run?.status !== "active")
608
+ return;
609
+ if (ctx.leaseHolder !== undefined && run.engine_lease_holder !== ctx.leaseHolder)
610
+ return;
611
+ repo.finishUnit({
601
612
  runId: ctx.runId,
602
613
  unitId: attemptId,
603
614
  status: outcome.ok ? "completed" : "failed",
@@ -611,21 +622,22 @@ async function dispatchJournaledAttempt(input) {
611
622
  // Harness-native session id (P2): journaled so resume can replay the
612
623
  // harness's own context cache (e.g. `codex exec resume <id>`).
613
624
  sessionId: outcome.sessionId ?? null,
614
- finishedAt: new Date().toISOString(),
615
- }));
616
- });
617
- appendEvent({
618
- eventType: "workflow_unit_finished",
619
- ref: ctx.workflowRef,
620
- metadata: {
621
- runId: ctx.runId,
622
- stepId: plan.stepId,
623
- unitId: attemptId,
624
- status: outcome.ok ? "completed" : "failed",
625
- ...(outcome.failureReason ? { failureReason: outcome.failureReason } : {}),
626
- ...(outcome.tokens !== undefined ? { tokens: outcome.tokens } : {}),
627
- },
628
- });
625
+ finishedAt,
626
+ });
627
+ insertEventStrict(db, {
628
+ eventType: "workflow_unit_finished",
629
+ ts: finishedAt,
630
+ ref: ctx.workflowRef,
631
+ metadata: {
632
+ runId: ctx.runId,
633
+ stepId: plan.stepId,
634
+ unitId: attemptId,
635
+ status: outcome.ok ? "completed" : "failed",
636
+ ...(outcome.failureReason ? { failureReason: outcome.failureReason } : {}),
637
+ ...(outcome.tokens !== undefined ? { tokens: outcome.tokens } : {}),
638
+ },
639
+ });
640
+ })));
629
641
  // Worktree lifecycle epilogue: a CLEAN worktree (`git status --porcelain`
630
642
  // empty) is removed; a DIRTY one is retained and logged — the unit left
631
643
  // uncollected work, and its journaled worktree_path says where. Cleanup is
@@ -701,12 +713,12 @@ async function dispatchUnit(request, dispatcher) {
701
713
  }
702
714
  const text = await dispatchOnce();
703
715
  // Normalize an EMPTY successful output to "no text". `finishUnit` journals
704
- // result_json = NULL for a falsy text, so durable-reuse and the R3 report
705
- // surface both rehydrate NO text from the row (unitOutcomeFromRow). Preserving
706
- // `text: ""` only in this live outcome would make the LIVE step artifact ("")
707
- // diverge from the resume/report artifact (null) — the exact byte-identical-
708
- // graph violation the cardinal rule forbids. Treating empty as absent keeps
709
- // the live engine, engine resume, and report surfaces identical.
716
+ // result_json = NULL for a falsy text, so durable-reuse rehydrates NO text
717
+ // from the row (unitOutcomeFromRow). Preserving `text: ""` only in this live
718
+ // outcome would make the LIVE step artifact ("") diverge from the artifact a
719
+ // resume rebuilds from the row (null) — the exact byte-identical-graph
720
+ // violation the cardinal rule forbids. Treating empty as absent keeps live
721
+ // dispatch and engine resume identical.
710
722
  return { unitId: request.unitId, ok: true, ...(text ? { text } : {}), ...captured() };
711
723
  }
712
724
  catch (err) {
@@ -778,6 +790,7 @@ async function resolveEnvBindings(refs) {
778
790
  export function buildAgentDispatchRequest(request, prompt) {
779
791
  return {
780
792
  prompt,
793
+ ...(request.systemPrompt ? { systemPrompt: request.systemPrompt } : {}),
781
794
  ...(request.invocation.model ? { model: request.invocation.model } : {}),
782
795
  ...(request.invocation.model ? { modelIsExact: true } : {}),
783
796
  ...(request.schema ? { schema: request.schema } : {}),
@@ -817,7 +830,13 @@ export const defaultUnitDispatcher = async (request, feedback) => {
817
830
  const { chatCompletion, LlmCallError } = await import("../../llm/client.js");
818
831
  const connection = resolved.connection;
819
832
  try {
820
- const text = await chatCompletion(connection, [{ role: "user", content: prompt }], {
833
+ const messages = request.systemPrompt
834
+ ? [
835
+ { role: "system", content: request.systemPrompt },
836
+ { role: "user", content: prompt },
837
+ ]
838
+ : [{ role: "user", content: prompt }];
839
+ const text = await chatCompletion(connection, messages, {
821
840
  timeoutMs: request.timeoutMs,
822
841
  ...(request.signal ? { signal: request.signal } : {}),
823
842
  // Native structured output where the connection supports it; the
@@ -923,9 +942,8 @@ function redactUnitOutcome(outcome, sensitiveValues) {
923
942
  * vocabulary `retry.on` accepts and the journal's `failure_reason` column
924
943
  * carries. Exhaustive over the code union (typecheck fails on drift):
925
944
  *
926
- * - `timeout` → `timeout` (wall-clock expiry; also covers
927
- * signal aborts, which chatCompletion
928
- * folds into its timeout code)
945
+ * - `timeout` → `timeout` (wall-clock expiry)
946
+ * - `aborted` → `aborted` (caller/budget cancellation)
929
947
  * - `rate_limited` → `llm_rate_limit` (HTTP 429 — the canonical transient)
930
948
  * - `parse_error` / `provider_html_error`
931
949
  * → `parse_error` (a response arrived but was not the
@@ -938,6 +956,8 @@ function redactUnitOutcome(outcome, sensitiveValues) {
938
956
  */
939
957
  export function llmFailureReasonFor(code) {
940
958
  switch (code) {
959
+ case "aborted":
960
+ return "aborted";
941
961
  case "timeout":
942
962
  return "timeout";
943
963
  case "rate_limited":
@@ -1014,8 +1034,8 @@ function materializeFrozenLlm(snapshot, invocation) {
1014
1034
  * Rehydrate a journaled completed unit row into a UnitOutcome (durable-row
1015
1035
  * reuse). Delegates to the shared {@link unitOutcomeFromRow} — the reuse path
1016
1036
  * only reaches here for completed rows (the caller guards `status ===
1017
- * "completed"`), so the mapping is identical to what the R3 report path applies
1018
- * when it replays the same journal.
1037
+ * "completed"`), so a reused unit contributes exactly what its original
1038
+ * dispatch did.
1019
1039
  */
1020
1040
  function reuseCompletedUnit(unitId, row, hasSchema) {
1021
1041
  return unitOutcomeFromRow(unitId, row, hasSchema);