wave-agent-sdk 0.0.8 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/dist/agent.d.ts +92 -23
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +340 -137
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +2 -0
  7. package/dist/managers/aiManager.d.ts +14 -36
  8. package/dist/managers/aiManager.d.ts.map +1 -1
  9. package/dist/managers/aiManager.js +74 -77
  10. package/dist/managers/backgroundBashManager.d.ts.map +1 -1
  11. package/dist/managers/backgroundBashManager.js +4 -3
  12. package/dist/managers/hookManager.d.ts +3 -8
  13. package/dist/managers/hookManager.d.ts.map +1 -1
  14. package/dist/managers/hookManager.js +39 -29
  15. package/dist/managers/liveConfigManager.d.ts +55 -18
  16. package/dist/managers/liveConfigManager.d.ts.map +1 -1
  17. package/dist/managers/liveConfigManager.js +372 -90
  18. package/dist/managers/lspManager.d.ts +43 -0
  19. package/dist/managers/lspManager.d.ts.map +1 -0
  20. package/dist/managers/lspManager.js +326 -0
  21. package/dist/managers/messageManager.d.ts +8 -16
  22. package/dist/managers/messageManager.d.ts.map +1 -1
  23. package/dist/managers/messageManager.js +52 -74
  24. package/dist/managers/permissionManager.d.ts +66 -0
  25. package/dist/managers/permissionManager.d.ts.map +1 -0
  26. package/dist/managers/permissionManager.js +208 -0
  27. package/dist/managers/skillManager.d.ts +1 -0
  28. package/dist/managers/skillManager.d.ts.map +1 -1
  29. package/dist/managers/skillManager.js +2 -1
  30. package/dist/managers/slashCommandManager.d.ts.map +1 -1
  31. package/dist/managers/slashCommandManager.js +0 -1
  32. package/dist/managers/subagentManager.d.ts +8 -23
  33. package/dist/managers/subagentManager.d.ts.map +1 -1
  34. package/dist/managers/subagentManager.js +97 -117
  35. package/dist/managers/toolManager.d.ts +38 -1
  36. package/dist/managers/toolManager.d.ts.map +1 -1
  37. package/dist/managers/toolManager.js +66 -2
  38. package/dist/services/aiService.d.ts +3 -1
  39. package/dist/services/aiService.d.ts.map +1 -1
  40. package/dist/services/aiService.js +123 -30
  41. package/dist/services/configurationService.d.ts +116 -0
  42. package/dist/services/configurationService.d.ts.map +1 -0
  43. package/dist/services/configurationService.js +585 -0
  44. package/dist/services/fileWatcher.d.ts.map +1 -1
  45. package/dist/services/fileWatcher.js +5 -6
  46. package/dist/services/hook.d.ts +7 -124
  47. package/dist/services/hook.d.ts.map +1 -1
  48. package/dist/services/hook.js +46 -458
  49. package/dist/services/jsonlHandler.d.ts +24 -15
  50. package/dist/services/jsonlHandler.d.ts.map +1 -1
  51. package/dist/services/jsonlHandler.js +67 -88
  52. package/dist/services/memory.d.ts +0 -9
  53. package/dist/services/memory.d.ts.map +1 -1
  54. package/dist/services/memory.js +2 -49
  55. package/dist/services/session.d.ts +82 -33
  56. package/dist/services/session.d.ts.map +1 -1
  57. package/dist/services/session.js +275 -181
  58. package/dist/tools/bashTool.d.ts.map +1 -1
  59. package/dist/tools/bashTool.js +72 -13
  60. package/dist/tools/deleteFileTool.d.ts.map +1 -1
  61. package/dist/tools/deleteFileTool.js +25 -0
  62. package/dist/tools/editTool.d.ts.map +1 -1
  63. package/dist/tools/editTool.js +30 -6
  64. package/dist/tools/lspTool.d.ts +6 -0
  65. package/dist/tools/lspTool.d.ts.map +1 -0
  66. package/dist/tools/lspTool.js +589 -0
  67. package/dist/tools/multiEditTool.d.ts.map +1 -1
  68. package/dist/tools/multiEditTool.js +26 -7
  69. package/dist/tools/readTool.d.ts.map +1 -1
  70. package/dist/tools/readTool.js +111 -2
  71. package/dist/tools/skillTool.js +2 -2
  72. package/dist/tools/todoWriteTool.d.ts.map +1 -1
  73. package/dist/tools/todoWriteTool.js +23 -0
  74. package/dist/tools/types.d.ts +11 -8
  75. package/dist/tools/types.d.ts.map +1 -1
  76. package/dist/tools/writeTool.d.ts.map +1 -1
  77. package/dist/tools/writeTool.js +25 -9
  78. package/dist/types/commands.d.ts +0 -1
  79. package/dist/types/commands.d.ts.map +1 -1
  80. package/dist/types/config.d.ts +4 -0
  81. package/dist/types/config.d.ts.map +1 -1
  82. package/dist/types/configuration.d.ts +69 -0
  83. package/dist/types/configuration.d.ts.map +1 -0
  84. package/dist/types/configuration.js +8 -0
  85. package/dist/types/core.d.ts +10 -0
  86. package/dist/types/core.d.ts.map +1 -1
  87. package/dist/types/environment.d.ts +41 -0
  88. package/dist/types/environment.d.ts.map +1 -1
  89. package/dist/types/fileSearch.d.ts +5 -0
  90. package/dist/types/fileSearch.d.ts.map +1 -0
  91. package/dist/types/fileSearch.js +1 -0
  92. package/dist/types/hooks.d.ts +11 -2
  93. package/dist/types/hooks.d.ts.map +1 -1
  94. package/dist/types/hooks.js +1 -7
  95. package/dist/types/index.d.ts +5 -0
  96. package/dist/types/index.d.ts.map +1 -1
  97. package/dist/types/index.js +5 -0
  98. package/dist/types/lsp.d.ts +90 -0
  99. package/dist/types/lsp.d.ts.map +1 -0
  100. package/dist/types/lsp.js +4 -0
  101. package/dist/types/messaging.d.ts +6 -11
  102. package/dist/types/messaging.d.ts.map +1 -1
  103. package/dist/types/permissions.d.ts +35 -0
  104. package/dist/types/permissions.d.ts.map +1 -0
  105. package/dist/types/permissions.js +12 -0
  106. package/dist/types/session.d.ts +1 -6
  107. package/dist/types/session.d.ts.map +1 -1
  108. package/dist/types/skills.d.ts +1 -0
  109. package/dist/types/skills.d.ts.map +1 -1
  110. package/dist/types/tools.d.ts +35 -0
  111. package/dist/types/tools.d.ts.map +1 -0
  112. package/dist/types/tools.js +4 -0
  113. package/dist/utils/abortUtils.d.ts +34 -0
  114. package/dist/utils/abortUtils.d.ts.map +1 -0
  115. package/dist/utils/abortUtils.js +92 -0
  116. package/dist/utils/bashHistory.d.ts +4 -0
  117. package/dist/utils/bashHistory.d.ts.map +1 -1
  118. package/dist/utils/bashHistory.js +21 -4
  119. package/dist/utils/builtinSubagents.d.ts +7 -0
  120. package/dist/utils/builtinSubagents.d.ts.map +1 -0
  121. package/dist/utils/builtinSubagents.js +65 -0
  122. package/dist/utils/cacheControlUtils.d.ts +8 -33
  123. package/dist/utils/cacheControlUtils.d.ts.map +1 -1
  124. package/dist/utils/cacheControlUtils.js +83 -126
  125. package/dist/utils/constants.d.ts +0 -12
  126. package/dist/utils/constants.d.ts.map +1 -1
  127. package/dist/utils/constants.js +1 -13
  128. package/dist/utils/convertMessagesForAPI.d.ts +2 -1
  129. package/dist/utils/convertMessagesForAPI.d.ts.map +1 -1
  130. package/dist/utils/convertMessagesForAPI.js +33 -14
  131. package/dist/utils/fileSearch.d.ts +14 -0
  132. package/dist/utils/fileSearch.d.ts.map +1 -0
  133. package/dist/utils/fileSearch.js +88 -0
  134. package/dist/utils/fileUtils.d.ts +14 -2
  135. package/dist/utils/fileUtils.d.ts.map +1 -1
  136. package/dist/utils/fileUtils.js +101 -17
  137. package/dist/utils/globalLogger.d.ts +0 -14
  138. package/dist/utils/globalLogger.d.ts.map +1 -1
  139. package/dist/utils/globalLogger.js +0 -16
  140. package/dist/utils/largeOutputHandler.d.ts +15 -0
  141. package/dist/utils/largeOutputHandler.d.ts.map +1 -0
  142. package/dist/utils/largeOutputHandler.js +40 -0
  143. package/dist/utils/markdownParser.d.ts.map +1 -1
  144. package/dist/utils/markdownParser.js +1 -17
  145. package/dist/utils/messageOperations.d.ts +1 -11
  146. package/dist/utils/messageOperations.d.ts.map +1 -1
  147. package/dist/utils/messageOperations.js +7 -24
  148. package/dist/utils/pathEncoder.d.ts +4 -0
  149. package/dist/utils/pathEncoder.d.ts.map +1 -1
  150. package/dist/utils/pathEncoder.js +16 -9
  151. package/dist/utils/subagentParser.d.ts +2 -2
  152. package/dist/utils/subagentParser.d.ts.map +1 -1
  153. package/dist/utils/subagentParser.js +10 -7
  154. package/dist/utils/tokenEstimator.d.ts +39 -0
  155. package/dist/utils/tokenEstimator.d.ts.map +1 -0
  156. package/dist/utils/tokenEstimator.js +55 -0
  157. package/package.json +5 -8
  158. package/src/agent.ts +460 -216
  159. package/src/index.ts +2 -0
  160. package/src/managers/aiManager.ts +107 -111
  161. package/src/managers/backgroundBashManager.ts +4 -3
  162. package/src/managers/hookManager.ts +44 -39
  163. package/src/managers/liveConfigManager.ts +524 -138
  164. package/src/managers/lspManager.ts +434 -0
  165. package/src/managers/messageManager.ts +73 -103
  166. package/src/managers/permissionManager.ts +276 -0
  167. package/src/managers/skillManager.ts +3 -1
  168. package/src/managers/slashCommandManager.ts +1 -2
  169. package/src/managers/subagentManager.ts +116 -159
  170. package/src/managers/toolManager.ts +95 -3
  171. package/src/services/aiService.ts +207 -26
  172. package/src/services/configurationService.ts +762 -0
  173. package/src/services/fileWatcher.ts +5 -6
  174. package/src/services/hook.ts +50 -631
  175. package/src/services/jsonlHandler.ts +84 -100
  176. package/src/services/memory.ts +2 -59
  177. package/src/services/session.ts +338 -213
  178. package/src/tools/bashTool.ts +89 -16
  179. package/src/tools/deleteFileTool.ts +36 -0
  180. package/src/tools/editTool.ts +41 -7
  181. package/src/tools/lspTool.ts +760 -0
  182. package/src/tools/multiEditTool.ts +37 -8
  183. package/src/tools/readTool.ts +125 -2
  184. package/src/tools/skillTool.ts +2 -2
  185. package/src/tools/todoWriteTool.ts +33 -1
  186. package/src/tools/types.ts +15 -9
  187. package/src/tools/writeTool.ts +36 -10
  188. package/src/types/commands.ts +0 -1
  189. package/src/types/config.ts +5 -0
  190. package/src/types/configuration.ts +73 -0
  191. package/src/types/core.ts +11 -0
  192. package/src/types/environment.ts +44 -0
  193. package/src/types/fileSearch.ts +4 -0
  194. package/src/types/hooks.ts +14 -11
  195. package/src/types/index.ts +5 -0
  196. package/src/types/lsp.ts +96 -0
  197. package/src/types/messaging.ts +8 -13
  198. package/src/types/permissions.ts +48 -0
  199. package/src/types/session.ts +3 -8
  200. package/src/types/skills.ts +1 -0
  201. package/src/types/tools.ts +38 -0
  202. package/src/utils/abortUtils.ts +118 -0
  203. package/src/utils/bashHistory.ts +28 -4
  204. package/src/utils/builtinSubagents.ts +71 -0
  205. package/src/utils/cacheControlUtils.ts +106 -171
  206. package/src/utils/constants.ts +1 -16
  207. package/src/utils/convertMessagesForAPI.ts +38 -14
  208. package/src/utils/fileSearch.ts +107 -0
  209. package/src/utils/fileUtils.ts +114 -19
  210. package/src/utils/globalLogger.ts +0 -17
  211. package/src/utils/largeOutputHandler.ts +55 -0
  212. package/src/utils/markdownParser.ts +1 -19
  213. package/src/utils/messageOperations.ts +7 -35
  214. package/src/utils/pathEncoder.ts +24 -9
  215. package/src/utils/subagentParser.ts +11 -8
  216. package/src/utils/tokenEstimator.ts +68 -0
  217. package/dist/constants/events.d.ts +0 -28
  218. package/dist/constants/events.d.ts.map +0 -1
  219. package/dist/constants/events.js +0 -27
  220. package/dist/services/configurationWatcher.d.ts +0 -120
  221. package/dist/services/configurationWatcher.d.ts.map +0 -1
  222. package/dist/services/configurationWatcher.js +0 -439
  223. package/dist/services/memoryStore.d.ts +0 -81
  224. package/dist/services/memoryStore.d.ts.map +0 -1
  225. package/dist/services/memoryStore.js +0 -200
  226. package/dist/types/memoryStore.d.ts +0 -82
  227. package/dist/types/memoryStore.d.ts.map +0 -1
  228. package/dist/types/memoryStore.js +0 -7
  229. package/dist/utils/configResolver.d.ts +0 -65
  230. package/dist/utils/configResolver.d.ts.map +0 -1
  231. package/dist/utils/configResolver.js +0 -210
  232. package/src/constants/events.ts +0 -38
  233. package/src/services/configurationWatcher.ts +0 -622
  234. package/src/services/memoryStore.ts +0 -279
  235. package/src/types/memoryStore.ts +0 -94
  236. package/src/utils/configResolver.ts +0 -302
