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