@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.
- package/CHANGELOG.md +12 -0
- package/README.md +2 -1
- package/dist/builtin/intercom/CHANGELOG.md +12 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/CHANGELOG.md +12 -0
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +16 -0
- package/dist/builtin/subagents/README.md +132 -21
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/prompts/parallel-context-build.md +4 -2
- package/dist/builtin/subagents/prompts/parallel-handoff-plan.md +3 -1
- package/dist/builtin/subagents/skills/subagent/SKILL.md +49 -11
- package/dist/builtin/subagents/src/agents/agent-management.ts +79 -16
- package/dist/builtin/subagents/src/agents/agents.ts +47 -16
- package/dist/builtin/subagents/src/agents/chain-serializer.ts +114 -0
- package/dist/builtin/subagents/src/extension/schemas.ts +139 -3
- package/dist/builtin/subagents/src/runs/background/async-execution.ts +92 -6
- package/dist/builtin/subagents/src/runs/background/async-status.ts +11 -1
- package/dist/builtin/subagents/src/runs/background/run-status.ts +4 -1
- package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +529 -32
- package/dist/builtin/subagents/src/runs/foreground/chain-execution.ts +361 -118
- package/dist/builtin/subagents/src/runs/foreground/execution.ts +75 -7
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +33 -0
- package/dist/builtin/subagents/src/runs/shared/acceptance.ts +611 -0
- package/dist/builtin/subagents/src/runs/shared/chain-outputs.ts +101 -0
- package/dist/builtin/subagents/src/runs/shared/dynamic-fanout.ts +293 -0
- package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +29 -1
- package/dist/builtin/subagents/src/runs/shared/pi-args.ts +11 -0
- package/dist/builtin/subagents/src/runs/shared/structured-output.ts +79 -0
- package/dist/builtin/subagents/src/runs/shared/subagent-prompt-runtime.ts +52 -2
- package/dist/builtin/subagents/src/runs/shared/workflow-graph.ts +206 -0
- package/dist/builtin/subagents/src/shared/formatters.ts +2 -2
- package/dist/builtin/subagents/src/shared/settings.ts +53 -4
- package/dist/builtin/subagents/src/shared/types.ts +226 -0
- package/dist/builtin/subagents/src/shared/utils.ts +2 -1
- package/dist/builtin/subagents/src/slash/slash-commands.ts +41 -3
- package/dist/builtin/subagents/src/tui/render.ts +152 -34
- package/dist/builtin/web-access/CHANGELOG.md +12 -0
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +12 -0
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/skills/create-spec/SKILL.md +1 -1
- package/dist/builtin/workflows/src/tui/stage-chat-view.ts +0 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +4 -3
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +1 -1
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/docs/usage.md +1 -0
- package/docs/workflows.md +173 -0
- package/node_modules/@earendil-works/pi-tui/README.md +779 -0
- package/node_modules/@earendil-works/pi-tui/dist/autocomplete.d.ts +54 -0
- package/node_modules/@earendil-works/pi-tui/dist/autocomplete.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/autocomplete.js +632 -0
- package/node_modules/@earendil-works/pi-tui/dist/autocomplete.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/box.d.ts +22 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/box.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/box.js +104 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/box.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/cancellable-loader.d.ts +22 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/cancellable-loader.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/cancellable-loader.js +35 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/cancellable-loader.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts +249 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +1857 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/image.d.ts +28 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/image.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/image.js +89 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/image.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/input.d.ts +37 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/input.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/input.js +378 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/input.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/loader.d.ts +31 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/loader.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/loader.js +69 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/loader.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.d.ts +96 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js +644 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/select-list.d.ts +50 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/select-list.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/select-list.js +159 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/select-list.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/settings-list.d.ts +50 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/settings-list.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/settings-list.js +185 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/settings-list.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/spacer.d.ts +12 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/spacer.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/spacer.js +23 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/spacer.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/text.d.ts +19 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/text.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/text.js +89 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/text.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/truncated-text.d.ts +13 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/truncated-text.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/truncated-text.js +51 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/truncated-text.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/editor-component.d.ts +39 -0
- package/node_modules/@earendil-works/pi-tui/dist/editor-component.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/editor-component.js +2 -0
- package/node_modules/@earendil-works/pi-tui/dist/editor-component.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/fuzzy.d.ts +16 -0
- package/node_modules/@earendil-works/pi-tui/dist/fuzzy.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/fuzzy.js +110 -0
- package/node_modules/@earendil-works/pi-tui/dist/fuzzy.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/index.d.ts +23 -0
- package/node_modules/@earendil-works/pi-tui/dist/index.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/index.js +32 -0
- package/node_modules/@earendil-works/pi-tui/dist/index.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/keybindings.d.ts +193 -0
- package/node_modules/@earendil-works/pi-tui/dist/keybindings.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/keybindings.js +174 -0
- package/node_modules/@earendil-works/pi-tui/dist/keybindings.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/keys.d.ts +184 -0
- package/node_modules/@earendil-works/pi-tui/dist/keys.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/keys.js +1173 -0
- package/node_modules/@earendil-works/pi-tui/dist/keys.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/kill-ring.d.ts +28 -0
- package/node_modules/@earendil-works/pi-tui/dist/kill-ring.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/kill-ring.js +44 -0
- package/node_modules/@earendil-works/pi-tui/dist/kill-ring.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/native-modifiers.d.ts +3 -0
- package/node_modules/@earendil-works/pi-tui/dist/native-modifiers.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/native-modifiers.js +53 -0
- package/node_modules/@earendil-works/pi-tui/dist/native-modifiers.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.d.ts +50 -0
- package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.js +361 -0
- package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.d.ts +90 -0
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js +366 -0
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts +113 -0
- package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/terminal.js +472 -0
- package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts +227 -0
- package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/tui.js +1106 -0
- package/node_modules/@earendil-works/pi-tui/dist/tui.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/undo-stack.d.ts +17 -0
- package/node_modules/@earendil-works/pi-tui/dist/undo-stack.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/undo-stack.js +25 -0
- package/node_modules/@earendil-works/pi-tui/dist/undo-stack.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/utils.d.ts +84 -0
- package/node_modules/@earendil-works/pi-tui/dist/utils.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/utils.js +1029 -0
- package/node_modules/@earendil-works/pi-tui/dist/utils.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/word-navigation.d.ts +25 -0
- package/node_modules/@earendil-works/pi-tui/dist/word-navigation.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/word-navigation.js +96 -0
- package/node_modules/@earendil-works/pi-tui/dist/word-navigation.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/native/darwin/prebuilds/darwin-arm64/darwin-modifiers.node +0 -0
- package/node_modules/@earendil-works/pi-tui/native/darwin/prebuilds/darwin-x64/darwin-modifiers.node +0 -0
- package/node_modules/@earendil-works/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
- package/node_modules/@earendil-works/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
- package/node_modules/@earendil-works/pi-tui/package.json +47 -0
- package/node_modules/get-east-asian-width/index.d.ts +60 -0
- package/node_modules/get-east-asian-width/index.js +30 -0
- package/node_modules/get-east-asian-width/license +9 -0
- package/node_modules/get-east-asian-width/lookup-data.js +21 -0
- package/node_modules/get-east-asian-width/lookup.js +138 -0
- package/node_modules/get-east-asian-width/package.json +71 -0
- package/node_modules/get-east-asian-width/readme.md +65 -0
- package/node_modules/get-east-asian-width/utilities.js +24 -0
- package/node_modules/marked/LICENSE.md +44 -0
- package/node_modules/marked/README.md +106 -0
- package/node_modules/marked/bin/main.js +282 -0
- package/node_modules/marked/bin/marked.js +15 -0
- package/node_modules/marked/lib/marked.cjs +2211 -0
- package/node_modules/marked/lib/marked.cjs.map +7 -0
- package/node_modules/marked/lib/marked.d.cts +728 -0
- package/node_modules/marked/lib/marked.d.ts +728 -0
- package/node_modules/marked/lib/marked.esm.js +2189 -0
- package/node_modules/marked/lib/marked.esm.js.map +7 -0
- package/node_modules/marked/lib/marked.umd.js +2213 -0
- package/node_modules/marked/lib/marked.umd.js.map +7 -0
- package/node_modules/marked/man/marked.1 +111 -0
- package/node_modules/marked/man/marked.1.md +92 -0
- package/node_modules/marked/marked.min.js +69 -0
- package/node_modules/marked/package.json +111 -0
- package/package.json +9 -1
|
@@ -20,9 +20,11 @@ import {
|
|
|
20
20
|
createParallelDirs,
|
|
21
21
|
suppressProgressForReadOnlyTask,
|
|
22
22
|
aggregateParallelOutputs,
|
|
23
|
+
isDynamicParallelStep,
|
|
23
24
|
isParallelStep,
|
|
24
25
|
type StepOverrides,
|
|
25
26
|
type ChainStep,
|
|
27
|
+
type ParallelStep,
|
|
26
28
|
type SequentialStep,
|
|
27
29
|
type ParallelTaskResult,
|
|
28
30
|
type ResolvedStepBehavior,
|
|
@@ -60,6 +62,12 @@ import {
|
|
|
60
62
|
} from "../../shared/types.ts";
|
|
61
63
|
import { resolveModelCandidate } from "../shared/model-fallback.ts";
|
|
62
64
|
import { validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
65
|
+
import { buildWorkflowGraphSnapshot } from "../shared/workflow-graph.ts";
|
|
66
|
+
import { ChainOutputValidationError, outputEntryFromResult, resolveOutputReferences, validateChainOutputBindings } from "../shared/chain-outputs.ts";
|
|
67
|
+
import { createStructuredOutputRuntime } from "../shared/structured-output.ts";
|
|
68
|
+
import { collectDynamicResults, DynamicFanoutError, materializeDynamicParallelStep, validateDynamicCollection, type DynamicCollectedResult } from "../shared/dynamic-fanout.ts";
|
|
69
|
+
import { acceptanceFailureMessage, aggregateAcceptanceReport, evaluateAcceptance, resolveEffectiveAcceptance } from "../shared/acceptance.ts";
|
|
70
|
+
import type { ChainOutputMap } from "../../shared/types.ts";
|
|
63
71
|
|
|
64
72
|
type RunSyncDependency = typeof runSync;
|
|
65
73
|
|
|
@@ -85,12 +93,18 @@ interface ChainExecutionDetailsInput {
|
|
|
85
93
|
allArtifactPaths: ArtifactPaths[];
|
|
86
94
|
artifactsDir: string;
|
|
87
95
|
chainAgents: string[];
|
|
96
|
+
chainSteps: ChainStep[];
|
|
88
97
|
totalSteps: number;
|
|
89
98
|
currentStepIndex?: number;
|
|
99
|
+
runId: string;
|
|
100
|
+
outputs?: ChainOutputMap;
|
|
101
|
+
currentFlatIndex?: number;
|
|
102
|
+
dynamicChildren?: Record<number, Array<{ agent: string; label?: string; flatIndex: number; itemKey: string; outputName?: string; structured?: boolean; error?: string }>>;
|
|
103
|
+
dynamicGroupStatuses?: Record<number, { status: "pending" | "running" | "completed" | "failed" | "paused" | "detached"; error?: string; acceptance?: SingleResult["acceptance"] }>;
|
|
90
104
|
}
|
|
91
105
|
|
|
92
106
|
interface ParallelChainRunInput {
|
|
93
|
-
step:
|
|
107
|
+
step: ParallelStep;
|
|
94
108
|
parallelTemplates: string[];
|
|
95
109
|
parallelBehaviors: ResolvedStepBehavior[];
|
|
96
110
|
agents: AgentConfig[];
|
|
@@ -118,8 +132,12 @@ interface ParallelChainRunInput {
|
|
|
118
132
|
foregroundControl?: ChainForegroundControl;
|
|
119
133
|
results: SingleResult[];
|
|
120
134
|
allProgress: AgentProgress[];
|
|
135
|
+
outputs: ChainOutputMap;
|
|
121
136
|
chainAgents: string[];
|
|
137
|
+
chainSteps: ChainStep[];
|
|
122
138
|
totalSteps: number;
|
|
139
|
+
dynamicChildren?: ChainExecutionDetailsInput["dynamicChildren"];
|
|
140
|
+
dynamicGroupStatuses?: ChainExecutionDetailsInput["dynamicGroupStatuses"];
|
|
123
141
|
worktreeSetup?: WorktreeSetup;
|
|
124
142
|
maxSubagentDepth: number;
|
|
125
143
|
workflowStageSubagentGuard?: boolean;
|
|
@@ -136,6 +154,17 @@ function buildChainExecutionDetails(input: ChainExecutionDetailsInput): Details
|
|
|
136
154
|
chainAgents: input.chainAgents,
|
|
137
155
|
totalSteps: input.totalSteps,
|
|
138
156
|
currentStepIndex: input.currentStepIndex,
|
|
157
|
+
outputs: input.outputs,
|
|
158
|
+
workflowGraph: buildWorkflowGraphSnapshot({
|
|
159
|
+
runId: input.runId,
|
|
160
|
+
mode: "chain",
|
|
161
|
+
steps: input.chainSteps,
|
|
162
|
+
results: input.results,
|
|
163
|
+
currentStepIndex: input.currentStepIndex,
|
|
164
|
+
currentFlatIndex: input.currentFlatIndex,
|
|
165
|
+
dynamicChildren: input.dynamicChildren,
|
|
166
|
+
dynamicGroupStatuses: input.dynamicGroupStatuses,
|
|
167
|
+
}),
|
|
139
168
|
});
|
|
140
169
|
}
|
|
141
170
|
|
|
@@ -202,7 +231,7 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
202
231
|
templateHasPrevious ? undefined : input.prev,
|
|
203
232
|
);
|
|
204
233
|
|
|
205
|
-
let taskStr = taskTemplate;
|
|
234
|
+
let taskStr = resolveOutputReferences(taskTemplate, input.outputs);
|
|
206
235
|
taskStr = taskStr.replace(/\{task\}/g, input.originalTask);
|
|
207
236
|
taskStr = taskStr.replace(/\{previous\}/g, input.prev);
|
|
208
237
|
taskStr = taskStr.replace(/\{chain_dir\}/g, input.chainDir);
|
|
@@ -237,6 +266,9 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
237
266
|
};
|
|
238
267
|
}
|
|
239
268
|
|
|
269
|
+
const structuredRuntime = task.outputSchema
|
|
270
|
+
? createStructuredOutputRuntime(task.outputSchema, path.join(input.chainDir, "structured-output"))
|
|
271
|
+
: undefined;
|
|
240
272
|
const result = await input.runSync(input.ctx.cwd, input.agents, task.agent, taskStr, {
|
|
241
273
|
cwd: taskCwd,
|
|
242
274
|
signal: input.signal,
|
|
@@ -264,6 +296,9 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
264
296
|
currentModel: currentModelFullId(input.ctx.model),
|
|
265
297
|
preferredModelProvider: input.ctx.model?.provider,
|
|
266
298
|
skills: behavior.skills === false ? [] : behavior.skills,
|
|
299
|
+
structuredOutput: structuredRuntime,
|
|
300
|
+
acceptance: task.acceptance,
|
|
301
|
+
acceptanceContext: { mode: "chain" },
|
|
267
302
|
onUpdate: input.onUpdate
|
|
268
303
|
? (progressUpdate) => {
|
|
269
304
|
const stepResults = progressUpdate.details?.results || [];
|
|
@@ -292,6 +327,17 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
292
327
|
chainAgents: input.chainAgents,
|
|
293
328
|
totalSteps: input.totalSteps,
|
|
294
329
|
currentStepIndex: input.stepIndex,
|
|
330
|
+
outputs: input.outputs,
|
|
331
|
+
workflowGraph: buildWorkflowGraphSnapshot({
|
|
332
|
+
runId: input.runId,
|
|
333
|
+
mode: "chain",
|
|
334
|
+
steps: input.chainSteps,
|
|
335
|
+
results: input.results.concat(stepResults),
|
|
336
|
+
currentStepIndex: input.stepIndex,
|
|
337
|
+
currentFlatIndex: input.globalTaskIndex + taskIndex,
|
|
338
|
+
dynamicChildren: input.dynamicChildren,
|
|
339
|
+
dynamicGroupStatuses: input.dynamicGroupStatuses,
|
|
340
|
+
}),
|
|
295
341
|
},
|
|
296
342
|
});
|
|
297
343
|
}
|
|
@@ -337,6 +383,7 @@ interface ChainExecutionParams {
|
|
|
337
383
|
foregroundControl?: ChainForegroundControl;
|
|
338
384
|
chainSkills?: string[];
|
|
339
385
|
chainDir?: string;
|
|
386
|
+
dynamicFanoutMaxItems?: number;
|
|
340
387
|
maxSubagentDepth: number;
|
|
341
388
|
workflowStageSubagentGuard?: boolean;
|
|
342
389
|
nestedRoute?: NestedRouteInfo;
|
|
@@ -387,22 +434,60 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
387
434
|
} = params;
|
|
388
435
|
const chainSkills = chainSkillsParam ?? [];
|
|
389
436
|
|
|
437
|
+
const results: SingleResult[] = [];
|
|
438
|
+
const outputs: ChainOutputMap = {};
|
|
439
|
+
const dynamicChildren: ChainExecutionDetailsInput["dynamicChildren"] = {};
|
|
440
|
+
const dynamicGroupStatuses: ChainExecutionDetailsInput["dynamicGroupStatuses"] = {};
|
|
390
441
|
const allProgress: AgentProgress[] = [];
|
|
391
442
|
const allArtifactPaths: ArtifactPaths[] = [];
|
|
392
443
|
|
|
393
444
|
const chainAgents: string[] = chainSteps.map((step) =>
|
|
394
445
|
isParallelStep(step)
|
|
395
446
|
? `[${step.parallel.map((t) => t.agent).join("+")}]`
|
|
447
|
+
: isDynamicParallelStep(step)
|
|
448
|
+
? `expand:${step.parallel.agent}`
|
|
396
449
|
: (step as SequentialStep).agent,
|
|
397
450
|
);
|
|
398
451
|
const totalSteps = chainSteps.length;
|
|
399
452
|
|
|
453
|
+
const makeDetailsInput = (overrides: Pick<Partial<ChainExecutionDetailsInput>, "currentStepIndex" | "currentFlatIndex"> = {}): ChainExecutionDetailsInput => ({
|
|
454
|
+
results,
|
|
455
|
+
...(includeProgress !== undefined ? { includeProgress } : {}),
|
|
456
|
+
allProgress,
|
|
457
|
+
allArtifactPaths,
|
|
458
|
+
artifactsDir,
|
|
459
|
+
chainAgents,
|
|
460
|
+
chainSteps,
|
|
461
|
+
totalSteps,
|
|
462
|
+
runId,
|
|
463
|
+
outputs,
|
|
464
|
+
dynamicChildren,
|
|
465
|
+
dynamicGroupStatuses,
|
|
466
|
+
...overrides,
|
|
467
|
+
});
|
|
468
|
+
|
|
400
469
|
const firstStep = chainSteps[0]!;
|
|
401
470
|
const originalTask = params.task
|
|
402
|
-
?? (isParallelStep(firstStep)
|
|
471
|
+
?? (isParallelStep(firstStep)
|
|
472
|
+
? firstStep.parallel[0]!.task!
|
|
473
|
+
: isDynamicParallelStep(firstStep)
|
|
474
|
+
? firstStep.parallel.task!
|
|
475
|
+
: (firstStep as SequentialStep).task!);
|
|
476
|
+
try {
|
|
477
|
+
validateChainOutputBindings(chainSteps, { maxItems: params.dynamicFanoutMaxItems });
|
|
478
|
+
} catch (error) {
|
|
479
|
+
if (error instanceof ChainOutputValidationError) {
|
|
480
|
+
return {
|
|
481
|
+
content: [{ type: "text", text: error.message }],
|
|
482
|
+
isError: true,
|
|
483
|
+
details: buildChainExecutionDetails(makeDetailsInput()),
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
throw error;
|
|
487
|
+
}
|
|
403
488
|
|
|
404
489
|
const chainDir = createChainDir(runId, chainDirBase);
|
|
405
|
-
const hasParallelSteps = chainSteps.some(isParallelStep);
|
|
490
|
+
const hasParallelSteps = chainSteps.some((step) => isParallelStep(step) || isDynamicParallelStep(step));
|
|
406
491
|
let templates: ResolvedTemplates = resolveChainTemplates(chainSteps);
|
|
407
492
|
const shouldClarify = clarify !== false && ctx.hasUI && !hasParallelSteps;
|
|
408
493
|
let tuiBehaviorOverrides: (BehaviorOverride | undefined)[] | undefined;
|
|
@@ -419,7 +504,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
419
504
|
return {
|
|
420
505
|
content: [{ type: "text", text: `Unknown agent: ${step.agent}` }],
|
|
421
506
|
isError: true,
|
|
422
|
-
details: {
|
|
507
|
+
details: buildChainExecutionDetails(makeDetailsInput({ currentStepIndex: seqSteps.indexOf(step) })),
|
|
423
508
|
};
|
|
424
509
|
}
|
|
425
510
|
agentConfigs.push(config);
|
|
@@ -464,7 +549,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
464
549
|
removeChainDir(chainDir);
|
|
465
550
|
return {
|
|
466
551
|
content: [{ type: "text", text: "Chain cancelled" }],
|
|
467
|
-
details:
|
|
552
|
+
details: buildChainExecutionDetails(makeDetailsInput()),
|
|
468
553
|
};
|
|
469
554
|
}
|
|
470
555
|
|
|
@@ -486,7 +571,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
486
571
|
});
|
|
487
572
|
return {
|
|
488
573
|
content: [{ type: "text", text: "Launching in background..." }],
|
|
489
|
-
details:
|
|
574
|
+
details: buildChainExecutionDetails(makeDetailsInput()),
|
|
490
575
|
requestedAsync: { chain: updatedChain, chainSkills },
|
|
491
576
|
};
|
|
492
577
|
}
|
|
@@ -495,7 +580,6 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
495
580
|
tuiBehaviorOverrides = result.behaviorOverrides;
|
|
496
581
|
}
|
|
497
582
|
|
|
498
|
-
const results: SingleResult[] = [];
|
|
499
583
|
let prev = "";
|
|
500
584
|
let globalTaskIndex = 0;
|
|
501
585
|
let progressCreated = false;
|
|
@@ -513,16 +597,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
513
597
|
if (worktreeTaskCwdConflict) {
|
|
514
598
|
return buildChainExecutionErrorResult(
|
|
515
599
|
`parallel chain step ${stepIndex + 1}: ${formatWorktreeTaskCwdConflict(worktreeTaskCwdConflict, parallelCwd)}`,
|
|
516
|
-
{
|
|
517
|
-
results,
|
|
518
|
-
includeProgress,
|
|
519
|
-
allProgress,
|
|
520
|
-
allArtifactPaths,
|
|
521
|
-
artifactsDir,
|
|
522
|
-
chainAgents,
|
|
523
|
-
totalSteps,
|
|
524
|
-
currentStepIndex: stepIndex,
|
|
525
|
-
},
|
|
600
|
+
makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex }),
|
|
526
601
|
);
|
|
527
602
|
}
|
|
528
603
|
try {
|
|
@@ -534,16 +609,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
534
609
|
});
|
|
535
610
|
} catch (error) {
|
|
536
611
|
const message = error instanceof Error ? error.message : String(error);
|
|
537
|
-
return buildChainExecutionErrorResult(message, {
|
|
538
|
-
results,
|
|
539
|
-
includeProgress,
|
|
540
|
-
allProgress,
|
|
541
|
-
allArtifactPaths,
|
|
542
|
-
artifactsDir,
|
|
543
|
-
chainAgents,
|
|
544
|
-
totalSteps,
|
|
545
|
-
currentStepIndex: stepIndex,
|
|
546
|
-
});
|
|
612
|
+
return buildChainExecutionErrorResult(message, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex }));
|
|
547
613
|
}
|
|
548
614
|
}
|
|
549
615
|
|
|
@@ -557,16 +623,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
557
623
|
? (path.isAbsolute(behavior.output) ? behavior.output : path.join(chainDir, behavior.output))
|
|
558
624
|
: undefined;
|
|
559
625
|
const validationError = validateFileOnlyOutputMode(behavior.outputMode, outputPath, `Parallel chain step ${stepIndex + 1} task ${taskIndex + 1} (${step.parallel[taskIndex]!.agent})`);
|
|
560
|
-
if (validationError) return buildChainExecutionErrorResult(validationError, {
|
|
561
|
-
results,
|
|
562
|
-
includeProgress,
|
|
563
|
-
allProgress,
|
|
564
|
-
allArtifactPaths,
|
|
565
|
-
artifactsDir,
|
|
566
|
-
chainAgents,
|
|
567
|
-
totalSteps,
|
|
568
|
-
currentStepIndex: stepIndex,
|
|
569
|
-
});
|
|
626
|
+
if (validationError) return buildChainExecutionErrorResult(validationError, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex + taskIndex }));
|
|
570
627
|
}
|
|
571
628
|
progressCreated = ensureParallelProgressFile(chainDir, progressCreated, parallelBehaviors);
|
|
572
629
|
createParallelDirs(chainDir, stepIndex, step.parallel.length, agentNames);
|
|
@@ -595,8 +652,12 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
595
652
|
onUpdate,
|
|
596
653
|
results,
|
|
597
654
|
allProgress,
|
|
655
|
+
outputs,
|
|
598
656
|
chainAgents,
|
|
657
|
+
chainSteps,
|
|
599
658
|
totalSteps,
|
|
659
|
+
dynamicChildren,
|
|
660
|
+
dynamicGroupStatuses,
|
|
600
661
|
controlConfig,
|
|
601
662
|
onControlEvent,
|
|
602
663
|
childIntercomTarget,
|
|
@@ -615,21 +676,15 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
615
676
|
if (result.progress) allProgress.push(result.progress);
|
|
616
677
|
if (result.artifactPaths) allArtifactPaths.push(result.artifactPaths);
|
|
617
678
|
}
|
|
618
|
-
|
|
619
|
-
const interrupted = parallelResults
|
|
679
|
+
const interruptedIndexInStep = parallelResults.findIndex((result) => result.interrupted);
|
|
680
|
+
const interrupted = interruptedIndexInStep >= 0 ? parallelResults[interruptedIndexInStep] : undefined;
|
|
620
681
|
if (interrupted) {
|
|
621
682
|
return {
|
|
622
683
|
content: [{ type: "text", text: `Chain paused after interrupt at step ${stepIndex + 1} (${interrupted.agent}). Waiting for explicit next action.` }],
|
|
623
|
-
details: buildChainExecutionDetails({
|
|
624
|
-
results,
|
|
625
|
-
includeProgress,
|
|
626
|
-
allProgress,
|
|
627
|
-
allArtifactPaths,
|
|
628
|
-
artifactsDir,
|
|
629
|
-
chainAgents,
|
|
630
|
-
totalSteps,
|
|
684
|
+
details: buildChainExecutionDetails(makeDetailsInput({
|
|
631
685
|
currentStepIndex: stepIndex,
|
|
632
|
-
|
|
686
|
+
currentFlatIndex: globalTaskIndex - step.parallel.length + interruptedIndexInStep,
|
|
687
|
+
})),
|
|
633
688
|
};
|
|
634
689
|
}
|
|
635
690
|
const detachedIndexInStep = parallelResults.findIndex((result) => result.detached);
|
|
@@ -637,16 +692,10 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
637
692
|
if (detached) {
|
|
638
693
|
return {
|
|
639
694
|
content: [{ type: "text", text: `Chain detached for intercom coordination at step ${stepIndex + 1} (${detached.agent}). Reply to the supervisor request first. After the child exits, start a fresh follow-up if needed.` }],
|
|
640
|
-
details: buildChainExecutionDetails({
|
|
641
|
-
results,
|
|
642
|
-
includeProgress,
|
|
643
|
-
allProgress,
|
|
644
|
-
allArtifactPaths,
|
|
645
|
-
artifactsDir,
|
|
646
|
-
chainAgents,
|
|
647
|
-
totalSteps,
|
|
695
|
+
details: buildChainExecutionDetails(makeDetailsInput({
|
|
648
696
|
currentStepIndex: stepIndex,
|
|
649
|
-
|
|
697
|
+
currentFlatIndex: globalTaskIndex - step.parallel.length + detachedIndexInStep,
|
|
698
|
+
})),
|
|
650
699
|
};
|
|
651
700
|
}
|
|
652
701
|
|
|
@@ -665,19 +714,18 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
665
714
|
return {
|
|
666
715
|
content: [{ type: "text", text: summary }],
|
|
667
716
|
isError: true,
|
|
668
|
-
details: buildChainExecutionDetails({
|
|
669
|
-
results,
|
|
670
|
-
includeProgress,
|
|
671
|
-
allProgress,
|
|
672
|
-
allArtifactPaths,
|
|
673
|
-
artifactsDir,
|
|
674
|
-
chainAgents,
|
|
675
|
-
totalSteps,
|
|
717
|
+
details: buildChainExecutionDetails(makeDetailsInput({
|
|
676
718
|
currentStepIndex: stepIndex,
|
|
677
|
-
|
|
719
|
+
currentFlatIndex: globalTaskIndex - step.parallel.length + failures[0]!.originalIndex,
|
|
720
|
+
})),
|
|
678
721
|
};
|
|
679
722
|
}
|
|
680
723
|
|
|
724
|
+
for (let taskIndex = 0; taskIndex < parallelResults.length; taskIndex++) {
|
|
725
|
+
const outputName = step.parallel[taskIndex]?.as;
|
|
726
|
+
if (outputName) outputs[outputName] = outputEntryFromResult(parallelResults[taskIndex]!, stepIndex);
|
|
727
|
+
}
|
|
728
|
+
|
|
681
729
|
const taskResults: ParallelTaskResult[] = parallelResults.map((result, i) => {
|
|
682
730
|
const outputTarget = parallelBehaviors[i]?.output;
|
|
683
731
|
const outputTargetPath = typeof outputTarget === "string"
|
|
@@ -703,6 +751,227 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
703
751
|
} finally {
|
|
704
752
|
if (worktreeSetup) cleanupWorktrees(worktreeSetup);
|
|
705
753
|
}
|
|
754
|
+
} else if (isDynamicParallelStep(step)) {
|
|
755
|
+
let materialized: ReturnType<typeof materializeDynamicParallelStep>;
|
|
756
|
+
try {
|
|
757
|
+
materialized = materializeDynamicParallelStep(step, outputs, stepIndex, { maxItems: params.dynamicFanoutMaxItems });
|
|
758
|
+
} catch (error) {
|
|
759
|
+
const message = error instanceof DynamicFanoutError ? error.message : error instanceof Error ? error.message : String(error);
|
|
760
|
+
dynamicGroupStatuses[stepIndex] = { status: "failed", error: message };
|
|
761
|
+
return buildChainExecutionErrorResult(message, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex }));
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
dynamicChildren[stepIndex] = materialized.items.map((item, itemIndex) => ({
|
|
765
|
+
agent: step.parallel.agent,
|
|
766
|
+
label: materialized.parallel[itemIndex]?.label,
|
|
767
|
+
flatIndex: globalTaskIndex + itemIndex,
|
|
768
|
+
itemKey: item.key,
|
|
769
|
+
structured: Boolean(step.parallel.outputSchema),
|
|
770
|
+
}));
|
|
771
|
+
|
|
772
|
+
if (materialized.parallel.length === 0) {
|
|
773
|
+
const collection: DynamicCollectedResult[] = [];
|
|
774
|
+
try {
|
|
775
|
+
validateDynamicCollection(step.collect.outputSchema, collection);
|
|
776
|
+
} catch (error) {
|
|
777
|
+
const message = error instanceof DynamicFanoutError ? error.message : error instanceof Error ? error.message : String(error);
|
|
778
|
+
dynamicGroupStatuses[stepIndex] = { status: "failed", error: message };
|
|
779
|
+
return buildChainExecutionErrorResult(message, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex }));
|
|
780
|
+
}
|
|
781
|
+
outputs[step.collect.as] = {
|
|
782
|
+
text: JSON.stringify(collection),
|
|
783
|
+
structured: collection,
|
|
784
|
+
agent: step.parallel.agent,
|
|
785
|
+
stepIndex,
|
|
786
|
+
};
|
|
787
|
+
dynamicGroupStatuses[stepIndex] = { status: "completed" };
|
|
788
|
+
if (step.acceptance !== undefined) {
|
|
789
|
+
const effectiveGroupAcceptance = resolveEffectiveAcceptance({
|
|
790
|
+
explicit: step.acceptance,
|
|
791
|
+
agentName: step.parallel.agent,
|
|
792
|
+
task: step.parallel.task ?? originalTask,
|
|
793
|
+
mode: "chain",
|
|
794
|
+
dynamicGroup: true,
|
|
795
|
+
});
|
|
796
|
+
const groupAcceptance = await evaluateAcceptance({
|
|
797
|
+
acceptance: effectiveGroupAcceptance,
|
|
798
|
+
output: "",
|
|
799
|
+
report: aggregateAcceptanceReport({
|
|
800
|
+
results: [],
|
|
801
|
+
notes: "Dynamic fanout produced 0 results.",
|
|
802
|
+
}),
|
|
803
|
+
cwd: cwd ?? ctx.cwd,
|
|
804
|
+
});
|
|
805
|
+
dynamicGroupStatuses[stepIndex].acceptance = groupAcceptance;
|
|
806
|
+
const groupAcceptanceFailure = acceptanceFailureMessage(groupAcceptance);
|
|
807
|
+
if (groupAcceptanceFailure) {
|
|
808
|
+
dynamicGroupStatuses[stepIndex] = { status: "failed", error: groupAcceptanceFailure, acceptance: groupAcceptance };
|
|
809
|
+
return buildChainExecutionErrorResult(groupAcceptanceFailure, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex }));
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
prev = "Dynamic fanout produced 0 results.";
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
const dynamicParallelStep: ParallelStep = {
|
|
817
|
+
parallel: materialized.parallel,
|
|
818
|
+
concurrency: step.concurrency,
|
|
819
|
+
failFast: step.failFast,
|
|
820
|
+
};
|
|
821
|
+
const parallelTemplates = materialized.parallel.map((task) => task.task ?? "{previous}");
|
|
822
|
+
const parallelBehaviors = resolveParallelBehaviors(dynamicParallelStep.parallel, agents, stepIndex, chainSkills)
|
|
823
|
+
.map((behavior, taskIndex) => suppressProgressForReadOnlyTask(behavior, parallelTemplates[taskIndex] ?? dynamicParallelStep.parallel[taskIndex]?.task, originalTask));
|
|
824
|
+
|
|
825
|
+
for (let taskIndex = 0; taskIndex < dynamicParallelStep.parallel.length; taskIndex++) {
|
|
826
|
+
const behavior = parallelBehaviors[taskIndex]!;
|
|
827
|
+
const outputPath = typeof behavior.output === "string"
|
|
828
|
+
? (path.isAbsolute(behavior.output) ? behavior.output : path.join(chainDir, behavior.output))
|
|
829
|
+
: undefined;
|
|
830
|
+
const validationError = validateFileOnlyOutputMode(behavior.outputMode, outputPath, `Dynamic chain step ${stepIndex + 1} item ${taskIndex + 1} (${dynamicParallelStep.parallel[taskIndex]!.agent})`);
|
|
831
|
+
if (validationError) {
|
|
832
|
+
dynamicGroupStatuses[stepIndex] = { status: "failed", error: validationError };
|
|
833
|
+
return buildChainExecutionErrorResult(validationError, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex + taskIndex }));
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
progressCreated = ensureParallelProgressFile(chainDir, progressCreated, parallelBehaviors);
|
|
838
|
+
createParallelDirs(chainDir, stepIndex, dynamicParallelStep.parallel.length, dynamicParallelStep.parallel.map((task) => task.agent));
|
|
839
|
+
const parallelResults = await runParallelChainTasks({
|
|
840
|
+
step: dynamicParallelStep,
|
|
841
|
+
parallelTemplates,
|
|
842
|
+
parallelBehaviors,
|
|
843
|
+
agents,
|
|
844
|
+
stepIndex,
|
|
845
|
+
availableModels,
|
|
846
|
+
chainDir,
|
|
847
|
+
prev,
|
|
848
|
+
originalTask,
|
|
849
|
+
ctx,
|
|
850
|
+
intercomEvents,
|
|
851
|
+
cwd,
|
|
852
|
+
runId,
|
|
853
|
+
globalTaskIndex,
|
|
854
|
+
sessionDirForIndex,
|
|
855
|
+
sessionFileForIndex,
|
|
856
|
+
shareEnabled,
|
|
857
|
+
artifactConfig,
|
|
858
|
+
artifactsDir,
|
|
859
|
+
signal,
|
|
860
|
+
onUpdate,
|
|
861
|
+
results,
|
|
862
|
+
allProgress,
|
|
863
|
+
outputs,
|
|
864
|
+
chainAgents,
|
|
865
|
+
chainSteps,
|
|
866
|
+
totalSteps,
|
|
867
|
+
dynamicChildren,
|
|
868
|
+
dynamicGroupStatuses,
|
|
869
|
+
controlConfig,
|
|
870
|
+
onControlEvent,
|
|
871
|
+
childIntercomTarget,
|
|
872
|
+
orchestratorIntercomTarget,
|
|
873
|
+
foregroundControl,
|
|
874
|
+
nestedRoute: params.nestedRoute,
|
|
875
|
+
maxSubagentDepth: params.maxSubagentDepth,
|
|
876
|
+
workflowStageSubagentGuard: params.workflowStageSubagentGuard,
|
|
877
|
+
runSync: executeRunSync,
|
|
878
|
+
});
|
|
879
|
+
globalTaskIndex += dynamicParallelStep.parallel.length;
|
|
880
|
+
|
|
881
|
+
for (const result of parallelResults) {
|
|
882
|
+
results.push(result);
|
|
883
|
+
if (result.progress) allProgress.push(result.progress);
|
|
884
|
+
if (result.artifactPaths) allArtifactPaths.push(result.artifactPaths);
|
|
885
|
+
}
|
|
886
|
+
const collected = collectDynamicResults(step, materialized.items, parallelResults);
|
|
887
|
+
const interruptedIndexInStep = parallelResults.findIndex((result) => result.interrupted);
|
|
888
|
+
const interrupted = interruptedIndexInStep >= 0 ? parallelResults[interruptedIndexInStep] : undefined;
|
|
889
|
+
if (interrupted) {
|
|
890
|
+
return {
|
|
891
|
+
content: [{ type: "text", text: `Chain paused after interrupt at step ${stepIndex + 1} (${interrupted.agent}). Waiting for explicit next action.` }],
|
|
892
|
+
details: buildChainExecutionDetails(makeDetailsInput({
|
|
893
|
+
currentStepIndex: stepIndex,
|
|
894
|
+
currentFlatIndex: globalTaskIndex - dynamicParallelStep.parallel.length + interruptedIndexInStep,
|
|
895
|
+
})),
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
const detachedIndexInStep = parallelResults.findIndex((result) => result.detached);
|
|
899
|
+
const detached = detachedIndexInStep >= 0 ? parallelResults[detachedIndexInStep] : undefined;
|
|
900
|
+
if (detached) {
|
|
901
|
+
return {
|
|
902
|
+
content: [{ type: "text", text: `Chain detached for intercom coordination at step ${stepIndex + 1} (${detached.agent}). Reply to the supervisor request first. After the child exits, start a fresh follow-up if needed.` }],
|
|
903
|
+
details: buildChainExecutionDetails(makeDetailsInput({
|
|
904
|
+
currentStepIndex: stepIndex,
|
|
905
|
+
currentFlatIndex: globalTaskIndex - dynamicParallelStep.parallel.length + detachedIndexInStep,
|
|
906
|
+
})),
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
const failures = parallelResults
|
|
910
|
+
.map((result, originalIndex) => ({ ...result, originalIndex }))
|
|
911
|
+
.filter((result) => result.exitCode !== 0 && result.exitCode !== -1);
|
|
912
|
+
if (failures.length > 0) {
|
|
913
|
+
const failureSummary = failures
|
|
914
|
+
.map((failure) => `- Item ${failure.originalIndex + 1} (${failure.agent}, key ${materialized.items[failure.originalIndex]?.key ?? failure.originalIndex}): ${failure.error || "failed"}`)
|
|
915
|
+
.join("\n");
|
|
916
|
+
const errorMsg = `Dynamic step ${stepIndex + 1} failed:\n${failureSummary}`;
|
|
917
|
+
dynamicGroupStatuses[stepIndex] = { status: "failed", error: errorMsg };
|
|
918
|
+
const summary = buildChainSummary(chainSteps, results, chainDir, "failed", {
|
|
919
|
+
index: stepIndex,
|
|
920
|
+
error: errorMsg,
|
|
921
|
+
});
|
|
922
|
+
return {
|
|
923
|
+
content: [{ type: "text", text: summary }],
|
|
924
|
+
isError: true,
|
|
925
|
+
details: buildChainExecutionDetails(makeDetailsInput({
|
|
926
|
+
currentStepIndex: stepIndex,
|
|
927
|
+
currentFlatIndex: globalTaskIndex - dynamicParallelStep.parallel.length + failures[0]!.originalIndex,
|
|
928
|
+
})),
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
try {
|
|
932
|
+
validateDynamicCollection(step.collect.outputSchema, collected);
|
|
933
|
+
} catch (error) {
|
|
934
|
+
const message = error instanceof DynamicFanoutError ? error.message : error instanceof Error ? error.message : String(error);
|
|
935
|
+
dynamicGroupStatuses[stepIndex] = { status: "failed", error: message };
|
|
936
|
+
return buildChainExecutionErrorResult(message, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex - dynamicParallelStep.parallel.length }));
|
|
937
|
+
}
|
|
938
|
+
outputs[step.collect.as] = {
|
|
939
|
+
text: JSON.stringify(collected),
|
|
940
|
+
structured: collected,
|
|
941
|
+
agent: step.parallel.agent,
|
|
942
|
+
stepIndex,
|
|
943
|
+
};
|
|
944
|
+
dynamicGroupStatuses[stepIndex] = { status: "completed" };
|
|
945
|
+
const effectiveGroupAcceptance = resolveEffectiveAcceptance({
|
|
946
|
+
explicit: step.acceptance,
|
|
947
|
+
agentName: step.parallel.agent,
|
|
948
|
+
task: step.parallel.task ?? originalTask,
|
|
949
|
+
mode: "chain",
|
|
950
|
+
dynamicGroup: true,
|
|
951
|
+
});
|
|
952
|
+
const groupAcceptance = await evaluateAcceptance({
|
|
953
|
+
acceptance: effectiveGroupAcceptance,
|
|
954
|
+
output: "",
|
|
955
|
+
report: aggregateAcceptanceReport({
|
|
956
|
+
results: parallelResults,
|
|
957
|
+
notes: `Dynamic fanout collected ${collected.length} result(s) into ${step.collect.as}.`,
|
|
958
|
+
}),
|
|
959
|
+
cwd: cwd ?? ctx.cwd,
|
|
960
|
+
});
|
|
961
|
+
dynamicGroupStatuses[stepIndex].acceptance = groupAcceptance;
|
|
962
|
+
const groupAcceptanceFailure = acceptanceFailureMessage(groupAcceptance);
|
|
963
|
+
if (groupAcceptanceFailure) {
|
|
964
|
+
dynamicGroupStatuses[stepIndex] = { status: "failed", error: groupAcceptanceFailure, acceptance: groupAcceptance };
|
|
965
|
+
return buildChainExecutionErrorResult(groupAcceptanceFailure, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex - dynamicParallelStep.parallel.length }));
|
|
966
|
+
}
|
|
967
|
+
const taskResults: ParallelTaskResult[] = parallelResults.map((result, i) => ({
|
|
968
|
+
agent: result.agent,
|
|
969
|
+
taskIndex: i,
|
|
970
|
+
output: getSingleResultOutput(result),
|
|
971
|
+
exitCode: result.exitCode,
|
|
972
|
+
error: result.error,
|
|
973
|
+
}));
|
|
974
|
+
prev = aggregateParallelOutputs(taskResults, (i, agent) => `=== Dynamic Item ${i + 1} (${agent}, key ${materialized.items[i]?.key ?? i}) ===`);
|
|
706
975
|
} else {
|
|
707
976
|
const seqStep = step as SequentialStep;
|
|
708
977
|
const stepTemplate = stepTemplates as string;
|
|
@@ -713,7 +982,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
713
982
|
return {
|
|
714
983
|
content: [{ type: "text", text: `Unknown agent: ${seqStep.agent}` }],
|
|
715
984
|
isError: true,
|
|
716
|
-
details: {
|
|
985
|
+
details: buildChainExecutionDetails(makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex })),
|
|
717
986
|
};
|
|
718
987
|
}
|
|
719
988
|
|
|
@@ -743,7 +1012,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
743
1012
|
templateHasPrevious ? undefined : prev,
|
|
744
1013
|
);
|
|
745
1014
|
|
|
746
|
-
let stepTask = stepTemplate;
|
|
1015
|
+
let stepTask = resolveOutputReferences(stepTemplate, outputs);
|
|
747
1016
|
stepTask = stepTask.replace(/\{task\}/g, originalTask);
|
|
748
1017
|
stepTask = stepTask.replace(/\{previous\}/g, prev);
|
|
749
1018
|
stepTask = stepTask.replace(/\{chain_dir\}/g, chainDir);
|
|
@@ -760,16 +1029,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
760
1029
|
: undefined;
|
|
761
1030
|
const validationError = validateFileOnlyOutputMode(behavior.outputMode, outputPath, `Chain step ${stepIndex + 1} (${seqStep.agent})`);
|
|
762
1031
|
if (validationError) {
|
|
763
|
-
return buildChainExecutionErrorResult(validationError, {
|
|
764
|
-
results,
|
|
765
|
-
includeProgress,
|
|
766
|
-
allProgress,
|
|
767
|
-
allArtifactPaths,
|
|
768
|
-
artifactsDir,
|
|
769
|
-
chainAgents,
|
|
770
|
-
totalSteps,
|
|
771
|
-
currentStepIndex: stepIndex,
|
|
772
|
-
});
|
|
1032
|
+
return buildChainExecutionErrorResult(validationError, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex }));
|
|
773
1033
|
}
|
|
774
1034
|
const maxSubagentDepth = resolveChildMaxSubagentDepth(params.maxSubagentDepth, agentConfig.maxSubagentDepth);
|
|
775
1035
|
const interruptController = new AbortController();
|
|
@@ -787,6 +1047,9 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
787
1047
|
};
|
|
788
1048
|
}
|
|
789
1049
|
|
|
1050
|
+
const structuredRuntime = seqStep.outputSchema
|
|
1051
|
+
? createStructuredOutputRuntime(seqStep.outputSchema, path.join(chainDir, "structured-output"))
|
|
1052
|
+
: undefined;
|
|
790
1053
|
const r = await executeRunSync(ctx.cwd, agents, seqStep.agent, stepTask, {
|
|
791
1054
|
cwd: resolveChildCwd(cwd ?? ctx.cwd, seqStep.cwd),
|
|
792
1055
|
signal,
|
|
@@ -814,6 +1077,9 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
814
1077
|
currentModel: currentModelFullId(ctx.model),
|
|
815
1078
|
preferredModelProvider: ctx.model?.provider,
|
|
816
1079
|
skills: behavior.skills === false ? [] : behavior.skills,
|
|
1080
|
+
structuredOutput: structuredRuntime,
|
|
1081
|
+
acceptance: seqStep.acceptance,
|
|
1082
|
+
acceptanceContext: { mode: "chain" },
|
|
817
1083
|
onUpdate: onUpdate
|
|
818
1084
|
? (p) => {
|
|
819
1085
|
const stepResults = p.details?.results || [];
|
|
@@ -842,6 +1108,17 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
842
1108
|
chainAgents,
|
|
843
1109
|
totalSteps,
|
|
844
1110
|
currentStepIndex: stepIndex,
|
|
1111
|
+
outputs,
|
|
1112
|
+
workflowGraph: buildWorkflowGraphSnapshot({
|
|
1113
|
+
runId,
|
|
1114
|
+
mode: "chain",
|
|
1115
|
+
steps: chainSteps,
|
|
1116
|
+
results: results.concat(stepResults),
|
|
1117
|
+
currentStepIndex: stepIndex,
|
|
1118
|
+
currentFlatIndex: globalTaskIndex,
|
|
1119
|
+
dynamicChildren,
|
|
1120
|
+
dynamicGroupStatuses,
|
|
1121
|
+
}),
|
|
845
1122
|
},
|
|
846
1123
|
});
|
|
847
1124
|
}
|
|
@@ -861,31 +1138,13 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
861
1138
|
if (r.interrupted) {
|
|
862
1139
|
return {
|
|
863
1140
|
content: [{ type: "text", text: `Chain paused after interrupt at step ${stepIndex + 1} (${r.agent}). Waiting for explicit next action.` }],
|
|
864
|
-
details: buildChainExecutionDetails({
|
|
865
|
-
results,
|
|
866
|
-
includeProgress,
|
|
867
|
-
allProgress,
|
|
868
|
-
allArtifactPaths,
|
|
869
|
-
artifactsDir,
|
|
870
|
-
chainAgents,
|
|
871
|
-
totalSteps,
|
|
872
|
-
currentStepIndex: stepIndex,
|
|
873
|
-
}),
|
|
1141
|
+
details: buildChainExecutionDetails(makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex - 1 })),
|
|
874
1142
|
};
|
|
875
1143
|
}
|
|
876
1144
|
if (r.detached) {
|
|
877
1145
|
return {
|
|
878
1146
|
content: [{ type: "text", text: `Chain detached for intercom coordination at step ${stepIndex + 1} (${r.agent}). Reply to the supervisor request first. After the child exits, start a fresh follow-up if needed.` }],
|
|
879
|
-
details: buildChainExecutionDetails({
|
|
880
|
-
results,
|
|
881
|
-
includeProgress,
|
|
882
|
-
allProgress,
|
|
883
|
-
allArtifactPaths,
|
|
884
|
-
artifactsDir,
|
|
885
|
-
chainAgents,
|
|
886
|
-
totalSteps,
|
|
887
|
-
currentStepIndex: stepIndex,
|
|
888
|
-
}),
|
|
1147
|
+
details: buildChainExecutionDetails(makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex - 1 })),
|
|
889
1148
|
};
|
|
890
1149
|
}
|
|
891
1150
|
|
|
@@ -896,16 +1155,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
896
1155
|
});
|
|
897
1156
|
return {
|
|
898
1157
|
content: [{ type: "text", text: summary }],
|
|
899
|
-
details: buildChainExecutionDetails({
|
|
900
|
-
results,
|
|
901
|
-
includeProgress,
|
|
902
|
-
allProgress,
|
|
903
|
-
allArtifactPaths,
|
|
904
|
-
artifactsDir,
|
|
905
|
-
chainAgents,
|
|
906
|
-
totalSteps,
|
|
907
|
-
currentStepIndex: stepIndex,
|
|
908
|
-
}),
|
|
1158
|
+
details: buildChainExecutionDetails(makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex - 1 })),
|
|
909
1159
|
isError: true,
|
|
910
1160
|
};
|
|
911
1161
|
}
|
|
@@ -928,6 +1178,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
928
1178
|
}
|
|
929
1179
|
}
|
|
930
1180
|
|
|
1181
|
+
if (seqStep.as) outputs[seqStep.as] = outputEntryFromResult(r, stepIndex);
|
|
931
1182
|
prev = getSingleResultOutput(r);
|
|
932
1183
|
}
|
|
933
1184
|
}
|
|
@@ -936,14 +1187,6 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
936
1187
|
|
|
937
1188
|
return {
|
|
938
1189
|
content: [{ type: "text", text: summary }],
|
|
939
|
-
details: buildChainExecutionDetails(
|
|
940
|
-
results,
|
|
941
|
-
includeProgress,
|
|
942
|
-
allProgress,
|
|
943
|
-
allArtifactPaths,
|
|
944
|
-
artifactsDir,
|
|
945
|
-
chainAgents,
|
|
946
|
-
totalSteps,
|
|
947
|
-
}),
|
|
1190
|
+
details: buildChainExecutionDetails(makeDetailsInput()),
|
|
948
1191
|
};
|
|
949
1192
|
}
|