@bastani/atomic 0.9.15 → 0.9.16-alpha.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 (151) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +10 -0
  3. package/dist/builtin/intercom/README.md +8 -8
  4. package/dist/builtin/intercom/contact-supervisor-tool.ts +68 -32
  5. package/dist/builtin/intercom/index-heavy.ts +1 -0
  6. package/dist/builtin/intercom/index.ts +21 -15
  7. package/dist/builtin/intercom/intercom-tool.ts +66 -10
  8. package/dist/builtin/intercom/package.json +1 -1
  9. package/dist/builtin/intercom/parent-ask-handoff.ts +72 -0
  10. package/dist/builtin/mcp/package.json +1 -1
  11. package/dist/builtin/subagents/CHANGELOG.md +26 -0
  12. package/dist/builtin/subagents/README.md +37 -114
  13. package/dist/builtin/subagents/agents/worker.md +2 -2
  14. package/dist/builtin/subagents/package.json +1 -5
  15. package/dist/builtin/subagents/skills/subagent/SKILL.md +26 -103
  16. package/dist/builtin/subagents/src/extension/index.ts +5 -30
  17. package/dist/builtin/subagents/src/extension/notification-content.ts +1 -1
  18. package/dist/builtin/subagents/src/extension/schemas.ts +2 -14
  19. package/dist/builtin/subagents/src/extension/tool-description.ts +1 -4
  20. package/dist/builtin/subagents/src/extension/tool-rendering.ts +53 -0
  21. package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +1 -54
  22. package/dist/builtin/subagents/src/intercom/result-intercom.ts +4 -4
  23. package/dist/builtin/subagents/src/runs/foreground/execution-parent-ask-handoff.ts +78 -0
  24. package/dist/builtin/subagents/src/runs/foreground/inprocess-run-sync.ts +7 -5
  25. package/dist/builtin/subagents/src/runs/foreground/notify.ts +5 -6
  26. package/dist/builtin/subagents/src/runs/foreground/parent-ask-output.ts +48 -0
  27. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-burst-display.ts +46 -0
  28. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-burst.ts +403 -0
  29. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-context.ts +48 -9
  30. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-input.ts +3 -1
  31. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-live-update.ts +11 -0
  32. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +164 -93
  33. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel.ts +36 -29
  34. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parent-ask-projection.ts +11 -0
  35. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +42 -17
  36. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-status.ts +3 -121
  37. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-types.ts +7 -2
  38. package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +30 -257
  39. package/dist/builtin/subagents/src/runs/inprocess/control-status.ts +0 -34
  40. package/dist/builtin/subagents/src/runs/inprocess/index.ts +0 -12
  41. package/dist/builtin/subagents/src/runs/inprocess/prompt-behavior.ts +1 -1
  42. package/dist/builtin/subagents/src/runs/inprocess/runner.ts +2 -141
  43. package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +8 -0
  44. package/dist/builtin/subagents/src/shared/status-format.ts +3 -3
  45. package/dist/builtin/subagents/src/shared/types-config.ts +41 -0
  46. package/dist/builtin/subagents/src/shared/types-foreground-state.ts +7 -15
  47. package/dist/builtin/subagents/src/shared/types-results.ts +3 -1
  48. package/dist/builtin/subagents/src/shared/types-runtime.ts +1 -11
  49. package/dist/builtin/subagents/src/slash/slash-commands.ts +4 -468
  50. package/dist/builtin/subagents/src/tui/render-progress.ts +2 -2
  51. package/dist/builtin/subagents/src/tui/render-result-compact.ts +2 -2
  52. package/dist/builtin/subagents/src/tui/render-result.ts +38 -14
  53. package/dist/builtin/subagents/src/tui/render-status-progress.ts +1 -1
  54. package/dist/builtin/web-access/package.json +1 -1
  55. package/dist/builtin/workflows/CHANGELOG.md +16 -0
  56. package/dist/builtin/workflows/README.md +1 -1
  57. package/dist/builtin/workflows/package.json +1 -1
  58. package/dist/builtin/workflows/skills/bro/LICENSE.txt +21 -0
  59. package/dist/builtin/workflows/skills/bro/SKILL.md +14 -0
  60. package/dist/builtin/workflows/skills/how/LICENSE.txt +21 -0
  61. package/dist/builtin/workflows/skills/how/SKILL.md +162 -0
  62. package/dist/builtin/workflows/skills/how/references/critic-prompt.md +59 -0
  63. package/dist/builtin/workflows/skills/how/references/critique-rubric.md +58 -0
  64. package/dist/builtin/workflows/skills/how/references/explainer-prompt.md +55 -0
  65. package/dist/builtin/workflows/skills/how/references/explorer-prompt.md +52 -0
  66. package/dist/builtin/workflows/skills/teach/LICENSE.txt +21 -0
  67. package/dist/builtin/workflows/skills/teach/SKILL.md +28 -0
  68. package/dist/builtin/workflows/skills/unslop/LICENSE.txt +21 -0
  69. package/dist/builtin/workflows/skills/unslop/SKILL.md +87 -0
  70. package/dist/builtin/workflows/skills/why/LICENSE.txt +21 -0
  71. package/dist/builtin/workflows/skills/why/SKILL.md +267 -0
  72. package/dist/builtin/workflows/skills/why/references/epistemics.md +144 -0
  73. package/dist/builtin/workflows/skills/why/references/investigator-prompt.md +103 -0
  74. package/dist/builtin/workflows/skills/why/references/source-playbook.md +17 -0
  75. package/dist/builtin/workflows/skills/why/references/sources/code-archaeology.md +88 -0
  76. package/dist/builtin/workflows/skills/why/references/sources/databricks.md +70 -0
  77. package/dist/builtin/workflows/skills/why/references/sources/datadog.md +99 -0
  78. package/dist/builtin/workflows/skills/why/references/sources/incident-postmortem.md +15 -0
  79. package/dist/builtin/workflows/skills/why/references/sources/linear.md +48 -0
  80. package/dist/builtin/workflows/skills/why/references/sources/notion.md +55 -0
  81. package/dist/builtin/workflows/skills/why/references/sources/sentry.md +100 -0
  82. package/dist/builtin/workflows/skills/why/references/sources/slack.md +54 -0
  83. package/dist/builtin/workflows/skills/why/references/synthesizer-prompt.md +135 -0
  84. package/dist/builtin/workflows/src/durable/dbos-embedded-postgres-root.ts +548 -39
  85. package/dist/builtin/workflows/src/durable/dbos-embedded-postgres.ts +529 -65
  86. package/dist/builtin/workflows/src/durable/dbos-lifecycle.ts +30 -8
  87. package/dist/builtin/workflows/src/durable/dbos-local-postgres.ts +36 -3
  88. package/dist/builtin/workflows/src/durable/local-command.ts +68 -10
  89. package/dist/builtin/workflows/src/extension/companions.ts +1 -1
  90. package/dist/builtin/workflows/src/extension/dispatcher.ts +7 -1
  91. package/dist/builtin/workflows/src/extension/extension-factory.ts +2 -2
  92. package/dist/builtin/workflows/src/extension/index.bundle.mjs +1094 -298
  93. package/dist/builtin/workflows/src/extension/public-types.ts +2 -2
  94. package/dist/builtin/workflows/src/extension/render-result.ts +16 -2
  95. package/dist/builtin/workflows/src/extension/runtime.ts +8 -1
  96. package/dist/builtin/workflows/src/extension/workflow-request-abort.ts +15 -0
  97. package/dist/builtin/workflows/src/extension/workflow-tool-content.ts +4 -3
  98. package/dist/builtin/workflows/src/extension/workflow-tool-registration.ts +107 -13
  99. package/dist/builtin/workflows/src/extension/workflow-tool.ts +22 -12
  100. package/dist/builtin/workflows/src/tui/widget.ts +25 -6
  101. package/dist/core/agent-session-auto-compaction.js +10 -10
  102. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  103. package/dist/core/agent-session-events.d.ts.map +1 -1
  104. package/dist/core/agent-session-events.js +11 -13
  105. package/dist/core/agent-session-events.js.map +1 -1
  106. package/dist/core/agent-session-methods.d.ts +1 -1
  107. package/dist/core/agent-session-methods.d.ts.map +1 -1
  108. package/dist/core/agent-session-methods.js.map +1 -1
  109. package/dist/core/agent-session-models.d.ts.map +1 -1
  110. package/dist/core/agent-session-models.js +5 -7
  111. package/dist/core/agent-session-models.js.map +1 -1
  112. package/dist/core/agent-session-prompt.js +4 -4
  113. package/dist/core/agent-session-prompt.js.map +1 -1
  114. package/dist/core/agent-session-retry.d.ts +4 -4
  115. package/dist/core/agent-session-retry.d.ts.map +1 -1
  116. package/dist/core/agent-session-retry.js +8 -34
  117. package/dist/core/agent-session-retry.js.map +1 -1
  118. package/dist/core/agent-session-tool-hooks.js +4 -4
  119. package/dist/core/agent-session-tool-hooks.js.map +1 -1
  120. package/dist/core/atomic-guide-command.d.ts.map +1 -1
  121. package/dist/core/atomic-guide-command.js +3 -10
  122. package/dist/core/atomic-guide-command.js.map +1 -1
  123. package/dist/core/event-bus.d.ts.map +1 -1
  124. package/dist/core/event-bus.js +19 -3
  125. package/dist/core/event-bus.js.map +1 -1
  126. package/dist/core/slash-commands.d.ts.map +1 -1
  127. package/dist/core/slash-commands.js +0 -3
  128. package/dist/core/slash-commands.js.map +1 -1
  129. package/dist/core/system-prompt.d.ts.map +1 -1
  130. package/dist/core/system-prompt.js +0 -12
  131. package/dist/core/system-prompt.js.map +1 -1
  132. package/dist/modes/interactive-engine/remote-command-catalog.d.ts +1 -1
  133. package/dist/modes/interactive-engine/remote-command-catalog.js +1 -1
  134. package/dist/modes/interactive-engine/remote-command-catalog.js.map +1 -1
  135. package/docs/intercom.md +29 -27
  136. package/docs/settings.md +2 -2
  137. package/docs/subagents.md +18 -15
  138. package/docs/usage.md +0 -9
  139. package/docs/workflows.md +27 -10
  140. package/npm-shrinkwrap.json +32 -32
  141. package/package.json +3 -3
  142. package/dist/builtin/subagents/prompts/gather-context-and-clarify.md +0 -27
  143. package/dist/builtin/subagents/prompts/parallel-cleanup.md +0 -61
  144. package/dist/builtin/subagents/prompts/parallel-context-build.md +0 -43
  145. package/dist/builtin/subagents/prompts/parallel-handoff-plan.md +0 -47
  146. package/dist/builtin/subagents/prompts/parallel-research.md +0 -34
  147. package/dist/builtin/subagents/prompts/parallel-review.md +0 -47
  148. package/dist/builtin/subagents/prompts/review-loop.md +0 -39
  149. package/dist/builtin/subagents/src/extension/doctor.ts +0 -188
  150. package/dist/builtin/subagents/src/runs/inprocess/attempt-handles.ts +0 -104
  151. /package/dist/builtin/subagents/src/runs/foreground/{subagent-executor-resume.ts → subagent-executor-cwd.ts} +0 -0
