wave-agent-sdk 0.2.1 → 0.5.0

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 (194) hide show
  1. package/dist/agent.d.ts +66 -20
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +156 -83
  4. package/dist/constants/prompts.d.ts +7 -2
  5. package/dist/constants/prompts.d.ts.map +1 -1
  6. package/dist/constants/prompts.js +41 -5
  7. package/dist/constants/tools.d.ts +2 -2
  8. package/dist/constants/tools.js +2 -2
  9. package/dist/index.d.ts +1 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +1 -1
  12. package/dist/managers/MemoryRuleManager.d.ts.map +1 -1
  13. package/dist/managers/MemoryRuleManager.js +16 -2
  14. package/dist/managers/aiManager.d.ts +14 -4
  15. package/dist/managers/aiManager.d.ts.map +1 -1
  16. package/dist/managers/aiManager.js +61 -9
  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 +249 -0
  22. package/dist/managers/bashManager.d.ts.map +1 -1
  23. package/dist/managers/bashManager.js +0 -3
  24. package/dist/managers/foregroundTaskManager.d.ts +9 -0
  25. package/dist/managers/foregroundTaskManager.d.ts.map +1 -0
  26. package/dist/managers/foregroundTaskManager.js +20 -0
  27. package/dist/managers/liveConfigManager.d.ts +1 -1
  28. package/dist/managers/liveConfigManager.d.ts.map +1 -1
  29. package/dist/managers/lspManager.d.ts.map +1 -1
  30. package/dist/managers/lspManager.js +3 -1
  31. package/dist/managers/messageManager.d.ts +34 -4
  32. package/dist/managers/messageManager.d.ts.map +1 -1
  33. package/dist/managers/messageManager.js +104 -13
  34. package/dist/managers/permissionManager.d.ts.map +1 -1
  35. package/dist/managers/permissionManager.js +11 -13
  36. package/dist/managers/pluginManager.d.ts.map +1 -1
  37. package/dist/managers/pluginManager.js +3 -2
  38. package/dist/managers/pluginScopeManager.d.ts +13 -2
  39. package/dist/managers/pluginScopeManager.d.ts.map +1 -1
  40. package/dist/managers/pluginScopeManager.js +38 -0
  41. package/dist/managers/reversionManager.d.ts +39 -0
  42. package/dist/managers/reversionManager.d.ts.map +1 -0
  43. package/dist/managers/reversionManager.js +118 -0
  44. package/dist/managers/slashCommandManager.d.ts +4 -1
  45. package/dist/managers/slashCommandManager.d.ts.map +1 -1
  46. package/dist/managers/slashCommandManager.js +16 -6
  47. package/dist/managers/subagentManager.d.ts +13 -2
  48. package/dist/managers/subagentManager.d.ts.map +1 -1
  49. package/dist/managers/subagentManager.js +144 -35
  50. package/dist/managers/toolManager.d.ts +11 -1
  51. package/dist/managers/toolManager.d.ts.map +1 -1
  52. package/dist/managers/toolManager.js +11 -3
  53. package/dist/services/GitService.d.ts.map +1 -1
  54. package/dist/services/GitService.js +6 -2
  55. package/dist/services/MarketplaceService.d.ts +14 -1
  56. package/dist/services/MarketplaceService.d.ts.map +1 -1
  57. package/dist/services/MarketplaceService.js +72 -4
  58. package/dist/services/MemoryRuleService.d.ts +1 -1
  59. package/dist/services/MemoryRuleService.d.ts.map +1 -1
  60. package/dist/services/MemoryRuleService.js +13 -2
  61. package/dist/services/aiService.js +1 -1
  62. package/dist/services/configurationService.d.ts +18 -2
  63. package/dist/services/configurationService.d.ts.map +1 -1
  64. package/dist/services/configurationService.js +62 -0
  65. package/dist/services/fileWatcher.d.ts +0 -5
  66. package/dist/services/fileWatcher.d.ts.map +1 -1
  67. package/dist/services/fileWatcher.js +0 -11
  68. package/dist/services/memory.js +1 -1
  69. package/dist/services/pluginLoader.d.ts.map +1 -1
  70. package/dist/services/pluginLoader.js +6 -1
  71. package/dist/services/reversionService.d.ts +24 -0
  72. package/dist/services/reversionService.d.ts.map +1 -0
  73. package/dist/services/reversionService.js +76 -0
  74. package/dist/services/session.d.ts +7 -0
  75. package/dist/services/session.d.ts.map +1 -1
  76. package/dist/services/session.js +126 -3
  77. package/dist/tools/bashTool.d.ts +0 -8
  78. package/dist/tools/bashTool.d.ts.map +1 -1
  79. package/dist/tools/bashTool.js +52 -174
  80. package/dist/tools/deleteFileTool.d.ts.map +1 -1
  81. package/dist/tools/deleteFileTool.js +9 -0
  82. package/dist/tools/editTool.d.ts.map +1 -1
  83. package/dist/tools/editTool.js +15 -4
  84. package/dist/tools/multiEditTool.d.ts.map +1 -1
  85. package/dist/tools/multiEditTool.js +16 -5
  86. package/dist/tools/taskOutputTool.d.ts +3 -0
  87. package/dist/tools/taskOutputTool.d.ts.map +1 -0
  88. package/dist/tools/taskOutputTool.js +149 -0
  89. package/dist/tools/taskStopTool.d.ts +3 -0
  90. package/dist/tools/taskStopTool.d.ts.map +1 -0
  91. package/dist/tools/taskStopTool.js +65 -0
  92. package/dist/tools/taskTool.d.ts.map +1 -1
  93. package/dist/tools/taskTool.js +105 -63
  94. package/dist/tools/types.d.ts +7 -0
  95. package/dist/tools/types.d.ts.map +1 -1
  96. package/dist/tools/writeTool.d.ts.map +1 -1
  97. package/dist/tools/writeTool.js +9 -0
  98. package/dist/types/commands.d.ts +1 -0
  99. package/dist/types/commands.d.ts.map +1 -1
  100. package/dist/types/configuration.d.ts +3 -0
  101. package/dist/types/configuration.d.ts.map +1 -1
  102. package/dist/types/environment.d.ts +2 -1
  103. package/dist/types/environment.d.ts.map +1 -1
  104. package/dist/types/environment.js +0 -6
  105. package/dist/types/history.d.ts +5 -0
  106. package/dist/types/history.d.ts.map +1 -0
  107. package/dist/types/history.js +1 -0
  108. package/dist/types/index.d.ts +1 -0
  109. package/dist/types/index.d.ts.map +1 -1
  110. package/dist/types/index.js +1 -0
  111. package/dist/types/marketplace.d.ts +4 -0
  112. package/dist/types/marketplace.d.ts.map +1 -1
  113. package/dist/types/messaging.d.ts +7 -1
  114. package/dist/types/messaging.d.ts.map +1 -1
  115. package/dist/types/processes.d.ts +24 -4
  116. package/dist/types/processes.d.ts.map +1 -1
  117. package/dist/types/reversion.d.ts +29 -0
  118. package/dist/types/reversion.d.ts.map +1 -0
  119. package/dist/types/reversion.js +1 -0
  120. package/dist/utils/builtinSubagents.d.ts.map +1 -1
  121. package/dist/utils/builtinSubagents.js +16 -0
  122. package/dist/utils/constants.d.ts +2 -2
  123. package/dist/utils/constants.d.ts.map +1 -1
  124. package/dist/utils/constants.js +2 -2
  125. package/dist/utils/editUtils.d.ts +4 -9
  126. package/dist/utils/editUtils.d.ts.map +1 -1
  127. package/dist/utils/editUtils.js +54 -55
  128. package/dist/utils/messageOperations.d.ts +3 -1
  129. package/dist/utils/messageOperations.d.ts.map +1 -1
  130. package/dist/utils/messageOperations.js +8 -1
  131. package/dist/utils/openaiClient.d.ts.map +1 -1
  132. package/dist/utils/openaiClient.js +56 -26
  133. package/dist/utils/promptHistory.d.ts +20 -0
  134. package/dist/utils/promptHistory.d.ts.map +1 -0
  135. package/dist/utils/promptHistory.js +117 -0
  136. package/package.json +5 -3
  137. package/src/agent.ts +193 -109
  138. package/src/constants/prompts.ts +45 -5
  139. package/src/constants/tools.ts +2 -2
  140. package/src/index.ts +1 -1
  141. package/src/managers/MemoryRuleManager.ts +18 -2
  142. package/src/managers/aiManager.ts +87 -18
  143. package/src/managers/backgroundBashManager.ts +1 -0
  144. package/src/managers/backgroundTaskManager.ts +306 -0
  145. package/src/managers/bashManager.ts +0 -4
  146. package/src/managers/foregroundTaskManager.ts +26 -0
  147. package/src/managers/liveConfigManager.ts +2 -1
  148. package/src/managers/lspManager.ts +3 -1
  149. package/src/managers/messageManager.ts +136 -18
  150. package/src/managers/permissionManager.ts +11 -13
  151. package/src/managers/pluginManager.ts +4 -3
  152. package/src/managers/pluginScopeManager.ts +57 -8
  153. package/src/managers/reversionManager.ts +152 -0
  154. package/src/managers/slashCommandManager.ts +30 -7
  155. package/src/managers/subagentManager.ts +176 -31
  156. package/src/managers/toolManager.ts +23 -4
  157. package/src/services/GitService.ts +6 -2
  158. package/src/services/MarketplaceService.ts +100 -4
  159. package/src/services/MemoryRuleService.ts +18 -6
  160. package/src/services/aiService.ts +1 -1
  161. package/src/services/configurationService.ts +79 -1
  162. package/src/services/fileWatcher.ts +0 -13
  163. package/src/services/memory.ts +1 -1
  164. package/src/services/pluginLoader.ts +7 -1
  165. package/src/services/reversionService.ts +94 -0
  166. package/src/services/session.ts +161 -3
  167. package/src/tools/bashTool.ts +73 -200
  168. package/src/tools/deleteFileTool.ts +15 -0
  169. package/src/tools/editTool.ts +20 -10
  170. package/src/tools/multiEditTool.ts +21 -11
  171. package/src/tools/taskOutputTool.ts +174 -0
  172. package/src/tools/taskStopTool.ts +72 -0
  173. package/src/tools/taskTool.ts +130 -74
  174. package/src/tools/types.ts +7 -0
  175. package/src/tools/writeTool.ts +14 -0
  176. package/src/types/commands.ts +3 -0
  177. package/src/types/configuration.ts +4 -0
  178. package/src/types/environment.ts +3 -1
  179. package/src/types/history.ts +4 -0
  180. package/src/types/index.ts +1 -0
  181. package/src/types/marketplace.ts +5 -0
  182. package/src/types/messaging.ts +9 -1
  183. package/src/types/processes.ts +33 -4
  184. package/src/types/reversion.ts +29 -0
  185. package/src/utils/builtinSubagents.ts +18 -0
  186. package/src/utils/constants.ts +2 -2
  187. package/src/utils/editUtils.ts +66 -58
  188. package/src/utils/messageOperations.ts +10 -0
  189. package/src/utils/openaiClient.ts +69 -35
  190. package/src/utils/promptHistory.ts +133 -0
  191. package/dist/utils/bashHistory.d.ts +0 -50
  192. package/dist/utils/bashHistory.d.ts.map +0 -1
  193. package/dist/utils/bashHistory.js +0 -256
  194. package/src/utils/bashHistory.ts +0 -320
