akm-cli 0.9.1 → 0.9.2-alpha.2

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 (350) hide show
  1. package/CHANGELOG.md +103 -28
  2. package/README.md +3 -1
  3. package/SECURITY.md +1 -1
  4. package/STABILITY.md +1 -1
  5. package/dist/akm +2 -2
  6. package/dist/akm-migrate +2 -2
  7. package/dist/assets/hints/cli-hints-full.md +14 -9
  8. package/dist/assets/improve-strategies/proactive-maintenance.json +1 -1
  9. package/dist/assets/improve-strategies/reflect-distill.json +1 -1
  10. package/dist/assets/models.json +35 -0
  11. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +3 -4
  12. package/dist/assets/stash-skeleton/facts/conventions/organization.md +1 -3
  13. package/dist/assets/tasks/core/extract.yml +6 -5
  14. package/dist/assets/tasks/core/improve.yml +6 -5
  15. package/dist/assets/tasks/core/index-refresh.yml +6 -5
  16. package/dist/assets/tasks/core/sync.yml +6 -5
  17. package/dist/assets/tasks/core/version-check.yml +6 -5
  18. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +6 -5
  19. package/dist/assets/tasks/improve/akm-improve-catchup.yml +6 -5
  20. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +6 -5
  21. package/dist/assets/tasks/improve/akm-improve-frequent.yml +6 -5
  22. package/dist/assets/tasks/improve/akm-improve-nightly.yml +6 -5
  23. package/dist/cli/confirm.js +2 -2
  24. package/dist/cli/parse-args.js +3 -24
  25. package/dist/cli/retired-commands.js +1 -1
  26. package/dist/cli/shared.js +2 -2
  27. package/dist/cli.js +11 -9
  28. package/dist/commands/agent/agent-dispatch.js +55 -89
  29. package/dist/commands/agent/contribute-cli.js +12 -45
  30. package/dist/commands/command/builtin-action.js +32 -0
  31. package/dist/commands/command/command-cli.js +99 -0
  32. package/dist/commands/command/command-execution.js +308 -0
  33. package/dist/commands/command/execution-source-loader.js +176 -0
  34. package/dist/commands/command/portable-template.js +60 -0
  35. package/dist/commands/config-cli.js +10 -4
  36. package/dist/commands/env/env.js +4 -2
  37. package/dist/commands/feedback-cli.js +1 -1
  38. package/dist/commands/health/checks.js +241 -29
  39. package/dist/commands/health/html-report.js +0 -14
  40. package/dist/commands/health/report-view-model.js +0 -1
  41. package/dist/commands/health/surfaces.js +6 -7
  42. package/dist/commands/health/types.js +0 -2
  43. package/dist/commands/health.js +63 -18
  44. package/dist/commands/improve/collapse-detector.js +5 -6
  45. package/dist/commands/improve/consolidate.js +251 -214
  46. package/dist/commands/improve/distill/promote-memory.js +71 -34
  47. package/dist/commands/improve/distill/quality-gate.js +17 -5
  48. package/dist/commands/improve/distill.js +232 -155
  49. package/dist/commands/improve/eligibility.js +112 -79
  50. package/dist/commands/improve/execution.js +57 -0
  51. package/dist/commands/improve/extract-cli.js +5 -5
  52. package/dist/commands/improve/extract-prompt.js +64 -22
  53. package/dist/commands/improve/extract.js +608 -360
  54. package/dist/commands/improve/improve-strategies.js +43 -14
  55. package/dist/commands/improve/improve.js +249 -29
  56. package/dist/commands/improve/loop-stages.js +11 -17
  57. package/dist/commands/improve/memory/memory-contradiction-detect.js +90 -66
  58. package/dist/commands/improve/outcome-loop.js +22 -38
  59. package/dist/commands/improve/planner.js +134 -0
  60. package/dist/commands/improve/preparation.js +730 -409
  61. package/dist/commands/improve/reflect.js +386 -223
  62. package/dist/commands/improve/run-context.js +3 -4
  63. package/dist/commands/improve/salience.js +6 -58
  64. package/dist/commands/improve/session-asset.js +12 -12
  65. package/dist/commands/lint/index.js +101 -29
  66. package/dist/commands/migrate-cli.js +11 -69
  67. package/dist/commands/migration-tool.js +6 -9
  68. package/dist/commands/models-cli.js +27 -0
  69. package/dist/commands/proposal/drain.js +258 -186
  70. package/dist/commands/proposal/proposal-cli.js +32 -10
  71. package/dist/commands/proposal/proposal.js +2 -5
  72. package/dist/commands/proposal/propose.js +192 -172
  73. package/dist/commands/proposal/repository.js +54 -91
  74. package/dist/commands/proposal/validators/proposal-validators.js +9 -7
  75. package/dist/commands/read/curate.js +53 -22
  76. package/dist/commands/read/registry-search.js +25 -9
  77. package/dist/commands/read/remember-cli.js +14 -2
  78. package/dist/commands/read/search.js +10 -4
  79. package/dist/commands/read/show.js +139 -153
  80. package/dist/commands/registry-cli.js +16 -7
  81. package/dist/commands/remember.js +33 -18
  82. package/dist/commands/sources/add-cli.js +19 -178
  83. package/dist/commands/sources/bundle-cli.js +15 -3
  84. package/dist/commands/sources/dangerous-env-audit.js +135 -0
  85. package/dist/commands/sources/info.js +2 -1
  86. package/dist/commands/sources/installed-stashes.js +901 -177
  87. package/dist/commands/sources/schema-repair.js +174 -95
  88. package/dist/commands/sources/self-update.js +30 -74
  89. package/dist/commands/sources/source-add.js +3 -5
  90. package/dist/commands/sources/sources-cli.js +2 -15
  91. package/dist/commands/sources/update-transaction.js +220 -0
  92. package/dist/commands/tasks/tasks-cli.js +3 -3
  93. package/dist/commands/tasks/tasks.js +736 -317
  94. package/dist/commands/workflow-cli.js +2 -2
  95. package/dist/core/adapter/adapters/agent-skills-adapter.js +3 -0
  96. package/dist/core/adapter/adapters/akm-adapter.js +85 -35
  97. package/dist/core/adapter/adapters/akm-lint.js +54 -39
  98. package/dist/core/adapter/adapters/akm-metadata.js +45 -45
  99. package/dist/core/adapter/adapters/akm-task-adapter.js +32 -49
  100. package/dist/core/adapter/adapters/akm-workflow-adapter.js +38 -23
  101. package/dist/core/adapter/adapters/dotenv-adapter.js +30 -1
  102. package/dist/core/adapter/adapters/generic-files-adapter.js +11 -0
  103. package/dist/core/adapter/adapters/index.js +0 -9
  104. package/dist/core/adapter/adapters/llm-wiki-adapter.js +4 -0
  105. package/dist/core/adapter/adapters/okf-adapter.js +4 -0
  106. package/dist/core/adapter/adapters/opencode-adapter.js +5 -8
  107. package/dist/core/adapter/adapters/tool-dir-shared.js +63 -6
  108. package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -0
  109. package/dist/core/adapter/execution-source.js +308 -0
  110. package/dist/core/adapter/recognize-match.js +36 -13
  111. package/dist/core/adapter/registry.js +0 -9
  112. package/dist/core/asset/stash-meta.js +94 -4
  113. package/dist/core/common.js +6 -11
  114. package/dist/core/config/config-io.js +3 -3
  115. package/dist/core/config/config-schema.js +18 -40
  116. package/dist/core/config/config-sources.js +11 -21
  117. package/dist/core/config/config-walker.js +31 -13
  118. package/dist/core/config/config.js +23 -26
  119. package/dist/core/config/schema/engines.js +8 -7
  120. package/dist/core/config/schema/improve-processes.js +29 -5
  121. package/dist/core/config/schema/index-config.js +0 -27
  122. package/dist/core/config/schema/primitives.js +1 -23
  123. package/dist/core/config/schema/sources-bundles.js +13 -16
  124. package/dist/core/errors.js +2 -0
  125. package/dist/core/events.js +68 -32
  126. package/dist/core/extra-params.js +1 -0
  127. package/dist/core/improve-result.js +315 -0
  128. package/dist/core/lesson-lint.js +0 -6
  129. package/dist/core/maintenance-barrier.js +4 -4
  130. package/dist/core/network-policy.js +152 -0
  131. package/dist/core/paths.js +1 -1
  132. package/dist/core/recognition-util.js +4 -4
  133. package/dist/core/registry-url.js +456 -0
  134. package/dist/core/state/migrations.js +161 -47
  135. package/dist/core/state-db.js +453 -80
  136. package/dist/core/system-error.js +32 -0
  137. package/dist/core/time.js +2 -12
  138. package/dist/core/write-source.js +0 -18
  139. package/dist/execution/directory-identity.js +52 -0
  140. package/dist/execution/executable-identity.js +107 -0
  141. package/dist/execution/guarded-source.js +398 -0
  142. package/dist/execution/json.js +95 -0
  143. package/dist/{commands/health/types-session-log.js → execution/limits.js} +2 -1
  144. package/dist/execution/record.js +55 -0
  145. package/dist/execution/resolved-request.js +730 -0
  146. package/dist/execution/source.js +320 -0
  147. package/dist/indexer/bundle-identity-guard.js +5 -4
  148. package/dist/indexer/db/graph-db.js +33 -0
  149. package/dist/indexer/graph/graph-boost.js +3 -4
  150. package/dist/indexer/graph/graph-extraction.js +562 -373
  151. package/dist/indexer/index-written-assets.js +78 -39
  152. package/dist/indexer/indexer.js +471 -432
  153. package/dist/indexer/installations.js +6 -0
  154. package/dist/indexer/lookup/adapter-concept-owner.js +283 -0
  155. package/dist/indexer/materialize-embeddings.js +155 -0
  156. package/dist/indexer/passes/memory-inference.js +227 -174
  157. package/dist/indexer/passes/metadata.js +263 -118
  158. package/dist/indexer/scan/doc-to-entry.js +7 -10
  159. package/dist/indexer/scan/drain-dir.js +51 -23
  160. package/dist/indexer/search/db-search.js +156 -50
  161. package/dist/indexer/search/fts-query.js +40 -40
  162. package/dist/indexer/search/ranking.js +36 -1
  163. package/dist/indexer/search/search-attribution.js +3 -1
  164. package/dist/indexer/search/search-fields.js +23 -14
  165. package/dist/indexer/search/search-hit-enrichers.js +1 -1
  166. package/dist/indexer/search/search-source.js +7 -16
  167. package/dist/indexer/search/semantic-status.js +10 -1
  168. package/dist/indexer/usage/show-usage.js +105 -0
  169. package/dist/indexer/usage/usage-events.js +7 -2
  170. package/dist/indexer/walk/matchers.js +40 -10
  171. package/dist/indexer/walk/path-resolver.js +5 -2
  172. package/dist/indexer/walk/walker.js +20 -2
  173. package/dist/integrations/agent/builder-shared.js +3 -6
  174. package/dist/integrations/agent/conversation-fallback.js +16 -0
  175. package/dist/integrations/agent/engine-resolution.js +87 -87
  176. package/dist/integrations/agent/execution-cascade.js +566 -0
  177. package/dist/integrations/agent/execution-definitions.js +211 -0
  178. package/dist/integrations/agent/execution-lowering.js +811 -0
  179. package/dist/integrations/agent/execution-preparation.js +67 -0
  180. package/dist/integrations/agent/index.js +0 -2
  181. package/dist/integrations/agent/inline-execution.js +74 -0
  182. package/dist/integrations/agent/model-map.js +515 -0
  183. package/dist/integrations/agent/persona-fallback.js +30 -0
  184. package/dist/integrations/agent/request-lowering.js +186 -0
  185. package/dist/integrations/agent/runner-dispatch.js +230 -37
  186. package/dist/integrations/agent/runner.js +12 -83
  187. package/dist/integrations/harnesses/aider/agent-builder.js +8 -0
  188. package/dist/integrations/harnesses/aider/index.js +0 -1
  189. package/dist/integrations/harnesses/amazonq/agent-builder.js +8 -0
  190. package/dist/integrations/harnesses/amazonq/index.js +0 -1
  191. package/dist/integrations/harnesses/claude/agent-builder.js +14 -1
  192. package/dist/integrations/harnesses/claude/index.js +1 -5
  193. package/dist/integrations/harnesses/claude/session-log.js +3 -33
  194. package/dist/integrations/harnesses/codex/agent-builder.js +8 -0
  195. package/dist/integrations/harnesses/codex/index.js +0 -1
  196. package/dist/integrations/harnesses/copilot/agent-builder.js +8 -0
  197. package/dist/integrations/harnesses/copilot/index.js +0 -1
  198. package/dist/integrations/harnesses/gemini/agent-builder.js +8 -0
  199. package/dist/integrations/harnesses/gemini/index.js +0 -1
  200. package/dist/integrations/harnesses/index.js +4 -44
  201. package/dist/integrations/harnesses/opencode/agent-builder.js +16 -9
  202. package/dist/integrations/harnesses/opencode/index.js +0 -2
  203. package/dist/integrations/harnesses/opencode/session-log.js +14 -204
  204. package/dist/integrations/harnesses/opencode-sdk/harness.js +12 -1
  205. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +40 -42
  206. package/dist/integrations/harnesses/openhands/agent-builder.js +8 -0
  207. package/dist/integrations/harnesses/openhands/index.js +0 -1
  208. package/dist/integrations/harnesses/pi/agent-builder.js +8 -0
  209. package/dist/integrations/harnesses/pi/index.js +0 -1
  210. package/dist/integrations/harnesses/shared.js +0 -1
  211. package/dist/integrations/harnesses/types.js +1 -3
  212. package/dist/integrations/lockfile.js +82 -79
  213. package/dist/integrations/session-logs/index.js +6 -17
  214. package/dist/integrations/session-logs/provider-base.js +1 -29
  215. package/dist/llm/client.js +10 -5
  216. package/dist/llm/embedder.js +6 -7
  217. package/dist/llm/embedders/local.js +37 -88
  218. package/dist/llm/embedders/types.js +1 -1
  219. package/dist/llm/graph-extract.js +75 -50
  220. package/dist/llm/index-passes.js +43 -5
  221. package/dist/llm/memory-infer.js +8 -6
  222. package/dist/llm/metadata-enhance.js +5 -3
  223. package/dist/llm/structured-call.js +122 -25
  224. package/dist/output/format-exempt.js +1 -1
  225. package/dist/output/render-registry.js +0 -16
  226. package/dist/output/renderers.js +12 -7
  227. package/dist/output/shapes/curate.js +1 -0
  228. package/dist/output/shapes/helpers.js +10 -2
  229. package/dist/output/shapes/passthrough.js +2 -0
  230. package/dist/output/text/command-format.js +31 -33
  231. package/dist/output/text/health-format.js +1 -29
  232. package/dist/output/text/migrate.js +6 -56
  233. package/dist/output/text/proposal-format.js +16 -1
  234. package/dist/output/text/workflow-format.js +16 -0
  235. package/dist/registry/network.js +279 -0
  236. package/dist/registry/pinned-request-helper.js +247 -0
  237. package/dist/registry/pinned-transport.js +717 -0
  238. package/dist/registry/providers/skills-sh.js +18 -6
  239. package/dist/registry/providers/static-index.js +20 -7
  240. package/dist/registry/resolve.js +53 -28
  241. package/dist/scripts/akm-migrate-node.js +19334 -52269
  242. package/dist/scripts/akm-migrate.js +19270 -51612
  243. package/dist/setup/registry-stash-loader.js +64 -20
  244. package/dist/setup/semantic-assets.js +9 -34
  245. package/dist/setup/setup.js +12 -30
  246. package/dist/setup/source-identity.js +17 -0
  247. package/dist/setup/steps/sources.js +36 -15
  248. package/dist/setup/steps/tasks.js +39 -11
  249. package/dist/sources/providers/git-provider.js +3 -3
  250. package/dist/sources/providers/npm.js +2 -2
  251. package/dist/sources/providers/provider-utils.js +4 -3
  252. package/dist/sources/providers/website.js +11 -7
  253. package/dist/sources/snapshot-fetchers/host-guard.js +9 -136
  254. package/dist/sources/snapshot-fetchers/website-ingest.js +25 -109
  255. package/dist/sources/website-url.js +73 -0
  256. package/dist/storage/engines/sqlite-migrations.js +81 -26
  257. package/dist/storage/managed-db.js +27 -24
  258. package/dist/storage/repositories/events-repository.js +3 -0
  259. package/dist/storage/repositories/index-connection.js +42 -10
  260. package/dist/storage/repositories/index-entries-repository.js +203 -229
  261. package/dist/storage/repositories/index-entry-mapper.js +8 -12
  262. package/dist/storage/repositories/index-entry-schema.js +255 -0
  263. package/dist/storage/repositories/index-fts-repository.js +64 -71
  264. package/dist/storage/repositories/index-llm-cache-repository.js +8 -13
  265. package/dist/storage/repositories/index-meta-repository.js +0 -11
  266. package/dist/storage/repositories/index-schema.js +74 -350
  267. package/dist/storage/repositories/index-utility-repository.js +12 -17
  268. package/dist/storage/repositories/index-vec-repository.js +56 -7
  269. package/dist/storage/repositories/proposals-repository.js +4 -127
  270. package/dist/storage/repositories/registry-cache.js +2 -1
  271. package/dist/storage/repositories/task-history-repository.js +20 -40
  272. package/dist/storage/repositories/workflow-runs-repository.js +228 -129
  273. package/dist/storage/sqlite-read-snapshot.js +148 -0
  274. package/dist/tasks/backends/cron.js +170 -42
  275. package/dist/tasks/backends/index.js +1 -1
  276. package/dist/tasks/backends/launchd.js +787 -202
  277. package/dist/tasks/backends/schtasks.js +282 -83
  278. package/dist/tasks/embedded.js +7 -7
  279. package/dist/tasks/frozen-script.js +50 -0
  280. package/dist/tasks/resolve-akm-bin.js +5 -1
  281. package/dist/tasks/runner.js +239 -251
  282. package/dist/tasks/runtime-v3.js +281 -0
  283. package/dist/tasks/scheduler-binding.js +272 -0
  284. package/dist/tasks/scheduler-invocation.js +57 -43
  285. package/dist/tasks/scheduler-sync.js +654 -0
  286. package/dist/tasks/source-v3.js +752 -0
  287. package/dist/tasks/standalone-script-entry.js +5 -0
  288. package/dist/tasks/task-id.js +29 -0
  289. package/dist/workflows/authoring/authoring.js +15 -32
  290. package/dist/workflows/exec/dispatch-redaction.js +14 -8
  291. package/dist/workflows/exec/exec-unit.js +7 -28
  292. package/dist/workflows/exec/frozen-judge.js +57 -89
  293. package/dist/workflows/exec/lowering-notices.js +23 -0
  294. package/dist/workflows/exec/native-executor.js +301 -458
  295. package/dist/workflows/exec/param-secrets.js +4 -3
  296. package/dist/workflows/exec/run-workflow.js +26 -32
  297. package/dist/workflows/exec/step-work.js +105 -109
  298. package/dist/workflows/exec/unit-dispatch.js +103 -27
  299. package/dist/workflows/exec/unit-writer.js +3 -3
  300. package/dist/workflows/exec/worktree.js +2 -2
  301. package/dist/workflows/ir/compile.js +86 -72
  302. package/dist/workflows/ir/environment-v4.js +328 -0
  303. package/dist/workflows/ir/freeze-v4.js +122 -0
  304. package/dist/workflows/ir/plan-hash.js +13 -7
  305. package/dist/workflows/ir/schema-v4.js +525 -0
  306. package/dist/workflows/ir/schema.js +25 -284
  307. package/dist/workflows/ir/source-freeze-v4.js +506 -0
  308. package/dist/workflows/parser.js +27 -24
  309. package/dist/workflows/program/schema.js +1 -2
  310. package/dist/workflows/renderer.js +42 -29
  311. package/dist/workflows/resource-limits.js +4 -5
  312. package/dist/workflows/runtime/agent-identity.js +11 -13
  313. package/dist/workflows/runtime/plan-classifier.js +8 -8
  314. package/dist/workflows/runtime/runs.js +27 -43
  315. package/dist/workflows/runtime/workflow-asset-loader.js +45 -205
  316. package/dist/workflows/source-files.js +373 -0
  317. package/dist/workflows/source-ir/compile.js +196 -0
  318. package/dist/workflows/source-ir/github-yaml.js +577 -0
  319. package/dist/workflows/source-ir/ordering.js +38 -0
  320. package/dist/workflows/source-ir/program.js +50 -0
  321. package/dist/workflows/source-ir/result.js +26 -0
  322. package/dist/workflows/source-ir/schema.js +772 -0
  323. package/dist/workflows/source-ir/semantics.js +242 -0
  324. package/dist/workflows/source-ir/uses.js +14 -0
  325. package/docs/README.md +2 -0
  326. package/docs/migration/README.md +3 -1
  327. package/docs/migration/release-notes/0.9.2.md +55 -0
  328. package/docs/migration/release-notes/README.md +5 -0
  329. package/docs/migration/v0.8-to-v0.9.md +76 -1077
  330. package/docs/migration/v0.9.0-troubleshooting.md +104 -516
  331. package/docs/migration/v0.9.1-to-v0.9.2.md +150 -0
  332. package/docs/reference/README.md +1 -0
  333. package/docs/reference/cli.md +230 -98
  334. package/docs/reference/configuration.md +159 -36
  335. package/docs/reference/data-and-telemetry.md +19 -1
  336. package/docs/reference/supported-formats.md +23 -3
  337. package/docs/reference/tasks.md +182 -0
  338. package/docs/reference/workflow-schema.md +91 -40
  339. package/docs/reference/workflows.md +33 -6
  340. package/package.json +10 -6
  341. package/schemas/akm-config.json +372 -224
  342. package/schemas/akm-task.json +324 -80
  343. package/schemas/akm-workflow.json +6 -9
  344. package/dist/core/migration-operation.js +0 -75
  345. package/dist/integrations/agent/model-aliases.js +0 -74
  346. package/dist/tasks/parser.js +0 -380
  347. package/dist/tasks/schema.js +0 -123
  348. package/dist/tasks/validator.js +0 -80
  349. package/dist/workflows/ir/freeze.js +0 -320
  350. package/dist/workflows/runtime/document-cache.js +0 -13