@@ -17,7 +17,7 @@ import {
17
17
  type SubagentState,
18
18
  type SubagentToolResult,
19
19
  } from "../../shared/types.js";
20
- import { compactForegroundDetails, compactForegroundResult, getSingleResultOutput } from "../../shared/utils.js";
20
+ import { compactForegroundDetails, getSingleResultOutput } from "../../shared/utils.js";
21
21
  import {
22
22
  formatControlIntercomMessage,
23
23
  formatControlNoticeMessage,
@@ -77,128 +77,10 @@ export function foregroundStatusResult(control: ForegroundControl): SubagentTool
77
77
  return { content: [{ type: "text", text: lines.join("\n") }], details: { mode: "management", results: [] } };
78
78
  }
79
79
 
80
- export function retainedForegroundStatusResult(state: SubagentState, runId: string): SubagentToolResult | undefined {
81
- const run = state.foregroundRuns?.get(runId);
82
- if (!run) return undefined;
83
- const statuses = run.children.map((child) => child.status);
84
- const stateLabel = statuses.includes("detached")
85
- ? "detached"
86
- : statuses.includes("failed")
87
- ? "failed"
88
- : statuses.includes("paused")
89
- ? "paused"
90
- : statuses.every((status) => status === "completed")
91
- ? "completed"
92
- : (statuses[0] ?? "unknown");
93
- const lines = [`Run: ${run.runId}`, `State: ${stateLabel}`, `Mode: ${run.mode}`];
94
- for (const child of run.children) {
95
- lines.push(`Child ${child.index + 1}: ${child.agent} (${child.status})`);
96
- const output = child.result ? getSingleResultOutput(child.result) : "";
97
- if (output) lines.push(output);
98
- else if (child.result?.error) lines.push(child.result.error);
99
- }
100
- return { content: [{ type: "text", text: lines.join("\n") }], details: { mode: "management", results: [] } };
101
- }
102
-
103
- const earlyDetachedResults = new WeakMap<SubagentState, Map<string, Map<number, SingleResult>>>();
104
- const MAX_EARLY_DETACHED_RUNS = 50;
105
- const MAX_EARLY_DETACHED_CHILDREN_PER_RUN = 50;
106
-
107
- function takeEarlyDetachedResult(state: SubagentState, runId: string, index: number): SingleResult | undefined {
108
- const byRun = earlyDetachedResults.get(state);
109
- const byIndex = byRun?.get(runId);
110
- const result = byIndex?.get(index);
111
- byIndex?.delete(index);
112
- if (byIndex?.size === 0) byRun?.delete(runId);
113
- return result;
114
- }
115
-
116
- export function rememberForegroundRun(
117
- state: SubagentState,
118
- input: {
119
- runId: string;
120
- mode: "single" | "parallel";
121
- cwd: string;
122
- results: SingleResult[];
123
- },
124
- ): void {
125
- state.foregroundRuns ??= new Map();
126
- state.foregroundRuns.set(input.runId, {
127
- runId: input.runId,
128
- mode: input.mode,
129
- cwd: input.cwd,
130
- updatedAt: Date.now(),
131
- children: input.results.map((originalResult, index) => {
132
- const result = takeEarlyDetachedResult(state, input.runId, index) ?? originalResult;
133
- return {
134
- agent: result.agent,
135
- index,
136
- status: resolveSubagentResultStatus({
137
- status: result.status,
138
- interrupted: result.interrupted,
139
- detached: result.detached,
140
- }),
141
- ...(result.sessionFile ? { sessionFile: result.sessionFile } : {}),
142
- result,
143
- };
144
- }),
145
- });
146
- while (state.foregroundRuns.size > 50) {
147
- const oldest = [...state.foregroundRuns.values()].sort((left, right) => left.updatedAt - right.updatedAt)[0];
148
- if (!oldest) break;
149
- state.foregroundRuns.delete(oldest.runId);
150
- }
151
- }
152
-
153
- export function replaceForegroundRunChild(
154
- state: SubagentState,
155
- runId: string,
156
- index: number,
157
- result: SingleResult,
158
- options: { onlyWhenDetached?: boolean } = {},
159
- ): void {
160
- const retainedResult = compactForegroundResult(result);
161
- const run = state.foregroundRuns?.get(runId);
162
- if (!run) {
163
- let byRun = earlyDetachedResults.get(state);
164
- if (!byRun) {
165
- byRun = new Map();
166
- earlyDetachedResults.set(state, byRun);
167
- }
168
- let byIndex = byRun.get(runId);
169
- if (!byIndex) {
170
- byIndex = new Map();
171
- byRun.set(runId, byIndex);
172
- }
173
- byIndex.set(index, retainedResult);
174
- while (byIndex.size > MAX_EARLY_DETACHED_CHILDREN_PER_RUN) {
175
- const oldestIndex = byIndex.keys().next().value;
176
- if (oldestIndex === undefined) break;
177
- byIndex.delete(oldestIndex);
178
- }
179
- while (byRun.size > MAX_EARLY_DETACHED_RUNS) {
180
- const oldestRun = byRun.keys().next().value;
181
- if (oldestRun === undefined) break;
182
- byRun.delete(oldestRun);
183
- }
184
- return;
185
- }
186
- const child = run.children.find((entry) => entry.index === index);
187
- if (!child || (options.onlyWhenDetached !== false && child.status !== "detached")) return;
188
- child.status = resolveSubagentResultStatus({
189
- status: retainedResult.status,
190
- interrupted: retainedResult.interrupted,
191
- detached: retainedResult.detached,
192
- });
193
- child.result = retainedResult;
194
- if (retainedResult.sessionFile) child.sessionFile = retainedResult.sessionFile;
195
- run.updatedAt = Date.now();
196
- }
197
-
198
80
  export function emitControlNotification(input: {
199
81
  pi: ExtensionAPI;
200
82
  controlConfig: ExecutionContextData["controlConfig"];
201
- intercomBridge: IntercomBridgeState;
83
+ intercomBridge: Pick<IntercomBridgeState, "active" | "orchestratorTarget">;
202
84
  event: ControlEvent;
203
85
  }): void {
204
86
  if (!shouldNotifyControlEvent(input.controlConfig, input.event)) return;
@@ -284,7 +166,7 @@ export function notifyDetachedForegroundChildExit(input: {
284
166
  agent: result.agent,
285
167
  status: result.status,
286
168
  summary: resultSummaryForIntercom(result),
287
- ...(result.interrupted ? { state: "paused" } : {}),
169
+ ...(result.interrupted ? { state: "interrupted" } : {}),
288
170
  timestamp: Date.now(),
289
171
  ...(result.progressSummary?.durationMs !== undefined ? { durationMs: result.progressSummary.durationMs } : {}),
290
172
  ...(result.sessionFile ? { sessionFile: result.sessionFile } : {}),
@@ -14,6 +14,8 @@ import type {
14
14
  import type { ChildModePolicy } from "../inprocess/child-policy.js";
15
15
  import type { runSync } from "./execution.js";
16
16
 
17
+ export const BURST_TASK_DISCOVERY_CWD = Symbol("burstTaskDiscoveryCwd");
18
+
17
19
  export interface TaskParam {
18
20
  agent: string;
19
21
  task: string;
@@ -28,14 +30,16 @@ export interface TaskParam {
28
30
  group?: string | true;
29
31
  }
30
32
 
33
+ export type BurstTaskParam = TaskParam & {
34
+ [BURST_TASK_DISCOVERY_CWD]?: string;
35
+ };
36
+
31
37
  export interface SubagentParamsLike {
32
38
  action?: (typeof SUBAGENT_ACTIONS)[number];
33
39
  id?: string;
34
40
  runId?: string;
35
- index?: number;
36
41
  agent?: string;
37
42
  task?: string;
38
- message?: string;
39
43
  config?: unknown;
40
44
  tasks?: TaskParam[];
41
45
  concurrency?: number;
@@ -94,6 +98,7 @@ export interface ExecutionContextData {
94
98
  signal: AbortSignal;
95
99
  onUpdate?: (r: SubagentToolResult) => void;
96
100
  agents: AgentConfig[];
101
+ parallelAgentConfigs?: AgentConfig[];
97
102
  runId: string;
98
103
  shareEnabled: boolean;
99
104
  sessionRoot: string;
@@ -1,55 +1,16 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
1
  import type { ExtensionContext } from "@bastani/atomic";
4
2
  import { handleManagementAction } from "../../agents/agent-management.js";
5
- import { resolveExecutionAgentScope } from "../../agents/agent-scope.js";
6
3
  import { clearPendingForegroundControlNotices } from "../../extension/control-notices.js";
7
- import { buildDoctorReport } from "../../extension/doctor.js";
8
- import {
9
- INTERCOM_BRIDGE_MARKER,
10
- resolveIntercomBridge,
11
- resolveIntercomSessionTarget,
12
- resolveSubagentIntercomTarget,
13
- } from "../../intercom/intercom-bridge.js";
14
- import { requestSupervisorAuthorization } from "../../intercom/supervisor-authorization.js";
15
- import { getArtifactsDir } from "../../shared/artifacts.js";
16
- import { collectKnownModelProviders, toModelInfo } from "../../shared/model-info.js";
17
- import { createCandidateModelResolver } from "../../shared/model-resolution.js";
18
- import {
19
- injectSingleProgressInstruction,
20
- resolveSingleProgress,
21
- writeInitialProgressFile,
22
- } from "../../shared/settings.js";
23
- import {
24
- DEFAULT_ARTIFACT_CONFIG,
25
- getCurrentSubagentDepth,
26
- isWorkflowStageOrchestrationContext,
27
- type SingleResult,
28
- SUBAGENT_ACTIONS,
29
- type SubagentToolResult,
30
- workflowSessionMetadataFromContext,
31
- } from "../../shared/types.js";
32
- import {
33
- inspectInProcessChildStatus,
34
- interruptInProcessChild,
35
- resumeInProcessChild,
36
- } from "../inprocess/control-status.js";
37
- import { inheritedIntercomGroup } from "../shared/intercom-group.js";
38
- import { currentModelFullId } from "../shared/model-fallback.js";
39
- import { resolveControlConfig } from "../shared/subagent-control.js";
4
+ import { SUBAGENT_ACTIONS, type SubagentToolResult } from "../../shared/types.js";
5
+ import { inspectInProcessChildStatus, interruptInProcessChild } from "../inprocess/control-status.js";
6
+ import { createExecutionBurstDispatcher } from "./subagent-executor-burst.js";
40
7
  import { prepareExecutionContext, refuseSubagentChildDelegation } from "./subagent-executor-context.js";
8
+ import { resolveRequestedCwd } from "./subagent-executor-cwd.js";
41
9
  import { toExecutionErrorResult, withForkContext } from "./subagent-executor-input.js";
42
10
  import { runParallelPath } from "./subagent-executor-parallel.js";
43
- import { resolveRequestedCwd } from "./subagent-executor-resume.js";
44
11
  import { resolveSubagentExecutorRuntimeDeps } from "./subagent-executor-runtime.js";
45
- import { createForwardSingleUpdate, runSinglePath } from "./subagent-executor-single.js";
46
- import {
47
- foregroundStatusResult,
48
- getForegroundControl,
49
- notifyDetachedForegroundChildExit,
50
- replaceForegroundRunChild,
51
- retainedForegroundStatusResult,
52
- } from "./subagent-executor-status.js";
12
+ import { runSinglePath } from "./subagent-executor-single.js";
13
+ import { foregroundStatusResult, getForegroundControl } from "./subagent-executor-status.js";
53
14
  import {
54
15
  type ExecutorDeps,
55
16
  isManagementActionsRestricted,
@@ -57,130 +18,9 @@ import {
57
18
  type SubagentParamsLike,
58
19
  } from "./subagent-executor-types.js";
59
20
 
60
- async function resumeRetainedForegroundChild(
61
- params: SubagentParamsLike,
62
- message: string,
63
- ctx: ExtensionContext,
64
- deps: ResolvedExecutorDeps,
65
- onUpdate: ((r: SubagentToolResult) => void) | undefined,
66
- ): Promise<SubagentToolResult | undefined> {
67
- const requested = params.id ?? params.runId;
68
- if (!requested) return undefined;
69
- const run = deps.state.foregroundRuns?.get(requested);
70
- if (!run) return undefined;
71
- const child = run.children[params.index ?? 0];
72
- if (!child) return undefined;
73
- const agents = deps.discoverAgents(run.cwd, resolveExecutionAgentScope(params.agentScope)).agents;
74
- const agentConfig = agents.find((agent) => agent.name === child.agent);
75
- if (!agentConfig) {
76
- return {
77
- content: [{ type: "text", text: `Unknown agent for resume: ${child.agent}` }],
78
- isError: true,
79
- details: { mode: "single", results: [] },
80
- };
81
- }
82
- const parentSessionFile = ctx.sessionManager.getSessionFile() ?? null;
83
- const sessionName = resolveIntercomSessionTarget(deps.pi.getSessionName(), ctx.sessionManager.getSessionId());
84
- const intercomBridge = resolveIntercomBridge({
85
- config: deps.config.intercomBridge,
86
- context: params.context,
87
- orchestratorTarget: sessionName,
88
- cwd: run.cwd,
89
- });
90
- const supervisedTarget = intercomBridge.active
91
- ? resolveSubagentIntercomTarget(run.runId, child.agent, child.index)
92
- : undefined;
93
- const supervisorAuthorization = await requestSupervisorAuthorization(deps.pi.events, supervisedTarget);
94
- const artifactConfig = { ...DEFAULT_ARTIFACT_CONFIG, enabled: params.artifacts !== false };
95
- const artifactsDir = getArtifactsDir(parentSessionFile);
96
- const progressDir = resolveSingleProgress(agentConfig, params.progress, message)
97
- ? path.join(artifactsDir, "progress", run.runId)
98
- : undefined;
99
- if (progressDir) {
100
- writeInitialProgressFile(progressDir);
101
- message = injectSingleProgressInstruction(message, progressDir);
102
- }
103
- const cleanupProgress = (): void => {
104
- if (!progressDir || artifactConfig.enabled) return;
105
- try {
106
- fs.rmSync(progressDir, { recursive: true, force: true });
107
- } catch {
108
- // Scratch cleanup must never replace the child run's result or original error.
109
- }
110
- };
111
- const forwardSingleUpdate = createForwardSingleUpdate(
112
- onUpdate,
113
- getForegroundControl(deps.state, run.runId),
114
- child.agent,
115
- child.index,
116
- );
117
- let result: SingleResult;
118
- try {
119
- result = await deps.runtime.runSync(run.cwd, agents, child.agent, message, {
120
- cwd: run.cwd,
121
- signal: ctx.signal,
122
- interruptSignal: ctx.signal,
123
- allowIntercomDetach: agentConfig.systemPrompt?.includes(INTERCOM_BRIDGE_MARKER) === true,
124
- intercomEvents: deps.pi.events,
125
- runId: run.runId,
126
- index: child.index,
127
- sessionDir: child.sessionFile ? path.dirname(child.sessionFile) : undefined,
128
- sessionFile: child.sessionFile,
129
- share: params.share === true,
130
- artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
131
- artifactConfig,
132
- maxOutput: params.maxOutput,
133
- parentDepth: getCurrentSubagentDepth(ctx),
134
- workflowStageSubagentGuard: isWorkflowStageOrchestrationContext(ctx),
135
- workflowSessionMetadata: workflowSessionMetadataFromContext(ctx),
136
- controlConfig: resolveControlConfig(deps.config.control, params.control),
137
- intercomSessionName: intercomBridge.active
138
- ? resolveSubagentIntercomTarget(run.runId, child.agent, child.index)
139
- : undefined,
140
- orchestratorIntercomTarget: intercomBridge.active ? intercomBridge.orchestratorTarget : undefined,
141
- intercomGroup: inheritedIntercomGroup(ctx),
142
- supervisorAuthorization,
143
- modelOverride: params.model,
144
- availableModels: ctx.modelRegistry.getAvailable().map(toModelInfo),
145
- knownModelProviders: collectKnownModelProviders(ctx.modelRegistry),
146
- resolveCandidateModel: createCandidateModelResolver(ctx.modelRegistry, ctx.model?.provider),
147
- preferredModelProvider: ctx.model?.provider,
148
- currentModel: currentModelFullId(ctx.model),
149
- currentThinkingLevel: ctx.thinkingLevel,
150
- onUpdate: forwardSingleUpdate,
151
- onDetachedExit: (detachedResult) => {
152
- cleanupProgress();
153
- if (detachedResult) {
154
- replaceForegroundRunChild(deps.state, run.runId, child.index, detachedResult);
155
- notifyDetachedForegroundChildExit({
156
- pi: deps.pi,
157
- runId: run.runId,
158
- mode: "single",
159
- index: child.index,
160
- result: detachedResult,
161
- });
162
- }
163
- },
164
- });
165
- } catch (error) {
166
- cleanupProgress();
167
- throw error;
168
- }
169
- if (!result.detached) cleanupProgress();
170
- replaceForegroundRunChild(deps.state, run.runId, child.index, result, { onlyWhenDetached: false });
171
- return {
172
- content: [{ type: "text", text: result.finalOutput ?? result.envelope ?? result.error ?? "" }],
173
- details: { mode: "single", runId: run.runId, results: [result] },
174
- ...(result.status === "error" ? { isError: true } : {}),
175
- };
176
- }
177
21
  const MUTATING_MANAGEMENT_ACTIONS = new Set(["create", "update", "delete"]);
178
- /**
179
- * Management actions that only observe. Every other action either mutates agent
180
- * definitions or starts/continues agent execution, so a child without fanout
181
- * authorization is refused all of them.
182
- */
183
- const READ_ONLY_MANAGEMENT_ACTIONS = new Set(["list", "get", "status", "doctor"]);
22
+ /** Observing management actions do not start or mutate child execution. */
23
+ const READ_ONLY_MANAGEMENT_ACTIONS = new Set(["list", "get", "status"]);
184
24
  const FANOUT_REFUSAL_MESSAGE = "Subagent fanout is not authorized for this child.";
185
25
 
186
26
  export type { SubagentExecutorRuntimeDeps, SubagentParamsLike } from "./subagent-executor-types.js";
@@ -191,9 +31,8 @@ async function handleManagementRequest(input: {
191
31
  requestCwd: string;
192
32
  ctx: ExtensionContext;
193
33
  deps: ResolvedExecutorDeps;
194
- onUpdate?: (r: SubagentToolResult) => void;
195
34
  }): Promise<SubagentToolResult> {
196
- const { params, paramsWithResolvedCwd, requestCwd, ctx, deps, onUpdate } = input;
35
+ const { params, paramsWithResolvedCwd, requestCwd, ctx, deps } = input;
197
36
  const action = params.action;
198
37
  if (!action) {
199
38
  return {
@@ -216,9 +55,8 @@ async function handleManagementRequest(input: {
216
55
  details: { mode: "management" as const, results: [] },
217
56
  };
218
57
  }
219
- // `resume` revives a child and `interrupt` is privileged control over a
220
- // running one; both continue agent execution, so only the observing actions
221
- // reach their handlers for a child without fanout authorization.
58
+ // `interrupt` is privileged control over a running child, so only the
59
+ // observing actions reach handlers for a child without fanout authorization.
222
60
  if (deps.childPolicy && !deps.childPolicy.fanoutAuthorized && !READ_ONLY_MANAGEMENT_ACTIONS.has(action)) {
223
61
  return {
224
62
  content: [{ type: "text", text: FANOUT_REFUSAL_MESSAGE }],
@@ -226,57 +64,17 @@ async function handleManagementRequest(input: {
226
64
  details: { mode: "management" as const, results: [] },
227
65
  };
228
66
  }
229
- // Delegation is one level deep: a session admitted as a subagent child may
230
- // never start or continue another agent, whatever else it is authorized for.
67
+ // Delegation is one level deep: a child may never control another child.
231
68
  if (!READ_ONLY_MANAGEMENT_ACTIONS.has(action)) {
232
69
  const childRefusal = refuseSubagentChildDelegation(ctx, "management");
233
70
  if (childRefusal) return childRefusal;
234
71
  }
235
- if (action === "doctor") {
236
- let currentSessionFile: string | null = null;
237
- let currentSessionId = deps.state.currentSessionId;
238
- let sessionError: string | undefined;
239
- try {
240
- currentSessionFile = ctx.sessionManager.getSessionFile() ?? null;
241
- currentSessionId = ctx.sessionManager.getSessionId();
242
- } catch (error) {
243
- sessionError = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
244
- }
245
- let orchestratorTarget: string | undefined;
246
- try {
247
- orchestratorTarget = resolveIntercomSessionTarget(deps.pi.getSessionName(), ctx.sessionManager.getSessionId());
248
- } catch {}
249
- return {
250
- content: [
251
- {
252
- type: "text",
253
- text: buildDoctorReport({
254
- cwd: requestCwd,
255
- config: deps.config,
256
- state: deps.state,
257
- context: paramsWithResolvedCwd.context,
258
- requestedSessionDir: paramsWithResolvedCwd.sessionDir,
259
- currentSessionFile,
260
- currentSessionId,
261
- orchestratorTarget,
262
- sessionError,
263
- expandTilde: deps.expandTilde,
264
- }),
265
- },
266
- ],
267
- details: { mode: "management", results: [] },
268
- };
269
- }
270
72
  if (action === "status") {
271
73
  const targetRunId = paramsWithResolvedCwd.id ?? paramsWithResolvedCwd.runId;
272
74
  const inProcess = inspectInProcessChildStatus(targetRunId);
273
75
  if (inProcess) return inProcess;
274
76
  const foreground = getForegroundControl(deps.state, targetRunId);
275
77
  if (foreground) return foregroundStatusResult(foreground);
276
- if (targetRunId) {
277
- const retained = retainedForegroundStatusResult(deps.state, targetRunId);
278
- if (retained) return retained;
279
- }
280
78
  return {
281
79
  content: [
282
80
  {
@@ -288,26 +86,6 @@ async function handleManagementRequest(input: {
288
86
  details: { mode: "management", results: [] },
289
87
  };
290
88
  }
291
- if (action === "resume") {
292
- const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
293
- const message = paramsWithResolvedCwd.message ?? paramsWithResolvedCwd.task;
294
- if (!targetRunId || !message) {
295
- return {
296
- content: [{ type: "text", text: "action='resume' requires id/runId and message." }],
297
- isError: true,
298
- details: { mode: "management", results: [] },
299
- };
300
- }
301
- const inProcess = await resumeInProcessChild(targetRunId, message, { model: ctx.model });
302
- if (inProcess) return inProcess;
303
- const retained = await resumeRetainedForegroundChild(paramsWithResolvedCwd, message, ctx, deps, onUpdate);
304
- if (retained) return retained;
305
- return {
306
- content: [{ type: "text", text: `No in-process child found for '${targetRunId}'.` }],
307
- isError: true,
308
- details: { mode: "management", results: [] },
309
- };
310
- }
311
89
  if (action === "interrupt") {
312
90
  const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
313
91
  if (targetRunId) {
@@ -366,18 +144,16 @@ export function createSubagentExecutor(rawDeps: ExecutorDeps): {
366
144
  ctx: ExtensionContext,
367
145
  ): Promise<SubagentToolResult> => {
368
146
  deps.state.baseCwd = ctx.cwd;
369
- deps.state.foregroundRuns ??= new Map();
370
147
  deps.state.foregroundControls ??= new Map();
371
148
  deps.state.lastForegroundControlId ??= null;
372
149
  const requestCwd = resolveRequestedCwd(ctx.cwd, params.cwd);
373
150
  const paramsWithResolvedCwd = params.cwd === undefined ? params : { ...params, cwd: requestCwd };
374
151
  if (params.action) {
375
- return handleManagementRequest({ params, paramsWithResolvedCwd, requestCwd, ctx, deps, onUpdate });
152
+ return handleManagementRequest({ params, paramsWithResolvedCwd, requestCwd, ctx, deps });
376
153
  }
377
- // Fanout authorization gates delegation and every management action that can
378
- // start or continue agent execution. Only `list`, `get`, `status`, and
379
- // `doctor` stay available to an unauthorized child; `resume`, `interrupt`,
380
- // and mutating management are refused inside handleManagementRequest.
154
+ // Fanout authorization gates delegation and privileged control. Only `list`,
155
+ // `get`, and `status` stay available to an unauthorized child; `interrupt`
156
+ // and mutating management are refused by handleManagementRequest.
381
157
  if (deps.childPolicy && !deps.childPolicy.fanoutAuthorized) {
382
158
  return {
383
159
  content: [{ type: "text", text: FANOUT_REFUSAL_MESSAGE }],
@@ -423,22 +199,19 @@ export function createSubagentExecutor(rawDeps: ExecutorDeps): {
423
199
  );
424
200
  };
425
201
 
426
- const executeWithSingleDispatchGuard = async (
427
- id: string,
428
- params: SubagentParamsLike,
429
- signal: AbortSignal,
430
- onUpdate: ((r: SubagentToolResult) => void) | undefined,
431
- ctx: ExtensionContext,
432
- ): Promise<SubagentToolResult> => {
433
- if (params.action) return execute(id, params, signal, onUpdate, ctx);
434
- if (deps.state.subagentInProgress === true) return duplicateSubagentCallResult(params);
435
- deps.state.subagentInProgress = true;
436
- try {
437
- return await execute(id, params, signal, onUpdate, ctx);
438
- } finally {
439
- deps.state.subagentInProgress = false;
440
- }
441
- };
202
+ const executeWithBurstCollection = createExecutionBurstDispatcher({
203
+ execute,
204
+ isActive: () => deps.state.subagentInProgress === true,
205
+ setActive: (active) => {
206
+ deps.state.subagentInProgress = active;
207
+ },
208
+ duplicateResult: duplicateSubagentCallResult,
209
+ });
442
210
 
443
- return { execute: executeWithSingleDispatchGuard };
211
+ return {
212
+ execute: (id, params, signal, onUpdate, ctx) =>
213
+ params.action
214
+ ? execute(id, params, signal, onUpdate, ctx)
215
+ : executeWithBurstCollection(id, params, signal, onUpdate, ctx),
216
+ };
444
217
  }
@@ -1,17 +1,10 @@
1
1
  import type { SubagentToolResult } from "../../shared/types.js";
2
2
  import { findSubagentControl, listSubagentControls } from "./control-registry.js";
3
- import type { ModelCandidate } from "./runner.js";
4
3
 
5
4
  function canonicalChildren(control: NonNullable<ReturnType<typeof findSubagentControl>>) {
6
5
  return [...control.listChildren()].sort((left, right) => left.path.localeCompare(right.path));
7
6
  }
8
7
 
9
- function resumeTarget(control: NonNullable<ReturnType<typeof findSubagentControl>>, id: string) {
10
- if (id !== control.parent.path) return control.findChild(id);
11
- const children = canonicalChildren(control);
12
- return children.find((child) => child.status === "interrupted") ?? children[0];
13
- }
14
-
15
8
  function childLines(control: ReturnType<typeof findSubagentControl>, id?: string): string[] {
16
9
  if (!control) return [];
17
10
  if (id) {
@@ -77,30 +70,3 @@ export async function interruptInProcessChild(id: string): Promise<SubagentToolR
77
70
  details: { mode: "management", results: [] },
78
71
  };
79
72
  }
80
-
81
- export async function resumeInProcessChild(
82
- id: string,
83
- message: string,
84
- candidate: ModelCandidate,
85
- ): Promise<SubagentToolResult | undefined> {
86
- const control = findSubagentControl(id);
87
- if (!control) return undefined;
88
- const target = resumeTarget(control, id);
89
- if (!target) {
90
- return {
91
- content: [{ type: "text", text: `No in-process child found for '${id}'.` }],
92
- isError: true,
93
- details: { mode: "management", results: [] },
94
- };
95
- }
96
- const outcome = await control.resumeChild(target.path, message, candidate);
97
- const selection =
98
- id === control.parent.path && control.listChildren().length > 1
99
- ? `Bare run id resolved to ${target.path}; interrupted children are preferred, then canonical path order.\n\n`
100
- : "";
101
- return {
102
- content: [{ type: "text", text: `${selection}${outcome.envelope}` }],
103
- isError: outcome.status === "error" || outcome.status === "interrupted" ? true : undefined,
104
- details: { mode: "management", results: [] },
105
- };
106
- }
@@ -1,14 +1,3 @@
1
- export type {
2
- InProcessAttemptControlResult,
3
- InProcessAttemptHandle,
4
- InProcessAttemptResumeOutcome,
5
- } from "./attempt-handles.js";
6
- export {
7
- clearInProcessAttemptHandles,
8
- interruptInProcessAttempt,
9
- registerInProcessAttempt,
10
- resumeInProcessAttempt,
11
- } from "./attempt-handles.js";
12
1
  export type {
13
2
  AdmissionRefusal,
14
3
  AdmittedResult,
@@ -32,7 +21,6 @@ export {
32
21
  continue_detached,
33
22
  createSubagentControl,
34
23
  deliver_child_result,
35
- reload_cold_child,
36
24
  run_child_attempt,
37
25
  SubagentControlRuntime,
38
26
  terminate_child_attempt,
@@ -11,7 +11,7 @@ export const CHILD_SUBAGENT_BOUNDARY_INSTRUCTIONS = [
11
11
  export const CHILD_FANOUT_BOUNDARY_INSTRUCTIONS = [
12
12
  "You are a child subagent with explicit fanout responsibility for this assigned task.",
13
13
  "The parent session owns final orchestration, completion decisions, and follow-up implementation launches.",
14
- "Delegation is one level deep: the `subagent` tool refuses every launch, resume, and interrupt from inside a subagent.",
14
+ "Delegation is one level deep: the `subagent` tool refuses every launch and interrupt from inside a subagent.",
15
15
  "Do the assigned fanout work yourself and report what you found; do not plan around delegating it.",
16
16
  "If you need to edit files, call the actual edit/write tools. Do not print tool-call syntax, patches, or pseudo-tool calls as text.",
17
17
  ].join("\n");