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
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { relative, basename } from "path";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Command path resolver utilities for nested command discovery
|
|
5
|
+
* Handles conversion between file paths and command IDs with colon syntax
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface CommandIdParts {
|
|
9
|
+
namespace?: string; // e.g., "openspec" for "openspec:apply"
|
|
10
|
+
commandName: string; // e.g., "apply" for "openspec:apply"
|
|
11
|
+
isNested: boolean; // true if command has namespace
|
|
12
|
+
depth: number; // 0 for root, 1 for nested
|
|
13
|
+
segments: string[]; // Path components array
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Generate command ID from file path
|
|
18
|
+
* @param filePath - Absolute path to markdown file
|
|
19
|
+
* @param rootDir - Root commands directory path
|
|
20
|
+
* @returns Command identifier string (e.g., "openspec:apply")
|
|
21
|
+
* @throws Error on invalid path structure
|
|
22
|
+
*/
|
|
23
|
+
export function generateCommandId(filePath: string, rootDir: string): string {
|
|
24
|
+
// Handle null/undefined inputs
|
|
25
|
+
if (filePath == null || rootDir == null) {
|
|
26
|
+
throw new Error("File path and root directory must be provided");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Handle empty root directory (for root level commands)
|
|
30
|
+
const relativePath = rootDir === "" ? filePath : relative(rootDir, filePath);
|
|
31
|
+
|
|
32
|
+
// Handle edge cases
|
|
33
|
+
if (!relativePath || relativePath === ".") {
|
|
34
|
+
throw new Error("Command filename cannot be empty");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const segments = relativePath.split("/").filter((segment) => segment !== "");
|
|
38
|
+
|
|
39
|
+
// Remove .md extension from the last segment
|
|
40
|
+
const lastSegment = segments[segments.length - 1];
|
|
41
|
+
if (!lastSegment.endsWith(".md")) {
|
|
42
|
+
throw new Error(`Command files must have .md extension`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
segments[segments.length - 1] = basename(lastSegment, ".md");
|
|
46
|
+
|
|
47
|
+
// Handle empty filename after removing extension
|
|
48
|
+
if (segments[segments.length - 1] === "") {
|
|
49
|
+
throw new Error("Command filename cannot be empty");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Validate depth (max 1 level of nesting)
|
|
53
|
+
if (segments.length > 2) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`Command nesting too deep: ${relativePath}. Maximum depth is 1 level.`,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Validate segments
|
|
60
|
+
for (const segment of segments) {
|
|
61
|
+
if (!validateSegment(segment)) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`Invalid command path segment: "${segment}" in ${relativePath}. Must match pattern /^[a-zA-Z][a-zA-Z0-9_.-]*$/`,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Generate command ID
|
|
69
|
+
if (segments.length === 1) {
|
|
70
|
+
return segments[0]; // Flat command
|
|
71
|
+
} else {
|
|
72
|
+
return segments.join(":"); // Nested command with colon syntax
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Parse command ID into components
|
|
78
|
+
* @param commandId - Command identifier (e.g., "openspec:apply")
|
|
79
|
+
* @returns Object with namespace and command name
|
|
80
|
+
* @throws Error on malformed command ID
|
|
81
|
+
*/
|
|
82
|
+
export function parseCommandId(commandId: string): CommandIdParts {
|
|
83
|
+
// Handle null/undefined inputs
|
|
84
|
+
if (commandId == null) {
|
|
85
|
+
throw new Error("Command ID cannot be null or undefined");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (commandId === "") {
|
|
89
|
+
throw new Error("Command ID cannot be empty");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!validateCommandId(commandId)) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
`Invalid command ID format: "${commandId}". Must match pattern /^[a-zA-Z0-9_-]+(?::[a-zA-Z0-9_-]+)?$/`,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const parts = commandId.split(":");
|
|
99
|
+
|
|
100
|
+
if (parts.length === 1) {
|
|
101
|
+
// Flat command
|
|
102
|
+
return {
|
|
103
|
+
namespace: undefined,
|
|
104
|
+
commandName: parts[0],
|
|
105
|
+
isNested: false,
|
|
106
|
+
depth: 0,
|
|
107
|
+
segments: [parts[0]],
|
|
108
|
+
};
|
|
109
|
+
} else if (parts.length === 2) {
|
|
110
|
+
// Nested command
|
|
111
|
+
return {
|
|
112
|
+
namespace: parts[0],
|
|
113
|
+
commandName: parts[1],
|
|
114
|
+
isNested: true,
|
|
115
|
+
depth: 1,
|
|
116
|
+
segments: [parts[0], parts[1]],
|
|
117
|
+
};
|
|
118
|
+
} else {
|
|
119
|
+
throw new Error(
|
|
120
|
+
`Invalid command ID format: "${commandId}". Too many colon separators.`,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Validate command ID format
|
|
127
|
+
* @param commandId - Command identifier to validate
|
|
128
|
+
* @returns Boolean indicating validity
|
|
129
|
+
*/
|
|
130
|
+
export function validateCommandId(commandId: string): boolean {
|
|
131
|
+
// Handle null/undefined inputs
|
|
132
|
+
if (commandId == null) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Command ID can have multiple colons (though generateCommandId enforces max 1 level)
|
|
137
|
+
// This validates the format but doesn't enforce depth limits
|
|
138
|
+
const pattern = /^[a-zA-Z][a-zA-Z0-9_-]*(?::[a-zA-Z][a-zA-Z0-9_-]*)*$/;
|
|
139
|
+
return pattern.test(commandId);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Validate individual path segment
|
|
144
|
+
* @param segment - Path segment to validate
|
|
145
|
+
* @returns Boolean indicating validity
|
|
146
|
+
*/
|
|
147
|
+
function validateSegment(segment: string): boolean {
|
|
148
|
+
// Segments should start with letters and can contain letters, numbers, dashes, underscores, dots
|
|
149
|
+
const pattern = /^[a-zA-Z][a-zA-Z0-9_.-]*$/;
|
|
150
|
+
return pattern.test(segment);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Convert file path to command segments array
|
|
155
|
+
* @param filePath - Absolute path to markdown file
|
|
156
|
+
* @param rootDir - Root commands directory path
|
|
157
|
+
* @returns Array of path segments
|
|
158
|
+
*/
|
|
159
|
+
export function getCommandSegments(
|
|
160
|
+
filePath: string,
|
|
161
|
+
rootDir: string,
|
|
162
|
+
): string[] {
|
|
163
|
+
const relativePath = relative(rootDir, filePath);
|
|
164
|
+
const segments = relativePath.split("/").filter((segment) => segment !== "");
|
|
165
|
+
|
|
166
|
+
// Remove .md extension from the last segment
|
|
167
|
+
const lastSegment = segments[segments.length - 1];
|
|
168
|
+
segments[segments.length - 1] = basename(lastSegment, ".md");
|
|
169
|
+
|
|
170
|
+
return segments;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Get namespace from command segments
|
|
175
|
+
* @param segments - Command path segments
|
|
176
|
+
* @returns Namespace string or undefined for flat commands
|
|
177
|
+
*/
|
|
178
|
+
export function getNamespace(segments: string[]): string | undefined {
|
|
179
|
+
return segments.length > 1 ? segments[0] : undefined;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Get command depth from segments
|
|
184
|
+
* @param segments - Command path segments
|
|
185
|
+
* @returns Depth number (0 for root, 1 for nested)
|
|
186
|
+
*/
|
|
187
|
+
export function getDepth(segments: string[]): number {
|
|
188
|
+
return Math.max(0, segments.length - 1);
|
|
189
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Path Utilities
|
|
3
|
+
*
|
|
4
|
+
* Centralized utilities for resolving Wave configuration file paths.
|
|
5
|
+
* Supports both regular settings.json and settings.local.json with proper priority.
|
|
6
|
+
*
|
|
7
|
+
* Priority system:
|
|
8
|
+
* - User configs: ~/.wave/settings.local.json > ~/.wave/settings.json
|
|
9
|
+
* - Project configs: {workdir}/.wave/settings.local.json > {workdir}/.wave/settings.json
|
|
10
|
+
* - Project configs override user configs (existing behavior)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { join } from "path";
|
|
14
|
+
import { homedir } from "os";
|
|
15
|
+
import { existsSync } from "fs";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get the user-specific configuration file path (legacy function)
|
|
19
|
+
* @deprecated Use getUserConfigPaths() for better priority support
|
|
20
|
+
*/
|
|
21
|
+
export function getUserConfigPath(): string {
|
|
22
|
+
return join(homedir(), ".wave", "settings.json");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get the project-specific configuration file path (legacy function)
|
|
27
|
+
* @deprecated Use getProjectConfigPaths() for better priority support
|
|
28
|
+
*/
|
|
29
|
+
export function getProjectConfigPath(workdir: string): string {
|
|
30
|
+
return join(workdir, ".wave", "settings.json");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get the user-specific configuration file paths in priority order
|
|
35
|
+
* Returns array with .local.json first, then .json
|
|
36
|
+
*/
|
|
37
|
+
export function getUserConfigPaths(): string[] {
|
|
38
|
+
const baseDir = join(homedir(), ".wave");
|
|
39
|
+
return [join(baseDir, "settings.local.json"), join(baseDir, "settings.json")];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get the project-specific configuration file paths in priority order
|
|
44
|
+
* Returns array with .local.json first, then .json
|
|
45
|
+
*/
|
|
46
|
+
export function getProjectConfigPaths(workdir: string): string[] {
|
|
47
|
+
const baseDir = join(workdir, ".wave");
|
|
48
|
+
return [join(baseDir, "settings.local.json"), join(baseDir, "settings.json")];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get all configuration file paths (user and project) in priority order
|
|
53
|
+
* Useful for comprehensive configuration detection
|
|
54
|
+
*/
|
|
55
|
+
export function getAllConfigPaths(workdir: string): {
|
|
56
|
+
userPaths: string[];
|
|
57
|
+
projectPaths: string[];
|
|
58
|
+
allPaths: string[];
|
|
59
|
+
} {
|
|
60
|
+
const userPaths = getUserConfigPaths();
|
|
61
|
+
const projectPaths = getProjectConfigPaths(workdir);
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
userPaths,
|
|
65
|
+
projectPaths,
|
|
66
|
+
allPaths: [...userPaths, ...projectPaths],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get existing configuration file paths
|
|
72
|
+
* Returns only the paths that actually exist on the filesystem
|
|
73
|
+
*/
|
|
74
|
+
export function getExistingConfigPaths(workdir: string): {
|
|
75
|
+
userPaths: string[];
|
|
76
|
+
projectPaths: string[];
|
|
77
|
+
existingPaths: string[];
|
|
78
|
+
} {
|
|
79
|
+
const allPaths = getAllConfigPaths(workdir);
|
|
80
|
+
|
|
81
|
+
const existingUserPaths = allPaths.userPaths.filter(existsSync);
|
|
82
|
+
const existingProjectPaths = allPaths.projectPaths.filter(existsSync);
|
|
83
|
+
const allExistingPaths = allPaths.allPaths.filter(existsSync);
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
userPaths: existingUserPaths,
|
|
87
|
+
projectPaths: existingProjectPaths,
|
|
88
|
+
existingPaths: allExistingPaths,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get the first existing configuration file path with the specified priority
|
|
94
|
+
* @param paths Array of paths in priority order
|
|
95
|
+
* @returns The first path that exists, or undefined if none exist
|
|
96
|
+
*/
|
|
97
|
+
export function getFirstExistingPath(paths: string[]): string | undefined {
|
|
98
|
+
return paths.find((path) => existsSync(path));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Get effective configuration paths (the ones that would actually be used)
|
|
103
|
+
* Returns the highest priority existing path for each category
|
|
104
|
+
*/
|
|
105
|
+
export function getEffectiveConfigPaths(workdir: string): {
|
|
106
|
+
userPath?: string;
|
|
107
|
+
projectPath?: string;
|
|
108
|
+
effectivePath?: string; // The path that takes final precedence
|
|
109
|
+
} {
|
|
110
|
+
const userPaths = getUserConfigPaths();
|
|
111
|
+
const projectPaths = getProjectConfigPaths(workdir);
|
|
112
|
+
|
|
113
|
+
const userPath = getFirstExistingPath(userPaths);
|
|
114
|
+
const projectPath = getFirstExistingPath(projectPaths);
|
|
115
|
+
|
|
116
|
+
// Project path takes precedence over user path if both exist
|
|
117
|
+
const effectivePath = projectPath || userPath;
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
userPath,
|
|
121
|
+
projectPath,
|
|
122
|
+
effectivePath,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Check if any configuration files exist
|
|
128
|
+
*/
|
|
129
|
+
export function hasAnyConfig(workdir: string): boolean {
|
|
130
|
+
const { existingPaths } = getExistingConfigPaths(workdir);
|
|
131
|
+
return existingPaths.length > 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Get configuration information for debugging and monitoring
|
|
136
|
+
*/
|
|
137
|
+
export function getConfigurationInfo(workdir: string): {
|
|
138
|
+
hasUser: boolean;
|
|
139
|
+
hasProject: boolean;
|
|
140
|
+
paths: string[];
|
|
141
|
+
userPaths: string[];
|
|
142
|
+
projectPaths: string[];
|
|
143
|
+
existingPaths: string[];
|
|
144
|
+
effectivePaths: {
|
|
145
|
+
userPath?: string;
|
|
146
|
+
projectPath?: string;
|
|
147
|
+
effectivePath?: string;
|
|
148
|
+
};
|
|
149
|
+
} {
|
|
150
|
+
const allPaths = getAllConfigPaths(workdir);
|
|
151
|
+
const existingPaths = getExistingConfigPaths(workdir);
|
|
152
|
+
const effectivePaths = getEffectiveConfigPaths(workdir);
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
hasUser: existingPaths.userPaths.length > 0,
|
|
156
|
+
hasProject: existingPaths.projectPaths.length > 0,
|
|
157
|
+
paths: allPaths.allPaths,
|
|
158
|
+
userPaths: allPaths.userPaths,
|
|
159
|
+
projectPaths: allPaths.projectPaths,
|
|
160
|
+
existingPaths: existingPaths.existingPaths,
|
|
161
|
+
effectivePaths,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Configuration resolver utilities for Agent Constructor Configuration
|
|
3
3
|
* Resolves configuration from constructor arguments with environment fallbacks
|
|
4
|
+
* Supports live configuration updates and cache invalidation
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
import {
|
|
@@ -9,32 +10,111 @@ import {
|
|
|
9
10
|
ConfigurationError,
|
|
10
11
|
CONFIG_ERRORS,
|
|
11
12
|
} from "../types/index.js";
|
|
13
|
+
import { DEFAULT_TOKEN_LIMIT } from "./constants.js";
|
|
14
|
+
import { loadMergedWaveConfig } from "../services/hook.js";
|
|
15
|
+
import { getGlobalLogger } from "./globalLogger.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Live configuration cache and invalidation support
|
|
19
|
+
*/
|
|
20
|
+
interface ConfigurationCache {
|
|
21
|
+
workdir?: string;
|
|
22
|
+
lastUpdated: number;
|
|
23
|
+
environmentVars: Record<string, string>;
|
|
24
|
+
isValid: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let configCache: ConfigurationCache | null = null;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Initialize configuration cache with current environment variables from settings.json
|
|
31
|
+
*/
|
|
32
|
+
function initializeConfigurationCache(workdir?: string): void {
|
|
33
|
+
try {
|
|
34
|
+
const waveConfig = workdir ? loadMergedWaveConfig(workdir) : null;
|
|
35
|
+
const envVars = waveConfig?.env || {};
|
|
36
|
+
|
|
37
|
+
configCache = {
|
|
38
|
+
workdir,
|
|
39
|
+
lastUpdated: Date.now(),
|
|
40
|
+
environmentVars: envVars,
|
|
41
|
+
isValid: true,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const logger = getGlobalLogger();
|
|
45
|
+
logger?.debug(
|
|
46
|
+
`Live Config: Configuration cache initialized with ${Object.keys(envVars).length} environment variables`,
|
|
47
|
+
);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
const logger = getGlobalLogger();
|
|
50
|
+
logger?.error(
|
|
51
|
+
`Live Config: Failed to initialize configuration cache: ${(error as Error).message}`,
|
|
52
|
+
);
|
|
53
|
+
configCache = {
|
|
54
|
+
workdir,
|
|
55
|
+
lastUpdated: Date.now(),
|
|
56
|
+
environmentVars: {},
|
|
57
|
+
isValid: false,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get current environment variable value with live configuration support
|
|
64
|
+
*/
|
|
65
|
+
function getCurrentEnvironmentValue(
|
|
66
|
+
key: string,
|
|
67
|
+
workdir?: string,
|
|
68
|
+
): string | undefined {
|
|
69
|
+
// Initialize cache if not present or workdir changed
|
|
70
|
+
if (!configCache || configCache.workdir !== workdir) {
|
|
71
|
+
initializeConfigurationCache(workdir);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Use cached environment variables if available and valid
|
|
75
|
+
if (configCache && configCache.isValid) {
|
|
76
|
+
const cachedValue = configCache.environmentVars[key];
|
|
77
|
+
if (cachedValue !== undefined) {
|
|
78
|
+
const logger = getGlobalLogger();
|
|
79
|
+
logger?.debug(
|
|
80
|
+
`Live Config: Using cached environment variable ${key}=${cachedValue}`,
|
|
81
|
+
);
|
|
82
|
+
return cachedValue;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Fallback to process environment
|
|
87
|
+
return process.env[key];
|
|
88
|
+
}
|
|
12
89
|
|
|
13
90
|
export class ConfigResolver {
|
|
14
91
|
/**
|
|
15
|
-
* Resolves gateway configuration from constructor args and environment
|
|
92
|
+
* Resolves gateway configuration from constructor args and environment with live config support
|
|
16
93
|
* @param apiKey - API key from constructor (optional)
|
|
17
94
|
* @param baseURL - Base URL from constructor (optional)
|
|
95
|
+
* @param workdir - Working directory for loading live configuration (optional)
|
|
18
96
|
* @returns Resolved gateway configuration
|
|
19
97
|
* @throws ConfigurationError if required configuration is missing after fallbacks
|
|
20
98
|
*/
|
|
21
99
|
static resolveGatewayConfig(
|
|
22
100
|
apiKey?: string,
|
|
23
101
|
baseURL?: string,
|
|
102
|
+
workdir?: string,
|
|
24
103
|
): GatewayConfig {
|
|
25
|
-
// Resolve API key: constructor > environment variable
|
|
104
|
+
// Resolve API key: constructor > live configuration > environment variable
|
|
26
105
|
// Note: Explicitly provided empty strings should be treated as invalid, not fall back to env
|
|
27
106
|
let resolvedApiKey: string;
|
|
28
107
|
if (apiKey !== undefined) {
|
|
29
108
|
resolvedApiKey = apiKey;
|
|
30
109
|
} else {
|
|
31
|
-
resolvedApiKey =
|
|
110
|
+
resolvedApiKey = getCurrentEnvironmentValue("AIGW_TOKEN", workdir) || "";
|
|
32
111
|
}
|
|
33
112
|
|
|
34
113
|
if (!resolvedApiKey && apiKey === undefined) {
|
|
114
|
+
const envValue = getCurrentEnvironmentValue("AIGW_TOKEN", workdir);
|
|
35
115
|
throw new ConfigurationError(CONFIG_ERRORS.MISSING_API_KEY, "apiKey", {
|
|
36
116
|
constructor: apiKey,
|
|
37
|
-
environment:
|
|
117
|
+
environment: envValue,
|
|
38
118
|
});
|
|
39
119
|
}
|
|
40
120
|
|
|
@@ -46,19 +126,20 @@ export class ConfigResolver {
|
|
|
46
126
|
);
|
|
47
127
|
}
|
|
48
128
|
|
|
49
|
-
// Resolve base URL: constructor > environment variable
|
|
129
|
+
// Resolve base URL: constructor > live configuration > environment variable
|
|
50
130
|
// Note: Explicitly provided empty strings should be treated as invalid, not fall back to env
|
|
51
131
|
let resolvedBaseURL: string;
|
|
52
132
|
if (baseURL !== undefined) {
|
|
53
133
|
resolvedBaseURL = baseURL;
|
|
54
134
|
} else {
|
|
55
|
-
resolvedBaseURL =
|
|
135
|
+
resolvedBaseURL = getCurrentEnvironmentValue("AIGW_URL", workdir) || "";
|
|
56
136
|
}
|
|
57
137
|
|
|
58
138
|
if (!resolvedBaseURL && baseURL === undefined) {
|
|
139
|
+
const envValue = getCurrentEnvironmentValue("AIGW_URL", workdir);
|
|
59
140
|
throw new ConfigurationError(CONFIG_ERRORS.MISSING_BASE_URL, "baseURL", {
|
|
60
141
|
constructor: baseURL,
|
|
61
|
-
environment:
|
|
142
|
+
environment: envValue,
|
|
62
143
|
});
|
|
63
144
|
}
|
|
64
145
|
|
|
@@ -77,26 +158,37 @@ export class ConfigResolver {
|
|
|
77
158
|
}
|
|
78
159
|
|
|
79
160
|
/**
|
|
80
|
-
* Resolves model configuration with fallbacks
|
|
161
|
+
* Resolves model configuration with fallbacks and live config support
|
|
81
162
|
* @param agentModel - Agent model from constructor (optional)
|
|
82
163
|
* @param fastModel - Fast model from constructor (optional)
|
|
164
|
+
* @param workdir - Working directory for loading live configuration (optional)
|
|
83
165
|
* @returns Resolved model configuration with defaults
|
|
84
166
|
*/
|
|
85
167
|
static resolveModelConfig(
|
|
86
168
|
agentModel?: string,
|
|
87
169
|
fastModel?: string,
|
|
170
|
+
workdir?: string,
|
|
88
171
|
): ModelConfig {
|
|
89
172
|
// Default values as per data-model.md
|
|
90
173
|
const DEFAULT_AGENT_MODEL = "claude-sonnet-4-20250514";
|
|
91
174
|
const DEFAULT_FAST_MODEL = "gemini-2.5-flash";
|
|
92
175
|
|
|
93
|
-
// Resolve agent model: constructor > environment > default
|
|
176
|
+
// Resolve agent model: constructor > live configuration > environment > default
|
|
94
177
|
const resolvedAgentModel =
|
|
95
|
-
agentModel ||
|
|
178
|
+
agentModel ||
|
|
179
|
+
getCurrentEnvironmentValue("AIGW_MODEL", workdir) ||
|
|
180
|
+
DEFAULT_AGENT_MODEL;
|
|
96
181
|
|
|
97
|
-
// Resolve fast model: constructor > environment > default
|
|
182
|
+
// Resolve fast model: constructor > live configuration > environment > default
|
|
98
183
|
const resolvedFastModel =
|
|
99
|
-
fastModel ||
|
|
184
|
+
fastModel ||
|
|
185
|
+
getCurrentEnvironmentValue("AIGW_FAST_MODEL", workdir) ||
|
|
186
|
+
DEFAULT_FAST_MODEL;
|
|
187
|
+
|
|
188
|
+
const logger = getGlobalLogger();
|
|
189
|
+
logger?.debug(
|
|
190
|
+
`Live Config: Resolved models - agent: ${resolvedAgentModel}, fast: ${resolvedFastModel}`,
|
|
191
|
+
);
|
|
100
192
|
|
|
101
193
|
return {
|
|
102
194
|
agentModel: resolvedAgentModel,
|
|
@@ -105,23 +197,29 @@ export class ConfigResolver {
|
|
|
105
197
|
}
|
|
106
198
|
|
|
107
199
|
/**
|
|
108
|
-
* Resolves token limit with fallbacks
|
|
200
|
+
* Resolves token limit with fallbacks and live config support
|
|
109
201
|
* @param constructorLimit - Token limit from constructor (optional)
|
|
202
|
+
* @param workdir - Working directory for loading live configuration (optional)
|
|
110
203
|
* @returns Resolved token limit
|
|
111
204
|
*/
|
|
112
|
-
static resolveTokenLimit(
|
|
113
|
-
|
|
114
|
-
|
|
205
|
+
static resolveTokenLimit(
|
|
206
|
+
constructorLimit?: number,
|
|
207
|
+
workdir?: string,
|
|
208
|
+
): number {
|
|
115
209
|
// If constructor value provided, use it
|
|
116
210
|
if (constructorLimit !== undefined) {
|
|
117
211
|
return constructorLimit;
|
|
118
212
|
}
|
|
119
213
|
|
|
120
|
-
// Try environment variable
|
|
121
|
-
const envTokenLimit =
|
|
214
|
+
// Try live configuration then environment variable
|
|
215
|
+
const envTokenLimit = getCurrentEnvironmentValue("TOKEN_LIMIT", workdir);
|
|
122
216
|
if (envTokenLimit) {
|
|
123
217
|
const parsed = parseInt(envTokenLimit, 10);
|
|
124
218
|
if (!isNaN(parsed)) {
|
|
219
|
+
const logger = getGlobalLogger();
|
|
220
|
+
logger?.debug(
|
|
221
|
+
`Live Config: Resolved token limit from configuration: ${parsed}`,
|
|
222
|
+
);
|
|
125
223
|
return parsed;
|
|
126
224
|
}
|
|
127
225
|
}
|
|
@@ -129,14 +227,76 @@ export class ConfigResolver {
|
|
|
129
227
|
// Use default
|
|
130
228
|
return DEFAULT_TOKEN_LIMIT;
|
|
131
229
|
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Invalidate configuration cache to force reload from settings.json
|
|
233
|
+
* @param workdir - Working directory to invalidate cache for (optional)
|
|
234
|
+
*/
|
|
235
|
+
static invalidateCache(workdir?: string): void {
|
|
236
|
+
if (
|
|
237
|
+
configCache &&
|
|
238
|
+
(workdir === undefined || configCache.workdir === workdir)
|
|
239
|
+
) {
|
|
240
|
+
const logger = getGlobalLogger();
|
|
241
|
+
logger?.info(
|
|
242
|
+
`Live Config: Configuration cache invalidated for workdir: ${workdir || "global"}`,
|
|
243
|
+
);
|
|
244
|
+
configCache = null;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Refresh configuration cache by reloading from settings.json
|
|
250
|
+
* @param workdir - Working directory to refresh cache for (optional)
|
|
251
|
+
*/
|
|
252
|
+
static refreshCache(workdir?: string): void {
|
|
253
|
+
const logger = getGlobalLogger();
|
|
254
|
+
logger?.info(
|
|
255
|
+
`Live Config: Refreshing configuration cache for workdir: ${workdir || "global"}`,
|
|
256
|
+
);
|
|
257
|
+
initializeConfigurationCache(workdir);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Get current cache status for monitoring
|
|
262
|
+
* @returns Cache information or null if no cache
|
|
263
|
+
*/
|
|
264
|
+
static getCacheStatus(): {
|
|
265
|
+
workdir?: string;
|
|
266
|
+
lastUpdated: number;
|
|
267
|
+
envVarCount: number;
|
|
268
|
+
isValid: boolean;
|
|
269
|
+
} | null {
|
|
270
|
+
if (!configCache) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
workdir: configCache.workdir,
|
|
276
|
+
lastUpdated: configCache.lastUpdated,
|
|
277
|
+
envVarCount: Object.keys(configCache.environmentVars).length,
|
|
278
|
+
isValid: configCache.isValid,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
132
281
|
}
|
|
133
282
|
|
|
134
283
|
/**
|
|
135
284
|
* Static configuration resolver instance
|
|
136
|
-
* Implements ConfigurationResolver interface from types.ts
|
|
285
|
+
* Implements ConfigurationResolver interface from types.ts with backward compatibility
|
|
137
286
|
*/
|
|
138
287
|
export const configResolver = {
|
|
139
|
-
resolveGatewayConfig:
|
|
140
|
-
|
|
141
|
-
|
|
288
|
+
resolveGatewayConfig: (apiKey?: string, baseURL?: string, workdir?: string) =>
|
|
289
|
+
ConfigResolver.resolveGatewayConfig(apiKey, baseURL, workdir),
|
|
290
|
+
resolveModelConfig: (
|
|
291
|
+
agentModel?: string,
|
|
292
|
+
fastModel?: string,
|
|
293
|
+
workdir?: string,
|
|
294
|
+
) => ConfigResolver.resolveModelConfig(agentModel, fastModel, workdir),
|
|
295
|
+
resolveTokenLimit: (constructorLimit?: number, workdir?: string) =>
|
|
296
|
+
ConfigResolver.resolveTokenLimit(constructorLimit, workdir),
|
|
297
|
+
|
|
298
|
+
// Live configuration management methods
|
|
299
|
+
invalidateCache: ConfigResolver.invalidateCache,
|
|
300
|
+
refreshCache: ConfigResolver.refreshCache,
|
|
301
|
+
getCacheStatus: ConfigResolver.getCacheStatus,
|
|
142
302
|
};
|
package/src/utils/constants.ts
CHANGED
|
@@ -29,7 +29,7 @@ export const USER_MEMORY_FILE = path.join(DATA_DIRECTORY, "user-memory.md");
|
|
|
29
29
|
/**
|
|
30
30
|
* AI related constants
|
|
31
31
|
*/
|
|
32
|
-
export const DEFAULT_TOKEN_LIMIT =
|
|
32
|
+
export const DEFAULT_TOKEN_LIMIT = 96000; // Default token limit
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* @deprecated These constants are now legacy. Use ModelConfig through Agent constructor instead.
|