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/src/agent.ts CHANGED
@@ -10,13 +10,19 @@ import {
10
10
  } from "./managers/subagentManager.js";
11
11
  import * as memory from "./services/memory.js";
12
12
  import { McpManager, type McpManagerCallbacks } from "./managers/mcpManager.js";
13
+ import { LspManager } from "./managers/lspManager.js";
13
14
  import { BashManager } from "./managers/bashManager.js";
14
15
  import {
15
16
  BackgroundBashManager,
16
17
  type BackgroundBashManagerCallbacks,
17
18
  } from "./managers/backgroundBashManager.js";
18
19
  import { SlashCommandManager } from "./managers/slashCommandManager.js";
19
- import type { SlashCommand, CustomSlashCommand } from "./types/index.js";
20
+ import { PermissionManager } from "./managers/permissionManager.js";
21
+ import type {
22
+ SlashCommand,
23
+ CustomSlashCommand,
24
+ ILspManager,
25
+ } from "./types/index.js";
20
26
  import type {
21
27
  Message,
22
28
  Logger,
@@ -24,17 +30,24 @@ import type {
24
30
  GatewayConfig,
25
31
  ModelConfig,
26
32
  Usage,
33
+ PermissionMode,
34
+ PermissionCallback,
27
35
  } from "./types/index.js";
28
36
  import { HookManager } from "./managers/hookManager.js";
29
- import { MemoryStoreService } from "./services/memoryStore.js";
30
- import { initializeMemoryStore } from "./services/memory.js";
31
37
  import { LiveConfigManager } from "./managers/liveConfigManager.js";
32
- import { configResolver } from "./utils/configResolver.js";
33
38
  import { configValidator } from "./utils/configValidator.js";
34
39
  import { SkillManager } from "./managers/skillManager.js";
35
- import { loadSessionFromJsonl } from "./services/session.js";
40
+ import {
41
+ loadSessionFromJsonl,
42
+ handleSessionRestoration,
43
+ } from "./services/session.js";
36
44
  import type { SubagentConfiguration } from "./utils/subagentParser.js";
37
45
  import { setGlobalLogger } from "./utils/globalLogger.js";
46
+ import { ConfigurationService } from "./services/configurationService.js";
47
+ import * as fs from "fs/promises";
48
+ import path from "path";
49
+ import os from "os";
50
+ import { ClientOptions } from "openai";
38
51
 
39
52
  /**
40
53
  * Configuration options for Agent instances
@@ -46,6 +59,9 @@ export interface AgentOptions {
46
59
  // Optional configuration with environment fallbacks
47
60
  apiKey?: string;
48
61
  baseURL?: string;
62
+ defaultHeaders?: Record<string, string>;
63
+ fetchOptions?: ClientOptions["fetchOptions"];
64
+ fetch?: ClientOptions["fetch"];
49
65
  agentModel?: string;
50
66
  fastModel?: string;
51
67
  tokenLimit?: number;
@@ -61,13 +77,23 @@ export interface AgentOptions {
61
77
  workdir?: string;
62
78
  /**Optional custom system prompt - if provided, replaces default system prompt */
63
79
  systemPrompt?: string;
80
+ /**Permission mode - defaults to "default" */
81
+ permissionMode?: PermissionMode;
82
+ /**Custom permission callback */
83
+ canUseTool?: PermissionCallback;
84
+ /**Whether to use streaming mode for AI responses - defaults to true */
85
+ stream?: boolean;
86
+ /**Optional custom LSP manager - if not provided, a standalone one will be created */
87
+ lspManager?: ILspManager;
64
88
  }
65
89
 
66
90
  export interface AgentCallbacks
67
91
  extends MessageManagerCallbacks,
68
92
  BackgroundBashManagerCallbacks,
69
93
  McpManagerCallbacks,
70
- SubagentManagerCallbacks {}
94
+ SubagentManagerCallbacks {
95
+ onPermissionModeChange?: (mode: PermissionMode) => void;
96
+ }
71
97
 
