wave-agent-sdk 0.0.7 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. package/dist/agent.d.ts +105 -24
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +438 -53
  4. package/dist/index.d.ts +4 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +4 -0
  7. package/dist/managers/aiManager.d.ts +18 -7
  8. package/dist/managers/aiManager.d.ts.map +1 -1
  9. package/dist/managers/aiManager.js +254 -142
  10. package/dist/managers/backgroundBashManager.d.ts.map +1 -1
  11. package/dist/managers/backgroundBashManager.js +11 -9
  12. package/dist/managers/hookManager.d.ts +6 -6
  13. package/dist/managers/hookManager.d.ts.map +1 -1
  14. package/dist/managers/hookManager.js +81 -39
  15. package/dist/managers/liveConfigManager.d.ts +95 -0
  16. package/dist/managers/liveConfigManager.d.ts.map +1 -0
  17. package/dist/managers/liveConfigManager.js +442 -0
  18. package/dist/managers/lspManager.d.ts +43 -0
  19. package/dist/managers/lspManager.d.ts.map +1 -0
  20. package/dist/managers/lspManager.js +326 -0
  21. package/dist/managers/messageManager.d.ts +41 -24
  22. package/dist/managers/messageManager.d.ts.map +1 -1
  23. package/dist/managers/messageManager.js +184 -73
  24. package/dist/managers/permissionManager.d.ts +66 -0
  25. package/dist/managers/permissionManager.d.ts.map +1 -0
  26. package/dist/managers/permissionManager.js +208 -0
  27. package/dist/managers/skillManager.d.ts +1 -0
  28. package/dist/managers/skillManager.d.ts.map +1 -1
  29. package/dist/managers/skillManager.js +2 -1
  30. package/dist/managers/slashCommandManager.d.ts.map +1 -1
  31. package/dist/managers/slashCommandManager.js +4 -2
  32. package/dist/managers/subagentManager.d.ts +42 -6
  33. package/dist/managers/subagentManager.d.ts.map +1 -1
  34. package/dist/managers/subagentManager.js +213 -62
  35. package/dist/managers/toolManager.d.ts +38 -1
  36. package/dist/managers/toolManager.d.ts.map +1 -1
  37. package/dist/managers/toolManager.js +66 -2
  38. package/dist/services/aiService.d.ts +15 -5
  39. package/dist/services/aiService.d.ts.map +1 -1
  40. package/dist/services/aiService.js +446 -77
  41. package/dist/services/configurationService.d.ts +116 -0
  42. package/dist/services/configurationService.d.ts.map +1 -0
  43. package/dist/services/configurationService.js +585 -0
  44. package/dist/services/fileWatcher.d.ts +69 -0
  45. package/dist/services/fileWatcher.d.ts.map +1 -0
  46. package/dist/services/fileWatcher.js +212 -0
  47. package/dist/services/hook.d.ts +5 -40
  48. package/dist/services/hook.d.ts.map +1 -1
  49. package/dist/services/hook.js +47 -109
  50. package/dist/services/jsonlHandler.d.ts +71 -0
  51. package/dist/services/jsonlHandler.d.ts.map +1 -0
  52. package/dist/services/jsonlHandler.js +236 -0
  53. package/dist/services/memory.d.ts.map +1 -1
  54. package/dist/services/memory.js +33 -11
  55. package/dist/services/session.d.ts +116 -52
  56. package/dist/services/session.d.ts.map +1 -1
  57. package/dist/services/session.js +415 -143
  58. package/dist/tools/bashTool.d.ts.map +1 -1
  59. package/dist/tools/bashTool.js +77 -17
  60. package/dist/tools/deleteFileTool.d.ts.map +1 -1
  61. package/dist/tools/deleteFileTool.js +27 -1
  62. package/dist/tools/editTool.d.ts.map +1 -1
  63. package/dist/tools/editTool.js +33 -8
  64. package/dist/tools/lspTool.d.ts +6 -0
  65. package/dist/tools/lspTool.d.ts.map +1 -0
  66. package/dist/tools/lspTool.js +589 -0
  67. package/dist/tools/multiEditTool.d.ts.map +1 -1
  68. package/dist/tools/multiEditTool.js +30 -10
  69. package/dist/tools/readTool.d.ts.map +1 -1
  70. package/dist/tools/readTool.js +113 -3
  71. package/dist/tools/skillTool.js +2 -2
  72. package/dist/tools/todoWriteTool.d.ts.map +1 -1
  73. package/dist/tools/todoWriteTool.js +23 -0
  74. package/dist/tools/types.d.ts +11 -8
  75. package/dist/tools/types.d.ts.map +1 -1
  76. package/dist/tools/writeTool.d.ts.map +1 -1
  77. package/dist/tools/writeTool.js +30 -15
  78. package/dist/types/commands.d.ts +4 -1
  79. package/dist/types/commands.d.ts.map +1 -1
  80. package/dist/types/config.d.ts +4 -0
  81. package/dist/types/config.d.ts.map +1 -1
  82. package/dist/types/configuration.d.ts +69 -0
  83. package/dist/types/configuration.d.ts.map +1 -0
  84. package/dist/types/configuration.js +8 -0
  85. package/dist/types/core.d.ts +45 -0
  86. package/dist/types/core.d.ts.map +1 -1
  87. package/dist/types/environment.d.ts +83 -0
  88. package/dist/types/environment.d.ts.map +1 -0
  89. package/dist/types/environment.js +21 -0
  90. package/dist/types/fileSearch.d.ts +5 -0
  91. package/dist/types/fileSearch.d.ts.map +1 -0
  92. package/dist/types/fileSearch.js +1 -0
  93. package/dist/types/hooks.d.ts +18 -3
  94. package/dist/types/hooks.d.ts.map +1 -1
  95. package/dist/types/hooks.js +8 -8
  96. package/dist/types/index.d.ts +7 -0
  97. package/dist/types/index.d.ts.map +1 -1
  98. package/dist/types/index.js +7 -0
  99. package/dist/types/lsp.d.ts +90 -0
  100. package/dist/types/lsp.d.ts.map +1 -0
  101. package/dist/types/lsp.js +4 -0
  102. package/dist/types/messaging.d.ts +19 -12
  103. package/dist/types/messaging.d.ts.map +1 -1
  104. package/dist/types/permissions.d.ts +35 -0
  105. package/dist/types/permissions.d.ts.map +1 -0
  106. package/dist/types/permissions.js +12 -0
  107. package/dist/types/session.d.ts +15 -0
  108. package/dist/types/session.d.ts.map +1 -0
  109. package/dist/types/session.js +7 -0
  110. package/dist/types/skills.d.ts +1 -0
  111. package/dist/types/skills.d.ts.map +1 -1
  112. package/dist/types/tools.d.ts +35 -0
  113. package/dist/types/tools.d.ts.map +1 -0
  114. package/dist/types/tools.js +4 -0
  115. package/dist/utils/abortUtils.d.ts +34 -0
  116. package/dist/utils/abortUtils.d.ts.map +1 -0
  117. package/dist/utils/abortUtils.js +92 -0
  118. package/dist/utils/bashHistory.d.ts +4 -0
  119. package/dist/utils/bashHistory.d.ts.map +1 -1
  120. package/dist/utils/bashHistory.js +48 -30
  121. package/dist/utils/builtinSubagents.d.ts +7 -0
  122. package/dist/utils/builtinSubagents.d.ts.map +1 -0
  123. package/dist/utils/builtinSubagents.js +65 -0
  124. package/dist/utils/cacheControlUtils.d.ts +96 -0
  125. package/dist/utils/cacheControlUtils.d.ts.map +1 -0
  126. package/dist/utils/cacheControlUtils.js +324 -0
  127. package/dist/utils/commandPathResolver.d.ts +52 -0
  128. package/dist/utils/commandPathResolver.d.ts.map +1 -0
  129. package/dist/utils/commandPathResolver.js +145 -0
  130. package/dist/utils/configPaths.d.ts +85 -0
  131. package/dist/utils/configPaths.d.ts.map +1 -0
  132. package/dist/utils/configPaths.js +121 -0
  133. package/dist/utils/constants.d.ts +1 -13
  134. package/dist/utils/constants.d.ts.map +1 -1
  135. package/dist/utils/constants.js +2 -14
  136. package/dist/utils/convertMessagesForAPI.d.ts +2 -1
  137. package/dist/utils/convertMessagesForAPI.d.ts.map +1 -1
  138. package/dist/utils/convertMessagesForAPI.js +39 -18
  139. package/dist/utils/customCommands.d.ts.map +1 -1
  140. package/dist/utils/customCommands.js +66 -21
  141. package/dist/utils/fileSearch.d.ts +14 -0
  142. package/dist/utils/fileSearch.d.ts.map +1 -0
  143. package/dist/utils/fileSearch.js +88 -0
  144. package/dist/utils/fileUtils.d.ts +27 -0
  145. package/dist/utils/fileUtils.d.ts.map +1 -0
  146. package/dist/utils/fileUtils.js +145 -0
  147. package/dist/utils/globalLogger.d.ts +88 -0
  148. package/dist/utils/globalLogger.d.ts.map +1 -0
  149. package/dist/utils/globalLogger.js +120 -0
  150. package/dist/utils/largeOutputHandler.d.ts +15 -0
  151. package/dist/utils/largeOutputHandler.d.ts.map +1 -0
  152. package/dist/utils/largeOutputHandler.js +40 -0
  153. package/dist/utils/markdownParser.d.ts.map +1 -1
  154. package/dist/utils/markdownParser.js +1 -17
  155. package/dist/utils/mcpUtils.d.ts.map +1 -1
  156. package/dist/utils/mcpUtils.js +25 -3
  157. package/dist/utils/messageOperations.d.ts +20 -18
  158. package/dist/utils/messageOperations.d.ts.map +1 -1
  159. package/dist/utils/messageOperations.js +30 -38
  160. package/dist/utils/pathEncoder.d.ts +108 -0
  161. package/dist/utils/pathEncoder.d.ts.map +1 -0
  162. package/dist/utils/pathEncoder.js +279 -0
  163. package/dist/utils/subagentParser.d.ts +2 -2
  164. package/dist/utils/subagentParser.d.ts.map +1 -1
  165. package/dist/utils/subagentParser.js +12 -8
  166. package/dist/utils/tokenCalculation.d.ts +26 -0
  167. package/dist/utils/tokenCalculation.d.ts.map +1 -0
  168. package/dist/utils/tokenCalculation.js +36 -0
  169. package/dist/utils/tokenEstimator.d.ts +39 -0
  170. package/dist/utils/tokenEstimator.d.ts.map +1 -0
  171. package/dist/utils/tokenEstimator.js +55 -0
  172. package/package.json +6 -6
  173. package/src/agent.ts +586 -78
  174. package/src/index.ts +4 -0
  175. package/src/managers/aiManager.ts +341 -192
  176. package/src/managers/backgroundBashManager.ts +11 -9
  177. package/src/managers/hookManager.ts +102 -54
  178. package/src/managers/liveConfigManager.ts +634 -0
  179. package/src/managers/lspManager.ts +434 -0
  180. package/src/managers/messageManager.ts +258 -121
  181. package/src/managers/permissionManager.ts +276 -0
  182. package/src/managers/skillManager.ts +3 -1
  183. package/src/managers/slashCommandManager.ts +5 -3
  184. package/src/managers/subagentManager.ts +295 -76
  185. package/src/managers/toolManager.ts +95 -3
  186. package/src/services/aiService.ts +656 -84
  187. package/src/services/configurationService.ts +762 -0
  188. package/src/services/fileWatcher.ts +300 -0
  189. package/src/services/hook.ts +54 -144
  190. package/src/services/jsonlHandler.ts +303 -0
  191. package/src/services/memory.ts +34 -11
  192. package/src/services/session.ts +522 -173
  193. package/src/tools/bashTool.ts +94 -20
  194. package/src/tools/deleteFileTool.ts +38 -1
  195. package/src/tools/editTool.ts +44 -9
  196. package/src/tools/lspTool.ts +760 -0
  197. package/src/tools/multiEditTool.ts +41 -11
  198. package/src/tools/readTool.ts +127 -3
  199. package/src/tools/skillTool.ts +2 -2
  200. package/src/tools/todoWriteTool.ts +33 -1
  201. package/src/tools/types.ts +15 -9
  202. package/src/tools/writeTool.ts +43 -16
  203. package/src/types/commands.ts +6 -1
  204. package/src/types/config.ts +5 -0
  205. package/src/types/configuration.ts +73 -0
  206. package/src/types/core.ts +55 -0
  207. package/src/types/environment.ts +104 -0
  208. package/src/types/fileSearch.ts +4 -0
  209. package/src/types/hooks.ts +32 -16
  210. package/src/types/index.ts +7 -0
  211. package/src/types/lsp.ts +96 -0
  212. package/src/types/messaging.ts +21 -14
  213. package/src/types/permissions.ts +48 -0
  214. package/src/types/session.ts +20 -0
  215. package/src/types/skills.ts +1 -0
  216. package/src/types/tools.ts +38 -0
  217. package/src/utils/abortUtils.ts +118 -0
  218. package/src/utils/bashHistory.ts +55 -31
  219. package/src/utils/builtinSubagents.ts +71 -0
  220. package/src/utils/cacheControlUtils.ts +475 -0
  221. package/src/utils/commandPathResolver.ts +189 -0
  222. package/src/utils/configPaths.ts +163 -0
  223. package/src/utils/constants.ts +2 -17
  224. package/src/utils/convertMessagesForAPI.ts +44 -18
  225. package/src/utils/customCommands.ts +90 -22
  226. package/src/utils/fileSearch.ts +107 -0
  227. package/src/utils/fileUtils.ts +160 -0
  228. package/src/utils/globalLogger.ts +128 -0
  229. package/src/utils/largeOutputHandler.ts +55 -0
  230. package/src/utils/markdownParser.ts +1 -19
  231. package/src/utils/mcpUtils.ts +34 -3
  232. package/src/utils/messageOperations.ts +47 -53
  233. package/src/utils/pathEncoder.ts +394 -0
  234. package/src/utils/subagentParser.ts +13 -9
  235. package/src/utils/tokenCalculation.ts +43 -0
  236. package/src/utils/tokenEstimator.ts +68 -0
  237. package/dist/utils/configResolver.d.ts +0 -38
  238. package/dist/utils/configResolver.d.ts.map +0 -1
  239. package/dist/utils/configResolver.js +0 -106
  240. package/src/utils/configResolver.ts +0 -142
