iosm-cli 0.2.3 → 0.2.4
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 +25 -0
- package/README.md +32 -5
- package/dist/cli/args.d.ts +1 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +1 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-profiles.d.ts +1 -1
- package/dist/core/agent-profiles.d.ts.map +1 -1
- package/dist/core/agent-profiles.js +9 -0
- package/dist/core/agent-profiles.js.map +1 -1
- package/dist/core/agent-session.d.ts +7 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +58 -13
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/types.d.ts +1 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/orchestration-limits.d.ts +6 -0
- package/dist/core/orchestration-limits.d.ts.map +1 -0
- package/dist/core/orchestration-limits.js +6 -0
- package/dist/core/orchestration-limits.js.map +1 -0
- package/dist/core/sdk.d.ts +6 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +34 -6
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/subagents.d.ts.map +1 -1
- package/dist/core/subagents.js +21 -7
- package/dist/core/subagents.js.map +1 -1
- package/dist/core/tools/edit.d.ts +8 -4
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +18 -3
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/index.d.ts +9 -7
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/task.d.ts +10 -3
- package/dist/core/tools/task.d.ts.map +1 -1
- package/dist/core/tools/task.js +404 -149
- package/dist/core/tools/task.js.map +1 -1
- package/dist/core/tools/todo.d.ts +10 -10
- package/dist/core/tools/todo.d.ts.map +1 -1
- package/dist/core/tools/todo.js +135 -17
- package/dist/core/tools/todo.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +8 -8
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +9 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +214 -14
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/docs/cli-reference.md +4 -0
- package/docs/configuration.md +5 -1
- package/docs/interactive-mode.md +5 -1
- package/package.json +1 -1
package/dist/core/tools/task.js
CHANGED
|
@@ -4,19 +4,26 @@ import { spawnSync } from "node:child_process";
|
|
|
4
4
|
import { Type } from "@sinclair/typebox";
|
|
5
5
|
import { getTeamRun, updateTeamTaskStatus } from "../agent-teams.js";
|
|
6
6
|
import { buildRetrospectiveDirective, classifyFailureCause, formatFailureCauseCounts, isRetrospectiveRetryable, } from "../failure-retrospective.js";
|
|
7
|
+
import { MAX_ORCHESTRATION_AGENTS, MAX_ORCHESTRATION_PARALLEL, MAX_SUBAGENT_DELEGATE_PARALLEL, MAX_SUBAGENT_DELEGATION_DEPTH, MAX_SUBAGENT_DELEGATIONS_PER_TASK, } from "../orchestration-limits.js";
|
|
7
8
|
const taskSchema = Type.Object({
|
|
8
|
-
description: Type.String({
|
|
9
|
-
description: "
|
|
10
|
-
}),
|
|
11
|
-
|
|
12
|
-
description: "
|
|
13
|
-
}),
|
|
9
|
+
description: Type.Optional(Type.String({
|
|
10
|
+
description: "Optional short 3-5 word description of what the subagent will do. If omitted, it is derived from prompt.",
|
|
11
|
+
})),
|
|
12
|
+
task: Type.Optional(Type.String({
|
|
13
|
+
description: "Legacy alias for prompt. If provided, it is treated as the subagent prompt when prompt is omitted.",
|
|
14
|
+
})),
|
|
15
|
+
args: Type.Optional(Type.String({
|
|
16
|
+
description: "Legacy alias for prompt used by some models. If provided, it is treated as the subagent prompt when prompt/task are omitted.",
|
|
17
|
+
})),
|
|
18
|
+
prompt: Type.Optional(Type.String({
|
|
19
|
+
description: "Optional full task prompt for the subagent. If omitted, the description is used as the prompt.",
|
|
20
|
+
})),
|
|
14
21
|
agent: Type.Optional(Type.String({
|
|
15
22
|
description: "Optional custom subagent name loaded from .iosm/agents or global agents directory.",
|
|
16
23
|
})),
|
|
17
|
-
profile: Type.String({
|
|
18
|
-
description: "
|
|
19
|
-
}),
|
|
24
|
+
profile: Type.Optional(Type.String({
|
|
25
|
+
description: "Optional subagent capability profile. Defaults to full when omitted. Recommended values: explore, plan, iosm, meta, iosm_analyst, iosm_verifier, cycle_planner, full. For custom agents, pass the agent name via `agent`, not `profile`.",
|
|
26
|
+
})),
|
|
20
27
|
cwd: Type.Optional(Type.String({
|
|
21
28
|
description: "Optional working directory for this subagent. Relative paths are resolved from the current workspace.",
|
|
22
29
|
})),
|
|
@@ -40,7 +47,7 @@ const taskSchema = Type.Object({
|
|
|
40
47
|
})),
|
|
41
48
|
delegate_parallel_hint: Type.Optional(Type.Integer({
|
|
42
49
|
minimum: 1,
|
|
43
|
-
maximum:
|
|
50
|
+
maximum: MAX_SUBAGENT_DELEGATE_PARALLEL,
|
|
44
51
|
description: "Optional hint for intra-task delegation fan-out. Higher value allows more delegated subtasks to run in parallel inside a single task execution.",
|
|
45
52
|
})),
|
|
46
53
|
});
|
|
@@ -49,6 +56,7 @@ const toolsByProfile = {
|
|
|
49
56
|
explore: ["read", "grep", "find", "ls"],
|
|
50
57
|
plan: ["read", "bash", "grep", "find", "ls"],
|
|
51
58
|
iosm: ["read", "bash", "edit", "write", "grep", "find", "ls"],
|
|
59
|
+
meta: ["read", "bash", "edit", "write", "grep", "find", "ls"],
|
|
52
60
|
iosm_analyst: ["read", "bash", "grep", "find", "ls"],
|
|
53
61
|
iosm_verifier: ["read", "bash", "write"],
|
|
54
62
|
cycle_planner: ["read", "bash", "write"],
|
|
@@ -59,12 +67,13 @@ const systemPromptByProfile = {
|
|
|
59
67
|
explore: "You are a fast read-only codebase explorer. Answer concisely. Never write or edit files.",
|
|
60
68
|
plan: "You are a technical architect. Analyze the codebase and produce a clear implementation plan. Do not write or edit files.",
|
|
61
69
|
iosm: "You are an IOSM execution agent. Use IOSM methodology and keep IOSM artifacts synchronized with implementation.",
|
|
70
|
+
meta: "You are a meta orchestration agent. Your main job is to maximize safe parallel execution through delegates, not to personally do most of the implementation. Start with bounded read-only recon, then form a concrete execution graph: subtasks, delegate subtasks, dependencies, lock domains, and verification steps. The parent agent remains responsible for orchestration and synthesis, so decompose work aggressively instead of collapsing complex work into one worker. For any non-trivial task, orchestration is required: after recon, launch multiple focused delegates instead of continuing manual implementation in the parent agent, avoid direct write/edit work in the parent agent before delegation unless the task is clearly trivial, and do not hand the whole task to one specialist child when independent workstreams exist. If a delegated workstream still contains multiple independent slices, split it again with nested <delegate_task> blocks. Default to aggressive safe parallelism. If the user requested a specific degree of parallelism, honor it when feasible or explain the exact blocker. When delegation is not used for non-trivial work, explain why in one line and include DELEGATION_IMPOSSIBLE. Enforce test verification for code changes, complete only after all delegated branches are resolved, and explicitly justify any no-code path where tests are skipped.",
|
|
62
71
|
iosm_analyst: "You are an IOSM metrics analyst. Analyze .iosm/ artifacts and codebase metrics. Be precise and evidence-based.",
|
|
63
72
|
iosm_verifier: "You are an IOSM verifier. Validate checks and update only required IOSM artifacts with deterministic reasoning.",
|
|
64
73
|
cycle_planner: "You are an IOSM cycle planner. Propose and align cycle goals with measurable outcomes and concrete risks.",
|
|
65
74
|
full: "You are a software engineering agent. Execute the task end-to-end.",
|
|
66
75
|
};
|
|
67
|
-
const writeCapableProfiles = new Set(["full", "iosm_verifier", "cycle_planner"]);
|
|
76
|
+
const writeCapableProfiles = new Set(["full", "meta", "iosm_verifier", "cycle_planner"]);
|
|
68
77
|
const delegationTagName = "delegate_task";
|
|
69
78
|
class Semaphore {
|
|
70
79
|
constructor(limit) {
|
|
@@ -121,11 +130,11 @@ class Mutex {
|
|
|
121
130
|
return !this.locked && this.waiters.length === 0;
|
|
122
131
|
}
|
|
123
132
|
}
|
|
124
|
-
const maxParallelFromEnv = parseBoundedInt(process.env.IOSM_SUBAGENT_MAX_PARALLEL,
|
|
133
|
+
const maxParallelFromEnv = parseBoundedInt(process.env.IOSM_SUBAGENT_MAX_PARALLEL, MAX_ORCHESTRATION_PARALLEL, 1, MAX_ORCHESTRATION_PARALLEL);
|
|
125
134
|
const subagentSemaphore = new Semaphore(maxParallelFromEnv);
|
|
126
|
-
const maxDelegationDepthFromEnv = parseBoundedInt(process.env.IOSM_SUBAGENT_MAX_DELEGATION_DEPTH, 1, 0,
|
|
127
|
-
const maxDelegationsPerTaskFromEnv = parseBoundedInt(process.env.IOSM_SUBAGENT_MAX_DELEGATIONS_PER_TASK,
|
|
128
|
-
const maxDelegatedParallelFromEnv = parseBoundedInt(process.env.IOSM_SUBAGENT_MAX_DELEGATE_PARALLEL,
|
|
135
|
+
const maxDelegationDepthFromEnv = parseBoundedInt(process.env.IOSM_SUBAGENT_MAX_DELEGATION_DEPTH, 1, 0, MAX_SUBAGENT_DELEGATION_DEPTH);
|
|
136
|
+
const maxDelegationsPerTaskFromEnv = parseBoundedInt(process.env.IOSM_SUBAGENT_MAX_DELEGATIONS_PER_TASK, MAX_SUBAGENT_DELEGATIONS_PER_TASK, 0, MAX_SUBAGENT_DELEGATIONS_PER_TASK);
|
|
137
|
+
const maxDelegatedParallelFromEnv = parseBoundedInt(process.env.IOSM_SUBAGENT_MAX_DELEGATE_PARALLEL, MAX_SUBAGENT_DELEGATE_PARALLEL, 1, MAX_SUBAGENT_DELEGATE_PARALLEL);
|
|
129
138
|
const emptyOutputRetriesFromEnv = parseBoundedInt(process.env.IOSM_SUBAGENT_EMPTY_OUTPUT_RETRIES, 1, 0, 2);
|
|
130
139
|
const retrospectiveRetriesFromEnv = parseBoundedInt(process.env.IOSM_SUBAGENT_RETRO_RETRIES, 1, 0, 1);
|
|
131
140
|
const orchestrationDependencyWaitTimeoutMsFromEnv = parseBoundedInt(process.env.IOSM_ORCHESTRATION_DEPENDENCY_WAIT_TIMEOUT_MS, 120_000, 5_000, 900_000);
|
|
@@ -141,14 +150,21 @@ function parseBoundedInt(raw, fallback, min, max) {
|
|
|
141
150
|
return fallback;
|
|
142
151
|
return Math.max(min, Math.min(max, parsed));
|
|
143
152
|
}
|
|
144
|
-
function
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
153
|
+
function shouldAutoDelegate(input) {
|
|
154
|
+
const profile = input.profile?.trim().toLowerCase();
|
|
155
|
+
if (profile === "meta")
|
|
156
|
+
return true;
|
|
157
|
+
const hostProfile = input.hostProfile?.trim().toLowerCase();
|
|
158
|
+
if (hostProfile === "meta")
|
|
159
|
+
return true;
|
|
160
|
+
const agentName = input.agentName?.trim().toLowerCase();
|
|
161
|
+
return !!agentName && agentName.includes("orchestrator");
|
|
149
162
|
}
|
|
150
|
-
function deriveAutoDelegateParallelHint(agentName, description, prompt) {
|
|
151
|
-
|
|
163
|
+
function deriveAutoDelegateParallelHint(profile, agentName, hostProfile, description, prompt) {
|
|
164
|
+
const normalizedProfile = profile?.trim().toLowerCase();
|
|
165
|
+
const isMetaProfile = normalizedProfile === "meta";
|
|
166
|
+
const isMetaHost = hostProfile?.trim().toLowerCase() === "meta";
|
|
167
|
+
if (!shouldAutoDelegate({ profile: normalizedProfile, agentName, hostProfile }))
|
|
152
168
|
return undefined;
|
|
153
169
|
const text = `${description}\n${prompt}`.trim();
|
|
154
170
|
if (!text)
|
|
@@ -186,6 +202,10 @@ function deriveAutoDelegateParallelHint(agentName, description, prompt) {
|
|
|
186
202
|
if (hasCodeBlock) {
|
|
187
203
|
score += 1;
|
|
188
204
|
}
|
|
205
|
+
if ((isMetaProfile || isMetaHost) && score > 0) {
|
|
206
|
+
// Meta profile is intentionally parallel-biased for non-trivial work.
|
|
207
|
+
score += 1;
|
|
208
|
+
}
|
|
189
209
|
if (score >= 6)
|
|
190
210
|
return 10;
|
|
191
211
|
if (score >= 5)
|
|
@@ -203,6 +223,40 @@ function deriveAutoDelegateParallelHint(agentName, description, prompt) {
|
|
|
203
223
|
function normalizeSpacing(text) {
|
|
204
224
|
return text.replace(/[ \t]+\n/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
205
225
|
}
|
|
226
|
+
function deriveTaskDescriptionFromPrompt(prompt) {
|
|
227
|
+
const firstMeaningfulLine = prompt
|
|
228
|
+
.split("\n")
|
|
229
|
+
.map((line) => line.trim())
|
|
230
|
+
.find((line) => line.length > 0) ?? "Run subtask";
|
|
231
|
+
const normalized = firstMeaningfulLine
|
|
232
|
+
.replace(/^[-*]\s+/, "")
|
|
233
|
+
.replace(/^\d+[.)]\s+/, "")
|
|
234
|
+
.replace(/\s+/g, " ")
|
|
235
|
+
.trim();
|
|
236
|
+
if (normalized.length <= 80) {
|
|
237
|
+
return normalized;
|
|
238
|
+
}
|
|
239
|
+
return `${normalized.slice(0, 77).trimEnd()}...`;
|
|
240
|
+
}
|
|
241
|
+
function normalizeTaskPayload(input) {
|
|
242
|
+
const rawDescription = input.description?.trim();
|
|
243
|
+
const rawTask = input.task?.trim();
|
|
244
|
+
const rawArgs = input.args?.trim();
|
|
245
|
+
const rawPrompt = input.prompt?.trim() || rawTask || rawArgs;
|
|
246
|
+
if (rawDescription && rawPrompt) {
|
|
247
|
+
return { description: rawDescription, prompt: rawPrompt };
|
|
248
|
+
}
|
|
249
|
+
if (rawDescription) {
|
|
250
|
+
return { description: rawDescription, prompt: rawDescription };
|
|
251
|
+
}
|
|
252
|
+
if (rawPrompt) {
|
|
253
|
+
return {
|
|
254
|
+
description: deriveTaskDescriptionFromPrompt(rawPrompt),
|
|
255
|
+
prompt: rawPrompt,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
throw new Error('Task tool requires at least one of "description", "task", "args", or "prompt".');
|
|
259
|
+
}
|
|
206
260
|
function cloneDelegateItems(items) {
|
|
207
261
|
return items ? items.map((item) => ({ ...item })) : undefined;
|
|
208
262
|
}
|
|
@@ -248,26 +302,43 @@ function mergeRunStats(base, next) {
|
|
|
248
302
|
assistantMessages: (base?.assistantMessages ?? 0) + (next?.assistantMessages ?? 0),
|
|
249
303
|
};
|
|
250
304
|
}
|
|
251
|
-
function buildDelegationProtocolPrompt(depthRemaining, maxDelegations) {
|
|
305
|
+
function buildDelegationProtocolPrompt(depthRemaining, maxDelegations, minDelegationsPreferred = 0) {
|
|
252
306
|
if (depthRemaining <= 0) {
|
|
253
307
|
return [
|
|
254
308
|
`Delegation protocol: depth limit reached.`,
|
|
255
309
|
`Do not emit <${delegationTagName}> blocks.`,
|
|
256
310
|
].join("\n");
|
|
257
311
|
}
|
|
312
|
+
if (minDelegationsPreferred > 0) {
|
|
313
|
+
const required = Math.min(Math.max(1, minDelegationsPreferred), maxDelegations);
|
|
314
|
+
return [
|
|
315
|
+
`Delegation protocol (required for this run): emit at least ${required} XML block(s) when the assigned work still contains independent slices.`,
|
|
316
|
+
`For broad audit, implementation, or verification tasks, split by subsystem, file family, or verification stream instead of producing one monolithic answer.`,
|
|
317
|
+
`<${delegationTagName} profile="explore|plan|iosm|meta|iosm_analyst|iosm_verifier|cycle_planner|full" agent="optional custom subagent name" description="short title" cwd="optional relative path" lock_key="optional lock key" model="optional model override" isolation="none|worktree" depends_on="optional indices like 1|3">`,
|
|
318
|
+
"Detailed delegated task prompt",
|
|
319
|
+
`</${delegationTagName}>`,
|
|
320
|
+
`Keep a brief coordinator note outside the blocks, but do not collapse the full workload into one monolithic answer.`,
|
|
321
|
+
`If safe decomposition is truly impossible, output exactly one line: DELEGATION_IMPOSSIBLE: <precise reason>.`,
|
|
322
|
+
`When shared_memory tools are available, exchange intermediate state through shared_memory_write/shared_memory_read instead of repeating large context.`,
|
|
323
|
+
].join("\n");
|
|
324
|
+
}
|
|
258
325
|
return [
|
|
259
326
|
`Delegation protocol (optional): if you discover concrete independent follow-ups, emit up to ${maxDelegations} XML block(s):`,
|
|
260
|
-
`<${delegationTagName} profile="explore|plan|iosm|iosm_analyst|iosm_verifier|cycle_planner|full" agent="optional custom subagent name" description="short title" cwd="optional relative path" lock_key="optional lock key" model="optional model override" isolation="none|worktree" depends_on="optional indices like 1|3">`,
|
|
327
|
+
`<${delegationTagName} profile="explore|plan|iosm|meta|iosm_analyst|iosm_verifier|cycle_planner|full" agent="optional custom subagent name" description="short title" cwd="optional relative path" lock_key="optional lock key" model="optional model override" isolation="none|worktree" depends_on="optional indices like 1|3">`,
|
|
261
328
|
"Detailed delegated task prompt",
|
|
262
329
|
`</${delegationTagName}>`,
|
|
263
330
|
`Only emit blocks when necessary. Keep normal analysis/answer text outside those blocks.`,
|
|
264
331
|
`When shared_memory tools are available, exchange intermediate state through shared_memory_write/shared_memory_read instead of repeating large context.`,
|
|
265
332
|
].join("\n");
|
|
266
333
|
}
|
|
267
|
-
function withDelegationPrompt(basePrompt, depthRemaining, maxDelegations) {
|
|
268
|
-
const protocol = buildDelegationProtocolPrompt(depthRemaining, maxDelegations);
|
|
334
|
+
function withDelegationPrompt(basePrompt, depthRemaining, maxDelegations, minDelegationsPreferred = 0) {
|
|
335
|
+
const protocol = buildDelegationProtocolPrompt(depthRemaining, maxDelegations, minDelegationsPreferred);
|
|
269
336
|
return `${basePrompt}\n\n${protocol}`;
|
|
270
337
|
}
|
|
338
|
+
function withSubagentInstructions(basePrompt, instructions) {
|
|
339
|
+
const trimmed = instructions?.trim();
|
|
340
|
+
return trimmed ? `${basePrompt}\n\n${trimmed}` : basePrompt;
|
|
341
|
+
}
|
|
271
342
|
function buildSharedMemoryGuidance(runId, taskId) {
|
|
272
343
|
return [
|
|
273
344
|
"[SHARED_MEMORY]",
|
|
@@ -368,9 +439,9 @@ function getRunParallelLimit(cwd, runId) {
|
|
|
368
439
|
return 1;
|
|
369
440
|
const maxParallel = teamRun.maxParallel;
|
|
370
441
|
if (!Number.isInteger(maxParallel) || !maxParallel || maxParallel < 1) {
|
|
371
|
-
return Math.max(1, Math.min(teamRun.agents,
|
|
442
|
+
return Math.max(1, Math.min(teamRun.agents, MAX_ORCHESTRATION_AGENTS));
|
|
372
443
|
}
|
|
373
|
-
return Math.max(1, Math.min(maxParallel,
|
|
444
|
+
return Math.max(1, Math.min(maxParallel, MAX_ORCHESTRATION_PARALLEL));
|
|
374
445
|
}
|
|
375
446
|
function getOrCreateOrchestrationSemaphore(cwd, runId) {
|
|
376
447
|
const limit = getRunParallelLimit(cwd, runId);
|
|
@@ -587,13 +658,13 @@ export function createTaskTool(cwd, runner, options) {
|
|
|
587
658
|
label: "task",
|
|
588
659
|
description: "Launch a specialized subagent to handle a subtask in isolation. " +
|
|
589
660
|
"Use for: codebase exploration (profile=explore), architectural planning (profile=plan), " +
|
|
590
|
-
"IOSM artifact analysis (profile=iosm_analyst/iosm_verifier/cycle_planner), or end-to-end implementation (profile=full). " +
|
|
661
|
+
"IOSM artifact analysis (profile=iosm_analyst/iosm_verifier/cycle_planner), orchestration-first execution (profile=meta), or end-to-end implementation (profile=full). " +
|
|
591
662
|
"Set cwd to isolate subagents into different project areas when orchestrating parallel work. " +
|
|
592
663
|
"The subagent runs to completion and returns its full text output. " +
|
|
593
664
|
"It may request bounded follow-up delegation via <delegate_task> blocks that are executed by the parent task tool." +
|
|
594
665
|
customAgentsSnippet,
|
|
595
666
|
parameters: taskSchema,
|
|
596
|
-
execute: async (_toolCallId, { description, prompt, agent: agentName, profile, cwd: targetCwd, lock_key: lockKey, run_id: orchestrationRunId, task_id: orchestrationTaskId, model: requestedModel, background, isolation, delegate_parallel_hint: delegateParallelHint, }, _signal, onUpdate) => {
|
|
667
|
+
execute: async (_toolCallId, { description: rawDescription, task: rawTask, args: rawArgs, prompt: rawPrompt, agent: agentName, profile, cwd: targetCwd, lock_key: lockKey, run_id: orchestrationRunId, task_id: orchestrationTaskId, model: requestedModel, background, isolation, delegate_parallel_hint: delegateParallelHint, }, _signal, onUpdate) => {
|
|
597
668
|
const updateTrackedTaskStatus = (status) => {
|
|
598
669
|
if (!orchestrationRunId || !orchestrationTaskId)
|
|
599
670
|
return;
|
|
@@ -610,6 +681,12 @@ export function createTaskTool(cwd, runner, options) {
|
|
|
610
681
|
throw new Error("Operation aborted");
|
|
611
682
|
}
|
|
612
683
|
};
|
|
684
|
+
const { description, prompt } = normalizeTaskPayload({
|
|
685
|
+
description: rawDescription,
|
|
686
|
+
task: rawTask,
|
|
687
|
+
args: rawArgs,
|
|
688
|
+
prompt: rawPrompt,
|
|
689
|
+
});
|
|
613
690
|
const runId = `subagent_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
614
691
|
const sharedMemoryRunId = orchestrationRunId?.trim() || runId;
|
|
615
692
|
const sharedMemoryTaskId = orchestrationTaskId?.trim() || runId;
|
|
@@ -620,10 +697,9 @@ export function createTaskTool(cwd, runner, options) {
|
|
|
620
697
|
return options.resolveCustomSubagent(name);
|
|
621
698
|
};
|
|
622
699
|
let normalizedAgentName = agentName?.trim() || undefined;
|
|
623
|
-
let normalizedProfile = profile.trim().toLowerCase();
|
|
624
|
-
if (!normalizedProfile)
|
|
625
|
-
normalizedProfile = "full";
|
|
626
700
|
let customSubagent = resolveCustom(normalizedAgentName);
|
|
701
|
+
let normalizedProfile = profile?.trim().toLowerCase() || customSubagent?.profile?.trim().toLowerCase() || "full";
|
|
702
|
+
const normalizedHostProfile = options?.getHostProfileName?.()?.trim().toLowerCase() ?? options?.hostProfileName?.trim().toLowerCase();
|
|
627
703
|
if (normalizedAgentName && !customSubagent) {
|
|
628
704
|
const available = availableCustomNames.length > 0 ? ` Available custom agents: ${availableCustomNames.join(", ")}.` : "";
|
|
629
705
|
throw new Error(`Unknown subagent: ${normalizedAgentName}.${available}`);
|
|
@@ -650,24 +726,32 @@ export function createTaskTool(cwd, runner, options) {
|
|
|
650
726
|
}
|
|
651
727
|
const delegationDepth = maxDelegationDepthFromEnv;
|
|
652
728
|
const requestedDelegateParallelHint = typeof delegateParallelHint === "number" && Number.isInteger(delegateParallelHint)
|
|
653
|
-
? Math.max(1, Math.min(
|
|
729
|
+
? Math.max(1, Math.min(MAX_SUBAGENT_DELEGATE_PARALLEL, delegateParallelHint))
|
|
654
730
|
: undefined;
|
|
655
731
|
const autoDelegateParallelHint = requestedDelegateParallelHint === undefined
|
|
656
|
-
? deriveAutoDelegateParallelHint(normalizedAgentName, description, prompt)
|
|
732
|
+
? deriveAutoDelegateParallelHint(effectiveProfile, normalizedAgentName, normalizedHostProfile, description, prompt)
|
|
657
733
|
: undefined;
|
|
658
|
-
|
|
659
|
-
const
|
|
660
|
-
|
|
734
|
+
let effectiveDelegateParallelHint = requestedDelegateParallelHint ?? autoDelegateParallelHint;
|
|
735
|
+
const effectiveDelegationDepth = effectiveProfile === "meta" || normalizedHostProfile === "meta" || normalizedAgentName?.toLowerCase().includes("orchestrator")
|
|
736
|
+
? Math.max(delegationDepth, 2)
|
|
737
|
+
: delegationDepth;
|
|
738
|
+
let effectiveMaxDelegations = Math.max(0, Math.min(maxDelegationsPerTaskFromEnv, effectiveDelegateParallelHint ?? maxDelegationsPerTaskFromEnv));
|
|
739
|
+
let effectiveMaxDelegateParallel = Math.max(1, Math.min(maxDelegatedParallelFromEnv, effectiveDelegateParallelHint ?? maxDelegatedParallelFromEnv));
|
|
740
|
+
const preferredDelegationFloor = effectiveProfile === "meta" || normalizedHostProfile === "meta" ? 3 : 2;
|
|
741
|
+
const applyMetaDelegationFloor = requestedDelegateParallelHint === undefined &&
|
|
742
|
+
(effectiveProfile === "meta" || normalizedHostProfile === "meta");
|
|
743
|
+
if (applyMetaDelegationFloor) {
|
|
744
|
+
effectiveMaxDelegations = Math.max(effectiveMaxDelegations, Math.min(maxDelegationsPerTaskFromEnv, preferredDelegationFloor));
|
|
745
|
+
effectiveMaxDelegateParallel = Math.max(effectiveMaxDelegateParallel, Math.min(maxDelegatedParallelFromEnv, preferredDelegationFloor));
|
|
746
|
+
}
|
|
661
747
|
const minDelegationsPreferred = (effectiveDelegateParallelHint ?? 0) >= 2 && effectiveMaxDelegations >= 2
|
|
662
|
-
? Math.min(
|
|
748
|
+
? Math.min(preferredDelegationFloor, effectiveMaxDelegations, effectiveDelegateParallelHint ?? preferredDelegationFloor)
|
|
663
749
|
: 0;
|
|
664
|
-
const baseSystemPrompt = customSubagent?.systemPrompt ??
|
|
750
|
+
const baseSystemPrompt = withSubagentInstructions(customSubagent?.systemPrompt ??
|
|
665
751
|
systemPromptByProfile[effectiveProfile] ??
|
|
666
|
-
systemPromptByProfile.full;
|
|
667
|
-
const systemPrompt = withDelegationPrompt(baseSystemPrompt,
|
|
668
|
-
const promptWithInstructions =
|
|
669
|
-
? `${customSubagent.instructions.trim()}\n\nUser task:\n${prompt}`
|
|
670
|
-
: prompt;
|
|
752
|
+
systemPromptByProfile.full, customSubagent?.instructions);
|
|
753
|
+
const systemPrompt = withDelegationPrompt(baseSystemPrompt, effectiveDelegationDepth, effectiveMaxDelegations, minDelegationsPreferred);
|
|
754
|
+
const promptWithInstructions = prompt;
|
|
671
755
|
const effectiveModelOverride = requestedModel?.trim() || customSubagent?.model?.trim() || undefined;
|
|
672
756
|
const requestedBackground = background === true || customSubagent?.background === true;
|
|
673
757
|
const trackedOrchestrationRun = orchestrationRunId && orchestrationTaskId ? getTeamRun(cwd, orchestrationRunId) : undefined;
|
|
@@ -850,6 +934,7 @@ export function createTaskTool(cwd, runner, options) {
|
|
|
850
934
|
try {
|
|
851
935
|
const result = await runner({
|
|
852
936
|
systemPrompt,
|
|
937
|
+
profileName: effectiveProfile,
|
|
853
938
|
tools,
|
|
854
939
|
prompt: promptForAttempt,
|
|
855
940
|
cwd: subagentCwd,
|
|
@@ -942,7 +1027,7 @@ export function createTaskTool(cwd, runner, options) {
|
|
|
942
1027
|
output = firstPass.output;
|
|
943
1028
|
subagentSessionId = firstPass.sessionId;
|
|
944
1029
|
runStats = firstPass.stats;
|
|
945
|
-
let parsedDelegation = parseDelegationRequests(output,
|
|
1030
|
+
let parsedDelegation = parseDelegationRequests(output, effectiveDelegationDepth > 0 ? effectiveMaxDelegations : 0);
|
|
946
1031
|
if (minDelegationsPreferred > 0 && parsedDelegation.requests.length < minDelegationsPreferred) {
|
|
947
1032
|
emitProgress({
|
|
948
1033
|
kind: "subagent_progress",
|
|
@@ -965,7 +1050,7 @@ export function createTaskTool(cwd, runner, options) {
|
|
|
965
1050
|
output = secondPass.output;
|
|
966
1051
|
subagentSessionId = secondPass.sessionId ?? subagentSessionId;
|
|
967
1052
|
runStats = secondPass.stats ?? runStats;
|
|
968
|
-
parsedDelegation = parseDelegationRequests(output,
|
|
1053
|
+
parsedDelegation = parseDelegationRequests(output, effectiveDelegationDepth > 0 ? effectiveMaxDelegations : 0);
|
|
969
1054
|
}
|
|
970
1055
|
if (minDelegationsPreferred > 0 && parsedDelegation.requests.length < minDelegationsPreferred) {
|
|
971
1056
|
const impossibleReason = output.match(/^\s*DELEGATION_IMPOSSIBLE\s*:\s*(.+)$/im)?.[1]?.trim() ?? "not provided";
|
|
@@ -1037,6 +1122,128 @@ export function createTaskTool(cwd, runner, options) {
|
|
|
1037
1122
|
delegateItems,
|
|
1038
1123
|
});
|
|
1039
1124
|
};
|
|
1125
|
+
const executeNestedDelegates = async (requests, parentCwd, depthRemaining, lineage) => {
|
|
1126
|
+
if (requests.length === 0 || depthRemaining <= 0) {
|
|
1127
|
+
return { sections: [], warnings: [] };
|
|
1128
|
+
}
|
|
1129
|
+
const nestedWarnings = [];
|
|
1130
|
+
const sections = [];
|
|
1131
|
+
for (const [nestedIndex, nestedRequest] of requests.entries()) {
|
|
1132
|
+
const requestLabel = `${lineage}${nestedIndex + 1}`;
|
|
1133
|
+
const requestedNestedAgent = nestedRequest.agent?.trim() || undefined;
|
|
1134
|
+
const nestedCustomSubagent = resolveCustom(requestedNestedAgent);
|
|
1135
|
+
if (requestedNestedAgent && !nestedCustomSubagent) {
|
|
1136
|
+
nestedWarnings.push(`Nested delegated task "${nestedRequest.description}" requested unknown agent "${requestedNestedAgent}". Falling back to profile "${nestedRequest.profile}".`);
|
|
1137
|
+
}
|
|
1138
|
+
const nestedProfileRaw = nestedCustomSubagent?.profile ?? nestedRequest.profile;
|
|
1139
|
+
const normalizedNestedProfile = nestedProfileRaw.trim().toLowerCase();
|
|
1140
|
+
const nestedProfile = normalizedNestedProfile && toolsByProfile[normalizedNestedProfile]
|
|
1141
|
+
? normalizedNestedProfile
|
|
1142
|
+
: "full";
|
|
1143
|
+
const nestedProfileLabel = nestedCustomSubagent?.name
|
|
1144
|
+
? `${nestedCustomSubagent.name}/${nestedProfile}`
|
|
1145
|
+
: nestedProfile;
|
|
1146
|
+
let nestedTools = nestedCustomSubagent?.tools
|
|
1147
|
+
? [...nestedCustomSubagent.tools]
|
|
1148
|
+
: [...(toolsByProfile[nestedProfile] ?? toolsByProfile.explore)];
|
|
1149
|
+
if (nestedCustomSubagent?.disallowedTools?.length) {
|
|
1150
|
+
const blocked = new Set(nestedCustomSubagent.disallowedTools);
|
|
1151
|
+
nestedTools = nestedTools.filter((tool) => !blocked.has(tool));
|
|
1152
|
+
}
|
|
1153
|
+
const nestedBaseSystemPrompt = withSubagentInstructions(nestedCustomSubagent?.systemPrompt ??
|
|
1154
|
+
systemPromptByProfile[nestedProfile] ??
|
|
1155
|
+
systemPromptByProfile.full, nestedCustomSubagent?.instructions);
|
|
1156
|
+
const nestedSystemPrompt = withDelegationPrompt(nestedBaseSystemPrompt, Math.max(0, depthRemaining - 1), effectiveMaxDelegations);
|
|
1157
|
+
const requestedNestedCwd = nestedRequest.cwd
|
|
1158
|
+
? path.resolve(parentCwd, nestedRequest.cwd)
|
|
1159
|
+
: nestedCustomSubagent?.cwd ?? parentCwd;
|
|
1160
|
+
if (!existsSync(requestedNestedCwd) || !statSync(requestedNestedCwd).isDirectory()) {
|
|
1161
|
+
recordFailureCause("dependency_env");
|
|
1162
|
+
delegatedFailed += 1;
|
|
1163
|
+
delegatedTasks += 1;
|
|
1164
|
+
sections.push(`###### ${requestLabel}. ${nestedRequest.description} (${nestedProfileLabel})\nERROR [cause=dependency_env]: nested delegate skipped: missing cwd`);
|
|
1165
|
+
continue;
|
|
1166
|
+
}
|
|
1167
|
+
let nestedReleaseLock;
|
|
1168
|
+
let nestedReleaseIsolation;
|
|
1169
|
+
let nestedCwd = requestedNestedCwd;
|
|
1170
|
+
try {
|
|
1171
|
+
if (writeCapableProfiles.has(nestedProfile) && nestedRequest.lockKey?.trim()) {
|
|
1172
|
+
const lock = getOrCreateWriteLock(nestedRequest.lockKey.trim());
|
|
1173
|
+
nestedReleaseLock = await lock.acquire();
|
|
1174
|
+
}
|
|
1175
|
+
if (nestedRequest.isolation === "worktree") {
|
|
1176
|
+
const isolated = provisionWorktree(cwd, requestedNestedCwd, `${runId}_nested_${requestLabel.replace(/\./g, "_")}`);
|
|
1177
|
+
nestedCwd = isolated.runCwd;
|
|
1178
|
+
nestedReleaseIsolation = isolated.cleanup;
|
|
1179
|
+
}
|
|
1180
|
+
const nestedPromptWithInstructions = nestedRequest.prompt;
|
|
1181
|
+
const nestedSharedMemoryGuidance = buildSharedMemoryGuidance(sharedMemoryRunId, sharedMemoryTaskId);
|
|
1182
|
+
const nestedPrompt = `${nestedPromptWithInstructions}\n\n${nestedSharedMemoryGuidance}`;
|
|
1183
|
+
const nestedModelOverride = nestedRequest.model?.trim() || nestedCustomSubagent?.model?.trim() || undefined;
|
|
1184
|
+
const nestedSharedMemoryContext = {
|
|
1185
|
+
rootCwd: cwd,
|
|
1186
|
+
runId: sharedMemoryRunId,
|
|
1187
|
+
taskId: sharedMemoryTaskId,
|
|
1188
|
+
delegateId: requestLabel,
|
|
1189
|
+
profile: nestedProfile,
|
|
1190
|
+
};
|
|
1191
|
+
const nestedResult = await runner({
|
|
1192
|
+
systemPrompt: nestedSystemPrompt,
|
|
1193
|
+
profileName: nestedProfile,
|
|
1194
|
+
tools: nestedTools,
|
|
1195
|
+
prompt: nestedPrompt,
|
|
1196
|
+
cwd: nestedCwd,
|
|
1197
|
+
modelOverride: nestedModelOverride,
|
|
1198
|
+
sharedMemoryContext: nestedSharedMemoryContext,
|
|
1199
|
+
signal: _signal,
|
|
1200
|
+
onProgress: (progress) => {
|
|
1201
|
+
emitProgress({
|
|
1202
|
+
kind: "subagent_progress",
|
|
1203
|
+
phase: "running",
|
|
1204
|
+
message: `delegate ${requestLabel}: ${progress.message}`,
|
|
1205
|
+
cwd: progress.cwd ?? nestedCwd,
|
|
1206
|
+
activeTool: progress.activeTool,
|
|
1207
|
+
});
|
|
1208
|
+
},
|
|
1209
|
+
});
|
|
1210
|
+
throwIfAborted();
|
|
1211
|
+
let nestedOutput = typeof nestedResult === "string" ? nestedResult : nestedResult.output;
|
|
1212
|
+
const nestedStats = typeof nestedResult === "string" ? undefined : nestedResult.stats;
|
|
1213
|
+
delegatedTasks += 1;
|
|
1214
|
+
delegatedSucceeded += 1;
|
|
1215
|
+
delegatedStats.toolCallsStarted += nestedStats?.toolCallsStarted ?? 0;
|
|
1216
|
+
delegatedStats.toolCallsCompleted += nestedStats?.toolCallsCompleted ?? 0;
|
|
1217
|
+
delegatedStats.assistantMessages += nestedStats?.assistantMessages ?? 0;
|
|
1218
|
+
const parsedNestedDelegation = parseDelegationRequests(nestedOutput, depthRemaining > 1 ? effectiveMaxDelegations : 0);
|
|
1219
|
+
nestedOutput = parsedNestedDelegation.cleanedOutput;
|
|
1220
|
+
nestedWarnings.push(...parsedNestedDelegation.warnings.map((warning) => `Nested child ${requestLabel}: ${warning}`));
|
|
1221
|
+
let nestedSection = `###### ${requestLabel}. ${nestedRequest.description} (${nestedProfileLabel})\n${nestedOutput.trim() || "(no output)"}`;
|
|
1222
|
+
if (parsedNestedDelegation.requests.length > 0 && depthRemaining > 1) {
|
|
1223
|
+
const deeper = await executeNestedDelegates(parsedNestedDelegation.requests, nestedCwd, depthRemaining - 1, `${requestLabel}.`);
|
|
1224
|
+
nestedWarnings.push(...deeper.warnings);
|
|
1225
|
+
if (deeper.sections.length > 0) {
|
|
1226
|
+
nestedSection = `${nestedSection}\n\n##### Nested Delegated Subtasks\n\n${deeper.sections.join("\n\n")}`;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
sections.push(nestedSection);
|
|
1230
|
+
}
|
|
1231
|
+
catch (error) {
|
|
1232
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1233
|
+
const cause = classifyFailureCause(message);
|
|
1234
|
+
recordFailureCause(cause);
|
|
1235
|
+
delegatedTasks += 1;
|
|
1236
|
+
delegatedFailed += 1;
|
|
1237
|
+
sections.push(`###### ${requestLabel}. ${nestedRequest.description} (${nestedProfileLabel})\nERROR [cause=${cause}]: ${message}`);
|
|
1238
|
+
}
|
|
1239
|
+
finally {
|
|
1240
|
+
nestedReleaseIsolation?.();
|
|
1241
|
+
nestedReleaseLock?.();
|
|
1242
|
+
cleanupWriteLock(nestedRequest.lockKey?.trim());
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
return { sections, warnings: nestedWarnings };
|
|
1246
|
+
};
|
|
1040
1247
|
const runDelegate = async (index) => {
|
|
1041
1248
|
throwIfAborted();
|
|
1042
1249
|
const request = parsedDelegation.requests[index];
|
|
@@ -1075,10 +1282,15 @@ export function createTaskTool(cwd, runner, options) {
|
|
|
1075
1282
|
const blocked = new Set(childCustomSubagent.disallowedTools);
|
|
1076
1283
|
childTools = childTools.filter((tool) => !blocked.has(tool));
|
|
1077
1284
|
}
|
|
1078
|
-
const childBaseSystemPrompt = childCustomSubagent?.systemPrompt ??
|
|
1285
|
+
const childBaseSystemPrompt = withSubagentInstructions(childCustomSubagent?.systemPrompt ??
|
|
1079
1286
|
systemPromptByProfile[childProfile] ??
|
|
1080
|
-
systemPromptByProfile.full;
|
|
1081
|
-
const
|
|
1287
|
+
systemPromptByProfile.full, childCustomSubagent?.instructions);
|
|
1288
|
+
const childAutoDelegateParallelHint = deriveAutoDelegateParallelHint(childProfile, requestedChildAgent, normalizedHostProfile, request.description, request.prompt);
|
|
1289
|
+
const childMinDelegationsPreferred = Math.max(0, effectiveDelegationDepth - 1) > 0 &&
|
|
1290
|
+
(childAutoDelegateParallelHint ?? 0) >= 2
|
|
1291
|
+
? Math.min(preferredDelegationFloor, effectiveMaxDelegations, childAutoDelegateParallelHint ?? preferredDelegationFloor)
|
|
1292
|
+
: 0;
|
|
1293
|
+
const childSystemPrompt = withDelegationPrompt(childBaseSystemPrompt, Math.max(0, effectiveDelegationDepth - 1), effectiveMaxDelegations, childMinDelegationsPreferred);
|
|
1082
1294
|
const requestedChildCwd = request.cwd
|
|
1083
1295
|
? path.resolve(subagentCwd, request.cwd)
|
|
1084
1296
|
: childCustomSubagent?.cwd ?? subagentCwd;
|
|
@@ -1104,9 +1316,7 @@ export function createTaskTool(cwd, runner, options) {
|
|
|
1104
1316
|
childReleaseIsolation = isolated.cleanup;
|
|
1105
1317
|
}
|
|
1106
1318
|
const delegateMeta = formatMetaCheckpoint(options?.getMetaMessages?.());
|
|
1107
|
-
const childPromptWithInstructions =
|
|
1108
|
-
? `${childCustomSubagent.instructions.trim()}\n\nUser task:\n${request.prompt}`
|
|
1109
|
-
: request.prompt;
|
|
1319
|
+
const childPromptWithInstructions = request.prompt;
|
|
1110
1320
|
const delegateSharedMemoryGuidance = buildSharedMemoryGuidance(sharedMemoryRunId, sharedMemoryTaskId);
|
|
1111
1321
|
const delegatePromptBase = `${childPromptWithInstructions}\n\n${delegateSharedMemoryGuidance}`;
|
|
1112
1322
|
const delegatePrompt = delegateMeta.section && delegateMeta.appliedCount > 0
|
|
@@ -1136,108 +1346,153 @@ export function createTaskTool(cwd, runner, options) {
|
|
|
1136
1346
|
};
|
|
1137
1347
|
let childOutput = "";
|
|
1138
1348
|
let childStats;
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
attemptOutput = childResult.output;
|
|
1175
|
-
attemptStats = childResult.stats;
|
|
1176
|
-
}
|
|
1177
|
-
childStats = mergeRunStats(childStats, attemptStats);
|
|
1178
|
-
if (attemptOutput.trim().length > 0) {
|
|
1179
|
-
childOutput = attemptOutput;
|
|
1180
|
-
if (childRetrospectiveAttempt > 0) {
|
|
1181
|
-
retrospectiveRecovered += 1;
|
|
1349
|
+
const runChildPass = async (runPrompt) => {
|
|
1350
|
+
let childEmptyAttempt = 0;
|
|
1351
|
+
let childRetrospectiveAttempt = 0;
|
|
1352
|
+
let childPromptForAttempt = runPrompt;
|
|
1353
|
+
while (true) {
|
|
1354
|
+
try {
|
|
1355
|
+
const childResult = await runner({
|
|
1356
|
+
systemPrompt: childSystemPrompt,
|
|
1357
|
+
profileName: childProfile,
|
|
1358
|
+
tools: childTools,
|
|
1359
|
+
prompt: childPromptForAttempt,
|
|
1360
|
+
cwd: childCwd,
|
|
1361
|
+
modelOverride: childModelOverride,
|
|
1362
|
+
sharedMemoryContext: childSharedMemoryContext,
|
|
1363
|
+
signal: _signal,
|
|
1364
|
+
onProgress: (progress) => {
|
|
1365
|
+
emitProgress({
|
|
1366
|
+
kind: "subagent_progress",
|
|
1367
|
+
phase: "running",
|
|
1368
|
+
message: `delegate ${index + 1}/${delegateTotal}: ${progress.message}`,
|
|
1369
|
+
cwd: progress.cwd ?? childCwd,
|
|
1370
|
+
activeTool: progress.activeTool,
|
|
1371
|
+
delegateIndex: index + 1,
|
|
1372
|
+
delegateTotal,
|
|
1373
|
+
delegateDescription: request.description,
|
|
1374
|
+
delegateProfile: childProfileLabel,
|
|
1375
|
+
delegateItems,
|
|
1376
|
+
});
|
|
1377
|
+
},
|
|
1378
|
+
});
|
|
1379
|
+
throwIfAborted();
|
|
1380
|
+
let attemptOutput;
|
|
1381
|
+
let attemptStats;
|
|
1382
|
+
if (typeof childResult === "string") {
|
|
1383
|
+
attemptOutput = childResult;
|
|
1182
1384
|
}
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1385
|
+
else {
|
|
1386
|
+
attemptOutput = childResult.output;
|
|
1387
|
+
attemptStats = childResult.stats;
|
|
1388
|
+
}
|
|
1389
|
+
childStats = mergeRunStats(childStats, attemptStats);
|
|
1390
|
+
if (attemptOutput.trim().length > 0) {
|
|
1391
|
+
if (childRetrospectiveAttempt > 0) {
|
|
1392
|
+
retrospectiveRecovered += 1;
|
|
1393
|
+
}
|
|
1394
|
+
return attemptOutput;
|
|
1395
|
+
}
|
|
1396
|
+
if (childEmptyAttempt >= emptyOutputRetriesFromEnv) {
|
|
1397
|
+
const totalAttempts = childEmptyAttempt + 1;
|
|
1398
|
+
throw new Error(`delegate ${index + 1}/${delegateTotal} returned empty output after ${totalAttempts} attempt${totalAttempts === 1 ? "" : "s"}.`);
|
|
1399
|
+
}
|
|
1400
|
+
childEmptyAttempt += 1;
|
|
1401
|
+
emitProgress({
|
|
1402
|
+
kind: "subagent_progress",
|
|
1403
|
+
phase: "running",
|
|
1404
|
+
message: `delegate ${index + 1}/${delegateTotal}: empty output, retry ${childEmptyAttempt}/${emptyOutputRetriesFromEnv}`,
|
|
1405
|
+
cwd: childCwd,
|
|
1406
|
+
activeTool: undefined,
|
|
1407
|
+
delegateIndex: index + 1,
|
|
1408
|
+
delegateTotal,
|
|
1409
|
+
delegateDescription: request.description,
|
|
1410
|
+
delegateProfile: childProfileLabel,
|
|
1411
|
+
delegateItems,
|
|
1412
|
+
});
|
|
1206
1413
|
}
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1414
|
+
catch (error) {
|
|
1415
|
+
if (_signal?.aborted || isAbortError(error)) {
|
|
1416
|
+
throw new Error("Operation aborted");
|
|
1417
|
+
}
|
|
1418
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1419
|
+
const cause = classifyFailureCause(message);
|
|
1420
|
+
recordFailureCause(cause);
|
|
1421
|
+
const canRetryRetrospective = childRetrospectiveAttempt < retrospectiveRetriesFromEnv &&
|
|
1422
|
+
isRetrospectiveRetryable(cause);
|
|
1423
|
+
if (!canRetryRetrospective) {
|
|
1424
|
+
throw Object.assign(new Error(message), { failureCause: cause });
|
|
1425
|
+
}
|
|
1426
|
+
childRetrospectiveAttempt += 1;
|
|
1427
|
+
retrospectiveAttempts += 1;
|
|
1428
|
+
const directive = buildRetrospectiveDirective({
|
|
1429
|
+
cause,
|
|
1430
|
+
errorMessage: message,
|
|
1431
|
+
attempt: childRetrospectiveAttempt,
|
|
1432
|
+
target: "delegate",
|
|
1433
|
+
});
|
|
1434
|
+
childPromptForAttempt = `${runPrompt}\n\n${directive}`;
|
|
1435
|
+
emitProgress({
|
|
1436
|
+
kind: "subagent_progress",
|
|
1437
|
+
phase: "running",
|
|
1438
|
+
message: `delegate ${index + 1}/${delegateTotal}: retrospective retry ${childRetrospectiveAttempt}/${retrospectiveRetriesFromEnv} (${cause})`,
|
|
1439
|
+
cwd: childCwd,
|
|
1440
|
+
activeTool: undefined,
|
|
1441
|
+
delegateIndex: index + 1,
|
|
1442
|
+
delegateTotal,
|
|
1443
|
+
delegateDescription: request.description,
|
|
1444
|
+
delegateProfile: childProfileLabel,
|
|
1445
|
+
delegateItems,
|
|
1446
|
+
});
|
|
1214
1447
|
}
|
|
1215
|
-
childRetrospectiveAttempt += 1;
|
|
1216
|
-
retrospectiveAttempts += 1;
|
|
1217
|
-
const directive = buildRetrospectiveDirective({
|
|
1218
|
-
cause,
|
|
1219
|
-
errorMessage: message,
|
|
1220
|
-
attempt: childRetrospectiveAttempt,
|
|
1221
|
-
target: "delegate",
|
|
1222
|
-
});
|
|
1223
|
-
childPromptForAttempt = `${delegatePrompt}\n\n${directive}`;
|
|
1224
|
-
emitProgress({
|
|
1225
|
-
kind: "subagent_progress",
|
|
1226
|
-
phase: "running",
|
|
1227
|
-
message: `delegate ${index + 1}/${delegateTotal}: retrospective retry ${childRetrospectiveAttempt}/${retrospectiveRetriesFromEnv} (${cause})`,
|
|
1228
|
-
cwd: childCwd,
|
|
1229
|
-
activeTool: undefined,
|
|
1230
|
-
delegateIndex: index + 1,
|
|
1231
|
-
delegateTotal,
|
|
1232
|
-
delegateDescription: request.description,
|
|
1233
|
-
delegateProfile: childProfileLabel,
|
|
1234
|
-
delegateItems,
|
|
1235
|
-
});
|
|
1236
1448
|
}
|
|
1449
|
+
};
|
|
1450
|
+
childOutput = await runChildPass(delegatePrompt);
|
|
1451
|
+
let parsedChildDelegation = parseDelegationRequests(childOutput, effectiveDelegationDepth > 1 ? effectiveMaxDelegations : 0);
|
|
1452
|
+
if (childMinDelegationsPreferred > 0 &&
|
|
1453
|
+
parsedChildDelegation.requests.length < childMinDelegationsPreferred) {
|
|
1454
|
+
emitProgress({
|
|
1455
|
+
kind: "subagent_progress",
|
|
1456
|
+
phase: "running",
|
|
1457
|
+
message: `delegate ${index + 1}/${delegateTotal}: nested delegation preference unmet (${parsedChildDelegation.requests.length}/${childMinDelegationsPreferred}), retrying with stronger split guidance`,
|
|
1458
|
+
cwd: childCwd,
|
|
1459
|
+
activeTool: undefined,
|
|
1460
|
+
delegateIndex: index + 1,
|
|
1461
|
+
delegateTotal,
|
|
1462
|
+
delegateDescription: request.description,
|
|
1463
|
+
delegateProfile: childProfileLabel,
|
|
1464
|
+
delegateItems,
|
|
1465
|
+
});
|
|
1466
|
+
const enforcedChildPrompt = [
|
|
1467
|
+
delegatePrompt,
|
|
1468
|
+
"",
|
|
1469
|
+
"[DELEGATION_ENFORCEMENT]",
|
|
1470
|
+
`This delegated workstream must emit at least ${childMinDelegationsPreferred} <delegate_task> blocks for independent sub-work when beneficial.`,
|
|
1471
|
+
`Target parallel fan-out: up to ${Math.min(effectiveMaxDelegateParallel, effectiveMaxDelegations)}.`,
|
|
1472
|
+
"For broad audits or implementations, split by subsystem / file cluster / verification stream instead of doing everything in one pass.",
|
|
1473
|
+
"If safe decomposition is impossible, output exactly one line:",
|
|
1474
|
+
"DELEGATION_IMPOSSIBLE: <reason>",
|
|
1475
|
+
"[/DELEGATION_ENFORCEMENT]",
|
|
1476
|
+
].join("\n");
|
|
1477
|
+
childOutput = await runChildPass(enforcedChildPrompt);
|
|
1478
|
+
parsedChildDelegation = parseDelegationRequests(childOutput, effectiveDelegationDepth > 1 ? effectiveMaxDelegations : 0);
|
|
1479
|
+
}
|
|
1480
|
+
if (childMinDelegationsPreferred > 0 &&
|
|
1481
|
+
parsedChildDelegation.requests.length < childMinDelegationsPreferred) {
|
|
1482
|
+
const impossibleReason = childOutput.match(/^\s*DELEGATION_IMPOSSIBLE\s*:\s*(.+)$/im)?.[1]?.trim() ??
|
|
1483
|
+
"not provided";
|
|
1484
|
+
delegationWarnings.push(`Child ${index + 1}: delegation fallback (preferred >=${childMinDelegationsPreferred}, got ${parsedChildDelegation.requests.length}). Reason: ${impossibleReason}.`);
|
|
1237
1485
|
}
|
|
1238
|
-
const parsedChildDelegation = parseDelegationRequests(childOutput, 0);
|
|
1239
1486
|
childOutput = parsedChildDelegation.cleanedOutput;
|
|
1240
1487
|
delegationWarnings.push(...parsedChildDelegation.warnings.map((warning) => `Child ${index + 1}: ${warning}`));
|
|
1488
|
+
let nestedSection = "";
|
|
1489
|
+
if (parsedChildDelegation.requests.length > 0 && effectiveDelegationDepth > 1) {
|
|
1490
|
+
const nested = await executeNestedDelegates(parsedChildDelegation.requests, childCwd, effectiveDelegationDepth - 1, `${index + 1}.`);
|
|
1491
|
+
delegationWarnings.push(...nested.warnings);
|
|
1492
|
+
if (nested.sections.length > 0) {
|
|
1493
|
+
nestedSection = `\n\n##### Nested Delegated Subtasks\n\n${nested.sections.join("\n\n")}`;
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1241
1496
|
delegatedSucceeded += 1;
|
|
1242
1497
|
if (delegateItems[index]) {
|
|
1243
1498
|
delegateItems[index].status = "done";
|
|
@@ -1250,7 +1505,7 @@ export function createTaskTool(cwd, runner, options) {
|
|
|
1250
1505
|
? `${normalizedChildOutput.slice(0, Math.max(1, maxDelegatedOutputCharsFromEnv - 3))}...`
|
|
1251
1506
|
: normalizedChildOutput;
|
|
1252
1507
|
delegatedSections[index] =
|
|
1253
|
-
`#### ${index + 1}. ${request.description} (${childProfileLabel})\n${childOutputExcerpt}`;
|
|
1508
|
+
`#### ${index + 1}. ${request.description} (${childProfileLabel})\n${childOutputExcerpt}${nestedSection}`;
|
|
1254
1509
|
emitProgress({
|
|
1255
1510
|
kind: "subagent_progress",
|
|
1256
1511
|
phase: "running",
|