pi-subagents 0.35.1 → 0.37.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 +71 -0
- package/README.md +224 -31
- package/agents/advisor.md +73 -0
- package/agents/delegate.md +2 -0
- package/agents/worker.md +2 -0
- package/package.json +11 -13
- package/skills/pi-subagents/SKILL.md +42 -13
- package/src/agents/agent-management.ts +7 -1
- package/src/agents/agents.ts +121 -17
- package/src/api/capability-ceiling.ts +17 -0
- package/src/api/delegation.ts +127 -0
- package/src/api/preflight.ts +399 -0
- package/src/extension/config.ts +7 -1
- package/src/extension/index.ts +52 -38
- package/src/extension/rpc.ts +31 -2
- package/src/extension/schemas.ts +29 -3
- package/src/extension/tool-description.ts +4 -2
- package/src/intercom/intercom-bridge.ts +1 -1
- package/src/intercom/native-supervisor-channel.ts +45 -6
- package/src/intercom/result-intercom.ts +7 -0
- package/src/runs/background/async-execution.ts +158 -17
- package/src/runs/background/async-job-tracker.ts +4 -0
- package/src/runs/background/async-resume.ts +47 -8
- package/src/runs/background/async-status.ts +94 -3
- package/src/runs/background/chain-append.ts +2 -0
- package/src/runs/background/completion-batcher.ts +6 -4
- package/src/runs/background/completion-dedupe.ts +2 -11
- package/src/runs/background/fleet-view.ts +9 -4
- package/src/runs/background/notify.ts +132 -120
- package/src/runs/background/process-terminal.ts +280 -0
- package/src/runs/background/result-watcher.ts +138 -78
- package/src/runs/background/run-status.ts +10 -2
- package/src/runs/background/scheduled-runs.ts +6 -1
- package/src/runs/background/stale-run-reconciler.ts +6 -0
- package/src/runs/background/subagent-runner.ts +406 -54
- package/src/runs/background/subagent-wait.ts +130 -4
- package/src/runs/background/wait-tool.ts +2 -2
- package/src/runs/foreground/chain-execution.ts +181 -111
- package/src/runs/foreground/execution.ts +147 -47
- package/src/runs/foreground/foreground-control.ts +90 -0
- package/src/runs/foreground/subagent-executor.ts +426 -165
- package/src/runs/shared/acceptance.ts +94 -41
- package/src/runs/shared/agent-contract.ts +38 -0
- package/src/runs/shared/capability-ceiling.ts +177 -0
- package/src/runs/shared/child-protocol.ts +1 -1
- package/src/runs/shared/completion-guard.ts +36 -5
- package/src/runs/shared/context-mode.ts +44 -0
- package/src/runs/shared/dynamic-fanout.ts +5 -5
- package/src/runs/shared/long-running-guard.ts +4 -0
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +12 -6
- package/src/runs/shared/nested-events.ts +35 -3
- package/src/runs/shared/parallel-handoff.ts +154 -0
- package/src/runs/shared/parallel-utils.ts +11 -0
- package/src/runs/shared/pi-args.ts +148 -56
- package/src/runs/shared/run-history.ts +90 -5
- package/src/runs/shared/session-lease.ts +25 -5
- package/src/runs/shared/structured-output.ts +112 -7
- package/src/runs/shared/subagent-control.ts +4 -0
- package/src/runs/shared/subagent-prompt-runtime.ts +31 -19
- package/src/runs/shared/task-intent.ts +10 -5
- package/src/runs/shared/tool-availability.ts +21 -3
- package/src/runs/shared/tool-budget.ts +11 -5
- package/src/runs/shared/turn-budget.ts +2 -1
- package/src/runs/shared/worktree.ts +63 -14
- package/src/shared/accessible-dir.ts +25 -0
- package/src/shared/artifacts.ts +37 -7
- package/src/shared/atomic-json.ts +14 -42
- package/src/shared/child-transcript.ts +52 -0
- package/src/shared/file-system-retry.ts +47 -0
- package/src/shared/launch-contract.ts +123 -0
- package/src/shared/settings.ts +9 -1
- package/src/shared/types.ts +314 -28
- package/src/shared/utils.ts +17 -42
- package/src/slash/delegation-adapters.ts +153 -6
- package/src/slash/delegation-json.ts +108 -0
- package/src/slash/delegation-request.ts +182 -36
- package/src/slash/prompt-template-bridge.ts +222 -37
- package/src/slash/selector.ts +147 -0
- package/src/slash/slash-commands.ts +15 -6
- package/src/slash/slash-live-state.ts +2 -2
- package/src/slash/subagents-admin.ts +42 -42
- package/src/tui/fleet-status.ts +362 -0
- package/src/tui/fleet-transcript.ts +472 -0
- package/src/tui/fleet.ts +318 -59
- package/src/tui/render.ts +17 -15
- package/src/watchdog/change-signature.ts +105 -12
- package/src/watchdog/review.ts +7 -2
- package/src/watchdog/runtime.ts +5 -3
- package/src/slash/subagents-editor.ts +0 -86
|
@@ -33,9 +33,11 @@ import {
|
|
|
33
33
|
import { discoverAvailableSkills, normalizeSkillInput } from "../../agents/skills.ts";
|
|
34
34
|
import { INTERCOM_BRIDGE_MARKER } from "../../intercom/intercom-bridge.ts";
|
|
35
35
|
import { runSync } from "./execution.ts";
|
|
36
|
+
import { beginForegroundChild, finishForegroundChild, updateForegroundChild } from "./foreground-control.ts";
|
|
36
37
|
import { buildChainSummary } from "../../shared/formatters.ts";
|
|
37
38
|
import { compactForegroundDetails, getSingleResultOutput, mapConcurrent, resolveChildCwd, sumResultsCost, sumResultsUsage } from "../../shared/utils.ts";
|
|
38
39
|
import { DEFAULT_GLOBAL_CONCURRENCY_LIMIT, Semaphore } from "../shared/parallel-utils.ts";
|
|
40
|
+
import { formatParallelHandoffError, formatParallelHandoffReference, parallelHandoffPath, writeParallelHandoffGroup } from "../shared/parallel-handoff.ts";
|
|
39
41
|
import { recordRun } from "../shared/run-history.ts";
|
|
40
42
|
import {
|
|
41
43
|
cleanupWorktrees,
|
|
@@ -48,11 +50,13 @@ import {
|
|
|
48
50
|
} from "../shared/worktree.ts";
|
|
49
51
|
import {
|
|
50
52
|
type ActivityState,
|
|
53
|
+
type AgentContract,
|
|
51
54
|
type AgentProgress,
|
|
52
55
|
type ArtifactConfig,
|
|
53
56
|
type ArtifactPaths,
|
|
54
57
|
type ControlEvent,
|
|
55
58
|
type Details,
|
|
59
|
+
type ForegroundRunControl,
|
|
56
60
|
type IntercomEventBus,
|
|
57
61
|
type NestedRouteInfo,
|
|
58
62
|
type ResolvedControlConfig,
|
|
@@ -71,8 +75,11 @@ import { ChainOutputValidationError, outputEntryFromResult, resolveOutputReferen
|
|
|
71
75
|
import { createStructuredOutputRuntime } from "../shared/structured-output.ts";
|
|
72
76
|
import { collectDynamicResults, DynamicFanoutError, materializeDynamicParallelStep, validateDynamicCollection, type DynamicCollectedResult } from "../shared/dynamic-fanout.ts";
|
|
73
77
|
import { acceptanceFailureMessage, aggregateAcceptanceReport, evaluateAcceptance, resolveEffectiveAcceptance } from "../shared/acceptance.ts";
|
|
78
|
+
import { isAgentContractV1 } from "../shared/agent-contract.ts";
|
|
74
79
|
import type { ChainOutputMap } from "../../shared/types.ts";
|
|
75
80
|
import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
|
|
81
|
+
import type { ContextMode } from "../shared/context-mode.ts";
|
|
82
|
+
import type { ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
76
83
|
|
|
77
84
|
interface ChainExecutionDetailsInput {
|
|
78
85
|
results: SingleResult[];
|
|
@@ -89,6 +96,7 @@ interface ChainExecutionDetailsInput {
|
|
|
89
96
|
currentFlatIndex?: number;
|
|
90
97
|
dynamicChildren?: Record<number, Array<{ agent: string; label?: string; flatIndex: number; itemKey: string; outputName?: string; structured?: boolean; error?: string }>>;
|
|
91
98
|
dynamicGroupStatuses?: Record<number, { status: "pending" | "running" | "completed" | "failed" | "paused" | "stopped" | "detached"; error?: string; acceptance?: SingleResult["acceptance"] }>;
|
|
99
|
+
parallelHandoff?: Details["parallelHandoff"];
|
|
92
100
|
}
|
|
93
101
|
|
|
94
102
|
interface ParallelChainRunInput {
|
|
@@ -111,6 +119,7 @@ interface ParallelChainRunInput {
|
|
|
111
119
|
sessionFileForIndex?: (idx?: number) => string | undefined;
|
|
112
120
|
sessionFileForTask?: (agentName: string, idx?: number, modelOverride?: string) => string | undefined;
|
|
113
121
|
thinkingOverrideForTask?: (agentName: string, idx?: number, modelOverride?: string) => AgentConfig["thinking"] | undefined;
|
|
122
|
+
contextForAgent?: (agentName: string) => ContextMode;
|
|
114
123
|
shareEnabled: boolean;
|
|
115
124
|
artifactConfig: ArtifactConfig;
|
|
116
125
|
artifactsDir: string;
|
|
@@ -118,22 +127,10 @@ interface ParallelChainRunInput {
|
|
|
118
127
|
onUpdate?: (r: AgentToolResult<Details>) => void;
|
|
119
128
|
onControlEvent?: (event: ControlEvent) => void;
|
|
120
129
|
controlConfig: ResolvedControlConfig;
|
|
130
|
+
agentContract?: AgentContract;
|
|
121
131
|
childIntercomTarget?: (agent: string, index: number) => string | undefined;
|
|
122
132
|
orchestratorIntercomTarget?: string;
|
|
123
|
-
foregroundControl?:
|
|
124
|
-
updatedAt: number;
|
|
125
|
-
currentAgent?: string;
|
|
126
|
-
currentIndex?: number;
|
|
127
|
-
currentActivityState?: ActivityState;
|
|
128
|
-
lastActivityAt?: number;
|
|
129
|
-
currentTool?: string;
|
|
130
|
-
currentToolStartedAt?: number;
|
|
131
|
-
currentPath?: string;
|
|
132
|
-
turnCount?: number;
|
|
133
|
-
tokens?: number;
|
|
134
|
-
toolCount?: number;
|
|
135
|
-
interrupt?: () => boolean;
|
|
136
|
-
};
|
|
133
|
+
foregroundControl?: ForegroundRunControl;
|
|
137
134
|
results: SingleResult[];
|
|
138
135
|
allProgress: AgentProgress[];
|
|
139
136
|
outputs: ChainOutputMap;
|
|
@@ -152,6 +149,7 @@ interface ParallelChainRunInput {
|
|
|
152
149
|
toolBudget?: ResolvedToolBudget;
|
|
153
150
|
configToolBudget?: ToolBudgetConfig;
|
|
154
151
|
globalSemaphore?: Semaphore;
|
|
152
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
155
153
|
dynamic?: boolean;
|
|
156
154
|
}
|
|
157
155
|
|
|
@@ -167,6 +165,7 @@ function buildChainExecutionDetails(input: ChainExecutionDetailsInput): Details
|
|
|
167
165
|
outputs: input.outputs,
|
|
168
166
|
totalChildUsage: sumResultsUsage(input.results),
|
|
169
167
|
totalCost: sumResultsCost(input.results),
|
|
168
|
+
...(input.parallelHandoff ? { parallelHandoff: input.parallelHandoff } : {}),
|
|
170
169
|
workflowGraph: buildWorkflowGraphSnapshot({
|
|
171
170
|
runId: input.runId,
|
|
172
171
|
mode: "chain",
|
|
@@ -200,17 +199,48 @@ function ensureParallelProgressFile(
|
|
|
200
199
|
return true;
|
|
201
200
|
}
|
|
202
201
|
|
|
203
|
-
function
|
|
204
|
-
output: string
|
|
205
|
-
worktreeSetup: WorktreeSetup
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
202
|
+
function finalizeParallelWorktreeHandoff(input: {
|
|
203
|
+
output: string;
|
|
204
|
+
worktreeSetup: WorktreeSetup;
|
|
205
|
+
artifactsDir: string;
|
|
206
|
+
runId: string;
|
|
207
|
+
cwd: string;
|
|
208
|
+
stepIndex: number;
|
|
209
|
+
flatStartIndex: number;
|
|
210
|
+
agents: string[];
|
|
211
|
+
results: SingleResult[];
|
|
212
|
+
}): { output: string; reference?: NonNullable<Details["parallelHandoff"]> } {
|
|
213
|
+
const diffs = diffWorktrees(input.worktreeSetup, input.agents, path.join(input.artifactsDir, "worktree-diffs", input.runId, `step-${input.stepIndex}`));
|
|
214
|
+
const cleanup = cleanupWorktrees(input.worktreeSetup);
|
|
211
215
|
const diffSummary = formatWorktreeDiffSummary(diffs);
|
|
212
|
-
|
|
213
|
-
|
|
216
|
+
try {
|
|
217
|
+
const reference = writeParallelHandoffGroup({
|
|
218
|
+
manifestPath: parallelHandoffPath(input.artifactsDir, input.runId),
|
|
219
|
+
runId: input.runId,
|
|
220
|
+
mode: "chain",
|
|
221
|
+
source: "foreground",
|
|
222
|
+
cwd: input.cwd,
|
|
223
|
+
stepIndex: input.stepIndex,
|
|
224
|
+
flatStartIndex: input.flatStartIndex,
|
|
225
|
+
setup: input.worktreeSetup,
|
|
226
|
+
diffs,
|
|
227
|
+
cleanup,
|
|
228
|
+
results: input.results.map((result) => ({
|
|
229
|
+
agent: result.agent,
|
|
230
|
+
status: result.stopped ? "stopped" : result.detached ? "detached" : result.interrupted ? "paused" : result.exitCode === 0 ? "completed" : "failed",
|
|
231
|
+
summary: getSingleResultOutput(result) || result.error || "(no output)",
|
|
232
|
+
...(result.artifactPaths?.outputPath ? { outputPath: result.artifactPaths.outputPath } : {}),
|
|
233
|
+
...(result.structuredOutput !== undefined ? { structuredOutput: result.structuredOutput } : {}),
|
|
234
|
+
...(result.structuredOutputPath ? { structuredOutputPath: result.structuredOutputPath } : {}),
|
|
235
|
+
...(result.sessionFile ? { sessionPath: result.sessionFile } : {}),
|
|
236
|
+
})),
|
|
237
|
+
});
|
|
238
|
+
const suffix = [diffSummary, formatParallelHandoffReference(reference)].filter(Boolean).join("\n\n");
|
|
239
|
+
return { output: suffix ? `${input.output}\n\n${suffix}` : input.output, reference };
|
|
240
|
+
} catch (error) {
|
|
241
|
+
const suffix = [diffSummary, formatParallelHandoffError(error)].filter(Boolean).join("\n\n");
|
|
242
|
+
return { output: suffix ? `${input.output}\n\n${suffix}` : input.output };
|
|
243
|
+
}
|
|
214
244
|
}
|
|
215
245
|
|
|
216
246
|
function resolveChainToolBudget(input: { stepBudget?: ToolBudgetConfig; runBudget?: ResolvedToolBudget; agentBudget?: ToolBudgetConfig; configBudget?: ToolBudgetConfig }): { toolBudget?: ResolvedToolBudget; error?: string } {
|
|
@@ -294,36 +324,39 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
294
324
|
: undefined;
|
|
295
325
|
taskStr = injectSingleOutputInstruction(taskStr, outputPath, taskAgentConfig);
|
|
296
326
|
const interruptController = new AbortController();
|
|
327
|
+
const childIndex = input.globalTaskIndex + taskIndex;
|
|
297
328
|
if (input.foregroundControl) {
|
|
298
|
-
input.foregroundControl
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
};
|
|
329
|
+
beginForegroundChild(input.foregroundControl, {
|
|
330
|
+
index: childIndex,
|
|
331
|
+
agent: task.agent,
|
|
332
|
+
description: cleanTask.trim(),
|
|
333
|
+
interrupt: () => {
|
|
334
|
+
if (interruptController.signal.aborted) return false;
|
|
335
|
+
interruptController.abort();
|
|
336
|
+
return true;
|
|
337
|
+
},
|
|
338
|
+
});
|
|
309
339
|
}
|
|
310
340
|
|
|
311
341
|
const structuredRuntime = task.outputSchema
|
|
312
342
|
? createStructuredOutputRuntime(task.outputSchema, path.join(input.chainDir, "structured-output"))
|
|
313
343
|
: undefined;
|
|
344
|
+
const agentContract = task.agentContract ?? input.step.agentContract ?? input.agentContract;
|
|
314
345
|
const result = await runSync(input.ctx.cwd, input.agents, task.agent, taskStr, {
|
|
315
346
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
347
|
+
capabilityCeiling: input.capabilityCeiling,
|
|
348
|
+
context: input.contextForAgent?.(task.agent),
|
|
316
349
|
cwd: taskCwd,
|
|
317
350
|
signal: input.signal,
|
|
318
351
|
interruptSignal: interruptController.signal,
|
|
319
352
|
allowIntercomDetach: taskAgentConfig?.systemPrompt?.includes(INTERCOM_BRIDGE_MARKER) === true,
|
|
320
353
|
intercomEvents: input.intercomEvents,
|
|
321
354
|
runId: input.runId,
|
|
322
|
-
index:
|
|
323
|
-
sessionDir: input.sessionDirForIndex(
|
|
324
|
-
sessionFile: input.sessionFileForTask?.(task.agent,
|
|
325
|
-
?? input.sessionFileForIndex?.(
|
|
326
|
-
thinkingOverride: input.thinkingOverrideForTask?.(task.agent,
|
|
355
|
+
index: childIndex,
|
|
356
|
+
sessionDir: input.sessionDirForIndex(childIndex),
|
|
357
|
+
sessionFile: input.sessionFileForTask?.(task.agent, childIndex, effectiveModel)
|
|
358
|
+
?? input.sessionFileForIndex?.(childIndex),
|
|
359
|
+
thinkingOverride: input.thinkingOverrideForTask?.(task.agent, childIndex, effectiveModel),
|
|
327
360
|
share: input.shareEnabled,
|
|
328
361
|
artifactsDir: input.artifactConfig.enabled ? input.artifactsDir : undefined,
|
|
329
362
|
artifactConfig: input.artifactConfig,
|
|
@@ -332,7 +365,7 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
332
365
|
maxSubagentDepth,
|
|
333
366
|
controlConfig: input.controlConfig,
|
|
334
367
|
onControlEvent: input.onControlEvent,
|
|
335
|
-
intercomSessionName: input.childIntercomTarget?.(task.agent,
|
|
368
|
+
intercomSessionName: input.childIntercomTarget?.(task.agent, childIndex),
|
|
336
369
|
orchestratorIntercomTarget: input.orchestratorIntercomTarget,
|
|
337
370
|
nestedRoute: input.nestedRoute,
|
|
338
371
|
modelOverride: effectiveModel,
|
|
@@ -341,13 +374,14 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
341
374
|
modelScope: input.modelScope,
|
|
342
375
|
skills: behavior.skills === false ? [] : behavior.skills,
|
|
343
376
|
structuredOutput: structuredRuntime,
|
|
377
|
+
agentContract,
|
|
344
378
|
acceptance: task.acceptance,
|
|
345
379
|
acceptanceContext: { mode: "chain", dynamic: input.dynamic && task.acceptance === undefined },
|
|
346
380
|
timeoutMs: input.timeoutMs,
|
|
347
381
|
deadlineAt: input.deadlineAt,
|
|
348
382
|
turnBudget: input.turnBudget,
|
|
349
383
|
onDetachedExit: input.onDetachedExit
|
|
350
|
-
? (result) => input.onDetachedExit?.(
|
|
384
|
+
? (result) => input.onDetachedExit?.(childIndex, result)
|
|
351
385
|
: undefined,
|
|
352
386
|
toolBudget: toolBudget.toolBudget,
|
|
353
387
|
onUpdate: input.onUpdate
|
|
@@ -355,18 +389,7 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
355
389
|
const stepResults = progressUpdate.details?.results || [];
|
|
356
390
|
const stepProgress = progressUpdate.details?.progress || [];
|
|
357
391
|
if (input.foregroundControl && stepProgress.length > 0) {
|
|
358
|
-
|
|
359
|
-
input.foregroundControl.currentAgent = task.agent;
|
|
360
|
-
input.foregroundControl.currentIndex = input.globalTaskIndex + taskIndex;
|
|
361
|
-
input.foregroundControl.currentActivityState = current?.activityState;
|
|
362
|
-
input.foregroundControl.lastActivityAt = current?.lastActivityAt;
|
|
363
|
-
input.foregroundControl.currentTool = current?.currentTool;
|
|
364
|
-
input.foregroundControl.currentToolStartedAt = current?.currentToolStartedAt;
|
|
365
|
-
input.foregroundControl.currentPath = current?.currentPath;
|
|
366
|
-
input.foregroundControl.turnCount = current?.turnCount;
|
|
367
|
-
input.foregroundControl.tokens = current?.tokens;
|
|
368
|
-
input.foregroundControl.toolCount = current?.toolCount;
|
|
369
|
-
input.foregroundControl.updatedAt = Date.now();
|
|
392
|
+
updateForegroundChild(input.foregroundControl, childIndex, stepProgress[0]);
|
|
370
393
|
}
|
|
371
394
|
input.onUpdate?.({
|
|
372
395
|
...progressUpdate,
|
|
@@ -385,7 +408,7 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
385
408
|
steps: input.chainSteps,
|
|
386
409
|
results: input.results.concat(stepResults),
|
|
387
410
|
currentStepIndex: input.stepIndex,
|
|
388
|
-
currentFlatIndex:
|
|
411
|
+
currentFlatIndex: childIndex,
|
|
389
412
|
dynamicChildren: input.dynamicChildren,
|
|
390
413
|
dynamicGroupStatuses: input.dynamicGroupStatuses,
|
|
391
414
|
}),
|
|
@@ -394,10 +417,7 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
394
417
|
}
|
|
395
418
|
: undefined,
|
|
396
419
|
});
|
|
397
|
-
if (input.foregroundControl
|
|
398
|
-
input.foregroundControl.interrupt = undefined;
|
|
399
|
-
input.foregroundControl.updatedAt = Date.now();
|
|
400
|
-
}
|
|
420
|
+
if (input.foregroundControl) finishForegroundChild(input.foregroundControl, childIndex);
|
|
401
421
|
|
|
402
422
|
if (result.exitCode !== 0 && failFast) {
|
|
403
423
|
aborted = true;
|
|
@@ -425,6 +445,8 @@ interface ChainExecutionParams {
|
|
|
425
445
|
sessionFileForIndex?: (idx?: number) => string | undefined;
|
|
426
446
|
sessionFileForTask?: (agentName: string, idx?: number, modelOverride?: string) => string | undefined;
|
|
427
447
|
thinkingOverrideForTask?: (agentName: string, idx?: number, modelOverride?: string) => AgentConfig["thinking"] | undefined;
|
|
448
|
+
contextForAgent?: (agentName: string) => ContextMode;
|
|
449
|
+
modelScope?: ModelScopeConfig;
|
|
428
450
|
artifactsDir: string;
|
|
429
451
|
artifactConfig: ArtifactConfig;
|
|
430
452
|
includeProgress?: boolean;
|
|
@@ -432,22 +454,10 @@ interface ChainExecutionParams {
|
|
|
432
454
|
onUpdate?: (r: AgentToolResult<Details>) => void;
|
|
433
455
|
onControlEvent?: (event: ControlEvent) => void;
|
|
434
456
|
controlConfig: ResolvedControlConfig;
|
|
457
|
+
agentContract?: AgentContract;
|
|
435
458
|
childIntercomTarget?: (agent: string, index: number) => string | undefined;
|
|
436
459
|
orchestratorIntercomTarget?: string;
|
|
437
|
-
foregroundControl?:
|
|
438
|
-
updatedAt: number;
|
|
439
|
-
currentAgent?: string;
|
|
440
|
-
currentIndex?: number;
|
|
441
|
-
currentActivityState?: ActivityState;
|
|
442
|
-
lastActivityAt?: number;
|
|
443
|
-
currentTool?: string;
|
|
444
|
-
currentToolStartedAt?: number;
|
|
445
|
-
currentPath?: string;
|
|
446
|
-
turnCount?: number;
|
|
447
|
-
tokens?: number;
|
|
448
|
-
toolCount?: number;
|
|
449
|
-
interrupt?: () => boolean;
|
|
450
|
-
};
|
|
460
|
+
foregroundControl?: ForegroundRunControl;
|
|
451
461
|
chainSkills?: string[];
|
|
452
462
|
chainDir?: string;
|
|
453
463
|
dynamicFanoutMaxItems?: number;
|
|
@@ -464,6 +474,7 @@ interface ChainExecutionParams {
|
|
|
464
474
|
configToolBudget?: ToolBudgetConfig;
|
|
465
475
|
/** Global cap on simultaneously-running tasks within this chain. Defaults to DEFAULT_GLOBAL_CONCURRENCY_LIMIT. */
|
|
466
476
|
globalConcurrencyLimit?: number;
|
|
477
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
467
478
|
}
|
|
468
479
|
|
|
469
480
|
interface ChainExecutionResult {
|
|
@@ -517,6 +528,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
517
528
|
const dynamicGroupStatuses: ChainExecutionDetailsInput["dynamicGroupStatuses"] = {};
|
|
518
529
|
const allProgress: AgentProgress[] = [];
|
|
519
530
|
const allArtifactPaths: ArtifactPaths[] = [];
|
|
531
|
+
let parallelHandoff: Details["parallelHandoff"];
|
|
520
532
|
|
|
521
533
|
const chainAgents: string[] = chainSteps.map((step) =>
|
|
522
534
|
isParallelStep(step)
|
|
@@ -540,6 +552,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
540
552
|
outputs,
|
|
541
553
|
dynamicChildren,
|
|
542
554
|
dynamicGroupStatuses,
|
|
555
|
+
...(parallelHandoff ? { parallelHandoff } : {}),
|
|
543
556
|
...overrides,
|
|
544
557
|
});
|
|
545
558
|
|
|
@@ -671,6 +684,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
671
684
|
const parallelTemplates = stepTemplates as string[];
|
|
672
685
|
const parallelCwd = resolveChildCwd(cwd ?? ctx.cwd, step.cwd);
|
|
673
686
|
let worktreeSetup: WorktreeSetup | undefined;
|
|
687
|
+
let worktreeFinalized = false;
|
|
674
688
|
if (step.worktree) {
|
|
675
689
|
const worktreeTaskCwdConflict = findWorktreeTaskCwdConflict(step.parallel, parallelCwd);
|
|
676
690
|
if (worktreeTaskCwdConflict) {
|
|
@@ -728,6 +742,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
728
742
|
sessionFileForIndex,
|
|
729
743
|
sessionFileForTask,
|
|
730
744
|
thinkingOverrideForTask,
|
|
745
|
+
contextForAgent: params.contextForAgent,
|
|
731
746
|
shareEnabled,
|
|
732
747
|
artifactConfig,
|
|
733
748
|
artifactsDir,
|
|
@@ -742,6 +757,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
742
757
|
dynamicChildren,
|
|
743
758
|
dynamicGroupStatuses,
|
|
744
759
|
controlConfig,
|
|
760
|
+
agentContract: params.agentContract,
|
|
745
761
|
onControlEvent,
|
|
746
762
|
childIntercomTarget,
|
|
747
763
|
orchestratorIntercomTarget,
|
|
@@ -764,6 +780,23 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
764
780
|
if (result.progress) allProgress.push(result.progress);
|
|
765
781
|
if (result.artifactPaths) allArtifactPaths.push(result.artifactPaths);
|
|
766
782
|
}
|
|
783
|
+
let worktreeSuffix = "";
|
|
784
|
+
if (worktreeSetup) {
|
|
785
|
+
worktreeFinalized = true;
|
|
786
|
+
const finalized = finalizeParallelWorktreeHandoff({
|
|
787
|
+
output: "",
|
|
788
|
+
worktreeSetup,
|
|
789
|
+
artifactsDir,
|
|
790
|
+
runId,
|
|
791
|
+
cwd: parallelCwd,
|
|
792
|
+
stepIndex,
|
|
793
|
+
flatStartIndex: globalTaskIndex - step.parallel.length,
|
|
794
|
+
agents: agentNames,
|
|
795
|
+
results: parallelResults,
|
|
796
|
+
});
|
|
797
|
+
if (finalized.reference) parallelHandoff = finalized.reference;
|
|
798
|
+
worktreeSuffix = finalized.output.trim();
|
|
799
|
+
}
|
|
767
800
|
const interruptedIndexInStep = parallelResults.findIndex((result) => result.interrupted);
|
|
768
801
|
const interrupted = interruptedIndexInStep >= 0 ? parallelResults[interruptedIndexInStep] : undefined;
|
|
769
802
|
if (interrupted) {
|
|
@@ -808,6 +841,24 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
808
841
|
})),
|
|
809
842
|
};
|
|
810
843
|
}
|
|
844
|
+
const acceptanceFailures = parallelResults
|
|
845
|
+
.map((result, originalIndex) => ({ result, originalIndex, task: step.parallel[originalIndex] }))
|
|
846
|
+
.filter(({ result, task }) => isAgentContractV1(task?.agentContract ?? step.agentContract ?? params.agentContract) && (task?.gateOn ?? step.gateOn) === "acceptance" && result.acceptance?.status === "rejected");
|
|
847
|
+
if (acceptanceFailures.length > 0) {
|
|
848
|
+
const acceptanceSummary = acceptanceFailures
|
|
849
|
+
.map(({ result, originalIndex }) => `- Task ${originalIndex + 1} (${result.agent}): ${acceptanceFailureMessage(result.acceptance) ?? "acceptance rejected"}`)
|
|
850
|
+
.join("\n");
|
|
851
|
+
const errorMsg = `Parallel step ${stepIndex + 1} acceptance gate failed:\n${acceptanceSummary}`;
|
|
852
|
+
const summary = buildChainSummary(chainSteps, results, chainDir, "failed", { index: stepIndex, error: errorMsg });
|
|
853
|
+
return {
|
|
854
|
+
content: [{ type: "text", text: summary }],
|
|
855
|
+
isError: true,
|
|
856
|
+
details: buildChainExecutionDetails(makeDetailsInput({
|
|
857
|
+
currentStepIndex: stepIndex,
|
|
858
|
+
currentFlatIndex: globalTaskIndex - step.parallel.length + acceptanceFailures[0]!.originalIndex,
|
|
859
|
+
})),
|
|
860
|
+
};
|
|
861
|
+
}
|
|
811
862
|
|
|
812
863
|
for (let taskIndex = 0; taskIndex < parallelResults.length; taskIndex++) {
|
|
813
864
|
const outputName = step.parallel[taskIndex]?.as;
|
|
@@ -831,14 +882,9 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
831
882
|
};
|
|
832
883
|
});
|
|
833
884
|
prev = aggregateParallelOutputs(taskResults);
|
|
834
|
-
prev =
|
|
835
|
-
prev,
|
|
836
|
-
worktreeSetup,
|
|
837
|
-
path.join(chainDir, "worktree-diffs", `step-${stepIndex}`),
|
|
838
|
-
agentNames,
|
|
839
|
-
);
|
|
885
|
+
if (worktreeSuffix) prev = `${prev}\n\n${worktreeSuffix}`;
|
|
840
886
|
} finally {
|
|
841
|
-
if (worktreeSetup) cleanupWorktrees(worktreeSetup);
|
|
887
|
+
if (worktreeSetup && !worktreeFinalized) cleanupWorktrees(worktreeSetup);
|
|
842
888
|
}
|
|
843
889
|
} else if (isDynamicParallelStep(step)) {
|
|
844
890
|
const dynamicStartIndex = globalTaskIndex;
|
|
@@ -863,7 +909,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
863
909
|
if (materialized.parallel.length === 0) {
|
|
864
910
|
const collection: DynamicCollectedResult[] = [];
|
|
865
911
|
try {
|
|
866
|
-
validateDynamicCollection(step.collect.outputSchema, collection);
|
|
912
|
+
await validateDynamicCollection(step.collect.outputSchema, collection);
|
|
867
913
|
} catch (error) {
|
|
868
914
|
const message = error instanceof DynamicFanoutError ? error.message : error instanceof Error ? error.message : String(error);
|
|
869
915
|
dynamicGroupStatuses[stepIndex] = { status: "failed", error: message };
|
|
@@ -884,6 +930,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
884
930
|
task: (step.parallel.task ?? originalTask ?? "").replace(/\{task\}/g, originalTask ?? ""),
|
|
885
931
|
mode: "chain",
|
|
886
932
|
dynamicGroup: true,
|
|
933
|
+
agentContract: step.agentContract ?? params.agentContract,
|
|
887
934
|
});
|
|
888
935
|
const groupAcceptance = await evaluateAcceptance({
|
|
889
936
|
acceptance: effectiveGroupAcceptance,
|
|
@@ -893,9 +940,10 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
893
940
|
notes: "Dynamic fanout produced 0 results.",
|
|
894
941
|
}),
|
|
895
942
|
cwd: cwd ?? ctx.cwd,
|
|
943
|
+
reportOptional: isAgentContractV1(step.agentContract ?? params.agentContract),
|
|
896
944
|
});
|
|
897
945
|
dynamicGroupStatuses[stepIndex].acceptance = groupAcceptance;
|
|
898
|
-
const groupAcceptanceFailure = acceptanceFailureMessage(groupAcceptance);
|
|
946
|
+
const groupAcceptanceFailure = !isAgentContractV1(step.agentContract ?? params.agentContract) || step.gateOn === "acceptance" ? acceptanceFailureMessage(groupAcceptance) : undefined;
|
|
899
947
|
if (groupAcceptanceFailure) {
|
|
900
948
|
dynamicGroupStatuses[stepIndex] = { status: "failed", error: groupAcceptanceFailure, acceptance: groupAcceptance };
|
|
901
949
|
return buildChainExecutionErrorResult(groupAcceptanceFailure, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex }));
|
|
@@ -910,6 +958,8 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
910
958
|
parallel: materialized.parallel,
|
|
911
959
|
concurrency: step.concurrency,
|
|
912
960
|
failFast: step.failFast,
|
|
961
|
+
agentContract: step.agentContract,
|
|
962
|
+
gateOn: step.gateOn,
|
|
913
963
|
};
|
|
914
964
|
const parallelTemplates = materialized.parallel.map((task) => task.task ?? "{previous}");
|
|
915
965
|
const parallelBehaviors = resolveParallelBehaviors(dynamicParallelStep.parallel, agents, stepIndex, chainSkills)
|
|
@@ -949,6 +999,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
949
999
|
sessionFileForIndex,
|
|
950
1000
|
sessionFileForTask,
|
|
951
1001
|
thinkingOverrideForTask,
|
|
1002
|
+
contextForAgent: params.contextForAgent,
|
|
952
1003
|
shareEnabled,
|
|
953
1004
|
artifactConfig,
|
|
954
1005
|
artifactsDir,
|
|
@@ -963,6 +1014,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
963
1014
|
dynamicChildren,
|
|
964
1015
|
dynamicGroupStatuses,
|
|
965
1016
|
controlConfig,
|
|
1017
|
+
agentContract: params.agentContract,
|
|
966
1018
|
onControlEvent,
|
|
967
1019
|
childIntercomTarget,
|
|
968
1020
|
orchestratorIntercomTarget,
|
|
@@ -1030,8 +1082,24 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
1030
1082
|
})),
|
|
1031
1083
|
};
|
|
1032
1084
|
}
|
|
1085
|
+
const acceptanceFailures = parallelResults
|
|
1086
|
+
.map((result, originalIndex) => ({ result, originalIndex, task: dynamicParallelStep.parallel[originalIndex] }))
|
|
1087
|
+
.filter(({ result, task }) => isAgentContractV1(task?.agentContract ?? dynamicParallelStep.agentContract ?? params.agentContract) && (task?.gateOn ?? dynamicParallelStep.gateOn) === "acceptance" && result.acceptance?.status === "rejected");
|
|
1088
|
+
if (acceptanceFailures.length > 0) {
|
|
1089
|
+
const acceptanceSummary = acceptanceFailures
|
|
1090
|
+
.map(({ result, originalIndex }) => `- Item ${originalIndex + 1} (${result.agent}, key ${materialized.items[originalIndex]?.key ?? originalIndex}): ${acceptanceFailureMessage(result.acceptance) ?? "acceptance rejected"}`)
|
|
1091
|
+
.join("\n");
|
|
1092
|
+
const errorMsg = `Dynamic step ${stepIndex + 1} acceptance gate failed:\n${acceptanceSummary}`;
|
|
1093
|
+
dynamicGroupStatuses[stepIndex] = { status: "failed", error: errorMsg };
|
|
1094
|
+
const summary = buildChainSummary(chainSteps, results, chainDir, "failed", { index: stepIndex, error: errorMsg });
|
|
1095
|
+
return {
|
|
1096
|
+
content: [{ type: "text", text: summary }],
|
|
1097
|
+
isError: true,
|
|
1098
|
+
details: buildChainExecutionDetails(makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: dynamicStartIndex + acceptanceFailures[0]!.originalIndex })),
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1033
1101
|
try {
|
|
1034
|
-
validateDynamicCollection(step.collect.outputSchema, collected);
|
|
1102
|
+
await validateDynamicCollection(step.collect.outputSchema, collected);
|
|
1035
1103
|
} catch (error) {
|
|
1036
1104
|
const message = error instanceof DynamicFanoutError ? error.message : error instanceof Error ? error.message : String(error);
|
|
1037
1105
|
dynamicGroupStatuses[stepIndex] = { status: "failed", error: message };
|
|
@@ -1053,6 +1121,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
1053
1121
|
.join("\n"),
|
|
1054
1122
|
mode: "chain",
|
|
1055
1123
|
dynamicGroup: true,
|
|
1124
|
+
agentContract: step.agentContract ?? params.agentContract,
|
|
1056
1125
|
});
|
|
1057
1126
|
const groupAcceptance = await evaluateAcceptance({
|
|
1058
1127
|
acceptance: effectiveGroupAcceptance,
|
|
@@ -1062,9 +1131,10 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
1062
1131
|
notes: `Dynamic fanout collected ${collected.length} result(s) into ${step.collect.as}.`,
|
|
1063
1132
|
}),
|
|
1064
1133
|
cwd: cwd ?? ctx.cwd,
|
|
1134
|
+
reportOptional: isAgentContractV1(step.agentContract ?? params.agentContract),
|
|
1065
1135
|
});
|
|
1066
1136
|
dynamicGroupStatuses[stepIndex].acceptance = groupAcceptance;
|
|
1067
|
-
const groupAcceptanceFailure = effectiveGroupAcceptance.explicit ? acceptanceFailureMessage(groupAcceptance) : undefined;
|
|
1137
|
+
const groupAcceptanceFailure = effectiveGroupAcceptance.explicit && (!isAgentContractV1(step.agentContract ?? params.agentContract) || step.gateOn === "acceptance") ? acceptanceFailureMessage(groupAcceptance) : undefined;
|
|
1068
1138
|
if (groupAcceptanceFailure) {
|
|
1069
1139
|
dynamicGroupStatuses[stepIndex] = { status: "failed", error: groupAcceptanceFailure, acceptance: groupAcceptance };
|
|
1070
1140
|
return buildChainExecutionErrorResult(groupAcceptanceFailure, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex - dynamicParallelStep.parallel.length }));
|
|
@@ -1147,22 +1217,22 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
1147
1217
|
const childIndex = globalTaskIndex;
|
|
1148
1218
|
const interruptController = new AbortController();
|
|
1149
1219
|
if (foregroundControl) {
|
|
1150
|
-
foregroundControl
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
};
|
|
1220
|
+
beginForegroundChild(foregroundControl, {
|
|
1221
|
+
index: childIndex,
|
|
1222
|
+
agent: seqStep.agent,
|
|
1223
|
+
description: cleanTask.trim(),
|
|
1224
|
+
interrupt: () => {
|
|
1225
|
+
if (interruptController.signal.aborted) return false;
|
|
1226
|
+
interruptController.abort();
|
|
1227
|
+
return true;
|
|
1228
|
+
},
|
|
1229
|
+
});
|
|
1161
1230
|
}
|
|
1162
1231
|
|
|
1163
1232
|
const structuredRuntime = seqStep.outputSchema
|
|
1164
1233
|
? createStructuredOutputRuntime(seqStep.outputSchema, path.join(chainDir, "structured-output"))
|
|
1165
1234
|
: undefined;
|
|
1235
|
+
const agentContract = seqStep.agentContract ?? params.agentContract;
|
|
1166
1236
|
const toolBudget = resolveChainToolBudget({ stepBudget: seqStep.toolBudget, runBudget: params.toolBudget, agentBudget: agentConfig?.toolBudget, configBudget: params.configToolBudget });
|
|
1167
1237
|
if (toolBudget.error) return buildChainExecutionErrorResult(toolBudget.error, {
|
|
1168
1238
|
results,
|
|
@@ -1182,6 +1252,8 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
1182
1252
|
});
|
|
1183
1253
|
const r = await runSync(ctx.cwd, agents, seqStep.agent, stepTask, {
|
|
1184
1254
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
1255
|
+
capabilityCeiling: params.capabilityCeiling,
|
|
1256
|
+
context: params.contextForAgent?.(seqStep.agent),
|
|
1185
1257
|
cwd: resolveChildCwd(cwd ?? ctx.cwd, seqStep.cwd),
|
|
1186
1258
|
signal,
|
|
1187
1259
|
interruptSignal: interruptController.signal,
|
|
@@ -1210,6 +1282,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
1210
1282
|
modelScope,
|
|
1211
1283
|
skills: behavior.skills === false ? [] : behavior.skills,
|
|
1212
1284
|
structuredOutput: structuredRuntime,
|
|
1285
|
+
agentContract,
|
|
1213
1286
|
acceptance: seqStep.acceptance,
|
|
1214
1287
|
acceptanceContext: { mode: "chain" },
|
|
1215
1288
|
timeoutMs: params.timeoutMs,
|
|
@@ -1224,18 +1297,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
1224
1297
|
const stepResults = p.details?.results || [];
|
|
1225
1298
|
const stepProgress = p.details?.progress || [];
|
|
1226
1299
|
if (foregroundControl && stepProgress.length > 0) {
|
|
1227
|
-
|
|
1228
|
-
foregroundControl.currentAgent = seqStep.agent;
|
|
1229
|
-
foregroundControl.currentIndex = childIndex;
|
|
1230
|
-
foregroundControl.currentActivityState = current?.activityState;
|
|
1231
|
-
foregroundControl.lastActivityAt = current?.lastActivityAt;
|
|
1232
|
-
foregroundControl.currentTool = current?.currentTool;
|
|
1233
|
-
foregroundControl.currentToolStartedAt = current?.currentToolStartedAt;
|
|
1234
|
-
foregroundControl.currentPath = current?.currentPath;
|
|
1235
|
-
foregroundControl.turnCount = current?.turnCount;
|
|
1236
|
-
foregroundControl.tokens = current?.tokens;
|
|
1237
|
-
foregroundControl.toolCount = current?.toolCount;
|
|
1238
|
-
foregroundControl.updatedAt = Date.now();
|
|
1300
|
+
updateForegroundChild(foregroundControl, childIndex, stepProgress[0]);
|
|
1239
1301
|
}
|
|
1240
1302
|
onUpdate({
|
|
1241
1303
|
...p,
|
|
@@ -1263,10 +1325,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
1263
1325
|
}
|
|
1264
1326
|
: undefined,
|
|
1265
1327
|
});
|
|
1266
|
-
if (foregroundControl
|
|
1267
|
-
foregroundControl.interrupt = undefined;
|
|
1268
|
-
foregroundControl.updatedAt = Date.now();
|
|
1269
|
-
}
|
|
1328
|
+
if (foregroundControl) finishForegroundChild(foregroundControl, childIndex);
|
|
1270
1329
|
recordRun(seqStep.agent, cleanTask, r.exitCode, r.progressSummary?.durationMs ?? 0);
|
|
1271
1330
|
|
|
1272
1331
|
globalTaskIndex++;
|
|
@@ -1298,6 +1357,17 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
1298
1357
|
isError: true,
|
|
1299
1358
|
};
|
|
1300
1359
|
}
|
|
1360
|
+
if (isAgentContractV1(agentContract) && seqStep.gateOn === "acceptance" && r.acceptance?.status === "rejected") {
|
|
1361
|
+
const summary = buildChainSummary(chainSteps, results, chainDir, "failed", {
|
|
1362
|
+
index: stepIndex,
|
|
1363
|
+
error: acceptanceFailureMessage(r.acceptance) ?? "Chain acceptance gate rejected the step.",
|
|
1364
|
+
});
|
|
1365
|
+
return {
|
|
1366
|
+
content: [{ type: "text", text: summary }],
|
|
1367
|
+
details: buildChainExecutionDetails(makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: childIndex })),
|
|
1368
|
+
isError: true,
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1301
1371
|
|
|
1302
1372
|
if (behavior.output) {
|
|
1303
1373
|
try {
|