@bastani/atomic 0.9.26 → 0.9.27-alpha.1

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 (82) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +6 -0
  3. package/dist/builtin/intercom/index.bundle.mjs +21 -6
  4. package/dist/builtin/intercom/package.json +1 -1
  5. package/dist/builtin/mcp/package.json +1 -1
  6. package/dist/builtin/subagents/CHANGELOG.md +8 -0
  7. package/dist/builtin/subagents/package.json +1 -1
  8. package/dist/builtin/subagents/src/extension/index.bundle.mjs +693 -642
  9. package/dist/builtin/web-access/package.json +1 -1
  10. package/dist/builtin/workflows/CHANGELOG.md +8 -0
  11. package/dist/builtin/workflows/package.json +1 -1
  12. package/dist/builtin/workflows/src/extension/index.bundle.mjs +71 -20
  13. package/dist/builtin/workflows/src/index.js +31 -10
  14. package/dist/codemode-worker.js +3309 -0
  15. package/dist/config.d.ts +7 -1
  16. package/dist/config.d.ts.map +1 -1
  17. package/dist/config.js +38 -12
  18. package/dist/config.js.map +1 -1
  19. package/dist/core/agent-session-custom-message-commit.d.ts.map +1 -1
  20. package/dist/core/agent-session-custom-message-commit.js +6 -0
  21. package/dist/core/agent-session-custom-message-commit.js.map +1 -1
  22. package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
  23. package/dist/core/agent-session-extension-bindings.js +22 -6
  24. package/dist/core/agent-session-extension-bindings.js.map +1 -1
  25. package/dist/core/agent-session-services.d.ts.map +1 -1
  26. package/dist/core/agent-session-services.js +3 -1
  27. package/dist/core/agent-session-services.js.map +1 -1
  28. package/dist/core/agent-session-tasks.js +9 -4
  29. package/dist/core/agent-session-tasks.js.map +1 -1
  30. package/dist/core/auth-storage-backends.d.ts +3 -0
  31. package/dist/core/auth-storage-backends.d.ts.map +1 -1
  32. package/dist/core/auth-storage-backends.js +45 -1
  33. package/dist/core/auth-storage-backends.js.map +1 -1
  34. package/dist/core/auth-storage.d.ts.map +1 -1
  35. package/dist/core/auth-storage.js +14 -0
  36. package/dist/core/auth-storage.js.map +1 -1
  37. package/dist/core/extensions/provider-types.d.ts +3 -2
  38. package/dist/core/extensions/provider-types.d.ts.map +1 -1
  39. package/dist/core/extensions/provider-types.js.map +1 -1
  40. package/dist/core/extensions/runner-events.d.ts.map +1 -1
  41. package/dist/core/extensions/runner-events.js +10 -1
  42. package/dist/core/extensions/runner-events.js.map +1 -1
  43. package/dist/extensions/codemode/execute.d.ts.map +1 -1
  44. package/dist/extensions/codemode/execute.js +40 -6
  45. package/dist/extensions/codemode/execute.js.map +1 -1
  46. package/dist/extensions/codemode/tool.d.ts.map +1 -1
  47. package/dist/extensions/codemode/tool.js +1 -0
  48. package/dist/extensions/codemode/tool.js.map +1 -1
  49. package/dist/extensions/mcp/tools.d.ts.map +1 -1
  50. package/dist/extensions/mcp/tools.js +4 -9
  51. package/dist/extensions/mcp/tools.js.map +1 -1
  52. package/dist/index.d.ts +1 -0
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +1 -0
  55. package/dist/index.js.map +1 -1
  56. package/dist/modes/interactive/interactive-agent-events.js +21 -0
  57. package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
  58. package/dist/modes/interactive/interactive-mode-base.d.ts +1 -0
  59. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  60. package/dist/modes/interactive/interactive-mode-base.js +1 -0
  61. package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
  62. package/dist/modes/interactive/interactive-mode-helpers.js +1 -1
  63. package/dist/modes/interactive/interactive-mode-helpers.js.map +1 -1
  64. package/dist/modes/interactive/interactive-mode-surface.d.ts +1 -0
  65. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  66. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  67. package/dist/modes/interactive/interactive-process-lifecycle.js +7 -4
  68. package/dist/modes/interactive/interactive-process-lifecycle.js.map +1 -1
  69. package/dist/utils/output-files.d.ts +7 -0
  70. package/dist/utils/output-files.d.ts.map +1 -0
  71. package/dist/utils/output-files.js +20 -0
  72. package/dist/utils/output-files.js.map +1 -0
  73. package/docs/codemode.md +3 -3
  74. package/docs/extensions/api-reference.md +3 -1
  75. package/docs/extensions.md +2 -0
  76. package/docs/sdk.md +2 -0
  77. package/docs/settings.md +2 -0
  78. package/docs/subagents.md +4 -0
  79. package/docs/workflows/api-reference.md +6 -2
  80. package/docs/workflows/operations.md +10 -10
  81. package/npm-shrinkwrap.json +32 -32
  82. package/package.json +6 -6
@@ -8001,6 +8001,25 @@ function presentCancelledStatus(status, cause) {
8001
8001
  }
8002
8002
 
8003
8003
  // dist/builtin/subagents/src/runs/shared/parallel-utils.ts
