wave-agent-sdk 0.0.7 → 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 +202 -20
- 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 +243 -128
- package/dist/managers/backgroundBashManager.d.ts.map +1 -1
- package/dist/managers/backgroundBashManager.js +7 -6
- package/dist/managers/hookManager.d.ts +9 -4
- package/dist/managers/hookManager.d.ts.map +1 -1
- package/dist/managers/hookManager.js +62 -30
- 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 +38 -13
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +163 -30
- package/dist/managers/slashCommandManager.d.ts.map +1 -1
- package/dist/managers/slashCommandManager.js +4 -1
- package/dist/managers/subagentManager.d.ts +51 -0
- package/dist/managers/subagentManager.d.ts.map +1 -1
- package/dist/managers/subagentManager.js +189 -18
- 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/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 +14 -2
- package/dist/types/messaging.d.ts.map +1 -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 +7 -5
- 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/mcpUtils.d.ts.map +1 -1
- package/dist/utils/mcpUtils.js +25 -3
- package/dist/utils/messageOperations.d.ts +20 -8
- package/dist/utils/messageOperations.d.ts.map +1 -1
- package/dist/utils/messageOperations.js +25 -16
- 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 +298 -34
- package/src/constants/events.ts +38 -0
- package/src/index.ts +2 -0
- package/src/managers/aiManager.ts +323 -170
- package/src/managers/backgroundBashManager.ts +7 -6
- package/src/managers/hookManager.ts +83 -40
- package/src/managers/liveConfigManager.ts +248 -0
- package/src/managers/messageManager.ts +230 -63
- package/src/managers/slashCommandManager.ts +4 -1
- package/src/managers/subagentManager.ts +283 -21
- 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/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 +14 -2
- 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 +7 -5
- package/src/utils/customCommands.ts +90 -22
- package/src/utils/fileUtils.ts +65 -0
- package/src/utils/globalLogger.ts +145 -0
- package/src/utils/mcpUtils.ts +34 -3
- package/src/utils/messageOperations.ts +42 -20
- package/src/utils/pathEncoder.ts +379 -0
- package/src/utils/subagentParser.ts +2 -1
- package/src/utils/tokenCalculation.ts +43 -0
package/dist/services/hook.js
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { spawn } from "child_process";
|
|
8
8
|
import { existsSync, readFileSync } from "fs";
|
|
9
|
-
import {
|
|
10
|
-
import { homedir } from "os";
|
|
9
|
+
import { getUserConfigPath, getProjectConfigPath, getUserConfigPaths, getProjectConfigPaths, hasAnyConfig, getConfigurationInfo, } from "../utils/configPaths.js";
|
|
11
10
|
import { getSessionFilePath, isValidHookEvent, } from "../types/hooks.js";
|
|
11
|
+
import { isValidEnvironmentVars, } from "../types/environment.js";
|
|
12
12
|
// =============================================================================
|
|
13
13
|
// Hook Execution Functions
|
|
14
14
|
// =============================================================================
|
|
@@ -39,12 +39,16 @@ function buildHookJsonInput(context) {
|
|
|
39
39
|
context.userPrompt !== undefined) {
|
|
40
40
|
jsonInput.user_prompt = context.userPrompt;
|
|
41
41
|
}
|
|
42
|
+
// Add subagent_type if present
|
|
43
|
+
if (context.subagentType !== undefined) {
|
|
44
|
+
jsonInput.subagent_type = context.subagentType;
|
|
45
|
+
}
|
|
42
46
|
return jsonInput;
|
|
43
47
|
}
|
|
44
48
|
/**
|
|
45
49
|
* Execute a single hook command
|
|
46
50
|
*/
|
|
47
|
-
export async function executeCommand(command, context, options) {
|
|
51
|
+
export async function executeCommand(command, context, options, additionalEnvVars) {
|
|
48
52
|
const defaultTimeout = 10000; // 10 seconds
|
|
49
53
|
const maxTimeout = 300000; // 5 minutes
|
|
50
54
|
const skipExecution = process.env.NODE_ENV === "test" &&
|
|
@@ -75,6 +79,7 @@ export async function executeCommand(command, context, options) {
|
|
|
75
79
|
cwd: context.projectDir,
|
|
76
80
|
env: {
|
|
77
81
|
...process.env,
|
|
82
|
+
...additionalEnvVars, // Merge additional environment variables from Wave configuration
|
|
78
83
|
HOOK_EVENT: context.event,
|
|
79
84
|
HOOK_TOOL_NAME: context.toolName || "",
|
|
80
85
|
HOOK_PROJECT_DIR: context.projectDir,
|
|
@@ -148,10 +153,10 @@ export async function executeCommand(command, context, options) {
|
|
|
148
153
|
/**
|
|
149
154
|
* Execute multiple commands in sequence
|
|
150
155
|
*/
|
|
151
|
-
export async function executeCommands(commands, context, options) {
|
|
156
|
+
export async function executeCommands(commands, context, options, additionalEnvVars) {
|
|
152
157
|
const results = [];
|
|
153
158
|
for (const command of commands) {
|
|
154
|
-
const result = await executeCommand(command, context, options);
|
|
159
|
+
const result = await executeCommand(command, context, options, additionalEnvVars);
|
|
155
160
|
results.push(result);
|
|
156
161
|
// Stop on first failure unless continueOnFailure is set
|
|
157
162
|
if (!result.success && !options?.continueOnFailure) {
|
|
@@ -182,53 +187,381 @@ export function isCommandSafe(command) {
|
|
|
182
187
|
return !dangerousPatterns.some((pattern) => pattern.test(trimmed.toLowerCase()));
|
|
183
188
|
}
|
|
184
189
|
// =============================================================================
|
|
185
|
-
//
|
|
190
|
+
// Environment Variable Functions
|
|
191
|
+
// =============================================================================
|
|
192
|
+
/**
|
|
193
|
+
* Validate environment variable configuration
|
|
194
|
+
*/
|
|
195
|
+
export function validateEnvironmentConfig(env, configPath) {
|
|
196
|
+
const result = {
|
|
197
|
+
isValid: true,
|
|
198
|
+
errors: [],
|
|
199
|
+
warnings: [],
|
|
200
|
+
};
|
|
201
|
+
// Check if env is defined
|
|
202
|
+
if (env === undefined || env === null) {
|
|
203
|
+
return result; // undefined/null env is valid (means no env vars)
|
|
204
|
+
}
|
|
205
|
+
// Validate that env is a Record<string, string>
|
|
206
|
+
if (!isValidEnvironmentVars(env)) {
|
|
207
|
+
result.isValid = false;
|
|
208
|
+
result.errors.push(`Invalid env field format${configPath ? ` in ${configPath}` : ""}. Environment variables must be a Record<string, string>.`);
|
|
209
|
+
return result;
|
|
210
|
+
}
|
|
211
|
+
// Additional validation for environment variable names
|
|
212
|
+
const envVars = env;
|
|
213
|
+
for (const [key, value] of Object.entries(envVars)) {
|
|
214
|
+
// Check for valid environment variable naming convention
|
|
215
|
+
if (!/^[A-Z_][A-Z0-9_]*$/i.test(key)) {
|
|
216
|
+
result.warnings.push(`Environment variable '${key}' does not follow standard naming convention (alphanumeric and underscores only).`);
|
|
217
|
+
}
|
|
218
|
+
// Check for empty values
|
|
219
|
+
if (value === "") {
|
|
220
|
+
result.warnings.push(`Environment variable '${key}' has an empty value.`);
|
|
221
|
+
}
|
|
222
|
+
// Check for reserved variable names that might cause conflicts
|
|
223
|
+
const reservedNames = [
|
|
224
|
+
"PATH",
|
|
225
|
+
"HOME",
|
|
226
|
+
"USER",
|
|
227
|
+
"PWD",
|
|
228
|
+
"SHELL",
|
|
229
|
+
"TERM",
|
|
230
|
+
"NODE_ENV",
|
|
231
|
+
];
|
|
232
|
+
if (reservedNames.includes(key.toUpperCase())) {
|
|
233
|
+
result.warnings.push(`Environment variable '${key}' overrides a system variable, which may cause unexpected behavior.`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return result;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Merge environment configurations with project taking precedence over user
|
|
240
|
+
*/
|
|
241
|
+
export function mergeEnvironmentConfig(userEnv, projectEnv, options = {}) {
|
|
242
|
+
const userVars = userEnv || {};
|
|
243
|
+
const projectVars = projectEnv || {};
|
|
244
|
+
const mergedVars = {};
|
|
245
|
+
const conflicts = [];
|
|
246
|
+
// Start with user environment variables
|
|
247
|
+
Object.assign(mergedVars, userVars);
|
|
248
|
+
// Override with project environment variables and track conflicts
|
|
249
|
+
for (const [key, projectValue] of Object.entries(projectVars)) {
|
|
250
|
+
const userValue = userVars[key];
|
|
251
|
+
if (userValue !== undefined &&
|
|
252
|
+
userValue !== projectValue &&
|
|
253
|
+
options.includeConflictWarnings !== false) {
|
|
254
|
+
// Conflict detected - project value takes precedence
|
|
255
|
+
conflicts.push({
|
|
256
|
+
key,
|
|
257
|
+
userValue,
|
|
258
|
+
projectValue,
|
|
259
|
+
resolvedValue: projectValue,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
mergedVars[key] = projectValue;
|
|
263
|
+
}
|
|
264
|
+
return {
|
|
265
|
+
userVars,
|
|
266
|
+
projectVars,
|
|
267
|
+
mergedVars,
|
|
268
|
+
conflicts,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
// =============================================================================
|
|
272
|
+
// Hook Settings Functions (using centralized config path utilities)
|
|
186
273
|
// =============================================================================
|
|
187
274
|
/**
|
|
188
|
-
* Get the user-specific hooks configuration file path
|
|
275
|
+
* Get the user-specific hooks configuration file path (legacy function)
|
|
276
|
+
* @deprecated Use getUserConfigPaths() from configPaths.ts for better priority support
|
|
189
277
|
*/
|
|
190
278
|
export function getUserHooksConfigPath() {
|
|
191
|
-
return
|
|
279
|
+
return getUserConfigPath();
|
|
192
280
|
}
|
|
193
281
|
/**
|
|
194
|
-
* Get the project-specific hooks configuration file path
|
|
282
|
+
* Get the project-specific hooks configuration file path (legacy function)
|
|
283
|
+
* @deprecated Use getProjectConfigPaths() from configPaths.ts for better priority support
|
|
195
284
|
*/
|
|
196
285
|
export function getProjectHooksConfigPath(workdir) {
|
|
197
|
-
return
|
|
286
|
+
return getProjectConfigPath(workdir);
|
|
198
287
|
}
|
|
199
288
|
/**
|
|
200
|
-
*
|
|
289
|
+
* Get the user-specific hooks configuration file paths in priority order
|
|
290
|
+
* @deprecated Use getUserConfigPaths() from configPaths.ts directly
|
|
201
291
|
*/
|
|
202
|
-
export function
|
|
292
|
+
export function getUserHooksConfigPaths() {
|
|
293
|
+
return getUserConfigPaths();
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Get the project-specific hooks configuration file paths in priority order
|
|
297
|
+
* @deprecated Use getProjectConfigPaths() from configPaths.ts directly
|
|
298
|
+
*/
|
|
299
|
+
export function getProjectHooksConfigPaths(workdir) {
|
|
300
|
+
return getProjectConfigPaths(workdir);
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Load Wave configuration from a JSON file with graceful fallback
|
|
304
|
+
* This version is optimized for live reload scenarios where invalid config should not crash the system
|
|
305
|
+
*/
|
|
306
|
+
export function loadWaveConfigFromFileWithFallback(filePath, previousValidConfig) {
|
|
307
|
+
if (!existsSync(filePath)) {
|
|
308
|
+
return { config: null, usedFallback: false };
|
|
309
|
+
}
|
|
310
|
+
try {
|
|
311
|
+
const content = readFileSync(filePath, "utf-8");
|
|
312
|
+
const config = JSON.parse(content);
|
|
313
|
+
// Validate basic structure
|
|
314
|
+
if (!config || typeof config !== "object") {
|
|
315
|
+
const error = `Invalid configuration structure in ${filePath}`;
|
|
316
|
+
return {
|
|
317
|
+
config: previousValidConfig || null,
|
|
318
|
+
error,
|
|
319
|
+
usedFallback: !!previousValidConfig,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
// Validate environment variables if present
|
|
323
|
+
if (config.env !== undefined) {
|
|
324
|
+
const envValidation = validateEnvironmentConfig(config.env, filePath);
|
|
325
|
+
if (!envValidation.isValid) {
|
|
326
|
+
const error = `Environment variable validation failed in ${filePath}: ${envValidation.errors.join(", ")}`;
|
|
327
|
+
return {
|
|
328
|
+
config: previousValidConfig || null,
|
|
329
|
+
error,
|
|
330
|
+
usedFallback: !!previousValidConfig,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
// Log warnings if any
|
|
334
|
+
if (envValidation.warnings.length > 0) {
|
|
335
|
+
console.warn(`Environment variable warnings in ${filePath}:\n- ${envValidation.warnings.join("\n- ")}`);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
// Return valid configuration
|
|
339
|
+
return {
|
|
340
|
+
config: {
|
|
341
|
+
hooks: config.hooks || undefined,
|
|
342
|
+
env: config.env || undefined,
|
|
343
|
+
},
|
|
344
|
+
usedFallback: false,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
catch (error) {
|
|
348
|
+
let errorMessage;
|
|
349
|
+
if (error instanceof SyntaxError) {
|
|
350
|
+
errorMessage = `Invalid JSON syntax in ${filePath}: ${error.message}`;
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
errorMessage = `Error loading configuration from ${filePath}: ${error.message}`;
|
|
354
|
+
}
|
|
355
|
+
return {
|
|
356
|
+
config: previousValidConfig || null,
|
|
357
|
+
error: errorMessage,
|
|
358
|
+
usedFallback: !!previousValidConfig,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Load Wave configuration from multiple file paths in priority order
|
|
364
|
+
* Returns the first valid configuration found, or null if none exist
|
|
365
|
+
*/
|
|
366
|
+
export function loadWaveConfigFromFiles(filePaths) {
|
|
367
|
+
for (const filePath of filePaths) {
|
|
368
|
+
const config = loadWaveConfigFromFile(filePath);
|
|
369
|
+
if (config !== null) {
|
|
370
|
+
return config;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Load Wave configuration from multiple file paths with graceful fallback
|
|
377
|
+
* Returns the first valid configuration found with fallback support
|
|
378
|
+
*/
|
|
379
|
+
export function loadWaveConfigFromFilesWithFallback(filePaths, previousValidConfig) {
|
|
380
|
+
let lastError;
|
|
381
|
+
for (const filePath of filePaths) {
|
|
382
|
+
const result = loadWaveConfigFromFileWithFallback(filePath, previousValidConfig);
|
|
383
|
+
if (result.config !== null && !result.usedFallback) {
|
|
384
|
+
// Found a valid config at this path
|
|
385
|
+
return {
|
|
386
|
+
config: result.config,
|
|
387
|
+
error: result.error,
|
|
388
|
+
usedFallback: result.usedFallback,
|
|
389
|
+
usedPath: filePath,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
if (result.error) {
|
|
393
|
+
lastError = result.error;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
// No valid config found in any path
|
|
397
|
+
return {
|
|
398
|
+
config: previousValidConfig || null,
|
|
399
|
+
error: lastError,
|
|
400
|
+
usedFallback: !!previousValidConfig,
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Load and merge Wave configuration with graceful fallback for live reload
|
|
405
|
+
* Provides error recovery by falling back to previous valid configuration
|
|
406
|
+
*/
|
|
407
|
+
export function loadMergedWaveConfigWithFallback(workdir, previousValidConfig) {
|
|
408
|
+
const errors = [];
|
|
409
|
+
let usedFallback = false;
|
|
410
|
+
// Load user config with fallback (check .local.json first, then .json)
|
|
411
|
+
const userResult = loadWaveConfigFromFilesWithFallback(getUserHooksConfigPaths(), previousValidConfig);
|
|
412
|
+
if (userResult.error) {
|
|
413
|
+
errors.push(`User config: ${userResult.error}`);
|
|
414
|
+
}
|
|
415
|
+
if (userResult.usedFallback) {
|
|
416
|
+
usedFallback = true;
|
|
417
|
+
}
|
|
418
|
+
// Load project config with fallback (check .local.json first, then .json)
|
|
419
|
+
const projectResult = loadWaveConfigFromFilesWithFallback(getProjectHooksConfigPaths(workdir), previousValidConfig);
|
|
420
|
+
if (projectResult.error) {
|
|
421
|
+
errors.push(`Project config: ${projectResult.error}`);
|
|
422
|
+
}
|
|
423
|
+
if (projectResult.usedFallback) {
|
|
424
|
+
usedFallback = true;
|
|
425
|
+
}
|
|
426
|
+
const userConfig = userResult.config;
|
|
427
|
+
const projectConfig = projectResult.config;
|
|
428
|
+
// If both configs failed and no fallback available
|
|
429
|
+
if (!userConfig && !projectConfig && errors.length > 0) {
|
|
430
|
+
return {
|
|
431
|
+
config: previousValidConfig || null,
|
|
432
|
+
errors,
|
|
433
|
+
usedFallback: !!previousValidConfig,
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
// No configuration found at all
|
|
437
|
+
if (!userConfig && !projectConfig) {
|
|
438
|
+
return { config: null, errors, usedFallback };
|
|
439
|
+
}
|
|
440
|
+
// Only one configuration found
|
|
441
|
+
if (!userConfig)
|
|
442
|
+
return { config: projectConfig, errors, usedFallback };
|
|
443
|
+
if (!projectConfig)
|
|
444
|
+
return { config: userConfig, errors, usedFallback };
|
|
445
|
+
// Merge configurations (project overrides user)
|
|
446
|
+
try {
|
|
447
|
+
const mergedHooks = {};
|
|
448
|
+
// Merge environment variables using the new mergeEnvironmentConfig function
|
|
449
|
+
const environmentContext = mergeEnvironmentConfig(userConfig.env, projectConfig.env, { includeConflictWarnings: true });
|
|
450
|
+
// Merge hooks (combine arrays, project configs come after user configs)
|
|
451
|
+
const allEvents = new Set([
|
|
452
|
+
...Object.keys(userConfig.hooks || {}),
|
|
453
|
+
...Object.keys(projectConfig.hooks || {}),
|
|
454
|
+
]);
|
|
455
|
+
for (const event of allEvents) {
|
|
456
|
+
if (!isValidHookEvent(event))
|
|
457
|
+
continue;
|
|
458
|
+
const userEventConfigs = userConfig.hooks?.[event] || [];
|
|
459
|
+
const projectEventConfigs = projectConfig.hooks?.[event] || [];
|
|
460
|
+
// Project configurations take precedence
|
|
461
|
+
mergedHooks[event] = [...userEventConfigs, ...projectEventConfigs];
|
|
462
|
+
}
|
|
463
|
+
const mergedConfig = {
|
|
464
|
+
hooks: Object.keys(mergedHooks).length > 0 ? mergedHooks : undefined,
|
|
465
|
+
env: Object.keys(environmentContext.mergedVars).length > 0
|
|
466
|
+
? environmentContext.mergedVars
|
|
467
|
+
: undefined,
|
|
468
|
+
};
|
|
469
|
+
return { config: mergedConfig, errors, usedFallback };
|
|
470
|
+
}
|
|
471
|
+
catch (error) {
|
|
472
|
+
errors.push(`Merge error: ${error.message}`);
|
|
473
|
+
return {
|
|
474
|
+
config: previousValidConfig || null,
|
|
475
|
+
errors,
|
|
476
|
+
usedFallback: !!previousValidConfig,
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Load Wave configuration from a JSON file
|
|
482
|
+
* Supports both hooks and environment variables with proper validation
|
|
483
|
+
*/
|
|
484
|
+
export function loadWaveConfigFromFile(filePath) {
|
|
203
485
|
if (!existsSync(filePath)) {
|
|
204
486
|
return null;
|
|
205
487
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
488
|
+
try {
|
|
489
|
+
const content = readFileSync(filePath, "utf-8");
|
|
490
|
+
const config = JSON.parse(content);
|
|
491
|
+
// Validate basic structure
|
|
492
|
+
if (!config || typeof config !== "object") {
|
|
493
|
+
throw new Error(`Invalid configuration structure in ${filePath}`);
|
|
494
|
+
}
|
|
495
|
+
// Validate environment variables if present
|
|
496
|
+
if (config.env !== undefined) {
|
|
497
|
+
const envValidation = validateEnvironmentConfig(config.env, filePath);
|
|
498
|
+
if (!envValidation.isValid) {
|
|
499
|
+
throw new Error(`Environment variable validation failed in ${filePath}: ${envValidation.errors.join(", ")}`);
|
|
500
|
+
}
|
|
501
|
+
// Log warnings if any
|
|
502
|
+
if (envValidation.warnings.length > 0) {
|
|
503
|
+
console.warn(`Environment variable warnings in ${filePath}:\n- ${envValidation.warnings.join("\n- ")}`);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
return {
|
|
507
|
+
hooks: config.hooks || undefined,
|
|
508
|
+
env: config.env || undefined,
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
catch (error) {
|
|
512
|
+
if (error instanceof SyntaxError) {
|
|
513
|
+
throw new Error(`Invalid JSON syntax in ${filePath}: ${error.message}`);
|
|
514
|
+
}
|
|
515
|
+
// Re-throw validation errors and other errors as-is
|
|
516
|
+
throw error;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Load hooks configuration from a JSON file (legacy function)
|
|
521
|
+
*/
|
|
522
|
+
export function loadHooksConfigFromFile(filePath) {
|
|
523
|
+
const waveConfig = loadWaveConfigFromFile(filePath);
|
|
524
|
+
if (!waveConfig) {
|
|
525
|
+
return null;
|
|
211
526
|
}
|
|
212
|
-
return
|
|
527
|
+
return waveConfig.hooks || null;
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Load user-specific Wave configuration
|
|
531
|
+
* Checks .local.json first, then falls back to .json
|
|
532
|
+
*/
|
|
533
|
+
export function loadUserWaveConfig() {
|
|
534
|
+
return loadWaveConfigFromFiles(getUserHooksConfigPaths());
|
|
213
535
|
}
|
|
214
536
|
/**
|
|
215
|
-
* Load
|
|
537
|
+
* Load project-specific Wave configuration
|
|
538
|
+
* Checks .local.json first, then falls back to .json
|
|
539
|
+
*/
|
|
540
|
+
export function loadProjectWaveConfig(workdir) {
|
|
541
|
+
return loadWaveConfigFromFiles(getProjectHooksConfigPaths(workdir));
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Load user-specific hooks configuration (legacy function)
|
|
216
545
|
*/
|
|
217
546
|
export function loadUserHooksConfig() {
|
|
218
|
-
|
|
547
|
+
const waveConfig = loadUserWaveConfig();
|
|
548
|
+
return waveConfig?.hooks || null;
|
|
219
549
|
}
|
|
220
550
|
/**
|
|
221
|
-
* Load project-specific hooks configuration
|
|
551
|
+
* Load project-specific hooks configuration (legacy function)
|
|
222
552
|
*/
|
|
223
553
|
export function loadProjectHooksConfig(workdir) {
|
|
224
|
-
|
|
554
|
+
const waveConfig = loadProjectWaveConfig(workdir);
|
|
555
|
+
return waveConfig?.hooks || null;
|
|
225
556
|
}
|
|
226
557
|
/**
|
|
227
|
-
* Load and merge
|
|
558
|
+
* Load and merge Wave configuration from both user and project sources
|
|
559
|
+
* Project configuration takes precedence over user configuration
|
|
560
|
+
* Checks .local.json files first, then falls back to .json files
|
|
228
561
|
*/
|
|
229
|
-
export function
|
|
230
|
-
const userConfig =
|
|
231
|
-
const projectConfig =
|
|
562
|
+
export function loadMergedWaveConfig(workdir) {
|
|
563
|
+
const userConfig = loadUserWaveConfig();
|
|
564
|
+
const projectConfig = loadProjectWaveConfig(workdir);
|
|
232
565
|
// No configuration found
|
|
233
566
|
if (!userConfig && !projectConfig) {
|
|
234
567
|
return null;
|
|
@@ -239,38 +572,55 @@ export function loadMergedHooksConfig(workdir) {
|
|
|
239
572
|
if (!projectConfig)
|
|
240
573
|
return userConfig;
|
|
241
574
|
// Merge configurations (project overrides user)
|
|
242
|
-
const
|
|
243
|
-
//
|
|
575
|
+
const mergedHooks = {};
|
|
576
|
+
// Merge environment variables using the new mergeEnvironmentConfig function
|
|
577
|
+
const environmentContext = mergeEnvironmentConfig(userConfig.env, projectConfig.env, { includeConflictWarnings: true });
|
|
578
|
+
// Log environment variable conflicts if any
|
|
579
|
+
if (environmentContext.conflicts.length > 0) {
|
|
580
|
+
console.warn(`Environment variable conflicts detected (project values take precedence):\n${environmentContext.conflicts
|
|
581
|
+
.map((conflict) => `- ${conflict.key}: "${conflict.userValue}" → "${conflict.projectValue}"`)
|
|
582
|
+
.join("\n")}`);
|
|
583
|
+
}
|
|
584
|
+
// Merge hooks (combine arrays, project configs come after user configs)
|
|
244
585
|
const allEvents = new Set([
|
|
245
|
-
...Object.keys(userConfig),
|
|
246
|
-
...Object.keys(projectConfig),
|
|
586
|
+
...Object.keys(userConfig.hooks || {}),
|
|
587
|
+
...Object.keys(projectConfig.hooks || {}),
|
|
247
588
|
]);
|
|
248
589
|
for (const event of allEvents) {
|
|
249
590
|
if (!isValidHookEvent(event))
|
|
250
591
|
continue;
|
|
251
|
-
const userEventConfigs = userConfig[event] || [];
|
|
252
|
-
const projectEventConfigs = projectConfig[event] || [];
|
|
592
|
+
const userEventConfigs = userConfig.hooks?.[event] || [];
|
|
593
|
+
const projectEventConfigs = projectConfig.hooks?.[event] || [];
|
|
253
594
|
// Project configurations take precedence
|
|
254
|
-
|
|
595
|
+
mergedHooks[event] = [...userEventConfigs, ...projectEventConfigs];
|
|
255
596
|
}
|
|
256
|
-
return
|
|
597
|
+
return {
|
|
598
|
+
hooks: Object.keys(mergedHooks).length > 0 ? mergedHooks : undefined,
|
|
599
|
+
env: Object.keys(environmentContext.mergedVars).length > 0
|
|
600
|
+
? environmentContext.mergedVars
|
|
601
|
+
: undefined,
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Load and merge hooks configuration from both user and project sources (legacy function)
|
|
606
|
+
*/
|
|
607
|
+
export function loadMergedHooksConfig(workdir) {
|
|
608
|
+
const waveConfig = loadMergedWaveConfig(workdir);
|
|
609
|
+
return waveConfig?.hooks || null;
|
|
257
610
|
}
|
|
258
611
|
/**
|
|
259
612
|
* Check if hooks configuration exists (user or project)
|
|
613
|
+
* Checks both .local.json and .json variants
|
|
614
|
+
* @deprecated Use hasAnyConfig() from configPaths.ts for better functionality
|
|
260
615
|
*/
|
|
261
616
|
export function hasHooksConfiguration(workdir) {
|
|
262
|
-
return (
|
|
263
|
-
existsSync(getProjectHooksConfigPath(workdir)));
|
|
617
|
+
return hasAnyConfig(workdir);
|
|
264
618
|
}
|
|
265
619
|
/**
|
|
266
620
|
* Get hooks configuration information for debugging
|
|
621
|
+
* Includes both .local.json and .json variants
|
|
622
|
+
* @deprecated Use getConfigurationInfo() from configPaths.ts for better functionality
|
|
267
623
|
*/
|
|
268
624
|
export function getHooksConfigurationInfo(workdir) {
|
|
269
|
-
|
|
270
|
-
const projectPath = getProjectHooksConfigPath(workdir);
|
|
271
|
-
return {
|
|
272
|
-
hasUser: existsSync(userPath),
|
|
273
|
-
hasProject: existsSync(projectPath),
|
|
274
|
-
paths: [userPath, projectPath],
|
|
275
|
-
};
|
|
625
|
+
return getConfigurationInfo(workdir);
|
|
276
626
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSONL file operations service
|
|
3
|
+
* Handles reading and writing JSONL (JSON Lines) session files for improved performance
|
|
4
|
+
*/
|
|
5
|
+
import type { Message } from "../types/index.js";
|
|
6
|
+
import type { SessionMessage, SessionMetadataLine } from "../types/session.js";
|
|
7
|
+
/**
|
|
8
|
+
* JSONL write options
|
|
9
|
+
*/
|
|
10
|
+
export interface JsonlWriteOptions {
|
|
11
|
+
atomic?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* JSONL handler class for message persistence operations
|
|
15
|
+
*/
|
|
16
|
+
export declare class JsonlHandler {
|
|
17
|
+
private readonly defaultWriteOptions;
|
|
18
|
+
constructor();
|
|
19
|
+
/**
|
|
20
|
+
* Create a new session file with metadata header
|
|
21
|
+
*/
|
|
22
|
+
createSession(filePath: string, sessionId: string, workdir: string, sessionType?: "main" | "subagent", parentSessionId?: string, subagentType?: string): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Append a single message to JSONL file
|
|
25
|
+
*/
|
|
26
|
+
appendMessage(filePath: string, message: Message): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Append multiple messages to JSONL file
|
|
29
|
+
*/
|
|
30
|
+
appendMessages(filePath: string, messages: Message[]): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Append messages to JSONL file
|
|
33
|
+
*/
|
|
34
|
+
append(filePath: string, messages: SessionMessage[], options?: JsonlWriteOptions): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Read all messages from JSONL file
|
|
37
|
+
* Includes metadata handling for backward compatibility
|
|
38
|
+
*/
|
|
39
|
+
read(filePath: string): Promise<SessionMessage[]>;
|
|
40
|
+
/**
|
|
41
|
+
* Get the last message from JSONL file using efficient file reading
|
|
42
|
+
*/
|
|
43
|
+
getLastMessage(filePath: string): Promise<SessionMessage | null>;
|
|
44
|
+
/**
|
|
45
|
+
* Read session metadata from first line (streaming - only reads first line)
|
|
46
|
+
*/
|
|
47
|
+
readMetadata(filePath: string): Promise<SessionMetadataLine | null>;
|
|
48
|
+
/**
|
|
49
|
+
* Check if a session file has metadata (first line check only)
|
|
50
|
+
* Very efficient - only reads first line
|
|
51
|
+
*/
|
|
52
|
+
hasMetadata(filePath: string): Promise<boolean>;
|
|
53
|
+
/**
|
|
54
|
+
* Validate messages before writing
|
|
55
|
+
*/
|
|
56
|
+
private validateMessages;
|
|
57
|
+
/**
|
|
58
|
+
* Ensure directory exists for the given file path
|
|
59
|
+
*/
|
|
60
|
+
private ensureDirectory;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=jsonlHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonlHandler.d.ts","sourceRoot":"","sources":["../../src/services/jsonlHandler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE/E;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAEhC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA8B;;IAQlE;;OAEG;IACG,aAAa,CACjB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,WAAW,GAAE,MAAM,GAAG,UAAmB,EACzC,eAAe,CAAC,EAAE,MAAM,EACxB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC;IAkBhB;;OAEG;IACG,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAItE;;OAEG;IACG,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAc1E;;OAEG;IACG,MAAM,CACV,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,cAAc,EAAE,EAC1B,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,IAAI,CAAC;IAyChB;;;OAGG;IACG,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAqDvD;;OAEG;IACG,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAwCtE;;OAEG;IACG,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;IAkCzE;;;OAGG;IACG,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKrD;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAwBxB;;OAEG;YACW,eAAe;CAU9B"}
|