@humain/terminal 0.1.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/bundle/chunks/{anthropic-messages-XULXUBIG.js → anthropic-messages-QFCEKGI3.js} +1 -1
  3. package/dist/bundle/chunks/{azure-openai-responses-JEG2XOYC.js → azure-openai-responses-G4ZMHV37.js} +1 -1
  4. package/dist/bundle/chunks/chunk-7T6XCLJF.js +22 -0
  5. package/dist/bundle/chunks/{chunk-LYAMSPR2.js → chunk-ADQYVZ6A.js} +483 -506
  6. package/dist/bundle/chunks/chunk-D6HHRSB5.js +103 -0
  7. package/dist/bundle/chunks/{chunk-JUPSJTFQ.js → chunk-K4JAZYQH.js} +1 -1
  8. package/dist/bundle/chunks/{chunk-HPPQHK3J.js → chunk-KKIB2TVR.js} +1 -1
  9. package/dist/bundle/chunks/chunk-LI2YVPWP.js +2 -0
  10. package/dist/bundle/chunks/chunk-NMPIM7MN.js +2 -0
  11. package/dist/bundle/chunks/{chunk-BZQ53VGE.js → chunk-XSR434CO.js} +1 -1
  12. package/dist/bundle/chunks/compat-CN7WN6DA.js +2 -0
  13. package/dist/bundle/chunks/dist-PRPIUY3U.js +2 -0
  14. package/dist/bundle/chunks/esm-E7UVEYTO.js +4 -0
  15. package/dist/bundle/chunks/{extract-TRW5FBY6.js → extract-CTIFQZDJ.js} +15 -14
  16. package/dist/bundle/chunks/{google-generative-ai-OX6VSCA6.js → google-generative-ai-RSUU4MWO.js} +1 -1
  17. package/dist/bundle/chunks/{google-vertex-GW76GSS2.js → google-vertex-CKQPOITO.js} +1 -1
  18. package/dist/bundle/chunks/host-WP3MVKS2.js +2 -0
  19. package/dist/bundle/chunks/{node-KOIP55YZ.js → node-NF43OPII.js} +1 -1
  20. package/dist/bundle/chunks/{node-XY3CBY3C.js → node-UMFM7CDZ.js} +1 -1
  21. package/dist/bundle/chunks/{openai-codex-responses-NPLWP6ZC.js → openai-codex-responses-S34V5ZSD.js} +1 -1
  22. package/dist/bundle/chunks/{openai-completions-OT2GTJKC.js → openai-completions-T2IGVUEI.js} +1 -1
  23. package/dist/bundle/chunks/{openai-responses-5A6CR7GI.js → openai-responses-GYZODCT3.js} +1 -1
  24. package/dist/bundle/chunks/readability-LAGF34IB.js +3 -0
  25. package/dist/bundle/chunks/turndown.cjs-HRMZVRUL.js +2 -0
  26. package/dist/bundle/cli.js +1 -1
  27. package/dist/bundle/index.js +1 -1
  28. package/dist/bundle/rpc-entry.js +1 -1
  29. package/dist/core/model-runtime.d.ts +21 -0
  30. package/dist/core/model-runtime.d.ts.map +1 -1
  31. package/dist/core/model-runtime.js +34 -4
  32. package/dist/core/model-runtime.js.map +1 -1
  33. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  34. package/dist/core/tools/file-mutation-queue.js +21 -8
  35. package/dist/core/tools/file-mutation-queue.js.map +1 -1
  36. package/dist/core/tools/subagent-env.d.ts +20 -0
  37. package/dist/core/tools/subagent-env.d.ts.map +1 -0
  38. package/dist/core/tools/subagent-env.js +47 -0
  39. package/dist/core/tools/subagent-env.js.map +1 -0
  40. package/dist/core/tools/subagent-scheduler.d.ts +36 -0
  41. package/dist/core/tools/subagent-scheduler.d.ts.map +1 -0
  42. package/dist/core/tools/subagent-scheduler.js +213 -0
  43. package/dist/core/tools/subagent-scheduler.js.map +1 -0
  44. package/dist/core/tools/subagent.d.ts +83 -3
  45. package/dist/core/tools/subagent.d.ts.map +1 -1
  46. package/dist/core/tools/subagent.js +215 -76
  47. package/dist/core/tools/subagent.js.map +1 -1
  48. package/dist/humain/mcp-adapter/vendor/CHANGELOG.md +78 -0
  49. package/dist/humain/mcp-adapter/vendor/README.md +125 -19
  50. package/dist/humain/mcp-adapter/vendor/agent-plugin-loader.ts +69 -30
  51. package/dist/humain/mcp-adapter/vendor/agent-plugin-provenance.ts +36 -0
  52. package/dist/humain/mcp-adapter/vendor/claude-plugin-loader.ts +365 -0
  53. package/dist/humain/mcp-adapter/vendor/commands.ts +13 -15
  54. package/dist/humain/mcp-adapter/vendor/config.ts +198 -30
  55. package/dist/humain/mcp-adapter/vendor/consent-manager.ts +40 -9
  56. package/dist/humain/mcp-adapter/vendor/direct-tool-surface.ts +250 -0
  57. package/dist/humain/mcp-adapter/vendor/direct-tools.ts +9 -228
  58. package/dist/humain/mcp-adapter/vendor/dist/agent-plugin-loader.js +74 -32
  59. package/dist/humain/mcp-adapter/vendor/dist/agent-plugin-loader.js.map +1 -1
  60. package/dist/humain/mcp-adapter/vendor/dist/agent-plugin-provenance.d.ts +1 -0
  61. package/dist/humain/mcp-adapter/vendor/dist/agent-plugin-provenance.js +31 -0
  62. package/dist/humain/mcp-adapter/vendor/dist/agent-plugin-provenance.js.map +1 -0
  63. package/dist/humain/mcp-adapter/vendor/dist/claude-plugin-loader.d.ts +9 -0
  64. package/dist/humain/mcp-adapter/vendor/dist/claude-plugin-loader.js +362 -0
  65. package/dist/humain/mcp-adapter/vendor/dist/claude-plugin-loader.js.map +1 -0
  66. package/dist/humain/mcp-adapter/vendor/dist/config.d.ts +3 -1
  67. package/dist/humain/mcp-adapter/vendor/dist/config.js +189 -27
  68. package/dist/humain/mcp-adapter/vendor/dist/config.js.map +1 -1
  69. package/dist/humain/mcp-adapter/vendor/dist/metadata-cache.js +7 -16
  70. package/dist/humain/mcp-adapter/vendor/dist/metadata-cache.js.map +1 -1
  71. package/dist/humain/mcp-adapter/vendor/dist/package-mcp-loader.js +5 -17
  72. package/dist/humain/mcp-adapter/vendor/dist/package-mcp-loader.js.map +1 -1
  73. package/dist/humain/mcp-adapter/vendor/dist/types.d.ts +30 -4
  74. package/dist/humain/mcp-adapter/vendor/dist/types.js +26 -0
  75. package/dist/humain/mcp-adapter/vendor/dist/types.js.map +1 -1
  76. package/dist/humain/mcp-adapter/vendor/dist/utils.d.ts +6 -0
  77. package/dist/humain/mcp-adapter/vendor/dist/utils.js +65 -6
  78. package/dist/humain/mcp-adapter/vendor/dist/utils.js.map +1 -1
  79. package/dist/humain/mcp-adapter/vendor/host-html-template.ts +20 -19
  80. package/dist/humain/mcp-adapter/vendor/http-ca.ts +84 -0
  81. package/dist/humain/mcp-adapter/vendor/index.bundle.mjs +37102 -15103
  82. package/dist/humain/mcp-adapter/vendor/index.ts +828 -164
  83. package/dist/humain/mcp-adapter/vendor/init.ts +48 -10
  84. package/dist/humain/mcp-adapter/vendor/lazy-loader.ts +7 -0
  85. package/dist/humain/mcp-adapter/vendor/mcp-auth-fetch.ts +113 -0
  86. package/dist/humain/mcp-adapter/vendor/mcp-auth-flow.ts +256 -77
  87. package/dist/humain/mcp-adapter/vendor/mcp-auth.ts +316 -54
  88. package/dist/humain/mcp-adapter/vendor/mcp-code.ts +45 -18
  89. package/dist/humain/mcp-adapter/vendor/mcp-install.ts +60 -0
  90. package/dist/humain/mcp-adapter/vendor/mcp-oauth-provider.ts +70 -6
  91. package/dist/humain/mcp-adapter/vendor/mcp-output-guard.ts +53 -42
  92. package/dist/humain/mcp-adapter/vendor/mcp-panel-theme.ts +105 -0
  93. package/dist/humain/mcp-adapter/vendor/mcp-panel.ts +366 -331
  94. package/dist/humain/mcp-adapter/vendor/mcp-references.ts +9 -14
  95. package/dist/humain/mcp-adapter/vendor/mcp-script-worker.mjs +3 -1
  96. package/dist/humain/mcp-adapter/vendor/mcp-setup-panel.ts +383 -341
  97. package/dist/humain/mcp-adapter/vendor/mcp-status.ts +5 -0
  98. package/dist/humain/mcp-adapter/vendor/metadata-cache.ts +7 -16
  99. package/dist/humain/mcp-adapter/vendor/namespace-tools.ts +61 -3
  100. package/dist/humain/mcp-adapter/vendor/oauth.ts +6 -2
  101. package/dist/humain/mcp-adapter/vendor/package-mcp-loader.ts +5 -17
  102. package/dist/humain/mcp-adapter/vendor/package.json +13 -3
  103. package/dist/humain/mcp-adapter/vendor/prompts.ts +35 -3
  104. package/dist/humain/mcp-adapter/vendor/proxy-modes.ts +108 -7
  105. package/dist/humain/mcp-adapter/vendor/runtime-owner.ts +16 -1
  106. package/dist/humain/mcp-adapter/vendor/sampling-handler.ts +9 -32
  107. package/dist/humain/mcp-adapter/vendor/sandbox-proxy-template.ts +20 -93
  108. package/dist/humain/mcp-adapter/vendor/server-manager.ts +227 -40
  109. package/dist/humain/mcp-adapter/vendor/session-approvals.ts +187 -0
  110. package/dist/humain/mcp-adapter/vendor/skills/mcp-scripting/SKILL.md +9 -1
  111. package/dist/humain/mcp-adapter/vendor/state.ts +10 -1
  112. package/dist/humain/mcp-adapter/vendor/tool-approval.ts +10 -22
  113. package/dist/humain/mcp-adapter/vendor/tool-metadata.ts +14 -0
  114. package/dist/humain/mcp-adapter/vendor/types.ts +57 -4
  115. package/dist/humain/mcp-adapter/vendor/ui-server.ts +36 -28
  116. package/dist/humain/mcp-adapter/vendor/utils.ts +64 -6
  117. package/dist/humain/web-access/vendor/abortable.d.ts +3 -0
  118. package/dist/humain/web-access/vendor/abortable.d.ts.map +1 -0
  119. package/dist/humain/web-access/vendor/abortable.js +22 -0
  120. package/dist/humain/web-access/vendor/abortable.js.map +1 -0
  121. package/dist/humain/web-access/vendor/crawl4ai.d.ts +9 -0
  122. package/dist/humain/web-access/vendor/crawl4ai.d.ts.map +1 -0
  123. package/dist/humain/web-access/vendor/crawl4ai.js +187 -0
  124. package/dist/humain/web-access/vendor/crawl4ai.js.map +1 -0
  125. package/dist/humain/web-access/vendor/credential-source.d.ts.map +1 -1
  126. package/dist/humain/web-access/vendor/credential-source.js +1 -0
  127. package/dist/humain/web-access/vendor/credential-source.js.map +1 -1
  128. package/dist/humain/web-access/vendor/curator-page.d.ts.map +1 -1
  129. package/dist/humain/web-access/vendor/curator-page.js +20 -2
  130. package/dist/humain/web-access/vendor/curator-page.js.map +1 -1
  131. package/dist/humain/web-access/vendor/curator-run.d.ts +11 -0
  132. package/dist/humain/web-access/vendor/curator-run.d.ts.map +1 -0
  133. package/dist/humain/web-access/vendor/curator-run.js +38 -0
  134. package/dist/humain/web-access/vendor/curator-run.js.map +1 -0
  135. package/dist/humain/web-access/vendor/curator-server.d.ts +1 -0
  136. package/dist/humain/web-access/vendor/curator-server.d.ts.map +1 -1
  137. package/dist/humain/web-access/vendor/curator-server.js +2 -0
  138. package/dist/humain/web-access/vendor/curator-server.js.map +1 -1
  139. package/dist/humain/web-access/vendor/duckduckgo.d.ts.map +1 -1
  140. package/dist/humain/web-access/vendor/duckduckgo.js +1 -1
  141. package/dist/humain/web-access/vendor/duckduckgo.js.map +1 -1
  142. package/dist/humain/web-access/vendor/extract.d.ts +1 -1
  143. package/dist/humain/web-access/vendor/extract.d.ts.map +1 -1
  144. package/dist/humain/web-access/vendor/extract.js +58 -11
  145. package/dist/humain/web-access/vendor/extract.js.map +1 -1
  146. package/dist/humain/web-access/vendor/fetch-params.js +1 -1
  147. package/dist/humain/web-access/vendor/fetch-params.js.map +1 -1
  148. package/dist/humain/web-access/vendor/gemini-search.d.ts +2 -2
  149. package/dist/humain/web-access/vendor/gemini-search.d.ts.map +1 -1
  150. package/dist/humain/web-access/vendor/gemini-search.js +12 -5
  151. package/dist/humain/web-access/vendor/gemini-search.js.map +1 -1
  152. package/dist/humain/web-access/vendor/index.d.ts.map +1 -1
  153. package/dist/humain/web-access/vendor/index.js +23 -19
  154. package/dist/humain/web-access/vendor/index.js.map +1 -1
  155. package/dist/humain/web-access/vendor/openai-search.d.ts.map +1 -1
  156. package/dist/humain/web-access/vendor/openai-search.js +40 -14
  157. package/dist/humain/web-access/vendor/openai-search.js.map +1 -1
  158. package/dist/humain/web-access/vendor/page-query.d.ts.map +1 -1
  159. package/dist/humain/web-access/vendor/page-query.js +6 -2
  160. package/dist/humain/web-access/vendor/page-query.js.map +1 -1
  161. package/dist/humain/web-access/vendor/query-rewrite.d.ts.map +1 -1
  162. package/dist/humain/web-access/vendor/query-rewrite.js +6 -2
  163. package/dist/humain/web-access/vendor/query-rewrite.js.map +1 -1
  164. package/dist/humain/web-access/vendor/serpapi.d.ts +4 -0
  165. package/dist/humain/web-access/vendor/serpapi.d.ts.map +1 -0
  166. package/dist/humain/web-access/vendor/serpapi.js +215 -0
  167. package/dist/humain/web-access/vendor/serpapi.js.map +1 -0
  168. package/dist/humain/web-access/vendor/ssrf-protection.d.ts.map +1 -1
  169. package/dist/humain/web-access/vendor/ssrf-protection.js +5 -1
  170. package/dist/humain/web-access/vendor/ssrf-protection.js.map +1 -1
  171. package/dist/humain/web-access/vendor/storage.d.ts.map +1 -1
  172. package/dist/humain/web-access/vendor/storage.js +9 -0
  173. package/dist/humain/web-access/vendor/storage.js.map +1 -1
  174. package/dist/humain/web-access/vendor/summary-review.d.ts +1 -1
  175. package/dist/humain/web-access/vendor/summary-review.d.ts.map +1 -1
  176. package/dist/humain/web-access/vendor/summary-review.js +35 -24
  177. package/dist/humain/web-access/vendor/summary-review.js.map +1 -1
  178. package/dist/humain/web-access/vendor/utils.d.ts.map +1 -1
  179. package/dist/humain/web-access/vendor/utils.js +5 -3
  180. package/dist/humain/web-access/vendor/utils.js.map +1 -1
  181. package/dist/modes/json-event.d.ts +8 -0
  182. package/dist/modes/json-event.d.ts.map +1 -1
  183. package/dist/modes/json-event.js +17 -0
  184. package/dist/modes/json-event.js.map +1 -1
  185. package/dist/modes/print-mode.d.ts.map +1 -1
  186. package/dist/modes/print-mode.js +3 -2
  187. package/dist/modes/print-mode.js.map +1 -1
  188. package/docs/environment-variables.md +9 -0
  189. package/docs/json.md +16 -0
  190. package/npm-shrinkwrap.json +6 -6
  191. package/package.json +2 -2
  192. package/src/humain/mcp-adapter/UPSTREAM.md +56 -7
  193. package/src/humain/mcp-adapter/upstream.json +6 -4
  194. package/src/humain/mcp-adapter/vendor/CHANGELOG.md +78 -0
  195. package/src/humain/web-access/UPSTREAM.md +18 -8
  196. package/src/humain/web-access/upstream.json +6 -5
  197. package/dist/bundle/chunks/chunk-2LC3S3VV.js +0 -105
