wave-agent-sdk 0.17.9 → 0.17.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 (73) hide show
  1. package/builtin/skills/settings/ENV.md +1 -1
  2. package/dist/managers/aiManager.d.ts +10 -4
  3. package/dist/managers/aiManager.d.ts.map +1 -1
  4. package/dist/managers/aiManager.js +214 -200
  5. package/dist/managers/backgroundTaskManager.js +1 -1
  6. package/dist/managers/bangManager.js +1 -1
  7. package/dist/managers/hookManager.js +2 -4
  8. package/dist/managers/mcpManager.d.ts.map +1 -1
  9. package/dist/managers/mcpManager.js +1 -3
  10. package/dist/managers/messageManager.d.ts.map +1 -1
  11. package/dist/managers/messageManager.js +1 -8
  12. package/dist/managers/toolManager.d.ts +6 -0
  13. package/dist/managers/toolManager.d.ts.map +1 -1
  14. package/dist/managers/toolManager.js +17 -12
  15. package/dist/services/aiService.d.ts.map +1 -1
  16. package/dist/services/aiService.js +20 -12
  17. package/dist/services/configurationService.d.ts.map +1 -1
  18. package/dist/services/configurationService.js +13 -0
  19. package/dist/services/jsonlHandler.d.ts +1 -1
  20. package/dist/services/jsonlHandler.d.ts.map +1 -1
  21. package/dist/services/jsonlHandler.js +7 -22
  22. package/dist/services/session.d.ts +2 -9
  23. package/dist/services/session.d.ts.map +1 -1
  24. package/dist/services/session.js +16 -46
  25. package/dist/tools/bashTool.d.ts.map +1 -1
  26. package/dist/tools/bashTool.js +3 -1
  27. package/dist/tools/editTool.d.ts.map +1 -1
  28. package/dist/tools/editTool.js +28 -1
  29. package/dist/tools/enterPlanMode.d.ts.map +1 -1
  30. package/dist/tools/enterPlanMode.js +8 -0
  31. package/dist/tools/exitPlanMode.d.ts.map +1 -1
  32. package/dist/tools/exitPlanMode.js +8 -0
  33. package/dist/tools/readTool.d.ts.map +1 -1
  34. package/dist/tools/readTool.js +18 -10
  35. package/dist/tools/types.d.ts +8 -2
  36. package/dist/tools/types.d.ts.map +1 -1
  37. package/dist/tools/writeTool.d.ts.map +1 -1
  38. package/dist/tools/writeTool.js +14 -1
  39. package/dist/types/agent.d.ts +0 -2
  40. package/dist/types/agent.d.ts.map +1 -1
  41. package/dist/types/config.d.ts +1 -0
  42. package/dist/types/config.d.ts.map +1 -1
  43. package/dist/utils/cacheControlUtils.d.ts +20 -15
  44. package/dist/utils/cacheControlUtils.d.ts.map +1 -1
  45. package/dist/utils/cacheControlUtils.js +69 -66
  46. package/dist/utils/constants.d.ts +1 -1
  47. package/dist/utils/constants.js +1 -1
  48. package/dist/utils/containerSetup.d.ts.map +1 -1
  49. package/dist/utils/containerSetup.js +1 -6
  50. package/package.json +1 -1
  51. package/src/managers/aiManager.ts +292 -263
  52. package/src/managers/backgroundTaskManager.ts +1 -1
  53. package/src/managers/bangManager.ts +1 -1
  54. package/src/managers/hookManager.ts +6 -6
  55. package/src/managers/mcpManager.ts +2 -4
  56. package/src/managers/messageManager.ts +1 -9
  57. package/src/managers/toolManager.ts +18 -12
  58. package/src/services/aiService.ts +23 -12
  59. package/src/services/configurationService.ts +21 -0
  60. package/src/services/jsonlHandler.ts +7 -27
  61. package/src/services/session.ts +16 -57
  62. package/src/tools/bashTool.ts +3 -1
  63. package/src/tools/editTool.ts +31 -1
  64. package/src/tools/enterPlanMode.ts +9 -0
  65. package/src/tools/exitPlanMode.ts +10 -0
  66. package/src/tools/readTool.ts +20 -10
  67. package/src/tools/types.ts +15 -3
  68. package/src/tools/writeTool.ts +15 -1
  69. package/src/types/agent.ts +0 -2
  70. package/src/types/config.ts +1 -0
  71. package/src/utils/cacheControlUtils.ts +77 -95
  72. package/src/utils/constants.ts +1 -1
  73. package/src/utils/containerSetup.ts +3 -7
