akm-cli 0.9.16 → 0.9.17-alpha.10

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 (403) hide show
  1. package/CHANGELOG.md +2101 -0
  2. package/STABILITY.md +11 -10
  3. package/dist/akm +124 -193
  4. package/dist/akm-migrate +38 -19
  5. package/dist/assets/hints/cli-hints-full.md +6 -7
  6. package/dist/assets/improve-strategies/catchup.json +0 -3
  7. package/dist/assets/improve-strategies/consolidate.json +0 -1
  8. package/dist/assets/improve-strategies/default.json +1 -2
  9. package/dist/assets/improve-strategies/proactive-maintenance.json +1 -2
  10. package/dist/assets/improve-strategies/quick.json +1 -2
  11. package/dist/assets/improve-strategies/reflect-distill.json +1 -2
  12. package/dist/assets/improve-strategies/thorough.json +0 -3
  13. package/dist/assets/prompts/consolidate-pair.md +20 -0
  14. package/dist/assets/prompts/consolidate-system.md +4 -11
  15. package/dist/assets/prompts/retrieval-relevance-judge.md +6 -0
  16. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +20 -20
  17. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -2
  18. package/dist/assets/templates/html/health.html +3 -5
  19. package/dist/cli/retired-commands.js +1 -1
  20. package/dist/cli/shared.js +6 -2
  21. package/dist/cli/unknown-flags.js +24 -1
  22. package/dist/cli.js +68 -10
  23. package/dist/commands/agent/agent-dispatch.js +1 -1
  24. package/dist/commands/command/command-execution.js +24 -62
  25. package/dist/commands/feedback-cli.js +0 -1
  26. package/dist/commands/health/accept-rate.js +6 -0
  27. package/dist/commands/health/archive-usage.js +92 -0
  28. package/dist/commands/health/checks.js +83 -74
  29. package/dist/commands/health/config-skew.js +38 -0
  30. package/dist/commands/health/data-dir-usage.js +25 -13
  31. package/dist/commands/health/egress.js +54 -0
  32. package/dist/commands/health/html-report.js +1 -42
  33. package/dist/commands/health/improve-metrics.js +136 -591
  34. package/dist/commands/health/md-report.js +1 -6
  35. package/dist/commands/health/plugin-staleness.js +53 -3
  36. package/dist/commands/health/renderers.js +12 -4
  37. package/dist/commands/health/report-view-model.js +14 -120
  38. package/dist/commands/health/types-improve.js +4 -19
  39. package/dist/commands/health/windows.js +64 -74
  40. package/dist/commands/health.js +145 -143
  41. package/dist/commands/improve/consolidate/chunking.js +26 -117
  42. package/dist/commands/improve/consolidate/continuity-check.js +137 -0
  43. package/dist/commands/improve/consolidate/pair-pass.js +791 -0
  44. package/dist/commands/improve/consolidate/sanitize.js +54 -149
  45. package/dist/commands/improve/consolidate.js +589 -1127
  46. package/dist/commands/improve/content-hash.js +16 -24
  47. package/dist/commands/improve/distill/content-repair.js +18 -100
  48. package/dist/commands/improve/distill-guards.js +20 -81
  49. package/dist/commands/improve/distill-promotion-policy.js +23 -243
  50. package/dist/commands/improve/distill.js +608 -1041
  51. package/dist/commands/improve/eligibility.js +126 -390
  52. package/dist/commands/improve/execution.js +8 -10
  53. package/dist/commands/improve/extract-prompt.js +1 -2
  54. package/dist/commands/improve/extract.js +487 -1046
  55. package/dist/commands/improve/feedback-valence.js +0 -25
  56. package/dist/commands/improve/improve-cli.js +75 -169
  57. package/dist/commands/improve/improve-result-file.js +10 -66
  58. package/dist/commands/improve/improve-strategies.js +52 -4
  59. package/dist/commands/improve/improve-usage-report.js +18 -64
  60. package/dist/commands/improve/improve.js +480 -1074
  61. package/dist/commands/improve/ledger.js +119 -0
  62. package/dist/commands/improve/locks.js +2 -8
  63. package/dist/commands/improve/loop-stages.js +415 -1073
  64. package/dist/commands/improve/memory/derived-ref.js +12 -77
  65. package/dist/commands/improve/memory/memory-belief.js +16 -118
  66. package/dist/commands/improve/memory/memory-improve.js +266 -14
  67. package/dist/commands/improve/outcome-loop.js +28 -156
  68. package/dist/commands/improve/planner.js +5 -15
  69. package/dist/commands/improve/preparation.js +779 -2319
  70. package/dist/commands/improve/proactive-maintenance.js +34 -101
  71. package/dist/commands/improve/reflect-noise.js +104 -280
  72. package/dist/commands/improve/reflect.js +642 -1353
  73. package/dist/commands/improve/retrieval-gate.js +127 -0
  74. package/dist/commands/improve/retrieval-scope.js +92 -0
  75. package/dist/commands/improve/salience.js +41 -240
  76. package/dist/commands/improve/session-asset.js +19 -100
  77. package/dist/commands/improve/stage.js +322 -0
  78. package/dist/commands/lint/base-linter.js +37 -15
  79. package/dist/commands/proposal/drain.js +261 -578
  80. package/dist/commands/proposal/proposal-cli.js +19 -20
  81. package/dist/commands/proposal/proposal-types.js +31 -24
  82. package/dist/commands/proposal/proposal.js +38 -8
  83. package/dist/commands/proposal/propose.js +134 -160
  84. package/dist/commands/proposal/repository.js +1097 -1394
  85. package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
  86. package/dist/commands/proposal/validators/proposal-validators.js +1 -1
  87. package/dist/commands/proposal/validators/proposals.js +22 -89
  88. package/dist/commands/read/curate.js +105 -462
  89. package/dist/commands/read/knowledge.js +3 -2
  90. package/dist/commands/read/search-cli.js +16 -33
  91. package/dist/commands/read/search.js +17 -23
  92. package/dist/commands/read/show.js +57 -108
  93. package/dist/commands/sources/bundle-cli.js +25 -2
  94. package/dist/commands/sources/bundle-config-ops.js +4 -0
  95. package/dist/commands/sources/dangerous-env-audit.js +1 -2
  96. package/dist/commands/sources/info.js +127 -29
  97. package/dist/commands/sources/installed-stashes.js +197 -746
  98. package/dist/commands/sources/schema-repair.js +98 -129
  99. package/dist/commands/sources/source-add.js +62 -12
  100. package/dist/commands/sources/source-manage.js +9 -2
  101. package/dist/commands/sources/stash-cli.js +24 -4
  102. package/dist/commands/tasks/explain.js +10 -13
  103. package/dist/commands/tasks/tasks-cli.js +12 -13
  104. package/dist/commands/tasks/tasks.js +350 -936
  105. package/dist/commands/tasks/validate.js +26 -24
  106. package/dist/commands/workflow/plan.js +22 -29
  107. package/dist/commands/workflow-cli.js +4 -4
  108. package/dist/core/adapter/adapters/akm-adapter.js +2 -1
  109. package/dist/core/adapter/adapters/akm-lint.js +2 -3
  110. package/dist/core/adapter/adapters/akm-metadata.js +42 -12
  111. package/dist/core/adapter/adapters/akm-task-adapter.js +29 -8
  112. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  113. package/dist/core/adapter/execution-source.js +17 -29
  114. package/dist/core/asset/asset-placement.js +4 -13
  115. package/dist/core/asset/frontmatter.js +106 -1
  116. package/dist/core/asset/resolve-ref.js +1 -1
  117. package/dist/core/bundle-id.js +42 -5
  118. package/dist/core/bundle-rename.js +285 -0
  119. package/dist/core/config/config-io.js +1 -2
  120. package/dist/core/config/config-schema.js +9 -34
  121. package/dist/core/config/config-walker.js +1 -1
  122. package/dist/core/config/config.js +184 -111
  123. package/dist/core/config/engine-semantics.js +0 -2
  124. package/dist/core/config/legacy-source-shape-shim.js +38 -9
  125. package/dist/core/config/schema/embedding.js +20 -5
  126. package/dist/core/config/schema/engines.js +5 -0
  127. package/dist/core/config/schema/execution.js +1 -1
  128. package/dist/core/config/schema/experimental.js +1 -1
  129. package/dist/core/config/schema/improve-processes.js +54 -125
  130. package/dist/core/config/schema/improve.js +4 -42
  131. package/dist/core/config/schema/index-config.js +9 -48
  132. package/dist/core/config/schema/scheduler.js +12 -12
  133. package/dist/core/config/schema/search.js +6 -22
  134. package/dist/core/env-secret-ref.js +0 -1
  135. package/dist/core/errors.js +8 -9
  136. package/dist/core/file-change.js +13 -5
  137. package/dist/core/file-lock.js +76 -173
  138. package/dist/core/improve-result.js +35 -7
  139. package/dist/core/improve-types.js +0 -1
  140. package/dist/core/logs-db.js +2 -2
  141. package/dist/core/loopback.js +7 -12
  142. package/dist/core/non-task-input.js +20 -0
  143. package/dist/core/parse.js +13 -16
  144. package/dist/core/paths.js +0 -24
  145. package/dist/core/redaction.js +109 -2
  146. package/dist/core/run-lock.js +2 -5
  147. package/dist/core/spawn-env.js +1 -1
  148. package/dist/core/state/migrations.js +123 -61
  149. package/dist/core/state-db-scope.js +2 -4
  150. package/dist/core/state-db.js +126 -692
  151. package/dist/core/time.js +0 -20
  152. package/dist/core/type-presentation.js +1 -9
  153. package/dist/core/write-source.js +294 -1005
  154. package/dist/execution/input-contract.js +1 -1
  155. package/dist/execution/resolved-request.js +135 -689
  156. package/dist/execution/source.js +63 -257
  157. package/dist/execution/target-ref.js +1 -1
  158. package/dist/indexer/bundle-identity-guard.js +2 -2
  159. package/dist/indexer/db/llm-cache.js +2 -2
  160. package/dist/indexer/ensure-index.js +77 -73
  161. package/dist/indexer/index-rebuild-lock.js +3 -11
  162. package/dist/indexer/index-writer-lock.js +8 -17
  163. package/dist/indexer/index-written-assets.js +141 -154
  164. package/dist/indexer/indexer.js +400 -1124
  165. package/dist/indexer/links/declared-links.js +90 -0
  166. package/dist/indexer/materialize-embeddings.js +60 -397
  167. package/dist/indexer/passes/memory-inference.js +96 -90
  168. package/dist/indexer/passes/metadata.js +132 -219
  169. package/dist/indexer/read-preflight.js +0 -7
  170. package/dist/indexer/scan/doc-to-entry.js +2 -3
  171. package/dist/indexer/scan/drain-dir.js +1 -1
  172. package/dist/indexer/search/db-search.js +190 -590
  173. package/dist/indexer/search/fts-query.js +30 -41
  174. package/dist/indexer/search/ranking.js +28 -154
  175. package/dist/indexer/search/search-attribution.js +12 -32
  176. package/dist/indexer/search/search-fields.js +11 -15
  177. package/dist/indexer/search/search-hit-enrichers.js +54 -85
  178. package/dist/indexer/search/search-source.js +1 -4
  179. package/dist/indexer/usage/usage-events.js +36 -7
  180. package/dist/indexer/walk/walker.js +3 -4
  181. package/dist/integrations/agent/engine-fallback.js +23 -40
  182. package/dist/integrations/agent/engine-resolution.js +93 -183
  183. package/dist/integrations/agent/execution.js +507 -0
  184. package/dist/integrations/agent/model-map.js +28 -156
  185. package/dist/integrations/agent/request-lowering.js +66 -141
  186. package/dist/integrations/agent/runner-dispatch.js +143 -321
  187. package/dist/integrations/agent/runner.js +54 -14
  188. package/dist/integrations/lockfile.js +53 -101
  189. package/dist/llm/client.js +18 -6
  190. package/dist/llm/embedders/deterministic.js +2 -3
  191. package/dist/llm/embedders/profile.js +71 -0
  192. package/dist/llm/embedders/remote.js +11 -17
  193. package/dist/llm/feature-gate.js +0 -8
  194. package/dist/llm/index-passes.js +3 -5
  195. package/dist/llm/memory-infer.js +1 -2
  196. package/dist/llm/structured-call.js +5 -24
  197. package/dist/output/generic-render.js +23 -11
  198. package/dist/output/html-render.js +13 -10
  199. package/dist/output/render-registry.js +3 -32
  200. package/dist/output/shapes/helpers.js +25 -38
  201. package/dist/output/shapes/passthrough.js +1 -9
  202. package/dist/{indexer/graph/graph-types.js → output/text/bundle-rename.js} +4 -1
  203. package/dist/output/text/command-format.js +69 -31
  204. package/dist/output/text/helpers.js +1 -1
  205. package/dist/output/text/migrate.js +5 -14
  206. package/dist/output/text/proposal-format.js +48 -3
  207. package/dist/output/text/show-format.js +13 -17
  208. package/dist/output/text/workflow-format.js +0 -32
  209. package/dist/output/text.js +2 -0
  210. package/dist/registry/factory.js +4 -19
  211. package/dist/registry/network.js +66 -220
  212. package/dist/registry/providers/index.js +0 -2
  213. package/dist/registry/providers/skills-sh.js +3 -14
  214. package/dist/registry/providers/static-index.js +24 -26
  215. package/dist/registry/resolve.js +55 -131
  216. package/dist/scripts/akm-migrate-node.js +42948 -92369
  217. package/dist/scripts/akm-migrate.js +42935 -92354
  218. package/dist/setup/registry-stash-loader.js +4 -13
  219. package/dist/setup/semantic-assets.js +3 -44
  220. package/dist/setup/setup.js +1 -1
  221. package/dist/setup/steps/connection.js +5 -6
  222. package/dist/setup/steps/platforms.js +2 -2
  223. package/dist/setup/steps/tasks.js +25 -15
  224. package/dist/sources/provider-factory.js +17 -18
  225. package/dist/sources/providers/filesystem.js +2 -3
  226. package/dist/sources/providers/git-install.js +7 -1
  227. package/dist/sources/providers/git-provider.js +0 -3
  228. package/dist/sources/providers/git-stash.js +83 -21
  229. package/dist/sources/providers/npm.js +2 -4
  230. package/dist/sources/providers/provider-utils.js +5 -10
  231. package/dist/sources/providers/website.js +0 -2
  232. package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
  233. package/dist/sources/website-url.js +2 -2
  234. package/dist/storage/database.js +9 -35
  235. package/dist/storage/repositories/improve-ledger-repository.js +209 -0
  236. package/dist/storage/repositories/index-connection.js +39 -72
  237. package/dist/storage/repositories/index-entries-repository.js +131 -129
  238. package/dist/storage/repositories/index-entry-mapper.js +1 -2
  239. package/dist/storage/repositories/index-entry-schema.js +101 -268
  240. package/dist/storage/repositories/index-fts-repository.js +86 -256
  241. package/dist/storage/repositories/index-links-repository.js +143 -0
  242. package/dist/storage/repositories/index-llm-cache-repository.js +7 -9
  243. package/dist/storage/repositories/index-meta-repository.js +6 -4
  244. package/dist/storage/repositories/index-schema.js +257 -325
  245. package/dist/storage/repositories/index-utility-repository.js +8 -29
  246. package/dist/storage/repositories/index-vec-repository.js +133 -414
  247. package/dist/storage/repositories/outcome-repository.js +2 -1
  248. package/dist/storage/repositories/proposals-repository.js +104 -1
  249. package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
  250. package/dist/storage/repositories/salience-repository.js +1 -19
  251. package/dist/storage/repositories/task-history-repository.js +26 -4
  252. package/dist/storage/repositories/workflow-runs-repository.js +53 -244
  253. package/dist/storage/sqlite-migrations.js +136 -0
  254. package/dist/storage/sqlite-pragmas.js +11 -9
  255. package/dist/storage/sqlite-transaction.js +170 -0
  256. package/dist/storage/state-db-integrity.js +130 -0
  257. package/dist/tasks/activation-config.js +134 -62
  258. package/dist/tasks/backends/cron.js +191 -302
  259. package/dist/tasks/backends/exec-utils.js +2 -5
  260. package/dist/tasks/backends/launchd.js +141 -748
  261. package/dist/tasks/backends/schtasks.js +119 -623
  262. package/dist/tasks/prepare/prepare-support.js +5 -15
  263. package/dist/tasks/prepare/prepare.js +0 -2
  264. package/dist/tasks/resolve-akm-bin.js +20 -79
  265. package/dist/tasks/run/attempt-lifecycle.js +0 -1
  266. package/dist/tasks/run/load-task.js +1 -1
  267. package/dist/tasks/scheduler-binding.js +20 -238
  268. package/dist/tasks/scheduler-invocation.js +136 -244
  269. package/dist/tasks/scheduler-lock.js +53 -0
  270. package/dist/tasks/scheduler-sync.js +368 -679
  271. package/dist/tasks/source/parse-task-source.js +55 -9
  272. package/dist/tasks/source/task-source-v3-frozen.js +3 -4
  273. package/dist/tasks/source/task-to-v4.js +464 -88
  274. package/dist/workflows/authoring/authoring.js +3 -12
  275. package/dist/workflows/compile.js +211 -0
  276. package/dist/workflows/concurrency-policy.js +13 -74
  277. package/dist/workflows/exec/child-invocation.js +3 -17
  278. package/dist/workflows/exec/child-workflow.js +32 -141
  279. package/dist/workflows/exec/dispatch-redaction.js +13 -53
  280. package/dist/workflows/exec/environment.js +98 -0
  281. package/dist/workflows/exec/exec-unit.js +33 -140
  282. package/dist/workflows/exec/frozen-judge.js +7 -59
  283. package/dist/workflows/exec/native-executor.js +82 -341
  284. package/dist/workflows/exec/param-secrets.js +29 -47
  285. package/dist/workflows/exec/run-workflow.js +154 -387
  286. package/dist/workflows/exec/scheduler.js +9 -36
  287. package/dist/workflows/exec/step-work.js +127 -430
  288. package/dist/workflows/exec/unit-dispatch.js +11 -63
  289. package/dist/workflows/exec/unit-writer.js +8 -52
  290. package/dist/workflows/exec/worktree.js +39 -273
  291. package/dist/workflows/freeze/child-output-references.js +4 -15
  292. package/dist/workflows/freeze/environment.js +99 -92
  293. package/dist/workflows/freeze/freeze.js +172 -0
  294. package/dist/workflows/freeze/step-values.js +19 -21
  295. package/dist/workflows/freeze/targets/child-workflow.js +23 -92
  296. package/dist/workflows/freeze/targets/command.js +10 -33
  297. package/dist/workflows/freeze/targets/script.js +5 -12
  298. package/dist/workflows/freeze/targets/shell.js +3 -6
  299. package/dist/workflows/freeze/targets/task.js +25 -80
  300. package/dist/workflows/freeze/task-bindings.js +20 -67
  301. package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
  302. package/dist/workflows/ir/params.js +6 -51
  303. package/dist/workflows/ir/plan-hash.js +2 -34
  304. package/dist/workflows/parser.js +140 -43
  305. package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
  306. package/dist/workflows/renderer.js +36 -69
  307. package/dist/workflows/resource-limits.js +12 -120
  308. package/dist/workflows/runtime/agent-identity.js +8 -40
  309. package/dist/workflows/runtime/run-outputs.js +3 -6
  310. package/dist/workflows/runtime/run-plan.js +316 -0
  311. package/dist/workflows/runtime/runs.js +48 -200
  312. package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
  313. package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
  314. package/dist/workflows/validate-summary.js +2 -7
  315. package/docs/integration/bundling-akm.md +49 -42
  316. package/docs/migration/README.md +1 -0
  317. package/docs/migration/release-notes/0.9.17.md +43 -0
  318. package/docs/migration/v0.9.1-to-v0.9.2.md +23 -7
  319. package/docs/reference/cli.md +232 -135
  320. package/docs/reference/configuration.md +71 -57
  321. package/docs/reference/data-and-telemetry.md +20 -21
  322. package/docs/reference/tasks.md +105 -39
  323. package/docs/reference/workflow-schema.md +14 -18
  324. package/docs/reference/workflows.md +6 -9
  325. package/package.json +1 -1
  326. package/schemas/akm-config.json +115 -738
  327. package/schemas/akm-workflow.json +1 -0
  328. package/dist/assets/improve-strategies/graph-refresh.json +0 -15
  329. package/dist/assets/prompts/contradiction-judge.md +0 -33
  330. package/dist/assets/prompts/graph-extract-system.md +0 -1
  331. package/dist/assets/prompts/graph-extract-user-prompt.md +0 -35
  332. package/dist/assets/prompts/metadata-enhance-system.md +0 -1
  333. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +0 -4
  334. package/dist/commands/health/advisories.js +0 -150
  335. package/dist/commands/health/metrics.js +0 -329
  336. package/dist/commands/health/surfaces.js +0 -102
  337. package/dist/commands/improve/anti-collapse.js +0 -83
  338. package/dist/commands/improve/collapse-detector.js +0 -432
  339. package/dist/commands/improve/consolidate/eligibility.js +0 -48
  340. package/dist/commands/improve/consolidate/merge.js +0 -149
  341. package/dist/commands/improve/distill/promote-memory.js +0 -291
  342. package/dist/commands/improve/distill/quality-gate.js +0 -337
  343. package/dist/commands/improve/eval-cases.js +0 -52
  344. package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
  345. package/dist/commands/improve/proposal-envelope.js +0 -31
  346. package/dist/commands/improve/run-context.js +0 -123
  347. package/dist/commands/improve/shared.js +0 -31
  348. package/dist/commands/improve/source-identity.js +0 -28
  349. package/dist/commands/improve/triage.js +0 -96
  350. package/dist/commands/proposal/drain-policies.js +0 -151
  351. package/dist/commands/sources/update-transaction.js +0 -220
  352. package/dist/core/action-contributors.js +0 -28
  353. package/dist/core/config/config-version-shim.js +0 -101
  354. package/dist/core/fs-txn.js +0 -405
  355. package/dist/core/lexical-score.js +0 -25
  356. package/dist/core/maintenance-barrier.js +0 -167
  357. package/dist/execution/executable-identity.js +0 -105
  358. package/dist/execution/guarded-source.js +0 -427
  359. package/dist/indexer/db/graph-db.js +0 -444
  360. package/dist/indexer/graph/graph-boost.js +0 -427
  361. package/dist/indexer/graph/graph-dedup.js +0 -95
  362. package/dist/indexer/graph/graph-extraction.js +0 -1108
  363. package/dist/indexer/search/name-match.js +0 -35
  364. package/dist/indexer/search/ranking-contributors.js +0 -515
  365. package/dist/indexer/search/ranking-types.js +0 -4
  366. package/dist/indexer/walk/project-context.js +0 -192
  367. package/dist/integrations/agent/execution-cascade.js +0 -566
  368. package/dist/integrations/agent/execution-definitions.js +0 -202
  369. package/dist/integrations/agent/execution-lowering.js +0 -841
  370. package/dist/integrations/agent/execution-preparation.js +0 -98
  371. package/dist/integrations/agent/inline-execution.js +0 -74
  372. package/dist/llm/graph-extract.js +0 -728
  373. package/dist/llm/metadata-enhance.js +0 -96
  374. package/dist/registry/create-provider-registry.js +0 -29
  375. package/dist/registry/pinned-request-helper.js +0 -247
  376. package/dist/registry/pinned-transport.js +0 -717
  377. package/dist/sources/providers/index.js +0 -14
  378. package/dist/storage/engines/sqlite-migrations.js +0 -271
  379. package/dist/storage/repositories/canaries-repository.js +0 -107
  380. package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
  381. package/dist/storage/repositories/registry-cache.js +0 -113
  382. package/dist/tasks/scheduler-sync-preview.js +0 -52
  383. package/dist/tasks/source/task-to-v3.js +0 -507
  384. package/dist/workflows/freeze/resolve-steps.js +0 -86
  385. package/dist/workflows/freeze/source-freeze.js +0 -64
  386. package/dist/workflows/ir/compile.js +0 -321
  387. package/dist/workflows/ir/environment-v4.js +0 -330
  388. package/dist/workflows/ir/freeze-v4.js +0 -153
  389. package/dist/workflows/ir/schema-v4.js +0 -745
  390. package/dist/workflows/ir/schema.js +0 -354
  391. package/dist/workflows/program/schema.js +0 -77
  392. package/dist/workflows/runtime/checkin.js +0 -57
  393. package/dist/workflows/runtime/plan-classifier.js +0 -196
  394. package/dist/workflows/runtime/unit-checkin.js +0 -45
  395. package/dist/workflows/runtime/unit-phases.js +0 -20
  396. package/dist/workflows/schema.js +0 -4
  397. package/dist/workflows/source-ir/compile.js +0 -200
  398. package/dist/workflows/source-ir/program.js +0 -50
  399. package/dist/workflows/source-ir/result.js +0 -26
  400. package/dist/workflows/source-ir/schema.js +0 -786
  401. package/dist/workflows/source-ir/triggers.js +0 -79
  402. package/dist/workflows/source-ir/uses.js +0 -40
  403. package/dist/workflows/validator.js +0 -60
