wave-agent-sdk 0.0.7 → 0.0.10

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 (240) hide show
  1. package/dist/agent.d.ts +105 -24
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +438 -53
  4. package/dist/index.d.ts +4 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +4 -0
  7. package/dist/managers/aiManager.d.ts +18 -7
  8. package/dist/managers/aiManager.d.ts.map +1 -1
  9. package/dist/managers/aiManager.js +254 -142
  10. package/dist/managers/backgroundBashManager.d.ts.map +1 -1
  11. package/dist/managers/backgroundBashManager.js +11 -9
  12. package/dist/managers/hookManager.d.ts +6 -6
  13. package/dist/managers/hookManager.d.ts.map +1 -1
  14. package/dist/managers/hookManager.js +81 -39
  15. package/dist/managers/liveConfigManager.d.ts +95 -0
  16. package/dist/managers/liveConfigManager.d.ts.map +1 -0
  17. package/dist/managers/liveConfigManager.js +442 -0
  18. package/dist/managers/lspManager.d.ts +43 -0
  19. package/dist/managers/lspManager.d.ts.map +1 -0
  20. package/dist/managers/lspManager.js +326 -0
  21. package/dist/managers/messageManager.d.ts +41 -24
  22. package/dist/managers/messageManager.d.ts.map +1 -1
  23. package/dist/managers/messageManager.js +184 -73
  24. package/dist/managers/permissionManager.d.ts +66 -0
  25. package/dist/managers/permissionManager.d.ts.map +1 -0
  26. package/dist/managers/permissionManager.js +208 -0
  27. package/dist/managers/skillManager.d.ts +1 -0
  28. package/dist/managers/skillManager.d.ts.map +1 -1
  29. package/dist/managers/skillManager.js +2 -1
  30. package/dist/managers/slashCommandManager.d.ts.map +1 -1
  31. package/dist/managers/slashCommandManager.js +4 -2
  32. package/dist/managers/subagentManager.d.ts +42 -6
  33. package/dist/managers/subagentManager.d.ts.map +1 -1
  34. package/dist/managers/subagentManager.js +213 -62
  35. package/dist/managers/toolManager.d.ts +38 -1
  36. package/dist/managers/toolManager.d.ts.map +1 -1
  37. package/dist/managers/toolManager.js +66 -2
  38. package/dist/services/aiService.d.ts +15 -5
  39. package/dist/services/aiService.d.ts.map +1 -1
  40. package/dist/services/aiService.js +446 -77
  41. package/dist/services/configurationService.d.ts +116 -0
  42. package/dist/services/configurationService.d.ts.map +1 -0
  43. package/dist/services/configurationService.js +585 -0
  44. package/dist/services/fileWatcher.d.ts +69 -0
  45. package/dist/services/fileWatcher.d.ts.map +1 -0
  46. package/dist/services/fileWatcher.js +212 -0
  47. package/dist/services/hook.d.ts +5 -40
  48. package/dist/services/hook.d.ts.map +1 -1
  49. package/dist/services/hook.js +47 -109
  50. package/dist/services/jsonlHandler.d.ts +71 -0
  51. package/dist/services/jsonlHandler.d.ts.map +1 -0
  52. package/dist/services/jsonlHandler.js +236 -0
  53. package/dist/services/memory.d.ts.map +1 -1
  54. package/dist/services/memory.js +33 -11
  55. package/dist/services/session.d.ts +116 -52
  56. package/dist/services/session.d.ts.map +1 -1
  57. package/dist/services/session.js +415 -143
  58. package/dist/tools/bashTool.d.ts.map +1 -1
  59. package/dist/tools/bashTool.js +77 -17
  60. package/dist/tools/deleteFileTool.d.ts.map +1 -1
  61. package/dist/tools/deleteFileTool.js +27 -1
  62. package/dist/tools/editTool.d.ts.map +1 -1
  63. package/dist/tools/editTool.js +33 -8
  64. package/dist/tools/lspTool.d.ts +6 -0
  65. package/dist/tools/lspTool.d.ts.map +1 -0
  66. package/dist/tools/lspTool.js +589 -0
  67. package/dist/tools/multiEditTool.d.ts.map +1 -1
  68. package/dist/tools/multiEditTool.js +30 -10
  69. package/dist/tools/readTool.d.ts.map +1 -1
  70. package/dist/tools/readTool.js +113 -3
  71. package/dist/tools/skillTool.js +2 -2
  72. package/dist/tools/todoWriteTool.d.ts.map +1 -1
  73. package/dist/tools/todoWriteTool.js +23 -0
  74. package/dist/tools/types.d.ts +11 -8
  75. package/dist/tools/types.d.ts.map +1 -1
  76. package/dist/tools/writeTool.d.ts.map +1 -1
  77. package/dist/tools/writeTool.js +30 -15
  78. package/dist/types/commands.d.ts +4 -1
  79. package/dist/types/commands.d.ts.map +1 -1
  80. package/dist/types/config.d.ts +4 -0
  81. package/dist/types/config.d.ts.map +1 -1
  82. package/dist/types/configuration.d.ts +69 -0
  83. package/dist/types/configuration.d.ts.map +1 -0
  84. package/dist/types/configuration.js +8 -0
  85. package/dist/types/core.d.ts +45 -0
  86. package/dist/types/core.d.ts.map +1 -1
  87. package/dist/types/environment.d.ts +83 -0
  88. package/dist/types/environment.d.ts.map +1 -0
  89. package/dist/types/environment.js +21 -0
  90. package/dist/types/fileSearch.d.ts +5 -0
  91. package/dist/types/fileSearch.d.ts.map +1 -0
  92. package/dist/types/fileSearch.js +1 -0
  93. package/dist/types/hooks.d.ts +18 -3
  94. package/dist/types/hooks.d.ts.map +1 -1
  95. package/dist/types/hooks.js +8 -8
  96. package/dist/types/index.d.ts +7 -0
  97. package/dist/types/index.d.ts.map +1 -1
  98. package/dist/types/index.js +7 -0
  99. package/dist/types/lsp.d.ts +90 -0
  100. package/dist/types/lsp.d.ts.map +1 -0
  101. package/dist/types/lsp.js +4 -0
  102. package/dist/types/messaging.d.ts +19 -12
  103. package/dist/types/messaging.d.ts.map +1 -1
  104. package/dist/types/permissions.d.ts +35 -0
  105. package/dist/types/permissions.d.ts.map +1 -0
  106. package/dist/types/permissions.js +12 -0
  107. package/dist/types/session.d.ts +15 -0
  108. package/dist/types/session.d.ts.map +1 -0
  109. package/dist/types/session.js +7 -0
  110. package/dist/types/skills.d.ts +1 -0
  111. package/dist/types/skills.d.ts.map +1 -1
  112. package/dist/types/tools.d.ts +35 -0
  113. package/dist/types/tools.d.ts.map +1 -0
  114. package/dist/types/tools.js +4 -0
  115. package/dist/utils/abortUtils.d.ts +34 -0
  116. package/dist/utils/abortUtils.d.ts.map +1 -0
  117. package/dist/utils/abortUtils.js +92 -0
  118. package/dist/utils/bashHistory.d.ts +4 -0
  119. package/dist/utils/bashHistory.d.ts.map +1 -1
  120. package/dist/utils/bashHistory.js +48 -30
  121. package/dist/utils/builtinSubagents.d.ts +7 -0
  122. package/dist/utils/builtinSubagents.d.ts.map +1 -0
  123. package/dist/utils/builtinSubagents.js +65 -0
  124. package/dist/utils/cacheControlUtils.d.ts +96 -0
  125. package/dist/utils/cacheControlUtils.d.ts.map +1 -0
  126. package/dist/utils/cacheControlUtils.js +324 -0
  127. package/dist/utils/commandPathResolver.d.ts +52 -0
  128. package/dist/utils/commandPathResolver.d.ts.map +1 -0
  129. package/dist/utils/commandPathResolver.js +145 -0
  130. package/dist/utils/configPaths.d.ts +85 -0
  131. package/dist/utils/configPaths.d.ts.map +1 -0
  132. package/dist/utils/configPaths.js +121 -0
  133. package/dist/utils/constants.d.ts +1 -13
  134. package/dist/utils/constants.d.ts.map +1 -1
  135. package/dist/utils/constants.js +2 -14
  136. package/dist/utils/convertMessagesForAPI.d.ts +2 -1
  137. package/dist/utils/convertMessagesForAPI.d.ts.map +1 -1
  138. package/dist/utils/convertMessagesForAPI.js +39 -18
  139. package/dist/utils/customCommands.d.ts.map +1 -1
  140. package/dist/utils/customCommands.js +66 -21
  141. package/dist/utils/fileSearch.d.ts +14 -0
  142. package/dist/utils/fileSearch.d.ts.map +1 -0
  143. package/dist/utils/fileSearch.js +88 -0
  144. package/dist/utils/fileUtils.d.ts +27 -0
  145. package/dist/utils/fileUtils.d.ts.map +1 -0
  146. package/dist/utils/fileUtils.js +145 -0
  147. package/dist/utils/globalLogger.d.ts +88 -0
  148. package/dist/utils/globalLogger.d.ts.map +1 -0
  149. package/dist/utils/globalLogger.js +120 -0
  150. package/dist/utils/largeOutputHandler.d.ts +15 -0
  151. package/dist/utils/largeOutputHandler.d.ts.map +1 -0
  152. package/dist/utils/largeOutputHandler.js +40 -0
  153. package/dist/utils/markdownParser.d.ts.map +1 -1
  154. package/dist/utils/markdownParser.js +1 -17
  155. package/dist/utils/mcpUtils.d.ts.map +1 -1
  156. package/dist/utils/mcpUtils.js +25 -3
  157. package/dist/utils/messageOperations.d.ts +20 -18
  158. package/dist/utils/messageOperations.d.ts.map +1 -1
  159. package/dist/utils/messageOperations.js +30 -38
  160. package/dist/utils/pathEncoder.d.ts +108 -0
  161. package/dist/utils/pathEncoder.d.ts.map +1 -0
  162. package/dist/utils/pathEncoder.js +279 -0
  163. package/dist/utils/subagentParser.d.ts +2 -2
  164. package/dist/utils/subagentParser.d.ts.map +1 -1
  165. package/dist/utils/subagentParser.js +12 -8
  166. package/dist/utils/tokenCalculation.d.ts +26 -0
  167. package/dist/utils/tokenCalculation.d.ts.map +1 -0
  168. package/dist/utils/tokenCalculation.js +36 -0
  169. package/dist/utils/tokenEstimator.d.ts +39 -0
  170. package/dist/utils/tokenEstimator.d.ts.map +1 -0
  171. package/dist/utils/tokenEstimator.js +55 -0
  172. package/package.json +6 -6
  173. package/src/agent.ts +586 -78
  174. package/src/index.ts +4 -0
  175. package/src/managers/aiManager.ts +341 -192
  176. package/src/managers/backgroundBashManager.ts +11 -9
  177. package/src/managers/hookManager.ts +102 -54
  178. package/src/managers/liveConfigManager.ts +634 -0
  179. package/src/managers/lspManager.ts +434 -0
  180. package/src/managers/messageManager.ts +258 -121
  181. package/src/managers/permissionManager.ts +276 -0
  182. package/src/managers/skillManager.ts +3 -1
  183. package/src/managers/slashCommandManager.ts +5 -3
  184. package/src/managers/subagentManager.ts +295 -76
  185. package/src/managers/toolManager.ts +95 -3
  186. package/src/services/aiService.ts +656 -84
  187. package/src/services/configurationService.ts +762 -0
  188. package/src/services/fileWatcher.ts +300 -0
  189. package/src/services/hook.ts +54 -144
  190. package/src/services/jsonlHandler.ts +303 -0
  191. package/src/services/memory.ts +34 -11
  192. package/src/services/session.ts +522 -173
  193. package/src/tools/bashTool.ts +94 -20
  194. package/src/tools/deleteFileTool.ts +38 -1
  195. package/src/tools/editTool.ts +44 -9
  196. package/src/tools/lspTool.ts +760 -0
  197. package/src/tools/multiEditTool.ts +41 -11
  198. package/src/tools/readTool.ts +127 -3
  199. package/src/tools/skillTool.ts +2 -2
  200. package/src/tools/todoWriteTool.ts +33 -1
  201. package/src/tools/types.ts +15 -9
  202. package/src/tools/writeTool.ts +43 -16
  203. package/src/types/commands.ts +6 -1
  204. package/src/types/config.ts +5 -0
  205. package/src/types/configuration.ts +73 -0
  206. package/src/types/core.ts +55 -0
  207. package/src/types/environment.ts +104 -0
  208. package/src/types/fileSearch.ts +4 -0
  209. package/src/types/hooks.ts +32 -16
  210. package/src/types/index.ts +7 -0
  211. package/src/types/lsp.ts +96 -0
  212. package/src/types/messaging.ts +21 -14
  213. package/src/types/permissions.ts +48 -0
  214. package/src/types/session.ts +20 -0
  215. package/src/types/skills.ts +1 -0
  216. package/src/types/tools.ts +38 -0
  217. package/src/utils/abortUtils.ts +118 -0
  218. package/src/utils/bashHistory.ts +55 -31
  219. package/src/utils/builtinSubagents.ts +71 -0
  220. package/src/utils/cacheControlUtils.ts +475 -0
  221. package/src/utils/commandPathResolver.ts +189 -0
  222. package/src/utils/configPaths.ts +163 -0
  223. package/src/utils/constants.ts +2 -17
  224. package/src/utils/convertMessagesForAPI.ts +44 -18
  225. package/src/utils/customCommands.ts +90 -22
  226. package/src/utils/fileSearch.ts +107 -0
  227. package/src/utils/fileUtils.ts +160 -0
  228. package/src/utils/globalLogger.ts +128 -0
  229. package/src/utils/largeOutputHandler.ts +55 -0
  230. package/src/utils/markdownParser.ts +1 -19
  231. package/src/utils/mcpUtils.ts +34 -3
  232. package/src/utils/messageOperations.ts +47 -53
  233. package/src/utils/pathEncoder.ts +394 -0
  234. package/src/utils/subagentParser.ts +13 -9
  235. package/src/utils/tokenCalculation.ts +43 -0
  236. package/src/utils/tokenEstimator.ts +68 -0
  237. package/dist/utils/configResolver.d.ts +0 -38
  238. package/dist/utils/configResolver.d.ts.map +0 -1
  239. package/dist/utils/configResolver.js +0 -106
  240. package/src/utils/configResolver.ts +0 -142