@@ -19,5 +19,6 @@ export interface ModelConfig {
19
19
  fastModel?: string;
20
20
  maxTokens?: number;
21
21
  permissionMode?: PermissionMode;
22
+ fastModelConfig?: Record<string, unknown>;
22
23
  [key: string]: unknown;
23
24
  }
@@ -10,7 +10,6 @@ import type {
10
10
  ChatCompletionMessageParam,
11
11
  ChatCompletionContentPart,
12
12
  ChatCompletionContentPartText,
13
- ChatCompletionFunctionTool,
14
13
  CompletionUsage,
15
14
  } from "openai/resources";
16
15
  import { logger } from "./globalLogger.js";
@@ -36,16 +35,6 @@ export interface ClaudeChatCompletionContentPartText
36
35
  cache_control?: CacheControl;
37
36
  }
38
37
 
39
- /**
40
- * Extended tool definition with cache control support
41
- */
42
- export interface ClaudeChatCompletionFunctionTool
43
- extends ChatCompletionFunctionTool {
44
- type: "function";
45
- function: ChatCompletionFunctionTool["function"];
46
- cache_control?: CacheControl;
47
- }
48
-
49
38
  /**
50
39
  * Extended prompt_tokens_details with cache_creation_input_tokens
51
40
  * Some models (e.g. Gemini, DeepSeek) return this field inside prompt_tokens_details
@@ -229,68 +218,53 @@ export function addCacheControlToContent(
229
218
  }
230
219
 
231
220
  /**
232
- * Adds cache control to the last tool in tools array
233
- * @param tools - Array of tool definitions
234
- * @returns Tools array with cache control on last tool
221
+ * Counts the total number of content blocks across all messages.
222
+ * Each element in a message's content array counts as one block.
223
+ * String content counts as one block. Null/undefined content counts as zero
224
+ * (e.g. assistant messages with only tool_calls).
225
+ * @param messages - Array of chat messages
226
+ * @returns Total content block count
235
227
  */
