akm-cli 0.9.31 → 0.10.26101002-alpha

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 (236) hide show
  1. package/CHANGELOG.md +265 -0
  2. package/STABILITY.md +79 -53
  3. package/dist/api-node.mjs +20 -0
  4. package/dist/api.d.ts +23 -0
  5. package/dist/api.js +45 -0
  6. package/dist/assets/hints/cli-hints-full.md +13 -27
  7. package/dist/assets/hints/cli-hints-short.md +1 -1
  8. package/dist/assets/improve-strategies/default.json +3 -3
  9. package/dist/assets/prompts/consolidate-pair.md +9 -7
  10. package/dist/cli/shared.js +31 -48
  11. package/dist/cli/unknown-flags.js +6 -43
  12. package/dist/cli.js +8 -34
  13. package/dist/commands/agent/contribute-cli.js +1 -1
  14. package/dist/commands/completions.js +1 -2
  15. package/dist/commands/config-cli.js +5 -28
  16. package/dist/commands/env/env-cli.js +5 -5
  17. package/dist/commands/env/secret-cli.js +2 -2
  18. package/dist/commands/health/checks.js +41 -16
  19. package/dist/commands/health/plugin-staleness.js +52 -22
  20. package/dist/commands/improve/autonomy-gate.js +5 -3
  21. package/dist/commands/improve/consolidate/pair-pass.js +7 -1
  22. package/dist/commands/improve/consolidate.js +6 -35
  23. package/dist/commands/improve/distill/lesson-update.js +91 -0
  24. package/dist/commands/improve/distill-guards.js +1 -43
  25. package/dist/commands/improve/distill.js +140 -20
  26. package/dist/commands/improve/eligibility.js +14 -1
  27. package/dist/commands/improve/extract-cli.js +1 -1
  28. package/dist/commands/improve/improve-cli.js +87 -102
  29. package/dist/commands/improve/improve-strategies.js +8 -12
  30. package/dist/commands/improve/improve.js +2 -2
  31. package/dist/commands/improve/loop-stages.js +0 -1
  32. package/dist/commands/improve/planner.js +0 -1
  33. package/dist/commands/improve/preparation.js +40 -99
  34. package/dist/commands/improve/proactive-maintenance.js +2 -2
  35. package/dist/commands/improve/reflect-noise.js +3 -55
  36. package/dist/commands/improve/reflect.js +3 -8
  37. package/dist/commands/improve/retrieval-scope.js +2 -2
  38. package/dist/commands/improve/salience.js +2 -9
  39. package/dist/commands/improve/stage.js +14 -9
  40. package/dist/commands/lint/index.js +5 -20
  41. package/dist/commands/observability-cli.js +3 -9
  42. package/dist/commands/proposal/drain.js +39 -6
  43. package/dist/commands/proposal/proposal-cli.js +11 -31
  44. package/dist/commands/proposal/repository.js +3 -3
  45. package/dist/commands/read/knowledge.js +3 -3
  46. package/dist/commands/read/remember-cli.js +0 -13
  47. package/dist/commands/read/search-cli.js +29 -97
  48. package/dist/commands/read/search.js +0 -11
  49. package/dist/commands/read/show.js +0 -19
  50. package/dist/commands/sources/plugin-upgrade.js +164 -101
  51. package/dist/commands/sources/sources-cli.js +1 -15
  52. package/dist/commands/sources/stash-cli.js +4 -12
  53. package/dist/commands/tasks/tasks-cli.js +4 -115
  54. package/dist/commands/workflow/plan.js +11 -33
  55. package/dist/commands/workflow-cli.js +2 -7
  56. package/dist/core/adapter/adapter-ids.js +1 -2
  57. package/dist/core/adapter/adapters/agent-skills-adapter.js +0 -4
  58. package/dist/core/adapter/adapters/akm-adapter.js +9 -48
  59. package/dist/core/adapter/adapters/akm-lint.js +10 -26
  60. package/dist/core/adapter/adapters/akm-metadata.js +2 -72
  61. package/dist/core/adapter/adapters/akm-task-adapter.js +0 -5
  62. package/dist/core/adapter/adapters/akm-workflow-adapter.js +9 -28
  63. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -20
  64. package/dist/core/adapter/adapters/generic-files-adapter.js +0 -10
  65. package/dist/core/adapter/adapters/llm-wiki-adapter.js +0 -4
  66. package/dist/core/adapter/adapters/tool-dir-shared.js +0 -24
  67. package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -4
  68. package/dist/core/adapter/recognize-match.js +3 -4
  69. package/dist/core/common.js +18 -2
  70. package/dist/core/config/config-schema.js +12 -15
  71. package/dist/core/config/config-types.js +6 -15
  72. package/dist/core/config/config-walker.js +6 -23
  73. package/dist/core/config/config.js +17 -80
  74. package/dist/core/config/engine-semantics.js +7 -3
  75. package/dist/core/config/schema/engines.js +7 -7
  76. package/dist/core/config/schema/improve-processes.js +8 -61
  77. package/dist/core/config/schema/improve.js +2 -21
  78. package/dist/core/config/schema/index-config.js +6 -39
  79. package/dist/core/env-secret-ref.js +2 -45
  80. package/dist/core/errors.js +1 -2
  81. package/dist/core/extra-params.js +2 -86
  82. package/dist/core/improve-result.js +11 -1
  83. package/dist/core/mutation-target.js +2 -2
  84. package/dist/core/recognition-util.js +4 -5
  85. package/dist/core/warn.js +21 -4
  86. package/dist/core/write-source.js +3 -3
  87. package/dist/execution/target-ref.js +1 -2
  88. package/dist/indexer/search/db-search.js +4 -19
  89. package/dist/indexer/search/fts-query.js +0 -19
  90. package/dist/integrations/agent/engine-resolution.js +5 -2
  91. package/dist/integrations/agent/index.js +1 -1
  92. package/dist/integrations/agent/profiles.js +41 -98
  93. package/dist/integrations/agent/request-lowering.js +3 -3
  94. package/dist/integrations/harnesses/aider/index.js +5 -0
  95. package/dist/integrations/harnesses/amazonq/index.js +1 -0
  96. package/dist/integrations/harnesses/claude/config-import.js +2 -3
  97. package/dist/integrations/harnesses/claude/index.js +5 -0
  98. package/dist/integrations/harnesses/codex/index.js +1 -0
  99. package/dist/integrations/harnesses/copilot/index.js +1 -0
  100. package/dist/integrations/harnesses/gemini/index.js +1 -0
  101. package/dist/integrations/harnesses/index.js +18 -39
  102. package/dist/integrations/harnesses/opencode/agent-builder.js +60 -35
  103. package/dist/integrations/harnesses/opencode/config-import.js +121 -52
  104. package/dist/integrations/harnesses/opencode/index.js +22 -0
  105. package/dist/integrations/harnesses/opencode/plugin-layout.js +68 -0
  106. package/dist/integrations/harnesses/opencode/session-log-types.js +12 -0
  107. package/dist/integrations/harnesses/opencode/session-log-v1.js +72 -0
  108. package/dist/integrations/harnesses/opencode/session-log-v2.js +140 -0
  109. package/dist/integrations/harnesses/opencode/session-log.js +104 -115
  110. package/dist/integrations/harnesses/opencode/version.js +98 -0
  111. package/dist/integrations/harnesses/opencode-sdk/{harness.js → index.js} +5 -17
  112. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +105 -206
  113. package/dist/integrations/harnesses/opencode-sdk/v1-adapter.js +199 -0
  114. package/dist/integrations/harnesses/opencode-sdk/v2-adapter.js +228 -0
  115. package/dist/integrations/harnesses/opencode-sdk/wire.js +66 -0
  116. package/dist/integrations/harnesses/openhands/index.js +1 -0
  117. package/dist/integrations/harnesses/pi/index.js +1 -0
  118. package/dist/integrations/harnesses/shared.js +2 -0
  119. package/dist/integrations/harnesses/types.js +2 -0
  120. package/dist/llm/client.js +41 -11
  121. package/dist/output/cli-hints.js +1 -1
  122. package/dist/output/context.js +10 -19
  123. package/dist/output/format-exempt.js +5 -8
  124. package/dist/output/shapes/helpers.js +1 -22
  125. package/dist/output/shapes/passthrough.js +0 -1
  126. package/dist/output/shapes.js +3 -31
  127. package/dist/output/stdout.js +5 -1
  128. package/dist/output/text/command-format.js +6 -1
  129. package/dist/output/text/workflow-format.js +4 -22
  130. package/dist/scripts/akm-migrate-node.js +13049 -14013
  131. package/dist/scripts/akm-migrate.js +16316 -17276
  132. package/dist/setup/detect.js +10 -4
  133. package/dist/setup/harness-config-import.js +5 -14
  134. package/dist/setup/setup.js +1 -1
  135. package/dist/setup/steps/tasks.js +12 -13
  136. package/dist/storage/repositories/task-history-repository.js +0 -5
  137. package/dist/storage/repositories/workflow-runs-repository.js +5 -15
  138. package/dist/tasks/embedded.js +2 -1
  139. package/dist/tasks/run/task-history.js +0 -2
  140. package/dist/tasks/schedule-gates.js +39 -0
  141. package/dist/tasks/scheduler-binding.js +0 -16
  142. package/dist/tasks/scheduler-sync.js +3 -97
  143. package/dist/tasks/source/bounded-document.js +1 -1
  144. package/dist/tasks/source/task-source-v4.js +2 -12
  145. package/dist/tasks/task-run-reserved-flags.js +2 -31
  146. package/dist/workflows/authoring/authoring.js +6 -6
  147. package/dist/workflows/compile.js +8 -65
  148. package/dist/workflows/exec/child-workflow.js +22 -3
  149. package/dist/workflows/exec/native-executor.js +12 -147
  150. package/dist/workflows/exec/run-workflow.js +31 -103
  151. package/dist/workflows/exec/step-work.js +9 -178
  152. package/dist/workflows/exec/unit-dispatch.js +0 -10
  153. package/dist/workflows/freeze/environment.js +3 -44
  154. package/dist/workflows/freeze/freeze.js +8 -41
  155. package/dist/workflows/freeze/targets/child-workflow.js +27 -63
  156. package/dist/workflows/freeze/targets/command.js +2 -16
  157. package/dist/workflows/freeze/targets/shell.js +0 -2
  158. package/dist/workflows/freeze/task-bindings.js +2 -28
  159. package/dist/workflows/ir/params.js +12 -2
  160. package/dist/workflows/parser.js +82 -232
  161. package/dist/workflows/plan.js +1 -1
  162. package/dist/workflows/renderer.js +10 -19
  163. package/dist/workflows/runtime/run-plan.js +11 -53
  164. package/dist/workflows/runtime/runs.js +4 -27
  165. package/dist/workflows/source-files.js +12 -16
  166. package/dist/workflows/source-semantics.js +1 -93
  167. package/docs/README.md +1 -1
  168. package/docs/agents/README.md +3 -3
  169. package/docs/agents/agent-install.md +1 -1
  170. package/docs/architecture/adapters.md +17 -31
  171. package/docs/architecture/architecture.md +4 -5
  172. package/docs/architecture/improvement.md +52 -21
  173. package/docs/architecture/internals/improve-workflow.md +15 -8
  174. package/docs/architecture/internals/indexing.md +1 -1
  175. package/docs/architecture/internals/search.md +1 -2
  176. package/docs/architecture/internals/storage-locations.md +5 -5
  177. package/docs/architecture/persisted-data-compat.md +13 -4
  178. package/docs/architecture/specs/0.9.0-decisions.md +6 -2
  179. package/docs/architecture/specs/improve-collapse-churn-detector-design.md +2 -2
  180. package/docs/architecture/specs/workflow-format-unification.md +1 -6
  181. package/docs/architecture/testing/manual-testing-checklist.md +20 -29
  182. package/docs/architecture/testing/task-workflow-manual-tests.md +106 -203
  183. package/docs/architecture/testing/testing-workflow.md +1 -1
  184. package/docs/architecture/workflow-engine.md +5 -56
  185. package/docs/guides/author-workflows.md +39 -69
  186. package/docs/guides/capture-knowledge.md +1 -1
  187. package/docs/guides/claude-code-vs-akm-workflows.md +1 -2
  188. package/docs/guides/discover-and-load.md +4 -4
  189. package/docs/guides/getting-started.md +1 -1
  190. package/docs/guides/improve-the-library.md +7 -7
  191. package/docs/guides/recipes/headless-install.md +2 -2
  192. package/docs/guides/run-workflows.md +1 -1
  193. package/docs/guides/use-with-any-agent.md +49 -4
  194. package/docs/integration/bundling-akm.md +7 -6
  195. package/docs/maintainers/curate-workmap.md +1 -2
  196. package/docs/maintainers/release-checklist.md +30 -1
  197. package/docs/posts/akm-command-tour.md +3 -3
  198. package/docs/product-surface.md +1 -1
  199. package/docs/reference/README.md +1 -0
  200. package/docs/reference/api.md +65 -0
  201. package/docs/reference/asset-types.md +3 -5
  202. package/docs/reference/bundle-types.md +2 -4
  203. package/docs/reference/cli.md +129 -171
  204. package/docs/reference/configuration.md +84 -19
  205. package/docs/reference/data-and-telemetry.md +1 -1
  206. package/docs/reference/env-and-secrets.md +2 -2
  207. package/docs/reference/supported-formats.md +3 -4
  208. package/docs/reference/tasks.md +12 -19
  209. package/docs/reference/website-sources.md +1 -1
  210. package/docs/reference/workflow-schema.md +89 -317
  211. package/docs/reference/workflows.md +20 -48
  212. package/package.json +12 -2
  213. package/schemas/akm-config.json +2 -243
  214. package/schemas/akm-task.json +2 -3
  215. package/schemas/akm-workflow.json +47 -84
  216. package/dist/assets/improve-strategies/catchup.json +0 -41
  217. package/dist/assets/improve-strategies/proactive-maintenance.json +0 -24
  218. package/dist/assets/improve-strategies/quick.json +0 -17
  219. package/dist/assets/improve-strategies/reflect-distill.json +0 -29
  220. package/dist/assets/improve-strategies/thorough.json +0 -49
  221. package/dist/assets/tasks/improve/akm-improve-catchup.yml +0 -6
  222. package/dist/assets/tasks/improve/akm-improve-frequent.yml +0 -4
  223. package/dist/assets/tasks/improve/akm-improve-nightly.yml +0 -4
  224. package/dist/cli/retired-commands.js +0 -118
  225. package/dist/commands/tasks/validate.js +0 -176
  226. package/dist/core/config/legacy-source-shape-shim.js +0 -117
  227. package/dist/integrations/harnesses/ids.js +0 -28
  228. package/dist/tasks/source/task-source-v3-frozen.js +0 -424
  229. package/dist/tasks/source/task-to-v4.js +0 -863
  230. package/dist/workflows/exec/worktree.js +0 -372
  231. package/dist/workflows/freeze/child-output-references.js +0 -83
  232. package/dist/workflows/freeze/identity.js +0 -22
  233. package/dist/workflows/freeze/targets/script.js +0 -50
  234. package/dist/workflows/freeze/targets/task.js +0 -124
  235. package/dist/workflows/github-yaml.js +0 -506
  236. package/dist/workflows/runtime/run-outputs.js +0 -80
