akm-cli 0.9.17-alpha.2 → 0.9.17-alpha.4

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 (343) hide show
  1. package/CHANGELOG.md +756 -0
  2. package/dist/akm +94 -196
  3. package/dist/cli/shared.js +6 -2
  4. package/dist/cli.js +22 -9
  5. package/dist/commands/agent/agent-dispatch.js +1 -1
  6. package/dist/commands/command/command-execution.js +24 -62
  7. package/dist/commands/feedback-cli.js +0 -1
  8. package/dist/commands/health/accept-rate.js +2 -2
  9. package/dist/commands/health/checks.js +30 -75
  10. package/dist/commands/health/config-skew.js +38 -0
  11. package/dist/commands/health/egress.js +54 -0
  12. package/dist/commands/health/html-report.js +0 -38
  13. package/dist/commands/health/improve-metrics.js +123 -562
  14. package/dist/commands/health/plugin-staleness.js +53 -3
  15. package/dist/commands/health/renderers.js +12 -4
  16. package/dist/commands/health/report-view-model.js +11 -106
  17. package/dist/commands/health/types-improve.js +4 -19
  18. package/dist/commands/health/windows.js +64 -73
  19. package/dist/commands/health.js +122 -143
  20. package/dist/commands/improve/consolidate/chunking.js +25 -100
  21. package/dist/commands/improve/consolidate/sanitize.js +54 -149
  22. package/dist/commands/improve/consolidate.js +538 -1075
  23. package/dist/commands/improve/content-hash.js +16 -24
  24. package/dist/commands/improve/distill/content-repair.js +18 -100
  25. package/dist/commands/improve/distill-guards.js +20 -81
  26. package/dist/commands/improve/distill-promotion-policy.js +23 -243
  27. package/dist/commands/improve/distill.js +608 -1075
  28. package/dist/commands/improve/eligibility.js +126 -400
  29. package/dist/commands/improve/execution.js +3 -5
  30. package/dist/commands/improve/extract.js +487 -1046
  31. package/dist/commands/improve/feedback-valence.js +0 -25
  32. package/dist/commands/improve/improve-cli.js +29 -166
  33. package/dist/commands/improve/improve-result-file.js +10 -66
  34. package/dist/commands/improve/improve-strategies.js +12 -7
  35. package/dist/commands/improve/improve-usage-report.js +18 -64
  36. package/dist/commands/improve/improve.js +443 -1063
  37. package/dist/commands/improve/ledger.js +114 -0
  38. package/dist/commands/improve/locks.js +2 -8
  39. package/dist/commands/improve/loop-stages.js +459 -1172
  40. package/dist/commands/improve/memory/derived-ref.js +12 -77
  41. package/dist/commands/improve/memory/memory-belief.js +14 -118
  42. package/dist/commands/improve/memory/memory-improve.js +4 -3
  43. package/dist/commands/improve/outcome-loop.js +28 -156
  44. package/dist/commands/improve/planner.js +5 -10
  45. package/dist/commands/improve/preparation.js +851 -2339
  46. package/dist/commands/improve/proactive-maintenance.js +34 -101
  47. package/dist/commands/improve/reflect-noise.js +104 -280
  48. package/dist/commands/improve/reflect.js +621 -1367
  49. package/dist/commands/improve/salience.js +46 -232
  50. package/dist/commands/improve/session-asset.js +19 -100
  51. package/dist/commands/improve/stage.js +323 -0
  52. package/dist/commands/proposal/drain.js +251 -644
  53. package/dist/commands/proposal/proposal-cli.js +3 -18
  54. package/dist/commands/proposal/proposal-types.js +20 -41
  55. package/dist/commands/proposal/proposal.js +1 -2
  56. package/dist/commands/proposal/propose.js +134 -160
  57. package/dist/commands/proposal/repository.js +502 -1487
  58. package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
  59. package/dist/commands/proposal/validators/proposal-validators.js +1 -1
  60. package/dist/commands/proposal/validators/proposals.js +13 -89
  61. package/dist/commands/read/curate.js +63 -413
  62. package/dist/commands/read/search-cli.js +16 -33
  63. package/dist/commands/read/search.js +17 -23
  64. package/dist/commands/read/show.js +2 -13
  65. package/dist/commands/sources/bundle-cli.js +25 -2
  66. package/dist/commands/sources/bundle-config-ops.js +7 -0
  67. package/dist/commands/sources/dangerous-env-audit.js +1 -2
  68. package/dist/commands/sources/info.js +2 -11
  69. package/dist/commands/sources/installed-stashes.js +197 -746
  70. package/dist/commands/sources/schema-repair.js +98 -129
  71. package/dist/commands/sources/source-add.js +62 -12
  72. package/dist/commands/sources/stash-cli.js +1 -1
  73. package/dist/commands/tasks/explain.js +10 -13
  74. package/dist/commands/tasks/tasks-cli.js +9 -8
  75. package/dist/commands/tasks/tasks.js +326 -930
  76. package/dist/commands/tasks/validate.js +42 -21
  77. package/dist/commands/workflow/plan.js +22 -29
  78. package/dist/commands/workflow-cli.js +4 -4
  79. package/dist/core/adapter/adapters/akm-adapter.js +0 -1
  80. package/dist/core/adapter/adapters/akm-lint.js +2 -3
  81. package/dist/core/adapter/adapters/akm-metadata.js +11 -12
  82. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  83. package/dist/core/adapter/execution-source.js +17 -29
  84. package/dist/core/asset/resolve-ref.js +1 -1
  85. package/dist/core/bundle-id.js +42 -5
  86. package/dist/core/bundle-rename.js +291 -0
  87. package/dist/core/config/config-io.js +1 -2
  88. package/dist/core/config/config-schema.js +1 -33
  89. package/dist/core/config/config-walker.js +1 -1
  90. package/dist/core/config/config.js +163 -68
  91. package/dist/core/config/legacy-source-shape-shim.js +38 -9
  92. package/dist/core/config/schema/embedding.js +20 -5
  93. package/dist/core/config/schema/engines.js +5 -0
  94. package/dist/core/config/schema/execution.js +1 -1
  95. package/dist/core/config/schema/experimental.js +1 -1
  96. package/dist/core/config/schema/improve-processes.js +21 -95
  97. package/dist/core/config/schema/improve.js +4 -42
  98. package/dist/core/config/schema/scheduler.js +12 -12
  99. package/dist/core/config/schema/search.js +6 -22
  100. package/dist/core/env-secret-ref.js +0 -1
  101. package/dist/core/errors.js +8 -9
  102. package/dist/core/file-lock.js +76 -173
  103. package/dist/core/logs-db.js +2 -2
  104. package/dist/core/paths.js +0 -27
  105. package/dist/core/redaction.js +109 -2
  106. package/dist/core/run-lock.js +2 -5
  107. package/dist/core/spawn-env.js +1 -1
  108. package/dist/core/state/migrations.js +108 -61
  109. package/dist/core/state-db-scope.js +2 -4
  110. package/dist/core/state-db.js +126 -692
  111. package/dist/core/type-presentation.js +1 -9
  112. package/dist/core/write-source.js +293 -1012
  113. package/dist/execution/input-contract.js +1 -1
  114. package/dist/execution/resolved-request.js +135 -689
  115. package/dist/execution/source.js +63 -257
  116. package/dist/execution/target-ref.js +1 -1
  117. package/dist/indexer/bundle-identity-guard.js +2 -2
  118. package/dist/indexer/db/graph-db.js +106 -46
  119. package/dist/indexer/ensure-index.js +44 -85
  120. package/dist/indexer/graph/graph-extraction.js +340 -562
  121. package/dist/indexer/graph/graph-related.js +130 -0
  122. package/dist/indexer/index-rebuild-lock.js +3 -11
  123. package/dist/indexer/index-writer-lock.js +8 -17
  124. package/dist/indexer/index-written-assets.js +139 -151
  125. package/dist/indexer/indexer.js +524 -846
  126. package/dist/indexer/materialize-embeddings.js +60 -397
  127. package/dist/indexer/passes/memory-inference.js +81 -90
  128. package/dist/indexer/passes/metadata.js +132 -200
  129. package/dist/indexer/read-preflight.js +0 -7
  130. package/dist/indexer/scan/doc-to-entry.js +1 -3
  131. package/dist/indexer/scan/drain-dir.js +1 -1
  132. package/dist/indexer/search/db-search.js +181 -590
  133. package/dist/indexer/search/fts-query.js +30 -41
  134. package/dist/indexer/search/ranking.js +28 -154
  135. package/dist/indexer/search/search-attribution.js +12 -32
  136. package/dist/indexer/search/search-fields.js +11 -15
  137. package/dist/indexer/search/search-hit-enrichers.js +54 -85
  138. package/dist/indexer/search/search-source.js +1 -4
  139. package/dist/indexer/usage/usage-events.js +2 -7
  140. package/dist/integrations/agent/engine-fallback.js +23 -40
  141. package/dist/integrations/agent/engine-resolution.js +93 -183
  142. package/dist/integrations/agent/execution.js +507 -0
  143. package/dist/integrations/agent/model-map.js +28 -156
  144. package/dist/integrations/agent/request-lowering.js +66 -141
  145. package/dist/integrations/agent/runner-dispatch.js +143 -321
  146. package/dist/integrations/agent/runner.js +54 -14
  147. package/dist/integrations/lockfile.js +53 -101
  148. package/dist/llm/embedders/deterministic.js +2 -3
  149. package/dist/llm/embedders/profile.js +71 -0
  150. package/dist/llm/embedders/remote.js +10 -15
  151. package/dist/llm/graph-extract.js +3 -12
  152. package/dist/llm/index-passes.js +3 -5
  153. package/dist/llm/memory-infer.js +1 -2
  154. package/dist/llm/metadata-enhance.js +1 -2
  155. package/dist/llm/structured-call.js +5 -24
  156. package/dist/output/generic-render.js +23 -11
  157. package/dist/output/html-render.js +13 -10
  158. package/dist/output/render-registry.js +3 -32
  159. package/dist/output/shapes/helpers.js +2 -34
  160. package/dist/output/shapes/passthrough.js +1 -9
  161. package/dist/{indexer/search/ranking-types.js → output/text/bundle-rename.js} +4 -1
  162. package/dist/output/text/command-format.js +60 -23
  163. package/dist/output/text/helpers.js +1 -1
  164. package/dist/output/text/migrate.js +5 -14
  165. package/dist/output/text/proposal-format.js +1 -2
  166. package/dist/output/text/workflow-format.js +0 -32
  167. package/dist/output/text.js +2 -0
  168. package/dist/registry/factory.js +4 -19
  169. package/dist/registry/network.js +66 -220
  170. package/dist/registry/providers/index.js +0 -2
  171. package/dist/registry/providers/skills-sh.js +3 -14
  172. package/dist/registry/providers/static-index.js +24 -26
  173. package/dist/registry/resolve.js +55 -131
  174. package/dist/scripts/akm-migrate-node.js +43937 -93313
  175. package/dist/scripts/akm-migrate.js +43697 -93071
  176. package/dist/setup/registry-stash-loader.js +4 -13
  177. package/dist/setup/semantic-assets.js +3 -44
  178. package/dist/setup/setup.js +1 -1
  179. package/dist/setup/steps/tasks.js +25 -15
  180. package/dist/sources/provider-factory.js +17 -18
  181. package/dist/sources/providers/filesystem.js +2 -3
  182. package/dist/sources/providers/git-install.js +7 -1
  183. package/dist/sources/providers/git-provider.js +0 -3
  184. package/dist/sources/providers/git-stash.js +0 -17
  185. package/dist/sources/providers/npm.js +2 -4
  186. package/dist/sources/providers/provider-utils.js +5 -10
  187. package/dist/sources/providers/website.js +0 -2
  188. package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
  189. package/dist/sources/website-url.js +2 -2
  190. package/dist/storage/database.js +9 -35
  191. package/dist/storage/repositories/improve-ledger-repository.js +168 -0
  192. package/dist/storage/repositories/index-connection.js +34 -70
  193. package/dist/storage/repositories/index-entries-repository.js +69 -111
  194. package/dist/storage/repositories/index-entry-mapper.js +1 -2
  195. package/dist/storage/repositories/index-entry-schema.js +83 -269
  196. package/dist/storage/repositories/index-fts-repository.js +86 -256
  197. package/dist/storage/repositories/index-llm-cache-repository.js +17 -0
  198. package/dist/storage/repositories/index-meta-repository.js +6 -4
  199. package/dist/storage/repositories/index-schema.js +192 -220
  200. package/dist/storage/repositories/index-utility-repository.js +8 -29
  201. package/dist/storage/repositories/index-vec-repository.js +133 -414
  202. package/dist/storage/repositories/outcome-repository.js +2 -1
  203. package/dist/storage/repositories/proposals-repository.js +35 -0
  204. package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
  205. package/dist/storage/repositories/task-history-repository.js +26 -4
  206. package/dist/storage/repositories/workflow-runs-repository.js +53 -244
  207. package/dist/storage/sqlite-migrations.js +136 -0
  208. package/dist/storage/sqlite-pragmas.js +11 -9
  209. package/dist/storage/sqlite-transaction.js +170 -0
  210. package/dist/storage/state-db-integrity.js +34 -27
  211. package/dist/tasks/activation-config.js +134 -62
  212. package/dist/tasks/backends/cron.js +129 -277
  213. package/dist/tasks/backends/exec-utils.js +2 -5
  214. package/dist/tasks/backends/launchd.js +125 -745
  215. package/dist/tasks/backends/schtasks.js +101 -620
  216. package/dist/tasks/prepare/prepare-support.js +5 -15
  217. package/dist/tasks/prepare/prepare.js +0 -2
  218. package/dist/tasks/resolve-akm-bin.js +20 -79
  219. package/dist/tasks/run/attempt-lifecycle.js +0 -1
  220. package/dist/tasks/scheduler-binding.js +18 -238
  221. package/dist/tasks/scheduler-invocation.js +52 -52
  222. package/dist/tasks/scheduler-lock.js +53 -0
  223. package/dist/tasks/scheduler-sync.js +363 -679
  224. package/dist/tasks/source/parse-task-source.js +160 -10
  225. package/dist/tasks/source/task-source-v3-frozen.js +3 -4
  226. package/dist/tasks/source/task-to-v4.js +2 -2
  227. package/dist/workflows/authoring/authoring.js +3 -12
  228. package/dist/workflows/compile.js +211 -0
  229. package/dist/workflows/concurrency-policy.js +13 -74
  230. package/dist/workflows/exec/child-invocation.js +3 -17
  231. package/dist/workflows/exec/child-workflow.js +32 -141
  232. package/dist/workflows/exec/dispatch-redaction.js +13 -53
  233. package/dist/workflows/exec/environment.js +98 -0
  234. package/dist/workflows/exec/exec-unit.js +33 -140
  235. package/dist/workflows/exec/frozen-judge.js +7 -59
  236. package/dist/workflows/exec/native-executor.js +82 -341
  237. package/dist/workflows/exec/param-secrets.js +29 -47
  238. package/dist/workflows/exec/run-workflow.js +154 -387
  239. package/dist/workflows/exec/scheduler.js +9 -36
  240. package/dist/workflows/exec/step-work.js +127 -430
  241. package/dist/workflows/exec/unit-dispatch.js +11 -63
  242. package/dist/workflows/exec/unit-writer.js +8 -52
  243. package/dist/workflows/exec/worktree.js +39 -273
  244. package/dist/workflows/freeze/child-output-references.js +4 -15
  245. package/dist/workflows/freeze/environment.js +99 -92
  246. package/dist/workflows/freeze/freeze.js +172 -0
  247. package/dist/workflows/freeze/step-values.js +19 -21
  248. package/dist/workflows/freeze/targets/child-workflow.js +23 -92
  249. package/dist/workflows/freeze/targets/command.js +10 -33
  250. package/dist/workflows/freeze/targets/script.js +5 -12
  251. package/dist/workflows/freeze/targets/shell.js +3 -6
  252. package/dist/workflows/freeze/targets/task.js +25 -80
  253. package/dist/workflows/freeze/task-bindings.js +20 -67
  254. package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
  255. package/dist/workflows/ir/params.js +6 -51
  256. package/dist/workflows/ir/plan-hash.js +2 -34
  257. package/dist/workflows/parser.js +140 -43
  258. package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
  259. package/dist/workflows/renderer.js +36 -69
  260. package/dist/workflows/resource-limits.js +12 -120
  261. package/dist/workflows/runtime/agent-identity.js +8 -40
  262. package/dist/workflows/runtime/run-outputs.js +3 -6
  263. package/dist/workflows/runtime/run-plan.js +316 -0
  264. package/dist/workflows/runtime/runs.js +48 -200
  265. package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
  266. package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
  267. package/dist/workflows/validate-summary.js +2 -7
  268. package/docs/integration/bundling-akm.md +49 -42
  269. package/docs/migration/README.md +1 -0
  270. package/docs/migration/release-notes/0.9.17.md +41 -0
  271. package/docs/migration/v0.9.1-to-v0.9.2.md +19 -7
  272. package/docs/reference/cli.md +182 -125
  273. package/docs/reference/configuration.md +49 -56
  274. package/docs/reference/data-and-telemetry.md +19 -20
  275. package/docs/reference/tasks.md +86 -38
  276. package/docs/reference/workflow-schema.md +14 -18
  277. package/docs/reference/workflows.md +6 -9
  278. package/package.json +1 -1
  279. package/schemas/akm-config.json +87 -406
  280. package/dist/commands/health/advisories.js +0 -150
  281. package/dist/commands/health/metrics.js +0 -329
  282. package/dist/commands/health/surfaces.js +0 -102
  283. package/dist/commands/improve/anti-collapse.js +0 -83
  284. package/dist/commands/improve/collapse-detector.js +0 -432
  285. package/dist/commands/improve/consolidate/eligibility.js +0 -48
  286. package/dist/commands/improve/consolidate/merge.js +0 -146
  287. package/dist/commands/improve/distill/promote-memory.js +0 -329
  288. package/dist/commands/improve/distill/quality-gate.js +0 -500
  289. package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
  290. package/dist/commands/improve/proposal-envelope.js +0 -31
  291. package/dist/commands/improve/run-context.js +0 -123
  292. package/dist/commands/improve/shared.js +0 -21
  293. package/dist/commands/improve/source-identity.js +0 -28
  294. package/dist/commands/improve/triage.js +0 -96
  295. package/dist/commands/proposal/drain-policies.js +0 -151
  296. package/dist/commands/sources/update-transaction.js +0 -220
  297. package/dist/core/action-contributors.js +0 -28
  298. package/dist/core/config/config-version-shim.js +0 -101
  299. package/dist/core/config/retired-experimental-keys-shim.js +0 -62
  300. package/dist/core/fs-txn.js +0 -405
  301. package/dist/core/lexical-score.js +0 -25
  302. package/dist/core/maintenance-barrier.js +0 -167
  303. package/dist/execution/executable-identity.js +0 -105
  304. package/dist/execution/guarded-source.js +0 -427
  305. package/dist/indexer/graph/graph-boost.js +0 -427
  306. package/dist/indexer/graph/graph-dedup.js +0 -95
  307. package/dist/indexer/search/name-match.js +0 -35
  308. package/dist/indexer/search/ranking-contributors.js +0 -515
  309. package/dist/indexer/walk/project-context.js +0 -192
  310. package/dist/integrations/agent/execution-cascade.js +0 -566
  311. package/dist/integrations/agent/execution-definitions.js +0 -202
  312. package/dist/integrations/agent/execution-lowering.js +0 -841
  313. package/dist/integrations/agent/execution-preparation.js +0 -98
  314. package/dist/integrations/agent/inline-execution.js +0 -74
  315. package/dist/registry/create-provider-registry.js +0 -29
  316. package/dist/registry/pinned-request-helper.js +0 -247
  317. package/dist/registry/pinned-transport.js +0 -717
  318. package/dist/sources/providers/index.js +0 -14
  319. package/dist/storage/engines/sqlite-migrations.js +0 -271
  320. package/dist/storage/repositories/canaries-repository.js +0 -107
  321. package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
  322. package/dist/storage/repositories/registry-cache.js +0 -113
  323. package/dist/tasks/scheduler-sync-preview.js +0 -52
  324. package/dist/workflows/freeze/resolve-steps.js +0 -86
  325. package/dist/workflows/freeze/source-freeze.js +0 -64
  326. package/dist/workflows/ir/compile.js +0 -321
  327. package/dist/workflows/ir/environment-v4.js +0 -330
  328. package/dist/workflows/ir/freeze-v4.js +0 -153
  329. package/dist/workflows/ir/schema-v4.js +0 -745
  330. package/dist/workflows/ir/schema.js +0 -354
  331. package/dist/workflows/program/schema.js +0 -78
  332. package/dist/workflows/runtime/checkin.js +0 -57
  333. package/dist/workflows/runtime/plan-classifier.js +0 -196
  334. package/dist/workflows/runtime/unit-checkin.js +0 -45
  335. package/dist/workflows/runtime/unit-phases.js +0 -20
  336. package/dist/workflows/schema.js +0 -4
  337. package/dist/workflows/source-ir/compile.js +0 -200
  338. package/dist/workflows/source-ir/program.js +0 -50
  339. package/dist/workflows/source-ir/result.js +0 -26
  340. package/dist/workflows/source-ir/schema.js +0 -786
  341. package/dist/workflows/source-ir/triggers.js +0 -79
  342. package/dist/workflows/source-ir/uses.js +0 -40
  343. package/dist/workflows/validator.js +0 -60
