wave-agent-sdk 0.0.8 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/dist/agent.d.ts +92 -23
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +340 -137
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +2 -0
  7. package/dist/managers/aiManager.d.ts +14 -36
  8. package/dist/managers/aiManager.d.ts.map +1 -1
  9. package/dist/managers/aiManager.js +74 -77
  10. package/dist/managers/backgroundBashManager.d.ts.map +1 -1
  11. package/dist/managers/backgroundBashManager.js +4 -3
  12. package/dist/managers/hookManager.d.ts +3 -8
  13. package/dist/managers/hookManager.d.ts.map +1 -1
  14. package/dist/managers/hookManager.js +39 -29
  15. package/dist/managers/liveConfigManager.d.ts +55 -18
  16. package/dist/managers/liveConfigManager.d.ts.map +1 -1
  17. package/dist/managers/liveConfigManager.js +372 -90
  18. package/dist/managers/lspManager.d.ts +43 -0
  19. package/dist/managers/lspManager.d.ts.map +1 -0
  20. package/dist/managers/lspManager.js +326 -0
  21. package/dist/managers/messageManager.d.ts +8 -16
  22. package/dist/managers/messageManager.d.ts.map +1 -1
  23. package/dist/managers/messageManager.js +52 -74
  24. package/dist/managers/permissionManager.d.ts +66 -0
  25. package/dist/managers/permissionManager.d.ts.map +1 -0
  26. package/dist/managers/permissionManager.js +208 -0
  27. package/dist/managers/skillManager.d.ts +1 -0
  28. package/dist/managers/skillManager.d.ts.map +1 -1
  29. package/dist/managers/skillManager.js +2 -1
  30. package/dist/managers/slashCommandManager.d.ts.map +1 -1
  31. package/dist/managers/slashCommandManager.js +0 -1
  32. package/dist/managers/subagentManager.d.ts +8 -23
  33. package/dist/managers/subagentManager.d.ts.map +1 -1
  34. package/dist/managers/subagentManager.js +97 -117
  35. package/dist/managers/toolManager.d.ts +38 -1
  36. package/dist/managers/toolManager.d.ts.map +1 -1
  37. package/dist/managers/toolManager.js +66 -2
  38. package/dist/services/aiService.d.ts +3 -1
  39. package/dist/services/aiService.d.ts.map +1 -1
  40. package/dist/services/aiService.js +123 -30
  41. package/dist/services/configurationService.d.ts +116 -0
  42. package/dist/services/configurationService.d.ts.map +1 -0
  43. package/dist/services/configurationService.js +585 -0
  44. package/dist/services/fileWatcher.d.ts.map +1 -1
  45. package/dist/services/fileWatcher.js +5 -6
  46. package/dist/services/hook.d.ts +7 -124
  47. package/dist/services/hook.d.ts.map +1 -1
  48. package/dist/services/hook.js +46 -458
  49. package/dist/services/jsonlHandler.d.ts +24 -15
  50. package/dist/services/jsonlHandler.d.ts.map +1 -1
  51. package/dist/services/jsonlHandler.js +67 -88
  52. package/dist/services/memory.d.ts +0 -9
  53. package/dist/services/memory.d.ts.map +1 -1
  54. package/dist/services/memory.js +2 -49
  55. package/dist/services/session.d.ts +82 -33
  56. package/dist/services/session.d.ts.map +1 -1
  57. package/dist/services/session.js +275 -181
  58. package/dist/tools/bashTool.d.ts.map +1 -1
  59. package/dist/tools/bashTool.js +72 -13
  60. package/dist/tools/deleteFileTool.d.ts.map +1 -1
  61. package/dist/tools/deleteFileTool.js +25 -0
  62. package/dist/tools/editTool.d.ts.map +1 -1
  63. package/dist/tools/editTool.js +30 -6
  64. package/dist/tools/lspTool.d.ts +6 -0
  65. package/dist/tools/lspTool.d.ts.map +1 -0
  66. package/dist/tools/lspTool.js +589 -0
  67. package/dist/tools/multiEditTool.d.ts.map +1 -1
  68. package/dist/tools/multiEditTool.js +26 -7
  69. package/dist/tools/readTool.d.ts.map +1 -1
  70. package/dist/tools/readTool.js +111 -2
  71. package/dist/tools/skillTool.js +2 -2
  72. package/dist/tools/todoWriteTool.d.ts.map +1 -1
  73. package/dist/tools/todoWriteTool.js +23 -0
  74. package/dist/tools/types.d.ts +11 -8
  75. package/dist/tools/types.d.ts.map +1 -1
  76. package/dist/tools/writeTool.d.ts.map +1 -1
  77. package/dist/tools/writeTool.js +25 -9
  78. package/dist/types/commands.d.ts +0 -1
  79. package/dist/types/commands.d.ts.map +1 -1
  80. package/dist/types/config.d.ts +4 -0
  81. package/dist/types/config.d.ts.map +1 -1
  82. package/dist/types/configuration.d.ts +69 -0
  83. package/dist/types/configuration.d.ts.map +1 -0
  84. package/dist/types/configuration.js +8 -0
  85. package/dist/types/core.d.ts +10 -0
  86. package/dist/types/core.d.ts.map +1 -1
  87. package/dist/types/environment.d.ts +41 -0
  88. package/dist/types/environment.d.ts.map +1 -1
  89. package/dist/types/fileSearch.d.ts +5 -0
  90. package/dist/types/fileSearch.d.ts.map +1 -0
  91. package/dist/types/fileSearch.js +1 -0
  92. package/dist/types/hooks.d.ts +11 -2
  93. package/dist/types/hooks.d.ts.map +1 -1
  94. package/dist/types/hooks.js +1 -7
  95. package/dist/types/index.d.ts +5 -0
  96. package/dist/types/index.d.ts.map +1 -1
  97. package/dist/types/index.js +5 -0
  98. package/dist/types/lsp.d.ts +90 -0
  99. package/dist/types/lsp.d.ts.map +1 -0
  100. package/dist/types/lsp.js +4 -0
  101. package/dist/types/messaging.d.ts +6 -11
  102. package/dist/types/messaging.d.ts.map +1 -1
  103. package/dist/types/permissions.d.ts +35 -0
  104. package/dist/types/permissions.d.ts.map +1 -0
  105. package/dist/types/permissions.js +12 -0
  106. package/dist/types/session.d.ts +1 -6
  107. package/dist/types/session.d.ts.map +1 -1
  108. package/dist/types/skills.d.ts +1 -0
  109. package/dist/types/skills.d.ts.map +1 -1
  110. package/dist/types/tools.d.ts +35 -0
  111. package/dist/types/tools.d.ts.map +1 -0
  112. package/dist/types/tools.js +4 -0
  113. package/dist/utils/abortUtils.d.ts +34 -0
  114. package/dist/utils/abortUtils.d.ts.map +1 -0
  115. package/dist/utils/abortUtils.js +92 -0
  116. package/dist/utils/bashHistory.d.ts +4 -0
  117. package/dist/utils/bashHistory.d.ts.map +1 -1
  118. package/dist/utils/bashHistory.js +21 -4
  119. package/dist/utils/builtinSubagents.d.ts +7 -0
  120. package/dist/utils/builtinSubagents.d.ts.map +1 -0
  121. package/dist/utils/builtinSubagents.js +65 -0
  122. package/dist/utils/cacheControlUtils.d.ts +8 -33
  123. package/dist/utils/cacheControlUtils.d.ts.map +1 -1
  124. package/dist/utils/cacheControlUtils.js +83 -126
  125. package/dist/utils/constants.d.ts +0 -12
  126. package/dist/utils/constants.d.ts.map +1 -1
  127. package/dist/utils/constants.js +1 -13
  128. package/dist/utils/convertMessagesForAPI.d.ts +2 -1
  129. package/dist/utils/convertMessagesForAPI.d.ts.map +1 -1
  130. package/dist/utils/convertMessagesForAPI.js +33 -14
  131. package/dist/utils/fileSearch.d.ts +14 -0
  132. package/dist/utils/fileSearch.d.ts.map +1 -0
  133. package/dist/utils/fileSearch.js +88 -0
  134. package/dist/utils/fileUtils.d.ts +14 -2
  135. package/dist/utils/fileUtils.d.ts.map +1 -1
  136. package/dist/utils/fileUtils.js +101 -17
  137. package/dist/utils/globalLogger.d.ts +0 -14
  138. package/dist/utils/globalLogger.d.ts.map +1 -1
  139. package/dist/utils/globalLogger.js +0 -16
  140. package/dist/utils/largeOutputHandler.d.ts +15 -0
  141. package/dist/utils/largeOutputHandler.d.ts.map +1 -0
  142. package/dist/utils/largeOutputHandler.js +40 -0
  143. package/dist/utils/markdownParser.d.ts.map +1 -1
  144. package/dist/utils/markdownParser.js +1 -17
  145. package/dist/utils/messageOperations.d.ts +1 -11
  146. package/dist/utils/messageOperations.d.ts.map +1 -1
  147. package/dist/utils/messageOperations.js +7 -24
  148. package/dist/utils/pathEncoder.d.ts +4 -0
  149. package/dist/utils/pathEncoder.d.ts.map +1 -1
  150. package/dist/utils/pathEncoder.js +16 -9
  151. package/dist/utils/subagentParser.d.ts +2 -2
  152. package/dist/utils/subagentParser.d.ts.map +1 -1
  153. package/dist/utils/subagentParser.js +10 -7
  154. package/dist/utils/tokenEstimator.d.ts +39 -0
  155. package/dist/utils/tokenEstimator.d.ts.map +1 -0
  156. package/dist/utils/tokenEstimator.js +55 -0
  157. package/package.json +5 -8
  158. package/src/agent.ts +460 -216
  159. package/src/index.ts +2 -0
  160. package/src/managers/aiManager.ts +107 -111
  161. package/src/managers/backgroundBashManager.ts +4 -3
  162. package/src/managers/hookManager.ts +44 -39
  163. package/src/managers/liveConfigManager.ts +524 -138
  164. package/src/managers/lspManager.ts +434 -0
  165. package/src/managers/messageManager.ts +73 -103
  166. package/src/managers/permissionManager.ts +276 -0
  167. package/src/managers/skillManager.ts +3 -1
  168. package/src/managers/slashCommandManager.ts +1 -2
  169. package/src/managers/subagentManager.ts +116 -159
  170. package/src/managers/toolManager.ts +95 -3
  171. package/src/services/aiService.ts +207 -26
  172. package/src/services/configurationService.ts +762 -0
  173. package/src/services/fileWatcher.ts +5 -6
  174. package/src/services/hook.ts +50 -631
  175. package/src/services/jsonlHandler.ts +84 -100
  176. package/src/services/memory.ts +2 -59
  177. package/src/services/session.ts +338 -213
  178. package/src/tools/bashTool.ts +89 -16
  179. package/src/tools/deleteFileTool.ts +36 -0
  180. package/src/tools/editTool.ts +41 -7
  181. package/src/tools/lspTool.ts +760 -0
  182. package/src/tools/multiEditTool.ts +37 -8
  183. package/src/tools/readTool.ts +125 -2
  184. package/src/tools/skillTool.ts +2 -2
  185. package/src/tools/todoWriteTool.ts +33 -1
  186. package/src/tools/types.ts +15 -9
  187. package/src/tools/writeTool.ts +36 -10
  188. package/src/types/commands.ts +0 -1
  189. package/src/types/config.ts +5 -0
  190. package/src/types/configuration.ts +73 -0
  191. package/src/types/core.ts +11 -0
  192. package/src/types/environment.ts +44 -0
  193. package/src/types/fileSearch.ts +4 -0
  194. package/src/types/hooks.ts +14 -11
  195. package/src/types/index.ts +5 -0
  196. package/src/types/lsp.ts +96 -0
  197. package/src/types/messaging.ts +8 -13
  198. package/src/types/permissions.ts +48 -0
  199. package/src/types/session.ts +3 -8
  200. package/src/types/skills.ts +1 -0
  201. package/src/types/tools.ts +38 -0
  202. package/src/utils/abortUtils.ts +118 -0
  203. package/src/utils/bashHistory.ts +28 -4
  204. package/src/utils/builtinSubagents.ts +71 -0
  205. package/src/utils/cacheControlUtils.ts +106 -171
  206. package/src/utils/constants.ts +1 -16
  207. package/src/utils/convertMessagesForAPI.ts +38 -14
  208. package/src/utils/fileSearch.ts +107 -0
  209. package/src/utils/fileUtils.ts +114 -19
  210. package/src/utils/globalLogger.ts +0 -17
  211. package/src/utils/largeOutputHandler.ts +55 -0
  212. package/src/utils/markdownParser.ts +1 -19
  213. package/src/utils/messageOperations.ts +7 -35
  214. package/src/utils/pathEncoder.ts +24 -9
  215. package/src/utils/subagentParser.ts +11 -8
  216. package/src/utils/tokenEstimator.ts +68 -0
  217. package/dist/constants/events.d.ts +0 -28
  218. package/dist/constants/events.d.ts.map +0 -1
  219. package/dist/constants/events.js +0 -27
  220. package/dist/services/configurationWatcher.d.ts +0 -120
  221. package/dist/services/configurationWatcher.d.ts.map +0 -1
  222. package/dist/services/configurationWatcher.js +0 -439
  223. package/dist/services/memoryStore.d.ts +0 -81
  224. package/dist/services/memoryStore.d.ts.map +0 -1
  225. package/dist/services/memoryStore.js +0 -200
  226. package/dist/types/memoryStore.d.ts +0 -82
  227. package/dist/types/memoryStore.d.ts.map +0 -1
  228. package/dist/types/memoryStore.js +0 -7
  229. package/dist/utils/configResolver.d.ts +0 -65
  230. package/dist/utils/configResolver.d.ts.map +0 -1
  231. package/dist/utils/configResolver.js +0 -210
  232. package/src/constants/events.ts +0 -38
  233. package/src/services/configurationWatcher.ts +0 -622
  234. package/src/services/memoryStore.ts +0 -279
  235. package/src/types/memoryStore.ts +0 -94
  236. package/src/utils/configResolver.ts +0 -302
