wave-agent-sdk 0.0.7 → 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 (240) hide show
  1. package/dist/agent.d.ts +105 -24
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +438 -53
  4. package/dist/index.d.ts +4 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +4 -0
  7. package/dist/managers/aiManager.d.ts +18 -7
  8. package/dist/managers/aiManager.d.ts.map +1 -1
  9. package/dist/managers/aiManager.js +254 -142
  10. package/dist/managers/backgroundBashManager.d.ts.map +1 -1
  11. package/dist/managers/backgroundBashManager.js +11 -9
  12. package/dist/managers/hookManager.d.ts +6 -6
  13. package/dist/managers/hookManager.d.ts.map +1 -1
  14. package/dist/managers/hookManager.js +81 -39
  15. package/dist/managers/liveConfigManager.d.ts +95 -0
  16. package/dist/managers/liveConfigManager.d.ts.map +1 -0
  17. package/dist/managers/liveConfigManager.js +442 -0
  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 +41 -24
  22. package/dist/managers/messageManager.d.ts.map +1 -1
  23. package/dist/managers/messageManager.js +184 -73
  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 +4 -2
  32. package/dist/managers/subagentManager.d.ts +42 -6
  33. package/dist/managers/subagentManager.d.ts.map +1 -1
  34. package/dist/managers/subagentManager.js +213 -62
  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 +15 -5
  39. package/dist/services/aiService.d.ts.map +1 -1
  40. package/dist/services/aiService.js +446 -77
  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 +69 -0
  45. package/dist/services/fileWatcher.d.ts.map +1 -0
  46. package/dist/services/fileWatcher.js +212 -0
  47. package/dist/services/hook.d.ts +5 -40
  48. package/dist/services/hook.d.ts.map +1 -1
  49. package/dist/services/hook.js +47 -109
  50. package/dist/services/jsonlHandler.d.ts +71 -0
  51. package/dist/services/jsonlHandler.d.ts.map +1 -0
  52. package/dist/services/jsonlHandler.js +236 -0
  53. package/dist/services/memory.d.ts.map +1 -1
  54. package/dist/services/memory.js +33 -11
  55. package/dist/services/session.d.ts +116 -52
  56. package/dist/services/session.d.ts.map +1 -1
  57. package/dist/services/session.js +415 -143
  58. package/dist/tools/bashTool.d.ts.map +1 -1
  59. package/dist/tools/bashTool.js +77 -17
  60. package/dist/tools/deleteFileTool.d.ts.map +1 -1
  61. package/dist/tools/deleteFileTool.js +27 -1
  62. package/dist/tools/editTool.d.ts.map +1 -1
  63. package/dist/tools/editTool.js +33 -8
  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 +30 -10
  69. package/dist/tools/readTool.d.ts.map +1 -1
  70. package/dist/tools/readTool.js +113 -3
  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 +30 -15
  78. package/dist/types/commands.d.ts +4 -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 +45 -0
  86. package/dist/types/core.d.ts.map +1 -1
  87. package/dist/types/environment.d.ts +83 -0
  88. package/dist/types/environment.d.ts.map +1 -0
  89. package/dist/types/environment.js +21 -0
  90. package/dist/types/fileSearch.d.ts +5 -0
  91. package/dist/types/fileSearch.d.ts.map +1 -0
  92. package/dist/types/fileSearch.js +1 -0
  93. package/dist/types/hooks.d.ts +18 -3
  94. package/dist/types/hooks.d.ts.map +1 -1
  95. package/dist/types/hooks.js +8 -8
  96. package/dist/types/index.d.ts +7 -0
  97. package/dist/types/index.d.ts.map +1 -1
  98. package/dist/types/index.js +7 -0
  99. package/dist/types/lsp.d.ts +90 -0
  100. package/dist/types/lsp.d.ts.map +1 -0
  101. package/dist/types/lsp.js +4 -0
  102. package/dist/types/messaging.d.ts +19 -12
  103. package/dist/types/messaging.d.ts.map +1 -1
  104. package/dist/types/permissions.d.ts +35 -0
  105. package/dist/types/permissions.d.ts.map +1 -0
  106. package/dist/types/permissions.js +12 -0
  107. package/dist/types/session.d.ts +15 -0
  108. package/dist/types/session.d.ts.map +1 -0
  109. package/dist/types/session.js +7 -0
  110. package/dist/types/skills.d.ts +1 -0
  111. package/dist/types/skills.d.ts.map +1 -1
  112. package/dist/types/tools.d.ts +35 -0
  113. package/dist/types/tools.d.ts.map +1 -0
  114. package/dist/types/tools.js +4 -0
  115. package/dist/utils/abortUtils.d.ts +34 -0
  116. package/dist/utils/abortUtils.d.ts.map +1 -0
  117. package/dist/utils/abortUtils.js +92 -0
  118. package/dist/utils/bashHistory.d.ts +4 -0
  119. package/dist/utils/bashHistory.d.ts.map +1 -1
  120. package/dist/utils/bashHistory.js +48 -30
  121. package/dist/utils/builtinSubagents.d.ts +7 -0
  122. package/dist/utils/builtinSubagents.d.ts.map +1 -0
  123. package/dist/utils/builtinSubagents.js +65 -0
  124. package/dist/utils/cacheControlUtils.d.ts +96 -0
  125. package/dist/utils/cacheControlUtils.d.ts.map +1 -0
  126. package/dist/utils/cacheControlUtils.js +324 -0
  127. package/dist/utils/commandPathResolver.d.ts +52 -0
  128. package/dist/utils/commandPathResolver.d.ts.map +1 -0
  129. package/dist/utils/commandPathResolver.js +145 -0
  130. package/dist/utils/configPaths.d.ts +85 -0
  131. package/dist/utils/configPaths.d.ts.map +1 -0
  132. package/dist/utils/configPaths.js +121 -0
  133. package/dist/utils/constants.d.ts +1 -13
  134. package/dist/utils/constants.d.ts.map +1 -1
  135. package/dist/utils/constants.js +2 -14
  136. package/dist/utils/convertMessagesForAPI.d.ts +2 -1
  137. package/dist/utils/convertMessagesForAPI.d.ts.map +1 -1
  138. package/dist/utils/convertMessagesForAPI.js +39 -18
  139. package/dist/utils/customCommands.d.ts.map +1 -1
  140. package/dist/utils/customCommands.js +66 -21
  141. package/dist/utils/fileSearch.d.ts +14 -0
  142. package/dist/utils/fileSearch.d.ts.map +1 -0
  143. package/dist/utils/fileSearch.js +88 -0
  144. package/dist/utils/fileUtils.d.ts +27 -0
  145. package/dist/utils/fileUtils.d.ts.map +1 -0
  146. package/dist/utils/fileUtils.js +145 -0
  147. package/dist/utils/globalLogger.d.ts +88 -0
  148. package/dist/utils/globalLogger.d.ts.map +1 -0
  149. package/dist/utils/globalLogger.js +120 -0
  150. package/dist/utils/largeOutputHandler.d.ts +15 -0
  151. package/dist/utils/largeOutputHandler.d.ts.map +1 -0
  152. package/dist/utils/largeOutputHandler.js +40 -0
  153. package/dist/utils/markdownParser.d.ts.map +1 -1
  154. package/dist/utils/markdownParser.js +1 -17
  155. package/dist/utils/mcpUtils.d.ts.map +1 -1
  156. package/dist/utils/mcpUtils.js +25 -3
  157. package/dist/utils/messageOperations.d.ts +20 -18
  158. package/dist/utils/messageOperations.d.ts.map +1 -1
  159. package/dist/utils/messageOperations.js +30 -38
  160. package/dist/utils/pathEncoder.d.ts +108 -0
  161. package/dist/utils/pathEncoder.d.ts.map +1 -0
  162. package/dist/utils/pathEncoder.js +279 -0
  163. package/dist/utils/subagentParser.d.ts +2 -2
  164. package/dist/utils/subagentParser.d.ts.map +1 -1
  165. package/dist/utils/subagentParser.js +12 -8
  166. package/dist/utils/tokenCalculation.d.ts +26 -0
  167. package/dist/utils/tokenCalculation.d.ts.map +1 -0
  168. package/dist/utils/tokenCalculation.js +36 -0
  169. package/dist/utils/tokenEstimator.d.ts +39 -0
  170. package/dist/utils/tokenEstimator.d.ts.map +1 -0
  171. package/dist/utils/tokenEstimator.js +55 -0
  172. package/package.json +6 -6
  173. package/src/agent.ts +586 -78
  174. package/src/index.ts +4 -0
  175. package/src/managers/aiManager.ts +341 -192
  176. package/src/managers/backgroundBashManager.ts +11 -9
  177. package/src/managers/hookManager.ts +102 -54
  178. package/src/managers/liveConfigManager.ts +634 -0
  179. package/src/managers/lspManager.ts +434 -0
  180. package/src/managers/messageManager.ts +258 -121
  181. package/src/managers/permissionManager.ts +276 -0
  182. package/src/managers/skillManager.ts +3 -1
  183. package/src/managers/slashCommandManager.ts +5 -3
  184. package/src/managers/subagentManager.ts +295 -76
  185. package/src/managers/toolManager.ts +95 -3
  186. package/src/services/aiService.ts +656 -84
  187. package/src/services/configurationService.ts +762 -0
  188. package/src/services/fileWatcher.ts +300 -0
  189. package/src/services/hook.ts +54 -144
  190. package/src/services/jsonlHandler.ts +303 -0
  191. package/src/services/memory.ts +34 -11
  192. package/src/services/session.ts +522 -173
  193. package/src/tools/bashTool.ts +94 -20
  194. package/src/tools/deleteFileTool.ts +38 -1
  195. package/src/tools/editTool.ts +44 -9
  196. package/src/tools/lspTool.ts +760 -0
  197. package/src/tools/multiEditTool.ts +41 -11
  198. package/src/tools/readTool.ts +127 -3
  199. package/src/tools/skillTool.ts +2 -2
  200. package/src/tools/todoWriteTool.ts +33 -1
  201. package/src/tools/types.ts +15 -9
  202. package/src/tools/writeTool.ts +43 -16
  203. package/src/types/commands.ts +6 -1
  204. package/src/types/config.ts +5 -0
  205. package/src/types/configuration.ts +73 -0
  206. package/src/types/core.ts +55 -0
  207. package/src/types/environment.ts +104 -0
  208. package/src/types/fileSearch.ts +4 -0
  209. package/src/types/hooks.ts +32 -16
  210. package/src/types/index.ts +7 -0
  211. package/src/types/lsp.ts +96 -0
  212. package/src/types/messaging.ts +21 -14
  213. package/src/types/permissions.ts +48 -0
  214. package/src/types/session.ts +20 -0
  215. package/src/types/skills.ts +1 -0
  216. package/src/types/tools.ts +38 -0
  217. package/src/utils/abortUtils.ts +118 -0
  218. package/src/utils/bashHistory.ts +55 -31
  219. package/src/utils/builtinSubagents.ts +71 -0
  220. package/src/utils/cacheControlUtils.ts +475 -0
  221. package/src/utils/commandPathResolver.ts +189 -0
  222. package/src/utils/configPaths.ts +163 -0
  223. package/src/utils/constants.ts +2 -17
  224. package/src/utils/convertMessagesForAPI.ts +44 -18
  225. package/src/utils/customCommands.ts +90 -22
  226. package/src/utils/fileSearch.ts +107 -0
  227. package/src/utils/fileUtils.ts +160 -0
  228. package/src/utils/globalLogger.ts +128 -0
  229. package/src/utils/largeOutputHandler.ts +55 -0
  230. package/src/utils/markdownParser.ts +1 -19
  231. package/src/utils/mcpUtils.ts +34 -3
  232. package/src/utils/messageOperations.ts +47 -53
  233. package/src/utils/pathEncoder.ts +394 -0
  234. package/src/utils/subagentParser.ts +13 -9
  235. package/src/utils/tokenCalculation.ts +43 -0
  236. package/src/utils/tokenEstimator.ts +68 -0
  237. package/dist/utils/configResolver.d.ts +0 -38
  238. package/dist/utils/configResolver.d.ts.map +0 -1
  239. package/dist/utils/configResolver.js +0 -106
  240. package/src/utils/configResolver.ts +0 -142
