wave-agent-sdk 0.6.5 → 0.7.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 +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/core/session.d.ts +3 -0
  11. package/dist/core/session.d.ts.map +1 -0
  12. package/dist/core/session.js +1 -0
  13. package/dist/index.d.ts +2 -9
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +2 -10
  16. package/dist/managers/MemoryRuleManager.d.ts +3 -1
  17. package/dist/managers/MemoryRuleManager.d.ts.map +1 -1
  18. package/dist/managers/MemoryRuleManager.js +2 -1
  19. package/dist/managers/aiManager.d.ts +13 -23
  20. package/dist/managers/aiManager.d.ts.map +1 -1
  21. package/dist/managers/aiManager.js +60 -32
  22. package/dist/managers/backgroundTaskManager.d.ts +3 -1
  23. package/dist/managers/backgroundTaskManager.d.ts.map +1 -1
  24. package/dist/managers/backgroundTaskManager.js +2 -1
  25. package/dist/managers/bashManager.d.ts +4 -4
  26. package/dist/managers/bashManager.d.ts.map +1 -1
  27. package/dist/managers/bashManager.js +5 -2
  28. package/dist/managers/foregroundTaskManager.d.ts +3 -0
  29. package/dist/managers/foregroundTaskManager.d.ts.map +1 -1
  30. package/dist/managers/foregroundTaskManager.js +2 -1
  31. package/dist/managers/hookManager.d.ts +3 -3
  32. package/dist/managers/hookManager.d.ts.map +1 -1
  33. package/dist/managers/hookManager.js +20 -19
  34. package/dist/managers/liveConfigManager.d.ts +6 -13
  35. package/dist/managers/liveConfigManager.d.ts.map +1 -1
  36. package/dist/managers/liveConfigManager.js +50 -45
  37. package/dist/managers/lspManager.d.ts +4 -5
  38. package/dist/managers/lspManager.d.ts.map +1 -1
  39. package/dist/managers/lspManager.js +13 -12
  40. package/dist/managers/mcpManager.d.ts +3 -2
  41. package/dist/managers/mcpManager.d.ts.map +1 -1
  42. package/dist/managers/mcpManager.js +16 -15
  43. package/dist/managers/messageManager.d.ts +5 -7
  44. package/dist/managers/messageManager.d.ts.map +1 -1
  45. package/dist/managers/messageManager.js +12 -7
  46. package/dist/managers/permissionManager.d.ts +6 -4
  47. package/dist/managers/permissionManager.d.ts.map +1 -1
  48. package/dist/managers/permissionManager.js +39 -63
  49. package/dist/managers/planManager.d.ts +4 -6
  50. package/dist/managers/planManager.d.ts.map +1 -1
  51. package/dist/managers/planManager.js +18 -4
  52. package/dist/managers/pluginManager.d.ts +10 -22
  53. package/dist/managers/pluginManager.d.ts.map +1 -1
  54. package/dist/managers/pluginManager.js +27 -14
  55. package/dist/managers/reversionManager.d.ts +4 -3
  56. package/dist/managers/reversionManager.d.ts.map +1 -1
  57. package/dist/managers/reversionManager.js +5 -2
  58. package/dist/managers/skillManager.d.ts +3 -2
  59. package/dist/managers/skillManager.d.ts.map +1 -1
  60. package/dist/managers/skillManager.js +15 -14
  61. package/dist/managers/slashCommandManager.d.ts +9 -16
  62. package/dist/managers/slashCommandManager.d.ts.map +1 -1
  63. package/dist/managers/slashCommandManager.js +21 -10
  64. package/dist/managers/subagentManager.d.ts +7 -17
  65. package/dist/managers/subagentManager.d.ts.map +1 -1
  66. package/dist/managers/subagentManager.js +41 -34
  67. package/dist/managers/toolManager.d.ts +15 -38
  68. package/dist/managers/toolManager.d.ts.map +1 -1
  69. package/dist/managers/toolManager.js +67 -56
  70. package/dist/prompts/index.d.ts +6 -2
  71. package/dist/prompts/index.d.ts.map +1 -1
  72. package/dist/prompts/index.js +8 -4
  73. package/dist/services/MarketplaceService.d.ts.map +1 -1
  74. package/dist/services/MarketplaceService.js +13 -0
  75. package/dist/services/aiService.d.ts +4 -0
  76. package/dist/services/aiService.d.ts.map +1 -1
  77. package/dist/services/aiService.js +47 -7
  78. package/dist/services/configurationService.d.ts.map +1 -1
  79. package/dist/services/configurationService.js +30 -11
  80. package/dist/services/taskManager.d.ts +3 -1
  81. package/dist/services/taskManager.d.ts.map +1 -1
  82. package/dist/services/taskManager.js +3 -2
  83. package/dist/tools/bashTool.d.ts.map +1 -1
  84. package/dist/tools/bashTool.js +19 -4
  85. package/dist/tools/editTool.d.ts.map +1 -1
  86. package/dist/tools/editTool.js +16 -4
  87. package/dist/tools/readTool.d.ts.map +1 -1
  88. package/dist/tools/readTool.js +2 -2
  89. package/dist/tools/skillTool.d.ts +2 -4
  90. package/dist/tools/skillTool.d.ts.map +1 -1
  91. package/dist/tools/skillTool.js +61 -61
  92. package/dist/tools/taskOutputTool.d.ts.map +1 -1
  93. package/dist/tools/taskOutputTool.js +3 -2
  94. package/dist/tools/taskTool.d.ts +2 -4
  95. package/dist/tools/taskTool.d.ts.map +1 -1
  96. package/dist/tools/taskTool.js +192 -187
  97. package/dist/tools/types.d.ts +12 -1
  98. package/dist/tools/types.d.ts.map +1 -1
  99. package/dist/tools/writeTool.d.ts.map +1 -1
  100. package/dist/tools/writeTool.js +5 -2
  101. package/dist/types/core.d.ts +2 -0
  102. package/dist/types/core.d.ts.map +1 -1
  103. package/dist/types/marketplace.d.ts +8 -0
  104. package/dist/types/marketplace.d.ts.map +1 -1
  105. package/dist/types/messaging.d.ts +1 -0
  106. package/dist/types/messaging.d.ts.map +1 -1
  107. package/dist/types/permissions.d.ts +3 -1
  108. package/dist/types/permissions.d.ts.map +1 -1
  109. package/dist/types/permissions.js +1 -3
  110. package/dist/types/skills.d.ts +0 -2
  111. package/dist/types/skills.d.ts.map +1 -1
  112. package/dist/types/tools.d.ts +0 -15
  113. package/dist/types/tools.d.ts.map +1 -1
  114. package/dist/utils/container.d.ts +31 -0
  115. package/dist/utils/container.d.ts.map +1 -0
  116. package/dist/utils/container.js +79 -0
  117. package/dist/utils/containerSetup.d.ts +26 -0
  118. package/dist/utils/containerSetup.d.ts.map +1 -0
  119. package/dist/utils/containerSetup.js +187 -0
  120. package/dist/utils/editUtils.d.ts +0 -3
  121. package/dist/utils/editUtils.d.ts.map +1 -1
  122. package/dist/utils/editUtils.js +4 -3
  123. package/dist/utils/hookMatcher.d.ts +1 -1
  124. package/dist/utils/hookMatcher.d.ts.map +1 -1
  125. package/dist/utils/hookMatcher.js +2 -2
  126. package/dist/utils/messageOperations.d.ts +2 -1
  127. package/dist/utils/messageOperations.d.ts.map +1 -1
  128. package/dist/utils/messageOperations.js +4 -1
  129. package/dist/utils/openaiClient.js +2 -2
  130. package/dist/utils/stringUtils.d.ts +6 -0
  131. package/dist/utils/stringUtils.d.ts.map +1 -1
  132. package/dist/utils/stringUtils.js +8 -0
  133. package/package.json +1 -1
  134. package/src/agent.ts +60 -282
  135. package/src/constants/tools.ts +0 -2
  136. package/src/core/plugin.ts +224 -0
  137. package/src/core/session.ts +6 -0
  138. package/src/index.ts +2 -11
  139. package/src/managers/MemoryRuleManager.ts +6 -1
  140. package/src/managers/aiManager.ts +84 -58
  141. package/src/managers/backgroundTaskManager.ts +5 -1
  142. package/src/managers/bashManager.ts +9 -4
  143. package/src/managers/foregroundTaskManager.ts +3 -0
  144. package/src/managers/hookManager.ts +21 -23
  145. package/src/managers/liveConfigManager.ts +57 -53
  146. package/src/managers/lspManager.ts +14 -19
  147. package/src/managers/mcpManager.ts +20 -20
  148. package/src/managers/messageManager.ts +19 -12
  149. package/src/managers/permissionManager.ts +45 -70
  150. package/src/managers/planManager.ts +26 -7
  151. package/src/managers/pluginManager.ts +37 -33
  152. package/src/managers/reversionManager.ts +5 -3
  153. package/src/managers/skillManager.ts +19 -20
  154. package/src/managers/slashCommandManager.ts +30 -25
  155. package/src/managers/subagentManager.ts +53 -53
  156. package/src/managers/toolManager.ts +93 -90
  157. package/src/prompts/index.ts +13 -5
  158. package/src/services/MarketplaceService.ts +13 -0
  159. package/src/services/aiService.ts +61 -15
  160. package/src/services/configurationService.ts +34 -13
  161. package/src/services/taskManager.ts +6 -2
  162. package/src/tools/bashTool.ts +19 -4
  163. package/src/tools/editTool.ts +17 -4
  164. package/src/tools/readTool.ts +2 -2
  165. package/src/tools/skillTool.ts +75 -71
  166. package/src/tools/taskOutputTool.ts +3 -2
  167. package/src/tools/taskTool.ts +224 -225
  168. package/src/tools/types.ts +14 -1
  169. package/src/tools/writeTool.ts +5 -2
  170. package/src/types/core.ts +2 -0
  171. package/src/types/marketplace.ts +9 -0
  172. package/src/types/messaging.ts +1 -0
  173. package/src/types/permissions.ts +2 -4
  174. package/src/types/skills.ts +0 -3
  175. package/src/types/tools.ts +0 -17
  176. package/src/utils/container.ts +92 -0
  177. package/src/utils/containerSetup.ts +279 -0
  178. package/src/utils/editUtils.ts +4 -3
  179. package/src/utils/hookMatcher.ts +2 -2
  180. package/src/utils/messageOperations.ts +5 -0
  181. package/src/utils/openaiClient.ts +2 -2
  182. package/src/utils/stringUtils.ts +9 -0
  183. package/dist/tools/deleteFileTool.d.ts +0 -6
  184. package/dist/tools/deleteFileTool.d.ts.map +0 -1
  185. package/dist/tools/deleteFileTool.js +0 -100
  186. package/dist/tools/multiEditTool.d.ts +0 -6
  187. package/dist/tools/multiEditTool.d.ts.map +0 -1
  188. package/dist/tools/multiEditTool.js +0 -246
  189. package/src/tools/deleteFileTool.ts +0 -127
  190. 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,55 @@ 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
