@leing2021/super-pi 0.22.0 → 0.23.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.
Files changed (101) hide show
  1. package/README.md +38 -209
  2. package/extensions/ce-core/index.ts +190 -7
  3. package/extensions/ce-core/tools/parallel-subagent.ts +3 -3
  4. package/extensions/ce-core/tools/subagent.ts +2 -2
  5. package/package.json +4 -18
  6. package/rules/common/naming.md +154 -0
  7. package/skills/02-plan/SKILL.md +3 -2
  8. package/skills/03-work/SKILL.md +9 -6
  9. package/skills/04-review/SKILL.md +3 -2
  10. package/skills/06-next/SKILL.md +38 -10
  11. package/skills/06-next/references/recommendation-logic.md +2 -2
  12. package/skills/07-worktree/SKILL.md +9 -8
  13. package/skills/{09-help → 08-help}/SKILL.md +3 -3
  14. package/skills/{09-help → 08-help}/references/workflow-sequence.md +2 -1
  15. package/skills/references/language-detection.md +44 -0
  16. package/extensions/subagent/agent-management.ts +0 -595
  17. package/extensions/subagent/agent-manager-chain-detail.ts +0 -162
  18. package/extensions/subagent/agent-manager-detail.ts +0 -231
  19. package/extensions/subagent/agent-manager-edit.ts +0 -390
  20. package/extensions/subagent/agent-manager-list.ts +0 -278
  21. package/extensions/subagent/agent-manager-parallel.ts +0 -304
  22. package/extensions/subagent/agent-manager.ts +0 -705
  23. package/extensions/subagent/agent-scope.ts +0 -8
  24. package/extensions/subagent/agent-selection.ts +0 -25
  25. package/extensions/subagent/agent-serializer.ts +0 -123
  26. package/extensions/subagent/agent-templates.ts +0 -62
  27. package/extensions/subagent/agents/context-builder.md +0 -37
  28. package/extensions/subagent/agents/delegate.md +0 -9
  29. package/extensions/subagent/agents/oracle.md +0 -73
  30. package/extensions/subagent/agents/planner.md +0 -52
  31. package/extensions/subagent/agents/researcher.md +0 -50
  32. package/extensions/subagent/agents/reviewer.md +0 -38
  33. package/extensions/subagent/agents/scout.md +0 -48
  34. package/extensions/subagent/agents/worker.md +0 -52
  35. package/extensions/subagent/agents.ts +0 -761
  36. package/extensions/subagent/artifacts.ts +0 -100
  37. package/extensions/subagent/async-execution.ts +0 -520
  38. package/extensions/subagent/async-job-tracker.ts +0 -216
  39. package/extensions/subagent/async-status.ts +0 -241
  40. package/extensions/subagent/chain-clarify.ts +0 -1364
  41. package/extensions/subagent/chain-execution.ts +0 -853
  42. package/extensions/subagent/chain-serializer.ts +0 -126
  43. package/extensions/subagent/completion-dedupe.ts +0 -65
  44. package/extensions/subagent/doctor.ts +0 -200
  45. package/extensions/subagent/execution.ts +0 -738
  46. package/extensions/subagent/file-coalescer.ts +0 -42
  47. package/extensions/subagent/fork-context.ts +0 -63
  48. package/extensions/subagent/formatters.ts +0 -122
  49. package/extensions/subagent/frontmatter.ts +0 -31
  50. package/extensions/subagent/index.ts +0 -585
  51. package/extensions/subagent/intercom-bridge.ts +0 -240
  52. package/extensions/subagent/jsonl-writer.ts +0 -83
  53. package/extensions/subagent/model-fallback.ts +0 -108
  54. package/extensions/subagent/notify.ts +0 -110
  55. package/extensions/subagent/parallel-utils.ts +0 -108
  56. package/extensions/subagent/pi-args.ts +0 -138
  57. package/extensions/subagent/pi-spawn.ts +0 -100
  58. package/extensions/subagent/post-exit-stdio-guard.ts +0 -87
  59. package/extensions/subagent/prompt-template-bridge.ts +0 -399
  60. package/extensions/subagent/prompts/gather-context-and-clarify.md +0 -13
  61. package/extensions/subagent/prompts/parallel-cleanup.md +0 -42
  62. package/extensions/subagent/prompts/parallel-research.md +0 -50
  63. package/extensions/subagent/prompts/parallel-review.md +0 -40
  64. package/extensions/subagent/render-helpers.ts +0 -82
  65. package/extensions/subagent/render.ts +0 -836
  66. package/extensions/subagent/result-intercom.ts +0 -237
  67. package/extensions/subagent/result-watcher.ts +0 -171
  68. package/extensions/subagent/run-history.ts +0 -57
  69. package/extensions/subagent/run-status.ts +0 -136
  70. package/extensions/subagent/schemas.ts +0 -164
  71. package/extensions/subagent/session-tokens.ts +0 -50
  72. package/extensions/subagent/settings.ts +0 -367
  73. package/extensions/subagent/single-output.ts +0 -97
  74. package/extensions/subagent/skills.ts +0 -626
  75. package/extensions/subagent/slash-bridge.ts +0 -176
  76. package/extensions/subagent/slash-commands.ts +0 -303
  77. package/extensions/subagent/slash-live-state.ts +0 -294
  78. package/extensions/subagent/subagent-control.ts +0 -150
  79. package/extensions/subagent/subagent-executor.ts +0 -1899
  80. package/extensions/subagent/subagent-prompt-runtime.ts +0 -75
  81. package/extensions/subagent/subagent-runner.ts +0 -1470
  82. package/extensions/subagent/subagents-status.ts +0 -472
  83. package/extensions/subagent/text-editor.ts +0 -272
  84. package/extensions/subagent/top-level-async.ts +0 -15
  85. package/extensions/subagent/types.ts +0 -623
  86. package/extensions/subagent/utils.ts +0 -456
  87. package/extensions/subagent/worktree.ts +0 -579
  88. package/extensions/super-pi-extension/agents/ce-oracle.md +0 -109
  89. package/extensions/super-pi-extension/agents/ce-planner.md +0 -116
  90. package/extensions/super-pi-extension/agents/ce-reviewer.md +0 -99
  91. package/extensions/super-pi-extension/agents/ce-scout.md +0 -80
  92. package/extensions/super-pi-extension/agents/ce-worker.md +0 -78
  93. package/extensions/super-pi-extension/chains/ce-parallel-review.chain.md +0 -34
  94. package/extensions/super-pi-extension/chains/ce-review-only.chain.md +0 -12
  95. package/extensions/super-pi-extension/chains/ce-standard.chain.md +0 -28
  96. package/extensions/super-pi-extension/index.ts +0 -129
  97. package/extensions/super-pi-extension/model-sync.ts +0 -204
  98. package/skills/08-status/SKILL.md +0 -41
  99. package/skills/08-status/references/artifact-locations.md +0 -10
  100. package/skills/10-rules/SKILL.md +0 -76
  101. package/skills/pi-subagents/SKILL.md +0 -566