@@ -11,8 +11,10 @@ import type {
11
11
  ChatCompletionContentPart,
12
12
  ChatCompletionContentPartText,
13
13
  ChatCompletionFunctionTool,
14
+ ChatCompletionMessageToolCall,
14
15
  CompletionUsage,
15
16
  } from "openai/resources";
17
+ import { logger } from "./globalLogger.js";
16
18
 
17
19
  // ============================================================================
18
20
  // Core Types
@@ -62,28 +64,10 @@ export interface ClaudeUsage extends CompletionUsage {
62
64
  };
63
65
  }
64
66
 
65
- /**
66
- * Configuration for cache control application
67
- */
68
- export interface CacheControlConfig {
69
- cacheSystemMessage: boolean;
70
- cacheUserMessageCount: number;
71
- cacheLastTool: boolean;
72
- }
73
-
74
67
  // ============================================================================
75
68
  // Default Configuration
76
69
  // ============================================================================
77
70
 
78
- /**
79
- * Default cache control configuration
80
- */
81
- export const DEFAULT_CACHE_CONTROL_CONFIG: CacheControlConfig = {
82
- cacheSystemMessage: true,
83
- cacheUserMessageCount: 2,
84
- cacheLastTool: true,
85
- } as const;
86
-
87
71
  // ============================================================================