@@ -3,43 +3,26 @@
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  /**
5
5
  * The workflow compiler's front door. `.md` goes through the Markdown grammar
6
- * (`parser.ts`), `.yml` through the GitHub-shaped YAML grammar
7
- * (`github-yaml.ts`); both produce a {@link WorkflowPlan} directly.
6
+ * (`parser.ts`), which produces a {@link WorkflowPlan} directly.
8
7
  *
9
8
  * {@link checkWorkflowPlan} is the one cross-step pass the grammars do not
10
- * run: every `steps.<id>` reference must name an earlier step (outputs may
11
- * name any step), `inputs:` never name a param, and two non-fatal advisories.
9
+ * run: every `steps.<id>` reference must name an earlier step, `inputs:` never name a param, and two non-fatal advisories.
12
10
  */
13
11
  import path from "node:path";
14
- import { parseBuiltinCommandAction } from "../commands/command/builtin-action.js";
15
- import { PORTABLE_ARGUMENTS_PLACEHOLDER } from "../commands/command/portable-template.js";
16
- import { parseGithubWorkflowSource, WorkflowSourceFailure } from "./github-yaml.js";
17
12
  import { parseWorkflow } from "./parser.js";
18
13
  import { formatReference, parseReference } from "./program/expressions.js";
19
14
  import { canonicalizeWorkflowWorkingDirectory, WorkflowSourceSemanticError } from "./source-semantics.js";