167
+ const canUseToolCallback = this.container.has("CanUseToolCallback")
168
+ ? this.container.get<PermissionCallback>("CanUseToolCallback")
169
+ : undefined;
170
+
187
171
  const enhancedContext: ToolContext = {
188
172
  ...context,
189
173
  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,
174
+ canUseToolCallback,
175
+ permissionManager,
176
+ taskManager:
177
+ this.container.get<import("../services/taskManager.js").TaskManager>(
178
+ "TaskManager",
179
+ )!,
180
+ reversionManager:
181
+ this.container.get<ReversionManager>("ReversionManager")!,
182
+ backgroundTaskManager: this.container.get<
183
+ import("./backgroundTaskManager.js").BackgroundTaskManager
184
+ >("BackgroundTaskManager")!,
185
+ foregroundTaskManager: this.container.get<
186
+ import("../types/processes.js").IForegroundTaskManager
187
+ >("ForegroundTaskManager")!,
196
188
  mcpManager: this.mcpManager,
197
- lspManager: this.lspManager,
189
+ lspManager: this.container.get<ILspManager>("LspManager")!,
190
+ subagentManager: this.container.has("SubagentManager")
191
+ ? this.container.get<SubagentManager>("SubagentManager")
192
+ : undefined,
193
+ skillManager: this.container.has("SkillManager")
194
+ ? this.container.get<SkillManager>("SkillManager")
195
+ : undefined,
198
196
  sessionId: context.sessionId,
199
197
  };