@@ -23,19 +23,13 @@ import { getMarkdownTheme } from "../../modes/interactive/theme/theme.js";
23
23
  import { parseFrontmatter } from "../../utils/frontmatter.js";
24
24
  import { withFileMutationQueue } from "./file-mutation-queue.js";
25
25
  import { shortenPath } from "./render-utils.js";
26
+ import { currentSubagentDepth, currentTaskId, maxParallelTasks, maxSubagentConcurrency, maxSubagentDepth, maxSubagentWorktrees, PARENT_TASK_ID_ENV, SUBAGENT_DEPTH_ENV, TASK_BASE_REVISION_ENV, TASK_ID_ENV, } from "./subagent-env.js";
27
+ import { runDag, validateTaskGraph } from "./subagent-scheduler.js";
28
+ export { SUBAGENT_DEPTH_ENV } from "./subagent-env.js";
26
29
  import { createSubagentWorktree, isGitWorkTree, } from "./subagent-worktree.js";
27
30
  import { wrapToolDefinition } from "./tool-definition-wrapper.js";
28
- const MAX_PARALLEL_TASKS = 8;
29
- const MAX_CONCURRENCY = 4;
30
31
  const COLLAPSED_ITEM_COUNT = 10;
31
32
  const PER_TASK_OUTPUT_CAP = 50 * 1024;
