wave-agent-sdk 0.0.6 → 0.0.8
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.
- package/dist/agent.d.ts +32 -20
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +209 -24
- package/dist/constants/events.d.ts +28 -0
- package/dist/constants/events.d.ts.map +1 -0
- package/dist/constants/events.js +27 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/managers/aiManager.d.ts +34 -1
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +248 -132
- package/dist/managers/backgroundBashManager.d.ts.map +1 -1
- package/dist/managers/backgroundBashManager.js +7 -6
- package/dist/managers/hookManager.d.ts +13 -16
- package/dist/managers/hookManager.d.ts.map +1 -1
- package/dist/managers/hookManager.js +81 -44
- package/dist/managers/liveConfigManager.d.ts +58 -0
- package/dist/managers/liveConfigManager.d.ts.map +1 -0
- package/dist/managers/liveConfigManager.js +160 -0
- package/dist/managers/messageManager.d.ts +41 -24
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +168 -49
- package/dist/managers/slashCommandManager.d.ts.map +1 -1
- package/dist/managers/slashCommandManager.js +9 -3
- package/dist/managers/subagentManager.d.ts +51 -0
- package/dist/managers/subagentManager.d.ts.map +1 -1
- package/dist/managers/subagentManager.js +190 -19
- package/dist/services/aiService.d.ts +13 -5
- package/dist/services/aiService.d.ts.map +1 -1
- package/dist/services/aiService.js +350 -74
- package/dist/services/configurationWatcher.d.ts +120 -0
- package/dist/services/configurationWatcher.d.ts.map +1 -0
- package/dist/services/configurationWatcher.js +439 -0
- package/dist/services/fileWatcher.d.ts +69 -0
- package/dist/services/fileWatcher.d.ts.map +1 -0
- package/dist/services/fileWatcher.js +213 -0
- package/dist/services/hook.d.ts +91 -9
- package/dist/services/hook.d.ts.map +1 -1
- package/dist/services/hook.js +393 -43
- package/dist/services/jsonlHandler.d.ts +62 -0
- package/dist/services/jsonlHandler.d.ts.map +1 -0
- package/dist/services/jsonlHandler.js +257 -0
- package/dist/services/memory.d.ts +9 -0
- package/dist/services/memory.d.ts.map +1 -1
- package/dist/services/memory.js +81 -12
- package/dist/services/memoryStore.d.ts +81 -0
- package/dist/services/memoryStore.d.ts.map +1 -0
- package/dist/services/memoryStore.js +200 -0
- package/dist/services/session.d.ts +64 -49
- package/dist/services/session.d.ts.map +1 -1
- package/dist/services/session.js +310 -132
- package/dist/tools/bashTool.d.ts.map +1 -1
- package/dist/tools/bashTool.js +5 -4
- package/dist/tools/deleteFileTool.d.ts.map +1 -1
- package/dist/tools/deleteFileTool.js +2 -1
- package/dist/tools/editTool.d.ts.map +1 -1
- package/dist/tools/editTool.js +3 -2
- package/dist/tools/multiEditTool.d.ts.map +1 -1
- package/dist/tools/multiEditTool.js +4 -3
- package/dist/tools/readTool.d.ts.map +1 -1
- package/dist/tools/readTool.js +2 -1
- package/dist/tools/todoWriteTool.d.ts.map +1 -1
- package/dist/tools/todoWriteTool.js +3 -10
- package/dist/tools/writeTool.d.ts.map +1 -1
- package/dist/tools/writeTool.js +5 -6
- package/dist/types/commands.d.ts +4 -0
- package/dist/types/commands.d.ts.map +1 -1
- package/dist/types/core.d.ts +35 -0
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/environment.d.ts +42 -0
- package/dist/types/environment.d.ts.map +1 -0
- package/dist/types/environment.js +21 -0
- package/dist/types/hooks.d.ts +8 -2
- package/dist/types/hooks.d.ts.map +1 -1
- package/dist/types/hooks.js +8 -2
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/memoryStore.d.ts +82 -0
- package/dist/types/memoryStore.d.ts.map +1 -0
- package/dist/types/memoryStore.js +7 -0
- package/dist/types/messaging.d.ts +21 -9
- package/dist/types/messaging.d.ts.map +1 -1
- package/dist/types/messaging.js +5 -1
- package/dist/types/session.d.ts +20 -0
- package/dist/types/session.d.ts.map +1 -0
- package/dist/types/session.js +7 -0
- package/dist/utils/bashHistory.d.ts.map +1 -1
- package/dist/utils/bashHistory.js +27 -26
- package/dist/utils/cacheControlUtils.d.ts +121 -0
- package/dist/utils/cacheControlUtils.d.ts.map +1 -0
- package/dist/utils/cacheControlUtils.js +367 -0
- package/dist/utils/commandPathResolver.d.ts +52 -0
- package/dist/utils/commandPathResolver.d.ts.map +1 -0
- package/dist/utils/commandPathResolver.js +145 -0
- package/dist/utils/configPaths.d.ts +85 -0
- package/dist/utils/configPaths.d.ts.map +1 -0
- package/dist/utils/configPaths.js +121 -0
- package/dist/utils/configResolver.d.ts +37 -10
- package/dist/utils/configResolver.d.ts.map +1 -1
- package/dist/utils/configResolver.js +127 -23
- package/dist/utils/constants.d.ts +1 -1
- package/dist/utils/constants.js +1 -1
- package/dist/utils/convertMessagesForAPI.d.ts.map +1 -1
- package/dist/utils/convertMessagesForAPI.js +8 -13
- package/dist/utils/customCommands.d.ts.map +1 -1
- package/dist/utils/customCommands.js +66 -21
- package/dist/utils/fileUtils.d.ts +15 -0
- package/dist/utils/fileUtils.d.ts.map +1 -0
- package/dist/utils/fileUtils.js +61 -0
- package/dist/utils/globalLogger.d.ts +102 -0
- package/dist/utils/globalLogger.d.ts.map +1 -0
- package/dist/utils/globalLogger.js +136 -0
- package/dist/utils/hookMatcher.d.ts +1 -6
- package/dist/utils/hookMatcher.d.ts.map +1 -1
- package/dist/utils/mcpUtils.d.ts.map +1 -1
- package/dist/utils/mcpUtils.js +25 -3
- package/dist/utils/messageOperations.d.ts +27 -27
- package/dist/utils/messageOperations.d.ts.map +1 -1
- package/dist/utils/messageOperations.js +46 -36
- package/dist/utils/pathEncoder.d.ts +104 -0
- package/dist/utils/pathEncoder.d.ts.map +1 -0
- package/dist/utils/pathEncoder.js +272 -0
- package/dist/utils/subagentParser.d.ts.map +1 -1
- package/dist/utils/subagentParser.js +2 -1
- package/dist/utils/tokenCalculation.d.ts +26 -0
- package/dist/utils/tokenCalculation.d.ts.map +1 -0
- package/dist/utils/tokenCalculation.js +36 -0
- package/package.json +6 -3
- package/src/agent.ts +301 -37
- package/src/constants/events.ts +38 -0
- package/src/index.ts +2 -0
- package/src/managers/aiManager.ts +325 -173
- package/src/managers/backgroundBashManager.ts +7 -6
- package/src/managers/hookManager.ts +106 -84
- package/src/managers/liveConfigManager.ts +248 -0
- package/src/managers/messageManager.ts +237 -100
- package/src/managers/slashCommandManager.ts +9 -7
- package/src/managers/subagentManager.ts +284 -22
- package/src/services/aiService.ts +474 -83
- package/src/services/configurationWatcher.ts +622 -0
- package/src/services/fileWatcher.ts +301 -0
- package/src/services/hook.ts +538 -47
- package/src/services/jsonlHandler.ts +319 -0
- package/src/services/memory.ts +92 -12
- package/src/services/memoryStore.ts +279 -0
- package/src/services/session.ts +381 -157
- package/src/tools/bashTool.ts +5 -4
- package/src/tools/deleteFileTool.ts +2 -1
- package/src/tools/editTool.ts +3 -2
- package/src/tools/multiEditTool.ts +4 -3
- package/src/tools/readTool.ts +2 -1
- package/src/tools/todoWriteTool.ts +3 -11
- package/src/tools/writeTool.ts +7 -6
- package/src/types/commands.ts +6 -0
- package/src/types/core.ts +44 -0
- package/src/types/environment.ts +60 -0
- package/src/types/hooks.ts +21 -8
- package/src/types/index.ts +2 -0
- package/src/types/memoryStore.ts +94 -0
- package/src/types/messaging.ts +21 -10
- package/src/types/session.ts +25 -0
- package/src/utils/bashHistory.ts +27 -27
- package/src/utils/cacheControlUtils.ts +540 -0
- package/src/utils/commandPathResolver.ts +189 -0
- package/src/utils/configPaths.ts +163 -0
- package/src/utils/configResolver.ts +182 -22
- package/src/utils/constants.ts +1 -1
- package/src/utils/convertMessagesForAPI.ts +8 -14
- package/src/utils/customCommands.ts +90 -22
- package/src/utils/fileUtils.ts +65 -0
- package/src/utils/globalLogger.ts +145 -0
- package/src/utils/hookMatcher.ts +1 -12
- package/src/utils/mcpUtils.ts +34 -3
- package/src/utils/messageOperations.ts +77 -60
- package/src/utils/pathEncoder.ts +379 -0
- package/src/utils/subagentParser.ts +2 -1
- package/src/utils/tokenCalculation.ts +43 -0
- package/src/types/index.ts.backup +0 -357
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from "child_process";
|
|
2
|
+
import { logger } from "../utils/globalLogger.js";
|
|
2
3
|
import type { BackgroundShell } from "../types/index.js";
|
|
3
4
|
|
|
4
5
|
export interface BackgroundBashManagerCallbacks {
|
|
@@ -127,8 +128,8 @@ export class BackgroundBashManager {
|
|
|
127
128
|
.split("\n")
|
|
128
129
|
.filter((line) => regex.test(line))
|
|
129
130
|
.join("\n");
|
|
130
|
-
} catch {
|
|
131
|
-
|
|
131
|
+
} catch (error) {
|
|
132
|
+
logger.warn(`Invalid filter regex: ${filter}`, error);
|
|
132
133
|
}
|
|
133
134
|
}
|
|
134
135
|
|
|
@@ -159,8 +160,8 @@ export class BackgroundBashManager {
|
|
|
159
160
|
) {
|
|
160
161
|
try {
|
|
161
162
|
process.kill(-shell.process.pid, "SIGKILL");
|
|
162
|
-
} catch {
|
|
163
|
-
|
|
163
|
+
} catch (error) {
|
|
164
|
+
logger.error("Failed to force kill process:", error);
|
|
164
165
|
}
|
|
165
166
|
}
|
|
166
167
|
}, 1000);
|
|
@@ -183,8 +184,8 @@ export class BackgroundBashManager {
|
|
|
183
184
|
shell.runtime = Date.now() - shell.startTime;
|
|
184
185
|
this.notifyShellsChange();
|
|
185
186
|
return true;
|
|
186
|
-
} catch {
|
|
187
|
-
|
|
187
|
+
} catch (directKillError) {
|
|
188
|
+
logger.error("Failed to kill child process:", directKillError);
|
|
188
189
|
return false;
|
|
189
190
|
}
|
|
190
191
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import {
|
|
9
9
|
type HookEvent,
|
|
10
10
|
type HookEventConfig,
|
|
11
|
-
type
|
|
11
|
+
type WaveConfiguration,
|
|
12
12
|
type PartialHookConfiguration,
|
|
13
13
|
type HookExecutionContext,
|
|
14
14
|
type ExtendedHookExecutionContext,
|
|
@@ -18,50 +18,26 @@ import {
|
|
|
18
18
|
isValidHookEvent,
|
|
19
19
|
isValidHookEventConfig,
|
|
20
20
|
} from "../types/hooks.js";
|
|
21
|
-
import {
|
|
21
|
+
import { HookMatcher } from "../utils/hookMatcher.js";
|
|
22
22
|
import {
|
|
23
23
|
executeCommand,
|
|
24
24
|
isCommandSafe,
|
|
25
|
-
|
|
25
|
+
loadMergedWaveConfig,
|
|
26
26
|
} from "../services/hook.js";
|
|
27
27
|
import type { Logger } from "../types/index.js";
|
|
28
|
+
import { MessageSource } from "../types/index.js";
|
|
28
29
|
import type { MessageManager } from "./messageManager.js";
|
|
29
30
|
|
|
30
|
-
export
|
|
31
|
-
// Load configuration from settings
|
|
32
|
-
loadConfiguration(
|
|
33
|
-
userHooks?: PartialHookConfiguration,
|
|
34
|
-
projectHooks?: PartialHookConfiguration,
|
|
35
|
-
): void;
|
|
36
|
-
|
|
37
|
-
// Load configuration from filesystem settings
|
|
38
|
-
loadConfigurationFromSettings(): void;
|
|
39
|
-
|
|
40
|
-
// Execute hooks for specific event
|
|
41
|
-
executeHooks(
|
|
42
|
-
event: HookEvent,
|
|
43
|
-
context: HookExecutionContext | ExtendedHookExecutionContext,
|
|
44
|
-
): Promise<HookExecutionResult[]>;
|
|
45
|
-
|
|
46
|
-
// Check if hooks are configured for event
|
|
47
|
-
hasHooks(event: HookEvent, toolName?: string): boolean;
|
|
48
|
-
|
|
49
|
-
// Validate hook configuration
|
|
50
|
-
validateConfiguration(config: HookConfiguration): ValidationResult;
|
|
51
|
-
|
|
52
|
-
// Get current configuration
|
|
53
|
-
getConfiguration(): PartialHookConfiguration | undefined;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export class HookManager implements IHookManager {
|
|
31
|
+
export class HookManager {
|
|
57
32
|
private configuration: PartialHookConfiguration | undefined;
|
|
58
|
-
private
|
|
33
|
+
private environmentVars: Record<string, string> | undefined;
|
|
34
|
+
private readonly matcher: HookMatcher;
|
|
59
35
|
private readonly logger?: Logger;
|
|
60
36
|
private readonly workdir: string;
|
|
61
37
|
|
|
62
38
|
constructor(
|
|
63
39
|
workdir: string,
|
|
64
|
-
matcher:
|
|
40
|
+
matcher: HookMatcher = new HookMatcher(),
|
|
65
41
|
logger?: Logger,
|
|
66
42
|
) {
|
|
67
43
|
this.workdir = workdir;
|
|
@@ -103,18 +79,25 @@ export class HookManager implements IHookManager {
|
|
|
103
79
|
|
|
104
80
|
/**
|
|
105
81
|
* Load configuration from filesystem settings
|
|
106
|
-
* Automatically loads and merges user and project hooks
|
|
82
|
+
* Automatically loads and merges user and project Wave configuration (hooks + environment)
|
|
107
83
|
*/
|
|
108
84
|
loadConfigurationFromSettings(): void {
|
|
109
85
|
try {
|
|
110
86
|
this.logger?.debug(`[HookManager] Loading configuration...`);
|
|
111
|
-
const
|
|
112
|
-
this.logger?.debug(
|
|
113
|
-
|
|
87
|
+
const mergedWaveConfig = loadMergedWaveConfig(this.workdir);
|
|
88
|
+
this.logger?.debug(
|
|
89
|
+
`[HookManager] Merged config result:`,
|
|
90
|
+
mergedWaveConfig,
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
this.configuration = mergedWaveConfig?.hooks || undefined;
|
|
94
|
+
this.environmentVars = mergedWaveConfig?.env || undefined;
|
|
114
95
|
|
|
115
96
|
// Validate the loaded configuration if it exists
|
|
116
|
-
if (
|
|
117
|
-
const validation = this.validatePartialConfiguration(
|
|
97
|
+
if (mergedWaveConfig?.hooks) {
|
|
98
|
+
const validation = this.validatePartialConfiguration(
|
|
99
|
+
mergedWaveConfig.hooks,
|
|
100
|
+
);
|
|
118
101
|
if (!validation.valid) {
|
|
119
102
|
throw new HookConfigurationError(
|
|
120
103
|
"filesystem settings",
|
|
@@ -124,11 +107,12 @@ export class HookManager implements IHookManager {
|
|
|
124
107
|
}
|
|
125
108
|
|
|
126
109
|
this.logger?.debug(
|
|
127
|
-
`[HookManager] Configuration loaded successfully with ${Object.keys(
|
|
110
|
+
`[HookManager] Configuration loaded successfully with ${Object.keys(mergedWaveConfig?.hooks || {}).length} event types and ${Object.keys(this.environmentVars || {}).length} environment variables`,
|
|
128
111
|
);
|
|
129
112
|
} catch (error) {
|
|
130
113
|
// If loading fails, start with undefined configuration (no hooks)
|
|
131
114
|
this.configuration = undefined;
|
|
115
|
+
this.environmentVars = undefined;
|
|
132
116
|
|
|
133
117
|
// Re-throw configuration errors, but handle file system errors gracefully
|
|
134
118
|
if (error instanceof HookConfigurationError) {
|
|
@@ -219,7 +203,12 @@ export class HookManager implements IHookManager {
|
|
|
219
203
|
`[HookManager] Executing command ${commandIndex + 1}/${config.hooks.length} in configuration ${configIndex + 1}`,
|
|
220
204
|
);
|
|
221
205
|
|
|
222
|
-
const result = await executeCommand(
|
|
206
|
+
const result = await executeCommand(
|
|
207
|
+
hookCommand.command,
|
|
208
|
+
context,
|
|
209
|
+
undefined,
|
|
210
|
+
this.environmentVars,
|
|
211
|
+
);
|
|
223
212
|
results.push(result);
|
|
224
213
|
|
|
225
214
|
// Report individual command result
|
|
@@ -228,7 +217,7 @@ export class HookManager implements IHookManager {
|
|
|
228
217
|
`[HookManager] Command ${commandIndex + 1} completed successfully in ${result.duration}ms`,
|
|
229
218
|
);
|
|
230
219
|
} else {
|
|
231
|
-
this.logger?.
|
|
220
|
+
this.logger?.debug(
|
|
232
221
|
`[HookManager] Command ${commandIndex + 1} failed in ${result.duration}ms (exit code: ${result.exitCode}, timed out: ${result.timedOut})`,
|
|
233
222
|
);
|
|
234
223
|
}
|
|
@@ -274,7 +263,7 @@ export class HookManager implements IHookManager {
|
|
|
274
263
|
results: HookExecutionResult[],
|
|
275
264
|
messageManager?: MessageManager,
|
|
276
265
|
toolId?: string,
|
|
277
|
-
|
|
266
|
+
toolParameters?: string,
|
|
278
267
|
): {
|
|
279
268
|
shouldBlock: boolean;
|
|
280
269
|
errorMessage?: string;
|
|
@@ -293,7 +282,7 @@ export class HookManager implements IHookManager {
|
|
|
293
282
|
result,
|
|
294
283
|
messageManager,
|
|
295
284
|
toolId,
|
|
296
|
-
|
|
285
|
+
toolParameters,
|
|
297
286
|
);
|
|
298
287
|
}
|
|
299
288
|
}
|
|
@@ -327,7 +316,10 @@ export class HookManager implements IHookManager {
|
|
|
327
316
|
): void {
|
|
328
317
|
if (event === "UserPromptSubmit" && result.stdout?.trim()) {
|
|
329
318
|
// Inject stdout as user message context for UserPromptSubmit
|
|
330
|
-
messageManager.addUserMessage(
|
|
319
|
+
messageManager.addUserMessage({
|
|
320
|
+
content: result.stdout.trim(),
|
|
321
|
+
source: MessageSource.HOOK,
|
|
322
|
+
});
|
|
331
323
|
}
|
|
332
324
|
// For other hook types (PreToolUse, PostToolUse, Stop), ignore stdout
|
|
333
325
|
}
|
|
@@ -340,7 +332,7 @@ export class HookManager implements IHookManager {
|
|
|
340
332
|
result: HookExecutionResult,
|
|
341
333
|
messageManager: MessageManager,
|
|
342
334
|
toolId?: string,
|
|
343
|
-
|
|
335
|
+
toolParameters?: string,
|
|
344
336
|
): {
|
|
345
337
|
shouldBlock: boolean;
|
|
346
338
|
errorMessage?: string;
|
|
@@ -361,28 +353,30 @@ export class HookManager implements IHookManager {
|
|
|
361
353
|
// Block tool execution and show error to Wave Agent via tool block
|
|
362
354
|
if (toolId) {
|
|
363
355
|
messageManager.updateToolBlock({
|
|
364
|
-
toolId,
|
|
356
|
+
id: toolId,
|
|
357
|
+
parameters: toolParameters || "",
|
|
365
358
|
result: errorMessage,
|
|
366
359
|
success: false,
|
|
367
360
|
error: "Hook blocked tool execution",
|
|
361
|
+
stage: "end", // Hook blocking results in end stage with error
|
|
368
362
|
});
|
|
369
363
|
}
|
|
370
364
|
return { shouldBlock: true };
|
|
371
365
|
|
|
372
366
|
case "PostToolUse":
|
|
373
|
-
// Show error to Wave Agent via
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
success: false,
|
|
379
|
-
});
|
|
380
|
-
}
|
|
367
|
+
// Show error to Wave Agent via user message and allow AI to continue
|
|
368
|
+
messageManager.addUserMessage({
|
|
369
|
+
content: errorMessage,
|
|
370
|
+
source: MessageSource.HOOK,
|
|
371
|
+
});
|
|
381
372
|
return { shouldBlock: false };
|
|
382
373
|
|
|
383
374
|
case "Stop":
|
|
384
375
|
// Show error to Wave Agent via user message and block stopping to continue conversation
|
|
385
|
-
messageManager.addUserMessage(
|
|
376
|
+
messageManager.addUserMessage({
|
|
377
|
+
content: errorMessage,
|
|
378
|
+
source: MessageSource.HOOK,
|
|
379
|
+
});
|
|
386
380
|
return { shouldBlock: true, errorMessage };
|
|
387
381
|
|
|
388
382
|
default:
|
|
@@ -416,46 +410,62 @@ export class HookManager implements IHookManager {
|
|
|
416
410
|
}
|
|
417
411
|
|
|
418
412
|
/**
|
|
419
|
-
* Validate
|
|
413
|
+
* Validate Wave configuration structure and content
|
|
420
414
|
*/
|
|
421
|
-
validateConfiguration(config:
|
|
415
|
+
validateConfiguration(config: WaveConfiguration): ValidationResult {
|
|
422
416
|
const errors: string[] = [];
|
|
423
417
|
|
|
424
418
|
if (!config || typeof config !== "object") {
|
|
425
419
|
return { valid: false, errors: ["Configuration must be an object"] };
|
|
426
420
|
}
|
|
427
421
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
errors
|
|
432
|
-
}
|
|
433
|
-
|
|
422
|
+
// Validate hooks if present
|
|
423
|
+
if (config.hooks) {
|
|
424
|
+
if (typeof config.hooks !== "object") {
|
|
425
|
+
errors.push("hooks property must be an object");
|
|
426
|
+
} else {
|
|
427
|
+
// Validate each hook event
|
|
428
|
+
for (const [eventName, eventConfigs] of Object.entries(config.hooks)) {
|
|
429
|
+
// Validate event name
|
|
430
|
+
if (!isValidHookEvent(eventName)) {
|
|
431
|
+
errors.push(`Invalid hook event: ${eventName}`);
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
434
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
435
|
+
// Validate event configurations
|
|
436
|
+
if (!Array.isArray(eventConfigs)) {
|
|
437
|
+
errors.push(
|
|
438
|
+
`Hook event ${eventName} must be an array of configurations`,
|
|
439
|
+
);
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
442
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
443
|
+
eventConfigs.forEach((eventConfig, index) => {
|
|
444
|
+
const configErrors = this.validateEventConfig(
|
|
445
|
+
eventName as HookEvent,
|
|
446
|
+
eventConfig,
|
|
447
|
+
index,
|
|
448
|
+
);
|
|
449
|
+
errors.push(...configErrors);
|
|
450
|
+
});
|
|
451
|
+
}
|
|
449
452
|
}
|
|
453
|
+
}
|
|
450
454
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
)
|
|
457
|
-
|
|
458
|
-
|
|
455
|
+
// Validate environment variables if present
|
|
456
|
+
if (config.env) {
|
|
457
|
+
if (typeof config.env !== "object" || Array.isArray(config.env)) {
|
|
458
|
+
errors.push("env property must be an object");
|
|
459
|
+
} else {
|
|
460
|
+
for (const [key, value] of Object.entries(config.env)) {
|
|
461
|
+
if (typeof key !== "string" || key.trim() === "") {
|
|
462
|
+
errors.push(`Invalid environment variable key: ${key}`);
|
|
463
|
+
}
|
|
464
|
+
if (typeof value !== "string") {
|
|
465
|
+
errors.push(`Environment variable ${key} must have a string value`);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
459
469
|
}
|
|
460
470
|
|
|
461
471
|
return {
|
|
@@ -518,6 +528,16 @@ export class HookManager implements IHookManager {
|
|
|
518
528
|
return JSON.parse(JSON.stringify(this.configuration));
|
|
519
529
|
}
|
|
520
530
|
|
|
531
|
+
/**
|
|
532
|
+
* Get current environment variables
|
|
533
|
+
*/
|
|
534
|
+
getEnvironmentVars(): Record<string, string> | undefined {
|
|
535
|
+
if (!this.environmentVars) return undefined;
|
|
536
|
+
|
|
537
|
+
// Deep clone to prevent external modification
|
|
538
|
+
return JSON.parse(JSON.stringify(this.environmentVars));
|
|
539
|
+
}
|
|
540
|
+
|
|
521
541
|
/**
|
|
522
542
|
* Clear current configuration
|
|
523
543
|
*/
|
|
@@ -718,6 +738,7 @@ export class HookManager implements IHookManager {
|
|
|
718
738
|
PostToolUse: 0,
|
|
719
739
|
UserPromptSubmit: 0,
|
|
720
740
|
Stop: 0,
|
|
741
|
+
SubagentStop: 0,
|
|
721
742
|
},
|
|
722
743
|
};
|
|
723
744
|
}
|
|
@@ -727,6 +748,7 @@ export class HookManager implements IHookManager {
|
|
|
727
748
|
PostToolUse: 0,
|
|
728
749
|
UserPromptSubmit: 0,
|
|
729
750
|
Stop: 0,
|
|
751
|
+
SubagentStop: 0,
|
|
730
752
|
};
|
|
731
753
|
|
|
732
754
|
let totalConfigs = 0;
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live Configuration Manager
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates live configuration reload functionality including:
|
|
5
|
+
* - Hook configuration watching and reloading
|
|
6
|
+
* - Memory store management for AGENTS.md files
|
|
7
|
+
* - Coordination between file watchers and configuration updates
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { Logger } from "../types/index.js";
|
|
11
|
+
import {
|
|
12
|
+
ConfigurationWatcher,
|
|
13
|
+
type ConfigurationChangeEvent,
|
|
14
|
+
} from "../services/configurationWatcher.js";
|
|
15
|
+
|
|
16
|
+
import { type FileWatchEvent } from "../services/fileWatcher.js";
|
|
17
|
+
import { configResolver } from "../utils/configResolver.js";
|
|
18
|
+
import { join } from "path";
|
|
19
|
+
import {
|
|
20
|
+
getUserConfigPaths,
|
|
21
|
+
getProjectConfigPaths,
|
|
22
|
+
} from "../utils/configPaths.js";
|
|
23
|
+
import { CONFIGURATION_EVENTS } from "../constants/events.js";
|
|
24
|
+
|
|
25
|
+
export interface LiveConfigManagerOptions {
|
|
26
|
+
workdir: string;
|
|
27
|
+
logger?: Logger;
|
|
28
|
+
onConfigurationChanged?: () => void; // Callback for when configuration changes
|
|
29
|
+
onMemoryStoreFileChanged?: (
|
|
30
|
+
filePath: string,
|
|
31
|
+
changeType: "add" | "change" | "unlink",
|
|
32
|
+
) => Promise<void>; // Callback for memory store file changes
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export class LiveConfigManager {
|
|
36
|
+
private readonly workdir: string;
|
|
37
|
+
private readonly logger?: Logger;
|
|
38
|
+
private readonly onConfigurationChanged?: () => void;
|
|
39
|
+
private readonly onMemoryStoreFileChanged?: (
|
|
40
|
+
filePath: string,
|
|
41
|
+
changeType: "add" | "change" | "unlink",
|
|
42
|
+
) => Promise<void>;
|
|
43
|
+
private configurationWatcher?: ConfigurationWatcher;
|
|
44
|
+
private isInitialized: boolean = false;
|
|
45
|
+
|
|
46
|
+
constructor(options: LiveConfigManagerOptions) {
|
|
47
|
+
this.workdir = options.workdir;
|
|
48
|
+
this.logger = options.logger;
|
|
49
|
+
this.onConfigurationChanged = options.onConfigurationChanged;
|
|
50
|
+
this.onMemoryStoreFileChanged = options.onMemoryStoreFileChanged;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Initialize live configuration management
|
|
55
|
+
*/
|
|
56
|
+
async initialize(): Promise<void> {
|
|
57
|
+
if (this.isInitialized) {
|
|
58
|
+
this.logger?.debug("[LiveConfigManager] Already initialized");
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
// Initialize configuration watcher for hook settings
|
|
64
|
+
await this.initializeConfigurationWatcher();
|
|
65
|
+
|
|
66
|
+
// Initialize memory store watching for AGENTS.md if callback is available
|
|
67
|
+
if (this.onMemoryStoreFileChanged) {
|
|
68
|
+
await this.initializeMemoryStoreWatching();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this.isInitialized = true;
|
|
72
|
+
this.logger?.info(
|
|
73
|
+
"Live Config: Live configuration management initialized successfully",
|
|
74
|
+
);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
this.logger?.error(
|
|
77
|
+
`Live Config: Failed to initialize: ${(error as Error).message}`,
|
|
78
|
+
);
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Shutdown live configuration management
|
|
85
|
+
*/
|
|
86
|
+
async shutdown(): Promise<void> {
|
|
87
|
+
if (!this.isInitialized) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
if (this.configurationWatcher) {
|
|
93
|
+
await this.configurationWatcher.shutdown();
|
|
94
|
+
this.configurationWatcher = undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
this.isInitialized = false;
|
|
98
|
+
this.logger?.info(
|
|
99
|
+
"Live Config: Live configuration management shutdown completed",
|
|
100
|
+
);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
this.logger?.error(
|
|
103
|
+
`Live Config: Error during shutdown: ${(error as Error).message}`,
|
|
104
|
+
);
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Initialize configuration watcher for hook settings
|
|
111
|
+
*/
|
|
112
|
+
private async initializeConfigurationWatcher(): Promise<void> {
|
|
113
|
+
this.configurationWatcher = new ConfigurationWatcher(
|
|
114
|
+
this.workdir,
|
|
115
|
+
this.logger,
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// Set up configuration change handler using EventEmitter pattern
|
|
119
|
+
this.configurationWatcher.on(
|
|
120
|
+
CONFIGURATION_EVENTS.CONFIGURATION_CHANGE,
|
|
121
|
+
(event: ConfigurationChangeEvent) => {
|
|
122
|
+
this.handleConfigurationChange(event);
|
|
123
|
+
},
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
// Initialize watching for user and project settings
|
|
127
|
+
const { userPaths, projectPaths } = this.getConfigurationPaths();
|
|
128
|
+
await this.configurationWatcher.initializeWatching(userPaths, projectPaths);
|
|
129
|
+
this.logger?.info("Live Config: Configuration watching initialized");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Initialize memory store watching for AGENTS.md files
|
|
134
|
+
*/
|
|
135
|
+
private async initializeMemoryStoreWatching(): Promise<void> {
|
|
136
|
+
if (!this.onMemoryStoreFileChanged || !this.configurationWatcher) {
|
|
137
|
+
this.logger?.debug(
|
|
138
|
+
"Live Config: Memory store callback or configuration watcher not available, skipping AGENTS.md watching",
|
|
139
|
+
);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
const agentsFilePath = join(this.workdir, "AGENTS.md");
|
|
145
|
+
|
|
146
|
+
// Add AGENTS.md to file watcher
|
|
147
|
+
await this.configurationWatcher.watchAdditionalFile(
|
|
148
|
+
agentsFilePath,
|
|
149
|
+
async (event: FileWatchEvent) => {
|
|
150
|
+
await this.handleMemoryStoreFileChange(event);
|
|
151
|
+
},
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
this.logger?.info("Live Config: AGENTS.md file watching initialized");
|
|
155
|
+
} catch (error) {
|
|
156
|
+
this.logger?.warn(
|
|
157
|
+
`Live Config: Failed to initialize AGENTS.md watching: ${(error as Error).message}`,
|
|
158
|
+
);
|
|
159
|
+
// Don't throw - memory optimization is not critical for core functionality
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Handle configuration change events
|
|
165
|
+
*/
|
|
166
|
+
private handleConfigurationChange(event: ConfigurationChangeEvent): void {
|
|
167
|
+
this.logger?.info(
|
|
168
|
+
`Live Config: Configuration change detected: ${event.type} at ${event.path}`,
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
// Invalidate and refresh configuration cache for live environment variable updates
|
|
172
|
+
configResolver.invalidateCache(this.workdir);
|
|
173
|
+
configResolver.refreshCache(this.workdir);
|
|
174
|
+
|
|
175
|
+
// Trigger Agent configuration update callback if provided
|
|
176
|
+
if (this.onConfigurationChanged) {
|
|
177
|
+
try {
|
|
178
|
+
this.logger?.info("Live Config: Triggering Agent configuration update");
|
|
179
|
+
this.onConfigurationChanged();
|
|
180
|
+
} catch (error) {
|
|
181
|
+
this.logger?.error(
|
|
182
|
+
`Live Config: Error in configuration change callback: ${(error as Error).message}`,
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Log cache status after refresh
|
|
188
|
+
const cacheStatus = configResolver.getCacheStatus();
|
|
189
|
+
if (cacheStatus) {
|
|
190
|
+
this.logger?.info(
|
|
191
|
+
`Live Config: Configuration cache refreshed - ${cacheStatus.envVarCount} environment variables loaded`,
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Handle AGENTS.md file change events
|
|
198
|
+
*/
|
|
199
|
+
private async handleMemoryStoreFileChange(
|
|
200
|
+
event: FileWatchEvent,
|
|
201
|
+
): Promise<void> {
|
|
202
|
+
if (!this.onMemoryStoreFileChanged) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
this.logger?.info(
|
|
208
|
+
`Live Config: AGENTS.md ${event.type} detected: ${event.path}`,
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
const changeType: "add" | "change" | "unlink" =
|
|
212
|
+
event.type === "delete"
|
|
213
|
+
? "unlink"
|
|
214
|
+
: event.type === "create"
|
|
215
|
+
? "add"
|
|
216
|
+
: "change";
|
|
217
|
+
await this.onMemoryStoreFileChanged(event.path, changeType);
|
|
218
|
+
|
|
219
|
+
this.logger?.info(
|
|
220
|
+
`Live Config: Memory store updated for AGENTS.md ${event.type}`,
|
|
221
|
+
);
|
|
222
|
+
} catch (error) {
|
|
223
|
+
this.logger?.error(
|
|
224
|
+
`Live Config: Failed to handle AGENTS.md file change: ${(error as Error).message}`,
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Get initialization status
|
|
231
|
+
*/
|
|
232
|
+
get initialized(): boolean {
|
|
233
|
+
return this.isInitialized;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Get configuration file paths for user and project settings
|
|
238
|
+
* Returns paths in priority order (local.json first, then .json)
|
|
239
|
+
*/
|
|
240
|
+
private getConfigurationPaths(): {
|
|
241
|
+
userPaths: string[];
|
|
242
|
+
projectPaths: string[];
|
|
243
|
+
} {
|
|
244
|
+
const userPaths = getUserConfigPaths();
|
|
245
|
+
const projectPaths = getProjectConfigPaths(this.workdir);
|
|
246
|
+
return { userPaths, projectPaths };
|
|
247
|
+
}
|
|
248
|
+
}
|