package/dist/agent.js CHANGED
@@ -1,17 +1,61 @@
1
1
  import { MessageManager, } from "./managers/messageManager.js";
2
2
  import { AIManager } from "./managers/aiManager.js";
3
3
  import { ToolManager } from "./managers/toolManager.js";
4
- import { SubagentManager } from "./managers/subagentManager.js";
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 { configResolver } from "./utils/configResolver.js";
13
+ import { LiveConfigManager } from "./managers/liveConfigManager.js";
12
14
  import { configValidator } from "./utils/configValidator.js";
13
15
  import { SkillManager } from "./managers/skillManager.js";
16
+ import { loadSessionFromJsonl, handleSessionRestoration, } from "./services/session.js";
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";
14
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
+ }
15
59
  /**
16
60
  * Agent constructor - handles configuration resolution and validation
17
61
  *
@@ -20,54 +64,115 @@ export class Agent {
20
64
  * Agent.create() to maintain API consistency.
21
65
  *
22
66
  * @param options - Configuration options for the Agent instance
23
- * @param options.sessionDir - Optional custom directory for session storage
24
67
  */
25
68
  constructor(options) {
26
69
  this.bashManager = null;
27
70
  this._usages = []; // Usage tracking array
28
- const { callbacks = {}, logger, workdir, systemPrompt, sessionDir, } = options;
29
- // Resolve configuration from constructor args and environment variables
30
- const gatewayConfig = configResolver.resolveGatewayConfig(options.apiKey, options.baseURL);
31
- const modelConfig = configResolver.resolveModelConfig(options.agentModel, options.fastModel);
32
- const tokenLimit = configResolver.resolveTokenLimit(options.tokenLimit);
33
- // Validate resolved configuration
34
- configValidator.validateGatewayConfig(gatewayConfig);
35
- configValidator.validateTokenLimit(tokenLimit);
36
- configValidator.validateModelConfig(modelConfig.agentModel, modelConfig.fastModel);
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
76
+ this.workdir = workdir || process.cwd();
77
+ // Initialize configuration service
78
+ this.configurationService = new ConfigurationService();
37
79
  this.logger = logger; // Save the passed logger
38
- this.workdir = workdir || process.cwd(); // Set working directory, default to current working directory
39
80
  this.systemPrompt = systemPrompt; // Save custom system prompt
40
- // Store resolved configuration
41
- this.gatewayConfig = gatewayConfig;
42
- this.modelConfig = modelConfig;
43
- this.tokenLimit = tokenLimit;
81
+ this.stream = stream; // Save streaming mode flag
82
+ // Store options for dynamic configuration resolution
83
+ this.options = options;
44
84
  this.backgroundBashManager = new BackgroundBashManager({
45
85
  callbacks,
46
86
  workdir: this.workdir,
47
87
  });
48
88
  this.mcpManager = new McpManager({ callbacks, logger: this.logger }); // Initialize MCP manager
49
- this.toolManager = new ToolManager({
50
- mcpManager: this.mcpManager,
51
- logger: this.logger,
52
- }); // Initialize tool registry, pass MCP manager
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
53
97
  this.hookManager = new HookManager(this.workdir, undefined, this.logger); // Initialize hooks manager
54
98
  // Initialize MessageManager
55
99
  this.messageManager = new MessageManager({
56
100
  callbacks,
57
101
  workdir: this.workdir,
58
102
  logger: this.logger,
59
- sessionDir,
60
103
  });
104
+ // Create a wrapper for canUseTool that triggers notification hooks
105
+ const canUseToolWithNotification = options.canUseTool
106
+ ? async (context) => {
107
+ try {
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
+ });
121
+ }
122
+ catch (error) {
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
128
+ }
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);
134
+ }
135
+ if (decision.newPermissionRule) {
136
+ await this.addPermissionRule(decision.newPermissionRule);
137
+ }
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({
151
+ workdir: this.workdir,
152
+ logger: this.logger,
153
+ hookManager: this.hookManager,
154
+ permissionManager: this.permissionManager,
155
+ configurationService: this.configurationService,
156
+ }); // Initialize live configuration manager
61
157
  // Initialize subagent manager with all dependencies in constructor
