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.
Files changed (54) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +32 -5
  3. package/dist/cli/args.d.ts +1 -1
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +1 -1
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/core/agent-profiles.d.ts +1 -1
  8. package/dist/core/agent-profiles.d.ts.map +1 -1
  9. package/dist/core/agent-profiles.js +9 -0
  10. package/dist/core/agent-profiles.js.map +1 -1
  11. package/dist/core/agent-session.d.ts +7 -0
  12. package/dist/core/agent-session.d.ts.map +1 -1
  13. package/dist/core/agent-session.js +58 -13
  14. package/dist/core/agent-session.js.map +1 -1
  15. package/dist/core/extensions/types.d.ts +1 -1
  16. package/dist/core/extensions/types.d.ts.map +1 -1
  17. package/dist/core/extensions/types.js.map +1 -1
  18. package/dist/core/orchestration-limits.d.ts +6 -0
  19. package/dist/core/orchestration-limits.d.ts.map +1 -0
  20. package/dist/core/orchestration-limits.js +6 -0
  21. package/dist/core/orchestration-limits.js.map +1 -0
  22. package/dist/core/sdk.d.ts +6 -1
  23. package/dist/core/sdk.d.ts.map +1 -1
  24. package/dist/core/sdk.js +34 -6
  25. package/dist/core/sdk.js.map +1 -1
  26. package/dist/core/subagents.d.ts.map +1 -1
  27. package/dist/core/subagents.js +21 -7
  28. package/dist/core/subagents.js.map +1 -1
  29. package/dist/core/tools/edit.d.ts +8 -4
  30. package/dist/core/tools/edit.d.ts.map +1 -1
  31. package/dist/core/tools/edit.js +18 -3
  32. package/dist/core/tools/edit.js.map +1 -1
  33. package/dist/core/tools/index.d.ts +9 -7
  34. package/dist/core/tools/index.d.ts.map +1 -1
  35. package/dist/core/tools/task.d.ts +10 -3
  36. package/dist/core/tools/task.d.ts.map +1 -1
  37. package/dist/core/tools/task.js +404 -149
  38. package/dist/core/tools/task.js.map +1 -1
  39. package/dist/core/tools/todo.d.ts +10 -10
  40. package/dist/core/tools/todo.d.ts.map +1 -1
  41. package/dist/core/tools/todo.js +135 -17
  42. package/dist/core/tools/todo.js.map +1 -1
  43. package/dist/modes/interactive/components/footer.d.ts +1 -1
  44. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  45. package/dist/modes/interactive/components/footer.js +8 -8
  46. package/dist/modes/interactive/components/footer.js.map +1 -1
  47. package/dist/modes/interactive/interactive-mode.d.ts +9 -0
  48. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  49. package/dist/modes/interactive/interactive-mode.js +214 -14
  50. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  51. package/docs/cli-reference.md +4 -0
  52. package/docs/configuration.md +5 -1
  53. package/docs/interactive-mode.md +5 -1
  54. package/package.json +1 -1
@@ -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: "Short 3-5 word description of what the subagent will do",
10
- }),
11
- prompt: Type.String({
12
- description: "Full task prompt for the subagent",
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: "Subagent capability profile. Recommended values: explore, plan, iosm, iosm_analyst, iosm_verifier, cycle_planner, full. For custom agents, pass the agent name via `agent`, not `profile`.",
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: 10,
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, 20, 1, 20);
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, 3);
127
- const maxDelegationsPerTaskFromEnv = parseBoundedInt(process.env.IOSM_SUBAGENT_MAX_DELEGATIONS_PER_TASK, 10, 0, 10);
128
- const maxDelegatedParallelFromEnv = parseBoundedInt(process.env.IOSM_SUBAGENT_MAX_DELEGATE_PARALLEL, 10, 1, 10);
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 shouldAutoDelegateByAgent(agentName) {
145
- if (!agentName)
146
- return false;
147
- const normalized = agentName.trim().toLowerCase();
148
- return normalized.includes("orchestrator");
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
- if (!shouldAutoDelegateByAgent(agentName))
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, 20));
442
+ return Math.max(1, Math.min(teamRun.agents, MAX_ORCHESTRATION_AGENTS));
372
443
  }