@@ -7,12 +7,49 @@ import type {
7
7
  ModelConfig,
8
8
  Usage,
9
9
  } from "../types/index.js";
10
+ import type { SessionData } from "../services/session.js";
10
11
  import { AIManager } from "./aiManager.js";
11
- import {
12
- MessageManager,
13
- type MessageManagerCallbacks,
14
- } from "./messageManager.js";
12
+ import { MessageManager } from "./messageManager.js";
15
13
  import { ToolManager } from "./toolManager.js";
14
+ import { HookManager } from "./hookManager.js";
15
+ import {
16
+ UserMessageParams,
17
+ type AgentToolBlockUpdateParams,
18
+ } from "../utils/messageOperations.js";
19
+ import {
20
+ addConsolidatedAbortListener,
21
+ createAbortPromise,
22
+ } from "../utils/abortUtils.js";
23
+
24
+ export interface SubagentManagerCallbacks {
25
+ // Granular subagent message callbacks (015-subagent-message-callbacks)
26
+ /** Triggered when subagent adds user message */
27
+ onSubagentUserMessageAdded?: (
28
+ subagentId: string,
29
+ params: UserMessageParams,
30
+ ) => void;
31
+ /** Triggered when subagent creates assistant message */
32
+ onSubagentAssistantMessageAdded?: (subagentId: string) => void;
33
+ /** Triggered during subagent content streaming updates */
34
+ onSubagentAssistantContentUpdated?: (
35
+ subagentId: string,
36
+ chunk: string,
37
+ accumulated: string,
38
+ ) => void;
39
+ /** Triggered during subagent reasoning streaming updates */
40
+ onSubagentAssistantReasoningUpdated?: (
41
+ subagentId: string,
42
+ chunk: string,
43
+ accumulated: string,
44
+ ) => void;
45
+ /** Triggered when subagent tool block is updated */
46
+ onSubagentToolBlockUpdated?: (
47
+ subagentId: string,
48
+ params: AgentToolBlockUpdateParams,
49
+ ) => void;
50
+ /** Triggered when subagent messages change */
51
+ onSubagentMessagesChange?: (subagentId: string, messages: Message[]) => void;
52
+ }
16
53
 