72
98
  export class Agent {
73
99
  private messageManager: MessageManager;
@@ -78,26 +104,80 @@ export class Agent {
78
104
  private logger?: Logger; // Add optional logger property
79
105
  private toolManager: ToolManager; // Add tool registry instance
80
106
  private mcpManager: McpManager; // Add MCP manager instance
107
+ private lspManager: ILspManager; // Add LSP manager instance
108
+ private permissionManager: PermissionManager; // Add permission manager instance
81
109
  private subagentManager: SubagentManager; // Add subagent manager instance
82
110
  private slashCommandManager: SlashCommandManager; // Add slash command manager instance
83
111
  private hookManager: HookManager; // Add hooks manager instance
84
- private memoryStore: MemoryStoreService; // Add memory store service
85
112
  private liveConfigManager: LiveConfigManager; // Add live configuration manager
113
+ private configurationService: ConfigurationService; // Add configuration service
86
114
  private workdir: string; // Working directory
87
115
  private systemPrompt?: string; // Custom system prompt
88
116
  private _usages: Usage[] = []; // Usage tracking array
117
+ private stream: boolean; // Streaming mode flag
118
+
119
+ // Configuration options storage for dynamic resolution
120
+ private options: AgentOptions;
121
+
122
+ // Memory content storage
123
+ private _projectMemoryContent: string = "";
124
+ private _userMemoryContent: string = "";
125
+
126
+ // Dynamic configuration getter methods
127
+ public getGatewayConfig(): GatewayConfig {
128
+ return this.configurationService.resolveGatewayConfig(
129
+ this.options.apiKey,
130
+ this.options.baseURL,
131
+ this.options.defaultHeaders,
132
+ this.options.fetchOptions,
133
+ this.options.fetch,
134
+ );
135
+ }
136
+
137
+ public getModelConfig(): ModelConfig {
138
+ return this.configurationService.resolveModelConfig(
139
+ this.options.agentModel,
140
+ this.options.fastModel,
141
+ );
142
+ }
143
+
144
+ public getTokenLimit(): number {
145
+ return this.configurationService.resolveTokenLimit(this.options.tokenLimit);
146
+ }
147
+
148
+ /**
149
+ * Update agent configuration dynamically
150
+ *
151
+ * @param config - Configuration updates for gateway, model, and token limit
152
+ */
153
+ public updateConfig(config: {
154
+ gateway?: Partial<GatewayConfig>;
155
+ model?: Partial<ModelConfig>;
156
+ tokenLimit?: number;
157
+ }): void {
158
+ if (config.gateway) {
159
+ this.options.apiKey = config.gateway.apiKey ?? this.options.apiKey;
160
+ this.options.baseURL = config.gateway.baseURL ?? this.options.baseURL;
161
+ this.options.defaultHeaders =
162
+ config.gateway.defaultHeaders ?? this.options.defaultHeaders;
163
+ this.options.fetchOptions =
164
+ config.gateway.fetchOptions ?? this.options.fetchOptions;
165
+ this.options.fetch = config.gateway.fetch ?? this.options.fetch;
166
+ }
167
+
168
+ if (config.model) {
169
+ this.options.agentModel =
170
+ config.model.agentModel ?? this.options.agentModel;
171
+ this.options.fastModel = config.model.fastModel ?? this.options.fastModel;
172
+ }
89
173
 
90
- // Configuration properties
91
- private gatewayConfig: GatewayConfig;
92
- private modelConfig: ModelConfig;
93
- private tokenLimit: number;
174
+ if (config.tokenLimit !== undefined) {
175
+ this.options.tokenLimit = config.tokenLimit;
176
+ }
94
177
 
95
- // Original constructor values for preserving overrides during live updates
96
- private readonly constructorApiKey?: string;
97
- private readonly constructorBaseURL?: string;
98
- private readonly constructorAgentModel?: string;
99
- private readonly constructorFastModel?: string;
100
- private readonly constructorTokenLimit?: number;
178
+ // Re-validate configuration after update
179
+ this.resolveAndValidateConfig();
180
+ }
101
181
 
102
182
  /**
103
183
  * Agent constructor - handles configuration resolution and validation
@@ -109,110 +189,43 @@ export class Agent {
109
189
  * @param options - Configuration options for the Agent instance
110
190
  */
111
191
  private constructor(options: AgentOptions) {
112
- const { callbacks = {}, logger, workdir, systemPrompt } = options;
113
-
114
- // Set working directory first so it can be used for configuration resolution
192
+ const {
193
+ callbacks = {},
194
+ logger,
195
+ workdir,
196
+ systemPrompt,
197
+ stream = true,
198
+ } = options;
199
+
200
+ // Set working directory early as we need it for loading configuration
115
201
  this.workdir = workdir || process.cwd();
116
202
 
117
- // Store original constructor values for live config updates
118
- this.constructorApiKey = options.apiKey;
119
- this.constructorBaseURL = options.baseURL;
120
- this.constructorAgentModel = options.agentModel;
121
- this.constructorFastModel = options.fastModel;
122
- this.constructorTokenLimit = options.tokenLimit;
123
-
124
- // Resolve configuration from constructor args and environment variables with live config support
125
- const gatewayConfig = configResolver.resolveGatewayConfig(
126
- options.apiKey,
127
- options.baseURL,
128
- this.workdir,
129
- );
130
- const modelConfig = configResolver.resolveModelConfig(
131
- options.agentModel,
132
- options.fastModel,
133
- this.workdir,
134
- );
135
- const tokenLimit = configResolver.resolveTokenLimit(
136
- options.tokenLimit,
137
- this.workdir,
138
- );
139
-
140
- // Validate resolved configuration
141
- configValidator.validateGatewayConfig(gatewayConfig);
142
- configValidator.validateTokenLimit(tokenLimit);
143
- configValidator.validateModelConfig(
144
- modelConfig.agentModel,
145
- modelConfig.fastModel,
146
- );
203
+ // Initialize configuration service
204
+ this.configurationService = new ConfigurationService();
147
205
 
148
206
  this.logger = logger; // Save the passed logger
149
207
  this.systemPrompt = systemPrompt; // Save custom system prompt
208
+ this.stream = stream; // Save streaming mode flag
150
209
 
151
- // Set global logger for SDK-wide access
152
- setGlobalLogger(logger || null);
153
-
154
- // Store resolved configuration
155
- this.gatewayConfig = gatewayConfig;
156
- this.modelConfig = modelConfig;
157
- this.tokenLimit = tokenLimit;
210
+ // Store options for dynamic configuration resolution
211
+ this.options = options;
158
212
 
159
213
  this.backgroundBashManager = new BackgroundBashManager({
160
214
  callbacks,
161
215
  workdir: this.workdir,
162
216
  });
163
217
  this.mcpManager = new McpManager({ callbacks, logger: this.logger }); // Initialize MCP manager
164
- this.toolManager = new ToolManager({
165
- mcpManager: this.mcpManager,
166
- logger: this.logger,
167
- }); // Initialize tool registry, pass MCP manager
218
+ this.lspManager =
219
+ options.lspManager || new LspManager({ logger: this.logger }); // Initialize LSP manager
168
220
 
169
- this.memoryStore = new MemoryStoreService(this.logger); // Initialize memory store service
170
- initializeMemoryStore(this.memoryStore); // Initialize global memory store reference
171
- this.hookManager = new HookManager(this.workdir, undefined, this.logger); // Initialize hooks manager
172
- this.liveConfigManager = new LiveConfigManager({
173
- workdir: this.workdir,
174
- logger: this.logger,
175
- onConfigurationChanged: () => {
176
- // Update Agent configuration (AIManager, SubagentManager)
177
- this.updateConfiguration();
221
+ // Initialize permission manager
222
+ this.permissionManager = new PermissionManager({ logger: this.logger });
223
+ this.permissionManager.setOnConfiguredDefaultModeChange((mode) => {
224
+ this.options.callbacks?.onPermissionModeChange?.(mode);
225
+ });
178
226
 
179
- // Reload hook configuration
180
- try {
181
- this.hookManager.loadConfigurationFromSettings();
182
- this.logger?.info(
183
- "Live Config: Hook configuration reloaded successfully",
184
- );
185
- } catch (error) {
186
- this.logger?.error(
187
- `Live Config: Failed to reload hook configuration: ${(error as Error).message}`,
188
- );
189
- }
190
- },
191
- onMemoryStoreFileChanged: async (
192
- filePath: string,
193
- changeType: "add" | "change" | "unlink",
194
- ) => {
195
- try {
196
- if (changeType === "unlink") {
197
- // Handle file deletion gracefully
198
- this.memoryStore.removeContent(filePath);
199
- this.logger?.info(
200
- "Live Config: Removed AGENTS.md from memory store due to file deletion",
201
- );
202
- } else {
203
- // Update memory store content for add/change
204
- await this.memoryStore.updateContent(filePath);
205
- this.logger?.info(
206
- "Live Config: Updated AGENTS.md content in memory store",
207
- );
208
- }
209
- } catch (error) {
210
- this.logger?.error(
211
- `Live Config: Failed to update memory store: ${(error as Error).message}`,
212
- );
213
- }
214
- },
215
- }); // Initialize live configuration manager
227
+ // Initialize configuration service and hooks manager
228
+ this.hookManager = new HookManager(this.workdir, undefined, this.logger); // Initialize hooks manager
216
229
 
217
230
  // Initialize MessageManager
218
231
  this.messageManager = new MessageManager({
@@ -221,6 +234,65 @@ export class Agent {
221
234
  logger: this.logger,
222
235
  });
223
236
 
237
+ // Create a wrapper for canUseTool that triggers notification hooks
238
+ const canUseToolWithNotification: PermissionCallback | undefined =
239
+ options.canUseTool
240
+ ? async (context) => {
241
+ try {
242
+ // Trigger notification hooks before calling the original callback
243
+ const notificationMessage = `Claude needs your permission to use ${context.toolName}`;
244
+ await this.hookManager.executeHooks("Notification", {
245
+ event: "Notification",
246
+ projectDir: this.workdir,
247
+ timestamp: new Date(),
248
+ sessionId: this.sessionId,
249
+ transcriptPath: this.messageManager.getTranscriptPath(),
250
+ cwd: this.workdir,
251
+ message: notificationMessage,
252
+ notificationType: "permission_prompt",
253
+ env: this.configurationService.getEnvironmentVars(), // Include configuration environment variables
254
+ });
255
+ } catch (error) {
256
+ this.logger?.warn("Failed to execute notification hooks", {
257
+ toolName: context.toolName,
258
+ error: error instanceof Error ? error.message : String(error),
259
+ });
260
+ // Continue with permission check even if hooks fail
261
+ }
262
+
263
+ // Call the original callback
264
+ const decision = await options.canUseTool!(context);
265
+
266
+ // Handle state changes from decision
267
+ if (decision.newPermissionMode) {
268
+ this.setPermissionMode(decision.newPermissionMode);
269
+ }
270
+
271
+ if (decision.newPermissionRule) {
272
+ await this.addPermissionRule(decision.newPermissionRule);
273
+ }
274
+
275
+ return decision;
276
+ }
277
+ : undefined;
278
+
279
+ // Initialize tool manager with permission context
280
+ this.toolManager = new ToolManager({
281
+ mcpManager: this.mcpManager,
282
+ lspManager: this.lspManager,
283
+ logger: this.logger,
284
+ permissionManager: this.permissionManager,
285
+ permissionMode: options.permissionMode, // Let PermissionManager handle defaultMode resolution
286
+ canUseToolCallback: canUseToolWithNotification,
287
+ }); // Initialize tool registry with permission support
288
+ this.liveConfigManager = new LiveConfigManager({
289
+ workdir: this.workdir,
290
+ logger: this.logger,
291
+ hookManager: this.hookManager,
292
+ permissionManager: this.permissionManager,
293
+ configurationService: this.configurationService,
294
+ }); // Initialize live configuration manager
295
+
224
296
  // Initialize subagent manager with all dependencies in constructor
225
297
  // IMPORTANT: Must be initialized AFTER MessageManager
226
298
  this.subagentManager = new SubagentManager({
@@ -233,13 +305,15 @@ export class Agent {
233
305
  callbacks.onSubagentAssistantMessageAdded,
234
306
  onSubagentAssistantContentUpdated:
235
307
  callbacks.onSubagentAssistantContentUpdated,
308
+ onSubagentAssistantReasoningUpdated:
309
+ callbacks.onSubagentAssistantReasoningUpdated,
236
310
  onSubagentToolBlockUpdated: callbacks.onSubagentToolBlockUpdated,
237
311
  onSubagentMessagesChange: callbacks.onSubagentMessagesChange,
238
312
  }, // Pass subagent callbacks for forwarding
239
313
  logger: this.logger,
240
- gatewayConfig,
241
- modelConfig,
242
- tokenLimit,
314
+ getGatewayConfig: () => this.getGatewayConfig(),
315
+ getModelConfig: () => this.getModelConfig(),
316
+ getTokenLimit: () => this.getTokenLimit(),
243
317
  hookManager: this.hookManager,
244
318
  onUsageAdded: (usage) => this.addUsage(usage),
245
319
  });
@@ -259,9 +333,11 @@ export class Agent {
259
333
  },
260
334
  workdir: this.workdir,
261
335
  systemPrompt: this.systemPrompt,
262
- gatewayConfig: this.gatewayConfig,
263
- modelConfig: this.modelConfig,
264
- tokenLimit: this.tokenLimit,
336
+ stream: this.stream, // Pass streaming mode flag
337
+ getGatewayConfig: () => this.getGatewayConfig(),
338
+ getModelConfig: () => this.getModelConfig(),
339
+ getTokenLimit: () => this.getTokenLimit(),
340
+ getEnvironmentVars: () => this.configurationService.getEnvironmentVars(), // Provide access to configuration environment variables
265
341
  });
266
342
 
267
343
  // Initialize command manager
@@ -300,9 +376,9 @@ export class Agent {
300
376
  * Rebuild usage array from messages containing usage metadata
301
377
  * Called during session restoration to reconstruct usage tracking
302
378
  */
303
- private rebuildUsageFromMessages(): void {
379
+ private rebuildUsageFromMessages(messages: Message[]): void {
304
380
  this._usages = [];
305
- this.messages.forEach((message) => {
381
+ messages.forEach((message) => {
306
382
  if (message.role === "assistant" && message.usage) {
307
383
  this._usages.push(message.usage);
308
384
  }
@@ -333,9 +409,29 @@ export class Agent {
333
409
  return this.workdir;
334
410
  }
335
411
 
336
- /** Get merged environment variables from Wave configuration */
337
- public get environmentVars(): Record<string, string> | undefined {
338
- return this.hookManager.getEnvironmentVars();
412
+ /** Get project memory content */
413
+ public get projectMemory(): string {
414
+ return this._projectMemoryContent;
415
+ }
416
+
417
+ /** Get user memory content */
418
+ public get userMemory(): string {
419
+ return this._userMemoryContent;
420
+ }
421
+
422
+ /** Get combined memory content (project + user) */
423
+ public get combinedMemory(): string {
424
+ let combined = "";
425
+ if (this._projectMemoryContent.trim()) {
426
+ combined += this._projectMemoryContent;
427
+ }
428
+ if (this._userMemoryContent.trim()) {
429
+ if (combined) {
430
+ combined += "\n\n";
431
+ }
432
+ combined += this._userMemoryContent;
433
+ }
434
+ return combined;
339
435
  }
340
436
 
341
437
  /** Get AI loading status */
@@ -384,6 +480,9 @@ export class Agent {
384
480
  * @param options - Configuration options for the Agent instance
385
481
  * @param options.apiKey - API key for the AI service (or set WAVE_API_KEY env var)
386
482
  * @param options.baseURL - Base URL for the AI service (or set WAVE_BASE_URL env var)
483
+ * @param options.defaultHeaders - Optional HTTP headers to pass to the AI service
484
+ * @param options.fetchOptions - Optional fetch options to pass to the AI service
485
+ * @param options.fetch - Optional custom fetch implementation
387
486
  * @param options.callbacks - Optional callbacks for various Agent events
388
487
  * @param options.restoreSessionId - Optional session ID to restore from
389
488
  * @param options.continueLastSession - Whether to continue the last session automatically
@@ -403,7 +502,7 @@ export class Agent {
403
502
  * ```
404
503
  */
405
504
  static async create(options: AgentOptions): Promise<Agent> {
406
- // Create Agent instance - configuration resolution and validation now happens in constructor
505
+ // Create Agent instance
407
506
  const instance = new Agent(options);
408
507
  await instance.initialize({
409
508
  restoreSessionId: options.restoreSessionId,
@@ -413,6 +512,27 @@ export class Agent {
413
512
  return instance;
414
513
  }
415
514
 
515
+ /**
516
+ * Resolve and validate configuration from constructor args, environment variables,
517
+ * and loaded settings.json.
518
+ *
519
+ * This is called during initialization after settings.json has been loaded.
520
+ */
521
+ private resolveAndValidateConfig(): void {
522
+ // Resolve configuration from constructor args and environment variables (including settings.json)
523
+ const gatewayConfig = this.getGatewayConfig();
524
+ const modelConfig = this.getModelConfig();
525
+ const tokenLimit = this.getTokenLimit();
526
+
527
+ // Validate resolved configuration
528
+ configValidator.validateGatewayConfig(gatewayConfig);
529
+ configValidator.validateTokenLimit(tokenLimit);
530
+ configValidator.validateModelConfig(
531
+ modelConfig.agentModel,
532
+ modelConfig.fastModel,
533
+ );
534
+ }
535
+
416
536
  /** Private initialization method, handles async initialization logic */
417
537
  private async initialize(options?: {
418
538
  restoreSessionId?: string;
@@ -422,7 +542,10 @@ export class Agent {
422
542
  // Initialize managers first
423
543
  try {
424
544
  // Initialize SkillManager
425
- const skillManager = new SkillManager({ logger: this.logger });
545
+ const skillManager = new SkillManager({
546
+ logger: this.logger,
547
+ workdir: this.workdir,
548
+ });
426
549
  await skillManager.initialize();
427
550
 
428
551
  // Initialize SubagentManager (load and cache configurations)
@@ -441,6 +564,9 @@ export class Agent {
441
564
  // Initialize MCP servers with auto-connect
442
565
  try {
443
566
  await this.mcpManager.initialize(this.workdir, true);
567
+ if (this.lspManager instanceof LspManager) {
568
+ await this.lspManager.initialize(this.workdir);
569
+ }
444
570
  } catch (error) {
445
571
  this.logger?.error("Failed to initialize MCP servers:", error);
446
572
  // Don't throw error to prevent app startup failure
@@ -448,20 +574,58 @@ export class Agent {
448
574
 
449
575
  // Initialize hooks configuration
450
576
  try {
451
- // Load hooks configuration from user and project settings
577
+ // Load hooks configuration using ConfigurationService
452
578
  this.logger?.debug("Loading hooks configuration...");
453
- this.hookManager.loadConfigurationFromSettings();
579
+ const configResult =
580
+ await this.configurationService.loadMergedConfiguration(this.workdir);
581
+
582
+ this.hookManager.loadConfigurationFromWaveConfig(
583
+ configResult.configuration,
584
+ );
454
585
  this.logger?.debug("Hooks system initialized successfully");
455
586
  } catch (error) {
456
587
  this.logger?.error("Failed to initialize hooks system:", error);
457
588
  // Don't throw error to prevent app startup failure
458
589
  }
459
590
 
591
+ // Resolve and validate configuration after loading settings.json
592
+ this.resolveAndValidateConfig();
593
+
594
+ // Set global logger for SDK-wide access after validation
595
+ setGlobalLogger(this.logger || null);
596
+
460
597
  // Initialize live configuration reload
461
598
  try {
462
599
  this.logger?.debug("Initializing live configuration reload...");
463
600
  await this.liveConfigManager.initialize();
464
601
  this.logger?.debug("Live configuration reload initialized successfully");
602
+
603
+ // Update permission manager with configuration-based defaultMode
604
+ const currentConfig = this.liveConfigManager.getCurrentConfiguration();
605
+ if (currentConfig?.defaultMode) {
606
+ this.logger?.debug(
607
+ "Applying configured defaultMode to PermissionManager",
608
+ {
609
+ defaultMode: currentConfig.defaultMode,
610
+ },
611
+ );
612
+ this.permissionManager.updateConfiguredDefaultMode(
613
+ currentConfig.defaultMode,
614
+ );
615
+ }
616
+
617
+ // Update permission manager with configuration-based allowed rules
618
+ if (currentConfig?.permissions?.allow) {
619
+ this.logger?.debug(
620
+ "Applying configured allowed rules to PermissionManager",
621
+ {
622
+ count: currentConfig.permissions.allow.length,
623
+ },
624
+ );
625
+ this.permissionManager.updateAllowedRules(
626
+ currentConfig.permissions.allow,
627
+ );
628
+ }
465
629
  } catch (error) {
466
630
  this.logger?.error(
467
631
  "Failed to initialize live configuration reload:",
@@ -470,23 +634,69 @@ export class Agent {
470
634
  // Don't throw error to prevent app startup failure - continue without live reload
471
635
  }
472
636
 
637
+ // Load memory files during initialization
638
+ try {
639
+ this.logger?.debug("Loading memory files...");
640
+
641
+ // Load project memory from AGENTS.md (bypass memory store for direct file access)
642
+ try {
643
+ const projectMemoryPath = path.join(this.workdir, "AGENTS.md");
644
+ this._projectMemoryContent = await fs.readFile(
645
+ projectMemoryPath,
646
+ "utf-8",
647
+ );
648
+ this.logger?.debug("Project memory loaded successfully");
649
+ } catch (error) {
650
+ this._projectMemoryContent = "";
651
+ this.logger?.debug(
652
+ "Project memory file not found or unreadable, using empty content:",
653
+ error instanceof Error ? error.message : String(error),
654
+ );
655
+ }
656
+
657
+ // Load user memory (bypass memory store for direct file access)
658
+ try {
659
+ const userMemoryPath = path.join(os.homedir(), ".wave", "AGENTS.md");
660
+ this._userMemoryContent = await fs.readFile(userMemoryPath, "utf-8");
661
+ this.logger?.debug("User memory loaded successfully");
662
+ } catch (error) {
663
+ this._userMemoryContent = "";
664
+ this.logger?.debug(
665
+ "User memory file not found or unreadable, using empty content:",
666
+ error instanceof Error ? error.message : String(error),
667
+ );
668
+ }
669
+
670
+ this.logger?.debug("Memory initialization completed");
671
+ } catch (error) {
672
+ // Ensure memory is always initialized even if loading fails
673
+ this._projectMemoryContent = "";
674
+ this._userMemoryContent = "";
675
+ this.logger?.error("Failed to load memory files:", error);
676
+ // Don't throw error to prevent app startup failure
677
+ }
678
+
473
679
  // Handle session restoration or set provided messages
474
680
  if (options?.messages) {
475
681
  // If messages are provided, use them directly (useful for testing)
476
682
  this.messageManager.setMessages(options.messages);
477
683
  // Rebuild usage array from restored messages
478
- this.rebuildUsageFromMessages();
684
+ this.rebuildUsageFromMessages(options.messages);
479
685
  } else {
480
686
  // Otherwise, handle session restoration
481
- await this.messageManager.handleSessionRestoration(
687
+ const sessionToRestore = await handleSessionRestoration(
482
688
  options?.restoreSessionId,
483
689
  options?.continueLastSession,
690
+ this.messageManager.getWorkdir(),
484
691
  );
485
692
  // Rebuild usage array from restored messages
486
- this.rebuildUsageFromMessages();
693
+ this.rebuildUsageFromMessages(sessionToRestore?.messages || []);
487
694
 
488
695
  // After main session is restored, restore any associated subagent sessions
489
- await this.restoreSubagentSessions();
696
+ await this.restoreSubagentSessions(sessionToRestore?.messages || []);
697
+
698
+ if (sessionToRestore)
699
+ this.messageManager.initializeFromSession(sessionToRestore);
490
700
  }
491
701
  }
492
702
 
@@ -494,10 +704,10 @@ export class Agent {
494
704
  * Restore subagent sessions associated with the current main session
495
705
  * This method is called after the main session is restored to load any subagent sessions
496
706
  */
497
- private async restoreSubagentSessions(): Promise<void> {
707
+ private async restoreSubagentSessions(messages: Message[]): Promise<void> {
498
708
  try {
499
709
  // Only attempt to restore subagent sessions if we have messages (session was restored)
500
- if (this.messages.length === 0) {
710
+ if (messages.length === 0) {
501
711
  return;
502
712
  }
503
713
 
@@ -507,7 +717,7 @@ export class Agent {
507
717
  { subagentId: string; configuration: SubagentConfiguration }
508
718
  >(); // sessionId -> { subagentId, configuration }
509
719
 
510
- for (const message of this.messages) {
720
+ for (const message of messages) {
511
721
  if (message.role === "assistant" && message.blocks) {
512
722
  for (const block of message.blocks) {
513
723
  if (
@@ -536,6 +746,7 @@ export class Agent {
536
746
  const sessionData = await loadSessionFromJsonl(
537
747
  sessionId,
538
748
  this.messageManager.getWorkdir(),
749
+ "subagent",
539
750
  );
540
751
  if (sessionData) {
541
752
  subagentSessions.push({
@@ -568,6 +779,48 @@ export class Agent {
568
779
  }
569
780
  }
570
781
 
782
+ /**
783
+ * Restore a session by ID, switching to the target session without destroying the Agent instance
784
+ * @param sessionId - The ID of the session to restore
785
+ */
786
+ public async restoreSession(sessionId: string): Promise<void> {
787
+ // 1. Validation
788
+ if (!sessionId || sessionId === this.sessionId) {
789
+ return; // No-op if session ID is invalid or already current
790
+ }
791
+
792
+ // 2. Auto-save current session
793
+ try {
794
+ await this.messageManager.saveSession();
795
+ } catch (error) {
796
+ this.logger?.warn(
797
+ "Failed to save current session before restore:",
798
+ error,
799
+ );
800
+ // Continue with restoration even if save fails
801
+ }
802
+
803
+ // 3. Load target session
804
+ const sessionData = await loadSessionFromJsonl(
805
+ sessionId,
806
+ this.messageManager.getWorkdir(),
807
+ );
808
+ if (!sessionData) {
809
+ throw new Error(`Session not found: ${sessionId}`);
810
+ }
811
+
812
+ // 4. Clean current state
813
+ this.abortMessage(); // Abort any running operations
814
+ this.subagentManager.cleanup(); // Clean up active subagents
815
+
816
+ // 5. Rebuild usage and restore subagents (in correct order)
817
+ this.rebuildUsageFromMessages(sessionData.messages);
818
+ await this.restoreSubagentSessions(sessionData.messages);
819
+
820
+ // 6. Initialize session state last
821
+ this.messageManager.initializeFromSession(sessionData);
822
+ }
823
+
571
824
  public abortAIMessage(): void {
572
825
  this.aiManager.abortAIMessage();
573
826
  }
@@ -616,6 +869,10 @@ export class Agent {
616
869
  this.backgroundBashManager.cleanup();
617
870
  // Cleanup MCP connections
618
871
  await this.mcpManager.cleanup();
872
+ // Cleanup LSP connections
873
+ if (this.lspManager instanceof LspManager) {
874
+ await this.lspManager.cleanup();
875
+ }
619
876
  // Cleanup subagent manager
620
877
  this.subagentManager.cleanup();
621
878
  // Cleanup live configuration reload
@@ -628,14 +885,34 @@ export class Agent {
628
885
  );
629
886
  }
630
887
  // Cleanup memory store
631
- try {
632
- this.memoryStore.clear();
633
- this.logger?.debug("Memory store cleared successfully");
634
- } catch (error) {
635
- this.logger?.error("Error clearing memory store:", error);
636
- }
637
888
  }
638
889
 
890
+ /**
891
+ * Send a message to the AI agent with optional images
892
+ *
893
+ * @param content - The text content of the message to send
894
+ * @param images - Optional array of images to include with the message
895
+ * @param images[].path - File path to the image or base64 encoded image data
896
+ * @param images[].mimeType - MIME type of the image (e.g., 'image/png', 'image/jpeg')
897
+ * @returns Promise that resolves when the message has been processed
898
+ *
899
+ * @example
900
+ * ```typescript
901
+ * // Send a text message
902
+ * await agent.sendMessage("Hello, how are you?");
903
+ *
904
+ * // Send a message with images using file paths
905
+ * await agent.sendMessage("What do you see in these images?", [
906
+ * { path: "/path/to/image.png", mimeType: "image/png" },
907
+ * { path: "/path/to/photo.jpg", mimeType: "image/jpeg" }
908
+ * ]);
909
+ *
910
+ * // Send a message with base64 encoded image
911
+ * await agent.sendMessage("Analyze this image", [
912
+ * { path: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...", mimeType: "image/png" }
913
+ * ]);
914
+ * ```
915
+ */
639
916
  public async sendMessage(
640
917
  content: string,
641
918
  images?: Array<{ path: string; mimeType: string }>,
@@ -690,6 +967,7 @@ export class Agent {
690
967
  transcriptPath: this.messageManager.getTranscriptPath(),
691
968
  cwd: this.workdir,
692
969
  userPrompt: content,
970
+ env: this.configurationService.getEnvironmentVars(), // Include configuration environment variables
693
971
  },
694
972
  );
695
973
 
@@ -728,16 +1006,25 @@ export class Agent {
728
1006
  type: "project" | "user",
729
1007
  ): Promise<void> {
730
1008
  try {
1009
+ // Ensure the message starts with # for memory functions
1010
+ const formattedMessage = message.startsWith("#")
1011
+ ? message
1012
+ : `#${message}`;
1013
+
731
1014
  if (type === "project") {
732
- await memory.addMemory(message, this.workdir);
1015
+ await memory.addMemory(formattedMessage, this.workdir);
1016
+ // Update internal state after successful save
1017
+ this._projectMemoryContent = await memory.readMemoryFile(this.workdir);
733
1018
  } else {
734
- await memory.addUserMemory(message);
1019
+ await memory.addUserMemory(formattedMessage);
1020
+ // Update internal state after successful save
1021
+ this._userMemoryContent = await memory.getUserMemoryContent();
735
1022
  }
736
1023
 
737
1024
  // Add successful MemoryBlock to the last assistant message
738
1025
  const memoryText = message.substring(1).trim();
739
1026
  const typeLabel = type === "project" ? "Project Memory" : "User Memory";
740
- const storagePath = type === "project" ? "AGENTS.md" : "user-memory.md";
1027
+ const storagePath = "AGENTS.md";
741
1028
 
742
1029
  this.messageManager.addMemoryBlock(
743
1030
  `${typeLabel}: ${memoryText}`,
@@ -747,13 +1034,14 @@ export class Agent {
747
1034
  );
748
1035
  } catch (error) {
749
1036
  // Add failed MemoryBlock to the last assistant message
1037
+ const memoryText = message.substring(1).trim();
750
1038
  const typeLabel = type === "project" ? "Project Memory" : "User Memory";
751
- const storagePath = type === "project" ? "AGENTS.md" : "user-memory.md";
1039
+ const storagePath = "AGENTS.md";
1040
+ const errorMessage =
1041
+ error instanceof Error ? error.message : String(error);
752
1042
 
753
1043
  this.messageManager.addMemoryBlock(
754
- `${typeLabel} add failed: ${
755
- error instanceof Error ? error.message : String(error)
756
- }`,
1044
+ `${typeLabel}: ${memoryText} - Error: ${errorMessage}`,
757
1045
  false,
758
1046
  type,
759
1047
  storagePath,
@@ -805,86 +1093,42 @@ export class Agent {
805
1093
  return this.slashCommandManager.getCustomCommands();
806
1094
  }
807
1095
 
808
- // ========== Live Configuration Management ==========
809
-
810
1096
  /**
811
- * Update Agent configuration from live settings.json changes
812
- * This method refreshes all configuration-dependent components with new values
813
- * Note: Constructor values still take precedence over live configuration
1097
+ * Get the current permission mode
814
1098
  */
815
- public updateConfiguration(): void {
816
- try {
817
- this.logger?.info(
818
- "Live Config: Updating Agent configuration from live settings",
819
- );
820
-
821
- // Re-resolve configuration with current workdir, preserving constructor overrides
822
- // We need to track what was explicitly provided in constructor vs. what should use live config
823
- const newGatewayConfig = configResolver.resolveGatewayConfig(
824
- this.constructorApiKey, // Preserve constructor override if provided
825
- this.constructorBaseURL, // Preserve constructor override if provided
826
- this.workdir,
827
- );
828
- const newModelConfig = configResolver.resolveModelConfig(
829
- this.constructorAgentModel, // Preserve constructor override if provided
830
- this.constructorFastModel, // Preserve constructor override if provided
831
- this.workdir,
832
- );
833
- const newTokenLimit = configResolver.resolveTokenLimit(
834
- this.constructorTokenLimit,
835
- this.workdir,
836
- );
837
-
838
- // Validate new configuration
839
- configValidator.validateGatewayConfig(newGatewayConfig);
840
- configValidator.validateTokenLimit(newTokenLimit);
841
- configValidator.validateModelConfig(
842
- newModelConfig.agentModel,
843
- newModelConfig.fastModel,
844
- );
845
-
846
- // Update stored configuration
847
- this.gatewayConfig = newGatewayConfig;
848
- this.modelConfig = newModelConfig;
849
- this.tokenLimit = newTokenLimit;
850
-
851
- // Update AIManager with new configuration
852
- this.aiManager.updateConfiguration(
853
- newGatewayConfig,
854
- newModelConfig,
855
- newTokenLimit,
856
- );
857
-
858
- // Update SubagentManager with new configuration
859
- this.subagentManager.updateConfiguration(
860
- newGatewayConfig,
861
- newModelConfig,
862
- newTokenLimit,
863
- );
1099
+ public getPermissionMode(): PermissionMode {
1100
+ return this.toolManager.getPermissionMode();
1101
+ }
864
1102
 
865
- this.logger?.info(
866
- `Live Config: Agent configuration updated successfully - model: ${newModelConfig.agentModel}, tokenLimit: ${newTokenLimit}`,
867
- );
868
- } catch (error) {
869
- this.logger?.error(
870
- `Live Config: Failed to update Agent configuration: ${(error as Error).message}`,
871
- );
872
- // Don't throw - continue with previous configuration
873
- }
1103
+ /**
1104
+ * Set the permission mode
1105
+ * @param mode - The new permission mode
1106
+ */
1107
+ public setPermissionMode(mode: PermissionMode): void {
1108
+ this.toolManager.setPermissionMode(mode);
1109
+ this.options.callbacks?.onPermissionModeChange?.(mode);
874
1110
  }
875
1111
 
876
1112
  /**
877
- * Get current Agent configuration for debugging
1113
+ * Add a persistent permission rule
1114
+ * @param rule - The rule to add (e.g., "Bash(ls)")
878
1115
  */
879
- public getCurrentConfiguration(): {
880
- gatewayConfig: GatewayConfig;
881
- modelConfig: ModelConfig;
882
- tokenLimit: number;
883
- } {
884
- return {
885
- gatewayConfig: { ...this.gatewayConfig },
886
- modelConfig: { ...this.modelConfig },
887
- tokenLimit: this.tokenLimit,
888
- };
1116
+ private async addPermissionRule(rule: string): Promise<void> {
1117
+ // 1. Update PermissionManager state
1118
+ const currentRules = this.permissionManager.getAllowedRules();
1119
+ if (!currentRules.includes(rule)) {
1120
+ this.permissionManager.updateAllowedRules([...currentRules, rule]);
1121
+
1122
+ // 2. Persist to settings.local.json
1123
+ try {
1124
+ await this.configurationService.addAllowedRule(this.workdir, rule);
1125
+ this.logger?.debug("Persistent permission rule added", { rule });
1126
+ } catch (error) {
1127
+ this.logger?.error("Failed to persist permission rule", {
1128
+ rule,
1129
+ error: error instanceof Error ? error.message : String(error),
1130
+ });
1131
+ }
1132
+ }
889
1133
  }
890
1134
  }