@bastani/atomic 0.8.24-alpha.2 → 0.8.24-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 (193) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +2 -1
  3. package/dist/builtin/intercom/CHANGELOG.md +12 -0
  4. package/dist/builtin/intercom/package.json +1 -1
  5. package/dist/builtin/mcp/CHANGELOG.md +12 -0
  6. package/dist/builtin/mcp/package.json +1 -1
  7. package/dist/builtin/subagents/CHANGELOG.md +16 -0
  8. package/dist/builtin/subagents/README.md +132 -21
  9. package/dist/builtin/subagents/package.json +1 -1
  10. package/dist/builtin/subagents/prompts/parallel-context-build.md +4 -2
  11. package/dist/builtin/subagents/prompts/parallel-handoff-plan.md +3 -1
  12. package/dist/builtin/subagents/skills/subagent/SKILL.md +49 -11
  13. package/dist/builtin/subagents/src/agents/agent-management.ts +79 -16
  14. package/dist/builtin/subagents/src/agents/agents.ts +47 -16
  15. package/dist/builtin/subagents/src/agents/chain-serializer.ts +114 -0
  16. package/dist/builtin/subagents/src/extension/schemas.ts +139 -3
  17. package/dist/builtin/subagents/src/runs/background/async-execution.ts +92 -6
  18. package/dist/builtin/subagents/src/runs/background/async-status.ts +11 -1
  19. package/dist/builtin/subagents/src/runs/background/run-status.ts +4 -1
  20. package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +529 -32
  21. package/dist/builtin/subagents/src/runs/foreground/chain-execution.ts +361 -118
  22. package/dist/builtin/subagents/src/runs/foreground/execution.ts +75 -7
  23. package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +33 -0
  24. package/dist/builtin/subagents/src/runs/shared/acceptance.ts +611 -0
  25. package/dist/builtin/subagents/src/runs/shared/chain-outputs.ts +101 -0
  26. package/dist/builtin/subagents/src/runs/shared/dynamic-fanout.ts +293 -0
  27. package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +29 -1
  28. package/dist/builtin/subagents/src/runs/shared/pi-args.ts +11 -0
  29. package/dist/builtin/subagents/src/runs/shared/structured-output.ts +79 -0
  30. package/dist/builtin/subagents/src/runs/shared/subagent-prompt-runtime.ts +52 -2
  31. package/dist/builtin/subagents/src/runs/shared/workflow-graph.ts +206 -0
  32. package/dist/builtin/subagents/src/shared/formatters.ts +2 -2
  33. package/dist/builtin/subagents/src/shared/settings.ts +53 -4
  34. package/dist/builtin/subagents/src/shared/types.ts +226 -0
  35. package/dist/builtin/subagents/src/shared/utils.ts +2 -1
  36. package/dist/builtin/subagents/src/slash/slash-commands.ts +41 -3
  37. package/dist/builtin/subagents/src/tui/render.ts +152 -34
  38. package/dist/builtin/web-access/CHANGELOG.md +12 -0
  39. package/dist/builtin/web-access/package.json +1 -1
  40. package/dist/builtin/workflows/CHANGELOG.md +12 -0
  41. package/dist/builtin/workflows/package.json +1 -1
  42. package/dist/builtin/workflows/skills/create-spec/SKILL.md +1 -1
  43. package/dist/builtin/workflows/src/tui/stage-chat-view.ts +0 -1
  44. package/dist/core/slash-commands.d.ts.map +1 -1
  45. package/dist/core/slash-commands.js +1 -0
  46. package/dist/core/slash-commands.js.map +1 -1
  47. package/dist/core/system-prompt.d.ts.map +1 -1
  48. package/dist/core/system-prompt.js +4 -3
  49. package/dist/core/system-prompt.js.map +1 -1
  50. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  51. package/dist/modes/interactive/interactive-mode.js +1 -1
  52. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  53. package/docs/usage.md +1 -0
  54. package/docs/workflows.md +173 -0
  55. package/node_modules/@earendil-works/pi-tui/README.md +779 -0
  56. package/node_modules/@earendil-works/pi-tui/dist/autocomplete.d.ts +54 -0
  57. package/node_modules/@earendil-works/pi-tui/dist/autocomplete.d.ts.map +1 -0
  58. package/node_modules/@earendil-works/pi-tui/dist/autocomplete.js +632 -0
  59. package/node_modules/@earendil-works/pi-tui/dist/autocomplete.js.map +1 -0
  60. package/node_modules/@earendil-works/pi-tui/dist/components/box.d.ts +22 -0
  61. package/node_modules/@earendil-works/pi-tui/dist/components/box.d.ts.map +1 -0
  62. package/node_modules/@earendil-works/pi-tui/dist/components/box.js +104 -0
  63. package/node_modules/@earendil-works/pi-tui/dist/components/box.js.map +1 -0
  64. package/node_modules/@earendil-works/pi-tui/dist/components/cancellable-loader.d.ts +22 -0
  65. package/node_modules/@earendil-works/pi-tui/dist/components/cancellable-loader.d.ts.map +1 -0
  66. package/node_modules/@earendil-works/pi-tui/dist/components/cancellable-loader.js +35 -0
  67. package/node_modules/@earendil-works/pi-tui/dist/components/cancellable-loader.js.map +1 -0
  68. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts +249 -0
  69. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -0
  70. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +1857 -0
  71. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -0
  72. package/node_modules/@earendil-works/pi-tui/dist/components/image.d.ts +28 -0
  73. package/node_modules/@earendil-works/pi-tui/dist/components/image.d.ts.map +1 -0
  74. package/node_modules/@earendil-works/pi-tui/dist/components/image.js +89 -0
  75. package/node_modules/@earendil-works/pi-tui/dist/components/image.js.map +1 -0
  76. package/node_modules/@earendil-works/pi-tui/dist/components/input.d.ts +37 -0
  77. package/node_modules/@earendil-works/pi-tui/dist/components/input.d.ts.map +1 -0
  78. package/node_modules/@earendil-works/pi-tui/dist/components/input.js +378 -0
  79. package/node_modules/@earendil-works/pi-tui/dist/components/input.js.map +1 -0
  80. package/node_modules/@earendil-works/pi-tui/dist/components/loader.d.ts +31 -0
  81. package/node_modules/@earendil-works/pi-tui/dist/components/loader.d.ts.map +1 -0
  82. package/node_modules/@earendil-works/pi-tui/dist/components/loader.js +69 -0
  83. package/node_modules/@earendil-works/pi-tui/dist/components/loader.js.map +1 -0
  84. package/node_modules/@earendil-works/pi-tui/dist/components/markdown.d.ts +96 -0
  85. package/node_modules/@earendil-works/pi-tui/dist/components/markdown.d.ts.map +1 -0
  86. package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js +644 -0
  87. package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js.map +1 -0
  88. package/node_modules/@earendil-works/pi-tui/dist/components/select-list.d.ts +50 -0
  89. package/node_modules/@earendil-works/pi-tui/dist/components/select-list.d.ts.map +1 -0
  90. package/node_modules/@earendil-works/pi-tui/dist/components/select-list.js +159 -0
  91. package/node_modules/@earendil-works/pi-tui/dist/components/select-list.js.map +1 -0
  92. package/node_modules/@earendil-works/pi-tui/dist/components/settings-list.d.ts +50 -0
  93. package/node_modules/@earendil-works/pi-tui/dist/components/settings-list.d.ts.map +1 -0
  94. package/node_modules/@earendil-works/pi-tui/dist/components/settings-list.js +185 -0
  95. package/node_modules/@earendil-works/pi-tui/dist/components/settings-list.js.map +1 -0
  96. package/node_modules/@earendil-works/pi-tui/dist/components/spacer.d.ts +12 -0
  97. package/node_modules/@earendil-works/pi-tui/dist/components/spacer.d.ts.map +1 -0
  98. package/node_modules/@earendil-works/pi-tui/dist/components/spacer.js +23 -0
  99. package/node_modules/@earendil-works/pi-tui/dist/components/spacer.js.map +1 -0
  100. package/node_modules/@earendil-works/pi-tui/dist/components/text.d.ts +19 -0
  101. package/node_modules/@earendil-works/pi-tui/dist/components/text.d.ts.map +1 -0
  102. package/node_modules/@earendil-works/pi-tui/dist/components/text.js +89 -0
  103. package/node_modules/@earendil-works/pi-tui/dist/components/text.js.map +1 -0
  104. package/node_modules/@earendil-works/pi-tui/dist/components/truncated-text.d.ts +13 -0
  105. package/node_modules/@earendil-works/pi-tui/dist/components/truncated-text.d.ts.map +1 -0
  106. package/node_modules/@earendil-works/pi-tui/dist/components/truncated-text.js +51 -0
  107. package/node_modules/@earendil-works/pi-tui/dist/components/truncated-text.js.map +1 -0
  108. package/node_modules/@earendil-works/pi-tui/dist/editor-component.d.ts +39 -0
  109. package/node_modules/@earendil-works/pi-tui/dist/editor-component.d.ts.map +1 -0
  110. package/node_modules/@earendil-works/pi-tui/dist/editor-component.js +2 -0
  111. package/node_modules/@earendil-works/pi-tui/dist/editor-component.js.map +1 -0
  112. package/node_modules/@earendil-works/pi-tui/dist/fuzzy.d.ts +16 -0
  113. package/node_modules/@earendil-works/pi-tui/dist/fuzzy.d.ts.map +1 -0
  114. package/node_modules/@earendil-works/pi-tui/dist/fuzzy.js +110 -0
  115. package/node_modules/@earendil-works/pi-tui/dist/fuzzy.js.map +1 -0
  116. package/node_modules/@earendil-works/pi-tui/dist/index.d.ts +23 -0
  117. package/node_modules/@earendil-works/pi-tui/dist/index.d.ts.map +1 -0
  118. package/node_modules/@earendil-works/pi-tui/dist/index.js +32 -0
  119. package/node_modules/@earendil-works/pi-tui/dist/index.js.map +1 -0
  120. package/node_modules/@earendil-works/pi-tui/dist/keybindings.d.ts +193 -0
  121. package/node_modules/@earendil-works/pi-tui/dist/keybindings.d.ts.map +1 -0
  122. package/node_modules/@earendil-works/pi-tui/dist/keybindings.js +174 -0
  123. package/node_modules/@earendil-works/pi-tui/dist/keybindings.js.map +1 -0
  124. package/node_modules/@earendil-works/pi-tui/dist/keys.d.ts +184 -0
  125. package/node_modules/@earendil-works/pi-tui/dist/keys.d.ts.map +1 -0
  126. package/node_modules/@earendil-works/pi-tui/dist/keys.js +1173 -0
  127. package/node_modules/@earendil-works/pi-tui/dist/keys.js.map +1 -0
  128. package/node_modules/@earendil-works/pi-tui/dist/kill-ring.d.ts +28 -0
  129. package/node_modules/@earendil-works/pi-tui/dist/kill-ring.d.ts.map +1 -0
  130. package/node_modules/@earendil-works/pi-tui/dist/kill-ring.js +44 -0
  131. package/node_modules/@earendil-works/pi-tui/dist/kill-ring.js.map +1 -0
  132. package/node_modules/@earendil-works/pi-tui/dist/native-modifiers.d.ts +3 -0
  133. package/node_modules/@earendil-works/pi-tui/dist/native-modifiers.d.ts.map +1 -0
  134. package/node_modules/@earendil-works/pi-tui/dist/native-modifiers.js +53 -0
  135. package/node_modules/@earendil-works/pi-tui/dist/native-modifiers.js.map +1 -0
  136. package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.d.ts +50 -0
  137. package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.d.ts.map +1 -0
  138. package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.js +361 -0
  139. package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.js.map +1 -0
  140. package/node_modules/@earendil-works/pi-tui/dist/terminal-image.d.ts +90 -0
  141. package/node_modules/@earendil-works/pi-tui/dist/terminal-image.d.ts.map +1 -0
  142. package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js +366 -0
  143. package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js.map +1 -0
  144. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts +113 -0
  145. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +1 -0
  146. package/node_modules/@earendil-works/pi-tui/dist/terminal.js +472 -0
  147. package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +1 -0
  148. package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts +227 -0
  149. package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts.map +1 -0
  150. package/node_modules/@earendil-works/pi-tui/dist/tui.js +1106 -0
  151. package/node_modules/@earendil-works/pi-tui/dist/tui.js.map +1 -0
  152. package/node_modules/@earendil-works/pi-tui/dist/undo-stack.d.ts +17 -0
  153. package/node_modules/@earendil-works/pi-tui/dist/undo-stack.d.ts.map +1 -0
  154. package/node_modules/@earendil-works/pi-tui/dist/undo-stack.js +25 -0
  155. package/node_modules/@earendil-works/pi-tui/dist/undo-stack.js.map +1 -0
  156. package/node_modules/@earendil-works/pi-tui/dist/utils.d.ts +84 -0
  157. package/node_modules/@earendil-works/pi-tui/dist/utils.d.ts.map +1 -0
  158. package/node_modules/@earendil-works/pi-tui/dist/utils.js +1029 -0
  159. package/node_modules/@earendil-works/pi-tui/dist/utils.js.map +1 -0
  160. package/node_modules/@earendil-works/pi-tui/dist/word-navigation.d.ts +25 -0
  161. package/node_modules/@earendil-works/pi-tui/dist/word-navigation.d.ts.map +1 -0
  162. package/node_modules/@earendil-works/pi-tui/dist/word-navigation.js +96 -0
  163. package/node_modules/@earendil-works/pi-tui/dist/word-navigation.js.map +1 -0
  164. package/node_modules/@earendil-works/pi-tui/native/darwin/prebuilds/darwin-arm64/darwin-modifiers.node +0 -0
  165. package/node_modules/@earendil-works/pi-tui/native/darwin/prebuilds/darwin-x64/darwin-modifiers.node +0 -0
  166. package/node_modules/@earendil-works/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  167. package/node_modules/@earendil-works/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  168. package/node_modules/@earendil-works/pi-tui/package.json +47 -0
  169. package/node_modules/get-east-asian-width/index.d.ts +60 -0
  170. package/node_modules/get-east-asian-width/index.js +30 -0
  171. package/node_modules/get-east-asian-width/license +9 -0
  172. package/node_modules/get-east-asian-width/lookup-data.js +21 -0
  173. package/node_modules/get-east-asian-width/lookup.js +138 -0
  174. package/node_modules/get-east-asian-width/package.json +71 -0
  175. package/node_modules/get-east-asian-width/readme.md +65 -0
  176. package/node_modules/get-east-asian-width/utilities.js +24 -0
  177. package/node_modules/marked/LICENSE.md +44 -0
  178. package/node_modules/marked/README.md +106 -0
  179. package/node_modules/marked/bin/main.js +282 -0
  180. package/node_modules/marked/bin/marked.js +15 -0
  181. package/node_modules/marked/lib/marked.cjs +2211 -0
  182. package/node_modules/marked/lib/marked.cjs.map +7 -0
  183. package/node_modules/marked/lib/marked.d.cts +728 -0
  184. package/node_modules/marked/lib/marked.d.ts +728 -0
  185. package/node_modules/marked/lib/marked.esm.js +2189 -0
  186. package/node_modules/marked/lib/marked.esm.js.map +7 -0
  187. package/node_modules/marked/lib/marked.umd.js +2213 -0
  188. package/node_modules/marked/lib/marked.umd.js.map +7 -0
  189. package/node_modules/marked/man/marked.1 +111 -0
  190. package/node_modules/marked/man/marked.1.md +92 -0
  191. package/node_modules/marked/marked.min.js +69 -0
  192. package/node_modules/marked/package.json +111 -0
  193. package/package.json +9 -1