@@ -1,853 +0,0 @@
1
- // Based on pi-subagents by Nico Bailon (https://github.com/nicobailon/pi-subagents)
2
- // MIT License
3
- /**
4
- * Chain execution logic for subagent tool
5
- */
6
-
7
- import * as fs from "node:fs";
8
- import * as path from "node:path";
9
- import type { AgentToolResult } from "@mariozechner/pi-agent-core";
10
- import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
11
- import type { AgentConfig } from "./agents.ts";
12
- import { ChainClarifyComponent, type ChainClarifyResult, type BehaviorOverride, type ModelInfo } from "./chain-clarify.ts";
13
- import {
14
- resolveChainTemplates,
15
- createChainDir,
16
- removeChainDir,
17
- resolveStepBehavior,
18
- resolveParallelBehaviors,
19
- buildChainInstructions,
20
- writeInitialProgressFile,
21
- createParallelDirs,
22
- aggregateParallelOutputs,
23
- isParallelStep,
24
- type StepOverrides,
25
- type ChainStep,
26
- type SequentialStep,
27
- type ParallelTaskResult,
28
- type ResolvedStepBehavior,
29
- type ResolvedTemplates,
30
- } from "./settings.ts";
31
- import { discoverAvailableSkills, normalizeSkillInput } from "./skills.ts";
32
- import { INTERCOM_BRIDGE_MARKER } from "./intercom-bridge.ts";
33
- import { runSync } from "./execution.ts";
34
- import { buildChainSummary } from "./formatters.ts";
35
- import { compactForegroundDetails, getSingleResultOutput, mapConcurrent, resolveChildCwd } from "./utils.ts";
36
- import { recordRun } from "./run-history.ts";
37
- import {
38
- cleanupWorktrees,
39
- createWorktrees,
40
- diffWorktrees,
41
- findWorktreeTaskCwdConflict,
42
- formatWorktreeDiffSummary,
43
- formatWorktreeTaskCwdConflict,
44
- type WorktreeSetup,
45
- } from "./worktree.ts";
46
- import {
47
- type ActivityState,
48
- type AgentProgress,
49
- type ArtifactConfig,
50
- type ArtifactPaths,
51
- type ControlEvent,
52
- type Details,
53
- type IntercomEventBus,
54
- type ResolvedControlConfig,
55
- type SingleResult,
56
- MAX_CONCURRENCY,
57
- resolveChildMaxSubagentDepth,
58
- } from "./types.ts";
59
- import { resolveModelCandidate } from "./model-fallback.ts";
60
-
61
- interface ChainExecutionDetailsInput {
62
- results: SingleResult[];
63
- includeProgress?: boolean;
64
- allProgress: AgentProgress[];
65
- allArtifactPaths: ArtifactPaths[];
66
- artifactsDir: string;
67
- chainAgents: string[];
68
- totalSteps: number;
69
- currentStepIndex?: number;
70
- }
71
-
72
- interface ParallelChainRunInput {
73
- step: Exclude<ChainStep, SequentialStep>;
74
- parallelTemplates: string[];
75
- parallelBehaviors: ResolvedStepBehavior[];
76
- agents: AgentConfig[];
77
- stepIndex: number;
78
- availableModels: ModelInfo[];
79
- chainDir: string;
80
- prev: string;
81
- originalTask: string;
82
- ctx: ExtensionContext;
83
- intercomEvents?: IntercomEventBus;
84
- cwd?: string;
85
- runId: string;
86
- globalTaskIndex: number;
87
- sessionDirForIndex: (idx?: number) => string | undefined;
88
- sessionFileForIndex?: (idx?: number) => string | undefined;
89
- shareEnabled: boolean;
90
- artifactConfig: ArtifactConfig;
91
- artifactsDir: string;
92
- signal?: AbortSignal;
93
- onUpdate?: (r: AgentToolResult<Details>) => void;
94
- onControlEvent?: (event: ControlEvent) => void;
95
- controlConfig: ResolvedControlConfig;
96
- childIntercomTarget?: (agent: string, index: number) => string | undefined;
97
- foregroundControl?: {
98
- updatedAt: number;
99
- currentAgent?: string;
100
- currentIndex?: number;
101
- currentActivityState?: ActivityState;
102
- lastActivityAt?: number;
103
- currentTool?: string;
104
- currentToolStartedAt?: number;
105
- interrupt?: () => boolean;
106
- };
107
- results: SingleResult[];
108
- allProgress: AgentProgress[];
109
- chainAgents: string[];
110
- totalSteps: number;
111
- worktreeSetup?: WorktreeSetup;
112
- maxSubagentDepth: number;
113
- }
114
-
115
- function buildChainExecutionDetails(input: ChainExecutionDetailsInput): Details {
116
- return compactForegroundDetails({
117
- mode: "chain",
118
- results: input.results,
119
- progress: input.includeProgress ? input.allProgress : undefined,
120
- artifacts: input.allArtifactPaths.length ? { dir: input.artifactsDir, files: input.allArtifactPaths } : undefined,
121
- chainAgents: input.chainAgents,
122
- totalSteps: input.totalSteps,
123
- currentStepIndex: input.currentStepIndex,
124
- });
125
- }
126
-
127
- function buildChainExecutionErrorResult(message: string, input: ChainExecutionDetailsInput): ChainExecutionResult {
128
- return {
129
- content: [{ type: "text", text: message }],
130
- isError: true,
131
- details: buildChainExecutionDetails(input),
132
- };
133
- }
134
-
135
- function ensureParallelProgressFile(
136
- chainDir: string,
137
- progressCreated: boolean,
138
- parallelBehaviors: ResolvedStepBehavior[],
139
- ): boolean {
140
- if (progressCreated || !parallelBehaviors.some((behavior) => behavior.progress)) {
141
- return progressCreated;
142
- }
143
- writeInitialProgressFile(chainDir);
144
- return true;
145
- }
146
-
147
- function appendParallelWorktreeSummary(
148
- output: string,
149
- worktreeSetup: WorktreeSetup | undefined,
150
- diffsDir: string,
151
- agents: string[],
152
- ): string {
153
- if (!worktreeSetup) return output;
154
- const diffs = diffWorktrees(worktreeSetup, agents, diffsDir);
155
- const diffSummary = formatWorktreeDiffSummary(diffs);
156
- if (!diffSummary) return output;
157
- return `${output}\n\n${diffSummary}`;
158
- }
159
-
160
- async function runParallelChainTasks(input: ParallelChainRunInput): Promise<SingleResult[]> {
161
- const concurrency = input.step.concurrency ?? MAX_CONCURRENCY;
162
- const failFast = input.step.failFast ?? false;
163
- let aborted = false;
164
-
165
- const parallelResults = await mapConcurrent(
166
- input.step.parallel,
167
- concurrency,
168
- async (task, taskIndex) => {
169
- if (aborted && failFast) {
170
- return {
171
- agent: task.agent,
172
- task: "(skipped)",
173
- exitCode: -1,
174
- messages: [],
175
- usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 },
176
- error: "Skipped due to fail-fast",
177
- } as SingleResult;
178
- }
179
-
180
- const behavior = input.parallelBehaviors[taskIndex]!;
181
- const taskTemplate = input.parallelTemplates[taskIndex] ?? "{previous}";
182
- const templateHasPrevious = taskTemplate.includes("{previous}");
183
- const { prefix, suffix } = buildChainInstructions(
184
- behavior,
185
- input.chainDir,
186
- false,
187
- templateHasPrevious ? undefined : input.prev,
188
- );
189
-
190
- let taskStr = taskTemplate;
191
- taskStr = taskStr.replace(/\{task\}/g, input.originalTask);
192
- taskStr = taskStr.replace(/\{previous\}/g, input.prev);
193
- taskStr = taskStr.replace(/\{chain_dir\}/g, input.chainDir);
194
- const cleanTask = taskStr;
195
- taskStr = prefix + taskStr + suffix;
196
-
197
- const taskAgentConfig = input.agents.find((agent) => agent.name === task.agent);
198
- const effectiveModel =
199
- (task.model ? resolveModelCandidate(task.model, input.availableModels, input.ctx.model?.provider) : null)
200
- ?? resolveModelCandidate(taskAgentConfig?.model, input.availableModels, input.ctx.model?.provider);
201
- const maxSubagentDepth = resolveChildMaxSubagentDepth(input.maxSubagentDepth, taskAgentConfig?.maxSubagentDepth);
202
-
203
- const taskCwd = input.worktreeSetup
204
- ? input.worktreeSetup.worktrees[taskIndex]!.agentCwd
205
- : resolveChildCwd(input.cwd ?? input.ctx.cwd, task.cwd);
206
-
207
- const outputPath = typeof behavior.output === "string"
208
- ? (path.isAbsolute(behavior.output) ? behavior.output : path.join(input.chainDir, behavior.output))
209
- : undefined;
210
- const interruptController = new AbortController();
211
- if (input.foregroundControl) {
212
- input.foregroundControl.currentAgent = task.agent;
213
- input.foregroundControl.currentIndex = input.globalTaskIndex + taskIndex;
214
- input.foregroundControl.currentActivityState = undefined;
215
- input.foregroundControl.updatedAt = Date.now();
216
- input.foregroundControl.interrupt = () => {
217
- if (interruptController.signal.aborted) return false;
218
- interruptController.abort();
219
- input.foregroundControl!.currentActivityState = undefined;
220
- input.foregroundControl!.updatedAt = Date.now();
221
- return true;
222
- };
223
- }
224
-
225
- const result = await runSync(input.ctx.cwd, input.agents, task.agent, taskStr, {
226
- cwd: taskCwd,
227
- signal: input.signal,
228
- interruptSignal: interruptController.signal,
229
- allowIntercomDetach: taskAgentConfig?.systemPrompt?.includes(INTERCOM_BRIDGE_MARKER) === true,
230
- intercomEvents: input.intercomEvents,
231
- runId: input.runId,
232
- index: input.globalTaskIndex + taskIndex,
233
- sessionDir: input.sessionDirForIndex(input.globalTaskIndex + taskIndex),
234
- sessionFile: input.sessionFileForIndex?.(input.globalTaskIndex + taskIndex),
235
- share: input.shareEnabled,
236
- artifactsDir: input.artifactConfig.enabled ? input.artifactsDir : undefined,
237
- artifactConfig: input.artifactConfig,
238
- outputPath,
239
- maxSubagentDepth,
240
- controlConfig: input.controlConfig,
241
- onControlEvent: input.onControlEvent,
242
- intercomSessionName: input.childIntercomTarget?.(task.agent, input.globalTaskIndex + taskIndex),
243
- modelOverride: effectiveModel,
244
- availableModels: input.availableModels,
245
- preferredModelProvider: input.ctx.model?.provider,
246
- skills: behavior.skills === false ? [] : behavior.skills,
247
- onUpdate: input.onUpdate
248
- ? (progressUpdate) => {
249
- const stepResults = progressUpdate.details?.results || [];
250
- const stepProgress = progressUpdate.details?.progress || [];
251
- if (input.foregroundControl && stepProgress.length > 0) {
252
- const current = stepProgress[0];
253
- input.foregroundControl.currentAgent = task.agent;
254
- input.foregroundControl.currentIndex = input.globalTaskIndex + taskIndex;
255
- input.foregroundControl.currentActivityState = current?.activityState;
256
- input.foregroundControl.lastActivityAt = current?.lastActivityAt;
257
- input.foregroundControl.currentTool = current?.currentTool;
258
- input.foregroundControl.currentToolStartedAt = current?.currentToolStartedAt;
259
- input.foregroundControl.updatedAt = Date.now();
260
- }
261
- input.onUpdate?.({
262
- ...progressUpdate,
263
- details: {
264
- mode: "chain",
265
- results: input.results.concat(stepResults),
266
- progress: input.allProgress.concat(stepProgress),
267
- controlEvents: progressUpdate.details?.controlEvents,
268
- chainAgents: input.chainAgents,
269
- totalSteps: input.totalSteps,
270
- currentStepIndex: input.stepIndex,
271
- },
272
- });
273
- }
274
- : undefined,
275
- });
276
- if (input.foregroundControl?.currentIndex === input.globalTaskIndex + taskIndex) {
277
- input.foregroundControl.interrupt = undefined;
278
- input.foregroundControl.updatedAt = Date.now();
279
- }
280
-
281
- if (result.exitCode !== 0 && failFast) {
282
- aborted = true;
283
- }
284
- recordRun(task.agent, cleanTask, result.exitCode, result.progressSummary?.durationMs ?? 0);
285
- return result;
286
- },
287
- );
288
-
289
- return parallelResults;
290
- }
291
-
292
- export interface ChainExecutionParams {
293
- chain: ChainStep[];
294
- task?: string;
295
- agents: AgentConfig[];
296
- ctx: ExtensionContext;
297
- intercomEvents?: IntercomEventBus;
298
- signal?: AbortSignal;
299
- runId: string;
300
- cwd?: string;
301
- shareEnabled: boolean;
302
- sessionDirForIndex: (idx?: number) => string | undefined;
303
- sessionFileForIndex?: (idx?: number) => string | undefined;
304
- artifactsDir: string;
305
- artifactConfig: ArtifactConfig;
306
- includeProgress?: boolean;
307
- clarify?: boolean;
308
- onUpdate?: (r: AgentToolResult<Details>) => void;
309
- onControlEvent?: (event: ControlEvent) => void;
310
- controlConfig: ResolvedControlConfig;
311
- childIntercomTarget?: (agent: string, index: number) => string | undefined;
312
- foregroundControl?: {
313
- updatedAt: number;
314
- currentAgent?: string;
315
- currentIndex?: number;
316
- currentActivityState?: ActivityState;
317
- lastActivityAt?: number;
318
- currentTool?: string;
319
- currentToolStartedAt?: number;
320
- interrupt?: () => boolean;
321
- };
322
- chainSkills?: string[];
323
- chainDir?: string;
324
- maxSubagentDepth: number;
325
- worktreeSetupHook?: string;
326
- worktreeSetupHookTimeoutMs?: number;
327
- }
328
-
329
- export interface ChainExecutionResult {
330
- content: Array<{ type: "text"; text: string }>;
331
- details: Details;
332
- isError?: boolean;
333
- /** User requested async execution via TUI - caller should dispatch to executeAsyncChain */
334
- requestedAsync?: {
335
- chain: ChainStep[];
336
- chainSkills: string[];
337
- };
338
- }
339
-
340
- /**
341
- * Execute a chain of subagent steps
342
- */
343
- export async function executeChain(params: ChainExecutionParams): Promise<ChainExecutionResult> {
344
- const {
345
- chain: chainSteps,
346
- agents,
347
- ctx,
348
- signal,
349
- runId,
350
- cwd,
351
- shareEnabled,
352
- sessionDirForIndex,
353
- sessionFileForIndex,
354
- artifactsDir,
355
- artifactConfig,
356
- includeProgress,
357
- clarify,
358
- onUpdate,
359
- onControlEvent,
360
- controlConfig,
361
- childIntercomTarget,
362
- foregroundControl,
363
- intercomEvents,
364
- chainSkills: chainSkillsParam,
365
- chainDir: chainDirBase,
366
- } = params;
367
- const chainSkills = chainSkillsParam ?? [];
368
-
369
- const allProgress: AgentProgress[] = [];
370
- const allArtifactPaths: ArtifactPaths[] = [];
371
-
372
- const chainAgents: string[] = chainSteps.map((step) =>
373
- isParallelStep(step)
374
- ? `[${step.parallel.map((t) => t.agent).join("+")}]`
375
- : (step as SequentialStep).agent,
376
- );
377
- const totalSteps = chainSteps.length;
378
-
379
- const firstStep = chainSteps[0]!;
380
- const originalTask = params.task
381
- ?? (isParallelStep(firstStep) ? firstStep.parallel[0]!.task! : (firstStep as SequentialStep).task!);
382
-
383
- const chainDir = createChainDir(runId, chainDirBase);
384
- const hasParallelSteps = chainSteps.some(isParallelStep);
385
- let templates: ResolvedTemplates = resolveChainTemplates(chainSteps);
386
- const shouldClarify = clarify !== false && ctx.hasUI && !hasParallelSteps;
387
- let tuiBehaviorOverrides: (BehaviorOverride | undefined)[] | undefined;
388
- const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map((m) => ({
389
- provider: m.provider,
390
- id: m.id,
391
- fullId: `${m.provider}/${m.id}`,
392
- }));
393
- const availableSkills = discoverAvailableSkills(cwd ?? ctx.cwd);
394
-
395
- if (shouldClarify) {
396
- const seqSteps = chainSteps as SequentialStep[];
397
- const agentConfigs: AgentConfig[] = [];
398
- for (const step of seqSteps) {
399
- const config = agents.find((a) => a.name === step.agent);
400
- if (!config) {
401
- removeChainDir(chainDir);
402
- return {
403
- content: [{ type: "text", text: `Unknown agent: ${step.agent}` }],
404
- isError: true,
405
- details: { mode: "chain" as const, results: [] },
406
- };
407
- }
408
- agentConfigs.push(config);
409
- }
410
-
411
- const stepOverrides: StepOverrides[] = seqSteps.map((step) => ({
412
- output: step.output,
413
- reads: step.reads,
414
- progress: step.progress,
415
- skills: normalizeSkillInput(step.skill),
416
- model: step.model,
417
- }));
418
-
419
- const resolvedBehaviors = agentConfigs.map((config, i) =>
420
- resolveStepBehavior(config, stepOverrides[i]!, chainSkills),
421
- );
422
- const flatTemplates = templates as string[];
423
-
424
- const result = await ctx.ui.custom<ChainClarifyResult>(
425
- (tui, theme, _kb, done) =>
426
- new ChainClarifyComponent(
427
- tui,
428
- theme,
429
- agentConfigs,
430
- flatTemplates,
431
- originalTask,
432
- chainDir,
433
- resolvedBehaviors,
434
- availableModels,
435
- ctx.model?.provider,
436
- availableSkills,
437
- done,
438
- ),
439
- {
440
- overlay: true,
441
- overlayOptions: { anchor: "center", width: 84, maxHeight: "80%" },
442
- },
443
- );
444
-
445
- if (!result || !result.confirmed) {
446
- removeChainDir(chainDir);
447
- return {
448
- content: [{ type: "text", text: "Chain cancelled" }],
449
- details: { mode: "chain", results: [] },
450
- };
451
- }
452
-
453
- if (result.runInBackground) {
454
- removeChainDir(chainDir);
455
- const updatedChain: ChainStep[] = chainSteps.map((step, i) => {
456
- if (isParallelStep(step)) return step;
457
- const override = result.behaviorOverrides[i];
458
- return {
459
- ...step,
460
- task: result.templates[i]!,
461
- ...(override?.model ? { model: override.model } : {}),
462
- ...(override?.output !== undefined ? { output: override.output } : {}),
463
- ...(override?.reads !== undefined ? { reads: override.reads } : {}),
464
- ...(override?.progress !== undefined ? { progress: override.progress } : {}),
465
- ...(override?.skills !== undefined ? { skill: override.skills } : {}),
466
- };
467
- });
468
- return {
469
- content: [{ type: "text", text: "Launching in background..." }],
470
- details: { mode: "chain", results: [] },
471
- requestedAsync: { chain: updatedChain, chainSkills },
472
- };
473
- }
474
-
475
- templates = result.templates;
476
- tuiBehaviorOverrides = result.behaviorOverrides;
477
- }
478
-
479
- const results: SingleResult[] = [];
480
- let prev = "";
481
- let globalTaskIndex = 0;
482
- let progressCreated = false;
483
-
484
- for (let stepIndex = 0; stepIndex < chainSteps.length; stepIndex++) {
485
- const step = chainSteps[stepIndex]!;
486
- const stepTemplates = templates[stepIndex]!;
487
-
488
- if (isParallelStep(step)) {
489
- const parallelTemplates = stepTemplates as string[];
490
- const parallelCwd = resolveChildCwd(cwd ?? ctx.cwd, step.cwd);
491
- let worktreeSetup: WorktreeSetup | undefined;
492
- if (step.worktree) {
493
- const worktreeTaskCwdConflict = findWorktreeTaskCwdConflict(step.parallel, parallelCwd);
494
- if (worktreeTaskCwdConflict) {
495
- return buildChainExecutionErrorResult(
496
- `parallel chain step ${stepIndex + 1}: ${formatWorktreeTaskCwdConflict(worktreeTaskCwdConflict, parallelCwd)}`,
497
- {
498
- results,
499
- includeProgress,
500
- allProgress,
501
- allArtifactPaths,
502
- artifactsDir,
503
- chainAgents,
504
- totalSteps,
505
- currentStepIndex: stepIndex,
506
- },
507
- );
508
- }
509
- try {
510
- worktreeSetup = createWorktrees(parallelCwd, `${runId}-s${stepIndex}`, step.parallel.length, {
511
- agents: step.parallel.map((task) => task.agent),
512
- setupHook: params.worktreeSetupHook
513
- ? { hookPath: params.worktreeSetupHook, timeoutMs: params.worktreeSetupHookTimeoutMs }
514
- : undefined,
515
- });
516
- } catch (error) {
517
- const message = error instanceof Error ? error.message : String(error);
518
- return buildChainExecutionErrorResult(message, {
519
- results,
520
- includeProgress,
521
- allProgress,
522
- allArtifactPaths,
523
- artifactsDir,
524
- chainAgents,
525
- totalSteps,
526
- currentStepIndex: stepIndex,
527
- });
528
- }
529
- }
530
-
531
- try {
532
- const agentNames = step.parallel.map((task) => task.agent);
533
- const parallelBehaviors = resolveParallelBehaviors(step.parallel, agents, stepIndex, chainSkills);
534
- progressCreated = ensureParallelProgressFile(chainDir, progressCreated, parallelBehaviors);
535
- createParallelDirs(chainDir, stepIndex, step.parallel.length, agentNames);
536
-
537
- const parallelResults = await runParallelChainTasks({
538
- step,
539
- parallelTemplates,
540
- parallelBehaviors,
541
- agents,
542
- stepIndex,
543
- availableModels,
544
- chainDir,
545
- prev,
546
- originalTask,
547
- ctx,
548
- intercomEvents,
549
- cwd,
550
- runId,
551
- globalTaskIndex,
552
- sessionDirForIndex,
553
- sessionFileForIndex,
554
- shareEnabled,
555
- artifactConfig,
556
- artifactsDir,
557
- signal,
558
- onUpdate,
559
- results,
560
- allProgress,
561
- chainAgents,
562
- totalSteps,
563
- controlConfig,
564
- onControlEvent,
565
- childIntercomTarget,
566
- foregroundControl,
567
- worktreeSetup,
568
- maxSubagentDepth: params.maxSubagentDepth,
569
- });
570
- globalTaskIndex += step.parallel.length;
571
-
572
- for (const result of parallelResults) {
573
- results.push(result);
574
- if (result.progress) allProgress.push(result.progress);
575
- if (result.artifactPaths) allArtifactPaths.push(result.artifactPaths);
576
- }
577
-
578
- const interrupted = parallelResults.find((result) => result.interrupted);
579
- if (interrupted) {
580
- return {
581
- content: [{ type: "text", text: `Chain paused after interrupt at step ${stepIndex + 1} (${interrupted.agent}). Waiting for explicit next action.` }],
582
- details: buildChainExecutionDetails({
583
- results,
584
- includeProgress,
585
- allProgress,
586
- allArtifactPaths,
587
- artifactsDir,
588
- chainAgents,
589
- totalSteps,
590
- currentStepIndex: stepIndex,
591
- }),
592
- };
593
- }
594
-
595
- const failures = parallelResults
596
- .map((result, originalIndex) => ({ ...result, originalIndex }))
597
- .filter((result) => result.exitCode !== 0 && result.exitCode !== -1);
598
- if (failures.length > 0) {
599
- const failureSummary = failures
600
- .map((failure) => `- Task ${failure.originalIndex + 1} (${failure.agent}): ${failure.error || "failed"}`)
601
- .join("\n");
602
- const errorMsg = `Parallel step ${stepIndex + 1} failed:\n${failureSummary}`;
603
- const summary = buildChainSummary(chainSteps, results, chainDir, "failed", {
604
- index: stepIndex,
605
- error: errorMsg,
606
- });
607
- return {
608
- content: [{ type: "text", text: summary }],
609
- isError: true,
610
- details: buildChainExecutionDetails({
611
- results,
612
- includeProgress,
613
- allProgress,
614
- allArtifactPaths,
615
- artifactsDir,
616
- chainAgents,
617
- totalSteps,
618
- currentStepIndex: stepIndex,
619
- }),
620
- };
621
- }
622
-
623
- const taskResults: ParallelTaskResult[] = parallelResults.map((result, i) => {
624
- const outputTarget = parallelBehaviors[i]?.output;
625
- const outputTargetPath = typeof outputTarget === "string"
626
- ? (path.isAbsolute(outputTarget) ? outputTarget : path.join(chainDir, outputTarget))
627
- : undefined;
628
- return {
629
- agent: result.agent,
630
- taskIndex: i,
631
- output: getSingleResultOutput(result),
632
- exitCode: result.exitCode,
633
- error: result.error,
634
- outputTargetPath,
635
- outputTargetExists: outputTargetPath ? fs.existsSync(outputTargetPath) : undefined,
636
- };
637
- });
638
- prev = aggregateParallelOutputs(taskResults);
639
- prev = appendParallelWorktreeSummary(
640
- prev,
641
- worktreeSetup,
642
- path.join(chainDir, "worktree-diffs", `step-${stepIndex}`),
643
- agentNames,
644
- );
645
- } finally {
646
- if (worktreeSetup) cleanupWorktrees(worktreeSetup);
647
- }
648
- } else {
649
- const seqStep = step as SequentialStep;
650
- const stepTemplate = stepTemplates as string;
651
-
652
- const agentConfig = agents.find((a) => a.name === seqStep.agent);
653
- if (!agentConfig) {
654
- removeChainDir(chainDir);
655
- return {
656
- content: [{ type: "text", text: `Unknown agent: ${seqStep.agent}` }],
657
- isError: true,
658
- details: { mode: "chain" as const, results: [] },
659
- };
660
- }
661
-
662
- const tuiOverride = tuiBehaviorOverrides?.[stepIndex];
663
- const stepOverride: StepOverrides = {
664
- output: tuiOverride?.output !== undefined ? tuiOverride.output : seqStep.output,
665
- reads: tuiOverride?.reads !== undefined ? tuiOverride.reads : seqStep.reads,
666
- progress: tuiOverride?.progress !== undefined ? tuiOverride.progress : seqStep.progress,
667
- skills:
668
- tuiOverride?.skills !== undefined
669
- ? tuiOverride.skills
670
- : normalizeSkillInput(seqStep.skill),
671
- };
672
- const behavior = resolveStepBehavior(agentConfig, stepOverride, chainSkills);
673
-
674
- const isFirstProgress = behavior.progress && !progressCreated;
675
- if (isFirstProgress) {
676
- progressCreated = true;
677
- }
678
-
679
- const templateHasPrevious = stepTemplate.includes("{previous}");
680
- const { prefix, suffix } = buildChainInstructions(
681
- behavior,
682
- chainDir,
683
- isFirstProgress,
684
- templateHasPrevious ? undefined : prev,
685
- );
686
-
687
- let stepTask = stepTemplate;
688
- stepTask = stepTask.replace(/\{task\}/g, originalTask);
689
- stepTask = stepTask.replace(/\{previous\}/g, prev);
690
- stepTask = stepTask.replace(/\{chain_dir\}/g, chainDir);
691
- const cleanTask = stepTask;
692
- stepTask = prefix + stepTask + suffix;
693
-
694
- const effectiveModel =
695
- tuiOverride?.model
696
- ?? (seqStep.model ? resolveModelCandidate(seqStep.model, availableModels, ctx.model?.provider) : null)
697
- ?? resolveModelCandidate(agentConfig.model, availableModels, ctx.model?.provider);
698
-
699
- const outputPath = typeof behavior.output === "string"
700
- ? (path.isAbsolute(behavior.output) ? behavior.output : path.join(chainDir, behavior.output))
701
- : undefined;
702
- const maxSubagentDepth = resolveChildMaxSubagentDepth(params.maxSubagentDepth, agentConfig.maxSubagentDepth);
703
- const interruptController = new AbortController();
704
- if (foregroundControl) {
705
- foregroundControl.currentAgent = seqStep.agent;
706
- foregroundControl.currentIndex = globalTaskIndex;
707
- foregroundControl.currentActivityState = undefined;
708
- foregroundControl.updatedAt = Date.now();
709
- foregroundControl.interrupt = () => {
710
- if (interruptController.signal.aborted) return false;
711
- interruptController.abort();
712
- foregroundControl.currentActivityState = undefined;
713
- foregroundControl.updatedAt = Date.now();
714
- return true;
715
- };
716
- }
717
-
718
- const r = await runSync(ctx.cwd, agents, seqStep.agent, stepTask, {
719
- cwd: resolveChildCwd(cwd ?? ctx.cwd, seqStep.cwd),
720
- signal,
721
- interruptSignal: interruptController.signal,
722
- allowIntercomDetach: agentConfig.systemPrompt?.includes(INTERCOM_BRIDGE_MARKER) === true,
723
- intercomEvents,
724
- runId,
725
- index: globalTaskIndex,
726
- sessionDir: sessionDirForIndex(globalTaskIndex),
727
- sessionFile: sessionFileForIndex?.(globalTaskIndex),
728
- share: shareEnabled,
729
- artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
730
- artifactConfig,
731
- outputPath,
732
- maxSubagentDepth,
733
- controlConfig,
734
- onControlEvent,
735
- intercomSessionName: childIntercomTarget?.(seqStep.agent, globalTaskIndex),
736
- modelOverride: effectiveModel,
737
- availableModels,
738
- preferredModelProvider: ctx.model?.provider,
739
- skills: behavior.skills === false ? [] : behavior.skills,
740
- onUpdate: onUpdate
741
- ? (p) => {
742
- const stepResults = p.details?.results || [];
743
- const stepProgress = p.details?.progress || [];
744
- if (foregroundControl && stepProgress.length > 0) {
745
- const current = stepProgress[0];
746
- foregroundControl.currentAgent = seqStep.agent;
747
- foregroundControl.currentIndex = globalTaskIndex;
748
- foregroundControl.currentActivityState = current?.activityState;
749
- foregroundControl.lastActivityAt = current?.lastActivityAt;
750
- foregroundControl.currentTool = current?.currentTool;
751
- foregroundControl.currentToolStartedAt = current?.currentToolStartedAt;
752
- foregroundControl.updatedAt = Date.now();
753
- }
754
- onUpdate({
755
- ...p,
756
- details: {
757
- mode: "chain",
758
- results: results.concat(stepResults),
759
- progress: allProgress.concat(stepProgress),
760
- controlEvents: p.details?.controlEvents,
761
- chainAgents,
762
- totalSteps,
763
- currentStepIndex: stepIndex,
764
- },
765
- });
766
- }
767
- : undefined,
768
- });
769
- if (foregroundControl?.currentIndex === globalTaskIndex) {
770
- foregroundControl.interrupt = undefined;
771
- foregroundControl.updatedAt = Date.now();
772
- }
773
- recordRun(seqStep.agent, cleanTask, r.exitCode, r.progressSummary?.durationMs ?? 0);
774
-
775
- globalTaskIndex++;
776
- results.push(r);
777
- if (r.progress) allProgress.push(r.progress);
778
- if (r.artifactPaths) allArtifactPaths.push(r.artifactPaths);
779
-
780
- if (behavior.output && r.exitCode === 0) {
781
- try {
782
- const expectedPath = path.isAbsolute(behavior.output)
783
- ? behavior.output
784
- : path.join(chainDir, behavior.output);
785
- if (!fs.existsSync(expectedPath)) {
786
- const dirFiles = fs.readdirSync(chainDir);
787
- const mdFiles = dirFiles.filter((file) => file.endsWith(".md") && file !== "progress.md");
788
- const warning = mdFiles.length > 0
789
- ? `Agent wrote to different file(s): ${mdFiles.join(", ")} instead of ${behavior.output}`
790
- : `Agent did not create expected output file: ${behavior.output}`;
791
- r.error = r.error ? `${r.error}\n${warning}` : warning;
792
- }
793
- } catch {
794
- // Ignore validation errors - this is just a diagnostic
795
- }
796
- }
797
-
798
- if (r.interrupted) {
799
- return {
800
- content: [{ type: "text", text: `Chain paused after interrupt at step ${stepIndex + 1} (${r.agent}). Waiting for explicit next action.` }],
801
- details: buildChainExecutionDetails({
802
- results,
803
- includeProgress,
804
- allProgress,
805
- allArtifactPaths,
806
- artifactsDir,
807
- chainAgents,
808
- totalSteps,
809
- currentStepIndex: stepIndex,
810
- }),
811
- };
812
- }
813
-
814
- if (r.exitCode !== 0) {
815
- const summary = buildChainSummary(chainSteps, results, chainDir, "failed", {
816
- index: stepIndex,
817
- error: r.error || "Chain failed",
818
- });
819
- return {
820
- content: [{ type: "text", text: summary }],
821
- details: buildChainExecutionDetails({
822
- results,
823
- includeProgress,
824
- allProgress,
825
- allArtifactPaths,
826
- artifactsDir,
827
- chainAgents,
828
- totalSteps,
829
- currentStepIndex: stepIndex,
830
- }),
831
- isError: true,
832
- };
833
- }
834
-
835
- prev = getSingleResultOutput(r);
836
- }
837
- }
838
-
839
- const summary = buildChainSummary(chainSteps, results, chainDir, "completed");
840
-
841
- return {
842
- content: [{ type: "text", text: summary }],
843
- details: buildChainExecutionDetails({
844
- results,
845
- includeProgress,
846
- allProgress,
847
- allArtifactPaths,
848
- artifactsDir,
849
- chainAgents,
850
- totalSteps,
851
- }),
852
- };
853
- }