17
54
  export interface SubagentInstance {
18
55
  subagentId: string;
@@ -22,16 +59,19 @@ export interface SubagentInstance {
22
59
  toolManager: ToolManager;
23
60
  status: "initializing" | "active" | "completed" | "error" | "aborted";
24
61
  messages: Message[];
62
+ subagentType: string; // Store the subagent type for hook context
25
63
  }
26
64
 
27
65
  export interface SubagentManagerOptions {
28
66
  workdir: string;
29
67
  parentToolManager: ToolManager;
30
68
  parentMessageManager: MessageManager;
69
+ callbacks?: SubagentManagerCallbacks; // Use SubagentManagerCallbacks instead of parentCallbacks
31
70
  logger?: Logger;
32
- gatewayConfig: GatewayConfig;
33
- modelConfig: ModelConfig;
34
- tokenLimit: number;
71
+ getGatewayConfig: () => GatewayConfig;
72
+ getModelConfig: () => ModelConfig;
73
+ getTokenLimit: () => number;
74
+ hookManager?: HookManager;
35
75
  onUsageAdded?: (usage: Usage) => void;
36
76
  }
37
77
 
@@ -42,20 +82,24 @@ export class SubagentManager {
42
82
  private workdir: string;
43
83
  private parentToolManager: ToolManager;
44
84
  private parentMessageManager: MessageManager;
85
+ private callbacks?: SubagentManagerCallbacks; // Use SubagentManagerCallbacks instead of parentCallbacks
45
86
  private logger?: Logger;
46
- private gatewayConfig: GatewayConfig;
47
- private modelConfig: ModelConfig;
48
- private tokenLimit: number;
87
+ private getGatewayConfig: () => GatewayConfig;
88
+ private getModelConfig: () => ModelConfig;
89
+ private getTokenLimit: () => number;
90
+ private hookManager?: HookManager;
49
91
  private onUsageAdded?: (usage: Usage) => void;
50
92
 
51
93
  constructor(options: SubagentManagerOptions) {
52
94
  this.workdir = options.workdir;
53
95
  this.parentToolManager = options.parentToolManager;
54
96
  this.parentMessageManager = options.parentMessageManager;
97
+ this.callbacks = options.callbacks; // Store SubagentManagerCallbacks
55
98
  this.logger = options.logger;
56
- this.gatewayConfig = options.gatewayConfig;
57
- this.modelConfig = options.modelConfig;
58
- this.tokenLimit = options.tokenLimit;
99
+ this.getGatewayConfig = options.getGatewayConfig;
100
+ this.getModelConfig = options.getModelConfig;
101
+ this.getTokenLimit = options.getTokenLimit;
102
+ this.hookManager = options.hookManager;
59
103
  this.onUsageAdded = options.onUsageAdded;
60
104
  }
61
105
 
@@ -112,12 +156,7 @@ export class SubagentManager {
112
156
  subagent_type: string;
113
157
  },
114
158
  ): Promise<SubagentInstance> {
115
- if (
116
- !this.parentToolManager ||
117
- !this.gatewayConfig ||
118
- !this.modelConfig ||
119
- !this.tokenLimit
120
- ) {
159
+ if (!this.parentToolManager) {
121
160
  throw new Error(
122
161
  "SubagentManager not properly initialized - call initialize() first",
123
162
  );
@@ -126,35 +165,19 @@ export class SubagentManager {
126
165
  const subagentId = randomUUID();
127
166
 
128
167
  // Create isolated MessageManager for the subagent
129
- const subagentCallbacks: MessageManagerCallbacks = {
130
- // These callbacks will be handled by the parent agent
131
- onMessagesChange: (messages: Message[]) => {
132
- const instance = this.instances.get(subagentId);
133
- if (instance) {
134
- instance.messages = messages;
135
- // Update parent's subagent block with latest messages
136
- this.parentMessageManager.updateSubagentBlock(subagentId, {
137
- messages: messages,
138
- });
139
- }
140
- },
141
- };
168
+ const subagentCallbacks = this.createSubagentCallbacks(subagentId);
142
169
 
143
170
  const messageManager = new MessageManager({
144
171
  callbacks: subagentCallbacks,
145
172
  workdir: this.workdir,
146
173
  logger: this.logger,
174
+ sessionType: "subagent",
175
+ subagentType: parameters.subagent_type,
147
176
  });
148
177
 
149
178
  // Use the parent tool manager directly - tool restrictions will be handled by allowedTools parameter
150
179
  const toolManager = this.parentToolManager;
151
180
 
152
- // Determine model to use
153
- const modelToUse =
154
- configuration.model && configuration.model !== "inherit"
155
- ? configuration.model
156
- : this.modelConfig.agentModel;
157
-
158
181
  // Create isolated AIManager for the subagent
159
182
  const aiManager = new AIManager({
160
183
  messageManager,
@@ -162,12 +185,31 @@ export class SubagentManager {
162
185
  logger: this.logger,
163
186
  workdir: this.workdir,
164
187
  systemPrompt: configuration.systemPrompt,
165
- gatewayConfig: this.gatewayConfig,
166
- modelConfig: {
167
- ...this.modelConfig,
168
- agentModel: modelToUse,
188
+ subagentType: parameters.subagent_type, // Pass subagent type for hook context
189
+ hookManager: this.hookManager,
190
+ getGatewayConfig: this.getGatewayConfig,
191
+ getModelConfig: () => {
192
+ // Determine model dynamically each time
193
+ const parentModelConfig = this.getModelConfig();
194
+ let modelToUse: string;
195
+
196
+ if (!configuration.model || configuration.model === "inherit") {
197
+ // Use parent's agentModel for "inherit" or undefined
198
+ modelToUse = parentModelConfig.agentModel;
199
+ } else if (configuration.model === "fastModel") {
200
+ // Use parent's fastModel for special "fastModel" value
201
+ modelToUse = parentModelConfig.fastModel;
202
+ } else {
203
+ // Use specific model name
204
+ modelToUse = configuration.model;
205
+ }
206
+
207
+ return {
208
+ ...parentModelConfig,
209
+ agentModel: modelToUse,
210
+ };
169
211
  },
170
- tokenLimit: this.tokenLimit,
212
+ getTokenLimit: this.getTokenLimit,
171
213
  callbacks: {
172
214
  onUsageAdded: this.onUsageAdded,
173
215
  },
@@ -181,6 +223,7 @@ export class SubagentManager {
181
223
  toolManager,
182
224
  status: "initializing",
183
225
  messages: [],
226
+ subagentType: parameters.subagent_type, // Store the subagent type
184
227
  };
185
228
 
186
229
  this.instances.set(subagentId, instance);
@@ -189,8 +232,9 @@ export class SubagentManager {
189
232
  this.parentMessageManager.addSubagentBlock(
190
233
  subagentId,
191
234
  configuration.name,
235
+ messageManager.getSessionId(),
236
+ configuration,
192
237
  "active",
193
- [],
194
238
  parameters,
195
239
  );
196
240
 
@@ -220,15 +264,22 @@ export class SubagentManager {
220
264
  status: "active",
221
265
  });
222
266
 
223
- // Set up abort handler
267
+ // Set up consolidated abort handler to prevent listener accumulation
268
+ let abortCleanup: (() => void) | undefined;
224
269
  if (abortSignal) {
225
- abortSignal.addEventListener("abort", () => {
226
- this.updateInstanceStatus(instance.subagentId, "aborted");
227
- this.parentMessageManager.updateSubagentBlock(instance.subagentId, {
228
- status: "aborted",
229
- messages: instance.messages,
230
- });
231
- });
270
+ abortCleanup = addConsolidatedAbortListener(abortSignal, [
271
+ () => {
272
+ // Update status to aborted
273
+ this.updateInstanceStatus(instance.subagentId, "aborted");
274
+ this.parentMessageManager.updateSubagentBlock(instance.subagentId, {
275
+ status: "aborted",
276
+ });
277
+ },
278
+ () => {
279
+ // Abort the AI execution
280
+ instance.aiManager.abortAIMessage();
281
+ },
282
+ ]);
232
283
  }
233
284
 
234
285
  // Add the user's prompt as a message
@@ -248,32 +299,43 @@ export class SubagentManager {
248
299
 
249
300
  // Execute the AI request with tool restrictions
250
301
  // The AIManager will handle abort signals through its own abort controllers
251
- // We need to abort the AI execution if the external abort signal is triggered
302
+ // Resolve model name for sendAIMessage
303
+ let resolvedModel: string | undefined;
304
+ if (
305
+ instance.configuration.model &&
306
+ instance.configuration.model !== "inherit"
307
+ ) {
308
+ if (instance.configuration.model === "fastModel") {
309
+ // Use parent's fastModel for special "fastModel" value
310
+ const parentModelConfig = this.getModelConfig();
311
+ resolvedModel = parentModelConfig.fastModel;
312
+ } else {
313
+ // Use specific model name
314
+ resolvedModel = instance.configuration.model;
315
+ }
316
+ }
317
+ // For "inherit" or undefined, resolvedModel remains undefined (uses AIManager default)
318
+
252
319
  const executeAI = instance.aiManager.sendAIMessage({
253
320
  allowedTools,
254
- model:
255
- instance.configuration.model !== "inherit"
256
- ? instance.configuration.model
257
- : undefined,
321
+ model: resolvedModel,
258
322
  });
259
323
 
260
- // If we have an abort signal, race against it
261
- if (abortSignal) {
262
- await Promise.race([
263
- executeAI,
264
- new Promise<never>((_, reject) => {
265
- if (abortSignal.aborted) {
266
- reject(new Error("Task was aborted"));
267
- }
268
- abortSignal.addEventListener("abort", () => {
269
- // Abort the AI execution
270
- instance.aiManager.abortAIMessage();
271
- reject(new Error("Task was aborted"));
272
- });
273
- }),
274
- ]);
275
- } else {
276
- await executeAI;
324
+ try {
325
+ // If we have an abort signal, race against it using utilities to prevent listener accumulation
326
+ if (abortSignal) {
327
+ await Promise.race([
328
+ executeAI,
329
+ createAbortPromise(abortSignal, "Task was aborted"),
330
+ ]);
331
+ } else {
332
+ await executeAI;
333
+ }
334
+ } finally {
335
+ // Clean up abort listeners to prevent memory leaks
336
+ if (abortCleanup) {
337
+ abortCleanup();
338
+ }
277
339
  }
278
340
 
279
341
  // Get the latest messages to extract the response
@@ -292,11 +354,10 @@ export class SubagentManager {
292
354
  );
293
355
  const response = textBlocks.map((block) => block.content).join("\n");
294
356
 
295
- // Update status to completed and update parent with final messages
357
+ // Update status to completed and update parent
296
358
  this.updateInstanceStatus(instance.subagentId, "completed");
297
359
  this.parentMessageManager.updateSubagentBlock(instance.subagentId, {
298
360
  status: "completed",
299
- messages: messages,
300
361
  });
301
362
 
302
363
  return response || "Task completed with no text response";
@@ -370,4 +431,162 @@ export class SubagentManager {
370
431
  cleanup(): void {
371
432
  this.instances.clear();
372
433
  }
434
+
435
+ /**
436
+ * Restore subagent instances from saved session data
437
+ * This method is called during agent initialization to restore previous subagent states
438
+ */
439
+ async restoreSubagentSessions(
440
+ subagentSessions: Array<{
441
+ sessionData: SessionData;
442
+ subagentId: string;
443
+ configuration: SubagentConfiguration;
444
+ }>,
445
+ ): Promise<void> {
446
+ for (const { sessionData, subagentId, configuration } of subagentSessions) {
447
+ try {
448
+ // Use the configuration from the SubagentBlock
449
+
450
+ // Create the subagent instance without executing - just restore the state
451
+
452
+ // Create MessageManager for the restored subagent
453
+ const subagentCallbacks = this.createSubagentCallbacks(subagentId);
454
+ const messageManager = new MessageManager({
455
+ callbacks: subagentCallbacks,
456
+ workdir: this.workdir,
457
+ logger: this.logger,
458
+ sessionType: "subagent",
459
+ subagentType: configuration.name, // Use configuration name for restored sessions
460
+ });
461
+
462
+ // Use the parent tool manager
463
+ const toolManager = this.parentToolManager;
464
+
465
+ // Determine model to use
466
+ let modelToUse: string;
467
+ const parentModelConfig = this.getModelConfig();
468
+
469
+ if (!configuration.model || configuration.model === "inherit") {
470
+ modelToUse = parentModelConfig.agentModel;
471
+ } else if (configuration.model === "fastModel") {
472
+ modelToUse = parentModelConfig.fastModel;
473
+ } else {
474
+ modelToUse = configuration.model;
475
+ }
476
+
477
+ // Create AIManager for the restored subagent
478
+ const aiManager = new AIManager({
479
+ messageManager,
480
+ toolManager,
481
+ logger: this.logger,
482
+ workdir: this.workdir,
483
+ systemPrompt: configuration.systemPrompt,
484
+ subagentType: configuration.name, // Use configuration name as subagent type for restored instances
485
+ hookManager: this.hookManager,
486
+ getGatewayConfig: this.getGatewayConfig,
487
+ getModelConfig: () => ({
488
+ ...parentModelConfig,
489
+ agentModel: modelToUse,
490
+ }),
491
+ getTokenLimit: this.getTokenLimit,
492
+ callbacks: {
493
+ onUsageAdded: this.onUsageAdded,
494
+ },
495
+ });
496
+
497
+ // Create restored instance
498
+ const instance: SubagentInstance = {
499
+ subagentId,
500
+ configuration,
501
+ aiManager,
502
+ messageManager,
503
+ toolManager,
504
+ status: "completed", // Restored sessions are considered completed
505
+ messages: sessionData.messages,
506
+ subagentType: configuration.name, // Use configuration name as subagent type for restored instances
507
+ };
508
+
509
+ // IMPORTANT: Store instance in map BEFORE calling setMessages
510
+ // This ensures the callback can find the instance
511
+ this.instances.set(subagentId, instance);
512
+
513
+ messageManager.initializeFromSession(sessionData);
514
+ } catch (error) {
515
+ this.logger?.warn(
516
+ `Failed to restore subagent session ${subagentId}:`,
517
+ error,
518
+ );
519
+ // Continue with other sessions even if one fails
520
+ }
521
+ }
522
+ }
523
+
524
+ /**
525
+ * Create subagent callbacks for a specific subagent ID
526
+ * Extracted to reuse in both create and restore flows
527
+ */
528
+ private createSubagentCallbacks(subagentId: string) {
529
+ return {
530
+ onUserMessageAdded: (params: UserMessageParams) => {
531
+ // Forward user message events to parent via SubagentManager callbacks
532
+ if (this.callbacks?.onSubagentUserMessageAdded) {
533
+ this.callbacks.onSubagentUserMessageAdded(subagentId, params);
534
+ }
535
+ },
536
+
537
+ onAssistantMessageAdded: () => {
538
+ // Forward assistant message events to parent via SubagentManager callbacks
539
+ if (this.callbacks?.onSubagentAssistantMessageAdded) {
540
+ this.callbacks.onSubagentAssistantMessageAdded(subagentId);
541
+ }
542
+ },
543
+
544
+ onAssistantContentUpdated: (chunk: string, accumulated: string) => {
545
+ // Forward assistant content updates to parent via SubagentManager callbacks
546
+ if (this.callbacks?.onSubagentAssistantContentUpdated) {
547
+ this.callbacks.onSubagentAssistantContentUpdated(
548
+ subagentId,
549
+ chunk,
550
+ accumulated,
551
+ );
552
+ }
553
+ },
554
+ onAssistantReasoningUpdated: (chunk: string, accumulated: string) => {
555
+ // Forward assistant reasoning updates to parent via SubagentManager callbacks
556
+ if (this.callbacks?.onSubagentAssistantReasoningUpdated) {
557
+ this.callbacks.onSubagentAssistantReasoningUpdated(
558
+ subagentId,
559
+ chunk,
560
+ accumulated,
561
+ );
562
+ }
563
+ },
564
+
565
+ onToolBlockUpdated: (params: AgentToolBlockUpdateParams) => {
566
+ // Forward tool block updates to parent via SubagentManager callbacks
567
+ if (this.callbacks?.onSubagentToolBlockUpdated) {
568
+ this.callbacks.onSubagentToolBlockUpdated(subagentId, params);
569
+ }
570
+ },
571
+
572
+ // These callbacks will be handled by the parent agent
573
+ onMessagesChange: (messages: Message[]) => {
574
+ const instance = this.instances.get(subagentId);
575
+ if (instance) {
576
+ instance.messages = messages;
577
+ // Forward subagent message changes to parent via callbacks
578
+ if (this.callbacks?.onSubagentMessagesChange) {
579
+ this.callbacks.onSubagentMessagesChange(subagentId, messages);
580
+ }
581
+ }
582
+ },
583
+
584
+ onSessionIdChange: (newSessionId: string) => {
585
+ // Update the subagent block with the new session ID
586
+ this.parentMessageManager.updateSubagentBlock(subagentId, {
587
+ sessionId: newSessionId,
588
+ });
589
+ },
590
+ };
591
+ }
373
592
  }
@@ -10,30 +10,56 @@ import { grepTool } from "../tools/grepTool.js";
10
10
  import { lsTool } from "../tools/lsTool.js";
11
11
  import { readTool } from "../tools/readTool.js";
12
12
  import { todoWriteTool } from "../tools/todoWriteTool.js";
13
+ import { lspTool } from "../tools/lspTool.js";
13
14
  import { createTaskTool } from "../tools/taskTool.js";
14
15
  import { createSkillTool } from "../tools/skillTool.js";
15
16
  import { McpManager } from "./mcpManager.js";
17
+ import { PermissionManager } from "./permissionManager.js";
16
18
  import { ChatCompletionFunctionTool } from "openai/resources.js";
17
- import type { Logger } from "../types/index.js";
19
+ import type {
20
+ Logger,
21
+ PermissionMode,
22
+ PermissionCallback,
23
+ ILspManager,
24
+ } from "../types/index.js";
18
25
  import type { SubagentManager } from "./subagentManager.js";
19
26
  import type { SkillManager } from "./skillManager.js";
20
27
 
21
28
  export interface ToolManagerOptions {
22
29
  mcpManager: McpManager;
30
+ lspManager?: ILspManager;
23
31
  logger?: Logger;
32
+ /** Optional permission manager for handling tool permission checks */
33
+ permissionManager?: PermissionManager;
34
+ /** Permission mode for tool execution (defaults to "default") */
35
+ permissionMode?: PermissionMode;
36
+ /** Custom permission callback for tool usage */
37
+ canUseToolCallback?: PermissionCallback;
24
38
  }
25
39
 
26
40
  /**
27
41
  * Tool Manager
42
+ *
43
+ * Manages tool registration and execution with optional permission system integration.
44
+ * Supports both built-in tools and MCP (Model Context Protocol) tools.
28
45
  */
29
46
  class ToolManager {
30
47
  private tools = new Map<string, ToolPlugin>();
31
48
  private mcpManager: McpManager;
49
+ private lspManager?: ILspManager;
32
50
  private logger?: Logger;
51
+ private permissionManager?: PermissionManager;
52
+ private permissionMode?: PermissionMode;
53
+ private canUseToolCallback?: PermissionCallback;
33
54
 
34
55
  constructor(options: ToolManagerOptions) {
35
56
  this.mcpManager = options.mcpManager;
57
+ this.lspManager = options.lspManager;
36
58
  this.logger = options.logger;
59
+ this.permissionManager = options.permissionManager;
60
+ // Store CLI permission mode, let PermissionManager resolve effective mode
61
+ this.permissionMode = options.permissionMode;
62
+ this.canUseToolCallback = options.canUseToolCallback;
37
63
  }
38
64
 
39
65
  /**
@@ -83,6 +109,7 @@ class ToolManager {
83
109
  lsTool,
84
110
  readTool,
85
111
  todoWriteTool,
112
+ lspTool,
86
113
  ];
87
114
 
88
115
  for (const tool of builtInTools) {
@@ -101,22 +128,66 @@ class ToolManager {
101
128
  }
102
129
  }
103
130
 
131
+ /**
132
+ * Execute a tool by name with the provided arguments and context
133
+ *
134
+ * Enhances the context with permission-related fields before execution:
135
+ * - permissionMode: The current permission mode (default or bypassPermissions)
136
+ * - canUseToolCallback: Custom permission callback if provided
137
+ * - permissionManager: The PermissionManager instance for permission checks
138
+ *
139
+ * @param name - Name of the tool to execute
140
+ * @param args - Arguments to pass to the tool
141
+ * @param context - Execution context for the tool
142
+ * @returns Promise resolving to the tool execution result
143
+ */
104
144
  async execute(
105
145
  name: string,
106
146
  args: Record<string, unknown>,
107
147
  context: ToolContext,
108
148
  ): Promise<ToolResult> {
149
+ // Resolve effective permission mode (CLI override > configuration > default)
150
+ const effectivePermissionMode = this.permissionManager
151
+ ? this.permissionManager.getCurrentEffectiveMode(this.permissionMode)
152
+ : this.permissionMode || "default";
153
+
154
+ // Enhance context with permission-related fields
155
+ const enhancedContext: ToolContext = {
156
+ ...context,
157
+ permissionMode: effectivePermissionMode,
158
+ canUseToolCallback: this.canUseToolCallback,
159
+ permissionManager: this.permissionManager,
160
+ mcpManager: this.mcpManager,
161
+ lspManager: this.lspManager,
162
+ };
163
+
164
+ this.logger?.debug("Executing tool with enhanced context", {
165
+ toolName: name,
166
+ cliPermissionMode: this.permissionMode,
167
+ effectivePermissionMode,
168
+ hasPermissionManager: !!this.permissionManager,
169
+ hasPermissionCallback: !!this.canUseToolCallback,
170
+ });
171
+
109
172
  // Check if it's an MCP tool first
110
173
  if (this.mcpManager.isMcpTool(name)) {
111
- return this.mcpManager.executeMcpToolByRegistry(name, args, context);
174
+ return this.mcpManager.executeMcpToolByRegistry(
175
+ name,
176
+ args,
177
+ enhancedContext,
178
+ );
112
179
  }
113
180
 
114
181
  // Check built-in tools
115
182
  const plugin = this.tools.get(name);
116
183
  if (plugin) {
117
184
  try {
118
- return await plugin.execute(args, context);
185
+ return await plugin.execute(args, enhancedContext);
119
186
  } catch (error) {
187
+ this.logger?.error("Tool execution failed", {
188
+ toolName: name,
189
+ error: error instanceof Error ? error.message : String(error),
190
+ });
120
191
  return {
121
192
  success: false,
122
193
  content: "",
@@ -125,6 +196,7 @@ class ToolManager {
125
196
  }
126
197
  }
127
198
 
199
+ this.logger?.warn("Tool not found", { toolName: name });
128
200
  return {
129
201
  success: false,
130
202
  content: "",
@@ -145,6 +217,26 @@ class ToolManager {
145
217
  const mcpToolsConfig = this.mcpManager.getMcpToolsConfig();
146
218
  return [...builtInToolsConfig, ...mcpToolsConfig];
147
219
  }
220
+
221
+ /**
222
+ * Get the current permission mode
223
+ */
224
+ public getPermissionMode(): PermissionMode {
225
+ if (this.permissionManager) {
226
+ return this.permissionManager.getCurrentEffectiveMode(
227
+ this.permissionMode,
228
+ );
229
+ }
230
+ return this.permissionMode || "default";
231
+ }
232
+
233
+ /**
234
+ * Set the permission mode
235
+ * @param mode - The new permission mode
236
+ */
237
+ public setPermissionMode(mode: PermissionMode): void {
238
+ this.permissionMode = mode;
239
+ }
148
240
  }
149
241
 
150
242
  // Export tool registry class and types