package/dist/agent.js CHANGED
@@ -4,19 +4,58 @@ import { ToolManager } from "./managers/toolManager.js";
4
4
  import { SubagentManager, } from "./managers/subagentManager.js";
5
5
  import * as memory from "./services/memory.js";
6
6
  import { McpManager } from "./managers/mcpManager.js";
7
+ import { LspManager } from "./managers/lspManager.js";
7
8
  import { BashManager } from "./managers/bashManager.js";
8
9
  import { BackgroundBashManager, } from "./managers/backgroundBashManager.js";
9
10
  import { SlashCommandManager } from "./managers/slashCommandManager.js";
11
+ import { PermissionManager } from "./managers/permissionManager.js";
10
12
  import { HookManager } from "./managers/hookManager.js";
11
- import { MemoryStoreService } from "./services/memoryStore.js";
12
- import { initializeMemoryStore } from "./services/memory.js";
13
13
  import { LiveConfigManager } from "./managers/liveConfigManager.js";
14
- import { configResolver } from "./utils/configResolver.js";
15
14
  import { configValidator } from "./utils/configValidator.js";
16
15
  import { SkillManager } from "./managers/skillManager.js";
17
- import { loadSessionFromJsonl } from "./services/session.js";
16
+ import { loadSessionFromJsonl, handleSessionRestoration, } from "./services/session.js";
18
17
  import { setGlobalLogger } from "./utils/globalLogger.js";