32
- /** Children must not delegate further: depth 0 = top-level agent, depth >= 1 = subagent. */
33
- export const SUBAGENT_DEPTH_ENV = "HUMAIN_TERMINAL_SUBAGENT_DEPTH";
34
- const MAX_SUBAGENT_DEPTH = 1;
35
- function currentSubagentDepth() {
36
- const raw = Number(process.env[SUBAGENT_DEPTH_ENV] ?? "0");
37
- return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 0;
38
- }
39
33
  function loadAgentsFromDir(dir, source) {
40
34
  const agents = [];
41
35
  if (!fs.existsSync(dir)) {
@@ -211,6 +205,16 @@ function formatToolCall(toolName, args, themeFg) {
211
205
  }
212
206
  }
213
207
  }
208
+ export function renderTaskWithContext(task, context) {
209
+ const parts = [`Task: ${task}`];
210
+ if (context?.files?.length)
211
+ parts.push(`Context files:\n${context.files.map((f) => `- ${f}`).join("\n")}`);
212
+ if (context?.acceptance?.length)
213
+ parts.push(`Acceptance criteria:\n${context.acceptance.map((a) => `- ${a}`).join("\n")}`);
214
+ if (context?.baseRevision)
215
+ parts.push(`Base revision: ${context.baseRevision}`);
216
+ return parts.join("\n\n");
217
+ }
214
218
  function getFinalOutput(messages) {
215
219
  for (let i = messages.length - 1; i >= 0; i--) {
216
220
  const msg = messages[i];
@@ -259,28 +263,6 @@ function getDisplayItems(messages) {
259
263
  }
260
264
  return items;
261
265
  }
262
- async function mapWithConcurrencyLimit(items, concurrency, fn, signal, onAbort) {
263
- if (items.length === 0)
264
- return [];
265
- const limit = Math.max(1, Math.min(concurrency, items.length));
266
- const results = new Array(items.length);
267
- let nextIndex = 0;
268
- const workers = new Array(limit).fill(null).map(async () => {
269
- while (true) {
270
- const current = nextIndex++;
271
- if (current >= items.length)
272
- return;
273
- if (signal?.aborted) {
274
- if (onAbort)
275
- results[current] = await onAbort(items[current], current);
276
- continue;
277
- }
278
- results[current] = await fn(items[current], current);
279
- }
280
- });
281
- await Promise.all(workers);
282
- return results;
283
- }
284
266
  async function writePromptToTempFile(agentName, prompt) {
285
267
  const tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), "humain-subagent-"));
