pi-subagents 0.25.0 → 0.28.0
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 +34 -0
- package/README.md +175 -19
- package/package.json +1 -1
- package/prompts/parallel-context-build.md +3 -1
- package/prompts/parallel-handoff-plan.md +3 -1
- package/skills/pi-subagents/SKILL.md +60 -17
- package/src/agents/agent-management.ts +71 -15
- package/src/agents/agent-serializer.ts +13 -2
- package/src/agents/agents.ts +88 -17
- package/src/agents/chain-serializer.ts +120 -0
- package/src/extension/fanout-child.ts +2 -0
- package/src/extension/index.ts +5 -2
- package/src/extension/schemas.ts +132 -6
- package/src/intercom/result-intercom.ts +5 -0
- package/src/runs/background/async-execution.ts +88 -6
- package/src/runs/background/async-status.ts +11 -1
- package/src/runs/background/run-status.ts +10 -1
- package/src/runs/background/subagent-runner.ts +665 -39
- package/src/runs/foreground/chain-execution.ts +369 -118
- package/src/runs/foreground/execution.ts +392 -19
- package/src/runs/foreground/subagent-executor.ts +126 -3
- package/src/runs/shared/acceptance-contract.ts +318 -0
- package/src/runs/shared/acceptance-evaluation.ts +221 -0
- package/src/runs/shared/acceptance-finalization.ts +173 -0
- package/src/runs/shared/acceptance-reports.ts +127 -0
- package/src/runs/shared/acceptance.ts +22 -0
- package/src/runs/shared/chain-outputs.ts +101 -0
- package/src/runs/shared/completion-guard.ts +26 -3
- package/src/runs/shared/dynamic-fanout.ts +293 -0
- package/src/runs/shared/parallel-utils.ts +33 -1
- package/src/runs/shared/pi-args.ts +11 -0
- package/src/runs/shared/structured-output.ts +77 -0
- package/src/runs/shared/subagent-prompt-runtime.ts +53 -3
- package/src/runs/shared/workflow-graph.ts +210 -0
- package/src/shared/formatters.ts +2 -2
- package/src/shared/settings.ts +53 -4
- package/src/shared/types.ts +265 -1
- package/src/shared/utils.ts +7 -0
- package/src/slash/slash-commands.ts +41 -3
- package/src/tui/render.ts +178 -45
|
@@ -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,
|
|
@@ -59,6 +61,11 @@ import {
|
|
|
59
61
|
} from "../../shared/types.ts";
|
|
60
62
|
import { resolveModelCandidate } from "../shared/model-fallback.ts";
|
|
61
63
|
import { validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
64
|
+
import { buildWorkflowGraphSnapshot } from "../shared/workflow-graph.ts";
|
|
65
|
+
import { ChainOutputValidationError, outputEntryFromResult, resolveOutputReferences, validateChainOutputBindings } from "../shared/chain-outputs.ts";
|
|
66
|
+
import { createStructuredOutputRuntime } from "../shared/structured-output.ts";
|
|
67
|
+
import { collectDynamicResults, DynamicFanoutError, materializeDynamicParallelStep, validateDynamicCollection, type DynamicCollectedResult } from "../shared/dynamic-fanout.ts";
|
|
68
|
+
import type { ChainOutputMap } from "../../shared/types.ts";
|
|
62
69
|
|
|
63
70
|
interface ChainExecutionDetailsInput {
|
|
64
71
|
results: SingleResult[];
|
|
@@ -67,12 +74,18 @@ interface ChainExecutionDetailsInput {
|
|
|
67
74
|
allArtifactPaths: ArtifactPaths[];
|
|
68
75
|
artifactsDir: string;
|
|
69
76
|
chainAgents: string[];
|
|
77
|
+
chainSteps: ChainStep[];
|
|
70
78
|
totalSteps: number;
|
|
71
79
|
currentStepIndex?: number;
|
|
80
|
+
runId: string;
|
|
81
|
+
outputs?: ChainOutputMap;
|
|
82
|
+
currentFlatIndex?: number;
|
|
83
|
+
dynamicChildren?: Record<number, Array<{ agent: string; label?: string; flatIndex: number; itemKey: string; outputName?: string; structured?: boolean; error?: string }>>;
|
|
84
|
+
dynamicGroupStatuses?: Record<number, { status: "pending" | "running" | "completed" | "failed" | "paused" | "detached" | "timed-out"; error?: string; acceptance?: SingleResult["acceptance"] }>;
|
|
72
85
|
}
|
|
73
86
|
|
|
74
87
|
interface ParallelChainRunInput {
|
|
75
|
-
step:
|
|
88
|
+
step: ParallelStep;
|
|
76
89
|
parallelTemplates: string[];
|
|
77
90
|
parallelBehaviors: ResolvedStepBehavior[];
|
|
78
91
|
agents: AgentConfig[];
|
|
@@ -90,6 +103,8 @@ interface ParallelChainRunInput {
|
|
|
90
103
|
sessionFileForIndex?: (idx?: number) => string | undefined;
|
|
91
104
|
shareEnabled: boolean;
|
|
92
105
|
artifactConfig: ArtifactConfig;
|
|
106
|
+
timeoutMs?: number;
|
|
107
|
+
timeoutAt?: number;
|
|
93
108
|
artifactsDir: string;
|
|
94
109
|
signal?: AbortSignal;
|
|
95
110
|
onUpdate?: (r: AgentToolResult<Details>) => void;
|
|
@@ -105,12 +120,20 @@ interface ParallelChainRunInput {
|
|
|
105
120
|
lastActivityAt?: number;
|
|
106
121
|
currentTool?: string;
|
|
107
122
|
currentToolStartedAt?: number;
|
|
123
|
+
currentPath?: string;
|
|
124
|
+
turnCount?: number;
|
|
125
|
+
tokens?: number;
|
|
126
|
+
toolCount?: number;
|
|
108
127
|
interrupt?: () => boolean;
|
|
109
128
|
};
|
|
110
129
|
results: SingleResult[];
|
|
111
130
|
allProgress: AgentProgress[];
|
|
131
|
+
outputs: ChainOutputMap;
|
|
112
132
|
chainAgents: string[];
|
|
133
|
+
chainSteps: ChainStep[];
|
|
113
134
|
totalSteps: number;
|
|
135
|
+
dynamicChildren?: ChainExecutionDetailsInput["dynamicChildren"];
|
|
136
|
+
dynamicGroupStatuses?: ChainExecutionDetailsInput["dynamicGroupStatuses"];
|
|
114
137
|
worktreeSetup?: WorktreeSetup;
|
|
115
138
|
maxSubagentDepth: number;
|
|
116
139
|
nestedRoute?: NestedRouteInfo;
|
|
@@ -125,6 +148,17 @@ function buildChainExecutionDetails(input: ChainExecutionDetailsInput): Details
|
|
|
125
148
|
chainAgents: input.chainAgents,
|
|
126
149
|
totalSteps: input.totalSteps,
|
|
127
150
|
currentStepIndex: input.currentStepIndex,
|
|
151
|
+
outputs: input.outputs,
|
|
152
|
+
workflowGraph: buildWorkflowGraphSnapshot({
|
|
153
|
+
runId: input.runId,
|
|
154
|
+
mode: "chain",
|
|
155
|
+
steps: input.chainSteps,
|
|
156
|
+
results: input.results,
|
|
157
|
+
currentStepIndex: input.currentStepIndex,
|
|
158
|
+
currentFlatIndex: input.currentFlatIndex,
|
|
159
|
+
dynamicChildren: input.dynamicChildren,
|
|
160
|
+
dynamicGroupStatuses: input.dynamicGroupStatuses,
|
|
161
|
+
}),
|
|
128
162
|
});
|
|
129
163
|
}
|
|
130
164
|
|
|
@@ -191,7 +225,7 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
191
225
|
templateHasPrevious ? undefined : input.prev,
|
|
192
226
|
);
|
|
193
227
|
|
|
194
|
-
let taskStr = taskTemplate;
|
|
228
|
+
let taskStr = resolveOutputReferences(taskTemplate, input.outputs);
|
|
195
229
|
taskStr = taskStr.replace(/\{task\}/g, input.originalTask);
|
|
196
230
|
taskStr = taskStr.replace(/\{previous\}/g, input.prev);
|
|
197
231
|
taskStr = taskStr.replace(/\{chain_dir\}/g, input.chainDir);
|
|
@@ -226,10 +260,14 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
226
260
|
};
|
|
227
261
|
}
|
|
228
262
|
|
|
263
|
+
const structuredRuntime = task.outputSchema
|
|
264
|
+
? createStructuredOutputRuntime(task.outputSchema, path.join(input.chainDir, "structured-output"))
|
|
265
|
+
: undefined;
|
|
229
266
|
const result = await runSync(input.ctx.cwd, input.agents, task.agent, taskStr, {
|
|
230
267
|
cwd: taskCwd,
|
|
231
268
|
signal: input.signal,
|
|
232
269
|
interruptSignal: interruptController.signal,
|
|
270
|
+
...(input.timeoutMs !== undefined && input.timeoutAt !== undefined ? { timeoutMs: input.timeoutMs, timeoutAt: input.timeoutAt } : {}),
|
|
233
271
|
allowIntercomDetach: taskAgentConfig?.systemPrompt?.includes(INTERCOM_BRIDGE_MARKER) === true,
|
|
234
272
|
intercomEvents: input.intercomEvents,
|
|
235
273
|
runId: input.runId,
|
|
@@ -242,6 +280,8 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
242
280
|
outputPath,
|
|
243
281
|
outputMode: behavior.outputMode,
|
|
244
282
|
maxSubagentDepth,
|
|
283
|
+
maxExecutionTimeMs: taskAgentConfig?.maxExecutionTimeMs,
|
|
284
|
+
maxTokens: taskAgentConfig?.maxTokens,
|
|
245
285
|
controlConfig: input.controlConfig,
|
|
246
286
|
onControlEvent: input.onControlEvent,
|
|
247
287
|
intercomSessionName: input.childIntercomTarget?.(task.agent, input.globalTaskIndex + taskIndex),
|
|
@@ -251,6 +291,9 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
251
291
|
availableModels: input.availableModels,
|
|
252
292
|
preferredModelProvider: input.ctx.model?.provider,
|
|
253
293
|
skills: behavior.skills === false ? [] : behavior.skills,
|
|
294
|
+
structuredOutput: structuredRuntime,
|
|
295
|
+
acceptance: task.acceptance,
|
|
296
|
+
acceptanceContext: { mode: "chain" },
|
|
254
297
|
onUpdate: input.onUpdate
|
|
255
298
|
? (progressUpdate) => {
|
|
256
299
|
const stepResults = progressUpdate.details?.results || [];
|
|
@@ -279,6 +322,17 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
279
322
|
chainAgents: input.chainAgents,
|
|
280
323
|
totalSteps: input.totalSteps,
|
|
281
324
|
currentStepIndex: input.stepIndex,
|
|
325
|
+
outputs: input.outputs,
|
|
326
|
+
workflowGraph: buildWorkflowGraphSnapshot({
|
|
327
|
+
runId: input.runId,
|
|
328
|
+
mode: "chain",
|
|
329
|
+
steps: input.chainSteps,
|
|
330
|
+
results: input.results.concat(stepResults),
|
|
331
|
+
currentStepIndex: input.stepIndex,
|
|
332
|
+
currentFlatIndex: input.globalTaskIndex + taskIndex,
|
|
333
|
+
dynamicChildren: input.dynamicChildren,
|
|
334
|
+
dynamicGroupStatuses: input.dynamicGroupStatuses,
|
|
335
|
+
}),
|
|
282
336
|
},
|
|
283
337
|
});
|
|
284
338
|
}
|
|
@@ -329,14 +383,20 @@ interface ChainExecutionParams {
|
|
|
329
383
|
lastActivityAt?: number;
|
|
330
384
|
currentTool?: string;
|
|
331
385
|
currentToolStartedAt?: number;
|
|
386
|
+
currentPath?: string;
|
|
387
|
+
turnCount?: number;
|
|
388
|
+
tokens?: number;
|
|
389
|
+
toolCount?: number;
|
|
332
390
|
interrupt?: () => boolean;
|
|
333
391
|
};
|
|
334
392
|
chainSkills?: string[];
|
|
335
393
|
chainDir?: string;
|
|
394
|
+
dynamicFanoutMaxItems?: number;
|
|
336
395
|
maxSubagentDepth: number;
|
|
337
396
|
nestedRoute?: NestedRouteInfo;
|
|
338
397
|
worktreeSetupHook?: string;
|
|
339
398
|
worktreeSetupHookTimeoutMs?: number;
|
|
399
|
+
timeoutMs?: number;
|
|
340
400
|
}
|
|
341
401
|
|
|
342
402
|
interface ChainExecutionResult {
|
|
@@ -380,22 +440,60 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
380
440
|
} = params;
|
|
381
441
|
const chainSkills = chainSkillsParam ?? [];
|
|
382
442
|
|
|
443
|
+
const results: SingleResult[] = [];
|
|
444
|
+
const outputs: ChainOutputMap = {};
|
|
445
|
+
const dynamicChildren: ChainExecutionDetailsInput["dynamicChildren"] = {};
|
|
446
|
+
const dynamicGroupStatuses: ChainExecutionDetailsInput["dynamicGroupStatuses"] = {};
|
|
383
447
|
const allProgress: AgentProgress[] = [];
|
|
384
448
|
const allArtifactPaths: ArtifactPaths[] = [];
|
|
385
449
|
|
|
386
450
|
const chainAgents: string[] = chainSteps.map((step) =>
|
|
387
451
|
isParallelStep(step)
|
|
388
452
|
? `[${step.parallel.map((t) => t.agent).join("+")}]`
|
|
453
|
+
: isDynamicParallelStep(step)
|
|
454
|
+
? `expand:${step.parallel.agent}`
|
|
389
455
|
: (step as SequentialStep).agent,
|
|
390
456
|
);
|
|
391
457
|
const totalSteps = chainSteps.length;
|
|
392
458
|
|
|
459
|
+
const makeDetailsInput = (overrides: Pick<Partial<ChainExecutionDetailsInput>, "currentStepIndex" | "currentFlatIndex"> = {}): ChainExecutionDetailsInput => ({
|
|
460
|
+
results,
|
|
461
|
+
...(includeProgress !== undefined ? { includeProgress } : {}),
|
|
462
|
+
allProgress,
|
|
463
|
+
allArtifactPaths,
|
|
464
|
+
artifactsDir,
|
|
465
|
+
chainAgents,
|
|
466
|
+
chainSteps,
|
|
467
|
+
totalSteps,
|
|
468
|
+
runId,
|
|
469
|
+
outputs,
|
|
470
|
+
dynamicChildren,
|
|
471
|
+
dynamicGroupStatuses,
|
|
472
|
+
...overrides,
|
|
473
|
+
});
|
|
474
|
+
|
|
393
475
|
const firstStep = chainSteps[0]!;
|
|
394
476
|
const originalTask = params.task
|
|
395
|
-
?? (isParallelStep(firstStep)
|
|
477
|
+
?? (isParallelStep(firstStep)
|
|
478
|
+
? firstStep.parallel[0]!.task!
|
|
479
|
+
: isDynamicParallelStep(firstStep)
|
|
480
|
+
? firstStep.parallel.task!
|
|
481
|
+
: (firstStep as SequentialStep).task!);
|
|
482
|
+
try {
|
|
483
|
+
validateChainOutputBindings(chainSteps, { maxItems: params.dynamicFanoutMaxItems });
|
|
484
|
+
} catch (error) {
|
|
485
|
+
if (error instanceof ChainOutputValidationError) {
|
|
486
|
+
return {
|
|
487
|
+
content: [{ type: "text", text: error.message }],
|
|
488
|
+
isError: true,
|
|
489
|
+
details: buildChainExecutionDetails(makeDetailsInput()),
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
throw error;
|
|
493
|
+
}
|
|
396
494
|
|
|
397
495
|
const chainDir = createChainDir(runId, chainDirBase);
|
|
398
|
-
const hasParallelSteps = chainSteps.some(isParallelStep);
|
|
496
|
+
const hasParallelSteps = chainSteps.some((step) => isParallelStep(step) || isDynamicParallelStep(step));
|
|
399
497
|
let templates: ResolvedTemplates = resolveChainTemplates(chainSteps);
|
|
400
498
|
const shouldClarify = clarify !== false && ctx.hasUI && !hasParallelSteps;
|
|
401
499
|
let tuiBehaviorOverrides: (BehaviorOverride | undefined)[] | undefined;
|
|
@@ -412,7 +510,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
412
510
|
return {
|
|
413
511
|
content: [{ type: "text", text: `Unknown agent: ${step.agent}` }],
|
|
414
512
|
isError: true,
|
|
415
|
-
details: {
|
|
513
|
+
details: buildChainExecutionDetails(makeDetailsInput({ currentStepIndex: seqSteps.indexOf(step) })),
|
|
416
514
|
};
|
|
417
515
|
}
|
|
418
516
|
agentConfigs.push(config);
|
|
@@ -457,7 +555,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
457
555
|
removeChainDir(chainDir);
|
|
458
556
|
return {
|
|
459
557
|
content: [{ type: "text", text: "Chain cancelled" }],
|
|
460
|
-
details:
|
|
558
|
+
details: buildChainExecutionDetails(makeDetailsInput()),
|
|
461
559
|
};
|
|
462
560
|
}
|
|
463
561
|
|
|
@@ -479,7 +577,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
479
577
|
});
|
|
480
578
|
return {
|
|
481
579
|
content: [{ type: "text", text: "Launching in background..." }],
|
|
482
|
-
details:
|
|
580
|
+
details: buildChainExecutionDetails(makeDetailsInput()),
|
|
483
581
|
requestedAsync: { chain: updatedChain, chainSkills },
|
|
484
582
|
};
|
|
485
583
|
}
|
|
@@ -488,7 +586,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
488
586
|
tuiBehaviorOverrides = result.behaviorOverrides;
|
|
489
587
|
}
|
|
490
588
|
|
|
491
|
-
const
|
|
589
|
+
const timeoutAt = params.timeoutMs !== undefined ? Date.now() + params.timeoutMs : undefined;
|
|
492
590
|
let prev = "";
|
|
493
591
|
let globalTaskIndex = 0;
|
|
494
592
|
let progressCreated = false;
|
|
@@ -506,16 +604,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
506
604
|
if (worktreeTaskCwdConflict) {
|
|
507
605
|
return buildChainExecutionErrorResult(
|
|
508
606
|
`parallel chain step ${stepIndex + 1}: ${formatWorktreeTaskCwdConflict(worktreeTaskCwdConflict, parallelCwd)}`,
|
|
509
|
-
{
|
|
510
|
-
results,
|
|
511
|
-
includeProgress,
|
|
512
|
-
allProgress,
|
|
513
|
-
allArtifactPaths,
|
|
514
|
-
artifactsDir,
|
|
515
|
-
chainAgents,
|
|
516
|
-
totalSteps,
|
|
517
|
-
currentStepIndex: stepIndex,
|
|
518
|
-
},
|
|
607
|
+
makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex }),
|
|
519
608
|
);
|
|
520
609
|
}
|
|
521
610
|
try {
|
|
@@ -527,16 +616,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
527
616
|
});
|
|
528
617
|
} catch (error) {
|
|
529
618
|
const message = error instanceof Error ? error.message : String(error);
|
|
530
|
-
return buildChainExecutionErrorResult(message, {
|
|
531
|
-
results,
|
|
532
|
-
includeProgress,
|
|
533
|
-
allProgress,
|
|
534
|
-
allArtifactPaths,
|
|
535
|
-
artifactsDir,
|
|
536
|
-
chainAgents,
|
|
537
|
-
totalSteps,
|
|
538
|
-
currentStepIndex: stepIndex,
|
|
539
|
-
});
|
|
619
|
+
return buildChainExecutionErrorResult(message, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex }));
|
|
540
620
|
}
|
|
541
621
|
}
|
|
542
622
|
|
|
@@ -550,16 +630,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
550
630
|
? (path.isAbsolute(behavior.output) ? behavior.output : path.join(chainDir, behavior.output))
|
|
551
631
|
: undefined;
|
|
552
632
|
const validationError = validateFileOnlyOutputMode(behavior.outputMode, outputPath, `Parallel chain step ${stepIndex + 1} task ${taskIndex + 1} (${step.parallel[taskIndex]!.agent})`);
|
|
553
|
-
if (validationError) return buildChainExecutionErrorResult(validationError, {
|
|
554
|
-
results,
|
|
555
|
-
includeProgress,
|
|
556
|
-
allProgress,
|
|
557
|
-
allArtifactPaths,
|
|
558
|
-
artifactsDir,
|
|
559
|
-
chainAgents,
|
|
560
|
-
totalSteps,
|
|
561
|
-
currentStepIndex: stepIndex,
|
|
562
|
-
});
|
|
633
|
+
if (validationError) return buildChainExecutionErrorResult(validationError, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex + taskIndex }));
|
|
563
634
|
}
|
|
564
635
|
progressCreated = ensureParallelProgressFile(chainDir, progressCreated, parallelBehaviors);
|
|
565
636
|
createParallelDirs(chainDir, stepIndex, step.parallel.length, agentNames);
|
|
@@ -584,12 +655,17 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
584
655
|
shareEnabled,
|
|
585
656
|
artifactConfig,
|
|
586
657
|
artifactsDir,
|
|
658
|
+
...(params.timeoutMs !== undefined && timeoutAt !== undefined ? { timeoutMs: params.timeoutMs, timeoutAt } : {}),
|
|
587
659
|
signal,
|
|
588
660
|
onUpdate,
|
|
589
661
|
results,
|
|
590
662
|
allProgress,
|
|
663
|
+
outputs,
|
|
591
664
|
chainAgents,
|
|
665
|
+
chainSteps,
|
|
592
666
|
totalSteps,
|
|
667
|
+
dynamicChildren,
|
|
668
|
+
dynamicGroupStatuses,
|
|
593
669
|
controlConfig,
|
|
594
670
|
onControlEvent,
|
|
595
671
|
childIntercomTarget,
|
|
@@ -606,21 +682,27 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
606
682
|
if (result.progress) allProgress.push(result.progress);
|
|
607
683
|
if (result.artifactPaths) allArtifactPaths.push(result.artifactPaths);
|
|
608
684
|
}
|
|
609
|
-
|
|
610
|
-
const
|
|
685
|
+
const timedOutIndexInStep = parallelResults.findIndex((result) => result.timedOut);
|
|
686
|
+
const timedOut = timedOutIndexInStep >= 0 ? parallelResults[timedOutIndexInStep] : undefined;
|
|
687
|
+
if (timedOut) {
|
|
688
|
+
return {
|
|
689
|
+
content: [{ type: "text", text: `Chain timed out at step ${stepIndex + 1} (${timedOut.agent}): ${timedOut.error ?? "timeout expired"}` }],
|
|
690
|
+
isError: true,
|
|
691
|
+
details: buildChainExecutionDetails(makeDetailsInput({
|
|
692
|
+
currentStepIndex: stepIndex,
|
|
693
|
+
currentFlatIndex: globalTaskIndex - step.parallel.length + timedOutIndexInStep,
|
|
694
|
+
})),
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
const interruptedIndexInStep = parallelResults.findIndex((result) => result.interrupted);
|
|
698
|
+
const interrupted = interruptedIndexInStep >= 0 ? parallelResults[interruptedIndexInStep] : undefined;
|
|
611
699
|
if (interrupted) {
|
|
612
700
|
return {
|
|
613
701
|
content: [{ type: "text", text: `Chain paused after interrupt at step ${stepIndex + 1} (${interrupted.agent}). Waiting for explicit next action.` }],
|
|
614
|
-
details: buildChainExecutionDetails({
|
|
615
|
-
results,
|
|
616
|
-
includeProgress,
|
|
617
|
-
allProgress,
|
|
618
|
-
allArtifactPaths,
|
|
619
|
-
artifactsDir,
|
|
620
|
-
chainAgents,
|
|
621
|
-
totalSteps,
|
|
702
|
+
details: buildChainExecutionDetails(makeDetailsInput({
|
|
622
703
|
currentStepIndex: stepIndex,
|
|
623
|
-
|
|
704
|
+
currentFlatIndex: globalTaskIndex - step.parallel.length + interruptedIndexInStep,
|
|
705
|
+
})),
|
|
624
706
|
};
|
|
625
707
|
}
|
|
626
708
|
const detachedIndexInStep = parallelResults.findIndex((result) => result.detached);
|
|
@@ -628,16 +710,10 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
628
710
|
if (detached) {
|
|
629
711
|
return {
|
|
630
712
|
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.` }],
|
|
631
|
-
details: buildChainExecutionDetails({
|
|
632
|
-
results,
|
|
633
|
-
includeProgress,
|
|
634
|
-
allProgress,
|
|
635
|
-
allArtifactPaths,
|
|
636
|
-
artifactsDir,
|
|
637
|
-
chainAgents,
|
|
638
|
-
totalSteps,
|
|
713
|
+
details: buildChainExecutionDetails(makeDetailsInput({
|
|
639
714
|
currentStepIndex: stepIndex,
|
|
640
|
-
|
|
715
|
+
currentFlatIndex: globalTaskIndex - step.parallel.length + detachedIndexInStep,
|
|
716
|
+
})),
|
|
641
717
|
};
|
|
642
718
|
}
|
|
643
719
|
|
|
@@ -656,19 +732,18 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
656
732
|
return {
|
|
657
733
|
content: [{ type: "text", text: summary }],
|
|
658
734
|
isError: true,
|
|
659
|
-
details: buildChainExecutionDetails({
|
|
660
|
-
results,
|
|
661
|
-
includeProgress,
|
|
662
|
-
allProgress,
|
|
663
|
-
allArtifactPaths,
|
|
664
|
-
artifactsDir,
|
|
665
|
-
chainAgents,
|
|
666
|
-
totalSteps,
|
|
735
|
+
details: buildChainExecutionDetails(makeDetailsInput({
|
|
667
736
|
currentStepIndex: stepIndex,
|
|
668
|
-
|
|
737
|
+
currentFlatIndex: globalTaskIndex - step.parallel.length + failures[0]!.originalIndex,
|
|
738
|
+
})),
|
|
669
739
|
};
|
|
670
740
|
}
|
|
671
741
|
|
|
742
|
+
for (let taskIndex = 0; taskIndex < parallelResults.length; taskIndex++) {
|
|
743
|
+
const outputName = step.parallel[taskIndex]?.as;
|
|
744
|
+
if (outputName) outputs[outputName] = outputEntryFromResult(parallelResults[taskIndex]!, stepIndex);
|
|
745
|
+
}
|
|
746
|
+
|
|
672
747
|
const taskResults: ParallelTaskResult[] = parallelResults.map((result, i) => {
|
|
673
748
|
const outputTarget = parallelBehaviors[i]?.output;
|
|
674
749
|
const outputTargetPath = typeof outputTarget === "string"
|
|
@@ -694,6 +769,198 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
694
769
|
} finally {
|
|
695
770
|
if (worktreeSetup) cleanupWorktrees(worktreeSetup);
|
|
696
771
|
}
|
|
772
|
+
} else if (isDynamicParallelStep(step)) {
|
|
773
|
+
if (Object.hasOwn(step, "acceptance")) {
|
|
774
|
+
const message = `Dynamic fanout step ${stepIndex + 1} does not support group-level acceptance; set acceptance on the child template instead.`;
|
|
775
|
+
dynamicGroupStatuses[stepIndex] = { status: "failed", error: message };
|
|
776
|
+
return buildChainExecutionErrorResult(message, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex }));
|
|
777
|
+
}
|
|
778
|
+
let materialized: ReturnType<typeof materializeDynamicParallelStep>;
|
|
779
|
+
try {
|
|
780
|
+
materialized = materializeDynamicParallelStep(step, outputs, stepIndex, { maxItems: params.dynamicFanoutMaxItems });
|
|
781
|
+
} catch (error) {
|
|
782
|
+
const message = error instanceof DynamicFanoutError ? error.message : error instanceof Error ? error.message : String(error);
|
|
783
|
+
dynamicGroupStatuses[stepIndex] = { status: "failed", error: message };
|
|
784
|
+
return buildChainExecutionErrorResult(message, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex }));
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
dynamicChildren[stepIndex] = materialized.items.map((item, itemIndex) => ({
|
|
788
|
+
agent: step.parallel.agent,
|
|
789
|
+
label: materialized.parallel[itemIndex]?.label,
|
|
790
|
+
flatIndex: globalTaskIndex + itemIndex,
|
|
791
|
+
itemKey: item.key,
|
|
792
|
+
structured: Boolean(step.parallel.outputSchema),
|
|
793
|
+
}));
|
|
794
|
+
|
|
795
|
+
if (materialized.parallel.length === 0) {
|
|
796
|
+
const collection: DynamicCollectedResult[] = [];
|
|
797
|
+
try {
|
|
798
|
+
validateDynamicCollection(step.collect.outputSchema, collection);
|
|
799
|
+
} catch (error) {
|
|
800
|
+
const message = error instanceof DynamicFanoutError ? error.message : error instanceof Error ? error.message : String(error);
|
|
801
|
+
dynamicGroupStatuses[stepIndex] = { status: "failed", error: message };
|
|
802
|
+
return buildChainExecutionErrorResult(message, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex }));
|
|
803
|
+
}
|
|
804
|
+
outputs[step.collect.as] = {
|
|
805
|
+
text: JSON.stringify(collection),
|
|
806
|
+
structured: collection,
|
|
807
|
+
agent: step.parallel.agent,
|
|
808
|
+
stepIndex,
|
|
809
|
+
};
|
|
810
|
+
dynamicGroupStatuses[stepIndex] = { status: "completed" };
|
|
811
|
+
prev = "Dynamic fanout produced 0 results.";
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
const dynamicParallelStep: ParallelStep = {
|
|
816
|
+
parallel: materialized.parallel,
|
|
817
|
+
concurrency: step.concurrency,
|
|
818
|
+
failFast: step.failFast,
|
|
819
|
+
};
|
|
820
|
+
const parallelTemplates = materialized.parallel.map((task) => task.task ?? "{previous}");
|
|
821
|
+
const parallelBehaviors = resolveParallelBehaviors(dynamicParallelStep.parallel, agents, stepIndex, chainSkills)
|
|
822
|
+
.map((behavior, taskIndex) => suppressProgressForReadOnlyTask(behavior, parallelTemplates[taskIndex] ?? dynamicParallelStep.parallel[taskIndex]?.task, originalTask));
|
|
823
|
+
|
|
824
|
+
for (let taskIndex = 0; taskIndex < dynamicParallelStep.parallel.length; taskIndex++) {
|
|
825
|
+
const behavior = parallelBehaviors[taskIndex]!;
|
|
826
|
+
const outputPath = typeof behavior.output === "string"
|
|
827
|
+
? (path.isAbsolute(behavior.output) ? behavior.output : path.join(chainDir, behavior.output))
|
|
828
|
+
: undefined;
|
|
829
|
+
const validationError = validateFileOnlyOutputMode(behavior.outputMode, outputPath, `Dynamic chain step ${stepIndex + 1} item ${taskIndex + 1} (${dynamicParallelStep.parallel[taskIndex]!.agent})`);
|
|
830
|
+
if (validationError) {
|
|
831
|
+
dynamicGroupStatuses[stepIndex] = { status: "failed", error: validationError };
|
|
832
|
+
return buildChainExecutionErrorResult(validationError, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex + taskIndex }));
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
progressCreated = ensureParallelProgressFile(chainDir, progressCreated, parallelBehaviors);
|
|
837
|
+
createParallelDirs(chainDir, stepIndex, dynamicParallelStep.parallel.length, dynamicParallelStep.parallel.map((task) => task.agent));
|
|
838
|
+
const parallelResults = await runParallelChainTasks({
|
|
839
|
+
step: dynamicParallelStep,
|
|
840
|
+
parallelTemplates,
|
|
841
|
+
parallelBehaviors,
|
|
842
|
+
agents,
|
|
843
|
+
stepIndex,
|
|
844
|
+
availableModels,
|
|
845
|
+
chainDir,
|
|
846
|
+
prev,
|
|
847
|
+
originalTask,
|
|
848
|
+
ctx,
|
|
849
|
+
intercomEvents,
|
|
850
|
+
cwd,
|
|
851
|
+
runId,
|
|
852
|
+
globalTaskIndex,
|
|
853
|
+
sessionDirForIndex,
|
|
854
|
+
sessionFileForIndex,
|
|
855
|
+
shareEnabled,
|
|
856
|
+
artifactConfig,
|
|
857
|
+
artifactsDir,
|
|
858
|
+
...(params.timeoutMs !== undefined && timeoutAt !== undefined ? { timeoutMs: params.timeoutMs, timeoutAt } : {}),
|
|
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
|
+
});
|
|
877
|
+
globalTaskIndex += dynamicParallelStep.parallel.length;
|
|
878
|
+
|
|
879
|
+
for (const result of parallelResults) {
|
|
880
|
+
results.push(result);
|
|
881
|
+
if (result.progress) allProgress.push(result.progress);
|
|
882
|
+
if (result.artifactPaths) allArtifactPaths.push(result.artifactPaths);
|
|
883
|
+
}
|
|
884
|
+
const collected = collectDynamicResults(step, materialized.items, parallelResults);
|
|
885
|
+
const timedOutIndexInStep = parallelResults.findIndex((result) => result.timedOut);
|
|
886
|
+
const timedOut = timedOutIndexInStep >= 0 ? parallelResults[timedOutIndexInStep] : undefined;
|
|
887
|
+
if (timedOut) {
|
|
888
|
+
dynamicGroupStatuses[stepIndex] = { status: "timed-out", error: timedOut.error };
|
|
889
|
+
return {
|
|
890
|
+
content: [{ type: "text", text: `Chain timed out at step ${stepIndex + 1} (${timedOut.agent}): ${timedOut.error ?? "timeout expired"}` }],
|
|
891
|
+
isError: true,
|
|
892
|
+
details: buildChainExecutionDetails(makeDetailsInput({
|
|
893
|
+
currentStepIndex: stepIndex,
|
|
894
|
+
currentFlatIndex: globalTaskIndex - dynamicParallelStep.parallel.length + timedOutIndexInStep,
|
|
895
|
+
})),
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
const interruptedIndexInStep = parallelResults.findIndex((result) => result.interrupted);
|
|
899
|
+
const interrupted = interruptedIndexInStep >= 0 ? parallelResults[interruptedIndexInStep] : undefined;
|
|
900
|
+
if (interrupted) {
|
|
901
|
+
return {
|
|
902
|
+
content: [{ type: "text", text: `Chain paused after interrupt at step ${stepIndex + 1} (${interrupted.agent}). Waiting for explicit next action.` }],
|
|
903
|
+
details: buildChainExecutionDetails(makeDetailsInput({
|
|
904
|
+
currentStepIndex: stepIndex,
|
|
905
|
+
currentFlatIndex: globalTaskIndex - dynamicParallelStep.parallel.length + interruptedIndexInStep,
|
|
906
|
+
})),
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
const detachedIndexInStep = parallelResults.findIndex((result) => result.detached);
|
|
910
|
+
const detached = detachedIndexInStep >= 0 ? parallelResults[detachedIndexInStep] : undefined;
|
|
911
|
+
if (detached) {
|
|
912
|
+
return {
|
|
913
|
+
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.` }],
|
|
914
|
+
details: buildChainExecutionDetails(makeDetailsInput({
|
|
915
|
+
currentStepIndex: stepIndex,
|
|
916
|
+
currentFlatIndex: globalTaskIndex - dynamicParallelStep.parallel.length + detachedIndexInStep,
|
|
917
|
+
})),
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
const failures = parallelResults
|
|
921
|
+
.map((result, originalIndex) => ({ ...result, originalIndex }))
|
|
922
|
+
.filter((result) => result.exitCode !== 0 && result.exitCode !== -1);
|
|
923
|
+
if (failures.length > 0) {
|
|
924
|
+
const failureSummary = failures
|
|
925
|
+
.map((failure) => `- Item ${failure.originalIndex + 1} (${failure.agent}, key ${materialized.items[failure.originalIndex]?.key ?? failure.originalIndex}): ${failure.error || "failed"}`)
|
|
926
|
+
.join("\n");
|
|
927
|
+
const errorMsg = `Dynamic step ${stepIndex + 1} failed:\n${failureSummary}`;
|
|
928
|
+
dynamicGroupStatuses[stepIndex] = { status: "failed", error: errorMsg };
|
|
929
|
+
const summary = buildChainSummary(chainSteps, results, chainDir, "failed", {
|
|
930
|
+
index: stepIndex,
|
|
931
|
+
error: errorMsg,
|
|
932
|
+
});
|
|
933
|
+
return {
|
|
934
|
+
content: [{ type: "text", text: summary }],
|
|
935
|
+
isError: true,
|
|
936
|
+
details: buildChainExecutionDetails(makeDetailsInput({
|
|
937
|
+
currentStepIndex: stepIndex,
|
|
938
|
+
currentFlatIndex: globalTaskIndex - dynamicParallelStep.parallel.length + failures[0]!.originalIndex,
|
|
939
|
+
})),
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
try {
|
|
943
|
+
validateDynamicCollection(step.collect.outputSchema, collected);
|
|
944
|
+
} catch (error) {
|
|
945
|
+
const message = error instanceof DynamicFanoutError ? error.message : error instanceof Error ? error.message : String(error);
|
|
946
|
+
dynamicGroupStatuses[stepIndex] = { status: "failed", error: message };
|
|
947
|
+
return buildChainExecutionErrorResult(message, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex - dynamicParallelStep.parallel.length }));
|
|
948
|
+
}
|
|
949
|
+
outputs[step.collect.as] = {
|
|
950
|
+
text: JSON.stringify(collected),
|
|
951
|
+
structured: collected,
|
|
952
|
+
agent: step.parallel.agent,
|
|
953
|
+
stepIndex,
|
|
954
|
+
};
|
|
955
|
+
dynamicGroupStatuses[stepIndex] = { status: "completed" };
|
|
956
|
+
const taskResults: ParallelTaskResult[] = parallelResults.map((result, i) => ({
|
|
957
|
+
agent: result.agent,
|
|
958
|
+
taskIndex: i,
|
|
959
|
+
output: getSingleResultOutput(result),
|
|
960
|
+
exitCode: result.exitCode,
|
|
961
|
+
error: result.error,
|
|
962
|
+
}));
|
|
963
|
+
prev = aggregateParallelOutputs(taskResults, (i, agent) => `=== Dynamic Item ${i + 1} (${agent}, key ${materialized.items[i]?.key ?? i}) ===`);
|
|
697
964
|
} else {
|
|
698
965
|
const seqStep = step as SequentialStep;
|
|
699
966
|
const stepTemplate = stepTemplates as string;
|
|
@@ -704,7 +971,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
704
971
|
return {
|
|
705
972
|
content: [{ type: "text", text: `Unknown agent: ${seqStep.agent}` }],
|
|
706
973
|
isError: true,
|
|
707
|
-
details: {
|
|
974
|
+
details: buildChainExecutionDetails(makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex })),
|
|
708
975
|
};
|
|
709
976
|
}
|
|
710
977
|
|
|
@@ -734,7 +1001,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
734
1001
|
templateHasPrevious ? undefined : prev,
|
|
735
1002
|
);
|
|
736
1003
|
|
|
737
|
-
let stepTask = stepTemplate;
|
|
1004
|
+
let stepTask = resolveOutputReferences(stepTemplate, outputs);
|
|
738
1005
|
stepTask = stepTask.replace(/\{task\}/g, originalTask);
|
|
739
1006
|
stepTask = stepTask.replace(/\{previous\}/g, prev);
|
|
740
1007
|
stepTask = stepTask.replace(/\{chain_dir\}/g, chainDir);
|
|
@@ -751,16 +1018,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
751
1018
|
: undefined;
|
|
752
1019
|
const validationError = validateFileOnlyOutputMode(behavior.outputMode, outputPath, `Chain step ${stepIndex + 1} (${seqStep.agent})`);
|
|
753
1020
|
if (validationError) {
|
|
754
|
-
return buildChainExecutionErrorResult(validationError, {
|
|
755
|
-
results,
|
|
756
|
-
includeProgress,
|
|
757
|
-
allProgress,
|
|
758
|
-
allArtifactPaths,
|
|
759
|
-
artifactsDir,
|
|
760
|
-
chainAgents,
|
|
761
|
-
totalSteps,
|
|
762
|
-
currentStepIndex: stepIndex,
|
|
763
|
-
});
|
|
1021
|
+
return buildChainExecutionErrorResult(validationError, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex }));
|
|
764
1022
|
}
|
|
765
1023
|
const maxSubagentDepth = resolveChildMaxSubagentDepth(params.maxSubagentDepth, agentConfig.maxSubagentDepth);
|
|
766
1024
|
const interruptController = new AbortController();
|
|
@@ -778,10 +1036,14 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
778
1036
|
};
|
|
779
1037
|
}
|
|
780
1038
|
|
|
1039
|
+
const structuredRuntime = seqStep.outputSchema
|
|
1040
|
+
? createStructuredOutputRuntime(seqStep.outputSchema, path.join(chainDir, "structured-output"))
|
|
1041
|
+
: undefined;
|
|
781
1042
|
const r = await runSync(ctx.cwd, agents, seqStep.agent, stepTask, {
|
|
782
1043
|
cwd: resolveChildCwd(cwd ?? ctx.cwd, seqStep.cwd),
|
|
783
1044
|
signal,
|
|
784
1045
|
interruptSignal: interruptController.signal,
|
|
1046
|
+
...(params.timeoutMs !== undefined && timeoutAt !== undefined ? { timeoutMs: params.timeoutMs, timeoutAt } : {}),
|
|
785
1047
|
allowIntercomDetach: agentConfig.systemPrompt?.includes(INTERCOM_BRIDGE_MARKER) === true,
|
|
786
1048
|
intercomEvents,
|
|
787
1049
|
runId,
|
|
@@ -794,6 +1056,8 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
794
1056
|
outputPath,
|
|
795
1057
|
outputMode: behavior.outputMode,
|
|
796
1058
|
maxSubagentDepth,
|
|
1059
|
+
maxExecutionTimeMs: agentConfig.maxExecutionTimeMs,
|
|
1060
|
+
maxTokens: agentConfig.maxTokens,
|
|
797
1061
|
controlConfig,
|
|
798
1062
|
onControlEvent,
|
|
799
1063
|
intercomSessionName: childIntercomTarget?.(seqStep.agent, globalTaskIndex),
|
|
@@ -803,6 +1067,9 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
803
1067
|
availableModels,
|
|
804
1068
|
preferredModelProvider: ctx.model?.provider,
|
|
805
1069
|
skills: behavior.skills === false ? [] : behavior.skills,
|
|
1070
|
+
structuredOutput: structuredRuntime,
|
|
1071
|
+
acceptance: seqStep.acceptance,
|
|
1072
|
+
acceptanceContext: { mode: "chain" },
|
|
806
1073
|
onUpdate: onUpdate
|
|
807
1074
|
? (p) => {
|
|
808
1075
|
const stepResults = p.details?.results || [];
|
|
@@ -831,6 +1098,17 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
831
1098
|
chainAgents,
|
|
832
1099
|
totalSteps,
|
|
833
1100
|
currentStepIndex: stepIndex,
|
|
1101
|
+
outputs,
|
|
1102
|
+
workflowGraph: buildWorkflowGraphSnapshot({
|
|
1103
|
+
runId,
|
|
1104
|
+
mode: "chain",
|
|
1105
|
+
steps: chainSteps,
|
|
1106
|
+
results: results.concat(stepResults),
|
|
1107
|
+
currentStepIndex: stepIndex,
|
|
1108
|
+
currentFlatIndex: globalTaskIndex,
|
|
1109
|
+
dynamicChildren,
|
|
1110
|
+
dynamicGroupStatuses,
|
|
1111
|
+
}),
|
|
834
1112
|
},
|
|
835
1113
|
});
|
|
836
1114
|
}
|
|
@@ -847,34 +1125,23 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
847
1125
|
if (r.progress) allProgress.push(r.progress);
|
|
848
1126
|
if (r.artifactPaths) allArtifactPaths.push(r.artifactPaths);
|
|
849
1127
|
|
|
1128
|
+
if (r.timedOut) {
|
|
1129
|
+
return {
|
|
1130
|
+
content: [{ type: "text", text: `Chain timed out at step ${stepIndex + 1} (${r.agent}): ${r.error ?? "timeout expired"}` }],
|
|
1131
|
+
isError: true,
|
|
1132
|
+
details: buildChainExecutionDetails(makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex - 1 })),
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
850
1135
|
if (r.interrupted) {
|
|
851
1136
|
return {
|
|
852
1137
|
content: [{ type: "text", text: `Chain paused after interrupt at step ${stepIndex + 1} (${r.agent}). Waiting for explicit next action.` }],
|
|
853
|
-
details: buildChainExecutionDetails({
|
|
854
|
-
results,
|
|
855
|
-
includeProgress,
|
|
856
|
-
allProgress,
|
|
857
|
-
allArtifactPaths,
|
|
858
|
-
artifactsDir,
|
|
859
|
-
chainAgents,
|
|
860
|
-
totalSteps,
|
|
861
|
-
currentStepIndex: stepIndex,
|
|
862
|
-
}),
|
|
1138
|
+
details: buildChainExecutionDetails(makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex - 1 })),
|
|
863
1139
|
};
|
|
864
1140
|
}
|
|
865
1141
|
if (r.detached) {
|
|
866
1142
|
return {
|
|
867
1143
|
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.` }],
|
|
868
|
-
details: buildChainExecutionDetails({
|
|
869
|
-
results,
|
|
870
|
-
includeProgress,
|
|
871
|
-
allProgress,
|
|
872
|
-
allArtifactPaths,
|
|
873
|
-
artifactsDir,
|
|
874
|
-
chainAgents,
|
|
875
|
-
totalSteps,
|
|
876
|
-
currentStepIndex: stepIndex,
|
|
877
|
-
}),
|
|
1144
|
+
details: buildChainExecutionDetails(makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex - 1 })),
|
|
878
1145
|
};
|
|
879
1146
|
}
|
|
880
1147
|
|
|
@@ -885,16 +1152,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
885
1152
|
});
|
|
886
1153
|
return {
|
|
887
1154
|
content: [{ type: "text", text: summary }],
|
|
888
|
-
details: buildChainExecutionDetails({
|
|
889
|
-
results,
|
|
890
|
-
includeProgress,
|
|
891
|
-
allProgress,
|
|
892
|
-
allArtifactPaths,
|
|
893
|
-
artifactsDir,
|
|
894
|
-
chainAgents,
|
|
895
|
-
totalSteps,
|
|
896
|
-
currentStepIndex: stepIndex,
|
|
897
|
-
}),
|
|
1155
|
+
details: buildChainExecutionDetails(makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex - 1 })),
|
|
898
1156
|
isError: true,
|
|
899
1157
|
};
|
|
900
1158
|
}
|
|
@@ -917,6 +1175,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
917
1175
|
}
|
|
918
1176
|
}
|
|
919
1177
|
|
|
1178
|
+
if (seqStep.as) outputs[seqStep.as] = outputEntryFromResult(r, stepIndex);
|
|
920
1179
|
prev = getSingleResultOutput(r);
|
|
921
1180
|
}
|
|
922
1181
|
}
|
|
@@ -925,14 +1184,6 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
925
1184
|
|
|
926
1185
|
return {
|
|
927
1186
|
content: [{ type: "text", text: summary }],
|
|
928
|
-
details: buildChainExecutionDetails(
|
|
929
|
-
results,
|
|
930
|
-
includeProgress,
|
|
931
|
-
allProgress,
|
|
932
|
-
allArtifactPaths,
|
|
933
|
-
artifactsDir,
|
|
934
|
-
chainAgents,
|
|
935
|
-
totalSteps,
|
|
936
|
-
}),
|
|
1187
|
+
details: buildChainExecutionDetails(makeDetailsInput()),
|
|
937
1188
|
};
|
|
938
1189
|
}
|