wave-agent-sdk 0.6.4 → 0.7.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 (174) hide show
  1. package/dist/agent.d.ts +8 -0
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +49 -240
  4. package/dist/constants/tools.d.ts +0 -2
  5. package/dist/constants/tools.d.ts.map +1 -1
  6. package/dist/constants/tools.js +0 -2
  7. package/dist/core/plugin.d.ts +86 -0
  8. package/dist/core/plugin.d.ts.map +1 -0
  9. package/dist/core/plugin.js +164 -0
  10. package/dist/index.d.ts +1 -4
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +1 -5
  13. package/dist/managers/MemoryRuleManager.d.ts +3 -1
  14. package/dist/managers/MemoryRuleManager.d.ts.map +1 -1
  15. package/dist/managers/MemoryRuleManager.js +2 -1
  16. package/dist/managers/aiManager.d.ts +13 -23
  17. package/dist/managers/aiManager.d.ts.map +1 -1
  18. package/dist/managers/aiManager.js +59 -32
  19. package/dist/managers/backgroundTaskManager.d.ts +3 -1
  20. package/dist/managers/backgroundTaskManager.d.ts.map +1 -1
  21. package/dist/managers/backgroundTaskManager.js +2 -1
  22. package/dist/managers/bashManager.d.ts +4 -4
  23. package/dist/managers/bashManager.d.ts.map +1 -1
  24. package/dist/managers/bashManager.js +5 -2
  25. package/dist/managers/foregroundTaskManager.d.ts +3 -0
  26. package/dist/managers/foregroundTaskManager.d.ts.map +1 -1
  27. package/dist/managers/foregroundTaskManager.js +2 -1
  28. package/dist/managers/hookManager.d.ts +3 -3
  29. package/dist/managers/hookManager.d.ts.map +1 -1
  30. package/dist/managers/hookManager.js +20 -19
  31. package/dist/managers/liveConfigManager.d.ts +6 -13
  32. package/dist/managers/liveConfigManager.d.ts.map +1 -1
  33. package/dist/managers/liveConfigManager.js +50 -45
  34. package/dist/managers/lspManager.d.ts +4 -5
  35. package/dist/managers/lspManager.d.ts.map +1 -1
  36. package/dist/managers/lspManager.js +13 -12
  37. package/dist/managers/mcpManager.d.ts +3 -2
  38. package/dist/managers/mcpManager.d.ts.map +1 -1
  39. package/dist/managers/mcpManager.js +16 -15
  40. package/dist/managers/messageManager.d.ts +5 -7
  41. package/dist/managers/messageManager.d.ts.map +1 -1
  42. package/dist/managers/messageManager.js +12 -7
  43. package/dist/managers/permissionManager.d.ts +6 -4
  44. package/dist/managers/permissionManager.d.ts.map +1 -1
  45. package/dist/managers/permissionManager.js +39 -63
  46. package/dist/managers/planManager.d.ts +4 -6
  47. package/dist/managers/planManager.d.ts.map +1 -1
  48. package/dist/managers/planManager.js +18 -4
  49. package/dist/managers/pluginManager.d.ts +10 -22
  50. package/dist/managers/pluginManager.d.ts.map +1 -1
  51. package/dist/managers/pluginManager.js +27 -14
  52. package/dist/managers/reversionManager.d.ts +4 -3
  53. package/dist/managers/reversionManager.d.ts.map +1 -1
  54. package/dist/managers/reversionManager.js +5 -2
  55. package/dist/managers/skillManager.d.ts +3 -2
  56. package/dist/managers/skillManager.d.ts.map +1 -1
  57. package/dist/managers/skillManager.js +15 -14
  58. package/dist/managers/slashCommandManager.d.ts +9 -16
  59. package/dist/managers/slashCommandManager.d.ts.map +1 -1
  60. package/dist/managers/slashCommandManager.js +21 -10
  61. package/dist/managers/subagentManager.d.ts +7 -17
  62. package/dist/managers/subagentManager.d.ts.map +1 -1
  63. package/dist/managers/subagentManager.js +41 -34
  64. package/dist/managers/toolManager.d.ts +15 -38
  65. package/dist/managers/toolManager.d.ts.map +1 -1
  66. package/dist/managers/toolManager.js +66 -56
  67. package/dist/prompts/index.d.ts +6 -3
  68. package/dist/prompts/index.d.ts.map +1 -1
  69. package/dist/prompts/index.js +8 -16
  70. package/dist/services/MarketplaceService.d.ts.map +1 -1
  71. package/dist/services/MarketplaceService.js +13 -0
  72. package/dist/services/aiService.d.ts +4 -0
  73. package/dist/services/aiService.d.ts.map +1 -1
  74. package/dist/services/aiService.js +47 -7
  75. package/dist/services/configurationService.d.ts.map +1 -1
  76. package/dist/services/configurationService.js +30 -11
  77. package/dist/services/taskManager.d.ts +3 -1
  78. package/dist/services/taskManager.d.ts.map +1 -1
  79. package/dist/services/taskManager.js +2 -1
  80. package/dist/tools/bashTool.js +2 -2
  81. package/dist/tools/editTool.d.ts.map +1 -1
  82. package/dist/tools/editTool.js +9 -1
  83. package/dist/tools/readTool.d.ts.map +1 -1
  84. package/dist/tools/readTool.js +2 -2
  85. package/dist/tools/skillTool.d.ts +2 -4
  86. package/dist/tools/skillTool.d.ts.map +1 -1
  87. package/dist/tools/skillTool.js +61 -61
  88. package/dist/tools/taskOutputTool.js +1 -1
  89. package/dist/tools/taskTool.d.ts +2 -4
  90. package/dist/tools/taskTool.d.ts.map +1 -1
  91. package/dist/tools/taskTool.js +192 -187
  92. package/dist/tools/types.d.ts +11 -1
  93. package/dist/tools/types.d.ts.map +1 -1
  94. package/dist/tools/writeTool.d.ts.map +1 -1
  95. package/dist/tools/writeTool.js +4 -2
  96. package/dist/types/marketplace.d.ts +8 -0
  97. package/dist/types/marketplace.d.ts.map +1 -1
  98. package/dist/types/permissions.d.ts +1 -1
  99. package/dist/types/permissions.d.ts.map +1 -1
  100. package/dist/types/permissions.js +1 -3
  101. package/dist/types/skills.d.ts +0 -2
  102. package/dist/types/skills.d.ts.map +1 -1
  103. package/dist/types/tools.d.ts +0 -15
  104. package/dist/types/tools.d.ts.map +1 -1
  105. package/dist/utils/container.d.ts +31 -0
  106. package/dist/utils/container.d.ts.map +1 -0
  107. package/dist/utils/container.js +79 -0
  108. package/dist/utils/containerSetup.d.ts +26 -0
  109. package/dist/utils/containerSetup.d.ts.map +1 -0
  110. package/dist/utils/containerSetup.js +165 -0
  111. package/dist/utils/editUtils.d.ts +0 -3
  112. package/dist/utils/editUtils.d.ts.map +1 -1
  113. package/dist/utils/editUtils.js +4 -3
  114. package/dist/utils/hookMatcher.d.ts +1 -1
  115. package/dist/utils/hookMatcher.d.ts.map +1 -1
  116. package/dist/utils/hookMatcher.js +2 -2
  117. package/dist/utils/openaiClient.js +2 -2
  118. package/dist/utils/stringUtils.d.ts +6 -0
  119. package/dist/utils/stringUtils.d.ts.map +1 -1
  120. package/dist/utils/stringUtils.js +8 -0
  121. package/package.json +1 -1
  122. package/src/agent.ts +60 -282
  123. package/src/constants/tools.ts +0 -2
  124. package/src/core/plugin.ts +224 -0
  125. package/src/index.ts +1 -6
  126. package/src/managers/MemoryRuleManager.ts +6 -1
  127. package/src/managers/aiManager.ts +83 -58
  128. package/src/managers/backgroundTaskManager.ts +5 -1
  129. package/src/managers/bashManager.ts +9 -4
  130. package/src/managers/foregroundTaskManager.ts +3 -0
  131. package/src/managers/hookManager.ts +21 -23
  132. package/src/managers/liveConfigManager.ts +57 -53
  133. package/src/managers/lspManager.ts +14 -19
  134. package/src/managers/mcpManager.ts +20 -20
  135. package/src/managers/messageManager.ts +19 -12
  136. package/src/managers/permissionManager.ts +45 -70
  137. package/src/managers/planManager.ts +26 -7
  138. package/src/managers/pluginManager.ts +37 -33
  139. package/src/managers/reversionManager.ts +5 -3
  140. package/src/managers/skillManager.ts +19 -20
  141. package/src/managers/slashCommandManager.ts +30 -25
  142. package/src/managers/subagentManager.ts +53 -53
  143. package/src/managers/toolManager.ts +91 -90
  144. package/src/prompts/index.ts +12 -24
  145. package/src/services/MarketplaceService.ts +13 -0
  146. package/src/services/aiService.ts +61 -15
  147. package/src/services/configurationService.ts +34 -13
  148. package/src/services/taskManager.ts +5 -1
  149. package/src/tools/bashTool.ts +2 -2
  150. package/src/tools/editTool.ts +9 -1
  151. package/src/tools/readTool.ts +2 -2
  152. package/src/tools/skillTool.ts +75 -71
  153. package/src/tools/taskOutputTool.ts +1 -1
  154. package/src/tools/taskTool.ts +224 -225
  155. package/src/tools/types.ts +12 -1
  156. package/src/tools/writeTool.ts +4 -2
  157. package/src/types/marketplace.ts +9 -0
  158. package/src/types/permissions.ts +0 -4
  159. package/src/types/skills.ts +0 -3
  160. package/src/types/tools.ts +0 -17
  161. package/src/utils/container.ts +92 -0
  162. package/src/utils/containerSetup.ts +256 -0
  163. package/src/utils/editUtils.ts +4 -3
  164. package/src/utils/hookMatcher.ts +2 -2
  165. package/src/utils/openaiClient.ts +2 -2
  166. package/src/utils/stringUtils.ts +9 -0
  167. package/dist/tools/deleteFileTool.d.ts +0 -6
  168. package/dist/tools/deleteFileTool.d.ts.map +0 -1
  169. package/dist/tools/deleteFileTool.js +0 -100
  170. package/dist/tools/multiEditTool.d.ts +0 -6
  171. package/dist/tools/multiEditTool.d.ts.map +0 -1
  172. package/dist/tools/multiEditTool.js +0 -246
  173. package/src/tools/deleteFileTool.ts +0 -127
  174. package/src/tools/multiEditTool.ts +0 -306
