wave-agent-sdk 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent.d.ts +23 -1
- package/dist/agent.js +34 -4
- package/dist/managers/aiManager.d.ts +36 -0
- package/dist/managers/aiManager.js +240 -8
- package/dist/managers/messageManager.d.ts +1 -3
- package/dist/managers/messageManager.js +19 -9
- package/dist/managers/slashCommandManager.js +28 -0
- package/dist/managers/subagentManager.d.ts +0 -2
- package/dist/services/session.js +2 -9
- package/dist/telemetry/instrumentation.d.ts +1 -1
- package/dist/telemetry/instrumentation.js +57 -28
- package/dist/tools/bashTool.js +16 -6
- package/dist/tools/grepTool.js +11 -3
- package/dist/types/index.d.ts +3 -0
- package/dist/types/messaging.d.ts +2 -0
- package/dist/utils/containerSetup.js +0 -3
- package/dist/utils/messageOperations.d.ts +10 -1
- package/dist/utils/messageOperations.js +19 -1
- package/dist/utils/notificationXml.js +5 -0
- package/dist/utils/ripgrep.d.ts +4 -4
- package/dist/utils/ripgrep.js +4 -27
- package/package.json +20 -8
- package/bin/rg +0 -79
- package/dist/managers/forkedAgentManager.d.ts +0 -50
- package/dist/managers/forkedAgentManager.js +0 -130
- package/scripts/install_ripgrep.js +0 -111
- package/scripts/postinstall.js +0 -38
- package/src/agent.ts +0 -1271
- package/src/constants/subagents.ts +0 -4
- package/src/constants/toolLimits.ts +0 -15
- package/src/constants/tools.ts +0 -24
- package/src/core/plugin.ts +0 -237
- package/src/core/session.ts +0 -9
- package/src/index.ts +0 -38
- package/src/managers/MemoryRuleManager.ts +0 -198
- package/src/managers/aiManager.ts +0 -2429
- package/src/managers/backgroundTaskManager.ts +0 -480
- package/src/managers/bangManager.ts +0 -119
- package/src/managers/cronManager.ts +0 -401
- package/src/managers/foregroundTaskManager.ts +0 -30
- package/src/managers/forkedAgentManager.ts +0 -216
- package/src/managers/hookManager.ts +0 -1089
- package/src/managers/liveConfigManager.ts +0 -445
- package/src/managers/lspManager.ts +0 -470
- package/src/managers/mcpManager.ts +0 -1010
- package/src/managers/messageManager.ts +0 -1180
- package/src/managers/messageQueue.ts +0 -157
- package/src/managers/permissionManager.ts +0 -1140
- package/src/managers/planManager.ts +0 -112
- package/src/managers/pluginManager.ts +0 -330
- package/src/managers/pluginScopeManager.ts +0 -124
- package/src/managers/reversionManager.ts +0 -178
- package/src/managers/skillManager.ts +0 -578
- package/src/managers/slashCommandManager.ts +0 -564
- package/src/managers/subagentManager.ts +0 -915
- package/src/managers/toolManager.ts +0 -489
- package/src/managers/workflowManager.ts +0 -493
- package/src/prompts/autoMemory.ts +0 -33
- package/src/prompts/autoMemoryExtraction.ts +0 -146
- package/src/prompts/index.ts +0 -506
- package/src/prompts/planModeReminders.ts +0 -126
- package/src/services/GitService.ts +0 -131
- package/src/services/MarketplaceService.ts +0 -1052
- package/src/services/MemoryRuleService.ts +0 -71
- package/src/services/aiService.ts +0 -919
- package/src/services/authService.ts +0 -565
- package/src/services/autoMemoryService.ts +0 -280
- package/src/services/configurationService.ts +0 -1520
- package/src/services/fileWatcher.ts +0 -319
- package/src/services/hook.ts +0 -348
- package/src/services/initializationService.ts +0 -357
- package/src/services/interactionService.ts +0 -229
- package/src/services/jsonlHandler.ts +0 -295
- package/src/services/memory.ts +0 -234
- package/src/services/pluginLoader.ts +0 -252
- package/src/services/remoteSettingsService.ts +0 -366
- package/src/services/reversionService.ts +0 -122
- package/src/services/session.ts +0 -922
- package/src/services/taskManager.ts +0 -321
- package/src/telemetry/events.ts +0 -65
- package/src/telemetry/instrumentation.ts +0 -499
- package/src/telemetry/sessionTracing.ts +0 -348
- package/src/tools/agentTool.ts +0 -322
- package/src/tools/askUserQuestion.ts +0 -160
- package/src/tools/bashTool.ts +0 -621
- package/src/tools/buildTool.ts +0 -61
- package/src/tools/cronCreateTool.ts +0 -161
- package/src/tools/cronDeleteTool.ts +0 -51
- package/src/tools/cronListTool.ts +0 -47
- package/src/tools/editTool.ts +0 -313
- package/src/tools/enterPlanMode.ts +0 -124
- package/src/tools/enterWorktreeTool.ts +0 -190
- package/src/tools/exitPlanMode.ts +0 -139
- package/src/tools/exitWorktreeTool.ts +0 -236
- package/src/tools/globTool.ts +0 -167
- package/src/tools/grepTool.ts +0 -399
- package/src/tools/lspTool.ts +0 -883
- package/src/tools/readTool.ts +0 -426
- package/src/tools/skillTool.ts +0 -248
- package/src/tools/taskManagementTools.ts +0 -574
- package/src/tools/taskStopTool.ts +0 -78
- package/src/tools/types.ts +0 -137
- package/src/tools/webFetchTool.ts +0 -373
- package/src/tools/workflowTool.ts +0 -205
- package/src/tools/writeTool.ts +0 -267
- package/src/types/agent.ts +0 -128
- package/src/types/auth.ts +0 -19
- package/src/types/commands.ts +0 -30
- package/src/types/config.ts +0 -43
- package/src/types/configuration.ts +0 -168
- package/src/types/core.ts +0 -105
- package/src/types/cron.ts +0 -13
- package/src/types/environment.ts +0 -106
- package/src/types/fileSearch.ts +0 -4
- package/src/types/history.ts +0 -7
- package/src/types/hooks.ts +0 -256
- package/src/types/index.ts +0 -41
- package/src/types/lsp.ts +0 -98
- package/src/types/marketplace.ts +0 -71
- package/src/types/mcp.ts +0 -43
- package/src/types/memoryRule.ts +0 -31
- package/src/types/messaging.ts +0 -114
- package/src/types/permissions.ts +0 -80
- package/src/types/plugins.ts +0 -39
- package/src/types/processes.ts +0 -99
- package/src/types/reversion.ts +0 -29
- package/src/types/session.ts +0 -12
- package/src/types/skills.ts +0 -108
- package/src/types/tasks.ts +0 -13
- package/src/types/telemetry.ts +0 -98
- package/src/types/tools.ts +0 -44
- package/src/types/workflow.ts +0 -6
- package/src/utils/abortUtils.ts +0 -118
- package/src/utils/atomicWrite.ts +0 -61
- package/src/utils/bashParser.ts +0 -899
- package/src/utils/cacheControlUtils.ts +0 -426
- package/src/utils/commandArgumentParser.ts +0 -161
- package/src/utils/commandPathResolver.ts +0 -88
- package/src/utils/configPaths.ts +0 -220
- package/src/utils/configValidator.ts +0 -100
- package/src/utils/constants.ts +0 -39
- package/src/utils/container.ts +0 -92
- package/src/utils/containerSetup.ts +0 -371
- package/src/utils/convertMessagesForAPI.ts +0 -326
- package/src/utils/cronTasks.ts +0 -128
- package/src/utils/cronTasksLock.ts +0 -182
- package/src/utils/cronToHuman.ts +0 -99
- package/src/utils/customCommands.ts +0 -95
- package/src/utils/editUtils.ts +0 -20
- package/src/utils/fileFormat.ts +0 -40
- package/src/utils/fileSearch.ts +0 -151
- package/src/utils/fileUtils.ts +0 -223
- package/src/utils/gitUtils.ts +0 -315
- package/src/utils/globalLogger.ts +0 -128
- package/src/utils/groupMessagesByApiRound.ts +0 -121
- package/src/utils/hookMatcher.ts +0 -176
- package/src/utils/markdownParser.ts +0 -317
- package/src/utils/mcpUtils.ts +0 -138
- package/src/utils/messageOperations.ts +0 -641
- package/src/utils/modelCapabilities.ts +0 -30
- package/src/utils/nameGenerator.ts +0 -99
- package/src/utils/notificationXml.ts +0 -57
- package/src/utils/openaiClient.ts +0 -251
- package/src/utils/parseCronExpression.ts +0 -78
- package/src/utils/path.ts +0 -72
- package/src/utils/pathEncoder.ts +0 -400
- package/src/utils/pathSafety.ts +0 -40
- package/src/utils/promptHistory.ts +0 -168
- package/src/utils/ripgrep.ts +0 -35
- package/src/utils/shellResolver.ts +0 -190
- package/src/utils/skillParser.ts +0 -246
- package/src/utils/stringUtils.ts +0 -156
- package/src/utils/subagentParser.ts +0 -279
- package/src/utils/taskReminder.ts +0 -96
- package/src/utils/tokenCalculation.ts +0 -43
- package/src/utils/tokenEstimate.ts +0 -34
- package/src/utils/toolResultStorage.ts +0 -117
- package/src/utils/worktreeSession.ts +0 -26
- package/src/utils/worktreeUtils.ts +0 -786
- package/src/workflow/budgetTracker.ts +0 -34
- package/src/workflow/concurrencyLimiter.ts +0 -47
- package/src/workflow/journal.ts +0 -95
- package/src/workflow/progressReporter.ts +0 -141
- package/src/workflow/runState.ts +0 -65
- package/src/workflow/scriptRuntime.ts +0 -274
- package/src/workflow/structuredOutput.ts +0 -123
- package/src/workflow/types.ts +0 -101
- package/src/workflow/workflowApis.ts +0 -410
- package/vendor/ripgrep/linux-aarch64/rg +0 -0
- package/vendor/ripgrep/linux-x86_64/rg +0 -0
- package/vendor/ripgrep/macos-aarch64/rg +0 -0
- package/vendor/ripgrep/macos-x86_64/rg +0 -0
- package/vendor/ripgrep/windows-aarch64/rg.exe +0 -0
- package/vendor/ripgrep/windows-x86_64/rg.exe +0 -0
|
@@ -1,915 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from "crypto";
|
|
2
|
-
import * as os from "os";
|
|
3
|
-
import * as fs from "fs";
|
|
4
|
-
import * as path from "path";
|
|
5
|
-
import type { SubagentConfiguration } from "../utils/subagentParser.js";
|
|
6
|
-
import type { Message, Usage } from "../types/index.js";
|
|
7
|
-
import { AIManager } from "./aiManager.js";
|
|
8
|
-
import { MessageManager } from "./messageManager.js";
|
|
9
|
-
import { ToolManager } from "./toolManager.js";
|
|
10
|
-
import {
|
|
11
|
-
AGENT_TOOL_NAME,
|
|
12
|
-
TASK_CREATE_TOOL_NAME,
|
|
13
|
-
TASK_GET_TOOL_NAME,
|
|
14
|
-
TASK_LIST_TOOL_NAME,
|
|
15
|
-
TASK_UPDATE_TOOL_NAME,
|
|
16
|
-
} from "../constants/tools.js";
|
|
17
|
-
import {
|
|
18
|
-
addConsolidatedAbortListener,
|
|
19
|
-
createAbortPromise,
|
|
20
|
-
} from "../utils/abortUtils.js";
|
|
21
|
-
import { BackgroundTaskManager } from "./backgroundTaskManager.js";
|
|
22
|
-
import { MessageQueue } from "./messageQueue.js";
|
|
23
|
-
import { logger } from "../utils/globalLogger.js";
|
|
24
|
-
import {
|
|
25
|
-
UserMessageParams,
|
|
26
|
-
type ToolBlockUpdateCallbackParams,
|
|
27
|
-
} from "../utils/messageOperations.js";
|
|
28
|
-
|
|
29
|
-
import { Container } from "../utils/container.js";
|
|
30
|
-
import type { PermissionManager } from "./permissionManager.js";
|
|
31
|
-
import type { PermissionMode } from "../types/permissions.js";
|
|
32
|
-
import { ConfigurationService } from "../services/configurationService.js";
|
|
33
|
-
|
|
34
|
-
export interface SubagentManagerCallbacks {
|
|
35
|
-
// Granular subagent message callbacks (009-subagent)
|
|
36
|
-
/** Triggered when subagent adds user message */
|
|
37
|
-
onSubagentUserMessageAdded?: (
|
|
38
|
-
subagentId: string,
|
|
39
|
-
params: UserMessageParams,
|
|
40
|
-
) => void;
|
|
41
|
-
/** Triggered when subagent creates assistant message */
|
|
42
|
-
onSubagentAssistantMessageAdded?: (
|
|
43
|
-
subagentId: string,
|
|
44
|
-
messageId: string,
|
|
45
|
-
) => void;
|
|
46
|
-
/** Triggered during subagent content streaming updates */
|
|
47
|
-
onSubagentAssistantContentUpdated?: (params: {
|
|
48
|
-
subagentId: string;
|
|
49
|
-
messageId: string;
|
|
50
|
-
chunk: string;
|
|
51
|
-
accumulated: string;
|
|
52
|
-
stage: "streaming" | "end";
|
|
53
|
-
}) => void;
|
|
54
|
-
/** Triggered during subagent reasoning streaming updates */
|
|
55
|
-
onSubagentAssistantReasoningUpdated?: (params: {
|
|
56
|
-
subagentId: string;
|
|
57
|
-
messageId: string;
|
|
58
|
-
chunk: string;
|
|
59
|
-
accumulated: string;
|
|
60
|
-
stage: "streaming" | "end";
|
|
61
|
-
}) => void;
|
|
62
|
-
/** Triggered when subagent tool block is updated */
|
|
63
|
-
onSubagentToolBlockUpdated?: (
|
|
64
|
-
subagentId: string,
|
|
65
|
-
params: ToolBlockUpdateCallbackParams,
|
|
66
|
-
) => void;
|
|
67
|
-
/** Triggered when subagent messages change */
|
|
68
|
-
onSubagentMessagesChange?: (subagentId: string, messages: Message[]) => void;
|
|
69
|
-
/** Triggered when subagent latest total tokens change */
|
|
70
|
-
onSubagentLatestTotalTokensChange?: (
|
|
71
|
-
subagentId: string,
|
|
72
|
-
tokens: number,
|
|
73
|
-
) => void;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export interface SubagentInstance {
|
|
77
|
-
subagentId: string;
|
|
78
|
-
configuration: SubagentConfiguration;
|
|
79
|
-
aiManager: AIManager;
|
|
80
|
-
messageManager: MessageManager;
|
|
81
|
-
toolManager: ToolManager;
|
|
82
|
-
permissionManager: PermissionManager;
|
|
83
|
-
backgroundTaskManager: BackgroundTaskManager;
|
|
84
|
-
status: "initializing" | "active" | "completed" | "error" | "aborted";
|
|
85
|
-
messages: Message[];
|
|
86
|
-
usedTools: {
|
|
87
|
-
name: string;
|
|
88
|
-
parameters: string;
|
|
89
|
-
compactParams?: string;
|
|
90
|
-
stage?: string;
|
|
91
|
-
}[]; // Track tools with display info
|
|
92
|
-
subagentType: string; // Store the subagent type for hook context
|
|
93
|
-
description: string; // Store the AI-generated description
|
|
94
|
-
allowedTools?: string[]; // Optional permission rules (e.g. git:*)
|
|
95
|
-
backgroundTaskId?: string; // ID of the background task if transitioned
|
|
96
|
-
onUpdate?: () => void; // Optional callback for real-time updates
|
|
97
|
-
model?: string; // Optional model override
|
|
98
|
-
logStream?: fs.WriteStream; // Optional log stream for background tasks
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export interface SubagentManagerOptions {
|
|
102
|
-
workdir: string;
|
|
103
|
-
callbacks?: SubagentManagerCallbacks; // Use SubagentManagerCallbacks instead of parentCallbacks
|
|
104
|
-
onUsageAdded?: (usage: Usage) => void;
|
|
105
|
-
stream: boolean;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export class SubagentManager {
|
|
109
|
-
private instances = new Map<string, SubagentInstance>();
|
|
110
|
-
private cachedConfigurations: SubagentConfiguration[] | null = null;
|
|
111
|
-
|
|
112
|
-
private workdir: string;
|
|
113
|
-
private callbacks?: SubagentManagerCallbacks; // Use SubagentManagerCallbacks instead of parentCallbacks
|
|
114
|
-
private onUsageAdded?: (usage: Usage) => void;
|
|
115
|
-
private container: Container;
|
|
116
|
-
private stream: boolean;
|
|
117
|
-
|
|
118
|
-
constructor(container: Container, options: SubagentManagerOptions) {
|
|
119
|
-
this.container = container;
|
|
120
|
-
this.workdir = options.workdir;
|
|
121
|
-
this.callbacks = options.callbacks; // Store SubagentManagerCallbacks
|
|
122
|
-
this.onUsageAdded = options.onUsageAdded;
|
|
123
|
-
this.stream = options.stream;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
private get configurationService(): ConfigurationService {
|
|
127
|
-
return this.container.get<ConfigurationService>("ConfigurationService")!;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Initialize the SubagentManager by loading and caching configurations
|
|
132
|
-
*/
|
|
133
|
-
async initialize(): Promise<void> {
|
|
134
|
-
await this.loadConfigurations();
|
|
135
|
-
|
|
136
|
-
// Hook into parent PermissionManager's update methods to propagate rules to subagents
|
|
137
|
-
const parentPm = this.container.get<PermissionManager>("PermissionManager");
|
|
138
|
-
if (
|
|
139
|
-
parentPm &&
|
|
140
|
-
typeof parentPm.updateAllowedRules === "function" &&
|
|
141
|
-
typeof parentPm.updateDeniedRules === "function" &&
|
|
142
|
-
typeof parentPm.updateAdditionalDirectories === "function"
|
|
143
|
-
) {
|
|
144
|
-
const origUpdateAllowed = parentPm.updateAllowedRules.bind(parentPm);
|
|
145
|
-
const origUpdateDenied = parentPm.updateDeniedRules.bind(parentPm);
|
|
146
|
-
const origUpdateDirs =
|
|
147
|
-
parentPm.updateAdditionalDirectories.bind(parentPm);
|
|
148
|
-
|
|
149
|
-
parentPm.updateAllowedRules = (rules: string[]) => {
|
|
150
|
-
origUpdateAllowed(rules);
|
|
151
|
-
this.syncPermissionRulesToSubagents();
|
|
152
|
-
};
|
|
153
|
-
parentPm.updateDeniedRules = (rules: string[]) => {
|
|
154
|
-
origUpdateDenied(rules);
|
|
155
|
-
this.syncPermissionRulesToSubagents();
|
|
156
|
-
};
|
|
157
|
-
parentPm.updateAdditionalDirectories = (directories: string[]) => {
|
|
158
|
-
origUpdateDirs(directories);
|
|
159
|
-
this.syncPermissionRulesToSubagents();
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Sync parent permission rules to all running subagents
|
|
166
|
-
*/
|
|
167
|
-
private syncPermissionRulesToSubagents(): void {
|
|
168
|
-
const parentPm = this.container.get<PermissionManager>("PermissionManager");
|
|
169
|
-
if (!parentPm) return;
|
|
170
|
-
|
|
171
|
-
for (const instance of this.instances.values()) {
|
|
172
|
-
instance.permissionManager.updateAllowedRules(parentPm.getAllowedRules());
|
|
173
|
-
instance.permissionManager.updateDeniedRules(parentPm.getDeniedRules());
|
|
174
|
-
instance.permissionManager.updateAdditionalDirectories(
|
|
175
|
-
parentPm.getAdditionalDirectories(),
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Load all available subagent configurations and cache them
|
|
182
|
-
*/
|
|
183
|
-
async loadConfigurations(): Promise<SubagentConfiguration[]> {
|
|
184
|
-
if (this.cachedConfigurations === null) {
|
|
185
|
-
const { loadSubagentConfigurations } = await import(
|
|
186
|
-
"../utils/subagentParser.js"
|
|
187
|
-
);
|
|
188
|
-
this.cachedConfigurations = await loadSubagentConfigurations(
|
|
189
|
-
this.workdir,
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
return this.cachedConfigurations;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Get cached configurations synchronously (must call loadConfigurations first)
|
|
197
|
-
*/
|
|
198
|
-
getConfigurations(): SubagentConfiguration[] {
|
|
199
|
-
if (this.cachedConfigurations === null) {
|
|
200
|
-
throw new Error(
|
|
201
|
-
"SubagentManager not initialized. Call loadConfigurations() first.",
|
|
202
|
-
);
|
|
203
|
-
}
|
|
204
|
-
return this.cachedConfigurations;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Find subagent by exact name match
|
|
209
|
-
*/
|
|
210
|
-
async findSubagent(name: string) {
|
|
211
|
-
// Check cached configurations first (includes plugin agents)
|
|
212
|
-
if (this.cachedConfigurations !== null) {
|
|
213
|
-
const cached = this.cachedConfigurations.find(
|
|
214
|
-
(config) => config.name === name,
|
|
215
|
-
);
|
|
216
|
-
if (cached) return cached;
|
|
217
|
-
}
|
|
218
|
-
// Fall back to filesystem scan for non-plugin agents
|
|
219
|
-
const { findSubagentByName } = await import("../utils/subagentParser.js");
|
|
220
|
-
return findSubagentByName(name, this.workdir);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Register plugin agents into the cached configurations.
|
|
225
|
-
* Names each agent as `pluginName:agentName` to avoid collisions.
|
|
226
|
-
*/
|
|
227
|
-
registerPluginAgents(
|
|
228
|
-
pluginName: string,
|
|
229
|
-
agents: SubagentConfiguration[],
|
|
230
|
-
): void {
|
|
231
|
-
if (this.cachedConfigurations === null) {
|
|
232
|
-
// Should not happen if initialization order is correct
|
|
233
|
-
this.cachedConfigurations = [];
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Remove any previously registered agents for this plugin (by name prefix)
|
|
237
|
-
this.cachedConfigurations = this.cachedConfigurations.filter(
|
|
238
|
-
(config) => !config.name.startsWith(`${pluginName}:`),
|
|
239
|
-
);
|
|
240
|
-
|
|
241
|
-
for (const agent of agents) {
|
|
242
|
-
const namespacedName = `${pluginName}:${agent.name}`;
|
|
243
|
-
const namespacedAgent: SubagentConfiguration = {
|
|
244
|
-
...agent,
|
|
245
|
-
name: namespacedName,
|
|
246
|
-
// Safety net: substitute any remaining ${WAVE_PLUGIN_ROOT} placeholders
|
|
247
|
-
systemPrompt: agent.systemPrompt.replace(
|
|
248
|
-
/\$\{WAVE_PLUGIN_ROOT\}/g,
|
|
249
|
-
agent.pluginRoot ?? "",
|
|
250
|
-
),
|
|
251
|
-
};
|
|
252
|
-
this.cachedConfigurations!.push(namespacedAgent);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// Re-sort by priority then name
|
|
256
|
-
this.cachedConfigurations!.sort((a, b) => {
|
|
257
|
-
if (a.priority !== b.priority) return a.priority - b.priority;
|
|
258
|
-
return a.name.localeCompare(b.name);
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Create a new subagent instance with isolated managers
|
|
264
|
-
*/
|
|
265
|
-
async createInstance(
|
|
266
|
-
configuration: SubagentConfiguration,
|
|
267
|
-
parameters: {
|
|
268
|
-
description: string;
|
|
269
|
-
prompt: string;
|
|
270
|
-
subagent_type: string;
|
|
271
|
-
allowedTools?: string[];
|
|
272
|
-
model?: string;
|
|
273
|
-
stream?: boolean;
|
|
274
|
-
permissionModeOverride?: PermissionMode;
|
|
275
|
-
maxTurns?: number;
|
|
276
|
-
},
|
|
277
|
-
runInBackground?: boolean,
|
|
278
|
-
onUpdate?: () => void,
|
|
279
|
-
): Promise<SubagentInstance> {
|
|
280
|
-
const parentToolManager = this.container.get<ToolManager>("ToolManager");
|
|
281
|
-
|
|
282
|
-
if (!parentToolManager) {
|
|
283
|
-
throw new Error(
|
|
284
|
-
"SubagentManager not properly initialized - ToolManager not found in container",
|
|
285
|
-
);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
const subagentId = randomUUID();
|
|
289
|
-
|
|
290
|
-
// Create a child container for the subagent to isolate its managers
|
|
291
|
-
const subagentContainer = this.container.createChild();
|
|
292
|
-
|
|
293
|
-
// Register an independent MessageQueue so the subagent's AIManager drains its
|
|
294
|
-
// own (empty) queue instead of falling back to the parent container's queue.
|
|
295
|
-
// Without this, concurrent background subagents steal sibling completion
|
|
296
|
-
// notifications from the parent queue, causing the main agent to exit early.
|
|
297
|
-
subagentContainer.register("MessageQueue", new MessageQueue());
|
|
298
|
-
|
|
299
|
-
// Register a modified AgentOptions without onLoadingChange to prevent subagent loading
|
|
300
|
-
// from affecting the parent agent's loading state
|
|
301
|
-
const parentOptions =
|
|
302
|
-
this.container.get<import("../types/agent.js").AgentOptions>(
|
|
303
|
-
"AgentOptions",
|
|
304
|
-
);
|
|
305
|
-
if (parentOptions) {
|
|
306
|
-
const subagentOptions: import("../types/agent.js").AgentOptions = {
|
|
307
|
-
...parentOptions,
|
|
308
|
-
defaultHeaders: {
|
|
309
|
-
...parentOptions.defaultHeaders,
|
|
310
|
-
...parentOptions.subagentHeaders?.[parameters.subagent_type],
|
|
311
|
-
},
|
|
312
|
-
callbacks: {
|
|
313
|
-
...parentOptions.callbacks,
|
|
314
|
-
onLoadingChange: undefined,
|
|
315
|
-
},
|
|
316
|
-
};
|
|
317
|
-
subagentContainer.register("AgentOptions", subagentOptions);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// Create isolated PermissionManager for the subagent
|
|
321
|
-
const { PermissionManager } = await import("./permissionManager.js");
|
|
322
|
-
const parentPermissionManager =
|
|
323
|
-
this.container.get<PermissionManager>("PermissionManager");
|
|
324
|
-
const subagentPermissionManager = new PermissionManager(subagentContainer, {
|
|
325
|
-
configuredPermissionMode:
|
|
326
|
-
parameters.permissionModeOverride ??
|
|
327
|
-
parentPermissionManager?.getConfiguredPermissionMode(),
|
|
328
|
-
allowedRules: parentPermissionManager?.getAllowedRules(),
|
|
329
|
-
deniedRules: parentPermissionManager?.getDeniedRules(),
|
|
330
|
-
instanceAllowedRules:
|
|
331
|
-
parentPermissionManager?.getInstanceAllowedRules?.(),
|
|
332
|
-
instanceDeniedRules: [
|
|
333
|
-
...(parentPermissionManager?.getInstanceDeniedRules?.() || []),
|
|
334
|
-
AGENT_TOOL_NAME, // Always deny Agent tool in subagents to prevent recursion
|
|
335
|
-
// Deny Task tools in subagents — they share the parent's TaskManager
|
|
336
|
-
// and could mutate the main agent's task list
|
|
337
|
-
TASK_CREATE_TOOL_NAME,
|
|
338
|
-
TASK_GET_TOOL_NAME,
|
|
339
|
-
TASK_UPDATE_TOOL_NAME,
|
|
340
|
-
TASK_LIST_TOOL_NAME,
|
|
341
|
-
],
|
|
342
|
-
additionalDirectories:
|
|
343
|
-
parentPermissionManager?.getAdditionalDirectories(),
|
|
344
|
-
instanceAdditionalDirectories:
|
|
345
|
-
parentPermissionManager?.getInstanceAdditionalDirectories?.(),
|
|
346
|
-
systemAdditionalDirectories:
|
|
347
|
-
parentPermissionManager?.getSystemAdditionalDirectories(),
|
|
348
|
-
planFilePath: parentPermissionManager?.getPlanFilePath(),
|
|
349
|
-
});
|
|
350
|
-
subagentContainer.register("PermissionManager", subagentPermissionManager);
|
|
351
|
-
|
|
352
|
-
// Register the permission mode override in the subagent container so it
|
|
353
|
-
// shadows the inherited parent value during tool execution
|
|
354
|
-
if (parameters.permissionModeOverride) {
|
|
355
|
-
subagentContainer.register(
|
|
356
|
-
"PermissionMode",
|
|
357
|
-
parameters.permissionModeOverride,
|
|
358
|
-
);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// Add temporary permission rules if provided
|
|
362
|
-
if (parameters.allowedTools) {
|
|
363
|
-
logger.debug(
|
|
364
|
-
`Adding ${parameters.allowedTools.length} temporary permission rules to subagent ${subagentId}`,
|
|
365
|
-
{ rules: parameters.allowedTools },
|
|
366
|
-
);
|
|
367
|
-
subagentPermissionManager.addTemporaryRules(parameters.allowedTools);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
// Create isolated MessageManager for the subagent
|
|
371
|
-
const subagentCallbacks = this.createSubagentCallbacks(subagentId);
|
|
372
|
-
|
|
373
|
-
const messageManager = new MessageManager(subagentContainer, {
|
|
374
|
-
callbacks: subagentCallbacks,
|
|
375
|
-
workdir: this.workdir,
|
|
376
|
-
sessionType: "subagent",
|
|
377
|
-
subagentType: parameters.subagent_type,
|
|
378
|
-
});
|
|
379
|
-
subagentContainer.register("MessageManager", messageManager);
|
|
380
|
-
|
|
381
|
-
// Create isolated ToolManager for the subagent to ensure it uses the subagent's PermissionManager
|
|
382
|
-
const toolManager = new ToolManager({
|
|
383
|
-
container: subagentContainer,
|
|
384
|
-
tools: configuration.tools,
|
|
385
|
-
});
|
|
386
|
-
toolManager.initializeBuiltInTools();
|
|
387
|
-
subagentContainer.register("ToolManager", toolManager);
|
|
388
|
-
|
|
389
|
-
// Create isolated AIManager for the subagent
|
|
390
|
-
const aiManager = new AIManager(subagentContainer, {
|
|
391
|
-
workdir: this.workdir,
|
|
392
|
-
systemPrompt: configuration.systemPrompt,
|
|
393
|
-
subagentType: parameters.subagent_type, // Pass subagent type for hook context
|
|
394
|
-
modelOverride: parameters.model || configuration.model, // Pass model override
|
|
395
|
-
stream: parameters.stream ?? this.stream, // Pass streaming mode flag
|
|
396
|
-
maxTurns: parameters.maxTurns, // Pass maxTurns limit
|
|
397
|
-
callbacks: {
|
|
398
|
-
onUsageAdded: this.onUsageAdded,
|
|
399
|
-
},
|
|
400
|
-
});
|
|
401
|
-
subagentContainer.register("AIManager", aiManager);
|
|
402
|
-
|
|
403
|
-
// Create isolated BackgroundTaskManager for the subagent/forked agent
|
|
404
|
-
const subagentBackgroundTaskManager = new BackgroundTaskManager(
|
|
405
|
-
subagentContainer,
|
|
406
|
-
{
|
|
407
|
-
workdir: this.workdir,
|
|
408
|
-
},
|
|
409
|
-
);
|
|
410
|
-
subagentContainer.register(
|
|
411
|
-
"BackgroundTaskManager",
|
|
412
|
-
subagentBackgroundTaskManager,
|
|
413
|
-
);
|
|
414
|
-
|
|
415
|
-
const instance: SubagentInstance = {
|
|
416
|
-
subagentId,
|
|
417
|
-
configuration,
|
|
418
|
-
aiManager,
|
|
419
|
-
messageManager,
|
|
420
|
-
toolManager,
|
|
421
|
-
permissionManager: subagentPermissionManager,
|
|
422
|
-
backgroundTaskManager: subagentBackgroundTaskManager,
|
|
423
|
-
status: "initializing",
|
|
424
|
-
messages: [],
|
|
425
|
-
usedTools: [], // Initialize usedTools
|
|
426
|
-
subagentType: parameters.subagent_type, // Store the subagent type
|
|
427
|
-
description: parameters.description, // Store the AI-generated description
|
|
428
|
-
allowedTools: parameters.allowedTools, // Store optional permission rules
|
|
429
|
-
model: parameters.model, // Store optional model override
|
|
430
|
-
onUpdate,
|
|
431
|
-
};
|
|
432
|
-
|
|
433
|
-
this.instances.set(subagentId, instance);
|
|
434
|
-
|
|
435
|
-
return instance;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
/**
|
|
439
|
-
* Execute agent using subagent instance
|
|
440
|
-
*
|
|
441
|
-
* IMPORTANT: This method automatically filters out the Agent tool from allowedTools
|
|
442
|
-
* to prevent subagents from spawning other subagents (infinite recursion protection)
|
|
443
|
-
*/
|
|
444
|
-
async executeAgent(
|
|
445
|
-
instance: SubagentInstance,
|
|
446
|
-
prompt: string,
|
|
447
|
-
abortSignal?: AbortSignal,
|
|
448
|
-
runInBackground?: boolean,
|
|
449
|
-
): Promise<string> {
|
|
450
|
-
try {
|
|
451
|
-
// Check if already aborted before starting
|
|
452
|
-
if (abortSignal?.aborted) {
|
|
453
|
-
throw new Error("Agent was aborted before execution started");
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
// Set status to active and update parent
|
|
457
|
-
this.updateInstanceStatus(instance.subagentId, "active");
|
|
458
|
-
|
|
459
|
-
const backgroundTaskManager = this.container.has("BackgroundTaskManager")
|
|
460
|
-
? this.container.get<BackgroundTaskManager>("BackgroundTaskManager")
|
|
461
|
-
: undefined;
|
|
462
|
-
|
|
463
|
-
if (runInBackground && backgroundTaskManager) {
|
|
464
|
-
const taskId = backgroundTaskManager.generateId();
|
|
465
|
-
const startTime = Date.now();
|
|
466
|
-
|
|
467
|
-
// Create log file
|
|
468
|
-
const logPath = path.join(os.tmpdir(), `wave-subagent-${taskId}.log`);
|
|
469
|
-
const logStream = fs.createWriteStream(logPath, { flags: "a" });
|
|
470
|
-
instance.logStream = logStream;
|
|
471
|
-
|
|
472
|
-
backgroundTaskManager.addTask({
|
|
473
|
-
id: taskId,
|
|
474
|
-
type: "subagent",
|
|
475
|
-
status: "running",
|
|
476
|
-
startTime,
|
|
477
|
-
description: instance.description,
|
|
478
|
-
stdout: "",
|
|
479
|
-
stderr: "",
|
|
480
|
-
outputPath: logPath,
|
|
481
|
-
subagentId: instance.subagentId,
|
|
482
|
-
onStop: () => {
|
|
483
|
-
instance.logStream?.destroy();
|
|
484
|
-
instance.logStream = undefined;
|
|
485
|
-
instance.aiManager.abortAIMessage();
|
|
486
|
-
this.cleanupInstance(instance.subagentId);
|
|
487
|
-
},
|
|
488
|
-
});
|
|
489
|
-
|
|
490
|
-
instance.backgroundTaskId = taskId;
|
|
491
|
-
|
|
492
|
-
// Execute in background
|
|
493
|
-
// Note: notification enqueueing is handled by internalExecute when instance.backgroundTaskId is set
|
|
494
|
-
(async () => {
|
|
495
|
-
try {
|
|
496
|
-
const result = await this.internalExecute(
|
|
497
|
-
instance,
|
|
498
|
-
prompt,
|
|
499
|
-
abortSignal,
|
|
500
|
-
);
|
|
501
|
-
const task = backgroundTaskManager?.getTask(taskId);
|
|
502
|
-
if (task) {
|
|
503
|
-
task.status = "completed";
|
|
504
|
-
task.stdout = result;
|
|
505
|
-
task.endTime = Date.now();
|
|
506
|
-
task.runtime = task.endTime - startTime;
|
|
507
|
-
}
|
|
508
|
-
} catch (error) {
|
|
509
|
-
const task = backgroundTaskManager?.getTask(taskId);
|
|
510
|
-
if (task) {
|
|
511
|
-
task.status = "failed";
|
|
512
|
-
task.stderr =
|
|
513
|
-
error instanceof Error ? error.message : String(error);
|
|
514
|
-
task.endTime = Date.now();
|
|
515
|
-
task.runtime = task.endTime - startTime;
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
})();
|
|
519
|
-
|
|
520
|
-
return taskId;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
return await this.internalExecute(instance, prompt, abortSignal);
|
|
524
|
-
} catch (error) {
|
|
525
|
-
this.updateInstanceStatus(instance.subagentId, "error");
|
|
526
|
-
throw error;
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
async backgroundInstance(subagentId: string): Promise<string> {
|
|
531
|
-
const instance = this.instances.get(subagentId);
|
|
532
|
-
if (!instance) {
|
|
533
|
-
throw new Error(`Subagent instance ${subagentId} not found`);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
const backgroundTaskManager = this.container.has("BackgroundTaskManager")
|
|
537
|
-
? this.container.get<BackgroundTaskManager>("BackgroundTaskManager")
|
|
538
|
-
: undefined;
|
|
539
|
-
|
|
540
|
-
if (!backgroundTaskManager) {
|
|
541
|
-
throw new Error("BackgroundTaskManager not available");
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
const taskId = backgroundTaskManager.generateId();
|
|
545
|
-
const startTime = Date.now();
|
|
546
|
-
|
|
547
|
-
// Create log file
|
|
548
|
-
const logPath = path.join(os.tmpdir(), `wave-subagent-${taskId}.log`);
|
|
549
|
-
const logStream = fs.createWriteStream(logPath, { flags: "a" });
|
|
550
|
-
instance.logStream = logStream;
|
|
551
|
-
|
|
552
|
-
backgroundTaskManager.addTask({
|
|
553
|
-
id: taskId,
|
|
554
|
-
type: "subagent",
|
|
555
|
-
status: "running",
|
|
556
|
-
startTime,
|
|
557
|
-
description: instance.description,
|
|
558
|
-
stdout: "",
|
|
559
|
-
stderr: "",
|
|
560
|
-
outputPath: logPath,
|
|
561
|
-
subagentId: instance.subagentId,
|
|
562
|
-
onStop: () => {
|
|
563
|
-
instance.logStream?.destroy();
|
|
564
|
-
instance.logStream = undefined;
|
|
565
|
-
instance.aiManager.abortAIMessage();
|
|
566
|
-
this.cleanupInstance(instance.subagentId);
|
|
567
|
-
},
|
|
568
|
-
});
|
|
569
|
-
|
|
570
|
-
instance.backgroundTaskId = taskId;
|
|
571
|
-
|
|
572
|
-
return taskId;
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
private async internalExecute(
|
|
576
|
-
instance: SubagentInstance,
|
|
577
|
-
prompt: string,
|
|
578
|
-
abortSignal?: AbortSignal,
|
|
579
|
-
): Promise<string> {
|
|
580
|
-
// Set up consolidated abort handler to prevent listener accumulation
|
|
581
|
-
let abortCleanup: (() => void) | undefined;
|
|
582
|
-
// Only link to parent abort signal if NOT running in background
|
|
583
|
-
if (abortSignal && !instance.backgroundTaskId) {
|
|
584
|
-
abortCleanup = addConsolidatedAbortListener(abortSignal, [
|
|
585
|
-
() => {
|
|
586
|
-
// Update status to aborted
|
|
587
|
-
this.updateInstanceStatus(instance.subagentId, "aborted");
|
|
588
|
-
// Cleanup instance immediately on abort
|
|
589
|
-
this.cleanupInstance(instance.subagentId);
|
|
590
|
-
},
|
|
591
|
-
() => {
|
|
592
|
-
// Abort the AI execution
|
|
593
|
-
instance.aiManager.abortAIMessage();
|
|
594
|
-
},
|
|
595
|
-
]);
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
try {
|
|
599
|
-
// Add the user's prompt as a message
|
|
600
|
-
instance.messageManager.addUserMessage({ content: prompt });
|
|
601
|
-
|
|
602
|
-
// Execute the AI request
|
|
603
|
-
// The AIManager will handle abort signals through its own abort controllers
|
|
604
|
-
const executeAI = instance.aiManager.sendAIMessage();
|
|
605
|
-
|
|
606
|
-
// If we have an abort signal, race against it using utilities to prevent listener accumulation
|
|
607
|
-
if (abortSignal && !instance.backgroundTaskId) {
|
|
608
|
-
await Promise.race([
|
|
609
|
-
executeAI,
|
|
610
|
-
createAbortPromise(abortSignal, "Agent was aborted"),
|
|
611
|
-
]);
|
|
612
|
-
} else {
|
|
613
|
-
await executeAI;
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
// Get the latest messages to extract the response
|
|
617
|
-
const messages = instance.messageManager.getMessages();
|
|
618
|
-
const lastAssistantMessage = messages
|
|
619
|
-
.filter((msg) => msg.role === "assistant")
|
|
620
|
-
.pop();
|
|
621
|
-
|
|
622
|
-
if (!lastAssistantMessage) {
|
|
623
|
-
throw new Error("No response from subagent");
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
// Extract text content from the last assistant message
|
|
627
|
-
const textBlocks = lastAssistantMessage.blocks.filter(
|
|
628
|
-
(block) => block.type === "text",
|
|
629
|
-
);
|
|
630
|
-
const response = textBlocks.map((block) => block.content).join("\n");
|
|
631
|
-
|
|
632
|
-
// Update status to completed and update parent
|
|
633
|
-
this.updateInstanceStatus(instance.subagentId, "completed");
|
|
634
|
-
|
|
635
|
-
const backgroundTaskManager = this.container.has("BackgroundTaskManager")
|
|
636
|
-
? this.container.get<BackgroundTaskManager>("BackgroundTaskManager")
|
|
637
|
-
: undefined;
|
|
638
|
-
|
|
639
|
-
// If this was transitioned to background, update the background task
|
|
640
|
-
if (instance.backgroundTaskId && backgroundTaskManager) {
|
|
641
|
-
// Write final response and completion status to log before closing
|
|
642
|
-
if (instance.logStream && response) {
|
|
643
|
-
instance.logStream.write(
|
|
644
|
-
`[${new Date().toISOString()}] Final response:\n${response}\n`,
|
|
645
|
-
);
|
|
646
|
-
}
|
|
647
|
-
instance.logStream?.write(
|
|
648
|
-
`[${new Date().toISOString()}] Agent completed\n`,
|
|
649
|
-
);
|
|
650
|
-
instance.logStream?.end();
|
|
651
|
-
const task = backgroundTaskManager.getTask(instance.backgroundTaskId);
|
|
652
|
-
if (task) {
|
|
653
|
-
const wasAlreadyKilled = task.status === "killed";
|
|
654
|
-
task.status = "completed";
|
|
655
|
-
task.stdout = response || "Agent completed with no text response";
|
|
656
|
-
task.endTime = Date.now();
|
|
657
|
-
if (task.startTime) {
|
|
658
|
-
task.runtime = task.endTime - task.startTime;
|
|
659
|
-
}
|
|
660
|
-
// Skip notification if task was already stopped (e.g. by main agent shutdown)
|
|
661
|
-
if (!wasAlreadyKilled) {
|
|
662
|
-
const messageQueue = this.container.has("MessageQueue")
|
|
663
|
-
? this.container.get<MessageQueue>("MessageQueue")
|
|
664
|
-
: undefined;
|
|
665
|
-
if (messageQueue) {
|
|
666
|
-
const summary = `Agent task "${instance.description}" completed`;
|
|
667
|
-
messageQueue.enqueueNotification(
|
|
668
|
-
`<task-notification>\n<task-id>${instance.backgroundTaskId}</task-id>\n<task-type>agent</task-type>\n<status>completed</status>\n<summary>${summary}</summary>\n</task-notification>`,
|
|
669
|
-
);
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
return response || "Agent completed with no text response";
|
|
676
|
-
} catch (error) {
|
|
677
|
-
const backgroundTaskManager = this.container.has("BackgroundTaskManager")
|
|
678
|
-
? this.container.get<BackgroundTaskManager>("BackgroundTaskManager")
|
|
679
|
-
: undefined;
|
|
680
|
-
|
|
681
|
-
// If this was transitioned to background, update the background task with error
|
|
682
|
-
if (instance.backgroundTaskId && backgroundTaskManager) {
|
|
683
|
-
// Write error to log before closing
|
|
684
|
-
instance.logStream?.write(
|
|
685
|
-
`[${new Date().toISOString()}] Agent failed: ${error instanceof Error ? error.message : String(error)}\n`,
|
|
686
|
-
);
|
|
687
|
-
instance.logStream?.end();
|
|
688
|
-
const task = backgroundTaskManager.getTask(instance.backgroundTaskId);
|
|
689
|
-
if (task) {
|
|
690
|
-
const wasAlreadyKilled = task.status === "killed";
|
|
691
|
-
task.status = "failed";
|
|
692
|
-
task.stderr = error instanceof Error ? error.message : String(error);
|
|
693
|
-
task.endTime = Date.now();
|
|
694
|
-
if (task.startTime) {
|
|
695
|
-
task.runtime = task.endTime - task.startTime;
|
|
696
|
-
}
|
|
697
|
-
// Skip notification if task was already stopped (e.g. by main agent shutdown)
|
|
698
|
-
if (!wasAlreadyKilled) {
|
|
699
|
-
const messageQueue = this.container.has("MessageQueue")
|
|
700
|
-
? this.container.get<MessageQueue>("MessageQueue")
|
|
701
|
-
: undefined;
|
|
702
|
-
if (messageQueue) {
|
|
703
|
-
const errorMsg =
|
|
704
|
-
error instanceof Error ? error.message : String(error);
|
|
705
|
-
const summary = `Agent task "${instance.description}" failed: ${errorMsg}`;
|
|
706
|
-
messageQueue.enqueueNotification(
|
|
707
|
-
`<task-notification>\n<task-id>${instance.backgroundTaskId}</task-id>\n<task-type>agent</task-type>\n<status>failed</status>\n<summary>${summary}</summary>\n</task-notification>`,
|
|
708
|
-
);
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
throw error;
|
|
714
|
-
} finally {
|
|
715
|
-
// Clean up abort listeners to prevent memory leaks
|
|
716
|
-
if (abortCleanup) {
|
|
717
|
-
abortCleanup();
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
/**
|
|
723
|
-
* Get instance by subagent ID
|
|
724
|
-
*/
|
|
725
|
-
getInstance(subagentId: string): SubagentInstance | null {
|
|
726
|
-
return this.instances.get(subagentId) || null;
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
/**
|
|
730
|
-
* Update instance status
|
|
731
|
-
*/
|
|
732
|
-
updateInstanceStatus(
|
|
733
|
-
subagentId: string,
|
|
734
|
-
status: SubagentInstance["status"],
|
|
735
|
-
): void {
|
|
736
|
-
const instance = this.instances.get(subagentId);
|
|
737
|
-
if (instance) {
|
|
738
|
-
instance.status = status;
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
/**
|
|
743
|
-
* Add message to instance
|
|
744
|
-
*/
|
|
745
|
-
addMessageToInstance(subagentId: string, message: Message): void {
|
|
746
|
-
const instance = this.instances.get(subagentId);
|
|
747
|
-
if (instance) {
|
|
748
|
-
instance.messages.push(message);
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
/**
|
|
753
|
-
* Clean up completed, errored, or aborted instances
|
|
754
|
-
*/
|
|
755
|
-
cleanupInstance(subagentId: string): void {
|
|
756
|
-
const instance = this.instances.get(subagentId);
|
|
757
|
-
if (
|
|
758
|
-
instance &&
|
|
759
|
-
(instance.status === "completed" ||
|
|
760
|
-
instance.status === "error" ||
|
|
761
|
-
instance.status === "aborted")
|
|
762
|
-
) {
|
|
763
|
-
this.instances.delete(subagentId);
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
/**
|
|
768
|
-
* Get all active instances
|
|
769
|
-
*/
|
|
770
|
-
getActiveInstances(): SubagentInstance[] {
|
|
771
|
-
return Array.from(this.instances.values()).filter(
|
|
772
|
-
(instance) =>
|
|
773
|
-
instance.status === "active" || instance.status === "initializing",
|
|
774
|
-
);
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
/**
|
|
778
|
-
* Clean up all instances (for session end)
|
|
779
|
-
*/
|
|
780
|
-
cleanup(): void {
|
|
781
|
-
for (const instance of this.instances.values()) {
|
|
782
|
-
instance.aiManager.abortAIMessage();
|
|
783
|
-
instance.backgroundTaskManager.cleanup();
|
|
784
|
-
}
|
|
785
|
-
this.instances.clear();
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
/**
|
|
789
|
-
* Create subagent callbacks for a specific subagent ID
|
|
790
|
-
* Extracted to reuse in both create and restore flows
|
|
791
|
-
*/
|
|
792
|
-
private createSubagentCallbacks(subagentId: string) {
|
|
793
|
-
return {
|
|
794
|
-
onUserMessageAdded: (params: UserMessageParams) => {
|
|
795
|
-
this.refreshSubagentState(subagentId);
|
|
796
|
-
// Forward user message events to parent via SubagentManager callbacks
|
|
797
|
-
if (this.callbacks?.onSubagentUserMessageAdded) {
|
|
798
|
-
this.callbacks.onSubagentUserMessageAdded(subagentId, params);
|
|
799
|
-
}
|
|
800
|
-
},
|
|
801
|
-
|
|
802
|
-
onAssistantMessageAdded: (messageId: string) => {
|
|
803
|
-
this.refreshSubagentState(subagentId);
|
|
804
|
-
// Forward assistant message events to parent via SubagentManager callbacks
|
|
805
|
-
if (this.callbacks?.onSubagentAssistantMessageAdded) {
|
|
806
|
-
this.callbacks.onSubagentAssistantMessageAdded(subagentId, messageId);
|
|
807
|
-
}
|
|
808
|
-
},
|
|
809
|
-
|
|
810
|
-
onAssistantContentUpdated: (params: {
|
|
811
|
-
messageId: string;
|
|
812
|
-
chunk: string;
|
|
813
|
-
accumulated: string;
|
|
814
|
-
stage: "streaming" | "end";
|
|
815
|
-
}) => {
|
|
816
|
-
this.refreshSubagentState(subagentId);
|
|
817
|
-
// Forward assistant content updates to parent via SubagentManager callbacks
|
|
818
|
-
if (this.callbacks?.onSubagentAssistantContentUpdated) {
|
|
819
|
-
this.callbacks.onSubagentAssistantContentUpdated({
|
|
820
|
-
...params,
|
|
821
|
-
subagentId,
|
|
822
|
-
});
|
|
823
|
-
}
|
|
824
|
-
},
|
|
825
|
-
onAssistantReasoningUpdated: (params: {
|
|
826
|
-
messageId: string;
|
|
827
|
-
chunk: string;
|
|
828
|
-
accumulated: string;
|
|
829
|
-
stage: "streaming" | "end";
|
|
830
|
-
}) => {
|
|
831
|
-
this.refreshSubagentState(subagentId);
|
|
832
|
-
// Forward assistant reasoning updates to parent via SubagentManager callbacks
|
|
833
|
-
if (this.callbacks?.onSubagentAssistantReasoningUpdated) {
|
|
834
|
-
this.callbacks.onSubagentAssistantReasoningUpdated({
|
|
835
|
-
...params,
|
|
836
|
-
subagentId,
|
|
837
|
-
});
|
|
838
|
-
}
|
|
839
|
-
},
|
|
840
|
-
|
|
841
|
-
onToolBlockUpdated: (params: ToolBlockUpdateCallbackParams) => {
|
|
842
|
-
this.refreshSubagentState(subagentId);
|
|
843
|
-
const instance = this.instances.get(subagentId);
|
|
844
|
-
if (instance) {
|
|
845
|
-
// Log tool execution to file only when finalized
|
|
846
|
-
if (instance.logStream && params.stage === "end") {
|
|
847
|
-
const displayParams =
|
|
848
|
-
params.compactParams ||
|
|
849
|
-
(params.parameters || "").substring(0, 100);
|
|
850
|
-
instance.logStream.write(
|
|
851
|
-
`[${new Date().toISOString()}] ${params.name}${displayParams ? ` ${displayParams}` : ""}\n`,
|
|
852
|
-
);
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
// Forward tool block updates to parent via SubagentManager callbacks
|
|
857
|
-
if (this.callbacks?.onSubagentToolBlockUpdated) {
|
|
858
|
-
this.callbacks.onSubagentToolBlockUpdated(subagentId, params);
|
|
859
|
-
}
|
|
860
|
-
},
|
|
861
|
-
|
|
862
|
-
onLatestTotalTokensChange: (tokens: number) => {
|
|
863
|
-
const instance = this.instances.get(subagentId);
|
|
864
|
-
if (instance) {
|
|
865
|
-
// Trigger the onUpdate callback if provided
|
|
866
|
-
instance.onUpdate?.();
|
|
867
|
-
}
|
|
868
|
-
// Forward latest total tokens to parent via SubagentManager callbacks
|
|
869
|
-
if (this.callbacks?.onSubagentLatestTotalTokensChange) {
|
|
870
|
-
this.callbacks.onSubagentLatestTotalTokensChange(subagentId, tokens);
|
|
871
|
-
}
|
|
872
|
-
},
|
|
873
|
-
|
|
874
|
-
onErrorBlockAdded: (error: string) => {
|
|
875
|
-
this.refreshSubagentState(subagentId);
|
|
876
|
-
const instance = this.instances.get(subagentId);
|
|
877
|
-
if (instance?.logStream) {
|
|
878
|
-
instance.logStream.write(
|
|
879
|
-
`[${new Date().toISOString()}] Error: ${error}\n`,
|
|
880
|
-
);
|
|
881
|
-
}
|
|
882
|
-
},
|
|
883
|
-
};
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
/**
|
|
887
|
-
* Pull the latest messages from the subagent instance's MessageManager and
|
|
888
|
-
* refresh the instance's cached messages, usedTools, onUpdate and the
|
|
889
|
-
* onSubagentMessagesChange forwarding. Triggered by incremental callbacks.
|
|
890
|
-
*/
|
|
891
|
-
private refreshSubagentState(subagentId: string): void {
|
|
892
|
-
const instance = this.instances.get(subagentId);
|
|
893
|
-
if (!instance) return;
|
|
894
|
-
|
|
895
|
-
const messages = instance.messageManager.getMessages();
|
|
896
|
-
instance.messages = messages;
|
|
897
|
-
// Compute usedTools from messages (last 2 tool blocks)
|
|
898
|
-
const toolBlocks = messages.flatMap(
|
|
899
|
-
(m) => m.blocks?.filter((b) => b.type === "tool") ?? [],
|
|
900
|
-
);
|
|
901
|
-
const last2 = toolBlocks.slice(-2);
|
|
902
|
-
instance.usedTools = last2.map((tb) => ({
|
|
903
|
-
name: tb.name ?? "",
|
|
904
|
-
parameters: tb.parameters ?? "",
|
|
905
|
-
compactParams: tb.compactParams,
|
|
906
|
-
stage: tb.stage,
|
|
907
|
-
}));
|
|
908
|
-
// Trigger the onUpdate callback if provided
|
|
909
|
-
instance.onUpdate?.();
|
|
910
|
-
// Forward subagent message changes to parent via callbacks
|
|
911
|
-
if (this.callbacks?.onSubagentMessagesChange) {
|
|
912
|
-
this.callbacks.onSubagentMessagesChange(subagentId, messages);
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
}
|