@agwab/pi-workflow 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -15
- package/agents/researcher.md +17 -7
- package/dist/artifact-graph-runtime.js +1 -0
- package/dist/compiler.d.ts +2 -0
- package/dist/compiler.js +29 -4
- package/dist/dynamic-generated-task-runtime.js +4 -3
- package/dist/dynamic-runtime-bundle.js +3 -2
- package/dist/engine.d.ts +2 -0
- package/dist/engine.js +3 -2
- package/dist/extension.js +240 -16
- package/dist/store.js +1 -0
- package/dist/subagent-backend.js +82 -27
- package/dist/tool-metadata.d.ts +1 -0
- package/dist/tool-metadata.js +13 -1
- package/dist/types.d.ts +3 -0
- package/dist/workflow-artifact-extension.js +3 -2
- package/dist/workflow-artifact-tool.js +84 -4
- package/dist/workflow-progress-health.d.ts +37 -0
- package/dist/workflow-progress-health.js +296 -0
- package/dist/workflow-runtime.d.ts +6 -0
- package/dist/workflow-runtime.js +33 -10
- package/dist/workflow-view.d.ts +2 -0
- package/dist/workflow-view.js +97 -18
- package/dist/workflow-web-source-extension.d.ts +43 -0
- package/dist/workflow-web-source-extension.js +1194 -0
- package/dist/workflow-web-source.d.ts +171 -0
- package/dist/workflow-web-source.js +915 -0
- package/docs/usage.md +32 -18
- package/node_modules/@agwab/pi-subagent/package.json +1 -1
- package/node_modules/@agwab/pi-subagent/src/api.ts +245 -132
- package/node_modules/@agwab/pi-subagent/src/artifacts/result.ts +243 -163
- package/node_modules/@agwab/pi-subagent/src/core/constants.ts +117 -90
- package/node_modules/@agwab/pi-subagent/src/core/validation.ts +728 -475
- package/node_modules/@agwab/pi-subagent/src/orchestrate/run.ts +305 -209
- package/node_modules/@agwab/pi-subagent/src/runners/headless-model.ts +750 -439
- package/node_modules/@agwab/pi-subagent/src/runners/tmux.ts +422 -268
- package/package.json +7 -7
- package/skills/workflow-guide/scaffolds/object-tool-fallback/schemas/fetch-control.schema.json +1 -1
- package/skills/workflow-guide/scaffolds/object-tool-fallback/spec.json +4 -3
- package/src/artifact-graph-runtime.ts +1 -0
- package/src/compiler.ts +43 -3
- package/src/dynamic-generated-task-runtime.ts +4 -2
- package/src/dynamic-runtime-bundle.ts +3 -2
- package/src/engine.ts +7 -16
- package/src/extension.ts +299 -22
- package/src/store.ts +1 -0
- package/src/subagent-backend.ts +121 -37
- package/src/tool-metadata.ts +22 -1
- package/src/types.ts +4 -0
- package/src/workflow-artifact-extension.ts +3 -2
- package/src/workflow-artifact-tool.ts +96 -4
- package/src/workflow-progress-health.ts +461 -0
- package/src/workflow-runtime.ts +50 -13
- package/src/workflow-view.ts +186 -41
- package/src/workflow-web-source-extension.ts +1411 -0
- package/src/workflow-web-source.ts +1294 -0
- package/workflows/README.md +1 -1
- package/workflows/deep-research/helpers/claim-evidence-gate.mjs +552 -44
- package/workflows/deep-research/helpers/final-audit-packet.mjs +396 -0
- package/workflows/deep-research/helpers/normalize-input-packet.mjs +545 -0
- package/workflows/deep-research/helpers/render-executive.mjs +1199 -192
- package/workflows/deep-research/helpers/sanitize-verification-candidates.mjs +624 -0
- package/workflows/deep-research/schemas/deep-research-executive-render-control.schema.json +37 -8
- package/workflows/deep-research/schemas/deep-research-final-synthesis-control.schema.json +110 -0
- package/workflows/deep-research/schemas/deep-research-normalize-claims-control.schema.json +45 -4
- package/workflows/deep-research/schemas/deep-research-verify-claims-control.schema.json +0 -2
- package/workflows/deep-research/spec.json +71 -26
- package/workflows/deep-review/helpers/render-review-report.mjs +502 -0
- package/workflows/deep-review/schemas/deep-review-render-control.schema.json +50 -0
- package/workflows/deep-review/spec.json +22 -1
|
@@ -1,251 +1,347 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { loadAgentByName, type AgentDefinition } from "../agents.ts";
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
4
|
+
appendRunEvent,
|
|
5
|
+
beginRunRecord,
|
|
6
|
+
createAttemptId,
|
|
7
|
+
createRunId,
|
|
8
|
+
finishAttemptFromResult,
|
|
9
|
+
updateAttemptProcess,
|
|
10
|
+
upsertRunAttempt,
|
|
11
|
+
type ProcessMetadata,
|
|
12
|
+
type ResultEnvelope,
|
|
13
13
|
} from "../artifacts/index.ts";
|
|
14
14
|
import type { ResolveInput, SubagentTaskInput } from "../core/constants.ts";
|
|
15
15
|
import { resolveBackend } from "../core/resolver.ts";
|
|
16
16
|
import { runHeadlessModel } from "../runners/headless-model.ts";
|
|
17
17
|
import { runInlineModel } from "../runners/inline.ts";
|
|
18
18
|
import { runTmuxModel } from "../runners/tmux.ts";
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
finalizeWorktreeResult,
|
|
21
|
+
resolveWorkspace,
|
|
22
|
+
type ResolvedWorkspace,
|
|
23
|
+
} from "../workspace/worktree.ts";
|
|
20
24
|
|
|
21
25
|
export const DEFAULT_PARALLEL_CONCURRENCY = 4;
|
|
22
26
|
export const MAX_PARALLEL_TASKS = 12;
|
|
23
27
|
export const MAX_PARALLEL_CONCURRENCY = 10;
|
|
24
28
|
|
|
25
29
|
export interface RunSubagentTaskOptions {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
input: ResolveInput;
|
|
31
|
+
cwd: string;
|
|
32
|
+
signal?: AbortSignal;
|
|
33
|
+
runId?: string;
|
|
34
|
+
attemptId?: string;
|
|
35
|
+
taskIndex?: number;
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
export interface MultiRunOptions {
|
|
35
|
-
|
|
39
|
+
correlationId?: string;
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
export interface ParallelRunResult {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
mode: "parallel";
|
|
44
|
+
runIds: string[];
|
|
45
|
+
results: ResultEnvelope[];
|
|
46
|
+
concurrency: number;
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
export class SubagentToolAuthorityError extends Error {
|
|
46
|
-
|
|
50
|
+
readonly failureKind = "validation" as const;
|
|
47
51
|
}
|
|
48
52
|
|
|
49
|
-
function mergeTaskInput(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
53
|
+
function mergeTaskInput(
|
|
54
|
+
parent: ResolveInput,
|
|
55
|
+
task: SubagentTaskInput,
|
|
56
|
+
): ResolveInput {
|
|
57
|
+
return {
|
|
58
|
+
...parent,
|
|
59
|
+
...task,
|
|
60
|
+
tasks: undefined,
|
|
61
|
+
mode: "single",
|
|
62
|
+
workspace: parent.workspace,
|
|
63
|
+
worktree: parent.worktree,
|
|
64
|
+
worktreePolicy: parent.worktreePolicy,
|
|
65
|
+
concurrency: undefined,
|
|
66
|
+
asyncDependency: undefined,
|
|
67
|
+
runsDir: parent.runsDir,
|
|
68
|
+
correlationId: parent.correlationId,
|
|
69
|
+
parentSessionId: parent.parentSessionId,
|
|
70
|
+
};
|
|
64
71
|
}
|
|
65
72
|
|
|
66
73
|
function workspaceMeta(workspace: ResolvedWorkspace) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
74
|
+
return {
|
|
75
|
+
mode: workspace.mode,
|
|
76
|
+
cwd: workspace.baseCwd,
|
|
77
|
+
worktreePath: workspace.worktreePath,
|
|
78
|
+
};
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
function parallelConcurrency(input: ResolveInput): number {
|
|
75
|
-
|
|
76
|
-
|
|
82
|
+
const requested = input.concurrency ?? DEFAULT_PARALLEL_CONCURRENCY;
|
|
83
|
+
return Math.max(1, Math.min(MAX_PARALLEL_CONCURRENCY, requested));
|
|
77
84
|
}
|
|
78
85
|
|
|
79
86
|
function toolListLabel(tools: readonly string[] | undefined): string {
|
|
80
|
-
|
|
87
|
+
return tools === undefined
|
|
88
|
+
? "(unspecified)"
|
|
89
|
+
: tools.length === 0
|
|
90
|
+
? "(none)"
|
|
91
|
+
: tools.join(", ");
|
|
81
92
|
}
|
|
82
93
|
|
|
83
|
-
function resolveEffectiveTools(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
function resolveEffectiveTools(
|
|
95
|
+
input: ResolveInput,
|
|
96
|
+
agentDefinition: AgentDefinition | undefined,
|
|
97
|
+
): string[] | undefined {
|
|
98
|
+
if (agentDefinition === undefined) return input.tools;
|
|
99
|
+
if (input.tools === undefined) return agentDefinition.tools;
|
|
100
|
+
if (agentDefinition.tools === undefined) {
|
|
101
|
+
throw new SubagentToolAuthorityError(
|
|
102
|
+
`agent ${agentDefinition.displayName} does not declare a tools authority ceiling; caller tools cannot be applied safely.`,
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
const allowed = new Set(agentDefinition.tools);
|
|
106
|
+
const outside = input.tools.filter((tool) => !allowed.has(tool));
|
|
107
|
+
if (outside.length > 0) {
|
|
108
|
+
throw new SubagentToolAuthorityError(
|
|
109
|
+
`caller tools expand agent ${agentDefinition.displayName}; disallowed: ${outside.join(", ")}; allowed tools: ${toolListLabel(agentDefinition.tools)}`,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
return input.tools;
|
|
95
113
|
}
|
|
96
114
|
|
|
97
|
-
export async function runSubagentTask(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
115
|
+
export async function runSubagentTask(
|
|
116
|
+
options: RunSubagentTaskOptions,
|
|
117
|
+
): Promise<ResultEnvelope> {
|
|
118
|
+
const input = options.input;
|
|
119
|
+
const resolved = resolveBackend(input);
|
|
120
|
+
if (resolved.status === "failed") throw new Error(resolved.error);
|
|
121
|
+
|
|
122
|
+
const backend = resolved.backend;
|
|
123
|
+
const runId = options.runId ?? createRunId();
|
|
124
|
+
const attemptId = options.attemptId ?? createAttemptId();
|
|
125
|
+
const baseCwd = resolve(input.cwd ?? options.cwd);
|
|
126
|
+
const startedAt = new Date();
|
|
127
|
+
const runRef = { cwd: baseCwd, runId, runsDir: input.runsDir };
|
|
128
|
+
const requestedAgent = input.agent ?? `${backend}-worker`;
|
|
129
|
+
const shouldLoadAgent = input.agent !== undefined;
|
|
130
|
+
const agentDefinition = shouldLoadAgent
|
|
131
|
+
? await loadAgentByName(input.agent!, baseCwd, input.agentScope)
|
|
132
|
+
: undefined;
|
|
133
|
+
const effectiveTools = resolveEffectiveTools(input, agentDefinition);
|
|
134
|
+
|
|
135
|
+
await beginRunRecord({
|
|
136
|
+
...runRef,
|
|
137
|
+
mode: "single",
|
|
138
|
+
backend,
|
|
139
|
+
startedAt,
|
|
140
|
+
dependency: input.asyncDependency ?? null,
|
|
141
|
+
correlationId: input.correlationId,
|
|
142
|
+
parentSessionId: input.parentSessionId,
|
|
143
|
+
activeAttemptId: attemptId,
|
|
144
|
+
attempts: [
|
|
145
|
+
{
|
|
146
|
+
attemptId,
|
|
147
|
+
status: "running",
|
|
148
|
+
backend,
|
|
149
|
+
startedAt: startedAt.toISOString(),
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
const workspace = await resolveWorkspace({
|
|
156
|
+
cwd: baseCwd,
|
|
157
|
+
input,
|
|
158
|
+
taskIndex: options.taskIndex,
|
|
159
|
+
runId,
|
|
160
|
+
});
|
|
161
|
+
const cwd = workspace.cwd;
|
|
162
|
+
const workspaceResult = workspaceMeta(workspace);
|
|
163
|
+
|
|
164
|
+
await upsertRunAttempt({
|
|
165
|
+
...runRef,
|
|
166
|
+
attemptId,
|
|
167
|
+
status: "running",
|
|
168
|
+
backend,
|
|
169
|
+
failureKind: null,
|
|
170
|
+
startedAt,
|
|
171
|
+
completedAt: null,
|
|
172
|
+
workspace: workspaceResult,
|
|
173
|
+
activate: true,
|
|
174
|
+
});
|
|
175
|
+
await appendRunEvent(
|
|
176
|
+
{ ...runRef },
|
|
177
|
+
{
|
|
178
|
+
type: "attempt.started",
|
|
179
|
+
attemptId,
|
|
180
|
+
status: "running",
|
|
181
|
+
message: `attempt ${attemptId} started`,
|
|
182
|
+
},
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
const onProcessStart = async (process: ProcessMetadata) => {
|
|
186
|
+
await updateAttemptProcess({ ...runRef, attemptId, process });
|
|
187
|
+
await appendRunEvent(
|
|
188
|
+
{ ...runRef },
|
|
189
|
+
{
|
|
190
|
+
type: "attempt.process_started",
|
|
191
|
+
attemptId,
|
|
192
|
+
status: "running",
|
|
193
|
+
data: { ...process },
|
|
194
|
+
},
|
|
195
|
+
);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const common = {
|
|
199
|
+
cwd,
|
|
200
|
+
artifactCwd: baseCwd,
|
|
201
|
+
signal: options.signal,
|
|
202
|
+
timeoutMs: input.timeoutMs,
|
|
203
|
+
sandbox: input.sandbox,
|
|
204
|
+
runId,
|
|
205
|
+
attemptId,
|
|
206
|
+
runsDir: input.runsDir,
|
|
207
|
+
correlationId: input.correlationId,
|
|
208
|
+
parentSessionId: input.parentSessionId,
|
|
209
|
+
workspace: workspaceResult,
|
|
210
|
+
onProcessStart,
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
if (input.task === undefined)
|
|
214
|
+
throw new Error(`${backend} execution requires agent/task input.`);
|
|
215
|
+
const modelOptions = {
|
|
216
|
+
...common,
|
|
217
|
+
captureToolCalls: input.captureToolCalls,
|
|
218
|
+
agent: requestedAgent,
|
|
219
|
+
task: input.task,
|
|
220
|
+
roleContext: input.roleContext,
|
|
221
|
+
agentScope: input.agentScope,
|
|
222
|
+
confirmProjectAgents: input.confirmProjectAgents,
|
|
223
|
+
model: input.model,
|
|
224
|
+
thinking: input.thinking,
|
|
225
|
+
tools: effectiveTools,
|
|
226
|
+
systemPrompt: input.systemPrompt,
|
|
227
|
+
skills: input.skills,
|
|
228
|
+
extensions: input.extensions,
|
|
229
|
+
sessionId: input.sessionId,
|
|
230
|
+
agentDefinition,
|
|
231
|
+
};
|
|
232
|
+
let result: ResultEnvelope =
|
|
233
|
+
backend === "tmux"
|
|
234
|
+
? await runTmuxModel(modelOptions)
|
|
235
|
+
: backend === "inline"
|
|
236
|
+
? await runInlineModel(modelOptions)
|
|
237
|
+
: await runHeadlessModel(modelOptions);
|
|
238
|
+
result = await finalizeWorktreeResult(workspace, result);
|
|
239
|
+
|
|
240
|
+
await finishAttemptFromResult(runRef, result);
|
|
241
|
+
await appendRunEvent(
|
|
242
|
+
{ ...runRef },
|
|
243
|
+
{
|
|
244
|
+
type:
|
|
245
|
+
result.status === "completed"
|
|
246
|
+
? "attempt.completed"
|
|
247
|
+
: result.status === "cancelled"
|
|
248
|
+
? "attempt.cancelled"
|
|
249
|
+
: "attempt.failed",
|
|
250
|
+
attemptId,
|
|
251
|
+
status: result.status,
|
|
252
|
+
message: `attempt ${attemptId} ${result.status}`,
|
|
253
|
+
data: {
|
|
254
|
+
failureKind: result.failureKind,
|
|
255
|
+
exitCode: result.exitCode,
|
|
256
|
+
signal: result.signal,
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
);
|
|
260
|
+
await appendRunEvent(
|
|
261
|
+
{ ...runRef },
|
|
262
|
+
{
|
|
263
|
+
type:
|
|
264
|
+
result.status === "completed"
|
|
265
|
+
? "run.completed"
|
|
266
|
+
: result.status === "cancelled"
|
|
267
|
+
? "run.cancelled"
|
|
268
|
+
: "run.failed",
|
|
269
|
+
status: result.status,
|
|
270
|
+
message: `run ${result.status}`,
|
|
271
|
+
},
|
|
272
|
+
);
|
|
273
|
+
return result;
|
|
274
|
+
} catch (error) {
|
|
275
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
276
|
+
await upsertRunAttempt({
|
|
277
|
+
...runRef,
|
|
278
|
+
attemptId,
|
|
279
|
+
status: "failed",
|
|
280
|
+
backend,
|
|
281
|
+
failureKind: "internal",
|
|
282
|
+
startedAt,
|
|
283
|
+
completedAt: new Date(),
|
|
284
|
+
activate: true,
|
|
285
|
+
}).catch(() => undefined);
|
|
286
|
+
await appendRunEvent(
|
|
287
|
+
{ ...runRef },
|
|
288
|
+
{ type: "attempt.failed", attemptId, status: "failed", message },
|
|
289
|
+
).catch(() => undefined);
|
|
290
|
+
await appendRunEvent(
|
|
291
|
+
{ ...runRef },
|
|
292
|
+
{ type: "run.failed", status: "failed", message },
|
|
293
|
+
).catch(() => undefined);
|
|
294
|
+
throw error;
|
|
295
|
+
}
|
|
219
296
|
}
|
|
220
297
|
|
|
221
|
-
export async function runParallelSubagentTasks(
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
298
|
+
export async function runParallelSubagentTasks(
|
|
299
|
+
input: ResolveInput,
|
|
300
|
+
cwd: string,
|
|
301
|
+
signal?: AbortSignal,
|
|
302
|
+
_options: MultiRunOptions = {},
|
|
303
|
+
): Promise<ParallelRunResult> {
|
|
304
|
+
if (!input.tasks || input.tasks.length === 0)
|
|
305
|
+
throw new SubagentToolAuthorityError(
|
|
306
|
+
"parallel mode requires a non-empty tasks array.",
|
|
307
|
+
);
|
|
308
|
+
if (input.tasks.length > MAX_PARALLEL_TASKS)
|
|
309
|
+
throw new SubagentToolAuthorityError(
|
|
310
|
+
`too many parallel tasks (${input.tasks.length}); max is ${MAX_PARALLEL_TASKS}.`,
|
|
311
|
+
);
|
|
312
|
+
for (const [index, task] of input.tasks.entries()) {
|
|
313
|
+
if (task.task === undefined)
|
|
314
|
+
throw new SubagentToolAuthorityError(
|
|
315
|
+
`parallel tasks[${index}] requires a non-empty task.`,
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const tasks = input.tasks;
|
|
320
|
+
const runCwd = resolve(input.cwd ?? cwd);
|
|
321
|
+
const concurrency = Math.min(parallelConcurrency(input), tasks.length);
|
|
322
|
+
const results: ResultEnvelope[] = new Array(tasks.length);
|
|
323
|
+
let nextIndex = 0;
|
|
324
|
+
|
|
325
|
+
async function worker(): Promise<void> {
|
|
326
|
+
while (true) {
|
|
327
|
+
const index = nextIndex;
|
|
328
|
+
nextIndex += 1;
|
|
329
|
+
if (index >= tasks.length) return;
|
|
330
|
+
const taskInput = mergeTaskInput(input, tasks[index]!);
|
|
331
|
+
results[index] = await runSubagentTask({
|
|
332
|
+
input: taskInput,
|
|
333
|
+
cwd: runCwd,
|
|
334
|
+
signal,
|
|
335
|
+
taskIndex: index,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
await Promise.all(Array.from({ length: concurrency }, () => worker()));
|
|
341
|
+
return {
|
|
342
|
+
mode: "parallel",
|
|
343
|
+
runIds: results.map((result) => result.runId),
|
|
344
|
+
results,
|
|
345
|
+
concurrency,
|
|
346
|
+
};
|
|
251
347
|
}
|