8004
+ function createExecutionScheduler(limit) {
8005
+ let active = 0;
8006
+ const queued = [];
8007
+ const pump = () => {
8008
+ while (active < limit && queued.length > 0) {
8009
+ const dispatch = queued.shift();
8010
+ active++;
8011
+ const release = () => {
8012
+ active--;
8013
+ pump();
8014
+ };
8015
+ dispatch().then(release, release);
8016
+ }
8017
+ };
8018
+ return (dispatch) => {
8019
+ queued.push(dispatch);
8020
+ pump();
8021
+ };
8022
+ }
8004
8023
  async function mapConcurrent(items, limit, fn) {
8005
8024
  const safeLimit = Math.max(1, Math.floor(limit) || 1);
8006
8025
  const results = new Array(items.length);
@@ -11703,6 +11722,119 @@ function resolveRequestedCwd(runtimeCwd, requestedCwd) {
11703
11722
  return requestedCwd ? path11.resolve(runtimeCwd, requestedCwd) : runtimeCwd;
11704
11723
  }
11705
11724
 
11725
+ // dist/builtin/subagents/src/runs/foreground/subagent-executor-input.ts
11726
+ function validateExecutionInput(params, agents, hasTasks, hasSingle, parallelAgentConfigs) {
11727
+ if (Number(hasTasks) + Number(hasSingle) !== 1) {
11728
+ return {
11729
+ content: [
11730
+ {
11731
+ type: "text",
11732
+ text: `Provide exactly one mode. Agents: ${agents.map((a) => a.name).join(", ") || "none"}`
11733
+ }
11734
+ ],
11735
+ isError: true,
11736
+ details: { mode: "single", results: [] }
11737
+ };
11738
+ }
11739
+ if (hasSingle) {
11740
+ const reads = params.reads;
11741
+ if (reads !== undefined && reads !== false && (!Array.isArray(reads) || reads.some((entry) => typeof entry !== "string"))) {
11742
+ return {
11743
+ content: [{ type: "text", text: "reads must be an array of file path strings or false" }],
11744
+ isError: true,
11745
+ details: { mode: "single", results: [] }
11746
+ };
11747
+ }
11748
+ }
11749
+ if (hasSingle && params.agent && !agents.find((agent) => agent.name === params.agent)) {
11750
+ return {
11751
+ content: [{ type: "text", text: `Unknown agent: ${params.agent}` }],
11752
+ isError: true,
11753
+ details: { mode: "single", results: [] }
11754
+ };
11755
+ }
11756
+ if (hasTasks && params.tasks) {
11757
+ for (let i = 0;i < params.tasks.length; i++) {
11758
+ const task = params.tasks[i];
11759
+ const agent = parallelAgentConfigs?.[i] ?? agents.find((candidate) => candidate.name === task.agent);
11760
+ if (!agent || agent.name !== task.agent) {
11761
+ return {
11762
+ content: [{ type: "text", text: `Unknown agent: ${task.agent} (task ${i + 1})` }],
11763
+ isError: true,
11764
+ details: { mode: "parallel", results: [] }
11765
+ };
11766
+ }
11767
+ }
11768
+ }
11769
+ return null;
11770
+ }
11771
+ function getRequestedModeLabel(params) {
11772
+ if ((params.tasks?.length ?? 0) > 0)
11773
+ return "parallel";
11774
+ if (params.agent)
11775
+ return "single";
11776
+ return "single";
11777
+ }
11778
+ function applyAgentDefaultContext(params, agents) {
11779
+ if (params.context !== undefined)
11780
+ return params;
11781
+ const byName = new Map(agents.map((agent) => [agent.name, agent]));
11782
+ const names = [];
11783
+ if (params.agent)
11784
+ names.push(params.agent);
11785
+ for (const task of params.tasks ?? [])
11786
+ names.push(task.agent);
11787
+ return names.some((name) => byName.get(name)?.defaultContext === "fork") ? { ...params, context: "fork" } : params;
11788
+ }
11789
+ function buildRequestedModeError(params, message) {
11790
+ return withForkContext({
11791
+ content: [{ type: "text", text: message }],
11792
+ isError: true,
11793
+ details: { mode: getRequestedModeLabel(params), results: [] }
11794
+ }, params.context);
11795
+ }
11796
+ function expandTopLevelTaskCounts(tasks) {
11797
+ const expanded = [];
11798
+ for (let taskIndex = 0;taskIndex < tasks.length; taskIndex++) {
11799
+ const task = tasks[taskIndex];
11800
+ const rawCount = task.count;
11801
+ if (rawCount !== undefined && (typeof rawCount !== "number" || !Number.isInteger(rawCount) || rawCount < 1)) {
11802
+ return { error: `tasks[${taskIndex}].count must be an integer >= 1` };
11803
+ }
11804
+ const { count: _count, ...concreteTask } = task;
11805
+ for (let repeat = 0;repeat < (rawCount ?? 1); repeat++)
11806
+ expanded.push({ ...concreteTask });
11807
+ }
11808
+ return { tasks: expanded };
11809
+ }
11810
+ function normalizeRepeatedParallelCounts(params) {
11811
+ if (!params.tasks)
11812
+ return { params };
11813
+ const expandedTasks = expandTopLevelTaskCounts(params.tasks);
11814
+ if (expandedTasks.error)
11815
+ return { error: buildRequestedModeError(params, expandedTasks.error) };
11816
+ return { params: { ...params, tasks: expandedTasks.tasks } };
11817
+ }
11818
+ function withForkContext(result, context) {
11819
+ if (context !== "fork" || !result.details)
11820
+ return result;
11821
+ return {
11822
+ ...result,
11823
+ details: {
11824
+ ...result.details,
11825
+ context: "fork"
11826
+ }
11827
+ };
11828
+ }
11829
+ function toExecutionErrorResult(params, error) {
11830
+ const message = error instanceof Error ? error.message : String(error);
11831
+ return withForkContext({
11832
+ content: [{ type: "text", text: message }],
11833
+ isError: true,
11834
+ details: { mode: getRequestedModeLabel(params), results: [] }
11835
+ }, params.context);
11836
+ }
11837
+
11706
11838
  // dist/builtin/subagents/src/runs/foreground/subagent-executor-live-update.ts
11707
11839
  var liveResultIndices = new WeakMap;
11708
11840
  function markLiveResultIndices(result, indices) {
@@ -11720,95 +11852,410 @@ function getParentAskHandoff(result) {
11720
11852
 
11721
11853
  // dist/builtin/subagents/src/runs/foreground/subagent-executor-types.ts
11722
11854
  var BURST_TASK_DISCOVERY_CWD = Symbol("burstTaskDiscoveryCwd");
11855
+ var BURST_EXECUTION_SCHEDULE = Symbol("burstExecutionSchedule");
11723
11856
  function isManagementActionsRestricted(deps) {
11724
11857
  return deps.childPolicy ? deps.childPolicy.managementActions === "restricted" : deps.allowMutatingManagementActions === false;
11725
11858
  }
11726
11859
 
11727
- // dist/builtin/subagents/src/runs/foreground/subagent-executor-burst.ts
11728
- var SHARED_FIELDS = [
11729
- "concurrency",
11730
- "worktree",
11731
- "context",
11732
- "share",
11733
- "control",
11734
- "sessionDir",
11735
- "maxOutput",
11736
- "artifacts",
11737
- "includeProgress",
11738
- "agentScope"
11739
- ];
11740
- function incompatibleField(items) {
11741
- const first = items[0].params;
11742
- for (const field of SHARED_FIELDS) {
11743
- for (let index = 1;index < items.length; index++) {
11744
- if (!isDeepStrictEqual(first[field], items[index].params[field]))
11745
- return field;
11746
- }
11747
- }
11748
- return;
11749
- }
11750
- function incompatibleFieldResult(field) {
11751
- return {
11752
- content: [
11753
- {
11754
- type: "text",
11755
- text: `Cannot coalesce sibling subagent calls: incompatible top-level field '${field}'.`
11756
- }
11757
- ],
11758
- isError: true,
11759
- details: { mode: "parallel", results: [] }
11760
- };
11860
+ // dist/builtin/subagents/src/runs/foreground/task-output-retention.ts
11861
+ import { mkdtempSync, readFileSync as readFileSync8, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "node:fs";
11862
+ import { tmpdir as tmpdir2 } from "node:os";
11863
+ import { join as join11 } from "node:path";
11864
+ var INLINE_TASK_OUTPUT_MAX_BYTES = 16 * 1024;
11865
+ var MAX_RETAINED_TASK_OUTPUTS = 256;
11866
+ var retained = new Map;
11867
+ var copyRoot;
11868
+ function retentionKey(ownerId, taskId) {
11869
+ return `${ownerId}\x00${taskId}`;
11761
11870
  }
11762
- function incompatibleWorktreeCwdResult() {
11763
- return {
11764
- content: [
11765
- {
11766
- type: "text",
11767
- text: "Cannot coalesce sibling subagent calls: incompatible top-level field 'cwd' for worktree."
11768
- }
11769
- ],
11770
- isError: true,
11771
- details: { mode: "parallel", results: [] }
11772
- };
11871
+ function fileSafe(value) {
11872
+ return value.replace(/[^A-Za-z0-9._-]+/g, "_");
11773
11873
  }
11774
- function taskExpandedLength(task) {
11775
- const count = task.count;
11776
- return typeof count === "number" && Number.isInteger(count) && count >= 1 ? count : 1;
11874
+ function privateCopyRoot() {
11875
+ if (copyRoot)
11876
+ return copyRoot;
11877
+ const root = mkdtempSync(join11(tmpdir2(), "atomic-subagent-output-"));
11878
+ copyRoot = root;
11879
+ process.once("exit", () => rmSync2(root, { recursive: true, force: true }));
11880
+ return root;
11777
11881
  }
11778
- function originatingCallCwd(item) {
11779
- return resolveRequestedCwd(item.ctx.cwd, item.params.cwd);
11882
+ function removeCopy(entry) {
11883
+ if (entry?.path)
11884
+ rmSync2(entry.path, { force: true });
11780
11885
  }
11781
- function originatingTaskCwd(callCwd, taskCwd) {
11782
- return resolveRequestedCwd(callCwd, taskCwd);
11886
+ function fullOutputText(child, text) {
11887
+ if (child.outputMode !== "file-only" || !child.outputReference)
11888
+ return text;
11889
+ try {
11890
+ return readFileSync8(child.outputReference.path, "utf-8");
11891
+ } catch {
11892
+ return text;
11893
+ }
11783
11894
  }
11784
- function topLevelSingleTask(item) {
11785
- if (!item.params.agent)
11895
+ function writePrivateCopy(ownerId, taskId, text) {
11896
+ try {
11897
+ const path = join11(privateCopyRoot(), `${fileSafe(ownerId)}-${fileSafe(taskId)}.md`);
11898
+ writeFileSync4(path, text, { encoding: "utf-8", mode: 384 });
11899
+ return path;
11900
+ } catch {
11786
11901
  return;
11787
- const callCwd = originatingCallCwd(item);
11788
- return {
11789
- agent: item.params.agent,
11790
- task: item.params.task ?? "",
11791
- cwd: originatingTaskCwd(callCwd, undefined),
11792
- [BURST_TASK_DISCOVERY_CWD]: callCwd,
11793
- ...item.params.output !== undefined ? { output: item.params.output } : {},
11794
- ...item.params.outputMode !== undefined ? { outputMode: item.params.outputMode } : {},
11795
- ...item.params.reads !== undefined ? { reads: item.params.reads } : {},
11796
- ...item.params.progress !== undefined ? { progress: item.params.progress } : {},
11797
- ...item.params.model !== undefined ? { model: item.params.model } : {},
11798
- ...item.params.modelConstraints !== undefined ? { modelConstraints: item.params.modelConstraints } : {},
11799
- ...item.params.taskNeeds !== undefined ? { taskNeeds: item.params.taskNeeds } : {},
11800
- ...item.params.skill !== undefined ? { skill: item.params.skill } : {},
11801
- ...item.params.group !== undefined ? { group: item.params.group } : {}
11802
- };
11902
+ }
11803
11903
  }
11804
- function existingParallelTask(item, task) {
11805
- const callCwd = originatingCallCwd(item);
11806
- return {
11807
- ...task,
11808
- cwd: originatingTaskCwd(callCwd, task.cwd),
11809
- [BURST_TASK_DISCOVERY_CWD]: callCwd,
11810
- ...task.group === undefined && item.params.group !== undefined ? { group: item.params.group } : {}
11811
- };
11904
+ function retainTaskOutput(child, text, ref) {
11905
+ const bytes = Buffer.from(text);
11906
+ const path = writePrivateCopy(ref.ownerId, ref.taskId, fullOutputText(child, text));
11907
+ const key = retentionKey(ref.ownerId, ref.taskId);
11908
+ removeCopy(retained.get(key));
11909
+ retained.delete(key);
11910
+ retained.set(key, {
11911
+ ...path ? { path } : {},
11912
+ ...child.outputReference ? { requestedPath: child.outputReference.path } : {},
11913
+ ...child.outputSaveError ? { saveError: child.outputSaveError } : {},
11914
+ head: new TextDecoder().decode(bytes.subarray(0, INLINE_TASK_OUTPUT_MAX_BYTES)),
11915
+ totalBytes: bytes.byteLength
11916
+ });
11917
+ while (retained.size > MAX_RETAINED_TASK_OUTPUTS) {
11918
+ const oldest = retained.keys().next().value;
11919
+ if (oldest === undefined)
11920
+ break;
11921
+ removeCopy(retained.get(oldest));
11922
+ retained.delete(oldest);
11923
+ }
11924
+ }
11925
+ function retainedTaskOutput(ownerId, taskId) {
11926
+ return retained.get(retentionKey(ownerId, taskId));
11927
+ }
11928
+
11929
+ // dist/builtin/subagents/src/runs/foreground/task-execution.ts
11930
+ function subagentTaskResultLabel(result) {
11931
+ return result.kind === "cancelled" && result.cause === "user" ? "killed (non-resumable)" : result.kind;
11932
+ }
11933
+ function subagentTaskResponseText(response) {
11934
+ const outcomes = response.kind === "parallel" ? response.slots.map((slot) => slot.outcome) : [response];
11935
+ const killed = outcomes.filter((outcome) => outcome.kind === "admitted" && outcome.observation.kind === "settled" && subagentTaskResultLabel(outcome.observation.result) === "killed (non-resumable)");
11936
+ return `${killed.length ? `${killed.length} killed. These children cannot be resumed. Underlying host response:
11937
+ ` : ""}${JSON.stringify(response)}`;
11938
+ }
11939
+ function settledOutputsFromResponse(response) {
11940
+ const outcomes = response.kind === "parallel" ? response.slots.map((slot) => slot.outcome) : [response];
11941
+ return outcomes.flatMap((outcome) => outcome.kind === "admitted" && outcome.observation.kind === "settled" ? [{ taskId: outcome.observation.taskId, result: outcome.observation.result }] : []);
11942
+ }
11943
+ function settledOutputsFromRecords(records) {
11944
+ return records.flatMap((record) => record.execution.kind === "settled" ? [{ taskId: record.ref.taskId, result: record.execution.result }] : []);
11945
+ }
11946
+ function settledOutputSection(taskId, label, output) {
11947
+ const truncated = output.shown < output.total ? `, first ${output.shown} shown${output.path ? "; read the full output file for the rest" : ""}` : "";
11948
+ const lines = [`Output of ${taskId} (${label}, ${output.total} bytes${truncated}):`];
11949
+ if (output.requestedPath)
11950
+ lines.push(`Requested output: ${output.requestedPath}`);
11951
+ if (output.saveError)
11952
+ lines.push(`Output file error: ${output.saveError}`);
11953
+ if (output.path)
11954
+ lines.push(`Full output: ${output.path}`);
11955
+ lines.push(output.head);
11956
+ return lines.join(`
11957
+ `);
11958
+ }
11959
+ async function readSettledOutput(host, settled) {
11960
+ const output = settled.result.output;
11961
+ if (!output)
11962
+ return;
11963
+ const label = subagentTaskResultLabel(settled.result);
11964
+ const kept = retainedTaskOutput(output.ownerId, output.taskId);
11965
+ if (kept) {
11966
+ return settledOutputSection(settled.taskId, label, {
11967
+ ...kept,
11968
+ shown: Math.min(kept.totalBytes, INLINE_TASK_OUTPUT_MAX_BYTES),
11969
+ total: kept.totalBytes
11970
+ });
11971
+ }
11972
+ const unavailable = (reason) => `Output of ${settled.taskId} (${label}) is unavailable: ${reason}`;
11973
+ const lease = host.resolveTask(settled.taskId);
11974
+ if (!lease.ok)
11975
+ return unavailable(lease.error.message);
11976
+ try {
11977
+ const page = await host.ownerBinding.supervisor.readTaskOutput(lease.value, {
11978
+ start: "0",
11979
+ maximumBytes: INLINE_TASK_OUTPUT_MAX_BYTES
11980
+ });
11981
+ if (!page.ok)
11982
+ return unavailable(page.error.message);
11983
+ const decoder = new TextDecoder;
11984
+ const head = page.value.chunks.map((chunk) => decoder.decode(chunk.bytes, { stream: true })).join("") + decoder.decode();
11985
+ const shown = page.value.chunks.reduce((sum, chunk) => sum + chunk.bytes.byteLength, 0);
11986
+ return settledOutputSection(settled.taskId, label, { head, shown, total: Number(output.byteCount) });
11987
+ } catch (error) {
11988
+ return unavailable(error instanceof Error ? error.message : String(error));
11989
+ }
11990
+ }
11991
+ async function settledTaskOutputText(host, settled) {
11992
+ if (!host || settled.length === 0)
11993
+ return "";
11994
+ const sections = await Promise.all(settled.map((entry) => readSettledOutput(host, entry)));
11995
+ const present = sections.filter((section) => section !== undefined);
11996
+ return present.length ? `
11997
+
11998
+ ${present.join(`
11999
+
12000
+ `)}` : "";
12001
+ }
12002
+ function taskToolResult(response, host) {
12003
+ return {
12004
+ content: [{ type: "text", text: subagentTaskResponseText(response) }],
12005
+ details: {
12006
+ mode: "single",
12007
+ results: [],
12008
+ taskResponse: response,
12009
+ taskRecords: taskResponseRecords(response, host)
12010
+ },
12011
+ ...response.kind === "unstarted" ? { isError: true } : {}
12012
+ };
12013
+ }
12014
+ async function taskToolResultWithOutput(response, host) {
12015
+ const result = taskToolResult(response, host);
12016
+ const outputText = await settledTaskOutputText(host, settledOutputsFromResponse(response));
12017
+ if (!outputText)
12018
+ return result;
12019
+ return {
12020
+ ...result,
12021
+ content: [{ type: "text", text: `${subagentTaskResponseText(response)}${outputText}` }]
12022
+ };
12023
+ }
12024
+ async function parallelTaskToolResultWithOutput(response, host) {
12025
+ return {
12026
+ content: [
12027
+ {
12028
+ type: "text",
12029
+ text: `${subagentTaskResponseText(response)}${await settledTaskOutputText(host, settledOutputsFromResponse(response))}`
12030
+ }
12031
+ ],
12032
+ details: {
12033
+ mode: "parallel",
12034
+ results: [],
12035
+ taskResponse: response,
12036
+ taskRecords: taskResponseRecords(response, host)
12037
+ }
12038
+ };
12039
+ }
12040
+ function taskResponseRecords(response, host) {
12041
+ const outcomes = response.kind === "parallel" ? response.slots.map((slot) => slot.outcome) : [response];
12042
+ const ids = new Set(outcomes.flatMap((outcome) => outcome.kind === "admitted" ? [outcome.observation.taskId] : []));
12043
+ const watched = host?.watchOwnerTasks();
12044
+ if (!watched?.ok)
12045
+ return [];
12046
+ try {
12047
+ return watched.value.snapshot.tasks.filter((task) => ids.has(task.ref.taskId));
12048
+ } finally {
12049
+ watched.value.dispose();
12050
+ }
12051
+ }
12052
+ async function runAgentTask(input) {
12053
+ let yieldWait;
12054
+ let pendingYield = false;
12055
+ let parentObserving = input.wait?.kind === "foreground";
12056
+ const registered = Promise.withResolvers();
12057
+ input.options.modelRoute?.assertCurrent();
12058
+ const started = await input.host.startAgentTask({
12059
+ kind: "agent",
12060
+ agent: input.agent,
12061
+ task: input.intentTask ?? input.task,
12062
+ cwd: input.options.cwd ?? input.cwd,
12063
+ ...input.options.modelRoute ? { routerSelection: input.options.modelRoute.routerSelection } : {}
12064
+ }, `${input.options.runId}:${input.options.index ?? 0}`, (context) => {
12065
+ const cleaned = Promise.withResolvers();
12066
+ let executionBound = false;
12067
+ const result = registered.promise.then(async () => {
12068
+ try {
12069
+ input.options.modelRoute?.assertCurrent();
12070
+ const child = await input.runtime.runSync(input.cwd, input.agents, input.agent, input.task, {
12071
+ ...input.options,
12072
+ signal: context.signal,
12073
+ taskExecution: {
12074
+ signal: context.signal,
12075
+ reportActivity: context.reportActivity,
12076
+ bindTranscript: (session) => context.bindTranscript({
12077
+ getSessionId: () => session.getSessionId(),
12078
+ getEntries: () => session.getEntries(),
12079
+ ...session.subscribe ? { subscribe: session.subscribe.bind(session) } : {},
12080
+ ...session.getStreamingMessage ? { getStreamingMessage: session.getStreamingMessage.bind(session) } : {},
12081
+ ...input.options.intercomSessionName ? {
12082
+ completionSource: {
12083
+ runId: input.options.runId,
12084
+ intercomTarget: input.options.intercomSessionName
12085
+ }
12086
+ } : {}
12087
+ }),
12088
+ onExecution: (execution) => {
12089
+ executionBound = true;
12090
+ execution.cleanup.then(cleaned.resolve, (error) => cleaned.resolve({
12091
+ kind: "failed",
12092
+ resources: [{ resource: "agent-session", code: "CleanupFailed", message: String(error) }]
12093
+ }));
12094
+ },
12095
+ yieldTaskWait: () => {
12096
+ parentObserving = false;
12097
+ if (yieldWait)
12098
+ yieldWait("intercom-coordination");
12099
+ else
12100
+ pendingYield = true;
12101
+ },
12102
+ isParentObserving: () => parentObserving
12103
+ }
12104
+ });
12105
+ if (child.model !== undefined || child.thinking !== undefined)
12106
+ context.reportActivity({
12107
+ reportId: "terminal-model",
12108
+ change: { kind: "model", model: child.model, thinking: child.thinking }
12109
+ });
12110
+ input.onTerminal?.(child);
12111
+ const text = input.outputText?.(child) ?? getSingleResultOutput(child);
12112
+ retainTaskOutput(child, text, context.ref);
12113
+ const bytes = Buffer.from(text);
12114
+ context.reportActivity({
12115
+ reportId: "terminal-output",
12116
+ change: { kind: "output", offset: "0", bytesBase64: bytes.toString("base64") }
12117
+ });
12118
+ const output = {
12119
+ ownerId: context.ref.ownerId,
12120
+ taskId: context.ref.taskId,
12121
+ artifactId: `output:${context.ref.taskId}`,
12122
+ byteCount: String(bytes.length),
12123
+ omittedRanges: []
12124
+ };
12125
+ if (child.status === "killed")
12126
+ return { kind: "cancelled", cause: "user", output };
12127
+ if (child.interrupted)
12128
+ return {
12129
+ kind: "cancelled",
12130
+ cause: input.options.interruptSignal?.aborted ? "parent-handoff" : "owner-close",
12131
+ output
12132
+ };
12133
+ return child.status === "ok" ? { kind: "completed", output } : {
12134
+ kind: "failed",
12135
+ code: "AgentFailed",
12136
+ message: child.error ?? child.envelope ?? "Agent failed",
12137
+ output
12138
+ };
12139
+ } finally {
12140
+ if (!executionBound)
12141
+ cleaned.resolve({ kind: "reaped" });
12142
+ }
12143
+ });
12144
+ return { result, cleanup: cleaned.promise };
12145
+ }, input.schedule);
12146
+ if (!started.ok)
12147
+ return { kind: "unstarted", reason: { kind: "rejected", error: started.error } };
12148
+ const yieldForIntercom = () => {
12149
+ parentObserving = false;
12150
+ if (yieldWait)
12151
+ yieldWait("intercom-coordination");
12152
+ else
12153
+ pendingYield = true;
12154
+ };
12155
+ input.options.intercomDetachSignal?.addEventListener("abort", yieldForIntercom, { once: true });
12156
+ if (input.options.intercomDetachSignal?.aborted)
12157
+ yieldForIntercom();
12158
+ const observation = input.host.observeAgentLaunch(started.value.taskId, input.wait, (yieldRegistered) => {
12159
+ yieldWait = yieldRegistered;
12160
+ if (pendingYield)
12161
+ yieldRegistered("intercom-coordination");
12162
+ registered.resolve();
12163
+ });
12164
+ registered.resolve();
12165
+ const observed = await observation.finally(() => {
12166
+ parentObserving = false;
12167
+ input.options.intercomDetachSignal?.removeEventListener("abort", yieldForIntercom);
12168
+ });
12169
+ if (!observed.ok)
12170
+ throw new Error(`${observed.error.code}: ${observed.error.message}`);
12171
+ return { kind: "admitted", observation: observed.value };
12172
+ }
12173
+
12174
+ // dist/builtin/subagents/src/runs/foreground/subagent-executor-burst.ts
12175
+ var SHARED_FIELDS = [
12176
+ "concurrency",
12177
+ "worktree",
12178
+ "context",
12179
+ "share",
12180
+ "control",
12181
+ "sessionDir",
12182
+ "maxOutput",
12183
+ "artifacts",
12184
+ "includeProgress",
12185
+ "agentScope"
12186
+ ];
12187
+ function incompatibleField(items) {
12188
+ const first = items[0].params;
12189
+ for (const field of SHARED_FIELDS) {
12190
+ for (let index = 1;index < items.length; index++) {
12191
+ if (!isDeepStrictEqual(first[field], items[index].params[field]))
12192
+ return field;
12193
+ }
12194
+ }
12195
+ return;
12196
+ }
12197
+ function incompatibleFieldResult(field) {
12198
+ return {
12199
+ content: [
12200
+ {
12201
+ type: "text",
12202
+ text: `Cannot coalesce sibling subagent calls: incompatible top-level field '${field}'.`
12203
+ }
12204
+ ],
12205
+ isError: true,
12206
+ details: { mode: "parallel", results: [] }
12207
+ };
12208
+ }
12209
+ function incompatibleWorktreeCwdResult() {
12210
+ return {
12211
+ content: [
12212
+ {
12213
+ type: "text",
12214
+ text: "Cannot coalesce sibling subagent calls: incompatible top-level field 'cwd' for worktree."
12215
+ }
12216
+ ],
12217
+ isError: true,
12218
+ details: { mode: "parallel", results: [] }
12219
+ };
12220
+ }
12221
+ function taskExpandedLength(task) {
12222
+ const count = task.count;
12223
+ return typeof count === "number" && Number.isInteger(count) && count >= 1 ? count : 1;
12224
+ }
12225
+ function originatingCallCwd(item) {
12226
+ return resolveRequestedCwd(item.ctx.cwd, item.params.cwd);
12227
+ }
12228
+ function originatingTaskCwd(callCwd, taskCwd) {
12229
+ return resolveRequestedCwd(callCwd, taskCwd);
12230
+ }
12231
+ function topLevelSingleTask(item) {
12232
+ if (!item.params.agent)
12233
+ return;
12234
+ const callCwd = originatingCallCwd(item);
12235
+ return {
12236
+ agent: item.params.agent,
12237
+ task: item.params.task ?? "",
12238
+ cwd: originatingTaskCwd(callCwd, undefined),
12239
+ [BURST_TASK_DISCOVERY_CWD]: callCwd,
12240
+ ...item.params.output !== undefined ? { output: item.params.output } : {},
12241
+ ...item.params.outputMode !== undefined ? { outputMode: item.params.outputMode } : {},
12242
+ ...item.params.reads !== undefined ? { reads: item.params.reads } : {},
12243
+ ...item.params.progress !== undefined ? { progress: item.params.progress } : {},
12244
+ ...item.params.model !== undefined ? { model: item.params.model } : {},
12245
+ ...item.params.modelConstraints !== undefined ? { modelConstraints: item.params.modelConstraints } : {},
12246
+ ...item.params.taskNeeds !== undefined ? { taskNeeds: item.params.taskNeeds } : {},
12247
+ ...item.params.skill !== undefined ? { skill: item.params.skill } : {},
12248
+ ...item.params.group !== undefined ? { group: item.params.group } : {}
12249
+ };
12250
+ }
12251
+ function existingParallelTask(item, task) {
12252
+ const callCwd = originatingCallCwd(item);
12253
+ return {
12254
+ ...task,
12255
+ cwd: originatingTaskCwd(callCwd, task.cwd),
12256
+ [BURST_TASK_DISCOVERY_CWD]: callCwd,
12257
+ ...task.group === undefined && item.params.group !== undefined ? { group: item.params.group } : {}
12258
+ };
11812
12259
  }
11813
12260
  function mergeBurst(items) {
11814
12261
  const field = incompatibleField(items);
@@ -11835,6 +12282,7 @@ function mergeBurst(items) {
11835
12282
  params: {
11836
12283
  ...commonCwd ? { cwd: commonCwd } : {},
11837
12284
  tasks,
12285
+ wait: first.wait,
11838
12286
  concurrency: first.concurrency,
11839
12287
  worktree: first.worktree,
11840
12288
  context: first.context,
@@ -11930,6 +12378,19 @@ function projectResult(result, route, live = false) {
11930
12378
  }
11931
12379
  };
11932
12380
  }
12381
+ function isSingleResponse(outcome) {
12382
+ return outcome.kind === "admitted" || outcome.reason.kind === "rejected";
12383
+ }
12384
+ async function projectOwnerResult(result, route, item, ctx) {
12385
+ const response = result.details?.taskResponse;
12386
+ if (response?.kind !== "parallel")
12387
+ return;
12388
+ const slots = response.slots.slice(route.start, route.start + route.length).map((slot, ordinal) => ({ ...slot, ordinal }));
12389
+ const host = ctx.getAgentTaskHost?.();
12390
+ const own = slots.length === 1 ? slots[0].outcome : undefined;
12391
+ const projected = item.params.agent !== undefined && own !== undefined && isSingleResponse(own) ? await taskToolResultWithOutput(own, host) : await parallelTaskToolResultWithOutput({ kind: "parallel", slots }, host);
12392
+ return withForkContext(projected, item.params.context ?? result.details?.context);
12393
+ }
11933
12394
  function resolveItem(item, result) {
11934
12395
  if (item.settled)
11935
12396
  return;
@@ -11965,19 +12426,30 @@ function observeLaterAbort(item) {
11965
12426
  }
11966
12427
  return cleanup;
11967
12428
  }
12429
+ function groupByWait(items) {
12430
+ const groups = [];
12431
+ for (const item of items) {
12432
+ const group = groups.find((candidate) => isDeepStrictEqual(candidate[0].params.wait, item.params.wait));
12433
+ if (group)
12434
+ group.push(item);
12435
+ else
12436
+ groups.push([item]);
12437
+ }
12438
+ return groups;
12439
+ }
11968
12440
  function createExecutionBurstDispatcher(input) {
11969
12441
  let queue = [];
11970
12442
  let flushScheduled = false;
11971
- const run = async (items) => {
12443
+ const run = async (items, finish, schedule) => {
11972
12444
  if (items.length === 1) {
11973
12445
  const item = items[0];
11974
- input.setActive(true);
12446
+ const params = schedule ? { ...item.params, [BURST_EXECUTION_SCHEDULE]: schedule } : item.params;
11975
12447
  try {
11976
- item.resolve(await input.execute(item.id, item.params, item.signal, item.onUpdate, item.ctx));
12448
+ item.resolve(await input.execute(item.id, params, item.signal, item.onUpdate, item.ctx));
11977
12449
  } catch (error) {
11978
12450
  item.reject(error instanceof Error ? error : new Error(String(error)));
11979
12451
  } finally {
11980
- input.setActive(false);
12452
+ finish();
11981
12453
  }
11982
12454
  return;
11983
12455
  }
@@ -11991,8 +12463,11 @@ function createExecutionBurstDispatcher(input) {
11991
12463
  for (const item of items)
11992
12464
  resolveItem(item, merged.error);
11993
12465
  cleanupLaterAborts();
12466
+ finish();
11994
12467
  return;
11995
12468
  }
12469
+ if (schedule)
12470
+ merged.params[BURST_EXECUTION_SCHEDULE] = schedule;
11996
12471
  const first = items[0];
11997
12472
  const display = registerBurstDisplay(items.map((item) => item.id), merged.routes.reduce((total, route) => total + route.length, 0));
11998
12473
  const onUpdate = (update) => {
@@ -12003,27 +12478,38 @@ function createExecutionBurstDispatcher(input) {
12003
12478
  item.onUpdate?.(projectResult(update, merged.routes[index], true));
12004
12479
  }
12005
12480
  };
12006
- input.setActive(true);
12007
12481
  try {
12008
12482
  const result = await input.execute(first.id, merged.params, first.signal, onUpdate, first.ctx);
12009
12483
  updateBurstDisplay(display, result);
12010
- for (let index = 0;index < items.length; index++) {
12011
- resolveItem(items[index], projectResult(result, merged.routes[index]));
12012
- }
12484
+ const projections = await Promise.all(items.map(async (item, index) => await projectOwnerResult(result, merged.routes[index], item, first.ctx) ?? projectResult(result, merged.routes[index])));
12485
+ for (let index = 0;index < items.length; index++)
12486
+ resolveItem(items[index], projections[index]);
12013
12487
  } catch (error) {
12014
12488
  const rejection = error instanceof Error ? error : new Error(String(error));
12015
12489
  for (const item of items)
12016
12490
  rejectItem(item, rejection);
12017
12491
  } finally {
12018
- input.setActive(false);
12019
12492
  cleanupLaterAborts();
12493
+ finish();
12020
12494
  }
12021
12495
  };
12496
+ const runBurst = (items) => {
12497
+ const groups = groupByWait(items);
12498
+ const schedule = groups.length > 1 ? createExecutionScheduler(Math.min(...items.map((item) => resolveTopLevelParallelConcurrency(item.params.concurrency, input.concurrencyLimit)))) : undefined;
12499
+ let remaining = groups.length;
12500
+ input.setActive(true);
12501
+ const finish = () => {
12502
+ if (--remaining === 0)
12503
+ input.setActive(false);
12504
+ };
12505
+ for (const group of groups)
12506
+ run(group, finish, schedule);
12507
+ };
12022
12508
  const flush = () => {
12023
12509
  flushScheduled = false;
12024
12510
  const items = queue;
12025
12511
  queue = [];
12026
- run(items);
12512
+ runBurst(items);
12027
12513
  };
12028
12514
  return (id, params, signal, onUpdate, ctx) => {
12029
12515
  if (input.isActive())
@@ -12162,144 +12648,31 @@ Create and maintain progress at: ${path12.join(progressDir, "progress.md")}`;
12162
12648
  }
12163
12649
  function buildTaskInstructions(behavior, cwd, isFirstProgressAgent) {
12164
12650
  const prefixParts = [];
12165
- const suffixParts = [];
12166
- const readInstruction = buildReadInstruction(behavior.reads, cwd);
12167
- if (readInstruction)
12168
- prefixParts.push(readInstruction);
12169
- if (behavior.output) {
12170
- const outputPath = path12.isAbsolute(behavior.output) ? behavior.output : path12.join(cwd, behavior.output);
12171
- prefixParts.push(`[Write to: ${outputPath}]`);
12172
- }
12173
- if (behavior.progress) {
12174
- const progressPath = path12.join(cwd, "progress.md");
12175
- if (isFirstProgressAgent)
12176
- suffixParts.push(`Create and maintain progress at: ${progressPath}`);
12177
- else
12178
- suffixParts.push(`Update progress at: ${progressPath}`);
12179
- }
12180
- const prefix = prefixParts.length > 0 ? `${prefixParts.join(`
12181
- `)}
12182
-
12183
- ` : "";
12184
- const suffix = suffixParts.length > 0 ? `
12185
-
12186
- ---
12187
- ${suffixParts.join(`
12188
- `)}` : "";
12189
- return { prefix, suffix };
12190
- }
12191
-
12192
- // dist/builtin/subagents/src/runs/foreground/subagent-executor-input.ts
12193
- function validateExecutionInput(params, agents, hasTasks, hasSingle, parallelAgentConfigs) {
12194
- if (Number(hasTasks) + Number(hasSingle) !== 1) {
12195
- return {
12196
- content: [
12197
- {
12198
- type: "text",
12199
- text: `Provide exactly one mode. Agents: ${agents.map((a) => a.name).join(", ") || "none"}`
12200
- }
12201
- ],
12202
- isError: true,
12203
- details: { mode: "single", results: [] }
12204
- };
12205
- }
12206
- if (hasSingle) {
12207
- const reads = params.reads;
12208
- if (reads !== undefined && reads !== false && (!Array.isArray(reads) || reads.some((entry) => typeof entry !== "string"))) {
12209
- return {
12210
- content: [{ type: "text", text: "reads must be an array of file path strings or false" }],
12211
- isError: true,
12212
- details: { mode: "single", results: [] }
12213
- };
12214
- }
12215
- }
12216
- if (hasSingle && params.agent && !agents.find((agent) => agent.name === params.agent)) {
12217
- return {
12218
- content: [{ type: "text", text: `Unknown agent: ${params.agent}` }],
12219
- isError: true,
12220
- details: { mode: "single", results: [] }
12221
- };
12222
- }
12223
- if (hasTasks && params.tasks) {
12224
- for (let i = 0;i < params.tasks.length; i++) {
12225
- const task = params.tasks[i];
12226
- const agent = parallelAgentConfigs?.[i] ?? agents.find((candidate) => candidate.name === task.agent);
12227
- if (!agent || agent.name !== task.agent) {
12228
- return {
12229
- content: [{ type: "text", text: `Unknown agent: ${task.agent} (task ${i + 1})` }],
12230
- isError: true,
12231
- details: { mode: "parallel", results: [] }
12232
- };
12233
- }
12234
- }
12235
- }
12236
- return null;
12237
- }
12238
- function getRequestedModeLabel(params) {
12239
- if ((params.tasks?.length ?? 0) > 0)
12240
- return "parallel";
12241
- if (params.agent)
12242
- return "single";
12243
- return "single";
12244
- }
12245
- function applyAgentDefaultContext(params, agents) {
12246
- if (params.context !== undefined)
12247
- return params;
12248
- const byName = new Map(agents.map((agent) => [agent.name, agent]));
12249
- const names = [];
12250
- if (params.agent)
12251
- names.push(params.agent);
12252
- for (const task of params.tasks ?? [])
12253
- names.push(task.agent);
12254
- return names.some((name) => byName.get(name)?.defaultContext === "fork") ? { ...params, context: "fork" } : params;
12255
- }
12256
- function buildRequestedModeError(params, message) {
12257
- return withForkContext({
12258
- content: [{ type: "text", text: message }],
12259
- isError: true,
12260
- details: { mode: getRequestedModeLabel(params), results: [] }
12261
- }, params.context);
12262
- }
12263
- function expandTopLevelTaskCounts(tasks) {
12264
- const expanded = [];
12265
- for (let taskIndex = 0;taskIndex < tasks.length; taskIndex++) {
12266
- const task = tasks[taskIndex];
12267
- const rawCount = task.count;
12268
- if (rawCount !== undefined && (typeof rawCount !== "number" || !Number.isInteger(rawCount) || rawCount < 1)) {
12269
- return { error: `tasks[${taskIndex}].count must be an integer >= 1` };
12270
- }
12271
- const { count: _count, ...concreteTask } = task;
12272
- for (let repeat = 0;repeat < (rawCount ?? 1); repeat++)
12273
- expanded.push({ ...concreteTask });
12274
- }
12275
- return { tasks: expanded };
12276
- }
12277
- function normalizeRepeatedParallelCounts(params) {
12278
- if (!params.tasks)
12279
- return { params };
12280
- const expandedTasks = expandTopLevelTaskCounts(params.tasks);
12281
- if (expandedTasks.error)
12282
- return { error: buildRequestedModeError(params, expandedTasks.error) };
12283
- return { params: { ...params, tasks: expandedTasks.tasks } };
12284
- }
12285
- function withForkContext(result, context) {
12286
- if (context !== "fork" || !result.details)
12287
- return result;
12288
- return {
12289
- ...result,
12290
- details: {
12291
- ...result.details,
12292
- context: "fork"
12293
- }
12294
- };
12295
- }
12296
- function toExecutionErrorResult(params, error) {
12297
- const message = error instanceof Error ? error.message : String(error);
12298
- return withForkContext({
12299
- content: [{ type: "text", text: message }],
12300
- isError: true,
12301
- details: { mode: getRequestedModeLabel(params), results: [] }
12302
- }, params.context);
12651
+ const suffixParts = [];
12652
+ const readInstruction = buildReadInstruction(behavior.reads, cwd);
12653
+ if (readInstruction)
12654
+ prefixParts.push(readInstruction);
12655
+ if (behavior.output) {
12656
+ const outputPath = path12.isAbsolute(behavior.output) ? behavior.output : path12.join(cwd, behavior.output);
12657
+ prefixParts.push(`[Write to: ${outputPath}]`);
12658
+ }
12659
+ if (behavior.progress) {
12660
+ const progressPath = path12.join(cwd, "progress.md");
12661
+ if (isFirstProgressAgent)
12662
+ suffixParts.push(`Create and maintain progress at: ${progressPath}`);
12663
+ else
12664
+ suffixParts.push(`Update progress at: ${progressPath}`);
12665
+ }
12666
+ const prefix = prefixParts.length > 0 ? `${prefixParts.join(`
12667
+ `)}
12668
+
12669
+ ` : "";
12670
+ const suffix = suffixParts.length > 0 ? `
12671
+
12672
+ ---
12673
+ ${suffixParts.join(`
12674
+ `)}` : "";
12675
+ return { prefix, suffix };
12303
12676
  }
12304
12677
 
12305
12678
  // dist/builtin/subagents/src/runs/foreground/subagent-executor-context.ts
@@ -13374,400 +13747,102 @@ function formatWorktreeDiffSummary(diffs) {
13374
13747
  lines.push(diff.diffStat);
13375
13748
  }
13376
13749
  lines.push("");
13377
- }
13378
- const patchesDir = path18.dirname(changed[0].patchPath);
13379
- lines.push(`Full patches: ${patchesDir}`);
13380
- return lines.join(`
13381
- `).trimEnd();
13382
- }
13383
-
13384
- // dist/builtin/subagents/src/runs/foreground/detached-cleanup-barrier.ts
13385
- function createDetachedCleanupBarrier(onReady) {
13386
- const recovered = new Set;
13387
- let expected;
13388
- let cleaned = false;
13389
- const finishIfReady = () => {
13390
- if (cleaned || !expected || ![...expected].every((index) => recovered.has(index)))
13391
- return;
13392
- cleaned = true;
13393
- onReady();
13394
- };
13395
- return {
13396
- recover(index) {
13397
- recovered.add(index);
13398
- finishIfReady();
13399
- },
13400
- defer(indices) {
13401
- if (indices.length === 0)
13402
- return false;
13403
- expected = new Set(indices);
13404
- finishIfReady();
13405
- return true;
13406
- }
13407
- };
13408
- }
13409
-
13410
- // dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts
13411
- import { join as join19 } from "node:path";
13412
-
13413
- // dist/builtin/subagents/src/runs/foreground/subagent-executor-worktree.ts
13414
- import * as path19 from "node:path";
13415
- function buildParallelModeError(message) {
13416
- return {
13417
- content: [{ type: "text", text: message }],
13418
- isError: true,
13419
- details: { mode: "parallel", results: [] }
13420
- };
13421
- }
13422
- function createParallelWorktreeSetup(enabled, cwd, runId, tasks, setupHook, setupHookTimeoutMs) {
13423
- if (!enabled)
13424
- return {};
13425
- try {
13426
- return {
13427
- setup: createWorktrees(cwd, runId, tasks.length, {
13428
- agents: tasks.map((task) => task.agent),
13429
- setupHook: setupHook ? { hookPath: setupHook, timeoutMs: setupHookTimeoutMs } : undefined
13430
- })
13431
- };
13432
- } catch (error) {
13433
- const message = error instanceof Error ? error.message : String(error);
13434
- return { errorResult: buildParallelModeError(message) };
13435
- }
13436
- }
13437
- function buildParallelWorktreeTaskCwdError(tasks, sharedCwd) {
13438
- const conflict = findWorktreeTaskCwdConflict(tasks, sharedCwd);
13439
- if (!conflict)
13440
- return;
13441
- return formatWorktreeTaskCwdConflict(conflict, sharedCwd);
13442
- }
13443
- function resolveParallelTaskCwd(task, paramsCwd, worktreeSetup, index) {
13444
- if (worktreeSetup)
13445
- return worktreeSetup.worktrees[index].agentCwd;
13446
- return resolveChildCwd(paramsCwd, task.cwd);
13447
- }
13448
- function buildParallelWorktreeSuffix(worktreeSetup, artifactsDir, tasks) {
13449
- if (!worktreeSetup)
13450
- return "";
13451
- const diffsDir = path19.join(artifactsDir, "worktree-diffs");
13452
- const diffs = diffWorktrees(worktreeSetup, tasks.map((task) => task.agent), diffsDir);
13453
- return formatWorktreeDiffSummary(diffs);
13454
- }
13455
- function findDuplicateParallelOutputPath(input) {
13456
- const seen = new Map;
13457
- for (let index = 0;index < input.tasks.length; index++) {
13458
- const behavior = input.behaviors[index];
13459
- if (!behavior?.output)
13460
- continue;
13461
- const task = input.tasks[index];
13462
- const taskCwd = resolveParallelTaskCwd(task, input.paramsCwd, input.worktreeSetup, index);
13463
- const outputPath = resolveSingleOutputPath(behavior.output, input.ctxCwd, taskCwd);
13464
- if (!outputPath)
13465
- continue;
13466
- const previous = seen.get(outputPath);
13467
- if (previous) {
13468
- return `Parallel tasks ${previous.index + 1} (${previous.agent}) and ${index + 1} (${task.agent}) resolve output to the same path: ${outputPath}. Use distinct output paths.`;
13469
- }
13470
- seen.set(outputPath, { index, agent: task.agent });
13471
- }
13472
- return;
13473
- }
13474
-
13475
- // dist/builtin/subagents/src/runs/foreground/task-output-retention.ts
13476
- import { mkdtempSync, readFileSync as readFileSync11, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "node:fs";
13477
- import { tmpdir as tmpdir2 } from "node:os";
13478
- import { join as join18 } from "node:path";
13479
- var INLINE_TASK_OUTPUT_MAX_BYTES = 16 * 1024;
13480
- var MAX_RETAINED_TASK_OUTPUTS = 256;
13481
- var retained = new Map;
13482
- var copyRoot;
13483
- function retentionKey(ownerId, taskId) {
13484
- return `${ownerId}\x00${taskId}`;
13485
- }
13486
- function fileSafe(value) {
13487
- return value.replace(/[^A-Za-z0-9._-]+/g, "_");
13488
- }
13489
- function privateCopyRoot() {
13490
- if (copyRoot)
13491
- return copyRoot;
13492
- const root = mkdtempSync(join18(tmpdir2(), "atomic-subagent-output-"));
13493
- copyRoot = root;
13494
- process.once("exit", () => rmSync3(root, { recursive: true, force: true }));
13495
- return root;
13496
- }
13497
- function removeCopy(entry) {
13498
- if (entry?.path)
13499
- rmSync3(entry.path, { force: true });
13500
- }
13501
- function fullOutputText(child, text) {
13502
- if (child.outputMode !== "file-only" || !child.outputReference)
13503
- return text;
13504
- try {
13505
- return readFileSync11(child.outputReference.path, "utf-8");
13506
- } catch {
13507
- return text;
13508
- }
13509
- }
13510
- function writePrivateCopy(ownerId, taskId, text) {
13511
- try {
13512
- const path = join18(privateCopyRoot(), `${fileSafe(ownerId)}-${fileSafe(taskId)}.md`);
13513
- writeFileSync8(path, text, { encoding: "utf-8", mode: 384 });
13514
- return path;
13515
- } catch {
13516
- return;
13517
- }
13518
- }
13519
- function retainTaskOutput(child, text, ref) {
13520
- const bytes = Buffer.from(text);
13521
- const path = writePrivateCopy(ref.ownerId, ref.taskId, fullOutputText(child, text));
13522
- const key = retentionKey(ref.ownerId, ref.taskId);
13523
- removeCopy(retained.get(key));
13524
- retained.delete(key);
13525
- retained.set(key, {
13526
- ...path ? { path } : {},
13527
- ...child.outputReference ? { requestedPath: child.outputReference.path } : {},
13528
- ...child.outputSaveError ? { saveError: child.outputSaveError } : {},
13529
- head: new TextDecoder().decode(bytes.subarray(0, INLINE_TASK_OUTPUT_MAX_BYTES)),
13530
- totalBytes: bytes.byteLength
13531
- });
13532
- while (retained.size > MAX_RETAINED_TASK_OUTPUTS) {
13533
- const oldest = retained.keys().next().value;
13534
- if (oldest === undefined)
13535
- break;
13536
- removeCopy(retained.get(oldest));
13537
- retained.delete(oldest);
13538
- }
13539
- }
13540
- function retainedTaskOutput(ownerId, taskId) {
13541
- return retained.get(retentionKey(ownerId, taskId));
13542
- }
13543
-
13544
- // dist/builtin/subagents/src/runs/foreground/task-execution.ts
13545
- function subagentTaskResultLabel(result) {
13546
- return result.kind === "cancelled" && result.cause === "user" ? "killed (non-resumable)" : result.kind;
13547
- }
13548
- function subagentTaskResponseText(response) {
13549
- const outcomes = response.kind === "parallel" ? response.slots.map((slot) => slot.outcome) : [response];
13550
- const killed = outcomes.filter((outcome) => outcome.kind === "admitted" && outcome.observation.kind === "settled" && subagentTaskResultLabel(outcome.observation.result) === "killed (non-resumable)");
13551
- return `${killed.length ? `${killed.length} killed. These children cannot be resumed. Underlying host response:
13552
- ` : ""}${JSON.stringify(response)}`;
13553
- }
13554
- function settledOutputsFromResponse(response) {
13555
- const outcomes = response.kind === "parallel" ? response.slots.map((slot) => slot.outcome) : [response];
13556
- return outcomes.flatMap((outcome) => outcome.kind === "admitted" && outcome.observation.kind === "settled" ? [{ taskId: outcome.observation.taskId, result: outcome.observation.result }] : []);
13557
- }
13558
- function settledOutputsFromRecords(records) {
13559
- return records.flatMap((record) => record.execution.kind === "settled" ? [{ taskId: record.ref.taskId, result: record.execution.result }] : []);
13560
- }
13561
- function settledOutputSection(taskId, label, output) {
13562
- const truncated = output.shown < output.total ? `, first ${output.shown} shown${output.path ? "; read the full output file for the rest" : ""}` : "";
13563
- const lines = [`Output of ${taskId} (${label}, ${output.total} bytes${truncated}):`];
13564
- if (output.requestedPath)
13565
- lines.push(`Requested output: ${output.requestedPath}`);
13566
- if (output.saveError)
13567
- lines.push(`Output file error: ${output.saveError}`);
13568
- if (output.path)
13569
- lines.push(`Full output: ${output.path}`);
13570
- lines.push(output.head);
13571
- return lines.join(`
13572
- `);
13573
- }
13574
- async function readSettledOutput(host, settled) {
13575
- const output = settled.result.output;
13576
- if (!output)
13577
- return;
13578
- const label = subagentTaskResultLabel(settled.result);
13579
- const kept = retainedTaskOutput(output.ownerId, output.taskId);
13580
- if (kept) {
13581
- return settledOutputSection(settled.taskId, label, {
13582
- ...kept,
13583
- shown: Math.min(kept.totalBytes, INLINE_TASK_OUTPUT_MAX_BYTES),
13584
- total: kept.totalBytes
13585
- });
13586
- }
13587
- const unavailable = (reason) => `Output of ${settled.taskId} (${label}) is unavailable: ${reason}`;
13588
- const lease = host.resolveTask(settled.taskId);
13589
- if (!lease.ok)
13590
- return unavailable(lease.error.message);
13591
- try {
13592
- const page = await host.ownerBinding.supervisor.readTaskOutput(lease.value, {
13593
- start: "0",
13594
- maximumBytes: INLINE_TASK_OUTPUT_MAX_BYTES
13595
- });
13596
- if (!page.ok)
13597
- return unavailable(page.error.message);
13598
- const decoder = new TextDecoder;
13599
- const head = page.value.chunks.map((chunk) => decoder.decode(chunk.bytes, { stream: true })).join("") + decoder.decode();
13600
- const shown = page.value.chunks.reduce((sum, chunk) => sum + chunk.bytes.byteLength, 0);
13601
- return settledOutputSection(settled.taskId, label, { head, shown, total: Number(output.byteCount) });
13602
- } catch (error) {
13603
- return unavailable(error instanceof Error ? error.message : String(error));
13604
- }
13605
- }
13606
- async function settledTaskOutputText(host, settled) {
13607
- if (!host || settled.length === 0)
13608
- return "";
13609
- const sections = await Promise.all(settled.map((entry) => readSettledOutput(host, entry)));
13610
- const present = sections.filter((section) => section !== undefined);
13611
- return present.length ? `
13612
-
13613
- ${present.join(`
13614
-
13615
- `)}` : "";
13750
+ }
13751
+ const patchesDir = path18.dirname(changed[0].patchPath);
13752
+ lines.push(`Full patches: ${patchesDir}`);
13753
+ return lines.join(`
13754
+ `).trimEnd();
13616
13755
  }
13617
- function taskToolResult(response, host) {
13756
+
13757
+ // dist/builtin/subagents/src/runs/foreground/detached-cleanup-barrier.ts
13758
+ function createDetachedCleanupBarrier(onReady) {
13759
+ const recovered = new Set;
13760
+ let expected;
13761
+ let cleaned = false;
13762
+ const finishIfReady = () => {
13763
+ if (cleaned || !expected || ![...expected].every((index) => recovered.has(index)))
13764
+ return;
13765
+ cleaned = true;
13766
+ onReady();
13767
+ };
13618
13768
  return {
13619
- content: [{ type: "text", text: subagentTaskResponseText(response) }],
13620
- details: {
13621
- mode: "single",
13622
- results: [],
13623
- taskResponse: response,
13624
- taskRecords: taskResponseRecords(response, host)
13769
+ recover(index) {
13770
+ recovered.add(index);
13771
+ finishIfReady();
13625
13772
  },
13626
- ...response.kind === "unstarted" ? { isError: true } : {}
13773
+ defer(indices) {
13774
+ if (indices.length === 0)
13775
+ return false;
13776
+ expected = new Set(indices);
13777
+ finishIfReady();
13778
+ return true;
13779
+ }
13627
13780
  };
13628
13781
  }
13629
- async function taskToolResultWithOutput(response, host) {
13630
- const result = taskToolResult(response, host);
13631
- const outputText = await settledTaskOutputText(host, settledOutputsFromResponse(response));
13632
- if (!outputText)
13633
- return result;
13782
+
13783
+ // dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts
13784
+ import { join as join19 } from "node:path";
13785
+
13786
+ // dist/builtin/subagents/src/runs/foreground/subagent-executor-worktree.ts
13787
+ import * as path19 from "node:path";
13788
+ function buildParallelModeError(message) {
13634
13789
  return {
13635
- ...result,
13636
- content: [{ type: "text", text: `${subagentTaskResponseText(response)}${outputText}` }]
13790
+ content: [{ type: "text", text: message }],
13791
+ isError: true,
13792
+ details: { mode: "parallel", results: [] }
13637
13793
  };
13638
13794
  }
13639
- function taskResponseRecords(response, host) {
13640
- const outcomes = response.kind === "parallel" ? response.slots.map((slot) => slot.outcome) : [response];
13641
- const ids = new Set(outcomes.flatMap((outcome) => outcome.kind === "admitted" ? [outcome.observation.taskId] : []));
13642
- const watched = host?.watchOwnerTasks();
13643
- if (!watched?.ok)
13644
- return [];
13795
+ function createParallelWorktreeSetup(enabled, cwd, runId, tasks, setupHook, setupHookTimeoutMs) {
13796
+ if (!enabled)
13797
+ return {};
13645
13798
  try {
13646
- return watched.value.snapshot.tasks.filter((task) => ids.has(task.ref.taskId));
13647
- } finally {
13648
- watched.value.dispose();
13799
+ return {
13800
+ setup: createWorktrees(cwd, runId, tasks.length, {
13801
+ agents: tasks.map((task) => task.agent),
13802
+ setupHook: setupHook ? { hookPath: setupHook, timeoutMs: setupHookTimeoutMs } : undefined
13803
+ })
13804
+ };
13805
+ } catch (error) {
13806
+ const message = error instanceof Error ? error.message : String(error);
13807
+ return { errorResult: buildParallelModeError(message) };
13649
13808
  }
13650
13809
  }
13651
- async function runAgentTask(input) {
13652
- let yieldWait;
13653
- let pendingYield = false;
13654
- let parentObserving = input.wait?.kind === "foreground";
13655
- const registered = Promise.withResolvers();
13656
- input.options.modelRoute?.assertCurrent();
13657
- const started = await input.host.startAgentTask({
13658
- kind: "agent",
13659
- agent: input.agent,
13660
- task: input.intentTask ?? input.task,
13661
- cwd: input.options.cwd ?? input.cwd,
13662
- ...input.options.modelRoute ? { routerSelection: input.options.modelRoute.routerSelection } : {}
13663
- }, `${input.options.runId}:${input.options.index ?? 0}`, (context) => {
13664
- const cleaned = Promise.withResolvers();
13665
- let executionBound = false;
13666
- const result = registered.promise.then(async () => {
13667
- try {
13668
- input.options.modelRoute?.assertCurrent();
13669
- const child = await input.runtime.runSync(input.cwd, input.agents, input.agent, input.task, {
13670
- ...input.options,
13671
- signal: context.signal,
13672
- taskExecution: {
13673
- signal: context.signal,
13674
- reportActivity: context.reportActivity,
13675
- bindTranscript: (session) => context.bindTranscript({
13676
- getSessionId: () => session.getSessionId(),
13677
- getEntries: () => session.getEntries(),
13678
- ...session.subscribe ? { subscribe: session.subscribe.bind(session) } : {},
13679
- ...session.getStreamingMessage ? { getStreamingMessage: session.getStreamingMessage.bind(session) } : {},
13680
- ...input.options.intercomSessionName ? {
13681
- completionSource: {
13682
- runId: input.options.runId,
13683
- intercomTarget: input.options.intercomSessionName
13684
- }
13685
- } : {}
13686
- }),
13687
- onExecution: (execution) => {
13688
- executionBound = true;
13689
- execution.cleanup.then(cleaned.resolve, (error) => cleaned.resolve({
13690
- kind: "failed",
13691
- resources: [{ resource: "agent-session", code: "CleanupFailed", message: String(error) }]
13692
- }));
13693
- },
13694
- yieldTaskWait: () => {
13695
- parentObserving = false;
13696
- if (yieldWait)
13697
- yieldWait("intercom-coordination");
13698
- else
13699
- pendingYield = true;
13700
- },
13701
- isParentObserving: () => parentObserving
13702
- }
13703
- });
13704
- if (child.model !== undefined || child.thinking !== undefined)
13705
- context.reportActivity({
13706
- reportId: "terminal-model",
13707
- change: { kind: "model", model: child.model, thinking: child.thinking }
13708
- });
13709
- input.onTerminal?.(child);
13710
- const text = input.outputText?.(child) ?? getSingleResultOutput(child);
13711
- retainTaskOutput(child, text, context.ref);
13712
- const bytes = Buffer.from(text);
13713
- context.reportActivity({
13714
- reportId: "terminal-output",
13715
- change: { kind: "output", offset: "0", bytesBase64: bytes.toString("base64") }
13716
- });
13717
- const output = {
13718
- ownerId: context.ref.ownerId,
13719
- taskId: context.ref.taskId,
13720
- artifactId: `output:${context.ref.taskId}`,
13721
- byteCount: String(bytes.length),
13722
- omittedRanges: []
13723
- };
13724
- if (child.status === "killed")
13725
- return { kind: "cancelled", cause: "user", output };
13726
- if (child.interrupted)
13727
- return {
13728
- kind: "cancelled",
13729
- cause: input.options.interruptSignal?.aborted ? "parent-handoff" : "owner-close",
13730
- output
13731
- };
13732
- return child.status === "ok" ? { kind: "completed", output } : {
13733
- kind: "failed",
13734
- code: "AgentFailed",
13735
- message: child.error ?? child.envelope ?? "Agent failed",
13736
- output
13737
- };
13738
- } finally {
13739
- if (!executionBound)
13740
- cleaned.resolve({ kind: "reaped" });
13741
- }
13742
- });
13743
- return { result, cleanup: cleaned.promise };
13744
- }, input.schedule);
13745
- if (!started.ok)
13746
- return { kind: "unstarted", reason: { kind: "rejected", error: started.error } };
13747
- const yieldForIntercom = () => {
13748
- parentObserving = false;
13749
- if (yieldWait)
13750
- yieldWait("intercom-coordination");
13751
- else
13752
- pendingYield = true;
13753
- };
13754
- input.options.intercomDetachSignal?.addEventListener("abort", yieldForIntercom, { once: true });
13755
- if (input.options.intercomDetachSignal?.aborted)
13756
- yieldForIntercom();
13757
- const observation = input.host.observeAgentLaunch(started.value.taskId, input.wait, (yieldRegistered) => {
13758
- yieldWait = yieldRegistered;
13759
- if (pendingYield)
13760
- yieldRegistered("intercom-coordination");
13761
- registered.resolve();
13762
- });
13763
- registered.resolve();
13764
- const observed = await observation.finally(() => {
13765
- parentObserving = false;
13766
- input.options.intercomDetachSignal?.removeEventListener("abort", yieldForIntercom);
13767
- });
13768
- if (!observed.ok)
13769
- throw new Error(`${observed.error.code}: ${observed.error.message}`);
13770
- return { kind: "admitted", observation: observed.value };
13810
+ function buildParallelWorktreeTaskCwdError(tasks, sharedCwd) {
13811
+ const conflict = findWorktreeTaskCwdConflict(tasks, sharedCwd);
13812
+ if (!conflict)
13813
+ return;
13814
+ return formatWorktreeTaskCwdConflict(conflict, sharedCwd);
13815
+ }
13816
+ function resolveParallelTaskCwd(task, paramsCwd, worktreeSetup, index) {
13817
+ if (worktreeSetup)
13818
+ return worktreeSetup.worktrees[index].agentCwd;
13819
+ return resolveChildCwd(paramsCwd, task.cwd);
13820
+ }
13821
+ function buildParallelWorktreeSuffix(worktreeSetup, artifactsDir, tasks) {
13822
+ if (!worktreeSetup)
13823
+ return "";
13824
+ const diffsDir = path19.join(artifactsDir, "worktree-diffs");
13825
+ const diffs = diffWorktrees(worktreeSetup, tasks.map((task) => task.agent), diffsDir);
13826
+ return formatWorktreeDiffSummary(diffs);
13827
+ }
13828
+ function findDuplicateParallelOutputPath(input) {
13829
+ const seen = new Map;
13830
+ for (let index = 0;index < input.tasks.length; index++) {
13831
+ const behavior = input.behaviors[index];
13832
+ if (!behavior?.output)
13833
+ continue;
13834
+ const task = input.tasks[index];
13835
+ const taskCwd = resolveParallelTaskCwd(task, input.paramsCwd, input.worktreeSetup, index);
13836
+ const outputPath = resolveSingleOutputPath(behavior.output, input.ctxCwd, taskCwd);
13837
+ if (!outputPath)
13838
+ continue;
13839
+ const previous = seen.get(outputPath);
13840
+ if (previous) {
13841
+ return `Parallel tasks ${previous.index + 1} (${previous.agent}) and ${index + 1} (${task.agent}) resolve output to the same path: ${outputPath}. Use distinct output paths.`;
13842
+ }
13843
+ seen.set(outputPath, { index, agent: task.agent });
13844
+ }
13845
+ return;
13771
13846
  }
13772
13847
 
13773
13848
  // dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts
@@ -13822,23 +13897,7 @@ function runUnboundParallelTask(options, agent, task, schedule, run) {
13822
13897
  async function runForegroundParallelTasks(input) {
13823
13898
  const intercomDetachController = new AbortController;
13824
13899
  const host = input.ctx.getAgentTaskHost?.();
13825
- let active = 0;
13826
- const queued = [];
13827
- const pump = () => {
13828
- while (active < input.concurrencyLimit && queued.length > 0) {
13829
- const dispatch = queued.shift();
13830
- active++;
13831
- const release = () => {
13832
- active--;
13833
- pump();
13834
- };
13835
- dispatch().then(release, release);
13836
- }
13837
- };
13838
- const schedule = (dispatch) => {
13839
- queued.push(dispatch);
13840
- pump();
13841
- };
13900
+ const schedule = input.schedule ?? createExecutionScheduler(input.concurrencyLimit);
13842
13901
  return mapConcurrent(input.tasks, input.tasks.length, async (task, index) => {
13843
13902
  const behavior = input.behaviors[index];
13844
13903
  const effectiveSkills = behavior?.skills;
@@ -14578,6 +14637,7 @@ async function runParallelPath(data, deps) {
14578
14637
  }
14579
14638
  const results = await runForegroundParallelTasks({
14580
14639
  wait: params.wait,
14640
+ schedule: params[BURST_EXECUTION_SCHEDULE],
14581
14641
  onTaskTerminal: (index) => detachedCleanup.recover(index),
14582
14642
  onDetachedExit: (index, result) => {
14583
14643
  try {
@@ -14646,20 +14706,7 @@ async function runParallelPath(data, deps) {
14646
14706
  }))
14647
14707
  };
14648
14708
  worktreeCleanupDeferred = detachedCleanup.defer(response.slots.flatMap(({ ordinal, outcome }) => outcome.kind === "admitted" && outcome.observation.kind === "yielded" ? [ordinal] : []));
14649
- return {
14650
- content: [
14651
- {
14652
- type: "text",
14653
- text: `${subagentTaskResponseText(response)}${await settledTaskOutputText(ctx.getAgentTaskHost(), settledOutputsFromResponse(response))}`
14654
- }
14655
- ],
14656
- details: {
14657
- mode: "parallel",
14658
- results: [],
14659
- taskResponse: response,
14660
- taskRecords: taskResponseRecords(response, ctx.getAgentTaskHost())
14661
- }
14662
- };
14709
+ return parallelTaskToolResultWithOutput(response, ctx.getAgentTaskHost());
14663
14710
  }
14664
14711
  for (let i = 0;i < results.length; i++) {
14665
14712
  const run = results[i];
@@ -15557,6 +15604,7 @@ async function runSinglePath(data, deps) {
15557
15604
  intentTask: handoffTaskContext,
15558
15605
  options: runOptions,
15559
15606
  wait: params.wait,
15607
+ schedule: params[BURST_EXECUTION_SCHEDULE],
15560
15608
  runtime: deps.runtime,
15561
15609
  onTerminal: (child) => {
15562
15610
  settledChild = child;
@@ -15572,8 +15620,10 @@ async function runSinglePath(data, deps) {
15572
15620
  if (!parentAsk || !settledChild?.interrupted)
15573
15621
  return await taskToolResultWithOutput(response, ctx.getAgentTaskHost());
15574
15622
  r = settledChild;
15575
- } else
15576
- r = await deps.runtime.runSync(ctx.cwd, agents, params.agent, task, runOptions);
15623
+ } else {
15624
+ const schedule = params[BURST_EXECUTION_SCHEDULE];
15625
+ r = schedule ? await runUnboundParallelTask(runOptions, params.agent, task, schedule, (options) => deps.runtime.runSync(ctx.cwd, agents, params.agent, task, options)) : await deps.runtime.runSync(ctx.cwd, agents, params.agent, task, runOptions);
15626
+ }
15577
15627
  } catch (error) {
15578
15628
  cleanupTransientProgress(progressDir, artifactConfig.enabled);
15579
15629
  throw error;
@@ -15911,6 +15961,7 @@ function createSubagentExecutor(rawDeps) {
15911
15961
  };
15912
15962
  const executeWithBurstCollection = createExecutionBurstDispatcher({
15913
15963
  execute,
15964
+ concurrencyLimit: deps.config.parallel?.concurrency,
15914
15965
  isActive: () => deps.state.subagentInProgress === true,
15915
15966
  setActive: (active) => {
15916
15967
  deps.state.subagentInProgress = active;