@@ -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)
@@ -60,6 +62,7 @@ export interface SubagentInstance {
60
62
  status: "initializing" | "active" | "completed" | "error" | "aborted";
61
63
  messages: Message[];
62
64
  subagentType: string; // Store the subagent type for hook context
65
+ backgroundTaskId?: string; // ID of the background task if transitioned
63
66
  }
64
67
 
65
68
  export interface SubagentManagerOptions {
@@ -71,8 +74,11 @@ export interface SubagentManagerOptions {
71
74
  getGatewayConfig: () => GatewayConfig;
72
75
  getModelConfig: () => ModelConfig;
73
76
  getMaxInputTokens: () => number;
77
+ getLanguage: () => string | undefined;
74
78
  hookManager?: HookManager;
75
79
  onUsageAdded?: (usage: Usage) => void;
80
+ backgroundTaskManager?: BackgroundTaskManager;
81
+ memoryRuleManager?: MemoryRuleManager;
76
82
  }
77
83
 
78
84
  export class SubagentManager {
@@ -87,8 +93,11 @@ export class SubagentManager {
87
93
  private getGatewayConfig: () => GatewayConfig;
88
94
  private getModelConfig: () => ModelConfig;
89
95
  private getMaxInputTokens: () => number;
96
+ private getLanguage: () => string | undefined;
90
97
  private hookManager?: HookManager;
91
98
  private onUsageAdded?: (usage: Usage) => void;
99
+ private backgroundTaskManager?: BackgroundTaskManager;
100
+ private memoryRuleManager?: MemoryRuleManager;
92
101
 
93
102
  constructor(options: SubagentManagerOptions) {
94
103
  this.workdir = options.workdir;
@@ -99,8 +108,11 @@ export class SubagentManager {
99
108
  this.getGatewayConfig = options.getGatewayConfig;
100
109
  this.getModelConfig = options.getModelConfig;
101
110
  this.getMaxInputTokens = options.getMaxInputTokens;
111
+ this.getLanguage = options.getLanguage;
102
112
  this.hookManager = options.hookManager;
103
113
  this.onUsageAdded = options.onUsageAdded;
114
+ this.backgroundTaskManager = options.backgroundTaskManager;
115
+ this.memoryRuleManager = options.memoryRuleManager;
104
116
  }
105
117
 
106
118
  /**
@@ -155,6 +167,7 @@ export class SubagentManager {
155
167
  prompt: string;
156
168
  subagent_type: string;
157
169
  },
170
+ runInBackground?: boolean,
158
171
  ): Promise<SubagentInstance> {
159
172
  if (!this.parentToolManager) {
160
173
  throw new Error(
@@ -173,6 +186,7 @@ export class SubagentManager {
173
186
  logger: this.logger,
174
187
  sessionType: "subagent",
175
188
  subagentType: parameters.subagent_type,
189
+ memoryRuleManager: this.memoryRuleManager,
176
190
  });
177
191
 
178
192
  // Use the parent tool manager directly - tool restrictions will be handled by allowedTools parameter
@@ -211,6 +225,7 @@ export class SubagentManager {
211
225
  };
212
226
  },
213
227
  getMaxInputTokens: this.getMaxInputTokens,
228
+ getLanguage: this.getLanguage,
214
229
  callbacks: {
215
230
  onUsageAdded: this.onUsageAdded,
216
231
  },
@@ -237,6 +252,7 @@ export class SubagentManager {
237
252
  configuration,
238
253
  "active",
239
254
  parameters,
255
+ runInBackground,
240
256
  );
241
257
 
242
258
  return instance;
@@ -252,6 +268,7 @@ export class SubagentManager {
252
268
  instance: SubagentInstance,
253
269
  prompt: string,
254
270
  abortSignal?: AbortSignal,
271
+ runInBackground?: boolean,
255
272
  ): Promise<string> {
256
273
  try {
257
274
  // Check if already aborted before starting
@@ -265,24 +282,125 @@ export class SubagentManager {
265
282
  status: "active",
266
283
  });
267
284
 
268
- // Set up consolidated abort handler to prevent listener accumulation
269
- let abortCleanup: (() => void) | undefined;
270
- if (abortSignal) {
271
- abortCleanup = addConsolidatedAbortListener(abortSignal, [
272
- () => {
273
- // Update status to aborted
285
+ if (runInBackground && this.backgroundTaskManager) {
286
+ const taskId = this.backgroundTaskManager.generateId();
287
+ const startTime = Date.now();
288
+
289
+ this.backgroundTaskManager.addTask({
290
+ id: taskId,
291
+ type: "subagent",
292
+ status: "running",
293
+ startTime,
294
+ description: instance.configuration.description,
295
+ stdout: "",
296
+ stderr: "",
297
+ });
298
+
299
+ instance.backgroundTaskId = taskId;
300
+
301
+ // Execute in background
302
+ (async () => {
303
+ try {
304
+ const result = await this.internalExecute(
305
+ instance,
306
+ prompt,
307
+ abortSignal,
308
+ );
309
+ const task = this.backgroundTaskManager?.getTask(taskId);
310
+ if (task) {
311
+ task.status = "completed";
312
+ task.stdout = result;
313
+ task.endTime = Date.now();
314
+ task.runtime = task.endTime - startTime;
315
+ }
316
+ } catch (error) {
317
+ const task = this.backgroundTaskManager?.getTask(taskId);
318
+ if (task) {
319
+ task.status = "failed";
320
+ task.stderr =
321
+ error instanceof Error ? error.message : String(error);
322
+ task.endTime = Date.now();
323
+ task.runtime = task.endTime - startTime;
324
+ }
325
+ }
326
+ })();
327
+
328
+ return taskId;
329
+ }
330
+
331
+ return await this.internalExecute(instance, prompt, abortSignal);
332
+ } catch (error) {
333
+ this.updateInstanceStatus(instance.subagentId, "error");
334
+ this.parentMessageManager.updateSubagentBlock(instance.subagentId, {
335
+ status: "error",
336
+ });
337
+ throw error;
338
+ }
339
+ }
340
+
341
+ async backgroundInstance(subagentId: string): Promise<string> {
342
+ const instance = this.instances.get(subagentId);
343
+ if (!instance) {
344
+ throw new Error(`Subagent instance ${subagentId} not found`);
345
+ }
346
+
347
+ if (!this.backgroundTaskManager) {
348
+ throw new Error("BackgroundTaskManager not available");
349
+ }
350
+
351
+ const taskId = this.backgroundTaskManager.generateId();
352
+ const startTime = Date.now();
353
+
354
+ this.backgroundTaskManager.addTask({
355
+ id: taskId,
356
+ type: "subagent",
357
+ status: "running",
358
+ startTime,
359
+ description: instance.configuration.description,
360
+ stdout: "",
361
+ stderr: "",
362
+ });
363
+
364
+ instance.backgroundTaskId = taskId;
365
+
366
+ // Update parent message manager to reflect background status
367
+ this.parentMessageManager.updateSubagentBlock(subagentId, {
368
+ runInBackground: true,
369
+ });
370
+
371
+ return taskId;
372
+ }
373
+
374
+ private async internalExecute(
375
+ instance: SubagentInstance,
376
+ prompt: string,
377
+ abortSignal?: AbortSignal,
378
+ ): Promise<string> {
379
+ // Set up consolidated abort handler to prevent listener accumulation
380
+ let abortCleanup: (() => void) | undefined;
381
+ if (abortSignal) {
382
+ abortCleanup = addConsolidatedAbortListener(abortSignal, [
383
+ () => {
384
+ // Update status to aborted
385
+ // Only update status if it's NOT a background task
386
+ if (!instance.backgroundTaskId) {
274
387
  this.updateInstanceStatus(instance.subagentId, "aborted");
275
388
  this.parentMessageManager.updateSubagentBlock(instance.subagentId, {
276
389
  status: "aborted",
277
390
  });
278
- },
279
- () => {
280
- // Abort the AI execution
391
+ }
392
+ },
393
+ () => {
394
+ // Abort the AI execution
395
+ // Only abort if it's NOT a background task
396
+ if (!instance.backgroundTaskId) {
281
397
  instance.aiManager.abortAIMessage();
282
- },
283
- ]);
284
- }
398
+ }
399
+ },
400
+ ]);
401
+ }
285
402
 
403
+ try {
286
404
  // Add the user's prompt as a message
287
405
  instance.messageManager.addUserMessage({ content: prompt });
288
406
 
@@ -322,21 +440,16 @@ export class SubagentManager {
322
440
  model: resolvedModel,
323
441
  });
324
442
 
325
- try {
326
- // If we have an abort signal, race against it using utilities to prevent listener accumulation
327
- if (abortSignal) {
328
- await Promise.race([
329
- executeAI,
330
- createAbortPromise(abortSignal, "Task was aborted"),
331
- ]);
332
- } else {
333
- await executeAI;
334
- }
335
- } finally {
336
- // Clean up abort listeners to prevent memory leaks
337
- if (abortCleanup) {
338
- abortCleanup();
339
- }
443
+ // If we have an abort signal, race against it using utilities to prevent listener accumulation
444
+ // BUT: If this is a background task, we DON'T want to race against the abort signal
445
+ // because the abort signal (Esc) should only stop the tool watching the task, not the task itself.
446
+ if (abortSignal && !instance.backgroundTaskId) {
447
+ await Promise.race([
448
+ executeAI,
449
+ createAbortPromise(abortSignal, "Task was aborted"),
450
+ ]);
451
+ } else {
452
+ await executeAI;
340
453
  }
341
454
 
342
455
  // Get the latest messages to extract the response
@@ -361,13 +474,43 @@ export class SubagentManager {
361
474
  status: "completed",
362
475
  });
363
476
 
477
+ // If this was transitioned to background, update the background task
478
+ if (instance.backgroundTaskId && this.backgroundTaskManager) {
479
+ const task = this.backgroundTaskManager.getTask(
480
+ instance.backgroundTaskId,
481
+ );
482
+ if (task) {
483
+ task.status = "completed";
484
+ task.stdout = response || "Task completed with no text response";
485
+ task.endTime = Date.now();
486
+ if (task.startTime) {
487
+ task.runtime = task.endTime - task.startTime;
488
+ }
489
+ }
490
+ }
491
+
364
492
  return response || "Task completed with no text response";
365
493
  } catch (error) {
366
- this.updateInstanceStatus(instance.subagentId, "error");
367
- this.parentMessageManager.updateSubagentBlock(instance.subagentId, {
368
- status: "error",
369
- });
494
+ // If this was transitioned to background, update the background task with error
495
+ if (instance.backgroundTaskId && this.backgroundTaskManager) {
496
+ const task = this.backgroundTaskManager.getTask(
497
+ instance.backgroundTaskId,
498
+ );
499
+ if (task) {
500
+ task.status = "failed";
501
+ task.stderr = error instanceof Error ? error.message : String(error);
502
+ task.endTime = Date.now();
503
+ if (task.startTime) {
504
+ task.runtime = task.endTime - task.startTime;
505
+ }
506
+ }
507
+ }
370
508
  throw error;
509
+ } finally {
510
+ // Clean up abort listeners to prevent memory leaks
511
+ if (abortCleanup) {
512
+ abortCleanup();
513
+ }
371
514
  }
372
515
  }
373
516
 
@@ -458,6 +601,7 @@ export class SubagentManager {
458
601
  logger: this.logger,
459
602
  sessionType: "subagent",
460
603
  subagentType: configuration.name, // Use configuration name for restored sessions
604
+ memoryRuleManager: this.memoryRuleManager,
461
605
  });
462
606
 
463
607
  // Use the parent tool manager
@@ -491,6 +635,7 @@ export class SubagentManager {
491
635
  agentModel: modelToUse,
492
636
  }),
493
637
  getMaxInputTokens: this.getMaxInputTokens,
638
+ getLanguage: this.getLanguage,
494
639
  callbacks: {
495
640
  onUsageAdded: this.onUsageAdded,
496
641
  },
@@ -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";
@@ -27,12 +29,20 @@ import type {
27
29
  import type { SubagentManager } from "./subagentManager.js";
28
30
  import type { SkillManager } from "./skillManager.js";
29
31
 
32
+ import { ReversionManager } from "./reversionManager.js";
33
+
30
34
  export interface ToolManagerOptions {
31
35
  mcpManager: McpManager;
32
36
  lspManager?: ILspManager;
33
37
  logger?: Logger;
34
- /** Optional permission manager for handling tool permission checks */
38
+ /** Permission manager for handling tool permission checks */
35
39
  permissionManager?: PermissionManager;
40
+ /** Foreground task manager for backgrounding tasks */
41
+ foregroundTaskManager?: import("../types/processes.js").IForegroundTaskManager;
42
+ /** Reversion manager for file snapshots */
43
+ reversionManager?: ReversionManager;
44
+ /** Background task manager for background execution */
45
+ backgroundTaskManager?: import("./backgroundTaskManager.js").BackgroundTaskManager;
36
46
  /** Permission mode for tool execution (defaults to "default") */
37
47
  permissionMode?: PermissionMode;
38
48
  /** Custom permission callback for tool usage */
@@ -51,6 +61,9 @@ class ToolManager {
51
61
  private lspManager?: ILspManager;
52
62
  private logger?: Logger;
53
63
  private permissionManager?: PermissionManager;
64
+ private foregroundTaskManager?: import("../types/processes.js").IForegroundTaskManager;
65
+ private reversionManager?: ReversionManager;
66
+ private backgroundTaskManager?: import("./backgroundTaskManager.js").BackgroundTaskManager;
54
67
  private permissionMode?: PermissionMode;
55
68
  private canUseToolCallback?: PermissionCallback;
56
69
 
@@ -59,6 +72,9 @@ class ToolManager {
59
72
  this.lspManager = options.lspManager;
60
73
  this.logger = options.logger;
61
74
  this.permissionManager = options.permissionManager;
75
+ this.foregroundTaskManager = options.foregroundTaskManager;
76
+ this.reversionManager = options.reversionManager;
77
+ this.backgroundTaskManager = options.backgroundTaskManager;
62
78
  // Store CLI permission mode, let PermissionManager resolve effective mode
63
79
  this.permissionMode = options.permissionMode;
64
80
  this.canUseToolCallback = options.canUseToolCallback;
@@ -100,8 +116,8 @@ class ToolManager {
100
116
  }): void {
101
117
  const builtInTools = [
102
118
  bashTool,
103
- bashOutputTool,
104
- killBashTool,
119
+ taskOutputTool,
120
+ taskStopTool,
105
121
  deleteFileTool,
106
122
  editTool,
107
123
  multiEditTool,
@@ -161,6 +177,9 @@ class ToolManager {
161
177
  permissionMode: effectivePermissionMode,
162
178
  canUseToolCallback: this.canUseToolCallback,
163
179
  permissionManager: this.permissionManager,
180
+ reversionManager: this.reversionManager,
181
+ backgroundTaskManager: this.backgroundTaskManager,
182
+ foregroundTaskManager: this.foregroundTaskManager,
164
183
  mcpManager: this.mcpManager,
165
184
  lspManager: this.lspManager,
166
185
  };
@@ -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
  }
@@ -25,6 +25,13 @@ export class MarketplaceService {
25
25
  private cacheDir: string;
26
26
  private marketplacesDir: string;
27
27
  private gitService: GitService;
28
+ private static readonly BUILTIN_MARKETPLACE: KnownMarketplace = {
29
+ name: "wave-plugins-official",
30
+ source: {
31
+ source: "github",
32
+ repo: "netease-lcap/wave-plugins-official",
33
+ },
34
+ };
28
35
 
29
36
  constructor() {
30
37
  this.pluginsDir = getPluginsDir();
@@ -62,7 +69,14 @@ export class MarketplaceService {
62
69
  */
63
70
  async getKnownMarketplaces(): Promise<KnownMarketplacesRegistry> {
64
71
  if (!existsSync(this.knownMarketplacesPath)) {
65
- return { marketplaces: [] };
72
+ return {
73
+ marketplaces: [
74
+ {
75
+ ...MarketplaceService.BUILTIN_MARKETPLACE,
76
+ isBuiltin: true,
77
+ },
78
+ ],
79
+ };
66
80
  }
67
81
  try {
68
82
  const content = await fs.readFile(this.knownMarketplacesPath, "utf-8");
@@ -245,6 +259,10 @@ export class MarketplaceService {
245
259
  registry.marketplaces.push(marketplace);
246
260
  }
247
261
 
262
+ // Ensure builtin is included if we are creating the file for the first time
263
+ // and it hasn't been explicitly removed yet.
264
+ // (getKnownMarketplaces already handles the default injection)
265
+
248
266
  await this.saveKnownMarketplaces(registry);
249
267
  return marketplace;
250
268
  }
@@ -254,7 +272,27 @@ export class MarketplaceService {
254
272
  */
255
273
  async listMarketplaces(): Promise<KnownMarketplace[]> {
256
274
  const registry = await this.getKnownMarketplaces();
257
- return registry.marketplaces;
275
+ return registry.marketplaces.map((m) => ({
276
+ ...m,
277
+ isBuiltin: m.name === MarketplaceService.BUILTIN_MARKETPLACE.name,
278
+ }));
279
+ }
280
+
281
+ /**
282
+ * Removes a marketplace by name
283
+ */
284
+ async removeMarketplace(name: string): Promise<void> {
285
+ const registry = await this.getKnownMarketplaces();
286
+ const initialCount = registry.marketplaces.length;
287
+ registry.marketplaces = registry.marketplaces.filter(
288
+ (m) => m.name !== name,
289
+ );
290
+
291
+ if (registry.marketplaces.length === initialCount) {
292
+ throw new Error(`Marketplace ${name} not found`);
293
+ }
294
+
295
+ await this.saveKnownMarketplaces(registry);
258
296
  }
259
297
 
260
298
  /**
@@ -308,7 +346,10 @@ export class MarketplaceService {
308
346
  /**
309
347
  * Installs a plugin from a marketplace
310
348
  */
311
- async installPlugin(pluginAtMarketplace: string): Promise<InstalledPlugin> {
349
+ async installPlugin(
350
+ pluginAtMarketplace: string,
351
+ projectPath?: string,
352
+ ): Promise<InstalledPlugin> {
312
353
  const [pluginName, marketplaceName] = pluginAtMarketplace.split("@");
313
354
  if (!pluginName || !marketplaceName) {
314
355
  throw new Error("Invalid plugin format. Use name@marketplace");
@@ -394,7 +435,10 @@ export class MarketplaceService {
394
435
 
395
436
  const installedRegistry = await this.getInstalledPlugins();
396
437
  const existingIndex = installedRegistry.plugins.findIndex(
397
- (p) => p.name === pluginName && p.marketplace === marketplaceName,
438
+ (p) =>
439
+ p.name === pluginName &&
440
+ p.marketplace === marketplaceName &&
441
+ p.projectPath === projectPath,
398
442
  );
399
443
 
400
444
  const installedPlugin: InstalledPlugin = {
@@ -402,6 +446,7 @@ export class MarketplaceService {
402
446
  marketplace: marketplaceName,
403
447
  version,
404
448
  cachePath,
449
+ projectPath,
405
450
  };
406
451
 
407
452
  if (existingIndex >= 0) {
@@ -425,4 +470,55 @@ export class MarketplaceService {
425
470
  );
426
471
  }
427
472
  }
473
+
474
+ /**
475
+ * Uninstalls a plugin
476
+ */
477
+ async uninstallPlugin(
478
+ pluginAtMarketplace: string,
479
+ projectPath?: string,
480
+ ): Promise<void> {
481
+ const [pluginName, marketplaceName] = pluginAtMarketplace.split("@");
482
+ if (!pluginName || !marketplaceName) {
483
+ throw new Error("Invalid plugin format. Use name@marketplace");
484
+ }
485
+
486
+ const installedRegistry = await this.getInstalledPlugins();
487
+ const pluginIndex = installedRegistry.plugins.findIndex(
488
+ (p) =>
489
+ p.name === pluginName &&
490
+ p.marketplace === marketplaceName &&
491
+ p.projectPath === projectPath,
492
+ );
493
+
494
+ if (pluginIndex === -1) {
495
+ throw new Error(
496
+ `Plugin ${pluginName}@${marketplaceName} is not installed${projectPath ? ` for project ${projectPath}` : ""}`,
497
+ );
498
+ }
499
+
500
+ const pluginToRemove = installedRegistry.plugins[pluginIndex];
501
+
502
+ // Remove from registry first
503
+ installedRegistry.plugins.splice(pluginIndex, 1);
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
+ }
515
+ }
516
+
517
+ /**
518
+ * Updates a plugin (uninstall followed by install)
519
+ */
520
+ async updatePlugin(pluginAtMarketplace: string): Promise<InstalledPlugin> {
521
+ await this.uninstallPlugin(pluginAtMarketplace);
522
+ return this.installPlugin(pluginAtMarketplace);
523
+ }
428
524
  }
@@ -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
  }
@@ -124,7 +124,7 @@ function getModelConfig(
124
124
  config.temperature = undefined;
125
125
  }
126
126
 
127
- if (modelName.startsWith("gemini-3")) {
127
+ if (modelName.startsWith("gemini-3-flash")) {
128
128
  config.vertexai = {
129
129
  thinking_config: {
130
130
  thinking_level: "minimal",