88
72
  // Utility Functions (Basic Structure - to be implemented)
89
73
  // ============================================================================
@@ -123,6 +107,30 @@ export function isValidCacheControl(control: unknown): control is CacheControl {
123
107
  );
124
108
  }
125
109
 
110
+ /**
111
+ * Adds cache control to the last tool call in an array
112
+ * @param toolCalls - Array of tool calls
113
+ * @returns Tool calls array with cache control on the last tool call
114
+ */
115
+ function addCacheControlToLastToolCall(
116
+ toolCalls: ChatCompletionMessageToolCall[],
117
+ ): ChatCompletionMessageToolCall[] {
118
+ if (!toolCalls || toolCalls.length === 0) {
119
+ return toolCalls;
120
+ }
121
+
122
+ const result = [...toolCalls];
123
+ const lastIndex = result.length - 1;
124
+
125
+ // Add cache control to the last tool call
126
+ result[lastIndex] = {
127
+ ...result[lastIndex],
128
+ cache_control: { type: "ephemeral" },
129
+ } as ChatCompletionMessageToolCall & { cache_control: CacheControl };
130
+
131
+ return result;
132
+ }
133
+
126
134
  /**
127
135
  * Adds cache control markers to message content
128
136
  * @param content - Original content (string or structured)
@@ -146,7 +154,7 @@ export function addCacheControlToContent(
146
154
 
147
155
  // Validate array input
148
156
  if (!Array.isArray(content)) {
149
- console.warn(
157
+ logger.warn(
150
158
  "Invalid content type for cache control transformation:",
151
159
  typeof content,
152
160
  );
@@ -178,7 +186,7 @@ export function addCacheControlToContent(
178
186
 
179
187
  // Validate array input
180
188
  if (!Array.isArray(content)) {
181
- console.warn(
189
+ logger.warn(
182
190
  "Invalid content type for cache control transformation:",
183
191
  typeof content,
184
192
  );
@@ -216,11 +224,11 @@ export function addCacheControlToLastTool(
216
224
  // Validate tools structure
217
225
  const validTools = tools.filter((tool) => {
218
226
  if (!tool || typeof tool !== "object") {
219
- console.warn("Invalid tool structure detected, skipping:", tool);
227
+ logger.warn("Invalid tool structure detected, skipping:", tool);
220
228
  return false;
221
229
  }
222
230
  if (tool.type !== "function" || !tool.function) {
223
- console.warn(
231
+ logger.warn(
224
232
  "Tool is not a function type or missing function property:",
225
233
  tool,
226
234
  );
@@ -230,7 +238,7 @@ export function addCacheControlToLastTool(
230
238
  });
231
239
 
232
240
  if (validTools.length === 0) {
233
- console.warn("No valid tools found for cache control");
241
+ logger.warn("No valid tools found for cache control");
234
242
  return [];
235
243
  }
236
244
 
@@ -250,20 +258,47 @@ export function addCacheControlToLastTool(
250
258
  }
251
259
 
252
260
  /**
253
- * Transforms messages for Claude cache control
261
+ * Finds the latest message index at 20-message intervals (sliding window approach)
262
+ * @param messages - Array of chat completion messages
263
+ * @returns Index of the latest interval message (20th, 40th, 60th, etc.) or -1 if none
264
+ */
265
+ export function findIntervalMessageIndex(
266
+ messages: ChatCompletionMessageParam[],
267
+ ): number {
268
+ if (!Array.isArray(messages) || messages.length === 0) {
269
+ return -1;
270
+ }
271
+
272
+ const interval = 20; // Hardcoded interval
273
+ const messageCount = messages.length;
274
+
275
+ // Find the largest interval that fits within the message count
276
+ // Math.floor(messageCount / interval) gives us how many complete intervals we have
277
+ // Multiply by interval to get the position of the latest interval message
278
+ const latestIntervalPosition = Math.floor(messageCount / interval) * interval;
279
+
280
+ // If no complete intervals exist, return -1
281
+ if (latestIntervalPosition === 0) {
282
+ return -1;
283
+ }
284
+
285
+ // Convert from 1-based position to 0-based index
286
+ return latestIntervalPosition - 1;
287
+ }
288
+
289
+ /**
290
+ * Transforms messages for Claude cache control with hardcoded strategy
254
291
  * @param messages - Original OpenAI message array
255
292
  * @param modelName - Model name for cache detection
256
- * @param config - Cache control configuration
257
293
  * @returns Messages with cache control markers applied
258
294
  */