20
- export { looksLikeGithubWorkflowSource } from "./github-yaml.js";
21
15
  /** Compile by authoritative source extension. */
22
16
  export function compileWorkflowSource(source, options) {
23
17
  const extension = path.extname(options.path).toLowerCase();
24
18
  const title = options.title ?? path.basename(options.path, path.extname(options.path));
25
- if (extension === ".yml") {
26
- try {
27
- return { ok: true, plan: parseGithubWorkflowSource(source, { ...options, title }) };
28
- }
29
- catch (cause) {
30
- if (cause instanceof WorkflowSourceFailure)
31
- return { ok: false, errors: [cause.error] };
32
- const message = cause instanceof Error ? cause.message : String(cause);
33
- return { ok: false, errors: [{ code: "invalid-workflow-source", message, path: options.path, line: 1 }] };
34
- }
35
- }
36
19
  if (extension !== ".md") {
37
20
  return {
38
21
  ok: false,
39
22
  errors: [
40
23
  {
41
24
  code: "unsupported-workflow-extension",
42
- message: `Workflow source ${options.path} must use .md or .yml.`,
25
+ message: `Workflow source ${options.path} must use .md.`,
43
26
  path: options.path,
44
27
  line: 1,
45
28
  },
@@ -74,8 +57,7 @@ export function compileWorkflowSource(source, options) {
74
57
  }
75
58
  /**
76
59
  * The display text a step contributes to `show`, search hints, and the run
77
- * spine: its authored prose, else what its target does. Empty for a route
78
- * step with no section.
60
+ * spine: its authored prose, else what its target does.
79
61
  */
80
62
  export function workflowStepInstructions(step) {
81
63
  const spec = step.spec;
@@ -83,25 +65,9 @@ export function workflowStepInstructions(step) {
83
65
  return "";
84
66
  if (spec.instructions !== undefined)
85
67
  return spec.instructions;
86
- if (spec.uses === "akm/command") {
87
- const action = parseBuiltinCommandAction(spec.with);
88
- if (action.kind === "stored") {
89
- return `Invoke stored command ${action.ref}${action.arguments === undefined ? "" : " with arguments"}.`;
90
- }
91
- if (spec.commandMode === "literal")
92
- return action.content;
93
- return action.content.split(PORTABLE_ARGUMENTS_PLACEHOLDER).join(action.arguments ?? "");
94
- }
95
- if (spec.uses !== undefined)
96
- return `Invoke local target ${spec.uses}.`;
97
- return "";
98
- }
99
- /** A route step's deterministic one-line description of its branch table. */
100
- export function routeDescription(route) {
101
- const branches = Object.entries(route.when).map(([match, stepId]) => `"${match}" -> ${stepId}`);
102
- if (route.defaultStepId !== undefined)
103
- branches.push(`default -> ${route.defaultStepId}`);
104
- return `Route on ${route.input}: ${branches.join(", ")}.`;
68
+ // A prose step is `akm/command` with its body as literal content.
69
+ const content = spec.with?.content;
70
+ return spec.uses === "akm/command" && typeof content === "string" ? content : "";
105
71
  }
106
72
  /** Cross-step reference validation plus the non-fatal advisories. Pure. */
107
73
  export function checkWorkflowPlan(plan) {
@@ -136,40 +102,18 @@ export function checkWorkflowPlan(plan) {
136
102
  const line = step.spec?.source.start ?? 1;
137
103
  if (step.spec?.map)
138
104
  check(step.spec.map.over, line, `Step "${step.stepId}" map.over`, true);
139
- if (step.route)
140
- check(step.route.input, line, `Step "${step.stepId}" route.input`, true);
141
105
  for (const [index, reference] of (step.spec?.inputs ?? []).entries()) {
142
106
  check(reference, line, `Step "${step.stepId}" inputs[${index}]`, false);
143
107
  }
144
108
  earlierStepIds.add(step.stepId);
145
109
  }
146
- // Outputs resolve at run completion, so they may name any declared step.
147
- for (const [name, declaration] of Object.entries(plan.outputs ?? {})) {
148
- const parsed = parseReference(declaration.from);
149
- const label = `Output "${name}" from`;
150
- if (!parsed.ok)
151
- errors.push({ line: 1, message: `${label}: ${parsed.message}` });
152
- else if (parsed.expr.kind === "param") {
153
- errors.push({
154
- line: 1,
155
- message: `${label}: "${formatReference(parsed.expr)}" names a param, not a step output — an output projects a ` +
156
- `STEP artifact, never a param. "outputs:" only names step outputs (steps.<id>.output...).`,
157
- });
158
- }
159
- else if (!allStepIds.has(parsed.expr.stepId)) {
160
- errors.push({
161
- line: 1,
162
- message: `${label}: "${formatReference(parsed.expr)}" cannot be resolved — "${parsed.expr.stepId}" is not a step in this workflow.`,
163
- });
164
- }
165
- }
166
110
  if (errors.length > 0)
167
111
  return { ok: false, errors };
168
112
  return { ok: true, warnings: workflowWarnings(plan) };
169
113
  }
170
114
  /**
171
115
  * Advisories that never fail compilation or change the plan:
172
- * A. a `params.<name>` reference (in `map.over`/`route.input`) to a param the
116
+ * A. a `params.<name>` reference (in `map.over`) to a param the
173
117
  * document's `params:` block does not declare — a likely typo;
174
118
  * B. `gate.max_loops` above 1 on an exec step, which is judged but never
175
119
  * looped (a frozen argv cannot read the judge's feedback).
@@ -205,7 +149,6 @@ function workflowWarnings(plan) {
205
149
  });
206
150
  };
207
151
  scan(step.spec?.map?.over, `Step "${step.stepId}" map.over`);
208
- scan(step.route?.input, `Step "${step.stepId}" route.input`);
209
152
  }
210
153
  return warnings;
211
154
  }
@@ -8,7 +8,8 @@
8
8
  * `with:` bindings against the child's `params:`; (2) derive the deterministic
9
9
  * invocation key; (3) publish the child run idempotently; (4) drive it with
10
10
  * the same engine as a top-level run unless it is already `blocked`/`failed`;
11
- * (5) map the child's final status onto this unit's outcome.
11
+ * (5) map the child's final status onto this unit's outcome; a completed
12
+ * child returns the output of its last step.
12
13
  *
13
14
  * `runWorkflowSteps` is reached through a lazy dynamic import: a static one
14
15
  * would close the cycle native-executor -> child-workflow -> run-workflow ->
@@ -19,7 +20,6 @@ import { TransientError } from "../../core/errors.js";
19
20
  import { withWorkflowRunsRepo } from "../../storage/repositories/workflow-runs-repository.js";
20
21
  import { validateWorkflowParams } from "../ir/params.js";
21
22
  import { canonicalPlanJson } from "../ir/plan-hash.js";
22
- import { workflowRunExportedResult } from "../runtime/run-outputs.js";
23
23
  import { frozenStepRows } from "../runtime/run-plan.js";
24
24
  import { computeChildInvocationKey } from "./child-invocation.js";
25
25
  /** The real engine, via a dynamic import (see the module doc). The caller re-reads the child row afterward. */
@@ -27,6 +27,25 @@ async function driveWithRealEngine(options) {
27
27
  const { runWorkflowSteps } = await import("./run-workflow.js");
28
28
  await runWorkflowSteps(options);
29
29
  }
30
+ /**
31
+ * A completed child's result: the artifact of its last step (a step with no
32
+ * recorded evidence yields `null`). Read from the persisted step rows, so a
33
+ * resumed parent sees the same value as the live one.
34
+ */
35
+ async function childFinalOutput(childRunId) {
36
+ const steps = await withWorkflowRunsRepo((repo) => repo.getStepsForRun(childRunId));
37
+ const evidenceJson = steps[steps.length - 1]?.evidence_json;
38
+ if (!evidenceJson)
39
+ return null;
40
+ try {
41
+ const evidence = JSON.parse(evidenceJson);
42
+ // Same projection as `steps.<id>.output` (step-work.ts `projectStepOutput`).
43
+ return Object.hasOwn(evidence, "output") ? evidence.output : evidence;
44
+ }
45
+ catch {
46
+ return null;
47
+ }
48
+ }
30
49
  function errorMessage(err) {
31
50
  return err instanceof Error ? err.message : String(err);
32
51
  }
@@ -218,7 +237,7 @@ export async function driveChildWorkflowUnit(input) {
218
237
  return {
219
238
  unitId: request.unitId,
220
239
  ok: true,
221
- result: workflowRunExportedResult(finalRow),
240
+ result: await childFinalOutput(finalRow.id),
222
241
  childRun: childRunSummary,
223
242
  };
224
243
  case "failed": {
@@ -20,8 +20,6 @@
20
20
  * gate loops journal under `~r<n>` / `~l<n>`.
21
21
  * - `onError: "fail"` fails on any unit failure, `"continue"` lets the gate
22
22
  * decide; `retry: { max, on }` re-dispatches listed failure reasons.
23
- * - `isolation: worktree` runs each attempt of an agent/sdk unit in a fresh
24
- * detached worktree (removed when clean, kept when dirty); llm units reject it.
25
23
  * - Run `budget` ceilings are consumed per actual dispatch and abort the step
26
24
  * when crossed, regardless of `on_error`.
27
25
  */
@@ -52,9 +50,7 @@ import { scheduleUnits } from "./scheduler.js";
52
50
  // path. This module dispatches; step-work.ts owns the pure decisions.
53
51
  import { clip, computeStepWorkList, reduceEmptyStep, reduceStepOutcomes, stepOutputsFromEvidence, unitOutcomeFromRow, } from "./step-work.js";
54
52
  import { dispatchWorkflowExecution, } from "./unit-dispatch.js";
55
- import { cleanupFrozenScript, frozenScriptCommand, materializeFrozenScript } from "../../tasks/frozen-script.js";
56
53
  import { enqueueUnitWrite } from "./unit-writer.js";
57
- import { assertGitWorkTree, cleanupUnitWorktree, createUnitWorktree } from "./worktree.js";
58
54
  /**
59
55
  * Per-step dispatch budget against the run's `budget` ceilings, seeded from
60
56
  * the journal and consumed per actual dispatch (never by a reused row).
@@ -171,7 +167,7 @@ function openDispatchBudget(ctx, dispatched) {
171
167
  }
172
168
  /** Resolve the live-at-dispatch prerequisites once, after durable-row reuse is known. */
173
169
  async function prepareStepDispatchPrerequisites(input) {
174
- const { plan, template, workUnits, ctx, willDispatch, dispatched } = input;
170
+ const { plan, workUnits, willDispatch, dispatched } = input;
175
171
  let env;
176
172
  let sensitiveValues;
177
173
  const frozenEnvironment = workUnits[0]?.environment;
@@ -195,35 +191,10 @@ async function prepareStepDispatchPrerequisites(input) {
195
191
  };
196
192
  }
197
193
  }
198
- let worktreeBase;
199
- if (willDispatch && template.isolation === "worktree") {
200
- if (template.frozenTarget.kind === "command" && template.frozenTarget.runner.kind === "llm") {
201
- return {
202
- ok: false,
203
- result: failedStep(dispatched, `Step "${plan.stepId}" declares isolation: worktree on an llm unit — the llm runner has no ` +
204
- `working directory to isolate. Use the agent or sdk runner for worktree-isolated units.`),
205
- };
206
- }
207
- const target = workUnits[0]?.frozenTarget;
208
- const frozenCwd = target && "cwdIdentity" in target ? target.cwdIdentity : undefined;
209
- if (frozenCwd)
210
- assertFrozenDirectoryContained(frozenCwd);
211
- const base = frozenCwd?.realRoot ?? ctx.workDir ?? process.cwd();
212
- const preflightWorktree = ctx.preflightWorktree ?? assertGitWorkTree;
213
- const gitError = preflightWorktree(base);
214
- if (gitError !== undefined) {
215
- return {
216
- ok: false,
217
- result: failedStep(dispatched, `Step "${plan.stepId}" cannot use isolation: worktree: ${gitError}`),
218
- };
219
- }
220
- worktreeBase = base;
221
- }
222
194
  return {
223
195
  ok: true,
224
196
  ...(env ? { env } : {}),
225
197
  ...(sensitiveValues ? { sensitiveValues } : {}),
226
- ...(worktreeBase !== undefined ? { worktreeBase } : {}),
227
198
  };
228
199
  }
229
200
  async function executeStepPlanInConnection(plan, ctx) {
@@ -251,32 +222,25 @@ async function executeStepPlanInConnection(plan, ctx) {
251
222
  const dispatcher = ctx.dispatcher ?? defaultUnitDispatcher;
252
223
  // Load journaled rows first: a completed unit is reused, never re-dispatched.
253
224
  const completedRows = indexCompletedRows(await withWorkflowRunsRepo((repo) => repo.getUnitsForStep(ctx.runId, plan.stepId)));
254
- // Env resolution and worktree preflight run only when some unit will
255
- // dispatch, so a fully-journaled step resumes even if an env asset or git is
256
- // gone. Every unit is classified once; the preflight and dispatch share it.
225
+ // Env resolution runs only when some unit will dispatch, so a fully-journaled
226
+ // step resumes even if an env asset is gone. Every unit is classified once;
227
+ // the preflight and dispatch share it.
257
228
  const reuseDecisions = workUnits.map((unit) => classifyUnitReuse(unit, completedRows));
258
229
  const willDispatch = reuseDecisions.some((decision) => decision.kind === "dispatch");
259
230
  const prerequisites = await prepareStepDispatchPrerequisites({
260
231
  plan,
261
- template,
262
232
  workUnits,
263
- ctx,
264
233
  willDispatch,
265
234
  dispatched,
266
235
  });
267
236
  if (!prerequisites.ok)
268
237
  return prerequisites.result;
269
- const { env, sensitiveValues, worktreeBase } = prerequisites;
238
+ const { env, sensitiveValues } = prerequisites;
270
239
  // Budget ceilings + lifetime-cap accounting, and the budget-chained abort
271
240
  // signal they trip. Extracted verbatim (behavior-identical) — see
272
241
  // {@link openDispatchBudget}.
273
242
  const { signal, budget, unchainSignal } = openDispatchBudget(ctx, dispatched);
274
243
  let outcomes;
275
- // Worktree removals started by finished units and awaited below, before this
276
- // step reports anything. Cleanup is best-effort, but "the step resolved" must
277
- // still mean "its clean worktrees are gone" — only the WAIT moves off the
278
- // unit's scheduler slot, not the guarantee.
279
- const pendingWorktreeCleanups = [];
280
244
  const frozenTargetConcurrency = template.frozenTarget.kind === "command" ? template.frozenTarget.concurrency : undefined;
281
245
  try {
282
246
  outcomes = await scheduleUnits(workUnits, (workUnit, index) => runUnit({
@@ -284,12 +248,10 @@ async function executeStepPlanInConnection(plan, ctx) {
284
248
  workUnit,
285
249
  env,
286
250
  sensitiveValues,
287
- ...(worktreeBase !== undefined ? { worktreeBase } : {}),
288
251
  ctx,
289
252
  signal,
290
253
  dispatcher,
291
254
  reuse: reuseDecisions[index],
292
- pendingWorktreeCleanups,
293
255
  budget,
294
256
  }), {
295
257
  concurrency: workList.list.concurrency,
@@ -300,9 +262,6 @@ async function executeStepPlanInConnection(plan, ctx) {
300
262
  }
301
263
  finally {
302
264
  unchainSignal?.();
303
- // The barrier. `allSettled` because each task already swallowed its own
304
- // failure into a warn — nothing here can fail the step.
305
- await Promise.allSettled(pendingWorktreeCleanups);
306
265
  }
307
266
  // Capture live-only diagnostics BEFORE any hard reduction replaces the unit
308
267
  // list with a failed-step envelope. Budget/cap and journal-write failures
@@ -358,11 +317,9 @@ async function runUnit(input) {
358
317
  prompt,
359
318
  frozenTarget: workUnit.frozenTarget,
360
319
  ...(workUnit.execContext ? { execContext: workUnit.execContext } : {}),
361
- // A NON-isolated exec unit spawns in the engine invocation's working
362
- // directory. `dispatchJournaledAttempt` overwrites this with the unit's
363
- // fresh worktree when `isolation: worktree` is in play. Only exec units get
364
- // it: handing an agent unit a cwd it never had would change harness
365
- // behavior, and the agent path already takes its cwd from its profile.
320
+ // An exec unit spawns in the engine invocation's working directory. Only
321
+ // exec units get it: handing an agent unit a cwd it never had would change
322
+ // harness behavior, and the agent path already takes its cwd from its profile.
366
323
  ...(workUnit.frozenTarget.kind !== "command" && ctx.workDir !== undefined ? { cwd: ctx.workDir } : {}),
367
324
  timeoutMs: workUnit.timeoutMs,
368
325
  ...(workUnit.schema ? { schema: workUnit.schema } : {}),
@@ -370,7 +327,7 @@ async function runUnit(input) {
370
327
  ...(sensitiveValues ? { sensitiveValues } : {}),
371
328
  ...(input.signal ? { signal: input.signal } : {}),
372
329
  // F-1: forwarded to exec-unit.ts's childEnv for a
373
- // "script"/"shell" unit, and to dispatchWorkflowExecution's
330
+ // "shell" unit, and to dispatchWorkflowExecution's
374
331
  // runExecution eventSource option (unit-dispatch.ts)
375
332
  // for a "command" unit — both arms observe it.
376
333
  ...(ctx.eventSource !== undefined ? { eventSource: ctx.eventSource } : {}),
@@ -424,8 +381,6 @@ async function runUnit(input) {
424
381
  request: { ...request, unitId: attemptId },
425
382
  attemptId,
426
383
  inputHash,
427
- ...(input.worktreeBase !== undefined ? { worktreeBase: input.worktreeBase } : {}),
428
- pendingWorktreeCleanups: input.pendingWorktreeCleanups,
429
384
  });
430
385
  // Attempts use `~r<n>` journal suffixes while durable step evidence remains
431
386
  // attached to the content-derived base identity.
@@ -463,30 +418,7 @@ function journaledUnitResultJson(outcome) {
463
418
  return null;
464
419
  return JSON.stringify(clip(parts.join("\n--- unit output ---\n"), WORKFLOW_UNIT_DIAGNOSTIC_CLIP));
465
420
  }
466
- async function prepareAttemptWorktree(input) {
467
- if (input.worktreeBase === undefined)
468
- return { ok: true, request: input.request };
469
- const created = await createUnitWorktree(input.worktreeBase, input.ctx.runId, input.attemptId,
470
- // A child-workflow target has no commit of its own (its prepared worktree goes unused).
471
- input.workUnit.frozenTarget.kind === "child-workflow" ? undefined : input.workUnit.frozenTarget.gitCommitOid);
472
- if (created.preservedLeftover !== undefined) {
473
- warn(`Workflow unit ${input.attemptId}: a previous attempt left uncollected work in its isolation worktree; ` +
474
- `preserved at ${created.preservedLeftover}`);
475
- }
476
- if (!created.ok) {
477
- return {
478
- ok: false,
479
- outcome: {
480
- unitId: input.request.unitId,
481
- ok: false,
482
- failureReason: "worktree_failed",
483
- error: created.error,
484
- },
485
- };
486
- }
487
- return { ok: true, request: { ...input.request, cwd: created.path }, worktreePath: created.path };
488
- }
489
- async function reserveJournaledDispatch(input, worktreePath, startedAt) {
421
+ async function reserveJournaledDispatch(input, startedAt) {
490
422
  const { plan, workUnit, ctx, attemptId, inputHash } = input;
491
423
  let durableAttempt;
492
424
  await enqueueUnitWrite(async () => {
@@ -503,7 +435,6 @@ async function reserveJournaledDispatch(input, worktreePath, startedAt) {
503
435
  engine: target.kind === "command" ? target.request.engine.name : null,
504
436
  model: target.kind === "command" ? (target.request.model?.resolved ?? null) : null,
505
437
  inputHash,
506
- worktreePath: worktreePath ?? null,
507
438
  now: startedAt,
508
439
  }).attempt;
509
440
  });
@@ -538,37 +469,14 @@ async function finishJournaledDispatch(input) {
538
469
  }
539
470
  }));
540
471
  }
541
- function queueAttemptWorktreeCleanup(input, worktreePath) {
542
- if (worktreePath === undefined || input.worktreeBase === undefined)
543
- return;
544
- const worktreeBase = input.worktreeBase;
545
- input.pendingWorktreeCleanups.push((async () => {
546
- try {
547
- const cleanup = await cleanupUnitWorktree(worktreeBase, worktreePath);
548
- if (cleanup.dirty) {
549
- warn(`Workflow unit ${input.attemptId} left uncommitted changes in its isolation worktree; retained at ${worktreePath}`);
550
- }
551
- else if (!cleanup.removed) {
552
- warn(`Workflow unit ${input.attemptId}: could not clean up isolation worktree ${worktreePath}: ${cleanup.error}`);
553
- }
554
- }
555
- catch (err) {
556
- warn(`Workflow unit ${input.attemptId}: could not clean up isolation worktree ${worktreePath}: ${message(err)}`);
557
- }
558
- })());
559
- }
560
472
  /** Journal one dispatch attempt: insert row, events, dispatch, finish row. */
561
473
  async function dispatchJournaledAttempt(input) {
562
474
  const { workUnit, ctx, dispatcher, attemptId } = input;
563
- const prepared = await prepareAttemptWorktree(input);
564
- if (!prepared.ok)
565
- return prepared.outcome;
566
- let { request } = prepared;
567
- const { worktreePath } = prepared;
475
+ let { request } = input;
568
476
  const startedAt = new Date().toISOString();
569
477
  let durableAttempt;
570
478
  try {
571
- durableAttempt = await reserveJournaledDispatch(input, worktreePath, startedAt);
479
+ durableAttempt = await reserveJournaledDispatch(input, startedAt);
572
480
  }
573
481
  catch (err) {
574
482
  // A failed dispatch-row insert means NOTHING dispatched (the row is the
@@ -576,8 +484,6 @@ async function dispatchJournaledAttempt(input) {
576
484
  // letting the throw escape into the scheduler, where a swallowed worker
577
485
  // error is indistinguishable from "never claimed" and used to be
578
486
  // misreported as an aborted, never-dispatched unit.
579
- if (worktreePath !== undefined && input.worktreeBase !== undefined)
580
- await cleanupUnitWorktree(input.worktreeBase, worktreePath);
581
487
  return {
582
488
  unitId: request.unitId,
583
489
  ok: false,
@@ -632,9 +538,6 @@ async function dispatchJournaledAttempt(input) {
632
538
  catch (err) {
633
539
  journalError = err;
634
540
  }
635
- // A clean worktree is removed, a dirty one kept and logged. Cleanup starts
636
- // here and is awaited at the step barrier, never holding this scheduler slot.
637
- queueAttemptWorktreeCleanup(input, worktreePath);
638
541
  // A journal-write failure AFTER a successful dispatch is its own loud
639
542
  // failure class: the unit's work ran (and may have succeeded), but its
640
543
  // terminal state could not be recorded, so the row may be stuck `running`.
@@ -796,44 +699,6 @@ async function dispatchUnit(request, dispatcher) {
796
699
  */
797
700
  export const defaultUnitDispatcher = async (request, feedback) => {
798
701
  const frozenTarget = request.frozenTarget;
799
- if (frozenTarget.kind === "script") {
800
- assertFrozenDirectoryContained(frozenTarget.cwdIdentity);
801
- const materialized = materializeFrozenScript({
802
- sourceRef: frozenTarget.ref,
803
- interpreter: frozenTarget.interpreter,
804
- extension: frozenTarget.extension,
805
- bytesBase64: frozenTarget.bytesBase64,
806
- byteLength: frozenTarget.byteLength,
807
- sha256: frozenTarget.contentHash,
808
- });
809
- try {
810
- const command = frozenScriptCommand({
811
- sourceRef: frozenTarget.ref,
812
- interpreter: frozenTarget.interpreter,
813
- extension: frozenTarget.extension,
814
- bytesBase64: frozenTarget.bytesBase64,
815
- byteLength: frozenTarget.byteLength,
816
- sha256: frozenTarget.contentHash,
817
- }, materialized.file);
818
- return await runExecUnit({
819
- unitId: request.unitId,
820
- exec: {
821
- ...frozenTarget.exec,
822
- command: command,
823
- },
824
- baseDir: request.cwd ?? frozenTarget.cwdIdentity.realCwd,
825
- ...(request.env ? { env: request.env } : {}),
826
- ...(request.execContext ? { context: request.execContext } : {}),
827
- ...(request.schema ? { hasOutputSchema: true } : {}),
828
- timeoutMs: request.timeoutMs,
829
- ...(request.signal ? { signal: request.signal } : {}),
830
- ...(request.eventSource !== undefined ? { eventSource: request.eventSource } : {}),
831
- });
832
- }
833
- finally {
834
- cleanupFrozenScript(materialized);
835
- }
836
- }
837
702
  if (frozenTarget.kind === "shell") {
838
703
  if (frozenTarget.cwdIdentity)
839
704
  assertFrozenDirectoryContained(frozenTarget.cwdIdentity);