@@ -11,7 +11,7 @@ import { APP_NAME, getEnvValue, type ExtensionAPI } from "@bastani/atomic";
11
11
  import type { AgentConfig } from "../../agents/agents.ts";
12
12
  import { applyThinkingSuffix, SUBAGENT_INTERCOM_SESSION_NAME_ENV } from "../shared/pi-args.ts";
13
13
  import { injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
14
- import { buildChainInstructions, isParallelStep, resolveStepBehavior, suppressProgressForReadOnlyTask, writeInitialProgressFile, type ChainStep, type ResolvedStepBehavior, type SequentialStep, type StepOverrides } from "../../shared/settings.ts";
14
+ import { buildChainInstructions, isDynamicParallelStep, isParallelStep, resolveStepBehavior, suppressProgressForReadOnlyTask, writeInitialProgressFile, type ChainStep, type ResolvedStepBehavior, type SequentialStep, type StepOverrides } from "../../shared/settings.ts";
15
15
  import type { RunnerStep } from "../shared/parallel-utils.ts";
16
16
  import { resolvePiPackageRoot } from "../shared/pi-spawn.ts";
17
17
  import { buildSkillInjection, normalizeSkillInput, resolveSkillsWithFallback } from "../../agents/skills.ts";
@@ -24,7 +24,12 @@ import {
24
24
  resolveSubagentModelFastModeMetadata,
25
25
  } from "../../shared/fast-mode.ts";
26
26
  import { resolveExpectedWorktreeAgentCwd } from "../shared/worktree.ts";
27
+ import { buildWorkflowGraphSnapshot } from "../shared/workflow-graph.ts";
28
+ import { ChainOutputValidationError, validateChainOutputBindings } from "../shared/chain-outputs.ts";
29
+ import { createStructuredOutputRuntime } from "../shared/structured-output.ts";
30
+ import { resolveEffectiveAcceptance } from "../shared/acceptance.ts";
27
31
  import {
32
+ type AcceptanceInput,
28
33
  type ArtifactConfig,
29
34
  type Details,
30
35
  type MaxOutputConfig,
@@ -112,6 +117,7 @@ interface AsyncChainParams {
112
117
  sessionRoot?: string;
113
118
  chainSkills?: string[];
114
119
  sessionFilesByFlatIndex?: (string | undefined)[];
120
+ dynamicFanoutMaxItems?: number;
115
121
  maxSubagentDepth: number;
116
122
  workflowStageSubagentGuard?: boolean;
117
123
  worktreeSetupHook?: string;
@@ -120,6 +126,7 @@ interface AsyncChainParams {
120
126
  controlIntercomTarget?: string;
121
127
  childIntercomTarget?: (agent: string, index: number) => string | undefined;
122
128
  nestedRoute?: NestedRouteInfo;
129
+ acceptance?: AcceptanceInput;
123
130
  }
124
131
 
125
132
  interface AsyncSingleParams {
@@ -147,6 +154,7 @@ interface AsyncSingleParams {
147
154
  controlIntercomTarget?: string;
148
155
  childIntercomTarget?: (agent: string, index: number) => string | undefined;
149
156
  nestedRoute?: NestedRouteInfo;
157
+ acceptance?: AcceptanceInput;
150
158
  }
151
159
 
152
160
  interface AsyncExecutionResult {
@@ -262,12 +270,25 @@ export function executeAsyncChain(
262
270
  const runnerCwd = resolveChildCwd(ctx.cwd, cwd);
263
271
  const firstStep = chain[0];
264
272
  const originalTask = params.task ?? (firstStep
265
- ? (isParallelStep(firstStep) ? firstStep.parallel[0]?.task : (firstStep as SequentialStep).task)
273
+ ? (isParallelStep(firstStep)
274
+ ? firstStep.parallel[0]?.task
275
+ : isDynamicParallelStep(firstStep)
276
+ ? firstStep.parallel.task
277
+ : (firstStep as SequentialStep).task)
266
278
  : undefined);
279
+ try {
280
+ validateChainOutputBindings(chain, { maxItems: params.dynamicFanoutMaxItems });
281
+ } catch (error) {
282
+ if (error instanceof ChainOutputValidationError) return formatAsyncStartError(resultMode, error.message);
283
+ throw error;
284
+ }
285
+ const workflowGraph = buildWorkflowGraphSnapshot({ runId: id, mode: resultMode, steps: chain });
267
286
 
268
287
  for (const s of chain) {
269
288
  const stepAgents = isParallelStep(s)
270
289
  ? s.parallel.map((t) => t.agent)
290
+ : isDynamicParallelStep(s)
291
+ ? [s.parallel.agent]
271
292
  : [(s as SequentialStep).agent];
272
293
  for (const agentName of stepAgents) {
273
294
  if (!agents.find((x) => x.name === agentName)) {
@@ -330,7 +351,10 @@ export function executeAsyncChain(
330
351
  const outputPath = resolveSingleOutputPath(behavior.output, ctx.cwd, instructionCwd);
331
352
  const validationError = validateFileOnlyOutputMode(behavior.outputMode, outputPath, `Async step (${s.agent})`);
332
353
  if (validationError) throw new AsyncStartValidationError(validationError);
333
- const task = injectSingleOutputInstruction(`${readInstructions.prefix}${s.task ?? "{previous}"}${progressInstructions.suffix}`, outputPath);
354
+ let taskTemplate = s.task ?? "{previous}";
355
+ taskTemplate = taskTemplate.replace(/\{task\}/g, originalTask ?? "");
356
+ taskTemplate = taskTemplate.replace(/\{chain_dir\}/g, runnerCwd);
357
+ const task = injectSingleOutputInstruction(`${readInstructions.prefix}${taskTemplate}${progressInstructions.suffix}`, outputPath);
334
358
 
335
359
  const primaryModel = resolveModelCandidate(behavior.model ?? a.model, availableModels, ctx.currentModelProvider);
336
360
  const model = applyThinkingSuffix(primaryModel, a.thinking);
@@ -341,6 +365,10 @@ export function executeAsyncChain(
341
365
  return {
342
366
  agent: s.agent,
343
367
  task,
368
+ phase: s.phase,
369
+ label: s.label,
370
+ outputName: s.as,
371
+ structured: Boolean(s.outputSchema),
344
372
  cwd: stepCwd,
345
373
  model,
346
374
  thinking: resolveEffectiveThinking(model, a.thinking),
@@ -362,6 +390,16 @@ export function executeAsyncChain(
362
390
  sessionFile,
363
391
  maxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, a.maxSubagentDepth),
364
392
  workflowStageSubagentGuard,
393
+ effectiveAcceptance: resolveEffectiveAcceptance({
394
+ explicit: s.acceptance,
395
+ agentName: s.agent,
396
+ task: s.task,
397
+ mode: resultMode,
398
+ async: true,
399
+ dynamic: false,
400
+ }),
401
+ ...(s.outputSchema ? { structuredOutputSchema: s.outputSchema } : {}),
402
+ ...(s.outputSchema ? { structuredOutput: createStructuredOutputRuntime(s.outputSchema, path.join(asyncDir, "structured-output")) } : {}),
365
403
  };
366
404
  };
367
405
 
@@ -402,6 +440,32 @@ export function executeAsyncChain(
402
440
  worktree: s.worktree,
403
441
  };
404
442
  }
443
+ if (isDynamicParallelStep(s)) {
444
+ const agent = agents.find((candidate) => candidate.name === s.parallel.agent)!;
445
+ const behavior = suppressProgressForReadOnlyTask(resolveStepBehavior(agent, buildStepOverrides(s.parallel), chainSkills), s.parallel.task, originalTask);
446
+ const progressPrecreated = behavior.progress;
447
+ if (progressPrecreated) {
448
+ writeInitialProgressFile(runnerCwd);
449
+ progressInstructionCreated = true;
450
+ }
451
+ return {
452
+ expand: s.expand,
453
+ parallel: buildSeqStep(s.parallel as SequentialStep, undefined, undefined, progressPrecreated, behavior),
454
+ collect: s.collect,
455
+ concurrency: s.concurrency,
456
+ failFast: s.failFast,
457
+ phase: s.phase,
458
+ label: s.label,
459
+ effectiveAcceptance: resolveEffectiveAcceptance({
460
+ explicit: s.acceptance,
461
+ agentName: s.parallel.agent,
462
+ task: s.parallel.task,
463
+ mode: resultMode,
464
+ async: true,
465
+ dynamicGroup: true,
466
+ }),
467
+ };
468
+ }
405
469
  return buildSeqStep(s as SequentialStep, nextSessionFile());
406
470
  });
407
471
  } catch (error) {
@@ -411,6 +475,10 @@ export function executeAsyncChain(
411
475
  let childTargetIndex = 0;
412
476
  const childIntercomTargets = childIntercomTarget ? steps.flatMap((step) => {
413
477
  if ("parallel" in step) {
478
+ if (!Array.isArray(step.parallel)) {
479
+ childTargetIndex++;
480
+ return [undefined];
481
+ }
414
482
  return step.parallel.map((task) => childIntercomTarget(task.agent, childTargetIndex++));
415
483
  }
416
484
  return [childIntercomTarget(step.agent, childTargetIndex++)];
@@ -440,6 +508,8 @@ export function executeAsyncChain(
440
508
  controlIntercomTarget,
441
509
  childIntercomTargets,
442
510
  resultMode,
511
+ dynamicFanoutMaxItems: params.dynamicFanoutMaxItems,
512
+ workflowGraph,
443
513
  nestedRoute: nestedRoute ?? inheritedNestedRoute,
444
514
  workflowStageSubagentGuard,
445
515
  nestedSelf: inheritedNestedRoute && nestedAddress ? {
@@ -465,6 +535,8 @@ export function executeAsyncChain(
465
535
  const firstStep = chain[0];
466
536
  const firstAgents = isParallelStep(firstStep)
467
537
  ? firstStep.parallel.map((t) => t.agent)
538
+ : isDynamicParallelStep(firstStep)
539
+ ? [firstStep.parallel.agent]
468
540
  : [(firstStep as SequentialStep).agent];
469
541
  const parallelGroups: Array<{ start: number; count: number; stepIndex: number }> = [];
470
542
  const flatAgents: string[] = [];
@@ -475,6 +547,10 @@ export function executeAsyncChain(
475
547
  parallelGroups.push({ start: flatStepStart, count: step.parallel.length, stepIndex });
476
548
  flatAgents.push(...step.parallel.map((task) => task.agent));
477
549
  flatStepStart += step.parallel.length;
550
+ } else if (isDynamicParallelStep(step)) {
551
+ parallelGroups.push({ start: flatStepStart, count: 1, stepIndex });
552
+ flatAgents.push(step.parallel.agent);
553
+ flatStepStart++;
478
554
  } else {
479
555
  flatAgents.push((step as SequentialStep).agent);
480
556
  flatStepStart++;
@@ -523,12 +599,15 @@ export function executeAsyncChain(
523
599
  agents: flatAgents,
524
600
  task: isParallelStep(firstStep)
525
601
  ? firstStep.parallel[0]?.task?.slice(0, 50)
602
+ : isDynamicParallelStep(firstStep)
603
+ ? firstStep.parallel.task?.slice(0, 50)
526
604
  : (firstStep as SequentialStep).task?.slice(0, 50),
527
605
  chain: chain.map((s) =>
528
- isParallelStep(s) ? `[${s.parallel.map((t) => t.agent).join("+")}]` : (s as SequentialStep).agent,
606
+ isParallelStep(s) ? `[${s.parallel.map((t) => t.agent).join("+")}]` : isDynamicParallelStep(s) ? `expand:${s.parallel.agent}` : (s as SequentialStep).agent,
529
607
  ),
530
608
  chainStepCount: chain.length,
531
609
  parallelGroups,
610
+ workflowGraph,
532
611
  cwd: runnerCwd,
533
612
  asyncDir,
534
613
  nestedRoute,
@@ -537,13 +616,13 @@ export function executeAsyncChain(
537
616
 
538
617
  const chainDesc = chain
539
618
  .map((s) =>
540
- isParallelStep(s) ? `[${s.parallel.map((t) => t.agent).join("+")}]` : (s as SequentialStep).agent,
619
+ isParallelStep(s) ? `[${s.parallel.map((t) => t.agent).join("+")}]` : isDynamicParallelStep(s) ? `expand:${s.parallel.agent}` : (s as SequentialStep).agent,
541
620
  )
542
621
  .join(" -> ");
543
622
 
544
623
  return {
545
624
  content: [{ type: "text", text: formatAsyncStartedMessage(`Async ${resultMode}: ${chainDesc} [${id}]`) }],
546
- details: { mode: resultMode, runId: id, results: [], asyncId: id, asyncDir },
625
+ details: { mode: resultMode, runId: id, results: [], asyncId: id, asyncDir, workflowGraph },
547
626
  };
548
627
  }
549
628
 
@@ -647,6 +726,13 @@ export function executeAsyncSingle(
647
726
  sessionFile,
648
727
  maxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, agentConfig.maxSubagentDepth),
649
728
  workflowStageSubagentGuard,
729
+ effectiveAcceptance: resolveEffectiveAcceptance({
730
+ explicit: params.acceptance,
731
+ agentName: agent,
732
+ task,
733
+ mode: "single",
734
+ async: true,
735
+ }),
650
736
  },
651
737
  ],
652
738
  resultPath: inheritedNestedRoute ? nestedResultsPath(inheritedNestedRoute.rootRunId, id) : path.join(RESULTS_DIR, `${id}.json`),
@@ -12,6 +12,10 @@ import { reconcileAsyncRun, reconcileNestedAsyncDescendants } from "./stale-run-
12
12
  interface AsyncRunStepSummary {
13
13
  index: number;
14
14
  agent: string;
15
+ label?: string;
16
+ phase?: string;
17
+ outputName?: string;
18
+ structured?: boolean;
15
19
  status: AsyncJobStep["status"];
16
20
  activityState?: ActivityState;
17
21
  lastActivityAt?: number;
@@ -140,6 +144,10 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
140
144
  return {
141
145
  index,
142
146
  agent: step.agent,
147
+ ...(step.label ? { label: step.label } : {}),
148
+ ...(step.phase ? { phase: step.phase } : {}),
149
+ ...(step.outputName ? { outputName: step.outputName } : {}),
150
+ ...(step.structured ? { structured: step.structured } : {}),
143
151
  status: step.status,
144
152
  ...(stepActivityState ? { activityState: stepActivityState } : {}),
145
153
  ...(stepLastActivityAt ? { lastActivityAt: stepLastActivityAt } : {}),
@@ -261,7 +269,9 @@ function formatActivityFacts(input: { activityState?: ActivityState; lastActivit
261
269
  }
262
270
 
263
271
  function formatStepLine(step: AsyncRunStepSummary): string {
264
- const parts = [`${step.index + 1}. ${step.agent}`, step.status];
272
+ const display = step.label ? `${step.label} (${step.agent})` : step.agent;
273
+ const phase = step.phase ? `[${step.phase}] ` : "";
274
+ const parts = [`${step.index + 1}. ${phase}${display}`, step.status];
265
275
  const activity = formatActivityFacts(step);
266
276
  if (activity) parts.push(activity);
267
277
  const modelThinking = formatModelThinking(step.model, step.thinking, step.fastMode);
@@ -216,7 +216,10 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
216
216
  const modelThinking = formatModelThinking(step.model, step.thinking, step.fastMode);
217
217
  const modelText = modelThinking ? ` (${modelThinking})` : "";
218
218
  const errorText = step.error ? `, error: ${step.error}` : "";
219
- lines.push(`${stepLineLabel(status, index)}: ${step.agent} ${step.status}${modelText}${stepActivityText ? `, ${stepActivityText}` : ""}${errorText}`);
219
+ const acceptanceText = step.acceptance?.status ? `, acceptance: ${step.acceptance.status}` : "";
220
+ const display = step.label ? `${step.label} (${step.agent})` : step.agent;
221
+ const phase = step.phase ? `[${step.phase}] ` : "";
222
+ lines.push(`${stepLineLabel(status, index)}: ${phase}${display} ${step.status}${modelText}${stepActivityText ? `, ${stepActivityText}` : ""}${acceptanceText}${errorText}`);
220
223
  lines.push(...formatNestedRunStatusLines(step.children, { indent: " ", commandHints: true, maxLines: 20 }));
221
224
  const stepOutputPath = path.join(asyncDir, `output-${index}.log`);
222
225
  if (stepOutputPath !== outputPath && fs.existsSync(stepOutputPath)) lines.push(` Output: ${stepOutputPath}`);