@bastani/atomic 0.9.15 → 0.9.16-alpha.2
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 +19 -0
- package/dist/builtin/intercom/CHANGELOG.md +10 -0
- package/dist/builtin/intercom/README.md +8 -8
- package/dist/builtin/intercom/contact-supervisor-tool.ts +68 -32
- package/dist/builtin/intercom/index-heavy.ts +1 -0
- package/dist/builtin/intercom/index.ts +21 -15
- package/dist/builtin/intercom/intercom-tool.ts +66 -10
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/intercom/parent-ask-handoff.ts +72 -0
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +26 -0
- package/dist/builtin/subagents/README.md +37 -114
- package/dist/builtin/subagents/agents/worker.md +2 -2
- package/dist/builtin/subagents/package.json +1 -5
- package/dist/builtin/subagents/skills/subagent/SKILL.md +26 -103
- package/dist/builtin/subagents/src/extension/index.ts +5 -30
- package/dist/builtin/subagents/src/extension/notification-content.ts +1 -1
- package/dist/builtin/subagents/src/extension/schemas.ts +2 -14
- package/dist/builtin/subagents/src/extension/tool-description.ts +1 -4
- package/dist/builtin/subagents/src/extension/tool-rendering.ts +53 -0
- package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +1 -54
- package/dist/builtin/subagents/src/intercom/result-intercom.ts +4 -4
- package/dist/builtin/subagents/src/runs/foreground/execution-parent-ask-handoff.ts +78 -0
- package/dist/builtin/subagents/src/runs/foreground/inprocess-run-sync.ts +7 -5
- package/dist/builtin/subagents/src/runs/foreground/notify.ts +5 -6
- package/dist/builtin/subagents/src/runs/foreground/parent-ask-output.ts +48 -0
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-burst-display.ts +46 -0
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-burst.ts +403 -0
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-context.ts +48 -9
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-input.ts +3 -1
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-live-update.ts +11 -0
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +164 -93
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel.ts +36 -29
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parent-ask-projection.ts +11 -0
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +42 -17
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-status.ts +3 -121
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-types.ts +7 -2
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +30 -257
- package/dist/builtin/subagents/src/runs/inprocess/control-status.ts +0 -34
- package/dist/builtin/subagents/src/runs/inprocess/index.ts +0 -12
- package/dist/builtin/subagents/src/runs/inprocess/prompt-behavior.ts +1 -1
- package/dist/builtin/subagents/src/runs/inprocess/runner.ts +2 -141
- package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +8 -0
- package/dist/builtin/subagents/src/shared/status-format.ts +3 -3
- package/dist/builtin/subagents/src/shared/types-config.ts +41 -0
- package/dist/builtin/subagents/src/shared/types-foreground-state.ts +7 -15
- package/dist/builtin/subagents/src/shared/types-results.ts +3 -1
- package/dist/builtin/subagents/src/shared/types-runtime.ts +1 -11
- package/dist/builtin/subagents/src/slash/slash-commands.ts +4 -468
- package/dist/builtin/subagents/src/tui/render-progress.ts +2 -2
- package/dist/builtin/subagents/src/tui/render-result-compact.ts +2 -2
- package/dist/builtin/subagents/src/tui/render-result.ts +38 -14
- package/dist/builtin/subagents/src/tui/render-status-progress.ts +1 -1
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +16 -0
- package/dist/builtin/workflows/README.md +1 -1
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/skills/bro/LICENSE.txt +21 -0
- package/dist/builtin/workflows/skills/bro/SKILL.md +14 -0
- package/dist/builtin/workflows/skills/how/LICENSE.txt +21 -0
- package/dist/builtin/workflows/skills/how/SKILL.md +162 -0
- package/dist/builtin/workflows/skills/how/references/critic-prompt.md +59 -0
- package/dist/builtin/workflows/skills/how/references/critique-rubric.md +58 -0
- package/dist/builtin/workflows/skills/how/references/explainer-prompt.md +55 -0
- package/dist/builtin/workflows/skills/how/references/explorer-prompt.md +52 -0
- package/dist/builtin/workflows/skills/teach/LICENSE.txt +21 -0
- package/dist/builtin/workflows/skills/teach/SKILL.md +28 -0
- package/dist/builtin/workflows/skills/unslop/LICENSE.txt +21 -0
- package/dist/builtin/workflows/skills/unslop/SKILL.md +87 -0
- package/dist/builtin/workflows/skills/why/LICENSE.txt +21 -0
- package/dist/builtin/workflows/skills/why/SKILL.md +267 -0
- package/dist/builtin/workflows/skills/why/references/epistemics.md +144 -0
- package/dist/builtin/workflows/skills/why/references/investigator-prompt.md +103 -0
- package/dist/builtin/workflows/skills/why/references/source-playbook.md +17 -0
- package/dist/builtin/workflows/skills/why/references/sources/code-archaeology.md +88 -0
- package/dist/builtin/workflows/skills/why/references/sources/databricks.md +70 -0
- package/dist/builtin/workflows/skills/why/references/sources/datadog.md +99 -0
- package/dist/builtin/workflows/skills/why/references/sources/incident-postmortem.md +15 -0
- package/dist/builtin/workflows/skills/why/references/sources/linear.md +48 -0
- package/dist/builtin/workflows/skills/why/references/sources/notion.md +55 -0
- package/dist/builtin/workflows/skills/why/references/sources/sentry.md +100 -0
- package/dist/builtin/workflows/skills/why/references/sources/slack.md +54 -0
- package/dist/builtin/workflows/skills/why/references/synthesizer-prompt.md +135 -0
- package/dist/builtin/workflows/src/durable/dbos-embedded-postgres-root.ts +548 -39
- package/dist/builtin/workflows/src/durable/dbos-embedded-postgres.ts +529 -65
- package/dist/builtin/workflows/src/durable/dbos-lifecycle.ts +30 -8
- package/dist/builtin/workflows/src/durable/dbos-local-postgres.ts +36 -3
- package/dist/builtin/workflows/src/durable/local-command.ts +68 -10
- package/dist/builtin/workflows/src/extension/companions.ts +1 -1
- package/dist/builtin/workflows/src/extension/dispatcher.ts +7 -1
- package/dist/builtin/workflows/src/extension/extension-factory.ts +2 -2
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +1094 -298
- package/dist/builtin/workflows/src/extension/public-types.ts +2 -2
- package/dist/builtin/workflows/src/extension/render-result.ts +16 -2
- package/dist/builtin/workflows/src/extension/runtime.ts +8 -1
- package/dist/builtin/workflows/src/extension/workflow-request-abort.ts +15 -0
- package/dist/builtin/workflows/src/extension/workflow-tool-content.ts +4 -3
- package/dist/builtin/workflows/src/extension/workflow-tool-registration.ts +107 -13
- package/dist/builtin/workflows/src/extension/workflow-tool.ts +22 -12
- package/dist/builtin/workflows/src/tui/widget.ts +25 -6
- package/dist/core/agent-session-auto-compaction.js +10 -10
- package/dist/core/agent-session-auto-compaction.js.map +1 -1
- package/dist/core/agent-session-events.d.ts.map +1 -1
- package/dist/core/agent-session-events.js +11 -13
- package/dist/core/agent-session-events.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +1 -1
- package/dist/core/agent-session-methods.d.ts.map +1 -1
- package/dist/core/agent-session-methods.js.map +1 -1
- package/dist/core/agent-session-models.d.ts.map +1 -1
- package/dist/core/agent-session-models.js +5 -7
- package/dist/core/agent-session-models.js.map +1 -1
- package/dist/core/agent-session-prompt.js +4 -4
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session-retry.d.ts +4 -4
- package/dist/core/agent-session-retry.d.ts.map +1 -1
- package/dist/core/agent-session-retry.js +8 -34
- package/dist/core/agent-session-retry.js.map +1 -1
- package/dist/core/agent-session-tool-hooks.js +4 -4
- package/dist/core/agent-session-tool-hooks.js.map +1 -1
- package/dist/core/atomic-guide-command.d.ts.map +1 -1
- package/dist/core/atomic-guide-command.js +3 -10
- package/dist/core/atomic-guide-command.js.map +1 -1
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js +19 -3
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +0 -3
- 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 +0 -12
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/modes/interactive-engine/remote-command-catalog.d.ts +1 -1
- package/dist/modes/interactive-engine/remote-command-catalog.js +1 -1
- package/dist/modes/interactive-engine/remote-command-catalog.js.map +1 -1
- package/docs/intercom.md +29 -27
- package/docs/settings.md +2 -2
- package/docs/subagents.md +18 -15
- package/docs/usage.md +0 -9
- package/docs/workflows.md +27 -10
- package/npm-shrinkwrap.json +32 -32
- package/package.json +3 -3
- package/dist/builtin/subagents/prompts/gather-context-and-clarify.md +0 -27
- package/dist/builtin/subagents/prompts/parallel-cleanup.md +0 -61
- package/dist/builtin/subagents/prompts/parallel-context-build.md +0 -43
- package/dist/builtin/subagents/prompts/parallel-handoff-plan.md +0 -47
- package/dist/builtin/subagents/prompts/parallel-research.md +0 -34
- package/dist/builtin/subagents/prompts/parallel-review.md +0 -47
- package/dist/builtin/subagents/prompts/review-loop.md +0 -39
- package/dist/builtin/subagents/src/extension/doctor.ts +0 -188
- package/dist/builtin/subagents/src/runs/inprocess/attempt-handles.ts +0 -104
- /package/dist/builtin/subagents/src/runs/foreground/{subagent-executor-resume.ts → subagent-executor-cwd.ts} +0 -0
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
2
|
+
import type { ExtensionContext } from "@bastani/atomic";
|
|
3
|
+
import type { SingleResult, SubagentToolResult } from "../../shared/types.js";
|
|
4
|
+
import { getSingleResultOutput } from "../../shared/utils.js";
|
|
5
|
+
import { formatParallelResultContent } from "../shared/parallel-utils.js";
|
|
6
|
+
import { formatParentAskHandoffOutput } from "./parent-ask-output.js";
|
|
7
|
+
import { registerBurstDisplay, updateBurstDisplay } from "./subagent-executor-burst-display.js";
|
|
8
|
+
import { resolveRequestedCwd } from "./subagent-executor-cwd.js";
|
|
9
|
+
import { getLiveResultIndices } from "./subagent-executor-live-update.js";
|
|
10
|
+
import { getParentAskHandoff } from "./subagent-executor-parent-ask-projection.js";
|
|
11
|
+
import {
|
|
12
|
+
BURST_TASK_DISCOVERY_CWD,
|
|
13
|
+
type BurstTaskParam,
|
|
14
|
+
type SubagentParamsLike,
|
|
15
|
+
type TaskParam,
|
|
16
|
+
} from "./subagent-executor-types.js";
|
|
17
|
+
|
|
18
|
+
interface BurstItem {
|
|
19
|
+
id: string;
|
|
20
|
+
params: SubagentParamsLike;
|
|
21
|
+
signal: AbortSignal;
|
|
22
|
+
onUpdate?: (result: SubagentToolResult) => void;
|
|
23
|
+
ctx: ExtensionContext;
|
|
24
|
+
settled: boolean;
|
|
25
|
+
resolve: (result: SubagentToolResult) => void;
|
|
26
|
+
reject: (reason?: unknown) => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface ResultRoute {
|
|
30
|
+
start: number;
|
|
31
|
+
length: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type ExecuteSubagent = (
|
|
35
|
+
id: string,
|
|
36
|
+
params: SubagentParamsLike,
|
|
37
|
+
signal: AbortSignal,
|
|
38
|
+
onUpdate: ((result: SubagentToolResult) => void) | undefined,
|
|
39
|
+
ctx: ExtensionContext,
|
|
40
|
+
) => Promise<SubagentToolResult>;
|
|
41
|
+
|
|
42
|
+
type SharedField =
|
|
43
|
+
| "concurrency"
|
|
44
|
+
| "worktree"
|
|
45
|
+
| "context"
|
|
46
|
+
| "share"
|
|
47
|
+
| "control"
|
|
48
|
+
| "sessionDir"
|
|
49
|
+
| "maxOutput"
|
|
50
|
+
| "artifacts"
|
|
51
|
+
| "includeProgress"
|
|
52
|
+
| "agentScope";
|
|
53
|
+
|
|
54
|
+
const SHARED_FIELDS: readonly SharedField[] = [
|
|
55
|
+
"concurrency",
|
|
56
|
+
"worktree",
|
|
57
|
+
"context",
|
|
58
|
+
"share",
|
|
59
|
+
"control",
|
|
60
|
+
"sessionDir",
|
|
61
|
+
"maxOutput",
|
|
62
|
+
"artifacts",
|
|
63
|
+
"includeProgress",
|
|
64
|
+
"agentScope",
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
function incompatibleField(items: BurstItem[]): SharedField | undefined {
|
|
68
|
+
const first = items[0]!.params;
|
|
69
|
+
for (const field of SHARED_FIELDS) {
|
|
70
|
+
for (let index = 1; index < items.length; index++) {
|
|
71
|
+
if (!isDeepStrictEqual(first[field], items[index]!.params[field])) return field;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function incompatibleFieldResult(field: SharedField): SubagentToolResult {
|
|
78
|
+
return {
|
|
79
|
+
content: [
|
|
80
|
+
{
|
|
81
|
+
type: "text",
|
|
82
|
+
text: `Cannot coalesce sibling subagent calls: incompatible top-level field '${field}'.`,
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
isError: true,
|
|
86
|
+
details: { mode: "parallel", results: [] },
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function incompatibleWorktreeCwdResult(): SubagentToolResult {
|
|
91
|
+
return {
|
|
92
|
+
content: [
|
|
93
|
+
{
|
|
94
|
+
type: "text",
|
|
95
|
+
text: "Cannot coalesce sibling subagent calls: incompatible top-level field 'cwd' for worktree.",
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
isError: true,
|
|
99
|
+
details: { mode: "parallel", results: [] },
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function taskExpandedLength(task: TaskParam): number {
|
|
104
|
+
const count = task.count;
|
|
105
|
+
return typeof count === "number" && Number.isInteger(count) && count >= 1 ? count : 1;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function originatingCallCwd(item: BurstItem): string {
|
|
109
|
+
return resolveRequestedCwd(item.ctx.cwd, item.params.cwd);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function originatingTaskCwd(callCwd: string, taskCwd: string | undefined): string {
|
|
113
|
+
return resolveRequestedCwd(callCwd, taskCwd);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function topLevelSingleTask(item: BurstItem): BurstTaskParam | undefined {
|
|
117
|
+
if (!item.params.agent) return undefined;
|
|
118
|
+
const callCwd = originatingCallCwd(item);
|
|
119
|
+
return {
|
|
120
|
+
agent: item.params.agent,
|
|
121
|
+
task: item.params.task ?? "",
|
|
122
|
+
cwd: originatingTaskCwd(callCwd, undefined),
|
|
123
|
+
[BURST_TASK_DISCOVERY_CWD]: callCwd,
|
|
124
|
+
...(item.params.output !== undefined ? { output: item.params.output } : {}),
|
|
125
|
+
...(item.params.outputMode !== undefined ? { outputMode: item.params.outputMode } : {}),
|
|
126
|
+
...(item.params.reads !== undefined ? { reads: item.params.reads } : {}),
|
|
127
|
+
...(item.params.progress !== undefined ? { progress: item.params.progress } : {}),
|
|
128
|
+
...(item.params.model !== undefined ? { model: item.params.model } : {}),
|
|
129
|
+
...(item.params.skill !== undefined ? { skill: item.params.skill } : {}),
|
|
130
|
+
...(item.params.group !== undefined ? { group: item.params.group } : {}),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function existingParallelTask(item: BurstItem, task: TaskParam): BurstTaskParam {
|
|
135
|
+
const callCwd = originatingCallCwd(item);
|
|
136
|
+
return {
|
|
137
|
+
...task,
|
|
138
|
+
cwd: originatingTaskCwd(callCwd, task.cwd),
|
|
139
|
+
[BURST_TASK_DISCOVERY_CWD]: callCwd,
|
|
140
|
+
...(task.group === undefined && item.params.group !== undefined ? { group: item.params.group } : {}),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function mergeBurst(items: BurstItem[]): {
|
|
145
|
+
params?: SubagentParamsLike;
|
|
146
|
+
routes?: ResultRoute[];
|
|
147
|
+
error?: SubagentToolResult;
|
|
148
|
+
} {
|
|
149
|
+
const field = incompatibleField(items);
|
|
150
|
+
if (field) return { error: incompatibleFieldResult(field) };
|
|
151
|
+
const callCwds = items.map(originatingCallCwd);
|
|
152
|
+
const commonCwd = callCwds.every((cwd) => cwd === callCwds[0]) ? callCwds[0] : undefined;
|
|
153
|
+
if (items[0]!.params.worktree === true && commonCwd === undefined) return { error: incompatibleWorktreeCwdResult() };
|
|
154
|
+
|
|
155
|
+
const tasks: TaskParam[] = [];
|
|
156
|
+
const routes: ResultRoute[] = [];
|
|
157
|
+
for (const item of items) {
|
|
158
|
+
const start = tasks.reduce((total, task) => total + taskExpandedLength(task), 0);
|
|
159
|
+
const single = topLevelSingleTask(item);
|
|
160
|
+
if (single) tasks.push(single);
|
|
161
|
+
for (const task of item.params.tasks ?? []) tasks.push(existingParallelTask(item, task));
|
|
162
|
+
const end = tasks.reduce((total, task) => total + taskExpandedLength(task), 0);
|
|
163
|
+
routes.push({ start, length: end - start });
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const first = items[0]!.params;
|
|
167
|
+
return {
|
|
168
|
+
params: {
|
|
169
|
+
...(commonCwd ? { cwd: commonCwd } : {}),
|
|
170
|
+
tasks,
|
|
171
|
+
concurrency: first.concurrency,
|
|
172
|
+
worktree: first.worktree,
|
|
173
|
+
context: first.context,
|
|
174
|
+
share: first.share,
|
|
175
|
+
control: first.control,
|
|
176
|
+
sessionDir: first.sessionDir,
|
|
177
|
+
maxOutput: first.maxOutput,
|
|
178
|
+
artifacts: first.artifacts,
|
|
179
|
+
includeProgress: first.includeProgress,
|
|
180
|
+
agentScope: first.agentScope,
|
|
181
|
+
},
|
|
182
|
+
routes,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function formatStandardParallelContent(results: SingleResult[]): string {
|
|
186
|
+
return formatParallelResultContent(
|
|
187
|
+
results.map((child) => ({
|
|
188
|
+
agent: child.agent,
|
|
189
|
+
output: child.truncation?.text || getSingleResultOutput(child),
|
|
190
|
+
status: child.status,
|
|
191
|
+
error: child.error,
|
|
192
|
+
})),
|
|
193
|
+
(index, agent) => `=== Task ${index + 1}: ${agent} ===`,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function projectParentAskContent(
|
|
198
|
+
result: SubagentToolResult,
|
|
199
|
+
route: ResultRoute,
|
|
200
|
+
projectedResults: SingleResult[],
|
|
201
|
+
): SubagentToolResult["content"] | undefined {
|
|
202
|
+
const handoff = getParentAskHandoff(result);
|
|
203
|
+
if (!handoff) return undefined;
|
|
204
|
+
const originalItem =
|
|
205
|
+
result.content.length === 1 && result.content[0]?.type === "text"
|
|
206
|
+
? result.content[0]
|
|
207
|
+
: { type: "text" as const, text: "" };
|
|
208
|
+
const routeEnd = route.start + route.length;
|
|
209
|
+
const inRoute = (index: number): boolean => index >= route.start && index < routeEnd;
|
|
210
|
+
if (!inRoute(handoff.askingChildIndex)) {
|
|
211
|
+
return [{ ...originalItem, text: formatStandardParallelContent(projectedResults) }];
|
|
212
|
+
}
|
|
213
|
+
const projectIndex = (index: number): number => index - route.start;
|
|
214
|
+
return [
|
|
215
|
+
{
|
|
216
|
+
...originalItem,
|
|
217
|
+
text: formatParentAskHandoffOutput({
|
|
218
|
+
...handoff,
|
|
219
|
+
askingChildIndex: projectIndex(handoff.askingChildIndex),
|
|
220
|
+
releasedChildIndices: handoff.releasedChildIndices.filter(inRoute).map(projectIndex),
|
|
221
|
+
unlaunchedChildIndices: handoff.unlaunchedChildIndices.filter(inRoute).map(projectIndex),
|
|
222
|
+
request: { ...handoff.request, index: projectIndex(handoff.request.index) },
|
|
223
|
+
}),
|
|
224
|
+
},
|
|
225
|
+
];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function projectParallelContent(
|
|
229
|
+
result: SubagentToolResult,
|
|
230
|
+
sharedResults: SingleResult[],
|
|
231
|
+
projectedResults: SingleResult[],
|
|
232
|
+
route: ResultRoute,
|
|
233
|
+
live: boolean,
|
|
234
|
+
): SubagentToolResult["content"] {
|
|
235
|
+
const projectedText = formatStandardParallelContent(projectedResults);
|
|
236
|
+
if (live) return [{ type: "text", text: projectedText }];
|
|
237
|
+
const parentAskContent = projectParentAskContent(result, route, projectedResults);
|
|
238
|
+
if (parentAskContent) return parentAskContent;
|
|
239
|
+
if (result.content.length !== 1 || result.content[0]?.type !== "text") return result.content;
|
|
240
|
+
const originalItem = result.content[0];
|
|
241
|
+
const originalText = originalItem.text;
|
|
242
|
+
const sharedText = formatStandardParallelContent(sharedResults);
|
|
243
|
+
if (originalText === sharedText) return [{ ...originalItem, text: projectedText }];
|
|
244
|
+
if (originalText.startsWith(`${sharedText}\n\n`)) {
|
|
245
|
+
return [{ ...originalItem, text: `${projectedText}${originalText.slice(sharedText.length)}` }];
|
|
246
|
+
}
|
|
247
|
+
return result.content;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function projectResult(result: SubagentToolResult, route: ResultRoute, live = false): SubagentToolResult {
|
|
251
|
+
const sharedDetails = result.details;
|
|
252
|
+
if (!sharedDetails || (!live && sharedDetails.results.length === 0)) return result;
|
|
253
|
+
|
|
254
|
+
const liveIndices = live ? getLiveResultIndices(result) : undefined;
|
|
255
|
+
const results = liveIndices
|
|
256
|
+
? sharedDetails.results.filter((_child, index) => {
|
|
257
|
+
const resultIndex = liveIndices[index];
|
|
258
|
+
return resultIndex !== undefined && resultIndex >= route.start && resultIndex < route.start + route.length;
|
|
259
|
+
})
|
|
260
|
+
: sharedDetails.results.slice(route.start, route.start + route.length);
|
|
261
|
+
const progress = sharedDetails.progress
|
|
262
|
+
?.filter((item) => item.index >= route.start && item.index < route.start + route.length)
|
|
263
|
+
.map((item) => ({ ...item, index: item.index - route.start }));
|
|
264
|
+
const controlEvents = sharedDetails.controlEvents
|
|
265
|
+
?.filter(
|
|
266
|
+
(event) => event.index !== undefined && event.index >= route.start && event.index < route.start + route.length,
|
|
267
|
+
)
|
|
268
|
+
.map((event) => ({ ...event, index: event.index! - route.start }));
|
|
269
|
+
const artifactFiles = results.flatMap((child) => (child.artifactPaths ? [child.artifactPaths] : []));
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
content: projectParallelContent(result, sharedDetails.results, results, route, live),
|
|
273
|
+
...(result.isError !== undefined ? { isError: result.isError } : {}),
|
|
274
|
+
details: {
|
|
275
|
+
mode: "parallel",
|
|
276
|
+
...(sharedDetails.runId !== undefined ? { runId: sharedDetails.runId } : {}),
|
|
277
|
+
...(sharedDetails.context !== undefined ? { context: sharedDetails.context } : {}),
|
|
278
|
+
results,
|
|
279
|
+
...(progress?.length ? { progress } : {}),
|
|
280
|
+
...(controlEvents?.length ? { controlEvents } : {}),
|
|
281
|
+
...(sharedDetails.totalSteps !== undefined ? { totalSteps: route.length } : {}),
|
|
282
|
+
...(sharedDetails.parentAskYielded !== undefined ? { parentAskYielded: sharedDetails.parentAskYielded } : {}),
|
|
283
|
+
...(sharedDetails.artifacts && artifactFiles.length
|
|
284
|
+
? { artifacts: { dir: sharedDetails.artifacts.dir, files: artifactFiles } }
|
|
285
|
+
: {}),
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function resolveItem(item: BurstItem, result: SubagentToolResult): void {
|
|
291
|
+
if (item.settled) return;
|
|
292
|
+
item.settled = true;
|
|
293
|
+
item.resolve(result);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function rejectItem(item: BurstItem, reason: unknown): void {
|
|
297
|
+
if (item.settled) return;
|
|
298
|
+
item.settled = true;
|
|
299
|
+
item.reject(reason);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function observeLaterAbort(item: BurstItem): () => void {
|
|
303
|
+
let listening = false;
|
|
304
|
+
const cleanup = (): void => {
|
|
305
|
+
if (!listening) return;
|
|
306
|
+
listening = false;
|
|
307
|
+
item.signal.removeEventListener("abort", onAbort);
|
|
308
|
+
};
|
|
309
|
+
const onAbort = (): void => {
|
|
310
|
+
cleanup();
|
|
311
|
+
const reason = item.signal.reason;
|
|
312
|
+
rejectItem(item, reason === undefined ? new DOMException("The operation was aborted", "AbortError") : reason);
|
|
313
|
+
};
|
|
314
|
+
if (item.signal.aborted) {
|
|
315
|
+
onAbort();
|
|
316
|
+
} else {
|
|
317
|
+
listening = true;
|
|
318
|
+
item.signal.addEventListener("abort", onAbort, { once: true });
|
|
319
|
+
if (item.signal.aborted) onAbort();
|
|
320
|
+
}
|
|
321
|
+
return cleanup;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export function createExecutionBurstDispatcher(input: {
|
|
325
|
+
execute: ExecuteSubagent;
|
|
326
|
+
isActive: () => boolean;
|
|
327
|
+
setActive: (active: boolean) => void;
|
|
328
|
+
duplicateResult: (params: SubagentParamsLike) => SubagentToolResult;
|
|
329
|
+
}): ExecuteSubagent {
|
|
330
|
+
let queue: BurstItem[] = [];
|
|
331
|
+
let flushScheduled = false;
|
|
332
|
+
|
|
333
|
+
const run = async (items: BurstItem[]): Promise<void> => {
|
|
334
|
+
if (items.length === 1) {
|
|
335
|
+
const item = items[0]!;
|
|
336
|
+
input.setActive(true);
|
|
337
|
+
try {
|
|
338
|
+
item.resolve(await input.execute(item.id, item.params, item.signal, item.onUpdate, item.ctx));
|
|
339
|
+
} catch (error) {
|
|
340
|
+
item.reject(error instanceof Error ? error : new Error(String(error)));
|
|
341
|
+
} finally {
|
|
342
|
+
input.setActive(false);
|
|
343
|
+
}
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const abortCleanups = items.slice(1).map(observeLaterAbort);
|
|
348
|
+
const cleanupLaterAborts = (): void => {
|
|
349
|
+
for (const cleanup of abortCleanups) cleanup();
|
|
350
|
+
};
|
|
351
|
+
const merged = mergeBurst(items);
|
|
352
|
+
if (merged.error) {
|
|
353
|
+
for (const item of items) resolveItem(item, merged.error);
|
|
354
|
+
cleanupLaterAborts();
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const first = items[0]!;
|
|
359
|
+
const display = registerBurstDisplay(
|
|
360
|
+
items.map((item) => item.id),
|
|
361
|
+
merged.routes!.reduce((total, route) => total + route.length, 0),
|
|
362
|
+
);
|
|
363
|
+
const onUpdate = (update: SubagentToolResult): void => {
|
|
364
|
+
updateBurstDisplay(display, update);
|
|
365
|
+
for (let index = 0; index < items.length; index++) {
|
|
366
|
+
const item = items[index]!;
|
|
367
|
+
if (!item.settled) item.onUpdate?.(projectResult(update, merged.routes![index]!, true));
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
input.setActive(true);
|
|
371
|
+
try {
|
|
372
|
+
const result = await input.execute(first.id, merged.params!, first.signal, onUpdate, first.ctx);
|
|
373
|
+
updateBurstDisplay(display, result);
|
|
374
|
+
for (let index = 0; index < items.length; index++) {
|
|
375
|
+
resolveItem(items[index]!, projectResult(result, merged.routes![index]!));
|
|
376
|
+
}
|
|
377
|
+
} catch (error) {
|
|
378
|
+
const rejection = error instanceof Error ? error : new Error(String(error));
|
|
379
|
+
for (const item of items) rejectItem(item, rejection);
|
|
380
|
+
} finally {
|
|
381
|
+
input.setActive(false);
|
|
382
|
+
cleanupLaterAborts();
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
const flush = (): void => {
|
|
387
|
+
flushScheduled = false;
|
|
388
|
+
const items = queue;
|
|
389
|
+
queue = [];
|
|
390
|
+
void run(items);
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
return (id, params, signal, onUpdate, ctx) => {
|
|
394
|
+
if (input.isActive()) return Promise.resolve(input.duplicateResult(params));
|
|
395
|
+
return new Promise<SubagentToolResult>((resolve, reject) => {
|
|
396
|
+
queue.push({ id, params, signal, onUpdate, ctx, settled: false, resolve, reject });
|
|
397
|
+
if (!flushScheduled) {
|
|
398
|
+
flushScheduled = true;
|
|
399
|
+
setImmediate(flush);
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
};
|
|
403
|
+
}
|
|
@@ -3,6 +3,7 @@ import * as fs from "node:fs";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import type { ExtensionContext } from "@bastani/atomic";
|
|
5
5
|
import { resolveExecutionAgentScope } from "../../agents/agent-scope.js";
|
|
6
|
+
import type { AgentConfig } from "../../agents/agents.js";
|
|
6
7
|
import {
|
|
7
8
|
applyIntercomBridgeToAgent,
|
|
8
9
|
resolveIntercomBridge,
|
|
@@ -29,11 +30,14 @@ import {
|
|
|
29
30
|
validateExecutionInput,
|
|
30
31
|
withForkContext,
|
|
31
32
|
} from "./subagent-executor-input.js";
|
|
32
|
-
import
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
import {
|
|
34
|
+
BURST_TASK_DISCOVERY_CWD,
|
|
35
|
+
type BurstTaskParam,
|
|
36
|
+
type ExecutionContextBuildResult,
|
|
37
|
+
type ExecutionContextData,
|
|
38
|
+
type ExecutorDeps,
|
|
39
|
+
type ResolvedExecutorDeps,
|
|
40
|
+
type SubagentParamsLike,
|
|
37
41
|
} from "./subagent-executor-types.js";
|
|
38
42
|
|
|
39
43
|
/**
|
|
@@ -71,8 +75,39 @@ export function prepareExecutionContext(input: {
|
|
|
71
75
|
const effectiveCwd = effectiveParams.cwd ?? ctx.cwd;
|
|
72
76
|
const parentSessionFile = ctx.sessionManager.getSessionFile() ?? null;
|
|
73
77
|
deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
74
|
-
const
|
|
75
|
-
|
|
78
|
+
const initialDiscoveryCwd =
|
|
79
|
+
(effectiveParams.tasks?.[0] as BurstTaskParam | undefined)?.[BURST_TASK_DISCOVERY_CWD] ?? effectiveCwd;
|
|
80
|
+
const discoveredAgents = deps.discoverAgents(initialDiscoveryCwd, scope).agents;
|
|
81
|
+
let resolvedTaskAgents: AgentConfig[] | undefined;
|
|
82
|
+
if (effectiveParams.tasks?.some((task) => (task as BurstTaskParam)[BURST_TASK_DISCOVERY_CWD] !== undefined)) {
|
|
83
|
+
const agentsByCwd = new Map<string, AgentConfig[]>([[initialDiscoveryCwd, discoveredAgents]]);
|
|
84
|
+
resolvedTaskAgents = [];
|
|
85
|
+
for (let index = 0; index < effectiveParams.tasks.length; index++) {
|
|
86
|
+
const task = effectiveParams.tasks[index]!;
|
|
87
|
+
const discoveryCwd = (task as BurstTaskParam)[BURST_TASK_DISCOVERY_CWD] ?? effectiveCwd;
|
|
88
|
+
let taskAgents = agentsByCwd.get(discoveryCwd);
|
|
89
|
+
if (!taskAgents) {
|
|
90
|
+
taskAgents = deps.discoverAgents(discoveryCwd, scope).agents;
|
|
91
|
+
agentsByCwd.set(discoveryCwd, taskAgents);
|
|
92
|
+
}
|
|
93
|
+
const agent = taskAgents.find((candidate) => candidate.name === task.agent);
|
|
94
|
+
if (!agent) {
|
|
95
|
+
return {
|
|
96
|
+
error: {
|
|
97
|
+
content: [{ type: "text", text: `Unknown agent: ${task.agent} (task ${index + 1})` }],
|
|
98
|
+
isError: true,
|
|
99
|
+
details: { mode: "parallel", results: [] },
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
resolvedTaskAgents.push(agent);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (effectiveParams.context === undefined && resolvedTaskAgents?.some((agent) => agent.defaultContext === "fork")) {
|
|
107
|
+
effectiveParams = { ...effectiveParams, context: "fork" };
|
|
108
|
+
} else {
|
|
109
|
+
effectiveParams = applyAgentDefaultContext(effectiveParams, discoveredAgents);
|
|
110
|
+
}
|
|
76
111
|
const sessionName = resolveIntercomSessionTarget(deps.pi.getSessionName(), ctx.sessionManager.getSessionId());
|
|
77
112
|
const intercomBridge = resolveIntercomBridge({
|
|
78
113
|
config: deps.config.intercomBridge,
|
|
@@ -83,11 +118,14 @@ export function prepareExecutionContext(input: {
|
|
|
83
118
|
const agents = intercomBridge.active
|
|
84
119
|
? discoveredAgents.map((agent) => applyIntercomBridgeToAgent(agent, intercomBridge))
|
|
85
120
|
: discoveredAgents;
|
|
121
|
+
const parallelAgentConfigs = intercomBridge.active
|
|
122
|
+
? resolvedTaskAgents?.map((agent) => applyIntercomBridgeToAgent(agent, intercomBridge))
|
|
123
|
+
: resolvedTaskAgents;
|
|
86
124
|
const runId = randomUUID().slice(0, 8);
|
|
87
125
|
const shareEnabled = effectiveParams.share === true;
|
|
88
126
|
const hasTasks = (effectiveParams.tasks?.length ?? 0) > 0;
|
|
89
127
|
const hasSingle = !hasTasks && Boolean(effectiveParams.agent);
|
|
90
|
-
const validationError = validateExecutionInput(effectiveParams, agents, hasTasks, hasSingle);
|
|
128
|
+
const validationError = validateExecutionInput(effectiveParams, agents, hasTasks, hasSingle, parallelAgentConfigs);
|
|
91
129
|
if (validationError) return { error: validationError };
|
|
92
130
|
if (hasSingle) {
|
|
93
131
|
const readInstruction = buildReadInstruction(effectiveParams.reads, effectiveCwd);
|
|
@@ -137,13 +175,14 @@ export function prepareExecutionContext(input: {
|
|
|
137
175
|
? (r: SubagentToolResult) => onUpdate(withForkContext(r, effectiveParams.context))
|
|
138
176
|
: undefined;
|
|
139
177
|
|
|
140
|
-
const execData = {
|
|
178
|
+
const execData: ExecutionContextData = {
|
|
141
179
|
params: effectiveParams,
|
|
142
180
|
effectiveCwd,
|
|
143
181
|
ctx,
|
|
144
182
|
signal,
|
|
145
183
|
onUpdate: onUpdateWithContext,
|
|
146
184
|
agents,
|
|
185
|
+
...(parallelAgentConfigs ? { parallelAgentConfigs } : {}),
|
|
147
186
|
runId,
|
|
148
187
|
shareEnabled,
|
|
149
188
|
sessionRoot,
|
|
@@ -7,6 +7,7 @@ export function validateExecutionInput(
|
|
|
7
7
|
agents: AgentConfig[],
|
|
8
8
|
hasTasks: boolean,
|
|
9
9
|
hasSingle: boolean,
|
|
10
|
+
parallelAgentConfigs?: AgentConfig[],
|
|
10
11
|
): SubagentToolResult | null {
|
|
11
12
|
if (Number(hasTasks) + Number(hasSingle) !== 1) {
|
|
12
13
|
return {
|
|
@@ -47,7 +48,8 @@ export function validateExecutionInput(
|
|
|
47
48
|
if (hasTasks && params.tasks) {
|
|
48
49
|
for (let i = 0; i < params.tasks.length; i++) {
|
|
49
50
|
const task = params.tasks[i]!;
|
|
50
|
-
|
|
51
|
+
const agent = parallelAgentConfigs?.[i] ?? agents.find((candidate) => candidate.name === task.agent);
|
|
52
|
+
if (!agent || agent.name !== task.agent) {
|
|
51
53
|
return {
|
|
52
54
|
content: [{ type: "text", text: `Unknown agent: ${task.agent} (task ${i + 1})` }],
|
|
53
55
|
isError: true,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SubagentToolResult } from "../../shared/types.js";
|
|
2
|
+
|
|
3
|
+
const liveResultIndices = new WeakMap<SubagentToolResult, readonly number[]>();
|
|
4
|
+
|
|
5
|
+
export function markLiveResultIndices(result: SubagentToolResult, indices: readonly number[]): void {
|
|
6
|
+
liveResultIndices.set(result, indices);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function getLiveResultIndices(result: SubagentToolResult): readonly number[] | undefined {
|
|
10
|
+
return liveResultIndices.get(result);
|
|
11
|
+
}
|