259
295
  export function transformMessagesForClaudeCache(
260
296
  messages: ChatCompletionMessageParam[],
261
297
  modelName: string,
262
- config: CacheControlConfig = DEFAULT_CACHE_CONTROL_CONFIG,
263
298
  ): ChatCompletionMessageParam[] {
264
299
  // Validate inputs
265
300
  if (!messages || !Array.isArray(messages)) {
266
- console.warn(
301
+ logger.warn(
267
302
  "Invalid messages array provided to transformMessagesForClaudeCache",
268
303
  );
269
304
  return [];
@@ -278,46 +313,64 @@ export function transformMessagesForClaudeCache(
278
313
  return messages;
279
314
  }
280
315
 
281
- // Validate config
282
- if (!config || typeof config !== "object") {
283
- console.warn("Invalid cache control config, using defaults");
284
- config = DEFAULT_CACHE_CONTROL_CONFIG;
316
+ // Find the latest interval message index (20th, 40th, 60th, etc.)
317
+ const intervalMessageIndex = findIntervalMessageIndex(messages);
318
+
319
+ // Find last system message index
320
+ let lastSystemIndex = -1;
321
+ for (let i = messages.length - 1; i >= 0; i--) {
322
+ if (messages[i].role === "system") {
323
+ lastSystemIndex = i;
324
+ break;
325
+ }
285
326
  }
286
327
 
287
328
  const result = messages.map((message, index) => {
288
329
  // Validate message structure
289
330
  if (!message || typeof message !== "object" || !message.role) {
290
- console.warn("Invalid message structure at index", index, ":", message);
331
+ logger.warn("Invalid message structure at index", index, ":", message);
291
332
  return message; // Return as-is to avoid breaking the flow
292
333
  }
293
334
 
294
- // System message: cache if enabled in config
295
- if (message.role === "system" && config.cacheSystemMessage) {
335
+ // Last system message: always cached (hardcoded)
336
+ if (message.role === "system" && index === lastSystemIndex) {
296
337
  return {
297
338
  ...message,
298
- content: addCacheControlToContent(message.content, true),
299
- };
339
+ content: addCacheControlToContent(
340
+ (message.content as string | ChatCompletionContentPart[]) || "",
341
+ true,
342
+ ),
343
+ } as ChatCompletionMessageParam;
300
344
  }
301
345
 
302
- // User messages: cache last N messages based on config
303
- if (message.role === "user" && config.cacheUserMessageCount > 0) {
304
- const userMessageIndices: number[] = [];
305
- messages.forEach((msg, idx) => {
306
- if (msg.role === "user") {
307
- userMessageIndices.push(idx);
308
- }
309
- });
310
-
311
- // Check if this user message is among the last N
312
- const isRecentUser = userMessageIndices
313
- .slice(-config.cacheUserMessageCount)
314
- .includes(index);
315
-
316
- if (isRecentUser) {
346
+ // Interval-based message caching: cache message at latest interval position (sliding window)
347
+ if (index === intervalMessageIndex) {
348
+ // If the message is a tool role, add cache control directly to the message
349
+ if (message.role === "tool") {
350
+ return {
351
+ ...message,
352
+ cache_control: { type: "ephemeral" },
353
+ } as ChatCompletionMessageParam;
354
+ }
355
+ // If the message has tool calls, cache the last tool call instead of content
356
+ else if (
357
+ message.role === "assistant" &&
358
+ message.tool_calls &&
359
+ message.tool_calls.length > 0
360
+ ) {
361
+ return {
362
+ ...message,
363
+ tool_calls: addCacheControlToLastToolCall(message.tool_calls),
364
+ } as ChatCompletionMessageParam;
365
+ } else {
366
+ // For other message types without tool calls, cache the content
317
367
  return {
318
368
  ...message,
319
- content: addCacheControlToContent(message.content, true),
320
- };
369
+ content: addCacheControlToContent(
370
+ (message.content as string | ChatCompletionContentPart[]) || "",
371
+ true,
372
+ ),
373
+ } as ChatCompletionMessageParam;
321
374
  }
322
375
  }
323
376
 
@@ -420,121 +473,3 @@ export function isValidClaudeUsage(usage: unknown): usage is ClaudeUsage {
420
473
 
421
474
  return true;
422
475
  }
423
-
424
- /**
425
- * Adds cache control to the last N user messages in a conversation
426
- * This optimizes multi-turn conversations by caching recent user context
427
- *
428
- * @param messages - Array of chat completion messages
429
- * @param maxUserMessagesToCache - Maximum number of recent user messages to cache (default: 2)
430
- * @returns Modified messages array with cache control on recent user messages
431
- */
432
- export function addCacheControlToRecentUserMessages(
433
- messages: ChatCompletionMessageParam[],
434
- maxUserMessagesToCache: number = 2,
435
- ): ChatCompletionMessageParam[] {
436
- // Validate inputs
437
- if (!messages || !Array.isArray(messages)) {
438
- console.warn(
439
- "Invalid messages array provided to addCacheControlToRecentUserMessages",
440
- );
441
- return [];
442
- }
443
-
444
- if (messages.length === 0 || maxUserMessagesToCache <= 0) {
445
- return messages;
446
- }
447
-
448
- // Validate maxUserMessagesToCache is a reasonable number
449
- if (maxUserMessagesToCache > 100) {
450
- console.warn(
451
- "maxUserMessagesToCache is unusually high:",
452
- maxUserMessagesToCache,
453
- "limiting to 100",
454
- );
455
- maxUserMessagesToCache = 100;
456
- }
457
-
458
- // Find all user message indices in reverse order (most recent first)
459
- const userMessageIndices: number[] = [];
460
- for (let i = messages.length - 1; i >= 0; i--) {
461
- const message = messages[i];
462
-
463
- // Validate message structure
464
- if (!message || typeof message !== "object" || !message.role) {
465
- console.warn("Invalid message at index", i, ", skipping");
466
- continue;
467
- }
468
-
469
- if (message.role === "user") {
470
- userMessageIndices.push(i);
471
- if (userMessageIndices.length >= maxUserMessagesToCache) {
472
- break;
473
- }
474
- }
475
- }
476
-
477
- // If no user messages found, return unchanged
478
- if (userMessageIndices.length === 0) {
479
- return messages;
480
- }
481
-
482
- // Create a copy of messages and modify the identified user messages
483
- const modifiedMessages = [...messages];
484
-
485
- for (const index of userMessageIndices) {
486
- const message = modifiedMessages[index];
487
- if (message.role === "user" && message.content != null) {
488
- try {
489
- modifiedMessages[index] = {
490
- ...message,
491
- content: addCacheControlToContent(message.content, true),
492
- };
493
- } catch (error) {
494
- console.warn(
495
- "Failed to add cache control to user message at index",
496
- index,
497
- ":",
498
- error,
499
- );
500
- // Continue with original message if transformation fails
501
- }
502
- }
503
- }
504
-
505
- return modifiedMessages;
506
- }
507
-
508
- /**
509
- * Helper function to identify user message indices that should be cached
510
- * Used for testing and validation purposes
511
- *
512
- * @param messages - Array of chat completion messages
513
- * @param maxUserMessagesToCache - Maximum number of recent user messages to identify
514
- * @returns Array of indices for user messages that should be cached
515
- */
516
- export function findRecentUserMessageIndices(
517
- messages: ChatCompletionMessageParam[],
518
- maxUserMessagesToCache: number = 2,
519
- ): number[] {
520
- if (
521
- !Array.isArray(messages) ||
522
- messages.length === 0 ||
523
- maxUserMessagesToCache <= 0
524
- ) {
525
- return [];
526
- }
527
-
528
- const userMessageIndices: number[] = [];
529
- for (let i = messages.length - 1; i >= 0; i--) {
530
- if (messages[i].role === "user") {
531
- userMessageIndices.push(i);
532
- if (userMessageIndices.length >= maxUserMessagesToCache) {
533
- break;
534
- }
535
- }
536
- }
537
-
538
- // Return indices in original order (not reversed)
539
- return userMessageIndices.reverse();
540
- }
@@ -24,24 +24,9 @@ export const ERROR_LOG_DIRECTORY = path.join(DATA_DIRECTORY, "error-logs");
24
24
  /**
25
25
  * User-level memory file path
26
26
  */
27
- export const USER_MEMORY_FILE = path.join(DATA_DIRECTORY, "user-memory.md");
27
+ export const USER_MEMORY_FILE = path.join(DATA_DIRECTORY, "AGENTS.md");
28
28
 
29
29
  /**
30
30
  * AI related constants
31
31
  */
32
32
  export const DEFAULT_TOKEN_LIMIT = 96000; // Default token limit
33
-
34
- /**
35
- * @deprecated These constants are now legacy. Use ModelConfig through Agent constructor instead.
36
- * They are maintained for backward compatibility with existing code that might still reference them,
37
- * but the actual AI services now use configuration injection.
38
- */
39
- export const FAST_MODEL_ID = process.env.AIGW_FAST_MODEL || "gemini-2.5-flash";
40
-
41
- /**
42
- * @deprecated These constants are now legacy. Use ModelConfig through Agent constructor instead.
43
- * They are maintained for backward compatibility with existing code that might still reference them,
44
- * but the actual AI services now use configuration injection.
45
- */
46
- export const AGENT_MODEL_ID =
47
- process.env.AIGW_MODEL || "claude-sonnet-4-20250514";
@@ -30,7 +30,8 @@ function safeToolArguments(args: string): string {
30
30
  }
31
31
 
32
32
  /**
33
- * Convert message format to API call format, stopping when a compressed message is encountered
33
+ * Convert message format to API call format, stopping when a compressed message is encountered.
34
+ * Messages with no meaningful content or tool calls are filtered out.
34
35
  * @param messages Message list
35
36
  * @returns Converted API message format list
36
37
  */
@@ -61,7 +62,7 @@ export function convertMessagesForAPI(
61
62
  break;
62
63
  }
63
64
 
64
- // Skip empty assistant messages
65
+ // Skip empty assistant messages (no blocks or all blocks are empty)
65
66
  if (message.role === "assistant" && message.blocks.length === 0) {
66
67
  continue;
67
68
  }
@@ -128,12 +129,17 @@ export function convertMessagesForAPI(
128
129
  let content = "";
129
130
  let tool_calls: ChatCompletionMessageToolCall[] | undefined = undefined;
130
131
 
131
- // Construct content from text blocks
132
+ // Construct content from text blocks - filter out empty content
132
133
  const textBlocks = message.blocks.filter(
133
- (block) => block.type === "text",
134
+ (block) =>
135
+ block.type === "text" &&
136
+ block.content &&
137
+ block.content.trim().length > 0,
134
138
  );
135
139
  if (textBlocks.length > 0) {
136
- content = textBlocks.map((block) => block.content || "").join("\n");
140
+ content = textBlocks
141
+ .map((block) => (block.type === "text" ? block.content : ""))
142
+ .join("\n");
137
143
  }
138
144
 
139
145
  // Construct tool calls from tool blocks
@@ -158,13 +164,16 @@ export function convertMessagesForAPI(
158
164
  }
159
165
  }
160
166
 
161
- // Construct assistant message - only add if there is content or tool calls
162
- if (content || tool_calls) {
167
+ // Construct assistant message - only add if there is meaningful content or tool calls
168
+ const hasContent = content && content.trim().length > 0;
169
+ const hasToolCalls = tool_calls && tool_calls.length > 0;
170
+
171
+ if (hasContent || hasToolCalls) {
163
172
  const assistantMessage: ChatCompletionMessageParam = {
164
173
  role: "assistant",
165
- content,
174
+ content: hasContent ? content : undefined,
166
175
  tool_calls,
167
- ...(message.metadata ? { ...message.metadata } : {}),
176
+ ...(message.additionalFields ? { ...message.additionalFields } : {}),
168
177
  };
169
178
 
170
179
  recentMessages.unshift(assistantMessage);
@@ -174,8 +183,12 @@ export function convertMessagesForAPI(
174
183
  const contentParts: ChatCompletionContentPart[] = [];
175
184
 
176
185
  message.blocks.forEach((block) => {
177
- // Add text content
178
- if (block.type === "text" && block.content) {
186
+ // Add text content - only if it has meaningful content
187
+ if (
188
+ block.type === "text" &&
189
+ block.content &&
190
+ block.content.trim().length > 0
191
+ ) {
179
192
  contentParts.push({
180
193
  type: "text",
181
194
  text: block.customCommandContent || block.content,
@@ -217,11 +230,22 @@ export function convertMessagesForAPI(
217
230
  }
218
231
  });
219
232
 
233
+ // Only add user message if there is meaningful content
220
234
  if (contentParts.length > 0) {
221
- recentMessages.unshift({
222
- role: "user",
223
- content: contentParts,
235
+ // Filter out empty text parts
236
+ const meaningfulParts = contentParts.filter((part) => {
237
+ if (part.type === "text") {
238
+ return part.text && part.text.trim().length > 0;
239
+ }
240
+ return true; // Keep image parts
224
241
  });
242
+
243
+ if (meaningfulParts.length > 0) {
244
+ recentMessages.unshift({
245
+ role: "user",
246
+ content: meaningfulParts,
247
+ });
248
+ }
225
249
  }
226
250
  }
227
251
  }
@@ -0,0 +1,107 @@
1
+ import { globIterate, type Path } from "glob";
2
+ import { getGlobIgnorePatterns } from "./fileFilter.js";
3
+ import type { FileItem } from "../types/fileSearch.js";
4
+
5
+ /**
6
+ * Convert Path objects to FileItem objects
7
+ */
8
+ export const convertPathsToFileItems = (paths: Path[]): FileItem[] => {
9
+ return paths.map((pathObj) => ({
10
+ path: pathObj.relative(),
11
+ type: pathObj.isDirectory() ? "directory" : "file",
12
+ }));
13
+ };
14
+
15
+ /**
16
+ * Search files and directories using glob patterns
17
+ */
18
+ export const searchFiles = async (
19
+ query: string,
20
+ options?: {
21
+ maxResults?: number;
22
+ workingDirectory?: string;
23
+ },
24
+ ): Promise<FileItem[]> => {
25
+ const { maxResults = 10, workingDirectory = process.cwd() } = options || {};
26
+
27
+ try {
28
+ const globOptions: import("glob").GlobOptionsWithFileTypesTrue = {
29
+ ignore: getGlobIgnorePatterns(workingDirectory),
30
+ maxDepth: 10,
31
+ nocase: true, // Case insensitive
32
+ dot: true, // Include hidden files and directories
33
+ cwd: workingDirectory, // Specify search root directory
34
+ withFileTypes: true, // Get Path objects instead of strings
35
+ };
36
+
37
+ // Build glob patterns based on query
38
+ let patterns: string[] = [];
39
+
40
+ if (!query.trim()) {
41
+ // When query is empty, show some common file types and directories
42
+ patterns = [
43
+ "**/*.{ts,tsx,js,jsx,json,py,java}", // Combine common file extensions
44
+ "*/", // First level directories
45
+ ];
46
+ } else {
47
+ // Build multiple glob patterns to support more flexible search
48
+ patterns = [
49
+ // Match files with filenames containing query
50
+ `**/*${query}*`,
51
+ // Match files with query in path (match directory names)
52
+ `**/${query}*/**/*`,
53
+ // Match directory names containing query
54
+ `**/*${query}*/`,
55
+ // Match directories containing query in path
56
+ `**/${query}*/`,
57
+ ];
58
+ }
59
+
60
+ // Collect results until we reach maxResults
61
+ const collectedPaths: Path[] = [];
62
+ const seenPaths = new Set<string>();
63
+
64
+ // Process each pattern sequentially
65
+ for (const pattern of patterns) {
66
+ if (collectedPaths.length >= maxResults) {
67
+ break;
68
+ }
69
+
70
+ // Use globIterate to get results one by one
71
+ const iterator = globIterate(pattern, globOptions) as AsyncGenerator<
72
+ Path,
73
+ void,
74
+ void
75
+ >;
76
+
77
+ for await (const pathObj of iterator) {
78
+ if (collectedPaths.length >= maxResults) {
79
+ // Stop the iterator when we have enough results
80
+ break;
81
+ }
82
+
83
+ const relativePath = pathObj.relative();
84
+ if (!seenPaths.has(relativePath)) {
85
+ seenPaths.add(relativePath);
86
+ collectedPaths.push(pathObj);
87
+ }
88
+ }
89
+ }
90
+
91
+ // Sort collected paths: directories first, then files
92
+ collectedPaths.sort((a, b) => {
93
+ const aIsDir = a.isDirectory();
94
+ const bIsDir = b.isDirectory();
95
+ if (aIsDir && !bIsDir) return -1;
96
+ if (!aIsDir && bIsDir) return 1;
97
+ return a.relative().localeCompare(b.relative());
98
+ });
99
+
100
+ // Convert to FileItems
101
+ const fileItems = convertPathsToFileItems(collectedPaths);
102
+ return fileItems;
103
+ } catch (error) {
104
+ console.error("Glob search error:", error);
105
+ return [];
106
+ }
107
+ };