236
- export function addCacheControlToLastTool(
237
- tools: ChatCompletionFunctionTool[],
238
- ): ClaudeChatCompletionFunctionTool[] {
239
- // Handle null, undefined, or empty arrays
240
- if (!tools || !Array.isArray(tools) || tools.length === 0) {
241
- return [];
242
- }
243
-
244
- // Validate tools structure
245
- const validTools = tools.filter((tool) => {
246
- if (!tool || typeof tool !== "object") {
247
- logger.warn("Invalid tool structure detected, skipping:", tool);
248
- return false;
249
- }
250
- if (tool.type !== "function" || !tool.function) {
251
- logger.warn(
252
- "Tool is not a function type or missing function property:",
253
- tool,
254
- );
255
- return false;
228
+ export function countContentBlocks(
229
+ messages: ChatCompletionMessageParam[],
230
+ ): number {
231
+ let count = 0;
232
+ for (const message of messages) {
233
+ if (!message || typeof message !== "object") continue;
234
+ const content = message.content;
235
+ if (typeof content === "string") {
236
+ count += 1;
237
+ } else if (Array.isArray(content)) {
238
+ count += content.length;
256
239
  }
257
- return true;
258
- });
259
-
260
- if (validTools.length === 0) {
261
- logger.warn("No valid tools found for cache control");
262
- return [];
263
240
  }
264
-
265
- // Create a copy of the valid tools array
266
- const result = validTools.map((tool) => ({
267
- ...tool,
268
- })) as ClaudeChatCompletionFunctionTool[];
269
-
270
- // Add cache control to the last tool only
271
- const lastIndex = result.length - 1;
272
- result[lastIndex] = {
273
- ...result[lastIndex],
274
- cache_control: { type: "ephemeral" },
275
- };
276
-
277
- return result;
241
+ return count;
278
242
  }
279
243
 
280
244
  /**
281
- * Transforms messages for Claude cache control with hardcoded strategy
245
+ * Transforms messages for explicit cache control.
246
+ *
247
+ * Cache breakpoints (following Claude Code's "last message marker" strategy):
248
+ * 1. System message — always marked (stable prefix)
249
+ * 2. Last message — the last user/assistant message with content is marked.
250
+ * The API scans backward from this marker within 20 blocks. Since each
251
+ * turn adds ~2 blocks (< 20), the previous request's marker position
252
+ * always falls within the scan window, ensuring cache hits.
253
+ *
254
+ * Tools are marked separately via addCacheControlToLastTool (called by aiService).
255
+ *
282
256
  * @param messages - Original OpenAI message array
283
257
  * @param modelName - Model name for cache detection
284
258
  * @returns Messages with cache control markers applied
285
259
  */
286
- export function transformMessagesForClaudeCache(
260
+ export function transformMessagesForExplicitCache(
287
261
  messages: ChatCompletionMessageParam[],
288
262
  modelName: string,
289
263
  ): ChatCompletionMessageParam[] {
290
264
  // Validate inputs
291
265
  if (!messages || !Array.isArray(messages)) {
292
266
  logger.warn(
293
- "Invalid messages array provided to transformMessagesForClaudeCache",
267
+ "Invalid messages array provided to transformMessagesForExplicitCache",
294
268
  );
295
269
  return [];
296
270
  }
@@ -307,60 +281,68 @@ export function transformMessagesForClaudeCache(
307
281
  // Find first system message index
308
282
  const firstSystemIndex = messages.findIndex((m) => m.role === "system");
309
283
 
310
- // Find last user message index
311
- let lastUserIndex = -1;
284
+ // Determine which message indices should receive cache_control markers
285
+ const cacheIndices = new Set<number>();
286
+
287
+ // Marker 1: First system message (always — stable prefix)
288
+ if (firstSystemIndex !== -1) {
289
+ cacheIndices.add(firstSystemIndex);
290
+ }
291
+
292
+ // Marker 2: Last message with content (user or assistant).
293
+ // This marker moves each turn (~2 blocks), but since the move is < 20 blocks,
294
+ // the previous marker position is always within the API's 20-block scan window.
295
+ // This ensures the entire conversation prefix is cached.
312
296
  for (let i = messages.length - 1; i >= 0; i--) {
313
- if (messages[i].role === "user") {
314
- lastUserIndex = i;
297
+ const msg = messages[i];
298
+ if (!msg || typeof msg !== "object") continue;
299
+ if (i === firstSystemIndex) continue;
300
+ if (msg.role !== "user" && msg.role !== "assistant") continue;
301
+
302
+ const content = msg.content;
303
+ const hasContent =
304
+ (typeof content === "string" && content.length > 0) ||
305
+ (Array.isArray(content) && content.length > 0);
306
+
307
+ if (hasContent) {
308
+ cacheIndices.add(i);
315
309
  break;
316
310
  }
317
311
  }
318
312
 
313
+ // Apply cache_control markers to selected messages
319
314
  const result = messages.map((message, index) => {
320
315
  // Validate message structure
321
316
  if (!message || typeof message !== "object" || !message.role) {
322
317
  logger.warn("Invalid message structure at index", index, ":", message);
323
- return message; // Return as-is to avoid breaking the flow
318
+ return message;
324
319
  }
325
320
 
326
- // First system message: always cached (hardcoded)
327
- if (message.role === "system" && index === firstSystemIndex) {
328
- const content =
329
- (message.content as string | ChatCompletionContentPart[]) || "";
330
- // If content is already an array, check if it already has cache control
331
- if (Array.isArray(content)) {
332
- const hasCacheControl = content.some(
333
- (part) =>
334
- part.type === "text" &&
335
- (part as ClaudeChatCompletionContentPartText).cache_control,
336
- );
337
- if (hasCacheControl) {
338
- return message;
339
- }
340
- }
341
-
342
- const transformedContent = addCacheControlToContent(content, true);
343
-
344
- return {
345
- ...message,
346
- content: transformedContent,
347
- } as ChatCompletionMessageParam;
321
+ if (!cacheIndices.has(index)) {
322
+ return message;
348
323
  }
349
324
 
350
- // Last user message: cache recent conversation history
351
- if (message.role === "user" && index === lastUserIndex) {
352
- const content =
353
- (message.content as string | ChatCompletionContentPart[]) || "";
354
- const transformedContent = addCacheControlToContent(content, true);
325
+ const content =
326
+ (message.content as string | ChatCompletionContentPart[]) || "";
355
327
 
356
- return {
357
- ...message,
358
- content: transformedContent,
359
- } as ChatCompletionMessageParam;
328
+ // Idempotency: skip if content already has cache_control (system message)
329
+ if (message.role === "system" && Array.isArray(content)) {
330
+ const hasCacheControl = content.some(
331
+ (part) =>
332
+ part.type === "text" &&
333
+ (part as ClaudeChatCompletionContentPartText).cache_control,
334
+ );
335
+ if (hasCacheControl) {
336
+ return message;
337
+ }
360
338
  }
361
339
 
362
- // Return message unchanged
363
- return message;
340
+ const transformedContent = addCacheControlToContent(content, true);
341
+
342
+ return {
343
+ ...message,
344
+ content: transformedContent,
345
+ } as ChatCompletionMessageParam;
364
346
  });
365
347
 
366
348
  return result;
@@ -29,5 +29,5 @@ export const USER_MEMORY_FILE = path.join(DATA_DIRECTORY, "AGENTS.md");
29
29
  /**
30
30
  * AI related constants
31
31
  */
32
- export const DEFAULT_WAVE_MAX_INPUT_TOKENS = 128000; // Default token limit
32
+ export const DEFAULT_WAVE_MAX_INPUT_TOKENS = 200000; // Default token limit
33
33
  export const DEFAULT_WAVE_MAX_OUTPUT_TOKENS = 16384; // Default output token limit
@@ -83,12 +83,6 @@ export function setupAgentContainer(
83
83
  container.register("AgentOptions", options);
84
84
  container.register("Workdir", workdir);
85
85
 
86
- const mergedEnv: Record<string, string> = {
87
- ...(process.env as Record<string, string>),
88
- ...(options.env || {}),
89
- };
90
- container.register("MergedEnv", mergedEnv);
91
-
92
86
  if (options.worktreeName) {
93
87
  container.register("WorktreeName", options.worktreeName);
94
88
  container.register("MainRepoRoot", getGitMainRepoRoot(workdir));
@@ -227,7 +221,9 @@ export function setupAgentContainer(
227
221
  toolName: context.toolName,
228
222
  toolInput: context.toolInput,
229
223
  planFilePath: permissionManager.getPlanFilePath(),
230
- env: mergedEnv,
224
+ env: Object.fromEntries(
225
+ Object.entries(process.env).filter((e) => e[1] !== undefined),
226
+ ) as Record<string, string>,
231
227
  });
232
228
 
233
229
  if (results.length > 0) {