@@ -0,0 +1,506 @@
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 { spawnSync } from "node:child_process";
5
+ import { createHash } from "node:crypto";
6
+ import fs from "node:fs";
7
+ import path from "node:path";
8
+ import { prepareCommandInvocation } from "../../commands/command/command-execution.js";
9
+ import { loadAdapterExecutionSource } from "../../commands/command/execution-source-loader.js";
10
+ import { makeBundleRef, parseBundleRef } from "../../core/asset/asset-ref.js";
11
+ import { parseEnvRef } from "../../core/env-secret-ref.js";
12
+ import { ConfigError, UsageError } from "../../core/errors.js";
13
+ import { captureFrozenDirectoryIdentity } from "../../execution/directory-identity.js";
14
+ import { freezeExecutableIdentity } from "../../execution/executable-identity.js";
15
+ import { canonicalResolvedExecutionRequest, decodeResolvedExecutionRequest, } from "../../execution/resolved-request.js";
16
+ import { deriveInstallations } from "../../indexer/installations.js";
17
+ import { resolveSourceEntries } from "../../indexer/search/search-source.js";
18
+ import { fallbackAnnouncement } from "../../integrations/agent/engine-fallback.js";
19
+ import { requireAuthorizedExecutionPlan } from "../../integrations/agent/execution-cascade.js";
20
+ import { lowerResolvedExecutionRequest } from "../../integrations/agent/execution-lowering.js";
21
+ import { prepareInlineExecution } from "../../integrations/agent/inline-execution.js";
22
+ import { resolveAssetPath } from "../../sources/resolve.js";
23
+ import { prepareTaskV3Execution, } from "../../tasks/runtime-v3.js";
24
+ import { parseTaskV3Yaml } from "../../tasks/source-v3.js";
25
+ import { defaultLlmEngineConcurrency } from "../concurrency-policy.js";
26
+ import { DEFAULT_EXEC_TIMEOUT_MS } from "../resource-limits.js";
27
+ import { compileWorkflowSource } from "../source-ir/compile.js";
28
+ import { sourceStepProgramUnit, sourceStepRef, workflowShellCommand } from "../source-ir/program.js";
29
+ import { classifyWorkflowStepUses } from "../source-ir/semantics.js";
30
+ import { freezeWorkflowEnvironment } from "./environment-v4.js";
31
+ /** Resolve every authored target through the shared command/task authorities before v4 publication. */
32
+ export async function resolveWorkflowSourceV4(asset, workflowSource, config, collector) {
33
+ const compiled = compileWorkflowSource(workflowSource.content, { path: asset.path, workspaceRoot: asset.sourcePath });
34
+ if (!compiled.ok) {
35
+ throw new UsageError(`Workflow source cannot be frozen: ${compiled.errors.map((error) => error.message).join("; ")}`, "INVALID_FLAG_VALUE");
36
+ }
37
+ if (compiled.ir.jobs.length !== 1) {
38
+ throw new UsageError("Multi-job workflow cannot execute until job boundaries and needs have a durable runtime representation.", "INVALID_FLAG_VALUE");
39
+ }
40
+ const context = { asset, config, collector, sourceIr: compiled.ir };
41
+ const units = new Map();
42
+ const judges = new Map();
43
+ let engineAnnouncement;
44
+ const sourceSteps = compiled.ir.jobs[0]?.steps ?? [];
45
+ for (const sourceStep of sourceSteps) {
46
+ if (!sourceStep.route) {
47
+ const resolved = await resolveStep(sourceStep, context);
48
+ units.set(sourceStep.id, Object.freeze(resolved));
49
+ engineAnnouncement ??= resolved.engineAnnouncement;
50
+ }
51
+ if (sourceStep.gate?.rubric?.trim()) {
52
+ const judge = resolveJudge(sourceStep, context);
53
+ if (judge.target.kind !== "command")
54
+ throw new Error(`workflow judge ${sourceStep.id} did not resolve to a command target`);
55
+ judges.set(sourceStep.id, judge.target);
56
+ engineAnnouncement ??= judge.engineAnnouncement;
57
+ }
58
+ }
59
+ return Object.freeze({
60
+ sourceIr: compiled.ir,
61
+ units,
62
+ judges,
63
+ ...(engineAnnouncement ? { engineAnnouncement } : {}),
64
+ });
65
+ }
66
+ async function resolveStep(source, context) {
67
+ const baseUnit = sourceStepProgramUnit(source);
68
+ if (source.exec || source.run !== undefined)
69
+ return directShell(source, baseUnit, context);
70
+ if (!source.uses)
71
+ return inlineDispatch(source, baseUnit, context);
72
+ const target = classifyWorkflowStepUses(source.uses);
73
+ if (target.kind === "task")
74
+ return taskDispatch(source, baseUnit, target.ref, context);
75
+ if (target.kind === "script")
76
+ return directScript(source, baseUnit, target.ref, context);
77
+ if (target.kind === "command" || target.kind === "builtin-command") {
78
+ const action = target.kind === "builtin-command"
79
+ ? source.with
80
+ : { ref: qualifyRef(target.ref, "commands", context.asset, context.config) };
81
+ return commandDispatch(source, baseUnit, action, context);
82
+ }
83
+ throw new UsageError(`Workflow target ${source.uses} is not executable in 0.9.2.`, "INVALID_FLAG_VALUE");
84
+ }
85
+ async function commandDispatch(source, baseUnit, action, context) {
86
+ const prepared = await prepareCommandInvocation({
87
+ action,
88
+ config: context.config,
89
+ invocationKind: "workflow",
90
+ ...(context.sourceIr.defaults
91
+ ? { invocationDefaults: executionUnitValues(context.sourceIr.defaults, context.asset.sourcePath) }
92
+ : {}),
93
+ ...(source.commandMode === "literal" ? { inlineContentMode: "literal" } : {}),
94
+ current: executionValues(source, context.asset.sourcePath),
95
+ sourceLoader: (ref, kind) => guardedExecutionSource(ref, kind, context),
96
+ });
97
+ return commandResult(source, baseUnit, prepared, context);
98
+ }
99
+ function inlineDispatch(source, baseUnit, context) {
100
+ const content = source.instructions ?? `Execute workflow step ${source.id}.`;
101
+ const prepared = prepareInlineExecution({
102
+ content,
103
+ config: context.config,
104
+ invocationKind: "workflow",
105
+ ...(context.sourceIr.defaults
106
+ ? { invocationDefaults: executionUnitValues(context.sourceIr.defaults, context.asset.sourcePath) }
107
+ : {}),
108
+ current: executionValues(source, context.asset.sourcePath),
109
+ });
110
+ return commandResult(source, baseUnit, prepared, context);
111
+ }
112
+ function resolveJudge(source, context) {
113
+ const engine = context.config.workflow?.judgeEngine;
114
+ if (!engine) {
115
+ throw new ConfigError("This workflow declares completion criteria but no verification engine is configured. Set workflow.judgeEngine to a named LLM or agent engine.", "INVALID_CONFIG_FILE");
116
+ }
117
+ const content = source.gate?.rubric?.trim() ?? "Judge workflow completion.";
118
+ const prepared = prepareInlineExecution({
119
+ content,
120
+ config: context.config,
121
+ invocationKind: "workflow",
122
+ current: { engine },
123
+ });
124
+ return commandResult(source, { onError: "fail", source: sourceStepRef(source) }, prepared, context);
125
+ }
126
+ async function taskDispatch(source, baseUnit, refInput, context) {
127
+ const owned = await resolveOwnedAsset(refInput, "task", context);
128
+ const retained = captureOwned(owned, context.collector);
129
+ const task = parseTaskV3Yaml({ yaml: retained.content, filePath: owned.file, workspaceRoot: owned.root });
130
+ if (task.target.kind === "uses" && task.target.uses.kind === "workflow") {
131
+ throw new UsageError("A workflow task step cannot compose a nested workflow target.", "INVALID_FLAG_VALUE");
132
+ }
133
+ const prepared = await prepareTaskV3Execution(task, {
134
+ taskId: parseBundleRef(owned.ref).conceptId.slice("tasks/".length),
135
+ taskRef: owned.ref,
136
+ bundleName: owned.bundle,
137
+ bundleRoot: owned.root,
138
+ config: context.config,
139
+ commandSourceLoader: (ref, kind) => guardedExecutionSource(ref, kind, context),
140
+ resolveAsset: async ({ ref, type }) => {
141
+ const target = await resolveOwnedAsset(ref, type, context);
142
+ captureOwned(target, context.collector);
143
+ return { file: target.file, bundleRoot: target.root };
144
+ },
145
+ readFile: (file, root = owned.root) => context.collector.readBytes(file, root),
146
+ });
147
+ if (prepared.kind === "workflow") {
148
+ throw new UsageError("A workflow task step cannot compose a nested workflow target.", "INVALID_FLAG_VALUE");
149
+ }
150
+ const taskLiterals = Object.entries(prepared.environment).map(([name, value]) => Object.freeze({ kind: "literal", name, value }));
151
+ if (prepared.kind === "command") {
152
+ return commandResult(source, baseUnit, prepared.invocation, context, taskLiterals);
153
+ }
154
+ if (prepared.kind === "shell") {
155
+ const authoredExec = {
156
+ command: workflowShellCommand(prepared.shell, prepared.command),
157
+ ...(prepared.cwdIdentity.realCwd !== prepared.cwdIdentity.realRoot
158
+ ? { cwd: path.relative(prepared.cwdIdentity.realRoot, prepared.cwdIdentity.realCwd) }
159
+ : {}),
160
+ };
161
+ const exec = freezeExecSpec(source, authoredExec, context);
162
+ const environment = Object.freeze([...taskLiterals, ...freezeEnvironment(source, authoredExec, context)]);
163
+ const executable = freezeExecutableIdentity(exec.command[0], { cwd: prepared.cwdIdentity.realCwd });
164
+ const target = Object.freeze({
165
+ kind: "shell",
166
+ contentHash: "",
167
+ exec,
168
+ cwdIdentity: prepared.cwdIdentity,
169
+ executable,
170
+ ...gitIdentity(baseUnit, prepared.cwdIdentity.realRoot),
171
+ });
172
+ return {
173
+ target,
174
+ environment,
175
+ unit: { ...baseUnit, exec: authoredExec },
176
+ instructions: source.instructions ?? `Run task ${owned.ref}.`,
177
+ };
178
+ }
179
+ return scriptResult(source, baseUnit, prepared, context, taskLiterals);
180
+ }
181
+ async function directScript(source, baseUnit, refInput, context) {
182
+ const owned = await resolveOwnedAsset(refInput, "script", context);
183
+ captureOwned(owned, context.collector);
184
+ const synthetic = parseTaskV3Yaml({
185
+ yaml: `version: 3\nuses: ${owned.ref}\nakm:\n schedule: "@daily"\n`,
186
+ filePath: `${context.asset.path}#${source.id}`,
187
+ workspaceRoot: context.asset.sourcePath,
188
+ });
189
+ const prepared = await prepareTaskV3Execution(synthetic, {
190
+ taskId: source.id,
191
+ taskRef: `${context.asset.ref}#${source.id}`,
192
+ bundleName: parseBundleRef(context.asset.ref).bundle ?? owned.bundle,
193
+ bundleRoot: context.asset.sourcePath,
194
+ config: context.config,
195
+ resolveAsset: async () => ({ file: owned.file, bundleRoot: owned.root }),
196
+ readFile: () => context.collector.readBytes(owned.file, owned.root),
197
+ });
198
+ if (prepared.kind !== "script")
199
+ throw new Error("direct script did not project as a script");
200
+ return scriptResult(source, baseUnit, prepared, context, []);
201
+ }
202
+ function scriptResult(source, baseUnit, prepared, context, literals) {
203
+ const requestedExecutable = scriptExecutable(prepared.interpreter);
204
+ const executable = freezeExecutableIdentity(requestedExecutable, { cwd: prepared.cwdIdentity.realCwd });
205
+ const authoredExec = { command: [executable.absolutePath, "<frozen-script>"] };
206
+ const exec = freezeExecSpec(source, authoredExec, context);
207
+ const environment = Object.freeze([...literals, ...freezeEnvironment(source, authoredExec, context)]);
208
+ const target = Object.freeze({
209
+ kind: "script",
210
+ ref: prepared.sourceRef,
211
+ contentHash: prepared.sha256,
212
+ exec,
213
+ interpreter: prepared.interpreter,
214
+ extension: prepared.extension,
215
+ bytesBase64: prepared.bytesBase64,
216
+ byteLength: prepared.byteLength,
217
+ cwdIdentity: prepared.cwdIdentity,
218
+ materialization: "ephemeral-0700-delete",
219
+ executable,
220
+ ...gitIdentity(baseUnit, prepared.cwdIdentity.realRoot),
221
+ });
222
+ return {
223
+ target,
224
+ environment,
225
+ unit: { ...baseUnit, exec: authoredExec },
226
+ instructions: source.instructions ?? `Run script ${prepared.sourceRef}.`,
227
+ };
228
+ }
229
+ function directShell(source, baseUnit, context) {
230
+ const authoredExec = baseUnit.exec;
231
+ if (!authoredExec)
232
+ throw new Error(`workflow shell step ${source.id} lost its source-IR execution spec`);
233
+ const exec = freezeExecSpec(source, authoredExec, context);
234
+ const cwdIdentity = captureFrozenDirectoryIdentity(context.asset.sourcePath, authoredExec.cwd);
235
+ const executable = freezeExecutableIdentity(authoredExec.command[0], { cwd: cwdIdentity.realCwd });
236
+ const environment = Object.freeze(freezeEnvironment(source, authoredExec, context));
237
+ const target = Object.freeze({
238
+ kind: "shell",
239
+ contentHash: "",
240
+ exec,
241
+ cwdIdentity,
242
+ executable,
243
+ ...gitIdentity(baseUnit, cwdIdentity.realRoot),
244
+ });
245
+ return {
246
+ target,
247
+ environment,
248
+ unit: { ...baseUnit, exec },
249
+ instructions: source.instructions ?? `Run ${source.run ?? authoredExec.command.join(" ")}.`,
250
+ };
251
+ }
252
+ function commandResult(source, baseUnit, prepared, context, literals = []) {
253
+ const request = durableRequest(requireAuthorizedExecutionPlan(prepared.plan));
254
+ const lowered = lowerResolvedExecutionRequest(request, prepared.config);
255
+ const cwdIdentity = captureFrozenDirectoryIdentity(context.asset.sourcePath);
256
+ let runner = lowered.runner;
257
+ let executable;
258
+ if (runner.kind === "agent") {
259
+ executable = freezeExecutableIdentity(runner.profile.bin, { cwd: cwdIdentity.realCwd });
260
+ runner = Object.freeze({ ...runner, profile: Object.freeze({ ...runner.profile, bin: executable.absolutePath }) });
261
+ }
262
+ const unit = {
263
+ ...baseUnit,
264
+ engine: request.engine.name,
265
+ ...(request.model ? { model: request.model.resolved } : {}),
266
+ ...(Object.hasOwn(request.runtime, "timeoutMs") ? { timeoutMs: request.runtime.timeoutMs } : {}),
267
+ ...(request.inference ? { llm: request.inference } : {}),
268
+ ...(request.outputSchema ? { output: request.outputSchema } : {}),
269
+ };
270
+ const environment = Object.freeze([...literals, ...freezeEnvironment(source, undefined, context)]);
271
+ const target = Object.freeze({
272
+ kind: "command",
273
+ ref: request.command.source?.ref ?? null,
274
+ contentHash: createHash("sha256").update(request.command.content).digest("hex"),
275
+ request: JSON.parse(canonicalResolvedExecutionRequest(request)),
276
+ runner,
277
+ ...(targetConcurrency(runner, context.config) ? { concurrency: targetConcurrency(runner, context.config) } : {}),
278
+ cwdIdentity,
279
+ ...(executable ? { executable } : {}),
280
+ ...gitIdentity(baseUnit, cwdIdentity.realRoot),
281
+ });
282
+ const engineAnnouncement = fallbackAnnouncement(prepared.fallbackEngineName, request.engine.name);
283
+ return {
284
+ target,
285
+ environment,
286
+ unit,
287
+ instructions: request.command.content,
288
+ ...(engineAnnouncement ? { engineAnnouncement } : {}),
289
+ };
290
+ }
291
+ function freezeExecSpec(source, exec, context) {
292
+ const declared = Object.hasOwn(source.unit ?? {}, "timeoutMs")
293
+ ? source.unit?.timeoutMs
294
+ : context.sourceIr.defaults && Object.hasOwn(context.sourceIr.defaults, "timeoutMs")
295
+ ? context.sourceIr.defaults.timeoutMs
296
+ : undefined;
297
+ return {
298
+ ...exec,
299
+ command: exec.command,
300
+ timeoutMs: declared === undefined ? DEFAULT_EXEC_TIMEOUT_MS : declared,
301
+ };
302
+ }
303
+ function targetConcurrency(runner, config) {
304
+ if (runner.kind === "llm") {
305
+ const configured = typeof runner.engine === "string" ? config.engines?.[runner.engine] : undefined;
306
+ return defaultLlmEngineConcurrency(runner.connection.endpoint, configured?.kind === "llm" ? configured.concurrency : undefined);
307
+ }
308
+ if (runner.kind !== "sdk" || !runner.fallbackConnection)
309
+ return undefined;
310
+ const selected = typeof runner.engine === "string" ? config.engines?.[runner.engine] : undefined;
311
+ const fallbackName = selected?.kind === "agent" ? (selected.llmEngine ?? config.defaults?.llmEngine) : undefined;
312
+ const fallback = fallbackName ? config.engines?.[fallbackName] : undefined;
313
+ return defaultLlmEngineConcurrency(runner.fallbackConnection.endpoint, fallback?.kind === "llm" ? fallback.concurrency : undefined);
314
+ }
315
+ function durableRequest(request) {
316
+ const wire = JSON.parse(canonicalResolvedExecutionRequest(request));
317
+ const runtime = { ...wire.runtime };
318
+ delete runtime.environment;
319
+ wire.runtime = runtime;
320
+ return decodeResolvedExecutionRequest(wire);
321
+ }
322
+ function executionValues(source, workspace) {
323
+ return executionUnitValues(source.unit, workspace);
324
+ }
325
+ function executionUnitValues(unit, workspace) {
326
+ return Object.freeze({
327
+ ...(unit && Object.hasOwn(unit, "engine") ? { engine: unit.engine } : {}),
328
+ ...(unit && Object.hasOwn(unit, "model") ? { model: unit.model } : {}),
329
+ ...(unit && Object.hasOwn(unit, "llm") ? { inference: unit.llm } : {}),
330
+ ...(unit && Object.hasOwn(unit, "timeoutMs") ? { timeout: unit.timeoutMs } : {}),
331
+ ...(unit && "output" in unit && Object.hasOwn(unit, "output") ? { outputSchema: unit.output } : {}),
332
+ workspace,
333
+ });
334
+ }
335
+ function freezeEnvironment(source, exec, context) {
336
+ const literals = Object.entries(source.env ?? {}).map(([name, value]) => Object.freeze({ kind: "literal", name, value: String(value) }));
337
+ const passThrough = (exec?.passEnv ?? []).map((name) => Object.freeze({ kind: "pass-through", name }));
338
+ const refs = source.unit?.env ?? [];
339
+ const envRefs = freezeWorkflowEnvironment(refs, {
340
+ collector: context.collector,
341
+ resolveRef: (ref) => {
342
+ const parsedEnv = parseEnvRef(ref);
343
+ if (parsedEnv.type !== "env")
344
+ throw new UsageError(`Expected an env ref; got ${ref}.`, "INVALID_FLAG_VALUE");
345
+ const owned = resolveOwnedAssetSync(ref, "env", context);
346
+ return { ref: owned.ref, bundle: owned.bundle, adapter: owned.adapter, root: owned.root, path: owned.file };
347
+ },
348
+ });
349
+ return [...literals, ...passThrough, ...envRefs];
350
+ }
351
+ async function guardedExecutionSource(ref, kind, context) {
352
+ const owned = await resolveOwnedAsset(ref, kind === "command" ? "command" : "agent", context);
353
+ captureOwned(owned, context.collector);
354
+ const options = {
355
+ config: context.config,
356
+ fileContext: () => context.collector.fileContext(owned.root, owned.file),
357
+ };
358
+ const rendered = kind === "command"
359
+ ? await loadAdapterExecutionSource(owned.ref, "command", options)
360
+ : await loadAdapterExecutionSource(owned.ref, "persona", options);
361
+ context.collector.bindIdentity(owned.file, owned.root, rendered.identity);
362
+ return rendered;
363
+ }
364
+ async function resolveOwnedAsset(ref, type, context) {
365
+ return resolveOwnedAssetCore(ref, type, context, false);
366
+ }
367
+ function resolveOwnedAssetSync(ref, type, context) {
368
+ return resolveOwnedAssetCore(ref, type, context, true);
369
+ }
370
+ function resolveOwnedAssetCore(refInput, type, context, sync) {
371
+ const parsed = parseBundleRef(refInput);
372
+ const plural = type === "env" ? "env" : `${type}s`;
373
+ const conceptId = parsed.conceptId.startsWith(`${plural}/`) ? parsed.conceptId : `${plural}/${parsed.conceptId}`;
374
+ const name = conceptId.slice(plural.length + 1);
375
+ const direct = parsed.bundle ? configuredOwner(parsed.bundle, context.config) : undefined;
376
+ const sources = resolveSourceEntries(undefined, context.config);
377
+ const installations = deriveInstallations(sources);
378
+ const candidates = direct
379
+ ? [direct]
380
+ : sources.flatMap((source, index) => {
381
+ const installation = installations[index];
382
+ if (!installation || (parsed.bundle && installation.id !== parsed.bundle))
383
+ return [];
384
+ return [
385
+ {
386
+ bundle: installation.id,
387
+ root: source.path,
388
+ adapter: source.adapterId ?? installation.components[0]?.adapter ?? "akm",
389
+ },
390
+ ];
391
+ });
392
+ const findSync = () => {
393
+ for (const candidate of candidates) {
394
+ const directory = path.join(candidate.root, plural);
395
+ for (const extension of assetExtensions(type)) {
396
+ const file = path.resolve(directory, `${name}${extension}`);
397
+ if (fs.existsSync(file) && fs.statSync(file).isFile()) {
398
+ return { ...candidate, ref: makeBundleRef(candidate.bundle, conceptId), file };
399
+ }
400
+ }
401
+ }
402
+ throw new UsageError(`Workflow source target ${refInput} was not found.`, "INVALID_FLAG_VALUE");
403
+ };
404
+ if (sync)
405
+ return findSync();
406
+ return (async () => {
407
+ for (const candidate of candidates) {
408
+ try {
409
+ const file = await resolveAssetPath(candidate.root, type, name);
410
+ return { ...candidate, ref: makeBundleRef(candidate.bundle, conceptId), file };
411
+ }
412
+ catch {
413
+ // Continue in installation priority order.
414
+ }
415
+ }
416
+ return findSync();
417
+ })();
418
+ }
419
+ function configuredOwner(bundle, config) {
420
+ const entry = config.bundles?.[bundle];
421
+ if (!entry || typeof entry.path !== "string")
422
+ return undefined;
423
+ const components = entry.components ? Object.values(entry.components) : [];
424
+ const component = components[0];
425
+ return {
426
+ bundle,
427
+ root: path.resolve(entry.path, component?.root ?? "."),
428
+ adapter: component?.adapter ?? "akm",
429
+ };
430
+ }
431
+ function assetExtensions(type) {
432
+ if (type === "script")
433
+ return [
434
+ "",
435
+ ".sh",
436
+ ".ts",
437
+ ".js",
438
+ ".py",
439
+ ".rb",
440
+ ".go",
441
+ ".pl",
442
+ ".php",
443
+ ".lua",
444
+ ".r",
445
+ ".swift",
446
+ ".kt",
447
+ ".kts",
448
+ ".ps1",
449
+ ".cmd",
450
+ ".bat",
451
+ ];
452
+ if (type === "env")
453
+ return ["", ".env"];
454
+ return ["", ".md", ".yml"];
455
+ }
456
+ function captureOwned(owned, collector) {
457
+ trackAncestry(collector, owned.root, owned.file);
458
+ const retained = collector.capture(owned.file, owned.root, { authored: true });
459
+ return collector.bindIdentity(owned.file, owned.root, {
460
+ ref: owned.ref,
461
+ bundle: owned.bundle,
462
+ adapter: owned.adapter,
463
+ file: retained.relativePath,
464
+ hash: retained.sha256,
465
+ });
466
+ }
467
+ function trackAncestry(collector, rootInput, file) {
468
+ const root = path.resolve(rootInput);
469
+ collector.trackDirectory(root, root);
470
+ const relative = path.relative(root, path.dirname(file));
471
+ if (relative.startsWith("..") || path.isAbsolute(relative)) {
472
+ throw new UsageError(`${file} resolves outside its owning root.`, "PATH_ESCAPE_VIOLATION");
473
+ }
474
+ let current = root;
475
+ for (const segment of relative === "" ? [] : relative.split(path.sep)) {
476
+ current = path.join(current, segment);
477
+ collector.trackDirectory(current, root);
478
+ }
479
+ }
480
+ function qualifyRef(ref, plural, asset, config) {
481
+ const parsed = parseBundleRef(ref);
482
+ if (parsed.bundle)
483
+ return ref;
484
+ const bundle = parseBundleRef(asset.ref).bundle ?? config.defaultBundle;
485
+ if (!bundle)
486
+ throw new UsageError(`Workflow ref ${ref} has no owning bundle.`, "INVALID_FLAG_VALUE");
487
+ const concept = parsed.conceptId.startsWith(`${plural}/`) ? parsed.conceptId : `${plural}/${parsed.conceptId}`;
488
+ return makeBundleRef(bundle, concept);
489
+ }
490
+ function scriptExecutable(interpreter) {
491
+ if (interpreter === "bun" || interpreter === "bun-standalone")
492
+ return process.execPath;
493
+ if (interpreter === "kotlin")
494
+ return "kotlin";
495
+ return interpreter;
496
+ }
497
+ function gitIdentity(unit, root) {
498
+ if (unit.isolation !== "worktree")
499
+ return {};
500
+ const result = spawnSync("git", ["-C", root, "rev-parse", "HEAD"], { encoding: "utf8" });
501
+ const oid = result.status === 0 ? result.stdout.trim() : "";
502
+ if (!/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(oid)) {
503
+ throw new UsageError(`Worktree-isolated workflow root ${root} has no immutable Git HEAD OID.`, "INVALID_FLAG_VALUE");
504
+ }
505
+ return { gitCommitOid: oid };
506
+ }
@@ -62,7 +62,7 @@ const DEFAULTS_KEYS = ["engine", "model", "timeout", "on_error", "llm"];
62
62
  const BUDGET_KEYS = ["max_tokens", "max_units"];