18
+ import { ConfigurationService } from "./services/configurationService.js";
19
+ import * as fs from "fs/promises";
20
+ import path from "path";
21
+ import os from "os";
19
22
  export class Agent {
23
+ // Dynamic configuration getter methods
24
+ getGatewayConfig() {
25
+ return this.configurationService.resolveGatewayConfig(this.options.apiKey, this.options.baseURL, this.options.defaultHeaders, this.options.fetchOptions, this.options.fetch);
26
+ }
27
+ getModelConfig() {
28
+ return this.configurationService.resolveModelConfig(this.options.agentModel, this.options.fastModel);
29
+ }
30
+ getTokenLimit() {
31
+ return this.configurationService.resolveTokenLimit(this.options.tokenLimit);
32
+ }
33
+ /**
34
+ * Update agent configuration dynamically
35
+ *
36
+ * @param config - Configuration updates for gateway, model, and token limit
37
+ */
38
+ updateConfig(config) {
39
+ if (config.gateway) {
40
+ this.options.apiKey = config.gateway.apiKey ?? this.options.apiKey;
41
+ this.options.baseURL = config.gateway.baseURL ?? this.options.baseURL;
42
+ this.options.defaultHeaders =
43
+ config.gateway.defaultHeaders ?? this.options.defaultHeaders;
44
+ this.options.fetchOptions =
45
+ config.gateway.fetchOptions ?? this.options.fetchOptions;
46
+ this.options.fetch = config.gateway.fetch ?? this.options.fetch;
47
+ }
48
+ if (config.model) {
49
+ this.options.agentModel =
50
+ config.model.agentModel ?? this.options.agentModel;
51
+ this.options.fastModel = config.model.fastModel ?? this.options.fastModel;
52
+ }
53
+ if (config.tokenLimit !== undefined) {
54
+ this.options.tokenLimit = config.tokenLimit;
55
+ }
56
+ // Re-validate configuration after update
57
+ this.resolveAndValidateConfig();
58
+ }
20
59
  /**
21
60
  * Agent constructor - handles configuration resolution and validation
22
61
  *
@@ -29,82 +68,92 @@ export class Agent {
29
68
  constructor(options) {
30
69
  this.bashManager = null;
31
70
  this._usages = []; // Usage tracking array
32
- const { callbacks = {}, logger, workdir, systemPrompt } = options;
33
- // Set working directory first so it can be used for configuration resolution
71
+ // Memory content storage
72
+ this._projectMemoryContent = "";
73
+ this._userMemoryContent = "";
74
+ const { callbacks = {}, logger, workdir, systemPrompt, stream = true, } = options;
75
+ // Set working directory early as we need it for loading configuration
34
76
  this.workdir = workdir || process.cwd();
35
- // Store original constructor values for live config updates
36
- this.constructorApiKey = options.apiKey;
37
- this.constructorBaseURL = options.baseURL;
38
- this.constructorAgentModel = options.agentModel;
39
- this.constructorFastModel = options.fastModel;
40
- this.constructorTokenLimit = options.tokenLimit;
41
- // Resolve configuration from constructor args and environment variables with live config support
42
- const gatewayConfig = configResolver.resolveGatewayConfig(options.apiKey, options.baseURL, this.workdir);
43
- const modelConfig = configResolver.resolveModelConfig(options.agentModel, options.fastModel, this.workdir);
44
- const tokenLimit = configResolver.resolveTokenLimit(options.tokenLimit, this.workdir);
45
- // Validate resolved configuration
46
- configValidator.validateGatewayConfig(gatewayConfig);
47
- configValidator.validateTokenLimit(tokenLimit);
48
- configValidator.validateModelConfig(modelConfig.agentModel, modelConfig.fastModel);
77
+ // Initialize configuration service
78
+ this.configurationService = new ConfigurationService();
49
79
  this.logger = logger; // Save the passed logger
50
80
  this.systemPrompt = systemPrompt; // Save custom system prompt
51
- // Set global logger for SDK-wide access
52
- setGlobalLogger(logger || null);
53
- // Store resolved configuration
54
- this.gatewayConfig = gatewayConfig;
55
- this.modelConfig = modelConfig;
56
- this.tokenLimit = tokenLimit;
81
+ this.stream = stream; // Save streaming mode flag
82
+ // Store options for dynamic configuration resolution
83
+ this.options = options;
57
84
  this.backgroundBashManager = new BackgroundBashManager({
58
85
  callbacks,
59
86
  workdir: this.workdir,
60
87
  });
61
88
  this.mcpManager = new McpManager({ callbacks, logger: this.logger }); // Initialize MCP manager
62
- this.toolManager = new ToolManager({
63
- mcpManager: this.mcpManager,
64
- logger: this.logger,
65
- }); // Initialize tool registry, pass MCP manager
66
- this.memoryStore = new MemoryStoreService(this.logger); // Initialize memory store service
67
- initializeMemoryStore(this.memoryStore); // Initialize global memory store reference
89
+ this.lspManager =
90
+ options.lspManager || new LspManager({ logger: this.logger }); // Initialize LSP manager
91
+ // Initialize permission manager
92
+ this.permissionManager = new PermissionManager({ logger: this.logger });
93
+ this.permissionManager.setOnConfiguredDefaultModeChange((mode) => {
94
+ this.options.callbacks?.onPermissionModeChange?.(mode);
95
+ });
96
+ // Initialize configuration service and hooks manager
68
97
  this.hookManager = new HookManager(this.workdir, undefined, this.logger); // Initialize hooks manager
69
- this.liveConfigManager = new LiveConfigManager({
98
+ // Initialize MessageManager
99
+ this.messageManager = new MessageManager({
100
+ callbacks,
70
101
  workdir: this.workdir,
71
102
  logger: this.logger,
72
- onConfigurationChanged: () => {
73
- // Update Agent configuration (AIManager, SubagentManager)
74
- this.updateConfiguration();
75
- // Reload hook configuration
103
+ });
104
+ // Create a wrapper for canUseTool that triggers notification hooks
105
+ const canUseToolWithNotification = options.canUseTool
106
+ ? async (context) => {
76
107
  try {
77
- this.hookManager.loadConfigurationFromSettings();
78
- this.logger?.info("Live Config: Hook configuration reloaded successfully");
108
+ // Trigger notification hooks before calling the original callback
109
+ const notificationMessage = `Claude needs your permission to use ${context.toolName}`;
110
+ await this.hookManager.executeHooks("Notification", {
111
+ event: "Notification",
112
+ projectDir: this.workdir,
113
+ timestamp: new Date(),
114
+ sessionId: this.sessionId,
115
+ transcriptPath: this.messageManager.getTranscriptPath(),
116
+ cwd: this.workdir,
117
+ message: notificationMessage,
118
+ notificationType: "permission_prompt",
119
+ env: this.configurationService.getEnvironmentVars(), // Include configuration environment variables
120
+ });
79
121
  }
80
122
  catch (error) {
81
- this.logger?.error(`Live Config: Failed to reload hook configuration: ${error.message}`);
123
+ this.logger?.warn("Failed to execute notification hooks", {
124
+ toolName: context.toolName,
125
+ error: error instanceof Error ? error.message : String(error),
126
+ });
127
+ // Continue with permission check even if hooks fail
82
128
  }
83
- },
84
- onMemoryStoreFileChanged: async (filePath, changeType) => {
85
- try {
86
- if (changeType === "unlink") {
87
- // Handle file deletion gracefully
88
- this.memoryStore.removeContent(filePath);
89
- this.logger?.info("Live Config: Removed AGENTS.md from memory store due to file deletion");
90
- }
91
- else {
92
- // Update memory store content for add/change
93
- await this.memoryStore.updateContent(filePath);
94
- this.logger?.info("Live Config: Updated AGENTS.md content in memory store");
95
- }
129
+ // Call the original callback
130
+ const decision = await options.canUseTool(context);
131
+ // Handle state changes from decision
132
+ if (decision.newPermissionMode) {
133
+ this.setPermissionMode(decision.newPermissionMode);
96
134
  }
97
- catch (error) {
98
- this.logger?.error(`Live Config: Failed to update memory store: ${error.message}`);
135
+ if (decision.newPermissionRule) {
136
+ await this.addPermissionRule(decision.newPermissionRule);
99
137
  }
100
- },
101
- }); // Initialize live configuration manager
102
- // Initialize MessageManager
103
- this.messageManager = new MessageManager({
104
- callbacks,
138
+ return decision;
139
+ }
140
+ : undefined;
141
+ // Initialize tool manager with permission context
142
+ this.toolManager = new ToolManager({
143
+ mcpManager: this.mcpManager,
144
+ lspManager: this.lspManager,
145
+ logger: this.logger,
146
+ permissionManager: this.permissionManager,
147
+ permissionMode: options.permissionMode, // Let PermissionManager handle defaultMode resolution
148
+ canUseToolCallback: canUseToolWithNotification,
149
+ }); // Initialize tool registry with permission support
150
+ this.liveConfigManager = new LiveConfigManager({
105
151
  workdir: this.workdir,
106
152
  logger: this.logger,
107
- });
153
+ hookManager: this.hookManager,
154
+ permissionManager: this.permissionManager,
155
+ configurationService: this.configurationService,
156
+ }); // Initialize live configuration manager
108
157
  // Initialize subagent manager with all dependencies in constructor
109
158
  // IMPORTANT: Must be initialized AFTER MessageManager
110
159
  this.subagentManager = new SubagentManager({
@@ -115,13 +164,14 @@ export class Agent {
115
164
  onSubagentUserMessageAdded: callbacks.onSubagentUserMessageAdded,
116
165
  onSubagentAssistantMessageAdded: callbacks.onSubagentAssistantMessageAdded,
117
166
  onSubagentAssistantContentUpdated: callbacks.onSubagentAssistantContentUpdated,
167
+ onSubagentAssistantReasoningUpdated: callbacks.onSubagentAssistantReasoningUpdated,
118
168
  onSubagentToolBlockUpdated: callbacks.onSubagentToolBlockUpdated,
119
169
  onSubagentMessagesChange: callbacks.onSubagentMessagesChange,
120
170
  }, // Pass subagent callbacks for forwarding
121
171
  logger: this.logger,
122
- gatewayConfig,
123
- modelConfig,
124
- tokenLimit,
172
+ getGatewayConfig: () => this.getGatewayConfig(),
173
+ getModelConfig: () => this.getModelConfig(),
174
+ getTokenLimit: () => this.getTokenLimit(),
125
175
  hookManager: this.hookManager,
126
176
  onUsageAdded: (usage) => this.addUsage(usage),
127
177
  });
@@ -140,9 +190,11 @@ export class Agent {
140
190
  },
141
191
  workdir: this.workdir,
142
192
  systemPrompt: this.systemPrompt,
143
- gatewayConfig: this.gatewayConfig,
144
- modelConfig: this.modelConfig,
145
- tokenLimit: this.tokenLimit,
193
+ stream: this.stream, // Pass streaming mode flag
194
+ getGatewayConfig: () => this.getGatewayConfig(),
195
+ getModelConfig: () => this.getModelConfig(),
196
+ getTokenLimit: () => this.getTokenLimit(),
197
+ getEnvironmentVars: () => this.configurationService.getEnvironmentVars(), // Provide access to configuration environment variables
146
198
  });
147
199
  // Initialize command manager
148
200
  this.slashCommandManager = new SlashCommandManager({
@@ -174,9 +226,9 @@ export class Agent {
174
226
  * Rebuild usage array from messages containing usage metadata
175
227
  * Called during session restoration to reconstruct usage tracking
176
228
  */
177
- rebuildUsageFromMessages() {
229
+ rebuildUsageFromMessages(messages) {
178
230
  this._usages = [];
179
- this.messages.forEach((message) => {
231
+ messages.forEach((message) => {
180
232
  if (message.role === "assistant" && message.usage) {
181
233
  this._usages.push(message.usage);
182
234
  }
@@ -202,9 +254,27 @@ export class Agent {
202
254
  get workingDirectory() {
203
255
  return this.workdir;
204
256
  }
205
- /** Get merged environment variables from Wave configuration */
206
- get environmentVars() {
207
- return this.hookManager.getEnvironmentVars();
257
+ /** Get project memory content */
258
+ get projectMemory() {
259
+ return this._projectMemoryContent;
260
+ }
261
+ /** Get user memory content */
262
+ get userMemory() {
263
+ return this._userMemoryContent;
264
+ }
265
+ /** Get combined memory content (project + user) */
266
+ get combinedMemory() {
267
+ let combined = "";
268
+ if (this._projectMemoryContent.trim()) {
269
+ combined += this._projectMemoryContent;
270
+ }
271
+ if (this._userMemoryContent.trim()) {
272
+ if (combined) {
273
+ combined += "\n\n";
274
+ }
275
+ combined += this._userMemoryContent;
276
+ }
277
+ return combined;
208
278
  }
209
279
  /** Get AI loading status */
210
280
  get isLoading() {
@@ -244,6 +314,9 @@ export class Agent {
244
314
  * @param options - Configuration options for the Agent instance
245
315
  * @param options.apiKey - API key for the AI service (or set WAVE_API_KEY env var)
246
316
  * @param options.baseURL - Base URL for the AI service (or set WAVE_BASE_URL env var)
317
+ * @param options.defaultHeaders - Optional HTTP headers to pass to the AI service
318
+ * @param options.fetchOptions - Optional fetch options to pass to the AI service
319
+ * @param options.fetch - Optional custom fetch implementation
247
320
  * @param options.callbacks - Optional callbacks for various Agent events
248
321
  * @param options.restoreSessionId - Optional session ID to restore from
249
322
  * @param options.continueLastSession - Whether to continue the last session automatically
@@ -263,7 +336,7 @@ export class Agent {
263
336
  * ```
264
337
  */
265
338
  static async create(options) {
266
- // Create Agent instance - configuration resolution and validation now happens in constructor
339
+ // Create Agent instance
267
340
  const instance = new Agent(options);
268
341
  await instance.initialize({
269
342
  restoreSessionId: options.restoreSessionId,
@@ -272,12 +345,31 @@ export class Agent {
272
345
  });
273
346
  return instance;
274
347
  }
348
+ /**
349
+ * Resolve and validate configuration from constructor args, environment variables,
350
+ * and loaded settings.json.
351
+ *
352
+ * This is called during initialization after settings.json has been loaded.
353
+ */
354
+ resolveAndValidateConfig() {
355
+ // Resolve configuration from constructor args and environment variables (including settings.json)
356
+ const gatewayConfig = this.getGatewayConfig();
357
+ const modelConfig = this.getModelConfig();
358
+ const tokenLimit = this.getTokenLimit();
359
+ // Validate resolved configuration
360
+ configValidator.validateGatewayConfig(gatewayConfig);
361
+ configValidator.validateTokenLimit(tokenLimit);
362
+ configValidator.validateModelConfig(modelConfig.agentModel, modelConfig.fastModel);
363
+ }
275
364
  /** Private initialization method, handles async initialization logic */
276
365
  async initialize(options) {
277
366
  // Initialize managers first
278
367
  try {
279
368
  // Initialize SkillManager
280
- const skillManager = new SkillManager({ logger: this.logger });
369
+ const skillManager = new SkillManager({
370
+ logger: this.logger,
371
+ workdir: this.workdir,
372
+ });
281
373
  await skillManager.initialize();
282
374
  // Initialize SubagentManager (load and cache configurations)
283
375
  await this.subagentManager.initialize();
@@ -294,6 +386,9 @@ export class Agent {
294
386
  // Initialize MCP servers with auto-connect
295
387
  try {
296
388
  await this.mcpManager.initialize(this.workdir, true);
389
+ if (this.lspManager instanceof LspManager) {
390
+ await this.lspManager.initialize(this.workdir);
391
+ }
297
392
  }
298
393
  catch (error) {
299
394
  this.logger?.error("Failed to initialize MCP servers:", error);
@@ -301,54 +396,108 @@ export class Agent {
301
396
  }
302
397
  // Initialize hooks configuration
303
398
  try {
304
- // Load hooks configuration from user and project settings
399
+ // Load hooks configuration using ConfigurationService
305
400
  this.logger?.debug("Loading hooks configuration...");
306
- this.hookManager.loadConfigurationFromSettings();
401
+ const configResult = await this.configurationService.loadMergedConfiguration(this.workdir);
402
+ this.hookManager.loadConfigurationFromWaveConfig(configResult.configuration);
307
403
  this.logger?.debug("Hooks system initialized successfully");
308
404
  }
309
405
  catch (error) {
310
406
  this.logger?.error("Failed to initialize hooks system:", error);
311
407
  // Don't throw error to prevent app startup failure
312
408
  }
409
+ // Resolve and validate configuration after loading settings.json
410
+ this.resolveAndValidateConfig();
411
+ // Set global logger for SDK-wide access after validation
412
+ setGlobalLogger(this.logger || null);
313
413
  // Initialize live configuration reload
314
414
  try {
315
415
  this.logger?.debug("Initializing live configuration reload...");
316
416
  await this.liveConfigManager.initialize();
317
417
  this.logger?.debug("Live configuration reload initialized successfully");
418
+ // Update permission manager with configuration-based defaultMode
419
+ const currentConfig = this.liveConfigManager.getCurrentConfiguration();
420
+ if (currentConfig?.defaultMode) {
421
+ this.logger?.debug("Applying configured defaultMode to PermissionManager", {
422
+ defaultMode: currentConfig.defaultMode,
423
+ });
424
+ this.permissionManager.updateConfiguredDefaultMode(currentConfig.defaultMode);
425
+ }
426
+ // Update permission manager with configuration-based allowed rules
427
+ if (currentConfig?.permissions?.allow) {
428
+ this.logger?.debug("Applying configured allowed rules to PermissionManager", {
429
+ count: currentConfig.permissions.allow.length,
430
+ });
431
+ this.permissionManager.updateAllowedRules(currentConfig.permissions.allow);
432
+ }
318
433
  }
319
434
  catch (error) {
320
435
  this.logger?.error("Failed to initialize live configuration reload:", error);
321
436
  // Don't throw error to prevent app startup failure - continue without live reload
322
437
  }
438
+ // Load memory files during initialization
439
+ try {
440
+ this.logger?.debug("Loading memory files...");
441
+ // Load project memory from AGENTS.md (bypass memory store for direct file access)
442
+ try {
443
+ const projectMemoryPath = path.join(this.workdir, "AGENTS.md");
444
+ this._projectMemoryContent = await fs.readFile(projectMemoryPath, "utf-8");
445
+ this.logger?.debug("Project memory loaded successfully");
446
+ }
447
+ catch (error) {
448
+ this._projectMemoryContent = "";
449
+ this.logger?.debug("Project memory file not found or unreadable, using empty content:", error instanceof Error ? error.message : String(error));
450
+ }
451
+ // Load user memory (bypass memory store for direct file access)
452
+ try {
453
+ const userMemoryPath = path.join(os.homedir(), ".wave", "AGENTS.md");
454
+ this._userMemoryContent = await fs.readFile(userMemoryPath, "utf-8");
455
+ this.logger?.debug("User memory loaded successfully");
456
+ }
457
+ catch (error) {
458
+ this._userMemoryContent = "";
459
+ this.logger?.debug("User memory file not found or unreadable, using empty content:", error instanceof Error ? error.message : String(error));
460
+ }
461
+ this.logger?.debug("Memory initialization completed");
462
+ }
463
+ catch (error) {
464
+ // Ensure memory is always initialized even if loading fails
465
+ this._projectMemoryContent = "";
466
+ this._userMemoryContent = "";
467
+ this.logger?.error("Failed to load memory files:", error);
468
+ // Don't throw error to prevent app startup failure
469
+ }
323
470
  // Handle session restoration or set provided messages
324
471
  if (options?.messages) {
325
472
  // If messages are provided, use them directly (useful for testing)
326
473
  this.messageManager.setMessages(options.messages);
327
474
  // Rebuild usage array from restored messages
328
- this.rebuildUsageFromMessages();
475
+ this.rebuildUsageFromMessages(options.messages);
329
476
  }
330
477
  else {
331
478
  // Otherwise, handle session restoration
332
- await this.messageManager.handleSessionRestoration(options?.restoreSessionId, options?.continueLastSession);
479
+ const sessionToRestore = await handleSessionRestoration(options?.restoreSessionId, options?.continueLastSession, this.messageManager.getWorkdir());
333
480
  // Rebuild usage array from restored messages
334
- this.rebuildUsageFromMessages();
481
+ this.rebuildUsageFromMessages(sessionToRestore?.messages || []);
335
482
  // After main session is restored, restore any associated subagent sessions
336
- await this.restoreSubagentSessions();
483
+ await this.restoreSubagentSessions(sessionToRestore?.messages || []);
484
+ if (sessionToRestore)
485
+ this.messageManager.initializeFromSession(sessionToRestore);
337
486
  }
338
487
  }
339
488
  /**
340
489
  * Restore subagent sessions associated with the current main session
341
490
  * This method is called after the main session is restored to load any subagent sessions
342
491
  */
343
- async restoreSubagentSessions() {
492
+ async restoreSubagentSessions(messages) {
344
493
  try {
345
494
  // Only attempt to restore subagent sessions if we have messages (session was restored)
346
- if (this.messages.length === 0) {
495
+ if (messages.length === 0) {
347
496
  return;
348
497
  }
349
498
  // Extract sessionId -> subagentId mapping from SubagentBlocks
350
499
  const subagentBlockMap = new Map(); // sessionId -> { subagentId, configuration }
351
- for (const message of this.messages) {
500
+ for (const message of messages) {
352
501
  if (message.role === "assistant" && message.blocks) {
353
502
  for (const block of message.blocks) {
354
503
  if (block.type === "subagent" &&
@@ -370,7 +519,7 @@ export class Agent {
370
519
  const subagentSessions = [];
371
520
  for (const [sessionId, blockData] of subagentBlockMap) {
372
521
  try {
373
- const sessionData = await loadSessionFromJsonl(sessionId, this.messageManager.getWorkdir());
522
+ const sessionData = await loadSessionFromJsonl(sessionId, this.messageManager.getWorkdir(), "subagent");
374
523
  if (sessionData) {
375
524
  subagentSessions.push({
376
525
  sessionData,
@@ -395,6 +544,37 @@ export class Agent {
395
544
  // Don't throw error to prevent app startup failure
396
545
  }
397
546
  }
547
+ /**
548
+ * Restore a session by ID, switching to the target session without destroying the Agent instance
549
+ * @param sessionId - The ID of the session to restore
550
+ */
551
+ async restoreSession(sessionId) {
552
+ // 1. Validation
553
+ if (!sessionId || sessionId === this.sessionId) {
554
+ return; // No-op if session ID is invalid or already current
555
+ }
556
+ // 2. Auto-save current session
557
+ try {
558
+ await this.messageManager.saveSession();
559
+ }
560
+ catch (error) {
561
+ this.logger?.warn("Failed to save current session before restore:", error);
562
+ // Continue with restoration even if save fails
563
+ }
564
+ // 3. Load target session
565
+ const sessionData = await loadSessionFromJsonl(sessionId, this.messageManager.getWorkdir());
566
+ if (!sessionData) {
567
+ throw new Error(`Session not found: ${sessionId}`);
568
+ }
569
+ // 4. Clean current state
570
+ this.abortMessage(); // Abort any running operations
571
+ this.subagentManager.cleanup(); // Clean up active subagents
572
+ // 5. Rebuild usage and restore subagents (in correct order)
573
+ this.rebuildUsageFromMessages(sessionData.messages);
574
+ await this.restoreSubagentSessions(sessionData.messages);
575
+ // 6. Initialize session state last
576
+ this.messageManager.initializeFromSession(sessionData);
577
+ }
398
578
  abortAIMessage() {
399
579
  this.aiManager.abortAIMessage();
400
580
  }
@@ -436,6 +616,10 @@ export class Agent {
436
616
  this.backgroundBashManager.cleanup();
437
617
  // Cleanup MCP connections
438
618
  await this.mcpManager.cleanup();
619
+ // Cleanup LSP connections
620
+ if (this.lspManager instanceof LspManager) {
621
+ await this.lspManager.cleanup();
622
+ }
439
623
  // Cleanup subagent manager
440
624
  this.subagentManager.cleanup();
441
625
  // Cleanup live configuration reload
@@ -446,14 +630,33 @@ export class Agent {
446
630
  this.logger?.error("Error shutting down live configuration reload:", error);
447
631
  }
448
632
  // Cleanup memory store
449
- try {
450
- this.memoryStore.clear();
451
- this.logger?.debug("Memory store cleared successfully");
452
- }
453
- catch (error) {
454
- this.logger?.error("Error clearing memory store:", error);
455
- }
456
633
  }
634
+ /**
635
+ * Send a message to the AI agent with optional images
636
+ *
637
+ * @param content - The text content of the message to send
638
+ * @param images - Optional array of images to include with the message
639
+ * @param images[].path - File path to the image or base64 encoded image data
640
+ * @param images[].mimeType - MIME type of the image (e.g., 'image/png', 'image/jpeg')
641
+ * @returns Promise that resolves when the message has been processed
642
+ *
643
+ * @example
644
+ * ```typescript
645
+ * // Send a text message
646
+ * await agent.sendMessage("Hello, how are you?");
647
+ *
648
+ * // Send a message with images using file paths
649
+ * await agent.sendMessage("What do you see in these images?", [
650
+ * { path: "/path/to/image.png", mimeType: "image/png" },
651
+ * { path: "/path/to/photo.jpg", mimeType: "image/jpeg" }
652
+ * ]);
653
+ *
654
+ * // Send a message with base64 encoded image
655
+ * await agent.sendMessage("Analyze this image", [
656
+ * { path: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...", mimeType: "image/png" }
657
+ * ]);
658
+ * ```
659
+ */
457
660
  async sendMessage(content, images) {
458
661
  try {
459
662
  // Handle slash command - check if it's a slash command (starts with /)
@@ -496,6 +699,7 @@ export class Agent {
496
699
  transcriptPath: this.messageManager.getTranscriptPath(),
497
700
  cwd: this.workdir,
498
701
  userPrompt: content,
702
+ env: this.configurationService.getEnvironmentVars(), // Include configuration environment variables
499
703
  });
500
704
  // Process hook results and determine if we should continue
501
705
  const processResult = this.hookManager.processHookResults("UserPromptSubmit", hookResults, this.messageManager);
@@ -521,23 +725,33 @@ export class Agent {
521
725
  /** Save memory to project or user memory file */
522
726
  async saveMemory(message, type) {
523
727
  try {
728
+ // Ensure the message starts with # for memory functions
729
+ const formattedMessage = message.startsWith("#")
730
+ ? message
731
+ : `#${message}`;
524
732
  if (type === "project") {
525
- await memory.addMemory(message, this.workdir);
733
+ await memory.addMemory(formattedMessage, this.workdir);
734
+ // Update internal state after successful save
735
+ this._projectMemoryContent = await memory.readMemoryFile(this.workdir);
526
736
  }
527
737
  else {
528
- await memory.addUserMemory(message);
738
+ await memory.addUserMemory(formattedMessage);
739
+ // Update internal state after successful save
740
+ this._userMemoryContent = await memory.getUserMemoryContent();
529
741
  }
530
742
  // Add successful MemoryBlock to the last assistant message
531
743
  const memoryText = message.substring(1).trim();
532
744
  const typeLabel = type === "project" ? "Project Memory" : "User Memory";
533
- const storagePath = type === "project" ? "AGENTS.md" : "user-memory.md";
745
+ const storagePath = "AGENTS.md";
534
746
  this.messageManager.addMemoryBlock(`${typeLabel}: ${memoryText}`, true, type, storagePath);
535
747
  }
536
748
  catch (error) {
537
749
  // Add failed MemoryBlock to the last assistant message
750
+ const memoryText = message.substring(1).trim();
538
751
  const typeLabel = type === "project" ? "Project Memory" : "User Memory";
539
- const storagePath = type === "project" ? "AGENTS.md" : "user-memory.md";
540
- this.messageManager.addMemoryBlock(`${typeLabel} add failed: ${error instanceof Error ? error.message : String(error)}`, false, type, storagePath);
752
+ const storagePath = "AGENTS.md";
753
+ const errorMessage = error instanceof Error ? error.message : String(error);
754
+ this.messageManager.addMemoryBlock(`${typeLabel}: ${memoryText} - Error: ${errorMessage}`, false, type, storagePath);
541
755
  }
542
756
  }
543
757
  // ========== MCP Management Methods ==========
@@ -574,51 +788,40 @@ export class Agent {
574
788
  getCustomCommands() {
575
789
  return this.slashCommandManager.getCustomCommands();
576
790
  }
577
- // ========== Live Configuration Management ==========
578
791
  /**
579
- * Update Agent configuration from live settings.json changes
580
- * This method refreshes all configuration-dependent components with new values
581
- * Note: Constructor values still take precedence over live configuration
792
+ * Get the current permission mode
582
793
  */
583
- updateConfiguration() {
584
- try {
585
- this.logger?.info("Live Config: Updating Agent configuration from live settings");
586
- // Re-resolve configuration with current workdir, preserving constructor overrides
587
- // We need to track what was explicitly provided in constructor vs. what should use live config
588
- const newGatewayConfig = configResolver.resolveGatewayConfig(this.constructorApiKey, // Preserve constructor override if provided
589
- this.constructorBaseURL, // Preserve constructor override if provided
590
- this.workdir);
591
- const newModelConfig = configResolver.resolveModelConfig(this.constructorAgentModel, // Preserve constructor override if provided
592
- this.constructorFastModel, // Preserve constructor override if provided
593
- this.workdir);
594
- const newTokenLimit = configResolver.resolveTokenLimit(this.constructorTokenLimit, this.workdir);
595
- // Validate new configuration
596
- configValidator.validateGatewayConfig(newGatewayConfig);
597
- configValidator.validateTokenLimit(newTokenLimit);
598
- configValidator.validateModelConfig(newModelConfig.agentModel, newModelConfig.fastModel);
599
- // Update stored configuration
600
- this.gatewayConfig = newGatewayConfig;
601
- this.modelConfig = newModelConfig;
602
- this.tokenLimit = newTokenLimit;
603
- // Update AIManager with new configuration
604
- this.aiManager.updateConfiguration(newGatewayConfig, newModelConfig, newTokenLimit);
605
- // Update SubagentManager with new configuration
606
- this.subagentManager.updateConfiguration(newGatewayConfig, newModelConfig, newTokenLimit);
607
- this.logger?.info(`Live Config: Agent configuration updated successfully - model: ${newModelConfig.agentModel}, tokenLimit: ${newTokenLimit}`);
608
- }
609
- catch (error) {
610
- this.logger?.error(`Live Config: Failed to update Agent configuration: ${error.message}`);
611
- // Don't throw - continue with previous configuration
612
- }
794
+ getPermissionMode() {
795
+ return this.toolManager.getPermissionMode();
613
796
  }
614
797
  /**
615
- * Get current Agent configuration for debugging
798
+ * Set the permission mode
799
+ * @param mode - The new permission mode
616
800
  */
617
- getCurrentConfiguration() {
618
- return {
619
- gatewayConfig: { ...this.gatewayConfig },
620
- modelConfig: { ...this.modelConfig },
621
- tokenLimit: this.tokenLimit,
622
- };
801
+ setPermissionMode(mode) {
802
+ this.toolManager.setPermissionMode(mode);
803
+ this.options.callbacks?.onPermissionModeChange?.(mode);
804
+ }
805
+ /**
806
+ * Add a persistent permission rule
807
+ * @param rule - The rule to add (e.g., "Bash(ls)")
808
+ */
809
+ async addPermissionRule(rule) {
810
+ // 1. Update PermissionManager state
811
+ const currentRules = this.permissionManager.getAllowedRules();
812
+ if (!currentRules.includes(rule)) {
813
+ this.permissionManager.updateAllowedRules([...currentRules, rule]);
814
+ // 2. Persist to settings.local.json
815
+ try {
816
+ await this.configurationService.addAllowedRule(this.workdir, rule);
817
+ this.logger?.debug("Persistent permission rule added", { rule });
818
+ }
819
+ catch (error) {
820
+ this.logger?.error("Failed to persist permission rule", {
821
+ rule,
822
+ error: error instanceof Error ? error.message : String(error),
823
+ });
824
+ }
825
+ }
623
826
  }
624
827
  }