wave-agent-sdk 0.4.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/dist/agent.d.ts +42 -11
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +114 -115
  4. package/dist/constants/prompts.d.ts +18 -14
  5. package/dist/constants/prompts.d.ts.map +1 -1
  6. package/dist/constants/prompts.js +130 -54
  7. package/dist/constants/tools.d.ts +6 -3
  8. package/dist/constants/tools.d.ts.map +1 -1
  9. package/dist/constants/tools.js +6 -3
  10. package/dist/index.d.ts +1 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +1 -0
  13. package/dist/managers/MemoryRuleManager.js +1 -1
  14. package/dist/managers/aiManager.d.ts +5 -3
  15. package/dist/managers/aiManager.d.ts.map +1 -1
  16. package/dist/managers/aiManager.js +57 -20
  17. package/dist/managers/backgroundBashManager.d.ts.map +1 -1
  18. package/dist/managers/backgroundBashManager.js +1 -0
  19. package/dist/managers/backgroundTaskManager.d.ts +35 -0
  20. package/dist/managers/backgroundTaskManager.d.ts.map +1 -0
  21. package/dist/managers/backgroundTaskManager.js +255 -0
  22. package/dist/managers/foregroundTaskManager.d.ts +9 -0
  23. package/dist/managers/foregroundTaskManager.d.ts.map +1 -0
  24. package/dist/managers/foregroundTaskManager.js +21 -0
  25. package/dist/managers/liveConfigManager.d.ts +1 -1
  26. package/dist/managers/lspManager.d.ts.map +1 -1
  27. package/dist/managers/lspManager.js +3 -1
  28. package/dist/managers/mcpManager.d.ts.map +1 -1
  29. package/dist/managers/messageManager.d.ts +26 -12
  30. package/dist/managers/messageManager.d.ts.map +1 -1
  31. package/dist/managers/messageManager.js +138 -64
  32. package/dist/managers/permissionManager.d.ts.map +1 -1
  33. package/dist/managers/permissionManager.js +26 -22
  34. package/dist/managers/planManager.d.ts +1 -1
  35. package/dist/managers/planManager.d.ts.map +1 -1
  36. package/dist/managers/planManager.js +2 -2
  37. package/dist/managers/pluginManager.d.ts.map +1 -1
  38. package/dist/managers/pluginManager.js +3 -2
  39. package/dist/managers/slashCommandManager.d.ts +6 -0
  40. package/dist/managers/slashCommandManager.d.ts.map +1 -1
  41. package/dist/managers/slashCommandManager.js +8 -2
  42. package/dist/managers/subagentManager.d.ts +15 -2
  43. package/dist/managers/subagentManager.d.ts.map +1 -1
  44. package/dist/managers/subagentManager.js +153 -39
  45. package/dist/managers/toolManager.d.ts +18 -1
  46. package/dist/managers/toolManager.d.ts.map +1 -1
  47. package/dist/managers/toolManager.js +29 -5
  48. package/dist/services/GitService.d.ts.map +1 -1
  49. package/dist/services/GitService.js +6 -2
  50. package/dist/services/MarketplaceService.d.ts +2 -2
  51. package/dist/services/MarketplaceService.d.ts.map +1 -1
  52. package/dist/services/MarketplaceService.js +18 -11
  53. package/dist/services/MemoryRuleService.d.ts +1 -1
  54. package/dist/services/MemoryRuleService.d.ts.map +1 -1
  55. package/dist/services/MemoryRuleService.js +13 -2
  56. package/dist/services/aiService.d.ts +0 -1
  57. package/dist/services/aiService.d.ts.map +1 -1
  58. package/dist/services/aiService.js +4 -140
  59. package/dist/services/memory.d.ts +0 -3
  60. package/dist/services/memory.d.ts.map +1 -1
  61. package/dist/services/memory.js +1 -60
  62. package/dist/services/session.d.ts +15 -1
  63. package/dist/services/session.d.ts.map +1 -1
  64. package/dist/services/session.js +57 -1
  65. package/dist/services/taskManager.d.ts +21 -0
  66. package/dist/services/taskManager.d.ts.map +1 -0
  67. package/dist/services/taskManager.js +158 -0
  68. package/dist/tools/askUserQuestion.d.ts.map +1 -1
  69. package/dist/tools/askUserQuestion.js +39 -25
  70. package/dist/tools/bashTool.d.ts +0 -8
  71. package/dist/tools/bashTool.d.ts.map +1 -1
  72. package/dist/tools/bashTool.js +48 -172
  73. package/dist/tools/editTool.d.ts.map +1 -1
  74. package/dist/tools/editTool.js +8 -6
  75. package/dist/tools/exitPlanMode.d.ts.map +1 -1
  76. package/dist/tools/exitPlanMode.js +25 -1
  77. package/dist/tools/globTool.d.ts.map +1 -1
  78. package/dist/tools/globTool.js +8 -2
  79. package/dist/tools/grepTool.d.ts.map +1 -1
  80. package/dist/tools/grepTool.js +17 -6
  81. package/dist/tools/lsTool.d.ts.map +1 -1
  82. package/dist/tools/lsTool.js +3 -1
  83. package/dist/tools/multiEditTool.d.ts.map +1 -1
  84. package/dist/tools/multiEditTool.js +7 -6
  85. package/dist/tools/readTool.d.ts.map +1 -1
  86. package/dist/tools/readTool.js +16 -1
  87. package/dist/tools/taskManagementTools.d.ts +6 -0
  88. package/dist/tools/taskManagementTools.d.ts.map +1 -0
  89. package/dist/tools/taskManagementTools.js +453 -0
  90. package/dist/tools/taskOutputTool.d.ts +3 -0
  91. package/dist/tools/taskOutputTool.d.ts.map +1 -0
  92. package/dist/tools/taskOutputTool.js +173 -0
  93. package/dist/tools/taskStopTool.d.ts +3 -0
  94. package/dist/tools/taskStopTool.d.ts.map +1 -0
  95. package/dist/tools/taskStopTool.js +71 -0
  96. package/dist/tools/taskTool.d.ts.map +1 -1
  97. package/dist/tools/taskTool.js +110 -63
  98. package/dist/tools/types.d.ts +12 -0
  99. package/dist/tools/types.d.ts.map +1 -1
  100. package/dist/tools/writeTool.d.ts.map +1 -1
  101. package/dist/tools/writeTool.js +9 -1
  102. package/dist/types/index.d.ts +1 -0
  103. package/dist/types/index.d.ts.map +1 -1
  104. package/dist/types/index.js +1 -0
  105. package/dist/types/marketplace.d.ts +1 -0
  106. package/dist/types/marketplace.d.ts.map +1 -1
  107. package/dist/types/messaging.d.ts +3 -8
  108. package/dist/types/messaging.d.ts.map +1 -1
  109. package/dist/types/processes.d.ts +29 -4
  110. package/dist/types/processes.d.ts.map +1 -1
  111. package/dist/types/tasks.d.ts +13 -0
  112. package/dist/types/tasks.d.ts.map +1 -0
  113. package/dist/types/tasks.js +1 -0
  114. package/dist/types/tools.d.ts +4 -1
  115. package/dist/types/tools.d.ts.map +1 -1
  116. package/dist/utils/builtinSubagents.d.ts.map +1 -1
  117. package/dist/utils/builtinSubagents.js +38 -1
  118. package/dist/utils/cacheControlUtils.d.ts.map +1 -1
  119. package/dist/utils/cacheControlUtils.js +18 -12
  120. package/dist/utils/constants.d.ts +0 -4
  121. package/dist/utils/constants.d.ts.map +1 -1
  122. package/dist/utils/constants.js +0 -4
  123. package/dist/utils/convertMessagesForAPI.js +2 -2
  124. package/dist/utils/editUtils.d.ts +2 -11
  125. package/dist/utils/editUtils.d.ts.map +1 -1
  126. package/dist/utils/editUtils.js +52 -79
  127. package/dist/utils/messageOperations.d.ts +5 -36
  128. package/dist/utils/messageOperations.d.ts.map +1 -1
  129. package/dist/utils/messageOperations.js +9 -98
  130. package/dist/utils/nameGenerator.d.ts +1 -1
  131. package/dist/utils/nameGenerator.d.ts.map +1 -1
  132. package/dist/utils/nameGenerator.js +19 -3
  133. package/package.json +5 -5
  134. package/src/agent.ts +157 -134
  135. package/src/constants/prompts.ts +156 -65
  136. package/src/constants/tools.ts +6 -3
  137. package/src/index.ts +1 -0
  138. package/src/managers/MemoryRuleManager.ts +1 -1
  139. package/src/managers/aiManager.ts +77 -35
  140. package/src/managers/backgroundBashManager.ts +1 -0
  141. package/src/managers/backgroundTaskManager.ts +305 -0
  142. package/src/managers/foregroundTaskManager.ts +27 -0
  143. package/src/managers/lspManager.ts +3 -1
  144. package/src/managers/mcpManager.ts +6 -3
  145. package/src/managers/messageManager.ts +185 -75
  146. package/src/managers/permissionManager.ts +33 -28
  147. package/src/managers/planManager.ts +2 -2
  148. package/src/managers/pluginManager.ts +4 -3
  149. package/src/managers/slashCommandManager.ts +15 -2
  150. package/src/managers/subagentManager.ts +194 -35
  151. package/src/managers/toolManager.ts +48 -6
  152. package/src/services/GitService.ts +6 -2
  153. package/src/services/MarketplaceService.ts +30 -12
  154. package/src/services/MemoryRuleService.ts +18 -6
  155. package/src/services/aiService.ts +3 -145
  156. package/src/services/memory.ts +1 -73
  157. package/src/services/session.ts +73 -0
  158. package/src/services/taskManager.ts +188 -0
  159. package/src/tools/askUserQuestion.ts +51 -29
  160. package/src/tools/bashTool.ts +63 -196
  161. package/src/tools/editTool.ts +9 -18
  162. package/src/tools/exitPlanMode.ts +26 -2
  163. package/src/tools/globTool.ts +10 -2
  164. package/src/tools/grepTool.ts +17 -6
  165. package/src/tools/lsTool.ts +3 -1
  166. package/src/tools/multiEditTool.ts +7 -18
  167. package/src/tools/readTool.ts +17 -1
  168. package/src/tools/taskManagementTools.ts +498 -0
  169. package/src/tools/taskOutputTool.ts +196 -0
  170. package/src/tools/taskStopTool.ts +78 -0
  171. package/src/tools/taskTool.ts +136 -74
  172. package/src/tools/types.ts +13 -0
  173. package/src/tools/writeTool.ts +9 -2
  174. package/src/types/index.ts +1 -0
  175. package/src/types/marketplace.ts +1 -0
  176. package/src/types/messaging.ts +2 -9
  177. package/src/types/processes.ts +39 -4
  178. package/src/types/tasks.ts +13 -0
  179. package/src/types/tools.ts +4 -1
  180. package/src/utils/builtinSubagents.ts +47 -1
  181. package/src/utils/cacheControlUtils.ts +26 -18
  182. package/src/utils/constants.ts +0 -5
  183. package/src/utils/convertMessagesForAPI.ts +2 -2
  184. package/src/utils/editUtils.ts +65 -103
  185. package/src/utils/messageOperations.ts +12 -136
  186. package/src/utils/nameGenerator.ts +20 -3
  187. package/dist/tools/todoWriteTool.d.ts +0 -6
  188. package/dist/tools/todoWriteTool.d.ts.map +0 -1
  189. package/dist/tools/todoWriteTool.js +0 -220
  190. package/src/tools/todoWriteTool.ts +0 -257