63
63
  const STEP_KEYS = ["id", "unit", "map", "route", "inputs", "output", "gate"];
64
64
  const UNIT_KEYS = ["exec", "engine", "model", "llm", "timeout", "retry", "on_error", "output", "env", "isolation"];
65
- const EXEC_KEYS = ["command", "cwd", "pass_env", "inherit_env"];
65
+ const EXEC_KEYS = ["command", "cwd", "pass_env"];
66
66
  /** Unit keys that name an ENGINE dispatch and therefore cannot appear beside `exec:`. */
67
67
  const UNIT_ENGINE_KEYS = ["engine", "model", "llm"];
68
68
  const MAP_KEYS = ["over", "concurrency", "reducer", "unit"];
@@ -74,19 +74,6 @@ const ACTOR_STAMP_KEYS = ["by", "at"];
74
74
  const TIMEOUT_VALUE = /^(\d+)(ms|s|m)?$/;
75
75
  const TIMEOUT_HINT = `Use "<n>ms", "<n>s", "<n>m" (e.g. "10m"), or "none"`;
76
76
  const LIFECYCLE_STATUSES = new Set(["draft", "stable", "deprecated"]);
77
- /**
78
- * Cheap structural probe retained ONLY for callers that still need a fast
79
- * "is this workflow-shaped" content check without touching the filesystem's
80
- * directory. Recognition itself no longer sniffs content (spec §2.5) — this
81
- * is a best-effort convenience for content-only contexts (e.g. a proposal
82
- * whose ref carries no path). It looks for `type: workflow` in frontmatter.
83
- */
84
- export function looksLikeWorkflow(raw) {
85
- const fmBlock = parseFrontmatterBlock(raw);
86
- if (!fmBlock)
87
- return false;
88
- return /^type:\s*['"]?workflow['"]?\s*(#.*)?$/m.test(fmBlock.frontmatter);
89
- }
90
77
  export function parseWorkflow(markdown, source) {
91
78
  if (utf8Bytes(markdown) > WORKFLOW_MAX_SOURCE_BYTES) {
92
79
  return {
@@ -155,6 +142,7 @@ export function parseWorkflow(markdown, source) {
155
142
  const ctx = {
156
143
  filePath: path,
157
144
  errors,
145
+ ...(source.validateExecCwd ? { validateExecCwd: source.validateExecCwd } : {}),
158
146
  lineAt,
159
147
  lineAtOffset: (offset) => Math.max(1, lineCounter.linePos(offset).line + lineOffset),
160
148
  nodeAt: (p) => (p.length === 0 ? doc.contents : doc.getIn(p, true)),
@@ -700,15 +688,6 @@ function parseExec(ctx, raw, path, stepLabel) {
700
688
  const passEnv = parseExecPassEnv(ctx, raw.pass_env, [...path, "pass_env"], stepLabel);
701
689
  if (passEnv !== undefined)
702
690
  exec.passEnv = passEnv;
703
- if (raw.inherit_env !== undefined) {
704
- if (typeof raw.inherit_env !== "boolean") {
705
- ctx.err([...path, "inherit_env"], `${stepLabel} "exec.inherit_env" must be true or false. true gives the command akm's whole environment ` +
706
- `instead of the default allowlist; omit it (or write false) to keep the allowlist.`);
707
- }
708
- else if (raw.inherit_env) {
709
- exec.inheritEnv = true;
710
- }
711
- }
712
691
  return exec;
713
692
  }
714
693
  /**
@@ -726,7 +705,7 @@ function parseExecPassEnv(ctx, raw, path, stepLabel) {
726
705
  }
727
706
  if (raw.length > WORKFLOW_MAX_EXEC_PASS_ENV) {
728
707
  ctx.err(path, `${stepLabel} "exec.pass_env" must have at most ${WORKFLOW_MAX_EXEC_PASS_ENV} entries. A command needing ` +
729
- `more than that wants "inherit_env: true", which says so explicitly.`);
708
+ `more than that should use explicit named "env:" bindings instead.`);
730
709
  return undefined;
731
710
  }
732
711
  const names = [];
@@ -761,6 +740,10 @@ function parseExecCommand(ctx, raw, path, stepLabel) {
761
740
  ctx.err(path, `${stepLabel} "exec.command[${index}]" must be a non-empty string.`);
762
741
  return undefined;
763
742
  }
743
+ if (entry.includes("\0")) {
744
+ ctx.err([...path, index], `${stepLabel} "exec.command[${index}]" may not contain NUL bytes.`);
745
+ return undefined;
746
+ }
764
747
  if (utf8Bytes(entry) > WORKFLOW_MAX_EXEC_ARG_BYTES) {
765
748
  ctx.err(path, `${stepLabel} "exec.command[${index}]" exceeds ${WORKFLOW_MAX_EXEC_ARG_BYTES} bytes.`);
766
749
  return undefined;
@@ -791,6 +774,18 @@ function parseExecCwd(ctx, raw, path, stepLabel) {
791
774
  `directory — absolute paths, Windows drive letters, "~", and ".." segments are rejected.`);
792
775
  return undefined;
793
776
  }
777
+ if (ctx.validateExecCwd) {
778
+ const semantic = ctx.validateExecCwd(value);
779
+ if (!semantic.ok) {
780
+ ctx.errors.push({
781
+ code: semantic.code,
782
+ line: ctx.lineAt(path),
783
+ message: semantic.message,
784
+ });
785
+ return undefined;
786
+ }
787
+ return semantic.value;
788
+ }
794
789
  return value;
795
790
  }
796
791
  function parseMap(ctx, raw, path, stepLabel) {
@@ -1077,6 +1072,7 @@ function parseLlmOverrides(ctx, raw, path, label) {
1077
1072
  "extra_params",
1078
1073
  "context_length",
1079
1074
  "enable_thinking",
1075
+ "reasoning_effort",
1080
1076
  ];
1081
1077
  checkUnknownKeys(ctx, raw, path, keys, label);
1082
1078
  const result = {};
@@ -1129,6 +1125,13 @@ function parseLlmOverrides(ctx, raw, path, label) {
1129
1125
  else
1130
1126
  ctx.err([...path, "enable_thinking"], `${label}.enable_thinking must be a boolean.`);
1131
1127
  }
1128
+ if (raw.reasoning_effort !== undefined) {
1129
+ if (typeof raw.reasoning_effort === "string" && raw.reasoning_effort.trim().length > 0) {
1130
+ result.reasoningEffort = raw.reasoning_effort;
1131
+ }
1132
+ else
1133
+ ctx.err([...path, "reasoning_effort"], `${label}.reasoning_effort must be a non-empty string.`);
1134
+ }
1132
1135
  return Object.keys(result).length > 0 ? result : undefined;
1133
1136
  }
1134
1137
  function parseSchemaObject(ctx, raw, path, label) {
@@ -30,7 +30,7 @@ const RETRY_REASON_SET = {
30
30
  export const PROGRAM_RETRY_REASONS = Object.keys(RETRY_REASON_SET);
31
31
  /**
32
32
  * Step ids: `[A-Za-z_][A-Za-z0-9_-]*` (also pinned in the JSON Schema) — the
33
- * ONE id grammar for the unified format (workflow-format-unification, spec
33
+ * ONE id grammar for the shared source IR (workflow-format-unification, spec
34
34
  * §2.2). This is EXACTLY the `<ident>` grammar `readIdent` accepts in
35
35
  * `program/expressions.ts` for `steps.<id>.output` references: a
36
36
  * letter/underscore first char, then letters/digits/underscores/dashes, and
@@ -70,6 +70,5 @@ export function projectExecCore(exec) {
70
70
  command: [...exec.command],
71
71
  ...(exec.cwd ? { cwd: exec.cwd } : {}),
72
72
  ...(exec.passEnv && exec.passEnv.length > 0 ? { passEnv: [...exec.passEnv] } : {}),
73
- ...(exec.inheritEnv ? { inheritEnv: true } : {}),
74
73
  };
75
74
  }