@@ -1,745 +0,0 @@
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
- * Additive durable workflow plan v4.
6
- *
7
- * This is the sole executable workflow plan. Older stored plans are rejected
8
- * at the run-storage boundary instead of being replayed through a second
9
- * runtime architecture.
10
- */
11
- import { createHash } from "node:crypto";
12
- import path from "node:path";
13
- import { compareCodePoints } from "../../core/common.js";
14
- import { UsageError } from "../../core/errors.js";
15
- import { decodeFrozenExecutableIdentity } from "../../execution/executable-identity.js";
16
- import { INPUT_NAME_PATTERN } from "../../execution/input-contract.js";
17
- import { canonicalResolvedExecutionRequest, decodeResolvedExecutionRequest, } from "../../execution/resolved-request.js";
18
- import { decodeExecutionSourceIdentity } from "../../execution/source.js";
19
- import { decodeFrozenRunnerSpec } from "../../integrations/agent/execution-lowering.js";
20
- import { parseReference } from "../program/expressions.js";
21
- import { PROGRAM_PARAM_NAME_PATTERN } from "../program/schema.js";
22
- import { utf8Bytes } from "../resource-limits.js";
23
- import { decodeWorkflowExecSpec, validateWorkflowPlanStructure, } from "./schema.js";
24
- export const WORKFLOW_IR_V5_VERSION = 5;
25
- /**
26
- * Strict v4 corruption gate. No authored source or config is consulted here.
27
- *
28
- * `depth` is the composition-depth recursion counter (0 at the root
29
- * workflow plan; +1 for every embedded child-workflow plan), and `budget` is
30
- * a MUTABLE, tree-wide running total of embedded child-plan bytes charged so
31
- * far. Both are internal to the recursive child-workflow decode path below
32
- * (`decodeChildWorkflowTarget` calls this function again on an embedded
33
- * `frozenPlan`, sharing the SAME `budget` object across the whole recursion —
34
- * mirroring `ChildCompositionContext.budget` on the freeze side) — every
35
- * external caller decodes a plan at the top level and relies on the
36
- * defaults. Re-enforces the composition depth bound AND the aggregate
37
- * embedded-bytes bound at decode as corruption gates (spec docs/plans/specs/
38
- * p3a-plan-v5-child-freeze.md §3.6 step 6, rows A-23, A-N6); the actionable
39
- * freeze-time `COMPOSITION_INVALID` gates live in
40
- * `src/workflows/freeze/targets/child-workflow.ts` (Lane B).
41
- */
42
- export function decodeWorkflowPlanV4(input, depth = 0, budget = { embeddedBytes: 0 }) {
43
- const raw = record(input, "plan");
44
- if (raw.irVersion !== WORKFLOW_IR_V5_VERSION)
45
- fail("irVersion must be 5");
46
- assertKeys(raw, ["irVersion", "title", "params", "paramSchemas", "budget", "execution", "steps", "sourceReadSet", "outputs"], "plan");
47
- if (!Object.hasOwn(raw, "sourceReadSet"))
48
- fail("sourceReadSet is required");
49
- const sourceReadSet = decodeSourceReadSet(raw.sourceReadSet);
50
- validateWorkflowPlanStructure(raw, {
51
- expectedVersion: WORKFLOW_IR_V5_VERSION,
52
- planExtraKeys: ["sourceReadSet", "outputs"],
53
- unitExtraKeys: ["frozenTarget", "environment"],
54
- gateExtraKeys: ["frozenJudge"],
55
- });
56
- const rawSteps = raw.steps;
57
- const stepIds = new Set(rawSteps.map((rawStep) => rawStep.stepId));
58
- const requiredSources = [];
59
- const steps = rawSteps.map((rawStep, index) => {
60
- const step = rawStep;
61
- const sourceStep = record(rawSteps[index], `step ${step.stepId}`);
62
- const gate = decodeGateV4(sourceStep.gate, step.stepId, requiredSources);
63
- if (!step.root)
64
- return Object.freeze({ ...step, gate });
65
- const rawRoot = record(sourceStep.root, `step ${step.stepId} root`);
66
- const root = rawRoot.kind === "map"
67
- ? (() => {
68
- const { template: _template, ...map } = rawRoot;
69
- return {
70
- ...map,
71
- template: decodeUnitV4(record(rawRoot.template, `map ${String(rawRoot.id)} template`), requiredSources, depth, budget),
72
- };
73
- })()
74
- : decodeUnitV4(rawRoot, requiredSources, depth, budget);
75
- return Object.freeze({ ...step, root, gate });
76
- });
77
- assertRequiredSources(sourceReadSet, requiredSources);
78
- const outputs = Object.hasOwn(raw, "outputs") ? decodeWorkflowOutputs(raw.outputs, stepIds) : undefined;
79
- const { outputs: _rawOutputs, ...restRaw } = raw;
80
- return Object.freeze({
81
- ...restRaw,
82
- irVersion: WORKFLOW_IR_V5_VERSION,
83
- sourceReadSet,
84
- steps,
85
- ...(outputs ? { outputs } : {}),
86
- });
87
- }
88
- /**
89
- * Decode a plan's `outputs` (P3b, spec §4.2): a corruption gate mirroring
90
- * {@link decodeInputBindings} — non-empty, sorted-unique keys (canonical wire
91
- * order, same rule `inputBindings` enforces), each name matching the
92
- * `params:` name pattern, each entry's closed key set, `from` re-parsing as a
93
- * `stepOutput` reference whose step id is declared in `plan.steps`.
94
- */
95
- function decodeWorkflowOutputs(value, stepIds) {
96
- const raw = record(value, "plan outputs");
97
- const names = Object.keys(raw);
98
- if (names.length === 0)
99
- fail("plan outputs must be a non-empty object");
100
- let prior;
101
- const outputs = {};
102
- for (const name of names) {
103
- if (!PROGRAM_PARAM_NAME_PATTERN.test(name))
104
- fail(`plan outputs has invalid name ${name}`);
105
- if (prior !== undefined && compareCodePoints(prior, name) >= 0) {
106
- fail("plan outputs must be sorted by unique name");
107
- }
108
- prior = name;
109
- const entry = record(raw[name], `plan outputs.${name}`);
110
- assertKeys(entry, ["from", "schema"], `plan outputs.${name}`);
111
- if (typeof entry.from !== "string" || !entry.from)
112
- fail(`plan outputs.${name} from is invalid`);
113
- const parsed = parseReference(entry.from);
114
- if (!parsed.ok || parsed.expr.kind !== "stepOutput") {
115
- fail(`plan outputs.${name} from must be a steps.<id>.output reference`);
116
- }
117
- if (!stepIds.has(parsed.expr.stepId)) {
118
- fail(`plan outputs.${name} from names step ${parsed.expr.stepId}, which is not in plan.steps`);
119
- }
120
- const schema = Object.hasOwn(entry, "schema") ? record(entry.schema, `plan outputs.${name} schema`) : undefined;
121
- outputs[name] = Object.freeze({ from: entry.from, ...(schema ? { schema: Object.freeze(schema) } : {}) });
122
- }
123
- return Object.freeze(outputs);
124
- }
125
- function decodeUnitV4(raw, requiredSources, depth, budget) {
126
- const id = raw.id;
127
- if (!Object.hasOwn(raw, "frozenTarget"))
128
- fail(`unit ${id} frozenTarget is required`);
129
- if (!Object.hasOwn(raw, "environment"))
130
- fail(`unit ${id} environment is required`);
131
- const environment = decodeEnvironment(raw.environment, id);
132
- const frozenTarget = decodeFrozenTarget(raw.frozenTarget, raw, environment, requiredSources, depth, budget);
133
- const { frozenTarget: _target, environment: _environment, ...core } = raw;
134
- return Object.freeze({
135
- ...core,
136
- frozenTarget,
137
- environment: Object.freeze(environment),
138
- });
139
- }
140
- function decodeGateV4(value, stepId, requiredSources) {
141
- const gate = record(value, `gate ${stepId}`);
142
- const criteria = gate.criteria;
143
- if (criteria.length === 0) {
144
- if (gate.frozenJudge !== null)
145
- fail(`gate ${stepId} without criteria cannot have a frozen judge target`);
146
- return Object.freeze({
147
- kind: "gate",
148
- id: gate.id,
149
- stepId,
150
- criteria,
151
- maxLoops: gate.maxLoops,
152
- frozenJudge: null,
153
- });
154
- }
155
- if (!Object.hasOwn(gate, "frozenJudge")) {
156
- fail(`gate ${stepId} with criteria is missing its frozenJudge field`);
157
- }
158
- if (gate.frozenJudge === null) {
159
- return Object.freeze({
160
- kind: "gate",
161
- id: gate.id,
162
- stepId,
163
- criteria,
164
- maxLoops: gate.maxLoops,
165
- frozenJudge: null,
166
- });
167
- }
168
- const identity = {
169
- kind: "unit",
170
- id: gate.id,
171
- instructions: criteria.join("\n"),
172
- onError: "fail",
173
- isolation: "none",
174
- };
175
- return Object.freeze({
176
- kind: "gate",
177
- id: gate.id,
178
- stepId,
179
- criteria,
180
- maxLoops: gate.maxLoops,
181
- frozenJudge: decodeCommandTarget(record(gate.frozenJudge, `gate ${stepId} frozenJudge`), identity, requiredSources),
182
- });
183
- }
184
- function decodeFrozenTarget(value, unit, environment, requiredSources, depth, budget) {
185
- const target = record(value, `unit ${unit.id} frozenTarget`);
186
- if (target.kind === "command")
187
- return decodeCommandTarget(target, unit, requiredSources);
188
- if (target.kind === "shell")
189
- return decodeShellTarget(target, unit, environment);
190
- if (target.kind === "script")
191
- return decodeScriptTarget(target, unit, requiredSources);
192
- if (target.kind === "child-workflow")
193
- return decodeChildWorkflowTarget(target, unit, depth, budget);
194
- fail(`unit ${unit.id} frozenTarget has unsupported kind ${String(target.kind)}`);
195
- }
196
- /**
197
- * Decode a `kind: "child-workflow"` frozen target (§3.5): the embedded
198
- * COMPLETE child plan, re-verified against its own `planHash` and this
199
- * target's own `contentHash` on every decode (rows A-20, A-21), recursively
200
- * enforcing `irVersion` 5 (row A-22, via the recursive
201
- * {@link decodeWorkflowPlanV4} call), the composition depth bound (row A-23),
202
- * and the AGGREGATE embedded-plan-bytes bound (§3.6 step 6, A-N6) as the
203
- * decoder recurses. `WORKFLOW_MAX_EMBEDDED_CHILD_PLAN_BYTES` — unlike the
204
- * depth bound above — is imported directly from
205
- * `src/workflows/resource-limits.ts` rather than reproduced: that constant
206
- * was already Lane B's when this decode-time re-enforcement was added (no
207
- * lane-ordering constraint left to honor), and re-deriving a byte cap by
208
- * hand invites drift a depth integer does not. `budget` is charged with
209
- * THIS child's own full canonical byte length AFTER it decodes (so any
210
- * further-nested grandchildren it embeds have already charged themselves,
211
- * mirroring the freeze-side `chargeEmbeddedBudget`'s post-recursion charge
212
- * order in `src/workflows/freeze/targets/child-workflow.ts`) — a rejected
213
- * plan therefore never partially charges the budget. `frozenPlan` is
214
- * covered wholesale through `planHash`, so it is deliberately NOT
215
- * re-serialized into `contentHash` (§3.5). No `requiredSources`
216
- * contribution: unlike a command/script target's own referenced source, the
217
- * child's transitive sources live in the child's OWN `sourceReadSet`,
218
- * verified by the recursive decode call itself; the PARENT's
219
- * `sourceReadSet` absorbing the child's files (row B-05) is a freeze-time
220
- * concern (Lane B), not a decode-time structural one.
221
- */
222
- function decodeChildWorkflowTarget(target, unit, depth, budget) {
223
- assertKeys(target, ["kind", "ref", "planHash", "frozenPlan", "contentHash", "via", "taskRef", "inputBindings"], `unit ${unit.id} child workflow target`);
224
- if (typeof target.ref !== "string" || !target.ref)
225
- fail(`unit ${unit.id} child workflow ref is invalid`);
226
- const planHash = digest(target.planHash, `unit ${unit.id} child workflow planHash`);
227
- if (target.via !== "direct" && target.via !== "task")
228
- fail(`unit ${unit.id} child workflow via is invalid`);
229
- const via = target.via;
230
- if (via === "task") {
231
- if (typeof target.taskRef !== "string" || !target.taskRef) {
232
- fail(`unit ${unit.id} child workflow via "task" requires a taskRef`);
233
- }
234
- }
235
- else if (target.taskRef !== undefined) {
236
- fail(`unit ${unit.id} child workflow via "direct" cannot carry a taskRef`);
237
- }
238
- const taskRef = target.taskRef;
239
- const inputBindings = decodeInputBindings(target.inputBindings, `unit ${unit.id} child workflow target`);
240
- const contentHash = digest(target.contentHash, `unit ${unit.id} child workflow contentHash`);
241
- const expectedContentHash = childWorkflowContentHash({ ref: target.ref, planHash, via, taskRef, inputBindings });
242
- if (contentHash !== expectedContentHash) {
243
- fail(`unit ${unit.id} child workflow contentHash does not match its frozen dispatch`);
244
- }
245
- const childDepth = depth + 1;
246
- const frozenPlan = decodeWorkflowPlanV4(target.frozenPlan, childDepth, budget);
247
- const embeddedPlanJson = canonicalJsonLocal(frozenPlan);
248
- const actualPlanHash = sha256(embeddedPlanJson);
249
- if (actualPlanHash !== planHash) {
250
- fail(`unit ${unit.id} child workflow embedded plan does not match its frozen planHash`);
251
- }
252
- budget.embeddedBytes += utf8Bytes(embeddedPlanJson);
253
- return Object.freeze({
254
- kind: "child-workflow",
255
- ref: target.ref,
256
- planHash,
257
- frozenPlan,
258
- contentHash,
259
- via,
260
- ...(taskRef !== undefined ? { taskRef } : {}),
261
- ...(inputBindings ? { inputBindings } : {}),
262
- });
263
- }
264
- /** §3.5's exact `contentHash` formula. */
265
- function childWorkflowContentHash(fields) {
266
- return createHash("sha256")
267
- .update("akm.workflow.child-workflow\0v1\0")
268
- .update(canonicalJsonLocal({
269
- ref: fields.ref,
270
- planHash: fields.planHash,
271
- via: fields.via,
272
- taskRef: fields.taskRef ?? null,
273
- inputBindings: fields.inputBindings ?? null,
274
- }))
275
- .digest("hex");
276
- }
277
- function decodeCommandTarget(target, unit, requiredSources) {
278
- assertKeys(target, [
279
- "kind",
280
- "ref",
281
- "contentHash",
282
- "request",
283
- "runner",
284
- "concurrency",
285
- "cwdIdentity",
286
- "executable",
287
- "gitCommitOid",
288
- "inputBindings",
289
- ], `unit ${unit.id} command target`);
290
- if (target.ref !== null && typeof target.ref !== "string")
291
- fail(`unit ${unit.id} command target ref is invalid`);
292
- const request = decodeResolvedExecutionRequest(target.request);
293
- if (request.authorization.status === "denied")
294
- fail(`unit ${unit.id} command authorization is denied by policy`);
295
- if (Object.hasOwn(request.runtime, "environment")) {
296
- fail(`unit ${unit.id} resolved request runtime.environment is live and cannot be persisted`);
297
- }
298
- const contentHash = digest(target.contentHash, `unit ${unit.id} command contentHash`);
299
- const actualContentHash = sha256(request.command.content);
300
- if (contentHash !== actualContentHash)
301
- fail(`unit ${unit.id} command contentHash does not match request content`);
302
- const runner = decodeFrozenRunnerSpec(target.runner);
303
- if (target.concurrency !== undefined &&
304
- (!Number.isSafeInteger(target.concurrency) ||
305
- target.concurrency < 1 ||
306
- target.concurrency > 64)) {
307
- fail(`unit ${unit.id} frozen target concurrency is invalid`);
308
- }
309
- if (runner.engine !== request.engine.name || runner.kind !== request.engine.kind) {
310
- fail(`unit ${unit.id} frozen runner must match the resolved request engine`);
311
- }
312
- const source = request.command.source;
313
- if (source) {
314
- requiredSources.push(source);
315
- if (target.ref !== source.ref)
316
- fail(`unit ${unit.id} command target ref must match request source`);
317
- }
318
- else if (target.ref !== null) {
319
- fail(`unit ${unit.id} inline command target ref must be null`);
320
- }
321
- if (request.persona)
322
- requiredSources.push(request.persona.source);
323
- const cwdIdentity = Object.hasOwn(target, "cwdIdentity")
324
- ? decodeDirectoryIdentity(target.cwdIdentity, unit.id)
325
- : undefined;
326
- const executable = Object.hasOwn(target, "executable")
327
- ? decodeFrozenExecutableIdentity(target.executable, `unit ${unit.id} executable`)
328
- : undefined;
329
- if (runner.kind === "agent" && (cwdIdentity !== undefined || executable !== undefined)) {
330
- if (!cwdIdentity || !executable)
331
- fail(`unit ${unit.id} CLI command target requires cwdIdentity and executable`);
332
- if (runner.profile.bin !== executable.requested && runner.profile.bin !== executable.absolutePath) {
333
- fail(`unit ${unit.id} executable does not match the frozen runner bin`);
334
- }
335
- }
336
- else if (runner.kind !== "agent" && executable !== undefined) {
337
- fail(`unit ${unit.id} non-CLI target cannot carry a host executable`);
338
- }
339
- const gitCommitOid = decodeGitCommitOid(target.gitCommitOid, unit);
340
- const inputBindings = decodeInputBindings(target.inputBindings, `unit ${unit.id} command target`);
341
- // Force the shared request canonicalizer across every accepted wire request.
342
- canonicalResolvedExecutionRequest(request);
343
- return Object.freeze({
344
- kind: "command",
345
- ref: target.ref,
346
- contentHash,
347
- request,
348
- runner,
349
- ...(target.concurrency !== undefined ? { concurrency: target.concurrency } : {}),
350
- ...(cwdIdentity ? { cwdIdentity } : {}),
351
- ...(executable ? { executable } : {}),
352
- ...(gitCommitOid ? { gitCommitOid } : {}),
353
- ...(inputBindings ? { inputBindings } : {}),
354
- });
355
- }
356
- function decodeShellTarget(target, unit, environment) {
357
- assertKeys(target, ["kind", "contentHash", "exec", "cwdIdentity", "executable", "gitCommitOid", "inputBindings"], `unit ${unit.id} shell target`);
358
- const exec = decodeWorkflowExecSpec(target.exec, `unit ${unit.id} shell target exec`);
359
- const cwdIdentity = decodeDirectoryIdentity(target.cwdIdentity, unit.id);
360
- const executable = Object.hasOwn(target, "executable")
361
- ? decodeFrozenExecutableIdentity(target.executable, `unit ${unit.id} executable`)
362
- : undefined;
363
- if (executable && exec.command[0] !== executable.requested && exec.command[0] !== executable.absolutePath) {
364
- fail(`unit ${unit.id} shell executable does not match the frozen command`);
365
- }
366
- const gitCommitOid = decodeGitCommitOid(target.gitCommitOid, unit);
367
- const contentHash = digest(target.contentHash, `unit ${unit.id} shell contentHash`);
368
- // inputBindings deliberately sits OUTSIDE this preimage (P2b A-N7): identity
369
- // coverage for a task-composed unit comes from computeUnitInputHash's own
370
- // frozenTarget field (step-work.ts), which hashes this whole target anyway.
371
- const expected = createHash("sha256")
372
- .update("akm.workflow.shell.v1\0")
373
- .update(canonicalJsonLocal({ exec, environment, cwdIdentity }))
374
- .digest("hex");
375
- if (contentHash !== expected)
376
- fail(`unit ${unit.id} shell contentHash does not match its frozen dispatch`);
377
- const inputBindings = decodeInputBindings(target.inputBindings, `unit ${unit.id} shell target`);
378
- return Object.freeze({
379
- kind: "shell",
380
- contentHash,
381
- exec,
382
- cwdIdentity,
383
- ...(executable ? { executable } : {}),
384
- ...(gitCommitOid ? { gitCommitOid } : {}),
385
- ...(inputBindings ? { inputBindings } : {}),
386
- });
387
- }
388
- function decodeScriptTarget(target, unit, requiredSources) {
389
- assertKeys(target, [
390
- "kind",
391
- "ref",
392
- "contentHash",
393
- "exec",
394
- "interpreter",
395
- "extension",
396
- "bytesBase64",
397
- "byteLength",
398
- "cwdIdentity",
399
- "materialization",
400
- "executable",
401
- "gitCommitOid",
402
- "inputBindings",
403
- ], `unit ${unit.id} script target`);
404
- const exec = decodeWorkflowExecSpec(target.exec, `unit ${unit.id} script target exec`);
405
- if (typeof target.ref !== "string" || !target.ref.includes("//"))
406
- fail(`unit ${unit.id} script ref is invalid`);
407
- if (typeof target.interpreter !== "string" || !target.interpreter)
408
- fail(`unit ${unit.id} script interpreter is invalid`);
409
- if (typeof target.extension !== "string" || !/^\.[A-Za-z0-9]+$/.test(target.extension)) {
410
- fail(`unit ${unit.id} script extension is invalid`);
411
- }
412
- if (typeof target.bytesBase64 !== "string")
413
- fail(`unit ${unit.id} script bytesBase64 is invalid`);
414
- const bytes = Buffer.from(target.bytesBase64, "base64");
415
- if (bytes.toString("base64") !== target.bytesBase64)
416
- fail(`unit ${unit.id} script bytesBase64 is noncanonical`);
417
- if (!Number.isSafeInteger(target.byteLength) || target.byteLength !== bytes.byteLength) {
418
- fail(`unit ${unit.id} script byteLength does not match frozen bytes`);
419
- }
420
- const contentHash = digest(target.contentHash, `unit ${unit.id} script contentHash`);
421
- if (contentHash !== sha256(bytes))
422
- fail(`unit ${unit.id} script contentHash does not match frozen bytes`);
423
- if (target.materialization !== "ephemeral-0700-delete")
424
- fail(`unit ${unit.id} script materialization is invalid`);
425
- const cwdIdentity = decodeDirectoryIdentity(target.cwdIdentity, unit.id);
426
- const executable = Object.hasOwn(target, "executable")
427
- ? decodeFrozenExecutableIdentity(target.executable, `unit ${unit.id} executable`)
428
- : undefined;
429
- const gitCommitOid = decodeGitCommitOid(target.gitCommitOid, unit);
430
- const inputBindings = decodeInputBindings(target.inputBindings, `unit ${unit.id} script target`);
431
- requiredSources.push({ ref: target.ref, bundle: "", adapter: "", file: "", hash: contentHash });
432
- return Object.freeze({
433
- kind: "script",
434
- ref: target.ref,
435
- contentHash,
436
- exec,
437
- interpreter: target.interpreter,
438
- extension: target.extension,
439
- bytesBase64: target.bytesBase64,
440
- byteLength: target.byteLength,
441
- cwdIdentity,
442
- materialization: "ephemeral-0700-delete",
443
- ...(executable ? { executable } : {}),
444
- ...(gitCommitOid ? { gitCommitOid } : {}),
445
- ...(inputBindings ? { inputBindings } : {}),
446
- });
447
- }
448
- /**
449
- * Decode a frozen target's `inputBindings` (P2b §3.2 A-N7): a composing
450
- * step's `with:` normalized against the composed task's declared inputs.
451
- * Closed `kind`, `INPUT_NAME_PATTERN` name, unique and sorted by name (never
452
- * `[]` — absence is the identity-preserving default). A `literal` entry
453
- * requires `value`; a `reference` entry requires a `parseReference`-valid
454
- * `from` plus the declaration's bounded `schema` (§3.6's widened reference
455
- * arm, `src/execution/input-contract.ts`).
456
- */
457
- function decodeInputBindings(value, label) {
458
- if (value === undefined)
459
- return undefined;
460
- if (!Array.isArray(value) || value.length === 0)
461
- fail(`${label} inputBindings must be a non-empty array`);
462
- let prior;
463
- const bindings = value.map((raw, index) => {
464
- const binding = record(raw, `${label} inputBindings[${index}]`);
465
- if (typeof binding.name !== "string" || !INPUT_NAME_PATTERN.test(binding.name)) {
466
- fail(`${label} inputBindings[${index}] name is invalid`);
467
- }
468
- if (prior !== undefined && compareCodePoints(prior, binding.name) >= 0) {
469
- fail(`${label} inputBindings must be sorted by unique name`);
470
- }
471
- prior = binding.name;
472
- if (binding.kind === "literal") {
473
- assertKeys(binding, ["kind", "name", "value"], `${label} inputBindings[${index}]`);
474
- if (!Object.hasOwn(binding, "value"))
475
- fail(`${label} inputBindings[${index}] literal binding requires value`);
476
- return Object.freeze({ kind: "literal", name: binding.name, value: binding.value });
477
- }
478
- if (binding.kind === "reference") {
479
- assertKeys(binding, ["kind", "name", "from", "schema"], `${label} inputBindings[${index}]`);
480
- if (typeof binding.from !== "string" || !parseReference(binding.from).ok) {
481
- fail(`${label} inputBindings[${index}] from is not a valid reference`);
482
- }
483
- const schema = record(binding.schema, `${label} inputBindings[${index}] schema`);
484
- return Object.freeze({
485
- kind: "reference",
486
- name: binding.name,
487
- from: binding.from,
488
- schema: Object.freeze(schema),
489
- });
490
- }
491
- return fail(`${label} inputBindings[${index}] has unsupported kind ${String(binding.kind)}`);
492
- });
493
- return Object.freeze(bindings);
494
- }
495
- function decodeGitCommitOid(value, unit) {
496
- if (unit.isolation === "worktree") {
497
- if (typeof value !== "string" || !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(value)) {
498
- fail(`unit ${unit.id} worktree target requires a canonical gitCommitOid`);
499
- }
500
- return value;
501
- }
502
- if (value !== undefined)
503
- fail(`unit ${unit.id} gitCommitOid is only valid for worktree isolation`);
504
- return undefined;
505
- }
506
- function decodeDirectoryIdentity(value, unitId) {
507
- const identity = record(value, `unit ${unitId} cwdIdentity`);
508
- const fields = [
509
- "requestedRoot",
510
- "realRoot",
511
- "rootDevice",
512
- "rootInode",
513
- "requestedCwd",
514
- "realCwd",
515
- "cwdDevice",
516
- "cwdInode",
517
- ];
518
- assertKeys(identity, fields, `unit ${unitId} cwdIdentity`);
519
- for (const field of fields) {
520
- if (typeof identity[field] !== "string" || !identity[field])
521
- fail(`unit ${unitId} cwdIdentity.${field} is invalid`);
522
- }
523
- for (const field of ["requestedRoot", "realRoot", "requestedCwd", "realCwd"]) {
524
- if (!path.isAbsolute(identity[field]))
525
- fail(`unit ${unitId} cwdIdentity.${field} must be absolute`);
526
- }
527
- return Object.freeze(identity);
528
- }
529
- function decodeEnvironment(value, unitId) {
530
- if (!Array.isArray(value))
531
- fail(`unit ${unitId} environment must be an array`);
532
- const out = [];
533
- const names = new Set();
534
- for (const [index, raw] of value.entries()) {
535
- const binding = record(raw, `unit ${unitId} environment[${index}]`);
536
- if (binding.kind === "literal") {
537
- assertKeys(binding, ["kind", "name", "value"], `unit ${unitId} environment[${index}]`);
538
- environmentName(binding.name, unitId, index);
539
- if (typeof binding.value !== "string")
540
- fail(`unit ${unitId} literal environment value must be a string`);
541
- if (literalLooksSecret(binding.name, binding.value)) {
542
- fail(`unit ${unitId} literal environment value is secret-shaped`);
543
- }
544
- out.push(Object.freeze({ kind: "literal", name: binding.name, value: binding.value }));
545
- }
546
- else if (binding.kind === "pass-through") {
547
- assertKeys(binding, ["kind", "name"], `unit ${unitId} environment[${index}]`);
548
- environmentName(binding.name, unitId, index);
549
- out.push(Object.freeze({ kind: "pass-through", name: binding.name }));
550
- }
551
- else if (binding.kind === "env-ref") {
552
- assertKeys(binding, ["kind", "ref", "owner", "keys", "secretNames", "precedence"], `unit ${unitId} environment[${index}]`);
553
- if (typeof binding.ref !== "string" || !binding.ref.includes("//")) {
554
- fail(`unit ${unitId} environment[${index}] env-ref must be fully qualified`);
555
- }
556
- const owner = decodeEnvironmentOwner(binding.owner, binding.ref, unitId, index);
557
- const keys = sortedUniqueStrings(binding.keys, `unit ${unitId} environment[${index}] keys`, (value) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(value));
558
- const secretNames = sortedUniqueStrings(binding.secretNames, `unit ${unitId} environment[${index}] secretNames`, (value) => /^[A-Za-z0-9_./-]+$/.test(value) && !value.includes(".."));
559
- if (!Number.isSafeInteger(binding.precedence) || binding.precedence < 0) {
560
- fail(`unit ${unitId} environment[${index}] precedence is invalid`);
561
- }
562
- out.push(Object.freeze({
563
- kind: "env-ref",
564
- ref: binding.ref,
565
- owner,
566
- keys: Object.freeze(keys),
567
- secretNames: Object.freeze(secretNames),
568
- precedence: binding.precedence,
569
- }));
570
- }
571
- else {
572
- fail(`unit ${unitId} environment[${index}] has unsupported kind`);
573
- }
574
- if (binding.kind !== "env-ref") {
575
- const name = binding.name;
576
- if (names.has(name))
577
- fail(`unit ${unitId} environment contains duplicate name ${name}`);
578
- names.add(name);
579
- }
580
- }
581
- return out;
582
- }
583
- function decodeEnvironmentOwner(value, ref, unitId, index) {
584
- const label = `unit ${unitId} environment[${index}] owner`;
585
- const owner = record(value, label);
586
- const fields = [
587
- "bundle",
588
- "adapter",
589
- "requestedRoot",
590
- "realRoot",
591
- "rootPhysicalIdentity",
592
- "requestedPath",
593
- "realPath",
594
- "relativePath",
595
- ];
596
- assertKeys(owner, fields, label);
597
- for (const field of fields) {
598
- if (typeof owner[field] !== "string" || !owner[field])
599
- fail(`${label}.${field} is invalid`);
600
- }
601
- if (!ref.startsWith(`${owner.bundle}//`))
602
- fail(`${label}.bundle does not own env-ref ${ref}`);
603
- for (const field of ["requestedRoot", "realRoot", "requestedPath", "realPath"]) {
604
- if (!path.isAbsolute(owner[field]))
605
- fail(`${label}.${field} must be absolute`);
606
- }
607
- const relativePath = owner.relativePath;
608
- if (relativePath.includes("\\") ||
609
- relativePath === "." ||
610
- relativePath.startsWith("../") ||
611
- path.isAbsolute(relativePath)) {
612
- fail(`${label}.relativePath is not canonical`);
613
- }
614
- if (path.resolve(owner.requestedRoot, relativePath) !== path.resolve(owner.requestedPath)) {
615
- fail(`${label}.requestedPath does not match relativePath`);
616
- }
617
- if (path.resolve(owner.realRoot, relativePath) !== path.resolve(owner.realPath)) {
618
- fail(`${label}.realPath does not match relativePath`);
619
- }
620
- return Object.freeze(owner);
621
- }
622
- function sortedUniqueStrings(value, label, accepts) {
623
- if (!Array.isArray(value))
624
- fail(`${label} must be an array`);
625
- const out = [];
626
- let prior;
627
- for (const item of value) {
628
- if (typeof item !== "string" || !accepts(item))
629
- fail(`${label} contains an invalid value`);
630
- if (prior !== undefined && compareCodePoints(prior, item) >= 0)
631
- fail(`${label} must be sorted and unique`);
632
- prior = item;
633
- out.push(item);
634
- }
635
- return out;
636
- }
637
- function decodeSourceReadSet(value) {
638
- if (!Array.isArray(value) || value.length === 0)
639
- fail("sourceReadSet must contain at least one source identity");
640
- const out = [];
641
- const logical = new Set();
642
- const physical = new Map();
643
- let priorKey;
644
- for (const [index, raw] of value.entries()) {
645
- const snapshot = record(raw, `sourceReadSet[${index}]`);
646
- assertKeys(snapshot, ["identity", "containmentPhysicalIdentity", "physicalIdentity", "size"], `sourceReadSet[${index}]`);
647
- const identity = decodeExecutionSourceIdentity(snapshot.identity, `sourceReadSet[${index}].identity`);
648
- const key = sourceKey(identity);
649
- if (logical.has(key))
650
- fail(`sourceReadSet contains duplicate identity key ${key}`);
651
- if (typeof snapshot.containmentPhysicalIdentity !== "string" || !snapshot.containmentPhysicalIdentity) {
652
- fail(`sourceReadSet[${index}] containment physical identity is invalid`);
653
- }
654
- if (typeof snapshot.physicalIdentity !== "string" || !snapshot.physicalIdentity) {
655
- fail(`sourceReadSet[${index}] physical identity is invalid`);
656
- }
657
- if (!Number.isSafeInteger(snapshot.size) || snapshot.size < 0)
658
- fail(`sourceReadSet[${index}] size is invalid`);
659
- const physicalKey = `${snapshot.containmentPhysicalIdentity}\0${snapshot.physicalIdentity}`;
660
- const alias = physical.get(physicalKey);
661
- if (alias !== undefined && alias !== key)
662
- fail(`sourceReadSet logical refs alias the same physical file identity`);
663
- physical.set(physicalKey, key);
664
- if (priorKey !== undefined && compareCodePoints(priorKey, key) >= 0) {
665
- fail("sourceReadSet is not in canonical ref, adapter, file sort order");
666
- }
667
- logical.add(key);
668
- priorKey = key;
669
- out.push(Object.freeze({
670
- identity,
671
- containmentPhysicalIdentity: snapshot.containmentPhysicalIdentity,
672
- physicalIdentity: snapshot.physicalIdentity,
673
- size: snapshot.size,
674
- }));
675
- }
676
- return out;
677
- }
678
- function assertRequiredSources(readSet, required) {
679
- for (const identity of required) {
680
- const found = readSet.find((snapshot) => snapshot.identity.ref === identity.ref);
681
- if (!found)
682
- fail(`source read-set is missing required command/persona/script source ${identity.ref}`);
683
- if (identity.bundle && sourceKey(found.identity) !== sourceKey(identity)) {
684
- fail(`source read-set identity mismatch for ${identity.ref}`);
685
- }
686
- if (found.identity.hash !== identity.hash)
687
- fail(`source read-set hash mismatch for ${identity.ref}`);
688
- }
689
- }
690
- function sourceKey(identity) {
691
- return `${identity.ref}\0${identity.adapter}\0${identity.file}`;
692
- }
693
- function environmentName(value, unitId, index) {
694
- if (typeof value !== "string" || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {
695
- fail(`unit ${unitId} environment[${index}] has an invalid name`);
696
- }
697
- }
698
- function literalLooksSecret(name, value) {
699
- const key = name.toLowerCase();
700
- if (/(?:secret|token|password|passwd|api[_-]?key|access[_-]?key|private[_-]?key|credential|bearer|client_secret)/.test(key)) {
701
- return true;
702
- }
703
- const candidate = value.trim();
704
- if (candidate.length < 20 || /\s/.test(candidate))
705
- return false;
706
- if (/^(?:sk-|rk-|ghp_|gho_|ghu_|ghs_|ghr_|github_pat_|xox[baprs]-|AKIA|ASIA|AIza|ya29\.|-----BEGIN)/.test(candidate)) {
707
- return true;
708
- }
709
- const classes = [/[a-z]/, /[A-Z]/, /[0-9]/, /[^A-Za-z0-9]/].filter((pattern) => pattern.test(candidate)).length;
710
- return (candidate.length >= 24 && classes >= 3) || (candidate.length >= 32 && /^[A-Za-z0-9+/=_-]+$/.test(candidate));
711
- }
712
- function digest(value, label) {
713
- if (typeof value !== "string" || !/^[0-9a-f]{64}$/.test(value))
714
- fail(`${label} must be a SHA-256 digest`);
715
- return value;
716
- }
717
- function sha256(value) {
718
- return createHash("sha256").update(value).digest("hex");
719
- }
720
- function canonicalJsonLocal(value) {
721
- const sort = (entry) => {
722
- if (Array.isArray(entry))
723
- return entry.map(sort);
724
- if (entry && typeof entry === "object") {
725
- return Object.fromEntries(Object.entries(entry)
726
- .sort(([left], [right]) => compareCodePoints(left, right))
727
- .map(([key, nested]) => [key, sort(nested)]));
728
- }
729
- return entry;
730
- };
731
- return JSON.stringify(sort(value));
732
- }
733
- function record(value, label) {
734
- if (!value || typeof value !== "object" || Array.isArray(value))
735
- fail(`${label} must be an object`);
736
- return value;
737
- }
738
- function assertKeys(value, allowed, label) {
739
- for (const key of Object.keys(value))
740
- if (!allowed.includes(key))
741
- fail(`${label} contains unknown key ${key}`);
742
- }
743
- function fail(message) {
744
- throw new UsageError(`Invalid frozen workflow plan v4: ${message}.`, "INVALID_JSON_ARGUMENT");
745
- }