@bastani/atomic 0.8.24-alpha.2 → 0.8.24-alpha.3
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 +6 -0
- package/README.md +2 -1
- package/dist/builtin/intercom/CHANGELOG.md +6 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/CHANGELOG.md +6 -0
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +10 -0
- package/dist/builtin/subagents/README.md +132 -21
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/prompts/parallel-context-build.md +4 -2
- package/dist/builtin/subagents/prompts/parallel-handoff-plan.md +3 -1
- package/dist/builtin/subagents/skills/subagent/SKILL.md +49 -11
- package/dist/builtin/subagents/src/agents/agent-management.ts +79 -16
- package/dist/builtin/subagents/src/agents/agents.ts +47 -16
- package/dist/builtin/subagents/src/agents/chain-serializer.ts +114 -0
- package/dist/builtin/subagents/src/extension/schemas.ts +139 -3
- package/dist/builtin/subagents/src/runs/background/async-execution.ts +92 -6
- package/dist/builtin/subagents/src/runs/background/async-status.ts +11 -1
- package/dist/builtin/subagents/src/runs/background/run-status.ts +4 -1
- package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +529 -32
- package/dist/builtin/subagents/src/runs/foreground/chain-execution.ts +361 -118
- package/dist/builtin/subagents/src/runs/foreground/execution.ts +75 -7
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +33 -0
- package/dist/builtin/subagents/src/runs/shared/acceptance.ts +611 -0
- package/dist/builtin/subagents/src/runs/shared/chain-outputs.ts +101 -0
- package/dist/builtin/subagents/src/runs/shared/dynamic-fanout.ts +293 -0
- package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +29 -1
- package/dist/builtin/subagents/src/runs/shared/pi-args.ts +11 -0
- package/dist/builtin/subagents/src/runs/shared/structured-output.ts +79 -0
- package/dist/builtin/subagents/src/runs/shared/subagent-prompt-runtime.ts +52 -2
- package/dist/builtin/subagents/src/runs/shared/workflow-graph.ts +206 -0
- package/dist/builtin/subagents/src/shared/formatters.ts +2 -2
- package/dist/builtin/subagents/src/shared/settings.ts +53 -4
- package/dist/builtin/subagents/src/shared/types.ts +226 -0
- package/dist/builtin/subagents/src/shared/utils.ts +2 -1
- package/dist/builtin/subagents/src/slash/slash-commands.ts +41 -3
- package/dist/builtin/subagents/src/tui/render.ts +152 -34
- package/dist/builtin/web-access/CHANGELOG.md +6 -0
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +6 -0
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/skills/create-spec/SKILL.md +1 -1
- package/dist/builtin/workflows/src/tui/stage-chat-view.ts +0 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +4 -3
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +1 -1
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/docs/usage.md +1 -0
- package/docs/workflows.md +173 -0
- package/package.json +1 -1
|
@@ -11,7 +11,7 @@ import { APP_NAME, getEnvValue, type ExtensionAPI } from "@bastani/atomic";
|
|
|
11
11
|
import type { AgentConfig } from "../../agents/agents.ts";
|
|
12
12
|
import { applyThinkingSuffix, SUBAGENT_INTERCOM_SESSION_NAME_ENV } from "../shared/pi-args.ts";
|
|
13
13
|
import { injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
14
|
-
import { buildChainInstructions, isParallelStep, resolveStepBehavior, suppressProgressForReadOnlyTask, writeInitialProgressFile, type ChainStep, type ResolvedStepBehavior, type SequentialStep, type StepOverrides } from "../../shared/settings.ts";
|
|
14
|
+
import { buildChainInstructions, isDynamicParallelStep, isParallelStep, resolveStepBehavior, suppressProgressForReadOnlyTask, writeInitialProgressFile, type ChainStep, type ResolvedStepBehavior, type SequentialStep, type StepOverrides } from "../../shared/settings.ts";
|
|
15
15
|
import type { RunnerStep } from "../shared/parallel-utils.ts";
|
|
16
16
|
import { resolvePiPackageRoot } from "../shared/pi-spawn.ts";
|
|
17
17
|
import { buildSkillInjection, normalizeSkillInput, resolveSkillsWithFallback } from "../../agents/skills.ts";
|
|
@@ -24,7 +24,12 @@ import {
|
|
|
24
24
|
resolveSubagentModelFastModeMetadata,
|
|
25
25
|
} from "../../shared/fast-mode.ts";
|
|
26
26
|
import { resolveExpectedWorktreeAgentCwd } from "../shared/worktree.ts";
|
|
27
|
+
import { buildWorkflowGraphSnapshot } from "../shared/workflow-graph.ts";
|
|
28
|
+
import { ChainOutputValidationError, validateChainOutputBindings } from "../shared/chain-outputs.ts";
|
|
29
|
+
import { createStructuredOutputRuntime } from "../shared/structured-output.ts";
|
|
30
|
+
import { resolveEffectiveAcceptance } from "../shared/acceptance.ts";
|
|
27
31
|
import {
|
|
32
|
+
type AcceptanceInput,
|
|
28
33
|
type ArtifactConfig,
|
|
29
34
|
type Details,
|
|
30
35
|
type MaxOutputConfig,
|
|
@@ -112,6 +117,7 @@ interface AsyncChainParams {
|
|
|
112
117
|
sessionRoot?: string;
|
|
113
118
|
chainSkills?: string[];
|
|
114
119
|
sessionFilesByFlatIndex?: (string | undefined)[];
|
|
120
|
+
dynamicFanoutMaxItems?: number;
|
|
115
121
|
maxSubagentDepth: number;
|
|
116
122
|
workflowStageSubagentGuard?: boolean;
|
|
117
123
|
worktreeSetupHook?: string;
|
|
@@ -120,6 +126,7 @@ interface AsyncChainParams {
|
|
|
120
126
|
controlIntercomTarget?: string;
|
|
121
127
|
childIntercomTarget?: (agent: string, index: number) => string | undefined;
|
|
122
128
|
nestedRoute?: NestedRouteInfo;
|
|
129
|
+
acceptance?: AcceptanceInput;
|
|
123
130
|
}
|
|
124
131
|
|
|
125
132
|
interface AsyncSingleParams {
|
|
@@ -147,6 +154,7 @@ interface AsyncSingleParams {
|
|
|
147
154
|
controlIntercomTarget?: string;
|
|
148
155
|
childIntercomTarget?: (agent: string, index: number) => string | undefined;
|
|
149
156
|
nestedRoute?: NestedRouteInfo;
|
|
157
|
+
acceptance?: AcceptanceInput;
|
|
150
158
|
}
|
|
151
159
|
|
|
152
160
|
interface AsyncExecutionResult {
|
|
@@ -262,12 +270,25 @@ export function executeAsyncChain(
|
|
|
262
270
|
const runnerCwd = resolveChildCwd(ctx.cwd, cwd);
|
|
263
271
|
const firstStep = chain[0];
|
|
264
272
|
const originalTask = params.task ?? (firstStep
|
|
265
|
-
? (isParallelStep(firstStep)
|
|
273
|
+
? (isParallelStep(firstStep)
|
|
274
|
+
? firstStep.parallel[0]?.task
|
|
275
|
+
: isDynamicParallelStep(firstStep)
|
|
276
|
+
? firstStep.parallel.task
|
|
277
|
+
: (firstStep as SequentialStep).task)
|
|
266
278
|
: undefined);
|
|
279
|
+
try {
|
|
280
|
+
validateChainOutputBindings(chain, { maxItems: params.dynamicFanoutMaxItems });
|
|
281
|
+
} catch (error) {
|
|
282
|
+
if (error instanceof ChainOutputValidationError) return formatAsyncStartError(resultMode, error.message);
|
|
283
|
+
throw error;
|
|
284
|
+
}
|
|
285
|
+
const workflowGraph = buildWorkflowGraphSnapshot({ runId: id, mode: resultMode, steps: chain });
|
|
267
286
|
|
|
268
287
|
for (const s of chain) {
|
|
269
288
|
const stepAgents = isParallelStep(s)
|
|
270
289
|
? s.parallel.map((t) => t.agent)
|
|
290
|
+
: isDynamicParallelStep(s)
|
|
291
|
+
? [s.parallel.agent]
|
|
271
292
|
: [(s as SequentialStep).agent];
|
|
272
293
|
for (const agentName of stepAgents) {
|
|
273
294
|
if (!agents.find((x) => x.name === agentName)) {
|
|
@@ -330,7 +351,10 @@ export function executeAsyncChain(
|
|
|
330
351
|
const outputPath = resolveSingleOutputPath(behavior.output, ctx.cwd, instructionCwd);
|
|
331
352
|
const validationError = validateFileOnlyOutputMode(behavior.outputMode, outputPath, `Async step (${s.agent})`);
|
|
332
353
|
if (validationError) throw new AsyncStartValidationError(validationError);
|
|
333
|
-
|
|
354
|
+
let taskTemplate = s.task ?? "{previous}";
|
|
355
|
+
taskTemplate = taskTemplate.replace(/\{task\}/g, originalTask ?? "");
|
|
356
|
+
taskTemplate = taskTemplate.replace(/\{chain_dir\}/g, runnerCwd);
|
|
357
|
+
const task = injectSingleOutputInstruction(`${readInstructions.prefix}${taskTemplate}${progressInstructions.suffix}`, outputPath);
|
|
334
358
|
|
|
335
359
|
const primaryModel = resolveModelCandidate(behavior.model ?? a.model, availableModels, ctx.currentModelProvider);
|
|
336
360
|
const model = applyThinkingSuffix(primaryModel, a.thinking);
|
|
@@ -341,6 +365,10 @@ export function executeAsyncChain(
|
|
|
341
365
|
return {
|
|
342
366
|
agent: s.agent,
|
|
343
367
|
task,
|
|
368
|
+
phase: s.phase,
|
|
369
|
+
label: s.label,
|
|
370
|
+
outputName: s.as,
|
|
371
|
+
structured: Boolean(s.outputSchema),
|
|
344
372
|
cwd: stepCwd,
|
|
345
373
|
model,
|
|
346
374
|
thinking: resolveEffectiveThinking(model, a.thinking),
|
|
@@ -362,6 +390,16 @@ export function executeAsyncChain(
|
|
|
362
390
|
sessionFile,
|
|
363
391
|
maxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, a.maxSubagentDepth),
|
|
364
392
|
workflowStageSubagentGuard,
|
|
393
|
+
effectiveAcceptance: resolveEffectiveAcceptance({
|
|
394
|
+
explicit: s.acceptance,
|
|
395
|
+
agentName: s.agent,
|
|
396
|
+
task: s.task,
|
|
397
|
+
mode: resultMode,
|
|
398
|
+
async: true,
|
|
399
|
+
dynamic: false,
|
|
400
|
+
}),
|
|
401
|
+
...(s.outputSchema ? { structuredOutputSchema: s.outputSchema } : {}),
|
|
402
|
+
...(s.outputSchema ? { structuredOutput: createStructuredOutputRuntime(s.outputSchema, path.join(asyncDir, "structured-output")) } : {}),
|
|
365
403
|
};
|
|
366
404
|
};
|
|
367
405
|
|
|
@@ -402,6 +440,32 @@ export function executeAsyncChain(
|
|
|
402
440
|
worktree: s.worktree,
|
|
403
441
|
};
|
|
404
442
|
}
|
|
443
|
+
if (isDynamicParallelStep(s)) {
|
|
444
|
+
const agent = agents.find((candidate) => candidate.name === s.parallel.agent)!;
|
|
445
|
+
const behavior = suppressProgressForReadOnlyTask(resolveStepBehavior(agent, buildStepOverrides(s.parallel), chainSkills), s.parallel.task, originalTask);
|
|
446
|
+
const progressPrecreated = behavior.progress;
|
|
447
|
+
if (progressPrecreated) {
|
|
448
|
+
writeInitialProgressFile(runnerCwd);
|
|
449
|
+
progressInstructionCreated = true;
|
|
450
|
+
}
|
|
451
|
+
return {
|
|
452
|
+
expand: s.expand,
|
|
453
|
+
parallel: buildSeqStep(s.parallel as SequentialStep, undefined, undefined, progressPrecreated, behavior),
|
|
454
|
+
collect: s.collect,
|
|
455
|
+
concurrency: s.concurrency,
|
|
456
|
+
failFast: s.failFast,
|
|
457
|
+
phase: s.phase,
|
|
458
|
+
label: s.label,
|
|
459
|
+
effectiveAcceptance: resolveEffectiveAcceptance({
|
|
460
|
+
explicit: s.acceptance,
|
|
461
|
+
agentName: s.parallel.agent,
|
|
462
|
+
task: s.parallel.task,
|
|
463
|
+
mode: resultMode,
|
|
464
|
+
async: true,
|
|
465
|
+
dynamicGroup: true,
|
|
466
|
+
}),
|
|
467
|
+
};
|
|
468
|
+
}
|
|
405
469
|
return buildSeqStep(s as SequentialStep, nextSessionFile());
|
|
406
470
|
});
|
|
407
471
|
} catch (error) {
|
|
@@ -411,6 +475,10 @@ export function executeAsyncChain(
|
|
|
411
475
|
let childTargetIndex = 0;
|
|
412
476
|
const childIntercomTargets = childIntercomTarget ? steps.flatMap((step) => {
|
|
413
477
|
if ("parallel" in step) {
|
|
478
|
+
if (!Array.isArray(step.parallel)) {
|
|
479
|
+
childTargetIndex++;
|
|
480
|
+
return [undefined];
|
|
481
|
+
}
|
|
414
482
|
return step.parallel.map((task) => childIntercomTarget(task.agent, childTargetIndex++));
|
|
415
483
|
}
|
|
416
484
|
return [childIntercomTarget(step.agent, childTargetIndex++)];
|
|
@@ -440,6 +508,8 @@ export function executeAsyncChain(
|
|
|
440
508
|
controlIntercomTarget,
|
|
441
509
|
childIntercomTargets,
|
|
442
510
|
resultMode,
|
|
511
|
+
dynamicFanoutMaxItems: params.dynamicFanoutMaxItems,
|
|
512
|
+
workflowGraph,
|
|
443
513
|
nestedRoute: nestedRoute ?? inheritedNestedRoute,
|
|
444
514
|
workflowStageSubagentGuard,
|
|
445
515
|
nestedSelf: inheritedNestedRoute && nestedAddress ? {
|
|
@@ -465,6 +535,8 @@ export function executeAsyncChain(
|
|
|
465
535
|
const firstStep = chain[0];
|
|
466
536
|
const firstAgents = isParallelStep(firstStep)
|
|
467
537
|
? firstStep.parallel.map((t) => t.agent)
|
|
538
|
+
: isDynamicParallelStep(firstStep)
|
|
539
|
+
? [firstStep.parallel.agent]
|
|
468
540
|
: [(firstStep as SequentialStep).agent];
|
|
469
541
|
const parallelGroups: Array<{ start: number; count: number; stepIndex: number }> = [];
|
|
470
542
|
const flatAgents: string[] = [];
|
|
@@ -475,6 +547,10 @@ export function executeAsyncChain(
|
|
|
475
547
|
parallelGroups.push({ start: flatStepStart, count: step.parallel.length, stepIndex });
|
|
476
548
|
flatAgents.push(...step.parallel.map((task) => task.agent));
|
|
477
549
|
flatStepStart += step.parallel.length;
|
|
550
|
+
} else if (isDynamicParallelStep(step)) {
|
|
551
|
+
parallelGroups.push({ start: flatStepStart, count: 1, stepIndex });
|
|
552
|
+
flatAgents.push(step.parallel.agent);
|
|
553
|
+
flatStepStart++;
|
|
478
554
|
} else {
|
|
479
555
|
flatAgents.push((step as SequentialStep).agent);
|
|
480
556
|
flatStepStart++;
|
|
@@ -523,12 +599,15 @@ export function executeAsyncChain(
|
|
|
523
599
|
agents: flatAgents,
|
|
524
600
|
task: isParallelStep(firstStep)
|
|
525
601
|
? firstStep.parallel[0]?.task?.slice(0, 50)
|
|
602
|
+
: isDynamicParallelStep(firstStep)
|
|
603
|
+
? firstStep.parallel.task?.slice(0, 50)
|
|
526
604
|
: (firstStep as SequentialStep).task?.slice(0, 50),
|
|
527
605
|
chain: chain.map((s) =>
|
|
528
|
-
isParallelStep(s) ? `[${s.parallel.map((t) => t.agent).join("+")}]` : (s as SequentialStep).agent,
|
|
606
|
+
isParallelStep(s) ? `[${s.parallel.map((t) => t.agent).join("+")}]` : isDynamicParallelStep(s) ? `expand:${s.parallel.agent}` : (s as SequentialStep).agent,
|
|
529
607
|
),
|
|
530
608
|
chainStepCount: chain.length,
|
|
531
609
|
parallelGroups,
|
|
610
|
+
workflowGraph,
|
|
532
611
|
cwd: runnerCwd,
|
|
533
612
|
asyncDir,
|
|
534
613
|
nestedRoute,
|
|
@@ -537,13 +616,13 @@ export function executeAsyncChain(
|
|
|
537
616
|
|
|
538
617
|
const chainDesc = chain
|
|
539
618
|
.map((s) =>
|
|
540
|
-
isParallelStep(s) ? `[${s.parallel.map((t) => t.agent).join("+")}]` : (s as SequentialStep).agent,
|
|
619
|
+
isParallelStep(s) ? `[${s.parallel.map((t) => t.agent).join("+")}]` : isDynamicParallelStep(s) ? `expand:${s.parallel.agent}` : (s as SequentialStep).agent,
|
|
541
620
|
)
|
|
542
621
|
.join(" -> ");
|
|
543
622
|
|
|
544
623
|
return {
|
|
545
624
|
content: [{ type: "text", text: formatAsyncStartedMessage(`Async ${resultMode}: ${chainDesc} [${id}]`) }],
|
|
546
|
-
details: { mode: resultMode, runId: id, results: [], asyncId: id, asyncDir },
|
|
625
|
+
details: { mode: resultMode, runId: id, results: [], asyncId: id, asyncDir, workflowGraph },
|
|
547
626
|
};
|
|
548
627
|
}
|
|
549
628
|
|
|
@@ -647,6 +726,13 @@ export function executeAsyncSingle(
|
|
|
647
726
|
sessionFile,
|
|
648
727
|
maxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, agentConfig.maxSubagentDepth),
|
|
649
728
|
workflowStageSubagentGuard,
|
|
729
|
+
effectiveAcceptance: resolveEffectiveAcceptance({
|
|
730
|
+
explicit: params.acceptance,
|
|
731
|
+
agentName: agent,
|
|
732
|
+
task,
|
|
733
|
+
mode: "single",
|
|
734
|
+
async: true,
|
|
735
|
+
}),
|
|
650
736
|
},
|
|
651
737
|
],
|
|
652
738
|
resultPath: inheritedNestedRoute ? nestedResultsPath(inheritedNestedRoute.rootRunId, id) : path.join(RESULTS_DIR, `${id}.json`),
|
|
@@ -12,6 +12,10 @@ import { reconcileAsyncRun, reconcileNestedAsyncDescendants } from "./stale-run-
|
|
|
12
12
|
interface AsyncRunStepSummary {
|
|
13
13
|
index: number;
|
|
14
14
|
agent: string;
|
|
15
|
+
label?: string;
|
|
16
|
+
phase?: string;
|
|
17
|
+
outputName?: string;
|
|
18
|
+
structured?: boolean;
|
|
15
19
|
status: AsyncJobStep["status"];
|
|
16
20
|
activityState?: ActivityState;
|
|
17
21
|
lastActivityAt?: number;
|
|
@@ -140,6 +144,10 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
|
|
|
140
144
|
return {
|
|
141
145
|
index,
|
|
142
146
|
agent: step.agent,
|
|
147
|
+
...(step.label ? { label: step.label } : {}),
|
|
148
|
+
...(step.phase ? { phase: step.phase } : {}),
|
|
149
|
+
...(step.outputName ? { outputName: step.outputName } : {}),
|
|
150
|
+
...(step.structured ? { structured: step.structured } : {}),
|
|
143
151
|
status: step.status,
|
|
144
152
|
...(stepActivityState ? { activityState: stepActivityState } : {}),
|
|
145
153
|
...(stepLastActivityAt ? { lastActivityAt: stepLastActivityAt } : {}),
|
|
@@ -261,7 +269,9 @@ function formatActivityFacts(input: { activityState?: ActivityState; lastActivit
|
|
|
261
269
|
}
|
|
262
270
|
|
|
263
271
|
function formatStepLine(step: AsyncRunStepSummary): string {
|
|
264
|
-
const
|
|
272
|
+
const display = step.label ? `${step.label} (${step.agent})` : step.agent;
|
|
273
|
+
const phase = step.phase ? `[${step.phase}] ` : "";
|
|
274
|
+
const parts = [`${step.index + 1}. ${phase}${display}`, step.status];
|
|
265
275
|
const activity = formatActivityFacts(step);
|
|
266
276
|
if (activity) parts.push(activity);
|
|
267
277
|
const modelThinking = formatModelThinking(step.model, step.thinking, step.fastMode);
|
|
@@ -216,7 +216,10 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
|
|
|
216
216
|
const modelThinking = formatModelThinking(step.model, step.thinking, step.fastMode);
|
|
217
217
|
const modelText = modelThinking ? ` (${modelThinking})` : "";
|
|
218
218
|
const errorText = step.error ? `, error: ${step.error}` : "";
|
|
219
|
-
|
|
219
|
+
const acceptanceText = step.acceptance?.status ? `, acceptance: ${step.acceptance.status}` : "";
|
|
220
|
+
const display = step.label ? `${step.label} (${step.agent})` : step.agent;
|
|
221
|
+
const phase = step.phase ? `[${step.phase}] ` : "";
|
|
222
|
+
lines.push(`${stepLineLabel(status, index)}: ${phase}${display} ${step.status}${modelText}${stepActivityText ? `, ${stepActivityText}` : ""}${acceptanceText}${errorText}`);
|
|
220
223
|
lines.push(...formatNestedRunStatusLines(step.children, { indent: " ", commandHints: true, maxLines: 20 }));
|
|
221
224
|
const stepOutputPath = path.join(asyncDir, `output-${index}.log`);
|
|
222
225
|
if (stepOutputPath !== outputPath && fs.existsSync(stepOutputPath)) lines.push(` Output: ${stepOutputPath}`);
|