286
268
  const safeName = agentName.replace(/[^\w.-]+/g, "_");
@@ -303,7 +285,9 @@ function getCliInvocation(args) {
303
285
  }
304
286
  return { command: "humain-terminal", args };
305
287
  }
306
- async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, isolation, step, model, parentModel, signal, onUpdate, makeDetails, toolCallId, childIndex) {
288
+ async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, isolation, step, model, parentModel, signal, onUpdate, makeDetails, toolCallId, childIndex, options) {
289
+ const childDepth = currentSubagentDepth() + 1;
290
+ const parentTaskId = currentTaskId() ?? toolCallId;
307
291
  const agent = agents.find((a) => a.name === agentName);
308
292
  if (!agent) {
309
293
  const available = agents.map((a) => `"${a.name}"`).join(", ") || "none";
@@ -311,12 +295,18 @@ async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, isolatio
311
295
  agent: agentName,
312
296
  agentSource: "unknown",
313
297
  task,
298
+ taskId: options.taskId,
299
+ parentTaskId,
300
+ depth: childDepth,
314
301
  exitCode: 1,
315
302
  messages: [],
316
303
  stderr: `Unknown agent: "${agentName}". Available agents: ${available}.`,
317
304
  usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0 },
318
305
  step,
319
306
  isolation: "shared",
307
+ thinking: options.thinking,
308
+ attempt: options.attempt,
309
+ baseRevision: options.context?.baseRevision,
320
310
  };