373
- return Math.max(1, Math.min(maxParallel, 20));
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(10, delegateParallelHint))
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
- const effectiveDelegateParallelHint = requestedDelegateParallelHint ?? autoDelegateParallelHint;
659
- const effectiveMaxDelegations = Math.max(0, Math.min(maxDelegationsPerTaskFromEnv, effectiveDelegateParallelHint ?? maxDelegationsPerTaskFromEnv));
660
- const effectiveMaxDelegateParallel = Math.max(1, Math.min(maxDelegatedParallelFromEnv, effectiveDelegateParallelHint ?? maxDelegatedParallelFromEnv));
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(2, effectiveMaxDelegations)
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, delegationDepth, effectiveMaxDelegations);
668
- const promptWithInstructions = customSubagent?.instructions && customSubagent.instructions.trim().length > 0
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, delegationDepth > 0 ? effectiveMaxDelegations : 0);
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, delegationDepth > 0 ? effectiveMaxDelegations : 0);
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 childSystemPrompt = withDelegationPrompt(childBaseSystemPrompt, Math.max(0, delegationDepth - 1), effectiveMaxDelegations);
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 = childCustomSubagent?.instructions && childCustomSubagent.instructions.trim().length > 0
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
- let childEmptyAttempt = 0;
1140
- let childRetrospectiveAttempt = 0;
1141
- let childPromptForAttempt = delegatePrompt;
1142
- while (true) {
1143
- try {
1144
- const childResult = await runner({
1145
- systemPrompt: childSystemPrompt,
1146
- tools: childTools,
1147
- prompt: childPromptForAttempt,
1148
- cwd: childCwd,
1149
- modelOverride: childModelOverride,
1150
- sharedMemoryContext: childSharedMemoryContext,
1151
- signal: _signal,
1152
- onProgress: (progress) => {
1153
- emitProgress({
1154
- kind: "subagent_progress",
1155
- phase: "running",
1156
- message: `delegate ${index + 1}/${delegateTotal}: ${progress.message}`,
1157
- cwd: progress.cwd ?? childCwd,
1158
- activeTool: progress.activeTool,
1159
- delegateIndex: index + 1,
1160
- delegateTotal,
1161
- delegateDescription: request.description,
1162
- delegateProfile: childProfileLabel,
1163
- delegateItems,
1164
- });
1165
- },
1166
- });
1167
- throwIfAborted();
1168
- let attemptOutput;
1169
- let attemptStats;
1170
- if (typeof childResult === "string") {
1171
- attemptOutput = childResult;
1172
- }
1173
- else {
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
- break;
1184
- }
1185
- if (childEmptyAttempt >= emptyOutputRetriesFromEnv) {
1186
- const totalAttempts = childEmptyAttempt + 1;
1187
- throw new Error(`delegate ${index + 1}/${delegateTotal} returned empty output after ${totalAttempts} attempt${totalAttempts === 1 ? "" : "s"}.`);
1188
- }
1189
- childEmptyAttempt += 1;
1190
- emitProgress({
1191
- kind: "subagent_progress",
1192
- phase: "running",
1193
- message: `delegate ${index + 1}/${delegateTotal}: empty output, retry ${childEmptyAttempt}/${emptyOutputRetriesFromEnv}`,
1194
- cwd: childCwd,
1195
- activeTool: undefined,
1196
- delegateIndex: index + 1,
1197
- delegateTotal,
1198
- delegateDescription: request.description,
1199
- delegateProfile: childProfileLabel,
1200
- delegateItems,
1201
- });
1202
- }
1203
- catch (error) {
1204
- if (_signal?.aborted || isAbortError(error)) {
1205
- throw new Error("Operation aborted");
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
- const message = error instanceof Error ? error.message : String(error);
1208
- const cause = classifyFailureCause(message);
1209
- recordFailureCause(cause);
1210
- const canRetryRetrospective = childRetrospectiveAttempt < retrospectiveRetriesFromEnv &&
1211
- isRetrospectiveRetryable(cause);
1212
- if (!canRetryRetrospective) {
1213
- throw Object.assign(new Error(message), { failureCause: cause });
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",