@@ -2,9 +2,7 @@ import type { ToolContext, ToolPlugin, ToolResult } from "../tools/types.js";
2
2
  import { bashTool } from "../tools/bashTool.js";
3
3
  import { taskOutputTool } from "../tools/taskOutputTool.js";
4
4
  import { taskStopTool } from "../tools/taskStopTool.js";
5
- import { deleteFileTool } from "../tools/deleteFileTool.js";
6
5
  import { editTool } from "../tools/editTool.js";
7
- import { multiEditTool } from "../tools/multiEditTool.js";
8
6
  import { writeTool } from "../tools/writeTool.js";
9
7
  import { exitPlanModeTool } from "../tools/exitPlanMode.js";
10
8
  import { askUserQuestionTool } from "../tools/askUserQuestion.js";
@@ -14,8 +12,8 @@ import { grepTool } from "../tools/grepTool.js";
14
12
  import { lsTool } from "../tools/lsTool.js";
15
13
  import { readTool } from "../tools/readTool.js";
16
14
  import { lspTool } from "../tools/lspTool.js";
17
- import { createTaskTool } from "../tools/taskTool.js";
18
- import { createSkillTool } from "../tools/skillTool.js";
15
+ import { taskTool } from "../tools/taskTool.js";
16
+ import { skillTool } from "../tools/skillTool.js";
19
17
  import {
20
18
  taskCreateTool,
21
19
  taskGetTool,
@@ -26,7 +24,6 @@ import { McpManager } from "./mcpManager.js";
26
24
  import { PermissionManager } from "./permissionManager.js";
27
25
  import { ChatCompletionFunctionTool } from "openai/resources.js";
28
26
  import type {
29
- Logger,
30
27
  PermissionMode,
31
28
  PermissionCallback,
32
29
  ILspManager,
@@ -36,24 +33,14 @@ import type { SkillManager } from "./skillManager.js";
36
33
 
37
34
  import { ReversionManager } from "./reversionManager.js";
38
35
 
36
+ import { Container } from "../utils/container.js";
37
+
38
+ import { logger } from "../utils/globalLogger.js";
39
+
39
40
  export interface ToolManagerOptions {
40
- mcpManager: McpManager;
41
- lspManager?: ILspManager;
42
- logger?: Logger;
43
- /** Permission manager for handling tool permission checks */
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;
49
- /** Reversion manager for file snapshots */
50
- reversionManager?: ReversionManager;
51
- /** Background task manager for background execution */
52
- backgroundTaskManager?: import("./backgroundTaskManager.js").BackgroundTaskManager;
53
- /** Permission mode for tool execution (defaults to "default") */
54
- permissionMode?: PermissionMode;
55
- /** Custom permission callback for tool usage */
56
- canUseToolCallback?: PermissionCallback;
41
+ container: Container;
42
+ /** Optional list of tool names to enable */
43
+ tools?: string[];
57
44
  }
58
45
 
59
46
  /**
@@ -63,44 +50,31 @@ export interface ToolManagerOptions {
63
50
  * Supports both built-in tools and MCP (Model Context Protocol) tools.
64
51
  */
65
52
  class ToolManager {
66
- private tools = new Map<string, ToolPlugin>();
67
- private mcpManager: McpManager;
68
- private lspManager?: ILspManager;
69
- private logger?: Logger;
70
- private permissionManager?: PermissionManager;
71
- private foregroundTaskManager?: import("../types/processes.js").IForegroundTaskManager;
72
- private reversionManager?: ReversionManager;
73
- private taskManager?: import("../services/taskManager.js").TaskManager;
74
- private backgroundTaskManager?: import("./backgroundTaskManager.js").BackgroundTaskManager;
75
- private permissionMode?: PermissionMode;
76
- private canUseToolCallback?: PermissionCallback;
53
+ private toolsRegistry = new Map<string, ToolPlugin>();
54
+ private tools?: string[];
55
+ private container: Container;
77
56
 
78
57
  constructor(options: ToolManagerOptions) {
79
- this.mcpManager = options.mcpManager;
80
- this.lspManager = options.lspManager;
81
- this.logger = options.logger;
82
- this.permissionManager = options.permissionManager;
83
- this.taskManager = options.taskManager;
84
- this.foregroundTaskManager = options.foregroundTaskManager;
85
- this.reversionManager = options.reversionManager;
86
- this.backgroundTaskManager = options.backgroundTaskManager;
87
- // Store CLI permission mode, let PermissionManager resolve effective mode
88
- this.permissionMode = options.permissionMode;
89
- this.canUseToolCallback = options.canUseToolCallback;
58
+ this.container = options.container;
59
+ this.tools = options.tools;
60
+ }
61
+
62
+ private get mcpManager(): McpManager {
63
+ return this.container.get<McpManager>("McpManager")!;
90
64
  }
91
65
 
92
66
  /**
93
67
  * Register a new tool
94
68
  */
95
69
  public register(tool: ToolPlugin): void {
96
- this.tools.set(tool.name, tool);
70
+ this.toolsRegistry.set(tool.name, tool);
97
71
  }
98
72
 
99
73
  /**
100
74
  * Initialize built-in tools. Can be called with dependencies for tools that require them.
101
75
  *
102
76
  * This method can be called multiple times safely. When called without dependencies,
103
- * it registers basic tools (Bash, Read, Write, TodoWrite, etc.). When called with
77
+ * it registers basic tools (Bash, Read, Write, TaskCreate, etc.). When called with
104
78
  * dependencies, it also registers tools that require managers (Task, Skill).
105
79
  *
106
80
  * @param deps Optional dependencies for advanced tools
@@ -119,17 +93,12 @@ class ToolManager {
119
93
  * });
120
94
  * ```
121
95
  */
122
- public initializeBuiltInTools(deps?: {
123
- subagentManager?: SubagentManager;
124
- skillManager?: SkillManager;
125
- }): void {
96
+ public initializeBuiltInTools(): void {
126
97
  const builtInTools = [
127
98
  bashTool,
128
99
  taskOutputTool,
129
100
  taskStopTool,
130
- deleteFileTool,
131
101
  editTool,
132
- multiEditTool,
133
102
  writeTool,
134
103
  exitPlanModeTool,
135
104
  askUserQuestionTool,
@@ -138,6 +107,8 @@ class ToolManager {
138
107
  lsTool,
139
108
  readTool,
140
109
  lspTool,
110
+ taskTool,
111
+ skillTool,
141
112
  taskCreateTool,
142
113
  taskGetTool,
143
114
  taskUpdateTool,
@@ -145,19 +116,22 @@ class ToolManager {
145
116
  ];
146
117
 
147
118
  for (const tool of builtInTools) {
148
- this.tools.set(tool.name, tool);
149
- }
150
-
151
- // Register tools that require dependencies
152
- if (deps?.subagentManager) {
153
- const taskTool = createTaskTool(deps.subagentManager);
154
- this.tools.set(taskTool.name, taskTool);
119
+ if (this.shouldEnableTool(tool.name)) {
120
+ this.toolsRegistry.set(tool.name, tool);
121
+ }
155
122
  }
123
+ }
156
124
 
157
- if (deps?.skillManager) {
158
- const skillTool = createSkillTool(deps.skillManager);
159
- this.tools.set(skillTool.name, skillTool);
125
+ /**
126
+ * Check if a tool should be enabled based on tools configuration
127
+ */
128
+ private shouldEnableTool(name: string): boolean {
129
+ if (!this.tools) {
130
+ return true;
160
131
  }
132
+ return this.tools.some(
133
+ (toolName) => toolName.toLowerCase() === name.toLowerCase(),
134
+ );
161
135
  }
162
136
 
163
137
  /**
@@ -178,32 +152,53 @@ class ToolManager {
178
152
  args: Record<string, unknown>,
179
153
  context: ToolContext,
180
154
  ): Promise<ToolResult> {
155
+ const permissionManager =
156
+ this.container.get<PermissionManager>("PermissionManager");
157
+ const permissionMode = this.container.has("PermissionMode")
158
+ ? this.container.get<PermissionMode>("PermissionMode")
159
+ : undefined;
160
+
181
161
  // Resolve effective permission mode (CLI override > configuration > default)
182
- const effectivePermissionMode = this.permissionManager
183
- ? this.permissionManager.getCurrentEffectiveMode(this.permissionMode)
184
- : this.permissionMode || "default";
162
+ const effectivePermissionMode = permissionManager
163
+ ? permissionManager.getCurrentEffectiveMode(permissionMode)
164
+ : permissionMode || "default";
185
165
 
186
166
  // Enhance context with permission-related fields
187
167
  const enhancedContext: ToolContext = {
188
168
  ...context,
189
169
  permissionMode: effectivePermissionMode,
190
- canUseToolCallback: this.canUseToolCallback,
191
- permissionManager: this.permissionManager,
192
- taskManager: this.taskManager!,
193
- reversionManager: this.reversionManager,
194
- backgroundTaskManager: this.backgroundTaskManager,
195
- foregroundTaskManager: this.foregroundTaskManager,
170
+ canUseToolCallback: this.container.has("CanUseToolCallback")
171
+ ? this.container.get<PermissionCallback>("CanUseToolCallback")
172
+ : undefined,
173
+ permissionManager,
174
+ taskManager:
175
+ this.container.get<import("../services/taskManager.js").TaskManager>(
176
+ "TaskManager",
177
+ )!,
178
+ reversionManager:
179
+ this.container.get<ReversionManager>("ReversionManager")!,
180
+ backgroundTaskManager: this.container.get<
181
+ import("./backgroundTaskManager.js").BackgroundTaskManager
182
+ >("BackgroundTaskManager")!,
183
+ foregroundTaskManager: this.container.get<
184
+ import("../types/processes.js").IForegroundTaskManager
185
+ >("ForegroundTaskManager")!,
196
186
  mcpManager: this.mcpManager,
197
- lspManager: this.lspManager,
187
+ lspManager: this.container.get<ILspManager>("LspManager")!,
188
+ subagentManager: this.container.has("SubagentManager")
189
+ ? this.container.get<SubagentManager>("SubagentManager")
190
+ : undefined,
191
+ skillManager: this.container.has("SkillManager")
192
+ ? this.container.get<SkillManager>("SkillManager")
193
+ : undefined,
198
194
  sessionId: context.sessionId,
199
195
  };
200
196
 
201
- this.logger?.debug("Executing tool with enhanced context", {
197
+ logger?.debug("Executing tool with enhanced context", {
202
198
  toolName: name,
203
- cliPermissionMode: this.permissionMode,
199
+ permissionMode,
204
200
  effectivePermissionMode,
205
- hasPermissionManager: !!this.permissionManager,
206
- hasPermissionCallback: !!this.canUseToolCallback,
201
+ hasPermissionManager: !!permissionManager,
207
202
  });
208
203
 
209
204
  // Check if it's an MCP tool first
@@ -216,12 +211,12 @@ class ToolManager {
216
211
  }
217
212
 
218
213
  // Check built-in tools
219
- const plugin = this.tools.get(name);
214
+ const plugin = this.toolsRegistry.get(name);
220
215
  if (plugin) {
221
216
  try {
222
217
  return await plugin.execute(args, enhancedContext);
223
218
  } catch (error) {
224
- this.logger?.error("Tool execution failed", {
219
+ logger?.error("Tool execution failed", {
225
220
  toolName: name,
226
221
  error: error instanceof Error ? error.message : String(error),
227
222
  });
@@ -233,7 +228,7 @@ class ToolManager {
233
228
  }
234
229
  }
235
230
 
236
- this.logger?.warn("Tool not found", { toolName: name });
231
+ logger?.warn("Tool not found", { toolName: name });
237
232
  return {
238
233
  success: false,
239
234
  content: "",
@@ -242,14 +237,14 @@ class ToolManager {
242
237
  }
243
238
 
244
239
  list(): ToolPlugin[] {
245
- const builtInTools = Array.from(this.tools.values());
240
+ const builtInTools = Array.from(this.toolsRegistry.values());
246
241
  const mcpTools = this.mcpManager.getMcpToolPlugins();
247
242
  return [...builtInTools, ...mcpTools];
248
243
  }
249
244
 
250
245
  getToolsConfig(): ChatCompletionFunctionTool[] {
251
246
  const effectivePermissionMode = this.getPermissionMode();
252
- const builtInToolsConfig = Array.from(this.tools.values())
247
+ const builtInToolsConfig = Array.from(this.toolsRegistry.values())
253
248
  .filter((tool) => {
254
249
  if (effectivePermissionMode === "bypassPermissions") {
255
250
  if (tool.name === "ExitPlanMode" || tool.name === "AskUserQuestion") {
@@ -270,19 +265,23 @@ class ToolManager {
270
265
  * Get the list of registered tool plugins
271
266
  */
272
267
  public getTools(): ToolPlugin[] {
273
- return Array.from(this.tools.values());
268
+ return Array.from(this.toolsRegistry.values());
274
269
  }
275
270
 
276
271
  /**
277
272
  * Get the current permission mode
278
273
  */
279
274
  public getPermissionMode(): PermissionMode {
280
- if (this.permissionManager) {
281
- return this.permissionManager.getCurrentEffectiveMode(
282
- this.permissionMode,
283
- );
275
+ const permissionManager =
276
+ this.container.get<PermissionManager>("PermissionManager");
277
+ const permissionMode = this.container.has("PermissionMode")
278
+ ? this.container.get<PermissionMode>("PermissionMode")
279
+ : undefined;
280
+
281
+ if (permissionManager) {
282
+ return permissionManager.getCurrentEffectiveMode(permissionMode);
284
283
  }
285
- return this.permissionMode || "default";
284
+ return permissionMode || "default";
286
285
  }
287
286
 
288
287
  /**
@@ -290,14 +289,14 @@ class ToolManager {
290
289
  * @param mode - The new permission mode
291
290
  */
292
291
  public setPermissionMode(mode: PermissionMode): void {
293
- this.permissionMode = mode;
292
+ this.container.register("PermissionMode", mode);
294
293
  }
295
294
 
296
295
  /**
297
296
  * Get the permission manager
298
297
  */
299
298
  public getPermissionManager(): PermissionManager | undefined {
300
- return this.permissionManager;
299
+ return this.container.get<PermissionManager>("PermissionManager");
301
300
  }
302
301
 
303
302
  /**
@@ -306,7 +305,9 @@ class ToolManager {
306
305
  public getTaskManager():
307
306
  | import("../services/taskManager.js").TaskManager
308
307
  | undefined {
309
- return this.taskManager;
308
+ return this.container.get<import("../services/taskManager.js").TaskManager>(
309
+ "TaskManager",
310
+ );
310
311
  }
311
312
  }
312
313
 
@@ -12,10 +12,6 @@ import {
12
12
  GLOB_TOOL_NAME,
13
13
  GREP_TOOL_NAME,
14
14
  READ_TOOL_NAME,
15
- TASK_CREATE_TOOL_NAME,
16
- TASK_GET_TOOL_NAME,
17
- TASK_UPDATE_TOOL_NAME,
18
- TASK_LIST_TOOL_NAME,
19
15
  WRITE_TOOL_NAME,
20
16
  EXIT_PLAN_MODE_TOOL_NAME,
21
17
  } from "../constants/tools.js";
@@ -38,12 +34,6 @@ export const TOOL_POLICY = `
38
34
  - However, if some tool calls depend on previous calls to inform dependent values, do NOT call these tools in parallel and instead call them sequentially. For instance, if one operation must complete before another starts, run these operations sequentially instead. Never use placeholders or guess missing parameters in tool calls.
39
35
  - If the user specifies that they want you to run tools "in parallel", you MUST send a single message with multiple tool use content blocks.`;
40
36
 
41
- export const TASK_MANAGEMENT_POLICY = `
42
- # Task Management
43
- You have access to the ${TASK_CREATE_TOOL_NAME}, ${TASK_GET_TOOL_NAME}, ${TASK_UPDATE_TOOL_NAME}, and ${TASK_LIST_TOOL_NAME} tools to help you manage and plan tasks. Use these tools VERY frequently to ensure that you are tracking your tasks and giving the user visibility into your progress.
44
- These tools are also EXTREMELY helpful for planning tasks, and for breaking down larger complex tasks into smaller steps. If you do not use this tool when planning, you may forget to do important tasks - and that is unacceptable.
45
- It is critical that you mark tasks as completed as soon as you are done with a task. Do not batch up multiple tasks before marking them as completed.`;
46
-
47
37
  export function buildPlanModePrompt(
48
38
  planFilePath: string,
49
39
  planExists: boolean,
@@ -171,7 +161,6 @@ export const EXPLORE_SUBAGENT_SYSTEM_PROMPT = `You are a file search specialist.
171
161
  This is a READ-ONLY exploration task. You are STRICTLY PROHIBITED from:
172
162
  - Creating new files (no Write, touch, or file creation of any kind)
173
163
  - Modifying existing files (no Edit operations)
174
- - Deleting files (no rm or deletion)
175
164
  - Moving or copying files (no mv or cp)
176
165
  - Creating temporary files anywhere, including /tmp
177
166
  - Using redirect operators (>, >>, |) or heredocs to write to files
@@ -209,7 +198,6 @@ export const PLAN_SUBAGENT_SYSTEM_PROMPT = `You are a software architect and pla
209
198
  This is a READ-ONLY planning task. You are STRICTLY PROHIBITED from:
210
199
  - Creating new files (no Write, touch, or file creation of any kind)
211
200
  - Modifying existing files (no Edit operations)
212
- - Deleting files (no rm or deletion)
213
201
  - Moving or copying files (no mv or cp)
214
202
  - Creating temporary files anywhere, including /tmp
215
203
  - Using redirect operators (>, >>, |) or heredocs to write to files
@@ -300,6 +288,9 @@ Any promises made to the user
300
288
  Be concise but complete—err on the side of including information that would prevent duplicate work or repeated mistakes. Write in a way that enables immediate resumption of the task.
301
289
  Wrap your summary in <summary></summary> tags.`;
302
290
 
291
+ import type { SubagentConfiguration } from "../utils/subagentParser.js";
292
+ import type { SkillMetadata } from "../types/skills.js";
293
+
303
294
  export function buildSystemPrompt(
304
295
  basePrompt: string | undefined,
305
296
  tools: ToolPlugin[],
@@ -312,25 +303,22 @@ export function buildSystemPrompt(
312
303
  planFilePath: string;
313
304
  planExists: boolean;
314
305
  };
306
+ availableSubagents?: SubagentConfiguration[];
307
+ availableSkills?: SkillMetadata[];
315
308
  } = {},
316
309
  ): string {
317
310
  let prompt = basePrompt || DEFAULT_SYSTEM_PROMPT;
318
- const toolNames = new Set(tools.map((t) => t.name));
319
-
320
- if (
321
- toolNames.has(TASK_CREATE_TOOL_NAME) ||
322
- toolNames.has(TASK_GET_TOOL_NAME) ||
323
- toolNames.has(TASK_UPDATE_TOOL_NAME) ||
324
- toolNames.has(TASK_LIST_TOOL_NAME)
325
- ) {
326
- prompt += TASK_MANAGEMENT_POLICY;
311
+ if (tools.length > 0) {
312
+ prompt += `\n\n${TOOL_POLICY}`;
327
313
  }
328
314
 
329
- prompt += `\n\n${TOOL_POLICY}`;
330
-
331
315
  for (const tool of tools) {
332
316
  if (tool.prompt) {
333
- prompt += tool.prompt();
317
+ prompt += tool.prompt({
318
+ availableSubagents: options.availableSubagents,
319
+ availableSkills: options.availableSkills,
320
+ workdir: options.workdir,
321
+ });
334
322
  }
335
323
  }
336
324
 
@@ -80,6 +80,16 @@ export class MarketplaceService {
80
80
  }
81
81
  try {
82
82
  const content = await fs.readFile(this.knownMarketplacesPath, "utf-8");
83
+ if (!content.trim()) {
84
+ return {
85
+ marketplaces: [
86
+ {
87
+ ...MarketplaceService.BUILTIN_MARKETPLACE,
88
+ isBuiltin: true,
89
+ },
90
+ ],
91
+ };
92
+ }
83
93
  return JSON.parse(content);
84
94
  } catch (error) {
85
95
  console.error("Failed to load known marketplaces:", error);
@@ -96,6 +106,9 @@ export class MarketplaceService {
96
106
  }
97
107
  try {
98
108
  const content = await fs.readFile(this.installedPluginsPath, "utf-8");
109
+ if (!content.trim()) {
110
+ return { plugins: [] };
111
+ }
99
112
  return JSON.parse(content);
100
113
  } catch (error) {
101
114
  console.error("Failed to load installed plugins:", error);
@@ -8,6 +8,7 @@ import {
8
8
  } from "openai/resources.js";
9
9
  import { OpenAIClient } from "../utils/openaiClient.js";
10
10
  import { logger } from "../utils/globalLogger.js";
11
+ import { addOnceAbortListener } from "../utils/abortUtils.js";
11
12
  import type { GatewayConfig, ModelConfig } from "../types/index.js";
12
13
  import {
13
14
  transformMessagesForClaudeCache,
@@ -69,13 +70,50 @@ interface ErrorData {
69
70
  type OpenAIModelConfig = Omit<
70
71
  ChatCompletionCreateParamsNonStreaming,
71
72
  "messages"
72
- > & {
73
- vertexai?: {
74
- thinking_config: {
75
- thinking_level: string;
76
- };
77
- };
78
- };
73
+ >;
74
+
75
+ // Global rate limiter state for 1 QPS
76
+ let nextAllowedTime = 0;
77
+ const MIN_INTERVAL = 1000; // 1 second for 1 QPS
78
+
79
+ /**
80
+ * Resets the rate limiter state. Primarily used for testing.
81
+ */
82
+ export function resetRateLimiter(): void {
83
+ nextAllowedTime = 0;
84
+ }
85
+
86
+ /**
87
+ * Acquires a slot for an AI request, ensuring 1 QPS limit.
88
+ * @param abortSignal Optional abort signal to cancel waiting
89
+ */
90
+ async function acquireSlot(abortSignal?: AbortSignal): Promise<void> {
91
+ if (abortSignal?.aborted) {
92
+ throw new Error("Request was aborted");
93
+ }
94
+
95
+ const now = Date.now();
96
+ const waitTime = Math.max(0, nextAllowedTime - now);
97
+
98
+ // Reserve the slot synchronously to ensure global ordering
99
+ nextAllowedTime = Math.max(now, nextAllowedTime) + MIN_INTERVAL;
100
+
101
+ if (waitTime > 0) {
102
+ await new Promise<void>((resolve, reject) => {
103
+ const timeout = setTimeout(() => {
104
+ cleanup();
105
+ resolve();
106
+ }, waitTime);
107
+
108
+ const cleanup = abortSignal
109
+ ? addOnceAbortListener(abortSignal, () => {
110
+ clearTimeout(timeout);
111
+ reject(new Error("Request was aborted"));
112
+ })
113
+ : () => {};
114
+ });
115
+ }
116
+ }
79
117
 
80
118
  /**
81
119
  * Get specific configuration parameters based on model name
@@ -99,14 +137,6 @@ function getModelConfig(
99
137
  config.temperature = undefined;
100
138
  }
101
139
 
102
- if (modelName.startsWith("gemini-3-flash")) {
103
- config.vertexai = {
104
- thinking_config: {
105
- thinking_level: "minimal",
106
- },
107
- };
108
- }
109
-
110
140
  return config;
111
141
  }
112
142
 
@@ -170,6 +200,14 @@ export async function callAgent(
170
200
  onReasoningUpdate,
171
201
  } = options;
172
202
 
203
+ // Apply global 1 QPS rate limit
204
+ if (
205
+ process.env.NODE_ENV !== "test" ||
206
+ modelConfig.agentModel === "rate-limit-test"
207
+ ) {
208
+ await acquireSlot(abortSignal);
209
+ }
210
+
173
211
  // Declare variables outside try block for error handling access
174
212
  let openaiMessages: ChatCompletionMessageParam[] | undefined;
175
213
  let createParams:
@@ -719,6 +757,14 @@ export async function compressMessages(
719
757
  ): Promise<CompressMessagesResult> {
720
758
  const { gatewayConfig, modelConfig, messages, abortSignal } = options;
721
759
 
760
+ // Apply global 1 QPS rate limit
761
+ if (
762
+ process.env.NODE_ENV !== "test" ||
763
+ modelConfig.agentModel === "rate-limit-test"
764
+ ) {
765
+ await acquireSlot(abortSignal);
766
+ }
767
+
722
768
  // Create OpenAI client with injected configuration
723
769
  const openai = new OpenAIClient({
724
770
  apiKey: gatewayConfig.apiKey,
@@ -374,7 +374,7 @@ export class ConfigurationService {
374
374
  if (apiKey !== undefined) {
375
375
  resolvedApiKey = apiKey;
376
376
  } else {
377
- resolvedApiKey = this.env.WAVE_API_KEY || process.env.WAVE_API_KEY;
377
+ resolvedApiKey = this.env.WAVE_API_KEY;
378
378
  }
379
379
 
380
380
  // Resolve base URL: constructor > env (settings.json) > process.env
@@ -383,8 +383,25 @@ export class ConfigurationService {
383
383
  if (baseURL !== undefined) {
384
384
  resolvedBaseURL = baseURL;
385
385
  } else {
386
- resolvedBaseURL =
387
- this.env.WAVE_BASE_URL || process.env.WAVE_BASE_URL || "";
386
+ resolvedBaseURL = this.env.WAVE_BASE_URL || "";
387
+ }
388
+
389
+ // If we have a parent configuration, use it as a fallback for API key and base URL
390
+ if (this.currentConfiguration?.env) {
391
+ if (resolvedApiKey === undefined) {
392
+ resolvedApiKey = this.currentConfiguration.env.WAVE_API_KEY;
393
+ }
394
+ if (!resolvedBaseURL) {
395
+ resolvedBaseURL = this.currentConfiguration.env.WAVE_BASE_URL || "";
396
+ }
397
+ }
398
+
399
+ // Fallback to process.env if still not resolved (for dynamic updates in tests)
400
+ if (resolvedApiKey === undefined) {
401
+ resolvedApiKey = process.env.WAVE_API_KEY;
402
+ }
403
+ if (!resolvedBaseURL) {
404
+ resolvedBaseURL = process.env.WAVE_BASE_URL || "";
388
405
  }
389
406
 
390
407
  if (!resolvedBaseURL && baseURL === undefined) {
@@ -443,18 +460,22 @@ export class ConfigurationService {
443
460
  const DEFAULT_FAST_MODEL = "gemini-2.5-flash";
444
461
 
445
462
  // Resolve agent model: constructor > env (settings.json) > process.env > default
446
- const resolvedAgentModel =
447
- agentModel ||
448
- this.env.WAVE_MODEL ||
449
- process.env.WAVE_MODEL ||
450
- DEFAULT_AGENT_MODEL;
463
+ let resolvedAgentModel = agentModel || this.env.WAVE_MODEL;
464
+
465
+ if (!resolvedAgentModel && this.currentConfiguration?.env?.WAVE_MODEL) {
466
+ resolvedAgentModel = this.currentConfiguration.env.WAVE_MODEL;
467
+ }
468
+ resolvedAgentModel =
469
+ resolvedAgentModel || process.env.WAVE_MODEL || DEFAULT_AGENT_MODEL;
451
470
 
452
471
  // Resolve fast model: constructor > env (settings.json) > process.env > default
453
- const resolvedFastModel =
454
- fastModel ||
455
- this.env.WAVE_FAST_MODEL ||
456
- process.env.WAVE_FAST_MODEL ||
457
- DEFAULT_FAST_MODEL;
472
+ let resolvedFastModel = fastModel || this.env.WAVE_FAST_MODEL;
473
+
474
+ if (!resolvedFastModel && this.currentConfiguration?.env?.WAVE_FAST_MODEL) {
475
+ resolvedFastModel = this.currentConfiguration.env.WAVE_FAST_MODEL;
476
+ }
477
+ resolvedFastModel =
478
+ resolvedFastModel || process.env.WAVE_FAST_MODEL || DEFAULT_FAST_MODEL;
458
479
 
459
480
  // Resolve max output tokens
460
481
  const resolvedMaxTokens = this.resolveMaxOutputTokens(maxTokens);
@@ -4,12 +4,16 @@ import { homedir } from "os";
4
4
  import { EventEmitter } from "events";
5
5
  import { Task } from "../types/tasks.js";
6
6
  import { logger } from "../utils/globalLogger.js";
7
+ import { Container } from "../utils/container.js";
7
8
 
8
9
  export class TaskManager extends EventEmitter {
9
10
  private readonly baseDir: string;
10
11
  private taskListId: string;
11
12
 
12
- constructor(taskListId: string) {
13
+ constructor(
14
+ private container: Container,
15
+ taskListId: string,
16
+ ) {
13
17
  super();
14
18
  this.taskListId = taskListId;
15
19
  this.baseDir = join(homedir(), ".wave", "tasks");
@@ -306,7 +306,7 @@ Usage notes:
306
306
  }
307
307
 
308
308
  child.stdout?.on("data", (data) => {
309
- if (!isAborted && !isBackgrounded) {
309
+ if (!isAborted && !isBackgrounded && !runInBackground) {
310
310
  const chunk = stripAnsiColors(data.toString());
311
311
  outputBuffer += chunk;
312
312
  context.onShortResultUpdate?.(chunk.trim().split("\n").pop() || "");
@@ -314,7 +314,7 @@ Usage notes:
314
314
  });
315
315
 
316
316
  child.stderr?.on("data", (data) => {
317
- if (!isAborted && !isBackgrounded) {
317
+ if (!isAborted && !isBackgrounded && !runInBackground) {
318
318
  const chunk = stripAnsiColors(data.toString());
319
319
  errorBuffer += chunk;
320
320
  context.onShortResultUpdate?.(chunk.trim().split("\n").pop() || "");