62
158
  // IMPORTANT: Must be initialized AFTER MessageManager
63
159
  this.subagentManager = new SubagentManager({
64
160
  workdir: this.workdir,
65
161
  parentToolManager: this.toolManager,
66
162
  parentMessageManager: this.messageManager,
163
+ callbacks: {
164
+ onSubagentUserMessageAdded: callbacks.onSubagentUserMessageAdded,
165
+ onSubagentAssistantMessageAdded: callbacks.onSubagentAssistantMessageAdded,
166
+ onSubagentAssistantContentUpdated: callbacks.onSubagentAssistantContentUpdated,
167
+ onSubagentAssistantReasoningUpdated: callbacks.onSubagentAssistantReasoningUpdated,
168
+ onSubagentToolBlockUpdated: callbacks.onSubagentToolBlockUpdated,
169
+ onSubagentMessagesChange: callbacks.onSubagentMessagesChange,
170
+ }, // Pass subagent callbacks for forwarding
67
171
  logger: this.logger,
68
- gatewayConfig,
69
- modelConfig,
70
- tokenLimit,
172
+ getGatewayConfig: () => this.getGatewayConfig(),
173
+ getModelConfig: () => this.getModelConfig(),
174
+ getTokenLimit: () => this.getTokenLimit(),
175
+ hookManager: this.hookManager,
71
176
  onUsageAdded: (usage) => this.addUsage(usage),
72
177
  });