200
198
 
201
- this.logger?.debug("Executing tool with enhanced context", {
199
+ logger?.debug("Executing tool with enhanced context", {
202
200
  toolName: name,
203
- cliPermissionMode: this.permissionMode,
201
+ permissionMode,
204
202
  effectivePermissionMode,
205
- hasPermissionManager: !!this.permissionManager,
206
- hasPermissionCallback: !!this.canUseToolCallback,
203
+ hasPermissionManager: !!permissionManager,
207
204
  });
208
205
 
209
206
  // Check if it's an MCP tool first
@@ -216,12 +213,12 @@ class ToolManager {
216
213
  }
217
214
 
218
215
  // Check built-in tools
219
- const plugin = this.tools.get(name);
216
+ const plugin = this.toolsRegistry.get(name);
220
217
  if (plugin) {
221
218
  try {
222
219
  return await plugin.execute(args, enhancedContext);
223
220
  } catch (error) {
224
- this.logger?.error("Tool execution failed", {
221
+ logger?.error("Tool execution failed", {
225
222
  toolName: name,
226
223
  error: error instanceof Error ? error.message : String(error),
227
224
  });
@@ -233,7 +230,7 @@ class ToolManager {
233
230
  }
234
231
  }
235
232
 
236
- this.logger?.warn("Tool not found", { toolName: name });
233
+ logger?.warn("Tool not found", { toolName: name });
237
234
  return {
238
235
  success: false,
239
236
  content: "",
@@ -242,14 +239,14 @@ class ToolManager {
242
239
  }
243
240
 
244
241
  list(): ToolPlugin[] {
245
- const builtInTools = Array.from(this.tools.values());
242
+ const builtInTools = Array.from(this.toolsRegistry.values());
246
243
  const mcpTools = this.mcpManager.getMcpToolPlugins();
247
244
  return [...builtInTools, ...mcpTools];
248
245
  }
249
246
 
250
247
  getToolsConfig(): ChatCompletionFunctionTool[] {
251
248
  const effectivePermissionMode = this.getPermissionMode();
252
- const builtInToolsConfig = Array.from(this.tools.values())
249
+ const builtInToolsConfig = Array.from(this.toolsRegistry.values())
253
250
  .filter((tool) => {
254
251
  if (effectivePermissionMode === "bypassPermissions") {
255
252
  if (tool.name === "ExitPlanMode" || tool.name === "AskUserQuestion") {
@@ -270,19 +267,23 @@ class ToolManager {
270
267
  * Get the list of registered tool plugins
271
268
  */
272
269
  public getTools(): ToolPlugin[] {
273
- return Array.from(this.tools.values());
270
+ return Array.from(this.toolsRegistry.values());
274
271
  }
275
272
 
276
273
  /**
277
274
  * Get the current permission mode
278
275
  */
279
276
  public getPermissionMode(): PermissionMode {
280
- if (this.permissionManager) {
281
- return this.permissionManager.getCurrentEffectiveMode(
282
- this.permissionMode,
283
- );
277
+ const permissionManager =
278
+ this.container.get<PermissionManager>("PermissionManager");
279
+ const permissionMode = this.container.has("PermissionMode")
280
+ ? this.container.get<PermissionMode>("PermissionMode")
281
+ : undefined;
282
+
283
+ if (permissionManager) {
284
+ return permissionManager.getCurrentEffectiveMode(permissionMode);
284
285
  }
285
- return this.permissionMode || "default";
286
+ return permissionMode || "default";
286
287
  }
287
288
 
288
289
  /**
@@ -290,14 +291,14 @@ class ToolManager {
290
291
  * @param mode - The new permission mode
291
292
  */
292
293
  public setPermissionMode(mode: PermissionMode): void {
293
- this.permissionMode = mode;
294
+ this.container.register("PermissionMode", mode);
294
295
  }
295
296
 
296
297
  /**
297
298
  * Get the permission manager
298
299
  */
299
300
  public getPermissionManager(): PermissionManager | undefined {
300
- return this.permissionManager;
301
+ return this.container.get<PermissionManager>("PermissionManager");
301
302
  }
302
303
 
303
304
  /**
@@ -306,7 +307,9 @@ class ToolManager {
306
307
  public getTaskManager():
307
308
  | import("../services/taskManager.js").TaskManager
308
309
  | undefined {
309
- return this.taskManager;
310
+ return this.container.get<import("../services/taskManager.js").TaskManager>(
311
+ "TaskManager",
312
+ );
310
313
  }
311
314
  }
312
315
 
@@ -161,7 +161,6 @@ export const EXPLORE_SUBAGENT_SYSTEM_PROMPT = `You are a file search specialist.
161
161
  This is a READ-ONLY exploration task. You are STRICTLY PROHIBITED from:
162
162
  - Creating new files (no Write, touch, or file creation of any kind)
163
163
  - Modifying existing files (no Edit operations)
164
- - Deleting files (no rm or deletion)
165
164
  - Moving or copying files (no mv or cp)
166
165
  - Creating temporary files anywhere, including /tmp
167
166
  - Using redirect operators (>, >>, |) or heredocs to write to files
@@ -199,7 +198,6 @@ export const PLAN_SUBAGENT_SYSTEM_PROMPT = `You are a software architect and pla
199
198
  This is a READ-ONLY planning task. You are STRICTLY PROHIBITED from:
200
199
  - Creating new files (no Write, touch, or file creation of any kind)
201
200
  - Modifying existing files (no Edit operations)
202
- - Deleting files (no rm or deletion)
203
201
  - Moving or copying files (no mv or cp)
204
202
  - Creating temporary files anywhere, including /tmp
205
203
  - Using redirect operators (>, >>, |) or heredocs to write to files
@@ -290,6 +288,9 @@ Any promises made to the user
290
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.
291
289
  Wrap your summary in <summary></summary> tags.`;
292
290
 
291
+ import type { SubagentConfiguration } from "../utils/subagentParser.js";
292
+ import type { SkillMetadata } from "../types/skills.js";
293
+
293
294
  export function buildSystemPrompt(
294
295
  basePrompt: string | undefined,
295
296
  tools: ToolPlugin[],
@@ -302,15 +303,22 @@ export function buildSystemPrompt(
302
303
  planFilePath: string;
303
304
  planExists: boolean;
304
305
  };
306
+ availableSubagents?: SubagentConfiguration[];
307
+ availableSkills?: SkillMetadata[];
305
308
  } = {},
306
309
  ): string {
307
310
  let prompt = basePrompt || DEFAULT_SYSTEM_PROMPT;
308
-
309
- prompt += `\n\n${TOOL_POLICY}`;
311
+ if (tools.length > 0) {
312
+ prompt += `\n\n${TOOL_POLICY}`;
313
+ }
310
314
 
311
315
  for (const tool of tools) {
312
316
  if (tool.prompt) {
313
- prompt += tool.prompt();
317
+ prompt += tool.prompt({
318
+ availableSubagents: options.availableSubagents,
319
+ availableSkills: options.availableSkills,
320
+ workdir: options.workdir,
321
+ });
314
322
  }
315
323
  }
316
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");
@@ -43,7 +47,7 @@ export class TaskManager extends EventEmitter {
43
47
  const lockPath = this.getLockPath();
44
48
  let lockHandle;
45
49
  const maxRetries = 100;
46
- const retryDelay = process.env.NODE_ENV === "test" ? 10 : 100;
50
+ const retryDelay = process.env.NODE_ENV === "test" ? 1 : 100;
47
51
 
48
52
  await this.ensureSessionDir();
49
53
 
@@ -197,9 +197,24 @@ Usage notes:
197
197
 
198
198
  let outputBuffer = "";
199
199
  let errorBuffer = "";
200
+ let shortResultBuffer = "";
200
201
  let isAborted = false;
201
202
  let isBackgrounded = false;
202
203
 
204
+ const updateShortResult = (chunk: string) => {
205
+ if (!chunk) return;
206
+ shortResultBuffer += chunk;
207
+ if (shortResultBuffer.length > MAX_OUTPUT_LENGTH) {
208
+ shortResultBuffer = shortResultBuffer.slice(-MAX_OUTPUT_LENGTH);
209
+ }
210
+ const lastLines = shortResultBuffer
211
+ .trim()
212
+ .split("\n")
213
+ .slice(-3)
214
+ .join("\n");
215
+ context.onShortResultUpdate?.(lastLines || "");
216
+ };
217
+
203
218
  const foregroundTaskId = `bash_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
204
219
 
205
220
  // Register as foreground task
@@ -306,18 +321,18 @@ Usage notes:
306
321
  }
307
322
 
308
323
  child.stdout?.on("data", (data) => {
309
- if (!isAborted && !isBackgrounded) {
324
+ if (!isAborted && !isBackgrounded && !runInBackground) {
310
325
  const chunk = stripAnsiColors(data.toString());
311
326
  outputBuffer += chunk;
312
- context.onShortResultUpdate?.(chunk.trim().split("\n").pop() || "");
327
+ updateShortResult(chunk);
313
328
  }
314
329
  });
315
330
 
316
331
  child.stderr?.on("data", (data) => {
317
- if (!isAborted && !isBackgrounded) {
332
+ if (!isAborted && !isBackgrounded && !runInBackground) {
318
333
  const chunk = stripAnsiColors(data.toString());
319
334
  errorBuffer += chunk;
320
- context.onShortResultUpdate?.(chunk.trim().split("\n").pop() || "");
335
+ updateShortResult(chunk);
321
336
  }
322
337
  });
323
338