@@ -1,4 +1,5 @@
1
1
  import { randomUUID } from "crypto";
2
+ import type { MemoryRuleManager } from "./MemoryRuleManager.js";
2
3
  import type { SubagentConfiguration } from "../utils/subagentParser.js";
3
4
  import type {
4
5
  Message,
@@ -20,6 +21,7 @@ import {
20
21
  addConsolidatedAbortListener,
21
22
  createAbortPromise,
22
23
  } from "../utils/abortUtils.js";
24
+ import { BackgroundTaskManager } from "./backgroundTaskManager.js";
23
25
 
24
26
  export interface SubagentManagerCallbacks {
25
27
  // Granular subagent message callbacks (015-subagent-message-callbacks)
@@ -49,6 +51,11 @@ export interface SubagentManagerCallbacks {
49
51
  ) => void;
50
52
  /** Triggered when subagent messages change */
51
53
  onSubagentMessagesChange?: (subagentId: string, messages: Message[]) => void;
54
+ /** Triggered when subagent latest total tokens change */
55
+ onSubagentLatestTotalTokensChange?: (
56
+ subagentId: string,
57
+ tokens: number,
58
+ ) => void;
52
59
  }
53
60
 
54
61
  export interface SubagentInstance {
@@ -60,12 +67,14 @@ export interface SubagentInstance {
60
67
  status: "initializing" | "active" | "completed" | "error" | "aborted";
61
68
  messages: Message[];
62
69
  subagentType: string; // Store the subagent type for hook context
70
+ backgroundTaskId?: string; // ID of the background task if transitioned
63
71
  }
64
72
 
65
73
  export interface SubagentManagerOptions {
66
74
  workdir: string;
67
75
  parentToolManager: ToolManager;
68
76
  parentMessageManager: MessageManager;
77
+ taskManager: import("../services/taskManager.js").TaskManager;
69
78
  callbacks?: SubagentManagerCallbacks; // Use SubagentManagerCallbacks instead of parentCallbacks
70
79
  logger?: Logger;
71
80
  getGatewayConfig: () => GatewayConfig;
@@ -74,6 +83,8 @@ export interface SubagentManagerOptions {
74
83
  getLanguage: () => string | undefined;
75
84
  hookManager?: HookManager;
76
85
  onUsageAdded?: (usage: Usage) => void;
86
+ backgroundTaskManager?: BackgroundTaskManager;
87
+ memoryRuleManager?: MemoryRuleManager;
77
88
  }
78
89
 
79
90
  export class SubagentManager {
@@ -83,6 +94,7 @@ export class SubagentManager {
83
94
  private workdir: string;
84
95
  private parentToolManager: ToolManager;
85
96
  private parentMessageManager: MessageManager;
97
+ private taskManager: import("../services/taskManager.js").TaskManager;
86
98
  private callbacks?: SubagentManagerCallbacks; // Use SubagentManagerCallbacks instead of parentCallbacks
87
99
  private logger?: Logger;
88
100
  private getGatewayConfig: () => GatewayConfig;
@@ -91,11 +103,14 @@ export class SubagentManager {
91
103
  private getLanguage: () => string | undefined;
92
104
  private hookManager?: HookManager;
93
105
  private onUsageAdded?: (usage: Usage) => void;
106
+ private backgroundTaskManager?: BackgroundTaskManager;
107
+ private memoryRuleManager?: MemoryRuleManager;
94
108
 
95
109
  constructor(options: SubagentManagerOptions) {
96
110
  this.workdir = options.workdir;
97
111
  this.parentToolManager = options.parentToolManager;
98
112
  this.parentMessageManager = options.parentMessageManager;
113
+ this.taskManager = options.taskManager;
99
114
  this.callbacks = options.callbacks; // Store SubagentManagerCallbacks
100
115
  this.logger = options.logger;
101
116
  this.getGatewayConfig = options.getGatewayConfig;
@@ -104,6 +119,8 @@ export class SubagentManager {
104
119
  this.getLanguage = options.getLanguage;
105
120
  this.hookManager = options.hookManager;
106
121
  this.onUsageAdded = options.onUsageAdded;
122
+ this.backgroundTaskManager = options.backgroundTaskManager;
123
+ this.memoryRuleManager = options.memoryRuleManager;
107
124
  }
108
125
 
109
126
  /**
@@ -158,6 +175,7 @@ export class SubagentManager {
158
175
  prompt: string;
159
176
  subagent_type: string;
160
177
  },
178
+ runInBackground?: boolean,
161
179
  ): Promise<SubagentInstance> {
162
180
  if (!this.parentToolManager) {
163
181
  throw new Error(
@@ -176,6 +194,7 @@ export class SubagentManager {
176
194
  logger: this.logger,
177
195
  sessionType: "subagent",
178
196
  subagentType: parameters.subagent_type,
197
+ memoryRuleManager: this.memoryRuleManager,
179
198
  });
180
199
 
181
200
  // Use the parent tool manager directly - tool restrictions will be handled by allowedTools parameter
@@ -185,6 +204,7 @@ export class SubagentManager {
185
204
  const aiManager = new AIManager({
186
205
  messageManager,
187
206
  toolManager,
207
+ taskManager: this.taskManager,
188
208
  logger: this.logger,
189
209
  workdir: this.workdir,
190
210
  systemPrompt: configuration.systemPrompt,
@@ -241,6 +261,7 @@ export class SubagentManager {
241
261
  configuration,
242
262
  "active",
243
263
  parameters,
264
+ runInBackground,
244
265
  );
245
266
 
246
267
  return instance;
@@ -256,6 +277,7 @@ export class SubagentManager {
256
277
  instance: SubagentInstance,
257
278
  prompt: string,
258
279
  abortSignal?: AbortSignal,
280
+ runInBackground?: boolean,
259
281
  ): Promise<string> {
260
282
  try {
261
283
  // Check if already aborted before starting
@@ -269,24 +291,122 @@ export class SubagentManager {
269
291
  status: "active",
270
292
  });
271
293
 
272
- // Set up consolidated abort handler to prevent listener accumulation
273
- let abortCleanup: (() => void) | undefined;
274
- if (abortSignal) {
275
- abortCleanup = addConsolidatedAbortListener(abortSignal, [
276
- () => {
277
- // Update status to aborted
278
- this.updateInstanceStatus(instance.subagentId, "aborted");
279
- this.parentMessageManager.updateSubagentBlock(instance.subagentId, {
280
- status: "aborted",
281
- });
282
- },
283
- () => {
284
- // Abort the AI execution
285
- instance.aiManager.abortAIMessage();
286
- },
287
- ]);
294
+ if (runInBackground && this.backgroundTaskManager) {
295
+ const taskId = this.backgroundTaskManager.generateId();
296
+ const startTime = Date.now();
297
+
298
+ this.backgroundTaskManager.addTask({
299
+ id: taskId,
300
+ type: "subagent",
301
+ status: "running",
302
+ startTime,
303
+ description: instance.configuration.description,
304
+ stdout: "",
305
+ stderr: "",
306
+ onStop: () => instance.aiManager.abortAIMessage(),
307
+ });
308
+
309
+ instance.backgroundTaskId = taskId;
310
+
311
+ // Execute in background
312
+ (async () => {
313
+ try {
314
+ const result = await this.internalExecute(
315
+ instance,
316
+ prompt,
317
+ abortSignal,
318
+ );
319
+ const task = this.backgroundTaskManager?.getTask(taskId);
320
+ if (task) {
321
+ task.status = "completed";
322
+ task.stdout = result;
323
+ task.endTime = Date.now();
324
+ task.runtime = task.endTime - startTime;
325
+ }
326
+ } catch (error) {
327
+ const task = this.backgroundTaskManager?.getTask(taskId);
328
+ if (task) {
329
+ task.status = "failed";
330
+ task.stderr =
331
+ error instanceof Error ? error.message : String(error);
332
+ task.endTime = Date.now();
333
+ task.runtime = task.endTime - startTime;
334
+ }
335
+ }
336
+ })();
337
+
338
+ return taskId;
288
339
  }
289
340
 
341
+ return await this.internalExecute(instance, prompt, abortSignal);
342
+ } catch (error) {
343
+ this.updateInstanceStatus(instance.subagentId, "error");
344
+ this.parentMessageManager.updateSubagentBlock(instance.subagentId, {
345
+ status: "error",
346
+ });
347
+ throw error;
348
+ }
349
+ }
350
+
351
+ async backgroundInstance(subagentId: string): Promise<string> {
352
+ const instance = this.instances.get(subagentId);
353
+ if (!instance) {
354
+ throw new Error(`Subagent instance ${subagentId} not found`);
355
+ }
356
+
357
+ if (!this.backgroundTaskManager) {
358
+ throw new Error("BackgroundTaskManager not available");
359
+ }
360
+
361
+ const taskId = this.backgroundTaskManager.generateId();
362
+ const startTime = Date.now();
363
+
364
+ this.backgroundTaskManager.addTask({
365
+ id: taskId,
366
+ type: "subagent",
367
+ status: "running",
368
+ startTime,
369
+ description: instance.configuration.description,
370
+ stdout: "",
371
+ stderr: "",
372
+ onStop: () => instance.aiManager.abortAIMessage(),
373
+ });
374
+
375
+ instance.backgroundTaskId = taskId;
376
+
377
+ // Update parent message manager to reflect background status
378
+ this.parentMessageManager.updateSubagentBlock(subagentId, {
379
+ runInBackground: true,
380
+ });
381
+
382
+ return taskId;
383
+ }
384
+
385
+ private async internalExecute(
386
+ instance: SubagentInstance,
387
+ prompt: string,
388
+ abortSignal?: AbortSignal,
389
+ ): Promise<string> {
390
+ // Set up consolidated abort handler to prevent listener accumulation
391
+ let abortCleanup: (() => void) | undefined;
392
+ // Only link to parent abort signal if NOT running in background
393
+ if (abortSignal && !instance.backgroundTaskId) {
394
+ abortCleanup = addConsolidatedAbortListener(abortSignal, [
395
+ () => {
396
+ // Update status to aborted
397
+ this.updateInstanceStatus(instance.subagentId, "aborted");
398
+ this.parentMessageManager.updateSubagentBlock(instance.subagentId, {
399
+ status: "aborted",
400
+ });
401
+ },
402
+ () => {
403
+ // Abort the AI execution
404
+ instance.aiManager.abortAIMessage();
405
+ },
406
+ ]);
407
+ }
408
+
409
+ try {
290
410
  // Add the user's prompt as a message
291
411
  instance.messageManager.addUserMessage({ content: prompt });
292
412
 
@@ -326,21 +446,14 @@ export class SubagentManager {
326
446
  model: resolvedModel,
327
447
  });
328
448
 
329
- try {
330
- // If we have an abort signal, race against it using utilities to prevent listener accumulation
331
- if (abortSignal) {
332
- await Promise.race([
333
- executeAI,
334
- createAbortPromise(abortSignal, "Task was aborted"),
335
- ]);
336
- } else {
337
- await executeAI;
338
- }
339
- } finally {
340
- // Clean up abort listeners to prevent memory leaks
341
- if (abortCleanup) {
342
- abortCleanup();
343
- }
449
+ // If we have an abort signal, race against it using utilities to prevent listener accumulation
450
+ if (abortSignal && !instance.backgroundTaskId) {
451
+ await Promise.race([
452
+ executeAI,
453
+ createAbortPromise(abortSignal, "Task was aborted"),
454
+ ]);
455
+ } else {
456
+ await executeAI;
344
457
  }
345
458
 
346
459
  // Get the latest messages to extract the response
@@ -365,13 +478,43 @@ export class SubagentManager {
365
478
  status: "completed",
366
479
  });
367
480
 
481
+ // If this was transitioned to background, update the background task
482
+ if (instance.backgroundTaskId && this.backgroundTaskManager) {
483
+ const task = this.backgroundTaskManager.getTask(
484
+ instance.backgroundTaskId,
485
+ );
486
+ if (task) {
487
+ task.status = "completed";
488
+ task.stdout = response || "Task completed with no text response";
489
+ task.endTime = Date.now();
490
+ if (task.startTime) {
491
+ task.runtime = task.endTime - task.startTime;
492
+ }
493
+ }
494
+ }
495
+
368
496
  return response || "Task completed with no text response";
369
497
  } catch (error) {
370
- this.updateInstanceStatus(instance.subagentId, "error");
371
- this.parentMessageManager.updateSubagentBlock(instance.subagentId, {
372
- status: "error",
373
- });
498
+ // If this was transitioned to background, update the background task with error
499
+ if (instance.backgroundTaskId && this.backgroundTaskManager) {
500
+ const task = this.backgroundTaskManager.getTask(
501
+ instance.backgroundTaskId,
502
+ );
503
+ if (task) {
504
+ task.status = "failed";
505
+ task.stderr = error instanceof Error ? error.message : String(error);
506
+ task.endTime = Date.now();
507
+ if (task.startTime) {
508
+ task.runtime = task.endTime - task.startTime;
509
+ }
510
+ }
511
+ }
374
512
  throw error;
513
+ } finally {
514
+ // Clean up abort listeners to prevent memory leaks
515
+ if (abortCleanup) {
516
+ abortCleanup();
517
+ }
375
518
  }
376
519
  }
377
520
 
@@ -462,6 +605,7 @@ export class SubagentManager {
462
605
  logger: this.logger,
463
606
  sessionType: "subagent",
464
607
  subagentType: configuration.name, // Use configuration name for restored sessions
608
+ memoryRuleManager: this.memoryRuleManager,
465
609
  });
466
610
 
467
611
  // Use the parent tool manager
@@ -483,6 +627,7 @@ export class SubagentManager {
483
627
  const aiManager = new AIManager({
484
628
  messageManager,
485
629
  toolManager,
630
+ taskManager: this.taskManager,
486
631
  logger: this.logger,
487
632
  workdir: this.workdir,
488
633
  systemPrompt: configuration.systemPrompt,
@@ -594,6 +739,20 @@ export class SubagentManager {
594
739
  sessionId: newSessionId,
595
740
  });
596
741
  },
742
+
743
+ onLatestTotalTokensChange: (tokens: number) => {
744
+ // Forward latest total tokens to parent via SubagentManager callbacks
745
+ if (this.callbacks?.onSubagentLatestTotalTokensChange) {
746
+ this.callbacks.onSubagentLatestTotalTokensChange(subagentId, tokens);
747
+ }
748
+ },
749
+
750
+ onFileHistoryBlockAdded: (
751
+ snapshots: import("../types/reversion.js").FileSnapshot[],
752
+ ) => {
753
+ // Forward file history blocks to parent MessageManager
754
+ this.parentMessageManager.addFileHistoryBlock(snapshots);
755
+ },
597
756
  };
598
757
  }
599
758
  }
@@ -1,5 +1,7 @@
1
1
  import type { ToolContext, ToolPlugin, ToolResult } from "../tools/types.js";
2
- import { bashTool, bashOutputTool, killBashTool } from "../tools/bashTool.js";
2
+ import { bashTool } from "../tools/bashTool.js";
3
+ import { taskOutputTool } from "../tools/taskOutputTool.js";
4
+ import { taskStopTool } from "../tools/taskStopTool.js";
3
5
  import { deleteFileTool } from "../tools/deleteFileTool.js";
4
6
  import { editTool } from "../tools/editTool.js";
5
7
  import { multiEditTool } from "../tools/multiEditTool.js";
@@ -11,10 +13,15 @@ import { globTool } from "../tools/globTool.js";
11
13
  import { grepTool } from "../tools/grepTool.js";
12
14
  import { lsTool } from "../tools/lsTool.js";
13
15
  import { readTool } from "../tools/readTool.js";
14
- import { todoWriteTool } from "../tools/todoWriteTool.js";
15
16
  import { lspTool } from "../tools/lspTool.js";
16
17
  import { createTaskTool } from "../tools/taskTool.js";
17
18
  import { createSkillTool } from "../tools/skillTool.js";
19
+ import {
20
+ taskCreateTool,
21
+ taskGetTool,
22
+ taskUpdateTool,
23
+ taskListTool,
24
+ } from "../tools/taskManagementTools.js";
18
25
  import { McpManager } from "./mcpManager.js";
19
26
  import { PermissionManager } from "./permissionManager.js";
20
27
  import { ChatCompletionFunctionTool } from "openai/resources.js";
@@ -33,10 +40,16 @@ export interface ToolManagerOptions {
33
40
  mcpManager: McpManager;
34
41
  lspManager?: ILspManager;
35
42
  logger?: Logger;
36
- /** Optional permission manager for handling tool permission checks */
43
+ /** Permission manager for handling tool permission checks */
37
44
  permissionManager?: PermissionManager;
45
+ /** Foreground task manager for backgrounding tasks */
46
+ foregroundTaskManager?: import("../types/processes.js").IForegroundTaskManager;
47
+ /** Task manager for task management */
48
+ taskManager?: import("../services/taskManager.js").TaskManager;
38
49
  /** Reversion manager for file snapshots */
39
50
  reversionManager?: ReversionManager;
51
+ /** Background task manager for background execution */
52
+ backgroundTaskManager?: import("./backgroundTaskManager.js").BackgroundTaskManager;
40
53
  /** Permission mode for tool execution (defaults to "default") */
41
54
  permissionMode?: PermissionMode;
42
55
  /** Custom permission callback for tool usage */
@@ -55,7 +68,10 @@ class ToolManager {
55
68
  private lspManager?: ILspManager;
56
69
  private logger?: Logger;
57
70
  private permissionManager?: PermissionManager;
71
+ private foregroundTaskManager?: import("../types/processes.js").IForegroundTaskManager;
58
72
  private reversionManager?: ReversionManager;
73
+ private taskManager?: import("../services/taskManager.js").TaskManager;
74
+ private backgroundTaskManager?: import("./backgroundTaskManager.js").BackgroundTaskManager;
59
75
  private permissionMode?: PermissionMode;
60
76
  private canUseToolCallback?: PermissionCallback;
61
77
 
@@ -64,7 +80,10 @@ class ToolManager {
64
80
  this.lspManager = options.lspManager;
65
81
  this.logger = options.logger;
66
82
  this.permissionManager = options.permissionManager;
83
+ this.taskManager = options.taskManager;
84
+ this.foregroundTaskManager = options.foregroundTaskManager;
67
85
  this.reversionManager = options.reversionManager;
86
+ this.backgroundTaskManager = options.backgroundTaskManager;
68
87
  // Store CLI permission mode, let PermissionManager resolve effective mode
69
88
  this.permissionMode = options.permissionMode;
70
89
  this.canUseToolCallback = options.canUseToolCallback;
@@ -106,8 +125,8 @@ class ToolManager {
106
125
  }): void {
107
126
  const builtInTools = [
108
127
  bashTool,
109
- bashOutputTool,
110
- killBashTool,
128
+ taskOutputTool,
129
+ taskStopTool,
111
130
  deleteFileTool,
112
131
  editTool,
113
132
  multiEditTool,
@@ -118,8 +137,11 @@ class ToolManager {
118
137
  grepTool,
119
138
  lsTool,
120
139
  readTool,
121
- todoWriteTool,
122
140
  lspTool,
141
+ taskCreateTool,
142
+ taskGetTool,
143
+ taskUpdateTool,
144
+ taskListTool,
123
145
  ];
124
146
 
125
147
  for (const tool of builtInTools) {
@@ -167,9 +189,13 @@ class ToolManager {
167
189
  permissionMode: effectivePermissionMode,
168
190
  canUseToolCallback: this.canUseToolCallback,
169
191
  permissionManager: this.permissionManager,
192
+ taskManager: this.taskManager!,
170
193
  reversionManager: this.reversionManager,
194
+ backgroundTaskManager: this.backgroundTaskManager,
195
+ foregroundTaskManager: this.foregroundTaskManager,
171
196
  mcpManager: this.mcpManager,
172
197
  lspManager: this.lspManager,
198
+ sessionId: context.sessionId,
173
199
  };
174
200
 
175
201
  this.logger?.debug("Executing tool with enhanced context", {
@@ -240,6 +266,13 @@ class ToolManager {
240
266
  return [...builtInToolsConfig, ...mcpToolsConfig];
241
267
  }
242
268
 
269
+ /**
270
+ * Get the list of registered tool plugins
271
+ */
272
+ public getTools(): ToolPlugin[] {
273
+ return Array.from(this.tools.values());
274
+ }
275
+
243
276
  /**
244
277
  * Get the current permission mode
245
278
  */
@@ -266,6 +299,15 @@ class ToolManager {
266
299
  public getPermissionManager(): PermissionManager | undefined {
267
300
  return this.permissionManager;
268
301
  }
302
+
303
+ /**
304
+ * Get the task manager
305
+ */
306
+ public getTaskManager():
307
+ | import("../services/taskManager.js").TaskManager
308
+ | undefined {
309
+ return this.taskManager;
310
+ }
269
311
  }
270
312
 
271
313
  // Export tool registry class and types
@@ -43,7 +43,9 @@ export class GitService {
43
43
 
44
44
  try {
45
45
  const refArgs = ref ? `-b "${ref}"` : "--depth 1";
46
- await execAsync(`LC_ALL=C git clone ${refArgs} "${url}" "${targetPath}"`);
46
+ await execAsync(`git clone ${refArgs} "${url}" "${targetPath}"`, {
47
+ env: { ...process.env, LC_ALL: "C" },
48
+ });
47
49
  } catch (error) {
48
50
  throw this.handleGitError(urlOrRepo, error);
49
51
  }
@@ -59,7 +61,9 @@ export class GitService {
59
61
  );
60
62
  }
61
63
  try {
62
- await execAsync(`LC_ALL=C git -C "${targetPath}" pull`);
64
+ await execAsync(`git -C "${targetPath}" pull`, {
65
+ env: { ...process.env, LC_ALL: "C" },
66
+ });
63
67
  } catch (error) {
64
68
  throw this.handleGitError(targetPath, error);
65
69
  }
@@ -346,7 +346,10 @@ export class MarketplaceService {
346
346
  /**
347
347
  * Installs a plugin from a marketplace
348
348
  */
349
- async installPlugin(pluginAtMarketplace: string): Promise<InstalledPlugin> {
349
+ async installPlugin(
350
+ pluginAtMarketplace: string,
351
+ projectPath?: string,
352
+ ): Promise<InstalledPlugin> {
350
353
  const [pluginName, marketplaceName] = pluginAtMarketplace.split("@");
351
354
  if (!pluginName || !marketplaceName) {
352
355
  throw new Error("Invalid plugin format. Use name@marketplace");
@@ -432,7 +435,10 @@ export class MarketplaceService {
432
435
 
433
436
  const installedRegistry = await this.getInstalledPlugins();
434
437
  const existingIndex = installedRegistry.plugins.findIndex(
435
- (p) => p.name === pluginName && p.marketplace === marketplaceName,
438
+ (p) =>
439
+ p.name === pluginName &&
440
+ p.marketplace === marketplaceName &&
441
+ p.projectPath === projectPath,
436
442
  );
437
443
 
438
444
  const installedPlugin: InstalledPlugin = {
@@ -440,6 +446,7 @@ export class MarketplaceService {
440
446
  marketplace: marketplaceName,
441
447
  version,
442
448
  cachePath,
449
+ projectPath,
443
450
  };
444
451
 
445
452
  if (existingIndex >= 0) {
@@ -467,7 +474,10 @@ export class MarketplaceService {
467
474
  /**
468
475
  * Uninstalls a plugin
469
476
  */
470
- async uninstallPlugin(pluginAtMarketplace: string): Promise<void> {
477
+ async uninstallPlugin(
478
+ pluginAtMarketplace: string,
479
+ projectPath?: string,
480
+ ): Promise<void> {
471
481
  const [pluginName, marketplaceName] = pluginAtMarketplace.split("@");
472
482
  if (!pluginName || !marketplaceName) {
473
483
  throw new Error("Invalid plugin format. Use name@marketplace");
@@ -475,25 +485,33 @@ export class MarketplaceService {
475
485
 
476
486
  const installedRegistry = await this.getInstalledPlugins();
477
487
  const pluginIndex = installedRegistry.plugins.findIndex(
478
- (p) => p.name === pluginName && p.marketplace === marketplaceName,
488
+ (p) =>
489
+ p.name === pluginName &&
490
+ p.marketplace === marketplaceName &&
491
+ p.projectPath === projectPath,
479
492
  );
480
493
 
481
494
  if (pluginIndex === -1) {
482
495
  throw new Error(
483
- `Plugin ${pluginName}@${marketplaceName} is not installed`,
496
+ `Plugin ${pluginName}@${marketplaceName} is not installed${projectPath ? ` for project ${projectPath}` : ""}`,
484
497
  );
485
498
  }
486
499
 
487
- const plugin = installedRegistry.plugins[pluginIndex];
488
-
489
- // Remove cached files
490
- if (existsSync(plugin.cachePath)) {
491
- await fs.rm(plugin.cachePath, { recursive: true, force: true });
492
- }
500
+ const pluginToRemove = installedRegistry.plugins[pluginIndex];
493
501
 
494
- // Remove from registry
502
+ // Remove from registry first
495
503
  installedRegistry.plugins.splice(pluginIndex, 1);
496
504
  await this.saveInstalledPlugins(installedRegistry);
505
+
506
+ // Check if any other project is still using this same cache path
507
+ const isStillReferenced = installedRegistry.plugins.some(
508
+ (p) => p.cachePath === pluginToRemove.cachePath,
509
+ );
510
+
511
+ // Only remove cached files if no other references exist
512
+ if (!isStillReferenced && existsSync(pluginToRemove.cachePath)) {
513
+ await fs.rm(pluginToRemove.cachePath, { recursive: true, force: true });
514
+ }
497
515
  }
498
516
 
499
517
  /**
@@ -1,4 +1,5 @@
1
1
  import { minimatch } from "minimatch";
2
+ import * as path from "node:path";
2
3
  import { parseFrontmatter } from "../utils/markdownParser.js";
3
4
  import type { MemoryRule, MemoryRuleMetadata } from "../types/memoryRule.js";
4
5
 
@@ -45,15 +46,26 @@ export class MemoryRuleService {
45
46
  /**
46
47
  * Determines if a rule matches any of the given file paths using minimatch.
47
48
  */
48
- isRuleActive(rule: MemoryRule, filesInContext: string[]): boolean {
49
+ isRuleActive(
50
+ rule: MemoryRule,
51
+ filesInContext: string[],
52
+ workdir?: string,
53
+ ): boolean {
49
54
  if (!rule.metadata.paths || rule.metadata.paths.length === 0) {
50
55
  return true;
51
56
  }
52
57
 
53
- return filesInContext.some((filePath) =>
54
- rule.metadata.paths!.some((pattern) =>
55
- minimatch(filePath, pattern, { dot: true }),
56
- ),
57
- );
58
+ return filesInContext.some((filePath) => {
59
+ // Normalize path relative to workdir if it's an absolute path
60
+ let normalizedPath = filePath;
61
+ if (workdir && path.isAbsolute(filePath)) {
62
+ normalizedPath = path.relative(workdir, filePath);
63
+ }
64
+
65
+ return rule.metadata.paths!.some((pattern) => {
66
+ const isMatch = minimatch(normalizedPath, pattern, { dot: true });
67
+ return isMatch;
68
+ });
69
+ });
58
70
  }
59
71
  }