73
178
  // Initialize AI manager with resolved configuration
@@ -85,9 +190,11 @@ export class Agent {
85
190
  },
86
191
  workdir: this.workdir,
87
192
  systemPrompt: this.systemPrompt,
88
- gatewayConfig: this.gatewayConfig,
89
- modelConfig: this.modelConfig,
90
- 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
91
198
  });
92
199
  // Initialize command manager
93
200
  this.slashCommandManager = new SlashCommandManager({
@@ -119,9 +226,9 @@ export class Agent {
119
226
  * Rebuild usage array from messages containing usage metadata
120
227
  * Called during session restoration to reconstruct usage tracking
121
228
  */
122
- rebuildUsageFromMessages() {
229
+ rebuildUsageFromMessages(messages) {
123
230
  this._usages = [];
124
- this.messages.forEach((message) => {
231
+ messages.forEach((message) => {
125
232
  if (message.role === "assistant" && message.usage) {
126
233
  this._usages.push(message.usage);
127
234
  }
@@ -147,6 +254,28 @@ export class Agent {
147
254
  get workingDirectory() {
148
255
  return this.workdir;
149
256
  }
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;
278
+ }
150
279
  /** Get AI loading status */
151
280
  get isLoading() {
152
281
  return this.aiManager.isLoading;
@@ -185,9 +314,9 @@ export class Agent {
185
314
  * @param options - Configuration options for the Agent instance
186
315
  * @param options.apiKey - API key for the AI service (or set WAVE_API_KEY env var)
187
316
  * @param options.baseURL - Base URL for the AI service (or set WAVE_BASE_URL env var)
188
- * @param options.sessionDir - Optional custom directory for session file storage.
189
- * If not provided, defaults to ~/.wave/sessions/. Can be relative or absolute path.
190
- * Examples: "./app-sessions", "/var/myapp/sessions", "~/Documents/sessions"
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
191
320
  * @param options.callbacks - Optional callbacks for various Agent events
192
321
  * @param options.restoreSessionId - Optional session ID to restore from
193
322
  * @param options.continueLastSession - Whether to continue the last session automatically
@@ -199,22 +328,15 @@ export class Agent {
199
328
  *
200
329
  * @example
201
330
  * ```typescript
202
- * // Basic usage with default session directory
331
+ * // Basic usage
203
332
  * const agent = await Agent.create({
204
333
  * apiKey: 'your-api-key',
205
334
  * baseURL: 'https://api.example.com'
206
335
  * });
207
- *
208
- * // Custom session directory
209
- * const agent = await Agent.create({
210
- * apiKey: 'your-api-key',
211
- * baseURL: 'https://api.example.com',
212
- * sessionDir: './app-sessions'
213
- * });
214
336
  * ```
215
337
  */
216
338
  static async create(options) {
217
- // Create Agent instance - configuration resolution and validation now happens in constructor
339
+ // Create Agent instance
218
340
  const instance = new Agent(options);
219
341
  await instance.initialize({
220
342
  restoreSessionId: options.restoreSessionId,
@@ -223,12 +345,31 @@ export class Agent {
223
345
  });
224
346
  return instance;
225
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
+ }
226
364
  /** Private initialization method, handles async initialization logic */
227
365
  async initialize(options) {
228
366
  // Initialize managers first
229
367
  try {
230
368
  // Initialize SkillManager
231
- const skillManager = new SkillManager({ logger: this.logger });
369
+ const skillManager = new SkillManager({
370
+ logger: this.logger,
371
+ workdir: this.workdir,
372
+ });
232
373
  await skillManager.initialize();
233
374
  // Initialize SubagentManager (load and cache configurations)
234
375
  await this.subagentManager.initialize();
@@ -245,6 +386,9 @@ export class Agent {
245
386
  // Initialize MCP servers with auto-connect
246
387
  try {
247
388
  await this.mcpManager.initialize(this.workdir, true);
389
+ if (this.lspManager instanceof LspManager) {
390
+ await this.lspManager.initialize(this.workdir);
391
+ }
248
392
  }
249
393
  catch (error) {
250
394
  this.logger?.error("Failed to initialize MCP servers:", error);
@@ -252,29 +396,185 @@ export class Agent {
252
396
  }
253
397
  // Initialize hooks configuration
254
398
  try {
255
- // Load hooks configuration from user and project settings
399
+ // Load hooks configuration using ConfigurationService
256
400
  this.logger?.debug("Loading hooks configuration...");
257
- this.hookManager.loadConfigurationFromSettings();
401
+ const configResult = await this.configurationService.loadMergedConfiguration(this.workdir);
402
+ this.hookManager.loadConfigurationFromWaveConfig(configResult.configuration);
258
403
  this.logger?.debug("Hooks system initialized successfully");
259
404
  }
260
405
  catch (error) {
261
406
  this.logger?.error("Failed to initialize hooks system:", error);
262
407
  // Don't throw error to prevent app startup failure
263
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);
413
+ // Initialize live configuration reload
414
+ try {
415
+ this.logger?.debug("Initializing live configuration reload...");
416
+ await this.liveConfigManager.initialize();
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
+ }
433
+ }
434
+ catch (error) {
435
+ this.logger?.error("Failed to initialize live configuration reload:", error);
436
+ // Don't throw error to prevent app startup failure - continue without live reload
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
+ }
264
470
  // Handle session restoration or set provided messages
265
471
  if (options?.messages) {
266
472
  // If messages are provided, use them directly (useful for testing)
267
473
  this.messageManager.setMessages(options.messages);
268
474
  // Rebuild usage array from restored messages
269
- this.rebuildUsageFromMessages();
475
+ this.rebuildUsageFromMessages(options.messages);
270
476
  }
271
477
  else {
272
478
  // Otherwise, handle session restoration
273
- await this.messageManager.handleSessionRestoration(options?.restoreSessionId, options?.continueLastSession);
479
+ const sessionToRestore = await handleSessionRestoration(options?.restoreSessionId, options?.continueLastSession, this.messageManager.getWorkdir());
274
480
  // Rebuild usage array from restored messages
275
- this.rebuildUsageFromMessages();
481
+ this.rebuildUsageFromMessages(sessionToRestore?.messages || []);
482
+ // After main session is restored, restore any associated subagent sessions
483
+ await this.restoreSubagentSessions(sessionToRestore?.messages || []);
484
+ if (sessionToRestore)
485
+ this.messageManager.initializeFromSession(sessionToRestore);
276
486
  }
277
487
  }
488
+ /**
489
+ * Restore subagent sessions associated with the current main session
490
+ * This method is called after the main session is restored to load any subagent sessions
491
+ */
492
+ async restoreSubagentSessions(messages) {
493
+ try {
494
+ // Only attempt to restore subagent sessions if we have messages (session was restored)
495
+ if (messages.length === 0) {
496
+ return;
497
+ }
498
+ // Extract sessionId -> subagentId mapping from SubagentBlocks
499
+ const subagentBlockMap = new Map(); // sessionId -> { subagentId, configuration }
500
+ for (const message of messages) {
501
+ if (message.role === "assistant" && message.blocks) {
502
+ for (const block of message.blocks) {
503
+ if (block.type === "subagent" &&
504
+ block.sessionId &&
505
+ block.subagentId &&
506
+ block.configuration) {
507
+ subagentBlockMap.set(block.sessionId, {
508
+ subagentId: block.subagentId,
509
+ configuration: block.configuration,
510
+ });
511
+ }
512
+ }
513
+ }
514
+ }
515
+ if (subagentBlockMap.size === 0) {
516
+ return; // No subagent blocks found
517
+ }
518
+ // Load subagent sessions using sessionIds
519
+ const subagentSessions = [];
520
+ for (const [sessionId, blockData] of subagentBlockMap) {
521
+ try {
522
+ const sessionData = await loadSessionFromJsonl(sessionId, this.messageManager.getWorkdir(), "subagent");
523
+ if (sessionData) {
524
+ subagentSessions.push({
525
+ sessionData,
526
+ subagentId: blockData.subagentId, // Use the subagentId from SubagentBlock
527
+ configuration: blockData.configuration, // Include configuration
528
+ });
529
+ }
530
+ }
531
+ catch (error) {
532
+ this.logger?.warn(`Failed to load subagent session ${sessionId}:`, error);
533
+ }
534
+ }
535
+ if (subagentSessions.length > 0) {
536
+ this.logger?.debug(`Found ${subagentSessions.length} subagent sessions to restore`);
537
+ // Restore subagent sessions through the SubagentManager
538
+ await this.subagentManager.restoreSubagentSessions(subagentSessions);
539
+ this.logger?.debug("Subagent sessions restored successfully");
540
+ }
541
+ }
542
+ catch (error) {
543
+ this.logger?.warn("Failed to restore subagent sessions:", error);
544
+ // Don't throw error to prevent app startup failure
545
+ }
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
+ }
278
578
  abortAIMessage() {
279
579
  this.aiManager.abortAIMessage();
280
580
  }
@@ -316,9 +616,47 @@ export class Agent {
316
616
  this.backgroundBashManager.cleanup();
317
617
  // Cleanup MCP connections
318
618
  await this.mcpManager.cleanup();
619
+ // Cleanup LSP connections
620
+ if (this.lspManager instanceof LspManager) {
621
+ await this.lspManager.cleanup();
622
+ }
319
623
  // Cleanup subagent manager
320
624
  this.subagentManager.cleanup();
625
+ // Cleanup live configuration reload
626
+ try {
627
+ await this.liveConfigManager.shutdown();
628
+ }
629
+ catch (error) {
630
+ this.logger?.error("Error shutting down live configuration reload:", error);
631
+ }
632
+ // Cleanup memory store
321
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
+ */
322
660
  async sendMessage(content, images) {
323
661
  try {
324
662
  // Handle slash command - check if it's a slash command (starts with /)
@@ -361,6 +699,7 @@ export class Agent {
361
699
  transcriptPath: this.messageManager.getTranscriptPath(),
362
700
  cwd: this.workdir,
363
701
  userPrompt: content,
702
+ env: this.configurationService.getEnvironmentVars(), // Include configuration environment variables
364
703
  });
365
704
  // Process hook results and determine if we should continue
366
705
  const processResult = this.hookManager.processHookResults("UserPromptSubmit", hookResults, this.messageManager);
@@ -386,23 +725,33 @@ export class Agent {
386
725
  /** Save memory to project or user memory file */
387
726
  async saveMemory(message, type) {
388
727
  try {
728
+ // Ensure the message starts with # for memory functions
729
+ const formattedMessage = message.startsWith("#")
730
+ ? message
731
+ : `#${message}`;
389
732
  if (type === "project") {
390
- 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);
391
736
  }
392
737
  else {
393
- await memory.addUserMemory(message);
738
+ await memory.addUserMemory(formattedMessage);
739
+ // Update internal state after successful save
740
+ this._userMemoryContent = await memory.getUserMemoryContent();
394
741
  }
395
742
  // Add successful MemoryBlock to the last assistant message
396
743
  const memoryText = message.substring(1).trim();
397
744
  const typeLabel = type === "project" ? "Project Memory" : "User Memory";
398
- const storagePath = type === "project" ? "AGENTS.md" : "user-memory.md";
745
+ const storagePath = "AGENTS.md";
399
746
  this.messageManager.addMemoryBlock(`${typeLabel}: ${memoryText}`, true, type, storagePath);
400
747
  }
401
748
  catch (error) {
402
749
  // Add failed MemoryBlock to the last assistant message
750
+ const memoryText = message.substring(1).trim();
403
751
  const typeLabel = type === "project" ? "Project Memory" : "User Memory";
404
- const storagePath = type === "project" ? "AGENTS.md" : "user-memory.md";
405
- 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);
406
755
  }
407
756
  }
408
757
  // ========== MCP Management Methods ==========
@@ -439,4 +788,40 @@ export class Agent {
439
788
  getCustomCommands() {
440
789
  return this.slashCommandManager.getCustomCommands();
441
790
  }
791
+ /**
792
+ * Get the current permission mode
793
+ */
794
+ getPermissionMode() {
795
+ return this.toolManager.getPermissionMode();
796
+ }
797
+ /**
798
+ * Set the permission mode
799
+ * @param mode - The new permission mode
800
+ */
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
+ }
826
+ }
442
827
  }