@@ -1,330 +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
- * Durable-v4 symbolic workflow environments.
6
- *
7
- * Env-file values are the deliberately narrow live-value exception to a
8
- * frozen workflow plan. Freeze persists only the qualified owner, physical
9
- * containment, exact key set, and secret-token topology. Materialization
10
- * later reads current values directly through that descriptor, without
11
- * consulting config, the index, or authored workflow source.
12
- */
13
- import fs from "node:fs";
14
- import path from "node:path";
15
- import dotenv from "dotenv";
16
- import { assetPathForName } from "../../core/asset/asset-placement.js";
17
- import { isWithin } from "../../core/common.js";
18
- import { NotFoundError, UsageError } from "../../core/errors.js";
19
- import { warn } from "../../core/warn.js";
20
- import { captureGuardedExecutionSource, GuardedExecutionSourceCollector } from "../../execution/guarded-source.js";
21
- const SECRET_TOKEN_RE = /\$\{secret:([A-Za-z0-9_./-]+)\}/g;
22
- const ENVIRONMENT_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
23
- const QUALIFIED_ENV_REF_RE = /^([^/]+)\/\/env\/(.+)$/;
24
- /** Freeze value-free descriptors in authored ref order. */
25
- export function freezeWorkflowEnvironment(refs, options) {
26
- const collector = options.collector ?? new GuardedExecutionSourceCollector();
27
- const logical = new Set();
28
- const physical = new Map();
29
- const out = [];
30
- for (const [precedence, inputRef] of refs.entries()) {
31
- const resolved = options.resolveRef(inputRef);
32
- const match = QUALIFIED_ENV_REF_RE.exec(resolved.ref);
33
- if (!match || match[1] !== resolved.bundle) {
34
- throw new UsageError(`Workflow env ref ${JSON.stringify(inputRef)} did not resolve to a canonical fully-qualified owner.`, "WORKFLOW_SOURCE_INVALID");
35
- }
36
- if (logical.has(resolved.ref)) {
37
- continue;
38
- }
39
- logical.add(resolved.ref);
40
- trackParentDirectories(collector, resolved.root, resolved.path);
41
- const retained = collector.capture(resolved.path, resolved.root, { authored: true });
42
- const captured = collector.bindIdentity(resolved.path, resolved.root, {
43
- ref: resolved.ref,
44
- bundle: resolved.bundle,
45
- adapter: resolved.adapter,
46
- file: retained.relativePath,
47
- hash: retained.sha256,
48
- });
49
- const physicalKey = `${captured.containmentPhysicalIdentity}\0${captured.physicalIdentity}`;
50
- const alias = physical.get(physicalKey);
51
- if (alias !== undefined && alias !== resolved.ref) {
52
- warn(`Workflow env ref ${resolved.ref} aliases the same physical environment source as ${alias} under a different logical owner. Using ${alias}.`);
53
- continue;
54
- }
55
- physical.set(physicalKey, resolved.ref);
56
- const parsed = dotenv.parse(Buffer.from(captured.bytesBase64, "base64"));
57
- const keys = sortedCodePoints(Object.keys(parsed));
58
- const secretNames = secretTokenNames(parsed);
59
- out.push(Object.freeze({
60
- kind: "env-ref",
61
- ref: resolved.ref,
62
- owner: Object.freeze({
63
- bundle: resolved.bundle,
64
- adapter: resolved.adapter,
65
- requestedRoot: captured.containmentRoot,
66
- realRoot: captured.containmentRealPath,
67
- rootPhysicalIdentity: captured.containmentPhysicalIdentity,
68
- requestedPath: captured.sourcePath,
69
- realPath: captured.realPath,
70
- relativePath: captured.relativePath,
71
- }),
72
- keys: Object.freeze(keys),
73
- secretNames: Object.freeze(secretNames),
74
- precedence,
75
- }));
76
- }
77
- return out;
78
- }
79
- /**
80
- * Materialize all current values atomically from frozen descriptors. Nothing
81
- * is returned until every owner/key/token check and every secret lookup has
82
- * succeeded.
83
- */
84
- export function materializeFrozenWorkflowEnvironment(descriptors, options = {}) {
85
- const values = {};
86
- const sensitive = new Set();
87
- const audits = [];
88
- const seenRefs = new Set();
89
- let priorPrecedence = -1;
90
- for (const descriptor of descriptors) {
91
- if (!descriptor || typeof descriptor !== "object")
92
- invalid("environment descriptor must be an object");
93
- if (descriptor.kind === "literal") {
94
- exactKeys(descriptor, ["kind", "name", "value"]);
95
- environmentName(descriptor.name);
96
- if (typeof descriptor.value !== "string")
97
- invalid("literal environment value must be a string");
98
- values[descriptor.name] = descriptor.value;
99
- if (descriptor.value)
100
- sensitive.add(descriptor.value);
101
- continue;
102
- }
103
- if (descriptor.kind === "pass-through") {
104
- exactKeys(descriptor, ["kind", "name"]);
105
- environmentName(descriptor.name);
106
- const value = options.readPassThrough ? options.readPassThrough(descriptor.name) : process.env[descriptor.name];
107
- if (value !== undefined) {
108
- values[descriptor.name] = value;
109
- if (value)
110
- sensitive.add(value);
111
- }
112
- continue;
113
- }
114
- if (descriptor.kind !== "env-ref")
115
- invalid("environment descriptor has an unsupported kind");
116
- exactKeys(descriptor, ["kind", "ref", "owner", "keys", "secretNames", "precedence"]);
117
- validateEnvRefDescriptor(descriptor);
118
- if (seenRefs.has(descriptor.ref))
119
- invalid(`duplicate environment ref ${descriptor.ref}`);
120
- if (descriptor.precedence <= priorPrecedence)
121
- invalid("env-ref precedence must be strictly increasing");
122
- seenRefs.add(descriptor.ref);
123
- priorPrecedence = descriptor.precedence;
124
- const source = options.readEnvFile ? options.readEnvFile(descriptor) : readFrozenEnvironmentFile(descriptor.owner);
125
- const parsed = dotenv.parse(typeof source === "string" ? source : Buffer.from(source));
126
- const keys = sortedCodePoints(Object.keys(parsed));
127
- const tokenNames = secretTokenNames(parsed);
128
- if (!sameStrings(keys, descriptor.keys)) {
129
- invalid(`environment ${descriptor.ref} key set changed after it was frozen`);
130
- }
131
- if (!sameStrings(tokenNames, descriptor.secretNames)) {
132
- invalid(`environment ${descriptor.ref} secret-token topology changed after it was frozen`);
133
- }
134
- const secretValues = new Map();
135
- for (const name of descriptor.secretNames) {
136
- const raw = options.readSecret
137
- ? options.readSecret({ name, descriptor })
138
- : readFrozenSecret(descriptor.owner, name);
139
- if (raw === undefined) {
140
- throw new NotFoundError(`Environment ${descriptor.ref} references missing secret ${name}; nothing was materialized.`, "FILE_NOT_FOUND");
141
- }
142
- const value = typeof raw === "string" ? raw : Buffer.from(raw).toString("utf8");
143
- secretValues.set(name, value);
144
- if (value)
145
- sensitive.add(value);
146
- }
147
- for (const [key, rawValue] of Object.entries(parsed)) {
148
- const resolved = rawValue.replace(SECRET_TOKEN_RE, (_token, name) => {
149
- const secret = secretValues.get(name);
150
- if (secret === undefined)
151
- invalid(`environment ${descriptor.ref} secret ${name} was not materialized`);
152
- return secret;
153
- });
154
- values[key] = resolved;
155
- if (resolved)
156
- sensitive.add(resolved);
157
- }
158
- audits.push(Object.freeze({
159
- eventType: "env_access",
160
- ref: descriptor.ref,
161
- keys: Object.freeze([...descriptor.keys]),
162
- secretNames: Object.freeze([...descriptor.secretNames]),
163
- }));
164
- }
165
- return {
166
- values,
167
- sensitiveValues: [...sensitive],
168
- audits,
169
- };
170
- }
171
- function validateEnvRefDescriptor(descriptor) {
172
- const match = QUALIFIED_ENV_REF_RE.exec(descriptor.ref);
173
- if (!match || match[1] !== descriptor.owner.bundle)
174
- invalid("env-ref has a noncanonical qualified owner");
175
- exactKeys(descriptor.owner, [
176
- "bundle",
177
- "adapter",
178
- "requestedRoot",
179
- "realRoot",
180
- "rootPhysicalIdentity",
181
- "requestedPath",
182
- "realPath",
183
- "relativePath",
184
- ]);
185
- for (const value of [descriptor.owner.bundle, descriptor.owner.adapter, descriptor.owner.rootPhysicalIdentity]) {
186
- if (typeof value !== "string" || !value)
187
- invalid("env-ref owner identity is incomplete");
188
- }
189
- for (const value of [
190
- descriptor.owner.requestedRoot,
191
- descriptor.owner.realRoot,
192
- descriptor.owner.requestedPath,
193
- descriptor.owner.realPath,
194
- ]) {
195
- if (typeof value !== "string" || !path.isAbsolute(value))
196
- invalid("env-ref owner paths must be absolute");
197
- }
198
- const relative = descriptor.owner.relativePath;
199
- if (typeof relative !== "string" ||
200
- !relative ||
201
- relative.includes("\\") ||
202
- relative.startsWith("../") ||
203
- path.isAbsolute(relative)) {
204
- invalid("env-ref owner relative path is noncanonical");
205
- }
206
- if (path.resolve(descriptor.owner.requestedRoot, relative) !== path.resolve(descriptor.owner.requestedPath)) {
207
- invalid("env-ref requested path does not match its owner");
208
- }
209
- if (path.resolve(descriptor.owner.realRoot, relative) !== path.resolve(descriptor.owner.realPath)) {
210
- invalid("env-ref real path does not match its owner");
211
- }
212
- sortedUnique(descriptor.keys, "env-ref keys", ENVIRONMENT_NAME_RE);
213
- sortedUnique(descriptor.secretNames, "env-ref secret names", /^[A-Za-z0-9_./-]+$/);
214
- if (!Number.isSafeInteger(descriptor.precedence) || descriptor.precedence < 0) {
215
- invalid("env-ref precedence is invalid");
216
- }
217
- }
218
- function readFrozenEnvironmentFile(owner) {
219
- const rootLstat = fs.lstatSync(owner.requestedRoot, { bigint: true });
220
- if (rootLstat.isSymbolicLink() || !rootLstat.isDirectory())
221
- invalid("environment owner root physical identity changed");
222
- const currentRealRoot = fs.realpathSync(owner.requestedRoot);
223
- if (currentRealRoot !== owner.realRoot ||
224
- physicalIdentity(currentRealRoot, rootLstat) !== owner.rootPhysicalIdentity) {
225
- invalid("environment owner root physical identity changed");
226
- }
227
- const captured = captureGuardedExecutionSource(owner.requestedPath, owner.requestedRoot, { authored: true });
228
- if (captured.realPath !== owner.realPath ||
229
- captured.relativePath !== owner.relativePath ||
230
- captured.containmentRealPath !== owner.realRoot ||
231
- captured.containmentPhysicalIdentity !== owner.rootPhysicalIdentity) {
232
- invalid("environment owner/path physical identity changed");
233
- }
234
- return Buffer.from(captured.bytesBase64, "base64");
235
- }
236
- function readFrozenSecret(owner, name) {
237
- const secretsRoot = path.join(owner.requestedRoot, "secrets");
238
- const secretPath = assetPathForName("secret", secretsRoot, name);
239
- if (!isWithin(secretPath, secretsRoot))
240
- invalid(`secret name ${name} escapes its frozen owner`);
241
- let descriptor;
242
- try {
243
- const noFollow = typeof fs.constants.O_NOFOLLOW === "number" ? fs.constants.O_NOFOLLOW : 0;
244
- descriptor = fs.openSync(secretPath, fs.constants.O_RDONLY | noFollow);
245
- const before = fs.fstatSync(descriptor, { bigint: true });
246
- if (!before.isFile())
247
- invalid(`secret ${name} is not a regular file`);
248
- const bytes = fs.readFileSync(descriptor);
249
- const after = fs.fstatSync(descriptor, { bigint: true });
250
- if (before.dev !== after.dev ||
251
- before.ino !== after.ino ||
252
- before.size !== after.size ||
253
- before.mtimeNs !== after.mtimeNs) {
254
- invalid(`secret ${name} changed while it was read`);
255
- }
256
- return bytes;
257
- }
258
- catch (cause) {
259
- if (cause.code === "ENOENT")
260
- return undefined;
261
- throw cause;
262
- }
263
- finally {
264
- if (descriptor !== undefined)
265
- fs.closeSync(descriptor);
266
- }
267
- }
268
- function trackParentDirectories(collector, rootInput, fileInput) {
269
- const root = path.resolve(rootInput);
270
- const parent = path.dirname(path.resolve(fileInput));
271
- const relative = path.relative(root, parent);
272
- if (relative.startsWith("..") || path.isAbsolute(relative))
273
- invalid("environment source escapes its owner root");
274
- let current = root;
275
- collector.trackDirectory(current, root);
276
- if (relative === "")
277
- return;
278
- for (const segment of relative.split(path.sep)) {
279
- current = path.join(current, segment);
280
- collector.trackDirectory(current, root);
281
- }
282
- }
283
- function secretTokenNames(values) {
284
- const names = new Set();
285
- for (const value of Object.values(values)) {
286
- for (const match of value.matchAll(SECRET_TOKEN_RE)) {
287
- const name = match[1];
288
- if (name)
289
- names.add(name);
290
- }
291
- }
292
- return sortedCodePoints([...names]);
293
- }
294
- function sortedCodePoints(values) {
295
- return values.sort((left, right) => (left < right ? -1 : left > right ? 1 : 0));
296
- }
297
- function sortedUnique(values, label, pattern) {
298
- if (!Array.isArray(values))
299
- invalid(`${label} must be an array`);
300
- let prior;
301
- for (const value of values) {
302
- if (typeof value !== "string" || !pattern.test(value) || value.includes(".."))
303
- invalid(`${label} is invalid`);
304
- if (prior !== undefined && prior >= value)
305
- invalid(`${label} must be sorted and unique`);
306
- prior = value;
307
- }
308
- }
309
- function sameStrings(left, right) {
310
- return left.length === right.length && left.every((value, index) => value === right[index]);
311
- }
312
- function exactKeys(value, keys) {
313
- const allowed = new Set(keys);
314
- for (const key of Object.keys(value))
315
- if (!allowed.has(key))
316
- invalid(`environment descriptor contains unknown key ${key}`);
317
- for (const key of keys)
318
- if (!Object.hasOwn(value, key))
319
- invalid(`environment descriptor is missing ${key}`);
320
- }
321
- function environmentName(value) {
322
- if (typeof value !== "string" || !ENVIRONMENT_NAME_RE.test(value))
323
- invalid("environment name is invalid");
324
- }
325
- function physicalIdentity(realPath, stat) {
326
- return stat.ino === 0n ? `path:${realPath}` : `inode:${stat.dev}:${stat.ino}`;
327
- }
328
- function invalid(message) {
329
- throw new UsageError(`Invalid frozen workflow environment: ${message}.`, "WORKFLOW_SOURCE_INVALID");
330
- }
@@ -1,153 +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
- import { createHash } from "node:crypto";
5
- import path from "node:path";
6
- import { parseBundleRef } from "../../core/asset/asset-ref.js";
7
- import { COMPOSITION_INVALID_MULTI_JOB_HINT, UsageError } from "../../core/errors.js";
8
- import { GuardedExecutionSourceCollector } from "../../execution/guarded-source.js";
9
- import { defaultMapConcurrency, workflowMaxConcurrency } from "../concurrency-policy.js";
10
- import { assertChildOutputReferences } from "../freeze/child-output-references.js";
11
- import { resolveWorkflowSourceV4 } from "../freeze/source-freeze.js";
12
- import { compileWorkflowPlan } from "./compile.js";
13
- import { canonicalJson } from "./plan-hash.js";
14
- import { decodeWorkflowPlanV4, WORKFLOW_IR_V5_VERSION, } from "./schema-v4.js";
15
- /** Compile, resolve, and freeze one executable plan without publishing any state. */
16
- export async function compileResolveFreezeWorkflowV4(asset, config, options = {}) {
17
- const sourceCollector = options.sourceCollector ?? new GuardedExecutionSourceCollector();
18
- const composition = options.composition ?? {
19
- depth: 0,
20
- refPath: [asset.ref],
21
- budget: { embeddedBytes: 0 },
22
- };
23
- // Injected rather than imported (A-N7/step-values.ts's `ChildCompositionContext`
24
- // doc): `targets/child-workflow.ts` is downstream of this module (via
25
- // `resolve-steps.ts` <- `source-freeze.ts`, imported directly above — the
26
- // `source-freeze-v4.ts` shim P4 deleted used to sit on this edge), so it
27
- // cannot import `compileResolveFreezeWorkflowV4` directly without closing
28
- // a static import cycle
29
- // (tests/architecture/import-cycle-ratchet.test.ts, shrink-only, empty
30
- // baseline). This closure is this function's own recursive call, passed
31
- // down as a plain value through `ResolutionContext.freezeChild`.
32
- const freezeChild = async (request) => {
33
- const child = await compileResolveFreezeWorkflowV4(request.asset, config, {
34
- sourceCollector: request.sourceCollector,
35
- composition: request.composition,
36
- });
37
- return { plan: child.plan, sourceCollector: child.sourceCollector };
38
- };
39
- const workflowSource = captureWorkflowSource(asset, sourceCollector);
40
- const resolved = await resolveWorkflowSourceV4(asset, workflowSource, config, sourceCollector, composition, freezeChild);
41
- const compiled = compileWorkflowPlan(resolved.sourceIr, asset.title, resolved.units);
42
- if (!compiled.ok) {
43
- // P4-N2's mapping (docs/plans/specs/p4-deletions-closeout.md §3.3.4)
44
- // applies here too, though `resolved.sourceIr` is already guaranteed
45
- // exactly one job by this point (source-freeze.ts's own compile already
46
- // succeeded), so this compiler's own errors never carry the
47
- // multi-job-unsupported code — the arm always resolves to
48
- // WORKFLOW_SOURCE_INVALID.
49
- const isMultiJob = compiled.errors.length === 1 && compiled.errors[0]?.code === "multi-job-unsupported";
50
- const code = isMultiJob ? "COMPOSITION_INVALID" : "WORKFLOW_SOURCE_INVALID";
51
- throw new UsageError(compiled.errors.map((error) => `${asset.path}:${error.line}: ${error.message}`).join("\n"), code, isMultiJob ? COMPOSITION_INVALID_MULTI_JOB_HINT : undefined);
52
- }
53
- const steps = compiled.plan.steps.map((step) => {
54
- const frozenJudge = step.gate.criteria.length === 0 ? null : (resolved.judges.get(step.stepId) ?? null);
55
- const gate = Object.freeze({ ...step.gate, maxLoops: step.gate.maxLoops ?? 1, frozenJudge });
56
- if (!step.root) {
57
- const { root: _root, ...withoutRoot } = step;
58
- return Object.freeze({ ...withoutRoot, gate });
59
- }
60
- const frozen = resolved.units.get(step.stepId);
61
- if (!frozen)
62
- throw new Error(`resolved workflow target missing for step ${step.stepId}`);
63
- const root = step.root.kind === "map"
64
- ? {
65
- ...step.root,
66
- concurrency: step.root.concurrency ?? defaultMapConcurrency(config.workflow?.defaultMapConcurrency),
67
- template: freezeResolvedUnit(step.root.template, frozen),
68
- }
69
- : freezeResolvedUnit(step.root, frozen);
70
- return Object.freeze({ ...step, root, gate });
71
- });
72
- // P3b, spec §4.4: after every step's target is resolved (so every embedded
73
- // child plan is available) and before the plan object is assembled.
74
- assertChildOutputReferences(steps);
75
- const sourceReadSet = sourceCollector
76
- .snapshot()
77
- .sources.filter((source) => Boolean(source.identity))
78
- .map((source) => sourceSnapshot(source));
79
- const plan = decodeWorkflowPlanV4({
80
- irVersion: WORKFLOW_IR_V5_VERSION,
81
- title: compiled.plan.title,
82
- ...(compiled.plan.params ? { params: compiled.plan.params } : {}),
83
- ...(compiled.plan.paramSchemas ? { paramSchemas: compiled.plan.paramSchemas } : {}),
84
- ...(compiled.plan.budget ? { budget: compiled.plan.budget } : {}),
85
- execution: { maxConcurrency: workflowMaxConcurrency(config.workflow?.maxConcurrency) },
86
- sourceReadSet,
87
- steps,
88
- ...(compiled.plan.outputs ? { outputs: compiled.plan.outputs } : {}),
89
- });
90
- return Object.freeze({
91
- plan,
92
- warnings: compiled.warnings,
93
- ...(resolved.engineAnnouncement ? { engineAnnouncement: resolved.engineAnnouncement } : {}),
94
- sourceCollector,
95
- });
96
- }
97
- function freezeResolvedUnit(unit, resolved) {
98
- const frozenTarget = resolved.target.kind === "shell"
99
- ? Object.freeze({
100
- ...resolved.target,
101
- contentHash: createHash("sha256")
102
- .update("akm.workflow.shell.v1\0")
103
- .update(canonicalJson({
104
- exec: resolved.target.exec,
105
- environment: resolved.environment,
106
- cwdIdentity: resolved.target.cwdIdentity,
107
- }))
108
- .digest("hex"),
109
- })
110
- : resolved.target;
111
- const { exec: _exec, ...common } = unit;
112
- return Object.freeze({
113
- ...common,
114
- isolation: common.isolation ?? "none",
115
- frozenTarget,
116
- environment: Object.freeze([...resolved.environment]),
117
- });
118
- }
119
- function captureWorkflowSource(asset, collector) {
120
- const root = path.resolve(asset.sourcePath);
121
- const file = path.resolve(asset.path);
122
- collector.trackDirectory(root, root);
123
- const parent = path.dirname(file);
124
- const relativeParent = path.relative(root, parent);
125
- if (relativeParent.startsWith("..") || path.isAbsolute(relativeParent)) {
126
- throw new UsageError(`${file} resolves outside its workflow source root.`, "PATH_ESCAPE_VIOLATION");
127
- }
128
- let current = root;
129
- for (const segment of relativeParent === "" ? [] : relativeParent.split(path.sep)) {
130
- current = path.join(current, segment);
131
- collector.trackDirectory(current, root);
132
- }
133
- const captured = collector.capture(file, root, { authored: true });
134
- const parsed = parseBundleRef(asset.ref);
135
- if (!parsed.bundle || !asset.adapterId) {
136
- throw new UsageError(`Workflow ${asset.ref} has no fully-qualified bundle/adapter owner for durable publication.`, "WORKFLOW_SOURCE_INVALID");
137
- }
138
- return collector.bindIdentity(file, root, {
139
- ref: asset.ref,
140
- bundle: parsed.bundle,
141
- adapter: asset.adapterId,
142
- file: captured.relativePath,
143
- hash: captured.sha256,
144
- });
145
- }
146
- function sourceSnapshot(source) {
147
- return Object.freeze({
148
- identity: source.identity,
149
- containmentPhysicalIdentity: source.containmentPhysicalIdentity,
150
- physicalIdentity: source.physicalIdentity,
151
- size: source.size,
152
- });
153
- }