321
311
  }
322
312
  let worktree = null;
@@ -342,12 +332,19 @@ async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, isolatio
342
332
  }
343
333
  if (agent.tools && agent.tools.length > 0)
344
334
  args.push("--tools", agent.tools.join(","));
335
+ if (options.thinking)
336
+ args.push("--thinking", options.thinking);
345
337
  let tmpPromptDir = null;
346
338
  let tmpPromptPath = null;
347
339
  const currentResult = {
348
340
  agent: agentName,
349
341
  agentSource: agent.source,
350
342
  task,
343
+ taskId: options.taskId,
344
+ parentTaskId,
345
+ depth: childDepth,
346
+ thinking: options.thinking,
347
+ attempt: options.attempt,
351
348
  exitCode: 0,
352
349
  messages: [],
353
350
  stderr: "",
@@ -355,6 +352,7 @@ async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, isolatio
355
352
  model: model ?? `${parentModel.provider}/${parentModel.id}`,
356
353
  step,
357
354
  isolation: "shared",
355
+ baseRevision: options.context?.baseRevision,
358
356
  };
359
357
  const emitUpdate = () => {
360
358
  if (onUpdate) {
@@ -386,14 +384,24 @@ async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, isolatio
386
384
  tmpPromptPath = tmp.filePath;
387
385
  args.push("--append-system-prompt", tmpPromptPath);
388
386
  }
389
- args.push(`Task: ${task}`);
387
+ args.push(renderTaskWithContext(task, options.context));
390
388
  const exitCode = await new Promise((resolve) => {
391
389
  const invocation = getCliInvocation(args);
390
+ const env = {
391
+ ...process.env,
392
+ [SUBAGENT_DEPTH_ENV]: String(childDepth),
393
+ [TASK_ID_ENV]: options.taskId,
394
+ [PARENT_TASK_ID_ENV]: parentTaskId,
395
+ };
396
+ if (options.context?.baseRevision)
397
+ env[TASK_BASE_REVISION_ENV] = options.context.baseRevision;
398
+ else
399
+ delete env[TASK_BASE_REVISION_ENV];
392
400
  const proc = spawn(invocation.command, invocation.args, {
393
401
  cwd: childCwd,
394
402
  shell: false,
395
403
  stdio: ["ignore", "pipe", "pipe"],
396
- env: { ...process.env, [SUBAGENT_DEPTH_ENV]: String(currentSubagentDepth() + 1) },
404
+ env,
397
405
  });
398
406
  let buffer = "";
399
407
  let settled = false;
@@ -549,9 +557,14 @@ async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, isolatio
549
557
  }
550
558
  return currentResult;
551
559
  }
560
+ const ThinkingLevelSchema = StringEnum(["off", "minimal", "low", "medium", "high", "xhigh", "max"], {
561
+ description: "Reasoning effort for this child. Maps to the CLI --thinking level. Defaults to the child's own default.",
562
+ });
552
563
  const TaskItem = Type.Object({
553
564
  agent: Type.String({ description: "Name of the agent to invoke" }),
554
565
  task: Type.String({ description: "Task to delegate to the agent" }),
566
+ id: Type.Optional(Type.String({ description: "Stable task id. Defaults to <toolCallId>-<index>. Referenced by dependsOn." })),
567
+ thinking: Type.Optional(ThinkingLevelSchema),
555
568
  model: Type.Optional(Type.String({
556
569
  description: "Optional canonical provider/model for this child. Defaults to the parent provider/model.",
557
570
  })),
@@ -559,6 +572,26 @@ const TaskItem = Type.Object({
559
572
  isolation: Type.Optional(StringEnum(["shared", "worktree"], {
560
573
  description: "Filesystem isolation for this child. 'worktree' runs it in a disposable git worktree and merges its changes back; the caller's policy sets this, not the model.",
561
574
  })),
575
+ dependsOn: Type.Optional(Type.Array(Type.String(), {
576
+ description: "Ids of sibling tasks that must complete successfully before this one starts.",
577
+ })),
578
+ ownerPaths: Type.Optional(Type.Array(Type.String(), {
579
+ description: "Repo-relative paths this task owns. Siblings whose ownerPaths overlap run one after another instead of in parallel.",
580
+ })),
581
+ onFailure: Type.Optional(Type.Object({
582
+ retryWith: Type.Optional(Type.Object({
583
+ model: Type.Optional(Type.String({ description: "Canonical provider/model for the retry." })),
584
+ thinking: Type.Optional(ThinkingLevelSchema),
585
+ })),
586
+ maxAttempts: Type.Optional(Type.Integer({ minimum: 1, maximum: 3, description: "Total attempts including the first. Default 1." })),
587
+ }, {
588
+ description: "Retry only this task on failure with a different model or thinking level. The caller's policy sets this.",
589
+ })),
590
+ context: Type.Optional(Type.Object({
591
+ files: Type.Optional(Type.Array(Type.String(), { description: "Files the child should read first." })),
592
+ acceptance: Type.Optional(Type.Array(Type.String(), { description: "Acceptance criteria the child must satisfy." })),
593
+ baseRevision: Type.Optional(Type.String({ description: "Git revision the packet was assembled against." })),
594
+ }, { description: "Bounded context packet for this child." })),
562
595
  });
563
596
  const ChainItem = Type.Object({
564
597
  agent: Type.String({ description: "Name of the agent to invoke" }),
@@ -566,6 +599,7 @@ const ChainItem = Type.Object({
566
599
  model: Type.Optional(Type.String({
567
600
  description: "Optional canonical provider/model for this child. Defaults to the parent provider/model.",
568
601
  })),
602
+ thinking: Type.Optional(ThinkingLevelSchema),
569
603
  cwd: Type.Optional(Type.String({ description: "Working directory for the agent process" })),
570
604
  });
571
605
  const AgentScopeSchema = StringEnum(["user", "project", "both"], {
@@ -578,6 +612,7 @@ const SubagentParams = Type.Object({
578
612
  model: Type.Optional(Type.String({
579
613
  description: "Optional canonical provider/model for this child. Defaults to the parent provider/model.",
580
614
  })),
615
+ thinking: Type.Optional(ThinkingLevelSchema),
581
616
  tasks: Type.Optional(Type.Array(TaskItem, { description: "Array of {agent, task} for parallel execution" })),
582
617
  chain: Type.Optional(Type.Array(ChainItem, { description: "Array of {agent, task} for sequential execution" })),
583
618
  agentScope: Type.Optional(AgentScopeSchema),
@@ -594,13 +629,14 @@ export function createSubagentToolDefinition(cwd) {
594
629
  description: [
595
630
  "Delegate tasks to specialized subagents with isolated context.",
596
631
  "Modes: single (agent + task), parallel (tasks array), chain (sequential with {previous} placeholder).",
597
- "The optional isolation field is policy-set for each child.",
632
+ "Parallel tasks may declare id, dependsOn (start after those ids succeed), ownerPaths (overlapping siblings run one at a time), thinking (reasoning level), context (files, acceptance criteria, base revision) and onFailure (retry this task with another model or thinking level).",
633
+ "The optional isolation, thinking and onFailure fields are policy-set for each child.",
598
634
  `Default agent scope is "user" (from ${path.join(getAgentDir(), "agents")}).`,
599
635
  `To enable project-local agents in ${CONFIG_DIR_NAME}/agents, set agentScope: "both" (or "project").`,
600
636
  ].join(" "),
601
637
  promptSnippet: "subagent: delegate work to isolated subagents — single task, parallel batch (tasks array), or sequential chain with {previous} placeholder",
602
638
  promptGuidelines: [
603
- "Use subagent to fan out independent work in parallel (tasks array) or run multi-step pipelines (chain). Subagents run with isolated context and cannot delegate further.",
639
+ "Use subagent to fan out independent work in parallel (tasks array) or run multi-step pipelines (chain). Subagents run with isolated context. Nested delegation is allowed only up to the configured depth; a child at the limit must complete its task directly.",
604
640
  ],
605
641
  parameters: SubagentParams,
606
642
  async execute(toolCallId, params, signal, onUpdate, ctx) {
@@ -615,17 +651,29 @@ export function createSubagentToolDefinition(cwd) {
615
651
  // the tool args and reconcile it against results by index.
616
652
  const requestedChildren = () => {
617
653
  if (params.tasks && params.tasks.length > 0)
618
- return params.tasks.map((t) => ({ agent: t.agent, task: t.task }));
654
+ return params.tasks.map((t, i) => ({
655
+ agent: t.agent,
656
+ task: t.task,
657
+ taskId: t.id ?? `${toolCallId}-${i}`,
658
+ }));
619
659
  if (params.chain && params.chain.length > 0)
620
- return params.chain.map((s, i) => ({ agent: s.agent, task: s.task, step: i + 1 }));
660
+ return params.chain.map((s, i) => ({
661
+ agent: s.agent,
662
+ task: s.task,
663
+ step: i + 1,
664
+ taskId: `${toolCallId}-${i}`,
665
+ }));
621
666
  if (params.agent)
622
- return [{ agent: params.agent, task: params.task }];
667
+ return [{ agent: params.agent, task: params.task, taskId: `${toolCallId}-0` }];
623
668
  return [];
624
669
  };
625
670
  const stubResult = (child, errorMessage) => ({
626
671
  agent: child.agent,
627
672
  agentSource: "unknown",
628
673
  task: child.task ?? "",
674
+ taskId: child.taskId ?? `${toolCallId}-${child.step !== undefined ? child.step - 1 : 0}`,
675
+ parentTaskId: currentTaskId() ?? toolCallId,
676
+ depth: currentSubagentDepth() + 1,
629
677
  exitCode: 1,
630
678
  messages: [],
631
679
  stderr: "",
@@ -641,12 +689,12 @@ export function createSubagentToolDefinition(cwd) {
641
689
  projectAgentsDir: discovery.projectAgentsDir,
642
690
  results,
643
691
  });
644
- if (currentSubagentDepth() >= MAX_SUBAGENT_DEPTH) {
692
+ if (currentSubagentDepth() >= maxSubagentDepth()) {
645
693
  return {
646
694
  content: [
647
695
  {
648
696
  type: "text",
649
- text: "Nested delegation is not allowed: this process is already a subagent. Complete the task directly.",
697
+ text: `Nested delegation limit reached (depth ${currentSubagentDepth()} of ${maxSubagentDepth()}). Complete the task directly.`,
650
698
  },
651
699
  ],
652
700
  details: makeDetails("single")([]),
@@ -725,7 +773,7 @@ export function createSubagentToolDefinition(cwd) {
725
773
  }
726
774
  }
727
775
  : undefined;
728
- const result = await runSingleAgent(defaultCwd, agents, step.agent, taskWithContext, step.cwd, undefined, i + 1, step.model, parentModel, signal, chainUpdate, makeDetails("chain"), toolCallId, i);
776
+ const result = await runSingleAgent(defaultCwd, agents, step.agent, taskWithContext, step.cwd, undefined, i + 1, step.model, parentModel, signal, chainUpdate, makeDetails("chain"), toolCallId, i, { taskId: `${toolCallId}-${i}`, thinking: step.thinking });
729
777
  results.push(result);
730
778
  const isError = isFailedResult(result);
731
779
  if (isError) {
@@ -747,32 +795,62 @@ export function createSubagentToolDefinition(cwd) {
747
795
  };
748
796
  }
749
797
  if (params.tasks && params.tasks.length > 0) {
750
- if (params.tasks.length > MAX_PARALLEL_TASKS)
798
+ const taskCap = maxParallelTasks();
799
+ if (params.tasks.length > taskCap)
751
800
  return {
752
801
  content: [
753
- {
754
- type: "text",
755
- text: `Too many parallel tasks (${params.tasks.length}). Max is ${MAX_PARALLEL_TASKS}.`,
756
- },
802
+ { type: "text", text: `Too many parallel tasks (${params.tasks.length}). Max is ${taskCap}.` },
757
803
  ],
758
- details: makeDetails("parallel")(stubAllChildren(`not run: too many parallel tasks (max ${MAX_PARALLEL_TASKS})`)),
804
+ details: makeDetails("parallel")(stubAllChildren(`not run: too many parallel tasks (max ${taskCap})`)),
759
805
  isError: true,
760
806
  };
761
- // Track all results for streaming updates
762
- const allResults = new Array(params.tasks.length);
763
- // Initialize placeholder results
764
- for (let i = 0; i < params.tasks.length; i++) {
765
- allResults[i] = {
766
- agent: params.tasks[i].agent,
767
- agentSource: "unknown",
768
- task: params.tasks[i].task,
769
- exitCode: -1, // -1 = still running
770
- messages: [],
771
- stderr: "",
772
- usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0 },
773
- isolation: "shared",
807
+ const childDepth = currentSubagentDepth() + 1;
808
+ const graph = params.tasks.map((t, i) => ({
809
+ ...t,
810
+ id: t.id ?? `${toolCallId}-${i}`,
811
+ dependsOn: t.dependsOn ?? [],
812
+ ownerPaths: t.ownerPaths ?? [],
813
+ isolation: t.isolation,
814
+ }));
815
+ const graphError = validateTaskGraph(graph);
816
+ if (graphError) {
817
+ return {
818
+ content: [{ type: "text", text: `Invalid task graph: ${graphError}.` }],
819
+ details: makeDetails("parallel")(stubAllChildren(`not run: invalid task graph (${graphError})`)),
820
+ isError: true,
774
821
  };
775
822
  }
823
+ const taskEvents = [];
824
+ const emitTaskEvent = (event) => {
825
+ taskEvents.push(event);
826
+ };
827
+ const now = () => Date.now();
828
+ for (const g of graph)
829
+ emitTaskEvent({
830
+ type: "schedule",
831
+ taskId: g.id,
832
+ dependsOn: g.dependsOn,
833
+ ownerPaths: g.ownerPaths,
834
+ baseRevision: g.context?.baseRevision,
835
+ at: now(),
836
+ });
837
+ const allResults = graph.map((g) => ({
838
+ agent: g.agent,
839
+ agentSource: "unknown",
840
+ task: g.task,
841
+ taskId: g.id,
842
+ parentTaskId: currentTaskId() ?? toolCallId,
843
+ depth: childDepth,
844
+ exitCode: -1, // -1 = still running
845
+ messages: [],
846
+ stderr: "",
847
+ usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0 },
848
+ isolation: "shared",
849
+ }));
850
+ const detailsWithEvents = (results) => ({
851
+ ...makeDetails("parallel")(results),
852
+ taskEvents: [...taskEvents],
853
+ });
776
854
  const emitParallelUpdate = () => {
777
855
  if (onUpdate) {
778
856
  const running = allResults.filter((r) => r.exitCode === -1).length;
@@ -781,23 +859,84 @@ export function createSubagentToolDefinition(cwd) {
781
859
  content: [
782
860
  { type: "text", text: `Parallel: ${done}/${allResults.length} done, ${running} running...` },
783
861
  ],
784
- details: makeDetails("parallel")([...allResults]),
862
+ details: detailsWithEvents([...allResults]),
785
863
  });
786
864
  }
787
865
  };
788
- const results = await mapWithConcurrencyLimit(params.tasks, MAX_CONCURRENCY, async (t, index) => {
789
- const result = await runSingleAgent(defaultCwd, agents, t.agent, t.task, t.cwd, t.isolation, undefined, t.model, parentModel, signal,
790
- // Per-task update callback
791
- (partial) => {
792
- if (partial.details?.results[0]) {
793
- allResults[index] = partial.details.results[0];
794
- emitParallelUpdate();
866
+ let results;
867
+ try {
868
+ results = await runDag(graph, { concurrency: maxSubagentConcurrency(), worktrees: maxSubagentWorktrees() }, async (g, index) => {
869
+ const maxAttempts = Math.min(3, Math.max(1, g.onFailure?.maxAttempts ?? 1));
870
+ let attempt = 1;
871
+ let model = g.model;
872
+ let thinking = g.thinking;
873
+ let result;
874
+ for (;;) {
875
+ emitTaskEvent({ type: "start", taskId: g.id, attempt, at: now() });
876
+ result = await runSingleAgent(defaultCwd, agents, g.agent, g.task, g.cwd, g.isolation, undefined, model, parentModel, signal, (partial) => {
877
+ if (partial.details?.results[0]) {
878
+ allResults[index] = partial.details.results[0];
879
+ emitParallelUpdate();
880
+ }
881
+ }, detailsWithEvents, toolCallId, index, { taskId: g.id, thinking, attempt, context: g.context });
882
+ if (!isFailedResult(result)) {
883
+ emitTaskEvent({ type: "complete", taskId: g.id, attempt, at: now() });
884
+ break;
885
+ }
886
+ emitTaskEvent({
887
+ type: "fail",
888
+ taskId: g.id,
889
+ attempt,
890
+ reason: getResultOutput(result).slice(0, 200),
891
+ at: now(),
892
+ });
893
+ if (attempt >= maxAttempts || signal?.aborted)
894
+ break;
895
+ const next = attempt + 1;
896
+ model = g.onFailure?.retryWith?.model ?? model;
897
+ thinking = g.onFailure?.retryWith?.thinking ?? thinking;
898
+ emitTaskEvent({
899
+ type: "escalate",
900
+ taskId: g.id,
901
+ fromAttempt: attempt,
902
+ toAttempt: next,
903
+ model: model ?? `${parentModel.provider}/${parentModel.id}`,
904
+ thinking,
905
+ at: now(),
906
+ });
907
+ attempt = next;
795
908
  }
796
- }, makeDetails("parallel"), toolCallId, index);
797
- allResults[index] = result;
798
- emitParallelUpdate();
799
- return result;
800
- }, signal, (t) => stubResult({ agent: t.agent, task: t.task }, "not run: subagent was aborted"));
909
+ allResults[index] = result;
910
+ emitParallelUpdate();
911
+ return result;
912
+ }, isFailedResult, (g, index, failedDependency) => {
913
+ const stub = stubResult({ agent: g.agent, task: g.task, taskId: g.id }, `not run: dependency "${failedDependency}" failed`);
914
+ allResults[index] = stub;
915
+ emitParallelUpdate();
916
+ return stub;
917
+ }, (event) => {
918
+ if (event.type === "wait")
919
+ emitTaskEvent({ type: "wait", taskId: event.id, reason: "concurrency", at: now() });
920
+ else if (event.type === "serialize")
921
+ emitTaskEvent({ type: "serialize", taskId: event.id, after: event.after, at: now() });
922
+ else if (event.type === "skip")
923
+ emitTaskEvent({
924
+ type: "skip",
925
+ taskId: event.id,
926
+ failedDependency: event.failedDependency,
927
+ at: now(),
928
+ });
929
+ // "start" is emitted inside run() together with the attempt number.
930
+ }, signal, (g) => stubResult({ agent: g.agent, task: g.task, taskId: g.id }, "not run: subagent was aborted"));
931
+ }
932
+ catch (err) {
933
+ const message = err instanceof Error ? err.message : String(err);
934
+ return {
935
+ content: [{ type: "text", text: `Parallel scheduling failed: ${message}` }],
936
+ details: detailsWithEvents(stubAllChildren(`not run: scheduling failed (${message})`)),
937
+ isError: true,
938
+ };
939
+ }
801
940
  const successCount = results.filter((r) => !isFailedResult(r)).length;
802
941
  const summaries = results.map((r) => {
803
942
  const output = truncateParallelOutput(getResultOutput(r));
@@ -813,12 +952,12 @@ export function createSubagentToolDefinition(cwd) {
813
952
  text: `Parallel: ${successCount}/${results.length} succeeded\n\n${summaries.join("\n\n---\n\n")}`,
814
953
  },
815
954
  ],
816
- details: makeDetails("parallel")(results),
955
+ details: detailsWithEvents(results),
817
956
  isError: successCount !== results.length,
818
957
  };
819
958
  }
820
959
  if (params.agent && params.task) {
821
- const result = await runSingleAgent(defaultCwd, agents, params.agent, params.task, params.cwd, params.isolation, undefined, params.model, parentModel, signal, onUpdate, makeDetails("single"), toolCallId, 0);
960
+ const result = await runSingleAgent(defaultCwd, agents, params.agent, params.task, params.cwd, params.isolation, undefined, params.model, parentModel, signal, onUpdate, makeDetails("single"), toolCallId, 0, { taskId: `${toolCallId}-0`, thinking: params.thinking });
822
961
  const isError = isFailedResult(result);
823
962
  if (isError) {
824
963
  const errorMsg = getResultOutput(result);