@@ -0,0 +1,394 @@
1
+ /**
2
+ * Path encoding utility for converting working directory paths to filesystem-safe names
3
+ * Handles cross-platform directory name encoding for project-based session organization
4
+ */
5
+
6
+ import { resolve, join } from "path";
7
+ import { createHash } from "crypto";
8
+ import { realpath, mkdir } from "fs/promises";
9
+ import { homedir, platform } from "os";
10
+
11
+ /**
12
+ * Project directory information
13
+ */
14
+ export interface ProjectDirectory {
15
+ readonly originalPath: string;
16
+ readonly encodedName: string;
17
+ readonly encodedPath: string;
18
+ readonly pathHash?: string; // For collision resolution
19
+ readonly isSymbolicLink: boolean;
20
+ }
21
+
22
+ /**
23
+ * Path encoding configuration options
24
+ */
25
+ export interface PathEncodingOptions {
26
+ maxLength?: number; // Default: 200 characters
27
+ pathSeparatorReplacement?: string; // Default: '-'
28
+ spaceReplacement?: string; // Default: '_'
29
+ invalidCharReplacement?: string; // Default: '_'
30
+ preserveCase?: boolean; // Default: false (convert to lowercase)
31
+ hashLength?: number; // Default: 8 characters
32
+ }
33
+
34
+ /**
35
+ * Platform-specific filesystem constraints
36
+ */
37
+ export interface FilesystemConstraints {
38
+ readonly maxDirectoryNameLength: number;
39
+ readonly maxPathLength: number;
40
+ readonly invalidCharacters: string[];
41
+ readonly reservedNames: string[];
42
+ readonly caseSensitive: boolean;
43
+ }
44
+
45
+ /**
46
+ * Path validation result
47
+ */
48
+ export interface PathValidationResult {
49
+ readonly isValid: boolean;
50
+ readonly errors: string[];
51
+ readonly warnings: string[];
52
+ readonly suggestedFix?: string;
53
+ }
54
+
55
+ /**
56
+ * PathEncoder class for converting working directory paths to filesystem-safe names
57
+ */
58
+ export class PathEncoder {
59
+ private readonly options: Required<PathEncodingOptions>;
60
+ private readonly constraints: FilesystemConstraints;
61
+
62
+ constructor(options: PathEncodingOptions = {}) {
63
+ this.options = {
64
+ maxLength: options.maxLength ?? 200,
65
+ pathSeparatorReplacement: options.pathSeparatorReplacement ?? "-",
66
+ spaceReplacement: options.spaceReplacement ?? "_",
67
+ invalidCharReplacement: options.invalidCharReplacement ?? "_",
68
+ preserveCase: options.preserveCase ?? false,
69
+ hashLength: options.hashLength ?? 8,
70
+ };
71
+ this.constraints = this.getFilesystemConstraints();
72
+ }
73
+
74
+ /**
75
+ * Encode a working directory path to a filesystem-safe directory name
76
+ */
77
+ async encode(originalPath: string): Promise<string> {
78
+ // Resolve symbolic links and normalize path
79
+ const resolvedPath = await this.resolvePath(originalPath);
80
+ return this.encodeSync(resolvedPath);
81
+ }
82
+
83
+ /**
84
+ * Synchronously encode a path to a filesystem-safe directory name
85
+ * Note: Does not resolve symbolic links - use encode() for full path resolution
86
+ */
87
+ encodeSync(pathToEncode: string): string {
88
+ // Convert to safe directory name
89
+ let encoded = pathToEncode;
90
+
91
+ // Remove leading slash to avoid empty directory names
92
+ if (encoded.startsWith("/")) {
93
+ encoded = encoded.substring(1);
94
+ }
95
+
96
+ // Replace path separators with hyphens
97
+ encoded = encoded.replace(/[/\\]/g, this.options.pathSeparatorReplacement);
98
+
99
+ // Replace spaces with underscores
100
+ encoded = encoded.replace(/\s+/g, this.options.spaceReplacement);
101
+
102
+ // Replace invalid characters with underscores
103
+ const escapedChars = this.constraints.invalidCharacters
104
+ .map((c) => `\\${c}`)
105
+ .join("");
106
+ const invalidChars = new RegExp(`[${escapedChars}]`, "g");
107
+ encoded = encoded.replace(
108
+ invalidChars,
109
+ this.options.invalidCharReplacement,
110
+ );
111
+
112
+ // Convert to lowercase unless preserveCase is true
113
+ if (!this.options.preserveCase) {
114
+ encoded = encoded.toLowerCase();
115
+ }
116
+
117
+ // Handle length limit with hash
118
+ if (encoded.length > this.options.maxLength) {
119
+ const hash = this.generateHash(pathToEncode, this.options.hashLength);
120
+ const maxBaseLength =
121
+ this.options.maxLength - this.options.hashLength - 1; // -1 for separator
122
+ encoded = `${encoded.substring(0, maxBaseLength)}-${hash}`;
123
+ }
124
+
125
+ return encoded;
126
+ }
127
+
128
+ /**
129
+ * Decode an encoded directory name back to original path (limited functionality)
130
+ * Note: This is best-effort as encoding is lossy
131
+ */
132
+ async decode(encodedName: string): Promise<string | null> {
133
+ return this.decodeSync(encodedName);
134
+ }
135
+
136
+ /**
137
+ * Synchronously decode an encoded directory name back to original path (limited functionality)
138
+ * Note: This is best-effort as encoding is lossy
139
+ */
140
+ decodeSync(encodedName: string): string | null {
141
+ // This is a simplified version - full reversal is not always possible
142
+ // due to lossy encoding (case changes, character replacements, hashing)
143
+
144
+ // Check if this has a hash suffix
145
+ const hashPattern = new RegExp(`-[a-f0-9]{${this.options.hashLength}}$`);
146
+ if (hashPattern.test(encodedName)) {
147
+ // Cannot reliably decode hashed paths
148
+ return null;
149
+ }
150
+
151
+ // Attempt basic reversal
152
+ let decoded = encodedName;
153
+
154
+ // Reverse path separator replacement
155
+ decoded = decoded.replace(
156
+ new RegExp(this.options.pathSeparatorReplacement, "g"),
157
+ "/",
158
+ );
159
+
160
+ // Reverse space replacement
161
+ decoded = decoded.replace(
162
+ new RegExp(this.options.spaceReplacement, "g"),
163
+ " ",
164
+ );
165
+
166
+ // Add leading slash
167
+ decoded = `/${decoded}`;
168
+
169
+ return decoded;
170
+ }
171
+
172
+ /**
173
+ * Resolve symbolic links and normalize path before encoding
174
+ */
175
+ async resolvePath(path: string): Promise<string> {
176
+ try {
177
+ // Expand tilde to home directory
178
+ const expandedPath = this.expandTilde(path);
179
+
180
+ // Resolve to absolute path
181
+ const absolutePath = resolve(expandedPath);
182
+
183
+ // Resolve symbolic links
184
+ const resolvedPath = await realpath(absolutePath);
185
+
186
+ return resolvedPath;
187
+ } catch (error) {
188
+ throw new Error(`Failed to resolve path "${path}": ${error}`);
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Get project directory info without creating the directory
194
+ */
195
+ async getProjectDirectory(
196
+ originalPath: string,
197
+ baseSessionDir: string,
198
+ ): Promise<ProjectDirectory> {
199
+ // Resolve the original path and check for symbolic links
200
+ const expandedPath = this.expandTilde(originalPath);
201
+ const absolutePath = resolve(expandedPath);
202
+
203
+ let resolvedPath: string;
204
+ let isSymbolicLink = false;
205
+
206
+ try {
207
+ resolvedPath = await realpath(absolutePath);
208
+ isSymbolicLink = resolvedPath !== absolutePath;
209
+ } catch {
210
+ // If realpath fails, use the absolute path
211
+ resolvedPath = absolutePath;
212
+ }
213
+
214
+ // Encode the resolved path
215
+ const encodedName = await this.encode(resolvedPath);
216
+ const encodedPath = join(baseSessionDir, encodedName);
217
+
218
+ // Generate hash if encoding resulted in truncation
219
+ let pathHash: string | undefined;
220
+ if (resolvedPath.length > this.options.maxLength) {
221
+ pathHash = this.generateHash(resolvedPath, this.options.hashLength);
222
+ }
223
+
224
+ return {
225
+ originalPath: resolvedPath,
226
+ encodedName,
227
+ encodedPath,
228
+ pathHash,
229
+ isSymbolicLink,
230
+ };
231
+ }
232
+
233
+ /**
234
+ * Create project directory entity from original path
235
+ */
236
+ async createProjectDirectory(
237
+ originalPath: string,
238
+ baseSessionDir: string,
239
+ ): Promise<ProjectDirectory> {
240
+ const projectDirectory = await this.getProjectDirectory(
241
+ originalPath,
242
+ baseSessionDir,
243
+ );
244
+
245
+ // Ensure the encoded directory exists
246
+ try {
247
+ await mkdir(projectDirectory.encodedPath, { recursive: true });
248
+ } catch {
249
+ // Ignore errors if directory already exists
250
+ }
251
+
252
+ return projectDirectory;
253
+ }
254
+
255
+ /**
256
+ * Validate that an encoded name is filesystem-safe
257
+ */
258
+ validateEncodedName(encodedName: string): boolean {
259
+ // Check length
260
+ if (encodedName.length > this.constraints.maxDirectoryNameLength) {
261
+ return false;
262
+ }
263
+
264
+ // Check for invalid characters
265
+ for (const char of this.constraints.invalidCharacters) {
266
+ if (encodedName.includes(char)) {
267
+ return false;
268
+ }
269
+ }
270
+
271
+ // Check for reserved names
272
+ const lowerName = encodedName.toLowerCase();
273
+ if (
274
+ this.constraints.reservedNames.some(
275
+ (reserved) => reserved.toLowerCase() === lowerName,
276
+ )
277
+ ) {
278
+ return false;
279
+ }
280
+
281
+ // Check for empty or dots-only names
282
+ if (!encodedName.trim() || /^\.+$/.test(encodedName)) {
283
+ return false;
284
+ }
285
+
286
+ return true;
287
+ }
288
+
289
+ /**
290
+ * Handle encoding collisions by generating unique names
291
+ */
292
+ resolveCollision(baseName: string, existingNames: Set<string>): string {
293
+ if (!existingNames.has(baseName)) {
294
+ return baseName;
295
+ }
296
+
297
+ // Try numbered suffixes first
298
+ for (let i = 1; i <= 999; i++) {
299
+ const candidate = `${baseName}-${i}`;
300
+ if (!existingNames.has(candidate)) {
301
+ return candidate;
302
+ }
303
+ }
304
+
305
+ // If all numbered suffixes are taken, use hash
306
+ const hash = this.generateHash(
307
+ baseName + Date.now(),
308
+ this.options.hashLength,
309
+ );
310
+ return `${baseName}-${hash}`;
311
+ }
312
+
313
+ /**
314
+ * Get platform-specific filesystem constraints
315
+ */
316
+ private getFilesystemConstraints(): FilesystemConstraints {
317
+ const currentPlatform = platform();
318
+
319
+ switch (currentPlatform) {
320
+ case "win32":
321
+ return {
322
+ maxDirectoryNameLength: 255,
323
+ maxPathLength: 260,
324
+ invalidCharacters: ["<", ">", ":", '"', "|", "?", "*"],
325
+ reservedNames: [
326
+ "CON",
327
+ "PRN",
328
+ "AUX",
329
+ "NUL",
330
+ "COM1",
331
+ "COM2",
332
+ "COM3",
333
+ "COM4",
334
+ "COM5",
335
+ "COM6",
336
+ "COM7",
337
+ "COM8",
338
+ "COM9",
339
+ "LPT1",
340
+ "LPT2",
341
+ "LPT3",
342
+ "LPT4",
343
+ "LPT5",
344
+ "LPT6",
345
+ "LPT7",
346
+ "LPT8",
347
+ "LPT9",
348
+ ],
349
+ caseSensitive: false,
350
+ };
351
+ case "darwin":
352
+ return {
353
+ maxDirectoryNameLength: 255,
354
+ maxPathLength: 1024,
355
+ invalidCharacters: [":"],
356
+ reservedNames: [],
357
+ caseSensitive: false, // HFS+ is case-insensitive by default
358
+ };
359
+ default: // Linux and other Unix-like systems
360
+ return {
361
+ maxDirectoryNameLength: 255,
362
+ maxPathLength: 4096,
363
+ invalidCharacters: ["\0"],
364
+ reservedNames: [],
365
+ caseSensitive: true,
366
+ };
367
+ }
368
+ }
369
+
370
+ /**
371
+ * Generate hash for collision resolution
372
+ */
373
+ private generateHash(input: string, length: number): string {
374
+ return createHash("sha256")
375
+ .update(input)
376
+ .digest("hex")
377
+ .substring(0, length);
378
+ }
379
+
380
+ /**
381
+ * Expand tilde (~) to home directory
382
+ */
383
+ private expandTilde(path: string): string {
384
+ if (path.startsWith("~/") || path === "~") {
385
+ return path.replace(/^~/, homedir());
386
+ }
387
+ return path;
388
+ }
389
+ }
390
+
391
+ /**
392
+ * Default PathEncoder instance
393
+ */
394
+ export const pathEncoder = new PathEncoder();
@@ -1,5 +1,6 @@
1
1
  import { readFileSync, readdirSync, statSync } from "fs";
2
2
  import { join, extname } from "path";
3
+ import { logger } from "./globalLogger.js";
3
4
 
4
5
  export interface SubagentConfiguration {
5
6
  name: string;
@@ -8,7 +9,7 @@ export interface SubagentConfiguration {
8
9
  model?: string;
9
10
  systemPrompt: string;
10
11
  filePath: string;
11
- scope: "project" | "user";
12
+ scope: "project" | "user" | "builtin";
12
13
  priority: number;
13
14
  }
14
15
 
@@ -100,11 +101,11 @@ function validateConfiguration(
100
101
  throw new Error(`Missing required field 'description' in ${filePath}`);
101
102
  }
102
103
 
103
- // Validate name pattern
104
- const namePattern = /^[a-z][a-z0-9-]*$/;
104
+ // Validate name pattern - allow letters (upper/lowercase), numbers, and hyphens
105
+ const namePattern = /^[a-zA-Z][a-zA-Z0-9-]*$/;
105
106
  if (!namePattern.test(config.name)) {
106
107
  throw new Error(
107
- `Invalid subagent name '${config.name}' in ${filePath}. Must start with a letter and contain only lowercase letters, numbers, and hyphens.`,
108
+ `Invalid subagent name '${config.name}' in ${filePath}. Must start with a letter and contain only letters, numbers, and hyphens.`,
108
109
  );
109
110
  }
110
111
 
@@ -172,7 +173,7 @@ function scanSubagentDirectory(
172
173
  configurations.push(config);
173
174
  } catch (parseError) {
174
175
  // Log error but continue with other files
175
- console.warn(
176
+ logger.warn(
176
177
  `Warning: ${parseError instanceof Error ? parseError.message : String(parseError)}`,
177
178
  );
178
179
  }
@@ -186,7 +187,7 @@ function scanSubagentDirectory(
186
187
  }
187
188
 
188
189
  /**
189
- * Load all subagent configurations from project and user directories
190
+ * Load all subagent configurations from project and user directories, plus built-in subagents
190
191
  */
191
192
  export async function loadSubagentConfigurations(
192
193
  workdir: string,
@@ -194,14 +195,17 @@ export async function loadSubagentConfigurations(
194
195
  const projectDir = join(workdir, ".wave", "agents");
195
196
  const userDir = join(process.env.HOME || "~", ".wave", "agents");
196
197
 
198
+ // Load configurations from all sources
199
+ const { getBuiltinSubagents } = await import("./builtinSubagents.js");
200
+ const builtinConfigs = getBuiltinSubagents();
197
201
  const projectConfigs = scanSubagentDirectory(projectDir, "project");
198
202
  const userConfigs = scanSubagentDirectory(userDir, "user");
199
203
 
200
- // Merge configurations, with project configs taking precedence
204
+ // Merge configurations, with project configs taking highest precedence
201
205
  const configMap = new Map<string, SubagentConfiguration>();
202
206
 
203
- // Process in reverse priority order (user first, then project)
204
- for (const config of [...userConfigs, ...projectConfigs]) {
207
+ // Process in reverse priority order (built-in first, then user, then project)
208
+ for (const config of [...builtinConfigs, ...userConfigs, ...projectConfigs]) {
205
209
  configMap.set(config.name, config);
206
210
  }
207
211
 
@@ -0,0 +1,43 @@
1
+ import type { Usage } from "../types/index.js";
2
+
3
+ /**
4
+ * Calculate comprehensive total tokens including cache-related tokens
5
+ *
6
+ * This function computes the true total token cost by including:
7
+ * - Base total_tokens (prompt + completion)
8
+ * - Cache read tokens (cost savings indicator)
9
+ * - Cache creation tokens (cache investment)
10
+ *
11
+ * For accurate cost tracking with Claude models that support cache control.
12
+ *
13
+ * @param usage - Usage statistics from AI operation
14
+ * @returns Comprehensive total including all cache-related tokens
15
+ */
16
+ export function calculateComprehensiveTotalTokens(usage: Usage): number {
17
+ const baseTokens = usage.total_tokens;
18
+ const cacheReadTokens = usage.cache_read_input_tokens || 0;
19
+ const cacheCreateTokens = usage.cache_creation_input_tokens || 0;
20
+
21
+ return baseTokens + cacheReadTokens + cacheCreateTokens;
22
+ }
23
+
24
+ /**
25
+ * Extract the latest total tokens from the last message with usage data
26
+ * Uses comprehensive calculation that includes cache tokens for accurate tracking
27
+ *
28
+ * @param messages - Array of messages to search
29
+ * @returns Comprehensive total tokens from the most recent usage data, or 0 if none found
30
+ */
31
+ export function extractLatestTotalTokens(
32
+ messages: Array<{ usage?: Usage }>,
33
+ ): number {
34
+ // Find the last message with usage data (iterate backwards for efficiency)
35
+ for (let i = messages.length - 1; i >= 0; i--) {
36
+ const message = messages[i];
37
+ if (message.usage) {
38
+ return calculateComprehensiveTotalTokens(message.usage);
39
+ }
40
+ }
41
+
42
+ return 0; // No usage data found
43
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Simple token estimation utility for text content
3
+ *
4
+ * This provides a fast approximation of token count without requiring
5
+ * actual tokenization, which would be expensive for large content.
6
+ */
7
+
8
+ /**
9
+ * Estimate the number of tokens in a text string
10
+ *
11
+ * Uses a simple heuristic based on character count and common patterns:
12
+ * - Average token length varies by language and content type
13
+ * - English text: ~4-5 characters per token
14
+ * - Code/structured text: ~3-4 characters per token
15
+ * - Numbers/symbols: ~2-3 characters per token
16
+ *
17
+ * This function uses a conservative estimate of 4 characters per token
18
+ * which works well for mixed content (text + code + symbols).
19
+ *
20
+ * @param text - The text to estimate tokens for
21
+ * @returns Estimated number of tokens
22
+ */
23
+ export function estimateTokenCount(text: string): number {
24
+ if (!text || text.length === 0) {
25
+ return 0;
26
+ }
27
+
28
+ // Base estimation: 4 characters per token (conservative)
29
+ const baseEstimate = Math.ceil(text.length / 4);
30
+
31
+ // Adjust for whitespace (spaces don't contribute much to token count)
32
+ const whitespaceCount = (text.match(/\s/g) || []).length;
33
+ const adjustedEstimate = Math.ceil((text.length - whitespaceCount * 0.5) / 4);
34
+
35
+ // Use the more conservative (higher) estimate
36
+ return Math.max(baseEstimate, adjustedEstimate);
37
+ }
38
+
39
+ /**
40
+ * Check if estimated token count exceeds a threshold
41
+ *
42
+ * @param text - The text to check
43
+ * @param threshold - Token threshold (default: 20,000)
44
+ * @returns True if estimated tokens exceed threshold
45
+ */
46
+ export function exceedsTokenThreshold(
47
+ text: string,
48
+ threshold: number = 20000,
49
+ ): boolean {
50
+ return estimateTokenCount(text) > threshold;
51
+ }
52
+
53
+ /**
54
+ * Get a human-readable description of estimated token usage
55
+ *
56
+ * @param text - The text to analyze
57
+ * @param threshold - Token threshold for comparison
58
+ * @returns Description string with token count and threshold info
59
+ */
60
+ export function getTokenUsageDescription(
61
+ text: string,
62
+ threshold: number = 20000,
63
+ ): string {
64
+ const estimatedTokens = estimateTokenCount(text);
65
+ const exceedsThreshold = estimatedTokens > threshold;
66
+
67
+ return `${estimatedTokens.toLocaleString()} tokens (${exceedsThreshold ? "exceeds" : "within"} ${threshold.toLocaleString()} limit)`;
68
+ }
@@ -1,38 +0,0 @@
1
- /**
2
- * Configuration resolver utilities for Agent Constructor Configuration
3
- * Resolves configuration from constructor arguments with environment fallbacks
4
- */
5
- import { GatewayConfig, ModelConfig } from "../types/index.js";
6
- export declare class ConfigResolver {
7
- /**
8
- * Resolves gateway configuration from constructor args and environment
9
- * @param apiKey - API key from constructor (optional)
10
- * @param baseURL - Base URL from constructor (optional)
11
- * @returns Resolved gateway configuration
12
- * @throws ConfigurationError if required configuration is missing after fallbacks
13
- */
14
- static resolveGatewayConfig(apiKey?: string, baseURL?: string): GatewayConfig;
15
- /**
16
- * Resolves model configuration with fallbacks
17
- * @param agentModel - Agent model from constructor (optional)
18
- * @param fastModel - Fast model from constructor (optional)
19
- * @returns Resolved model configuration with defaults
20
- */
21
- static resolveModelConfig(agentModel?: string, fastModel?: string): ModelConfig;
22
- /**
23
- * Resolves token limit with fallbacks
24
- * @param constructorLimit - Token limit from constructor (optional)
25
- * @returns Resolved token limit
26
- */
27
- static resolveTokenLimit(constructorLimit?: number): number;
28
- }
29
- /**
30
- * Static configuration resolver instance
31
- * Implements ConfigurationResolver interface from types.ts
32
- */
33
- export declare const configResolver: {
34
- resolveGatewayConfig: typeof ConfigResolver.resolveGatewayConfig;
35
- resolveModelConfig: typeof ConfigResolver.resolveModelConfig;
36
- resolveTokenLimit: typeof ConfigResolver.resolveTokenLimit;
37
- };
38
- //# sourceMappingURL=configResolver.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"configResolver.d.ts","sourceRoot":"","sources":["../../src/utils/configResolver.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,aAAa,EACb,WAAW,EAGZ,MAAM,mBAAmB,CAAC;AAE3B,qBAAa,cAAc;IACzB;;;;;;OAMG;IACH,MAAM,CAAC,oBAAoB,CACzB,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,GACf,aAAa;IAuDhB;;;;;OAKG;IACH,MAAM,CAAC,kBAAkB,CACvB,UAAU,CAAC,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,WAAW;IAmBd;;;;OAIG;IACH,MAAM,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM;CAoB5D;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc;;;;CAI1B,CAAC"}