wave-agent-sdk 0.0.7 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/dist/agent.d.ts +32 -20
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +202 -20
  4. package/dist/constants/events.d.ts +28 -0
  5. package/dist/constants/events.d.ts.map +1 -0
  6. package/dist/constants/events.js +27 -0
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +2 -0
  10. package/dist/managers/aiManager.d.ts +34 -1
  11. package/dist/managers/aiManager.d.ts.map +1 -1
  12. package/dist/managers/aiManager.js +243 -128
  13. package/dist/managers/backgroundBashManager.d.ts.map +1 -1
  14. package/dist/managers/backgroundBashManager.js +7 -6
  15. package/dist/managers/hookManager.d.ts +9 -4
  16. package/dist/managers/hookManager.d.ts.map +1 -1
  17. package/dist/managers/hookManager.js +62 -30
  18. package/dist/managers/liveConfigManager.d.ts +58 -0
  19. package/dist/managers/liveConfigManager.d.ts.map +1 -0
  20. package/dist/managers/liveConfigManager.js +160 -0
  21. package/dist/managers/messageManager.d.ts +38 -13
  22. package/dist/managers/messageManager.d.ts.map +1 -1
  23. package/dist/managers/messageManager.js +163 -30
  24. package/dist/managers/slashCommandManager.d.ts.map +1 -1
  25. package/dist/managers/slashCommandManager.js +4 -1
  26. package/dist/managers/subagentManager.d.ts +51 -0
  27. package/dist/managers/subagentManager.d.ts.map +1 -1
  28. package/dist/managers/subagentManager.js +189 -18
  29. package/dist/services/aiService.d.ts +13 -5
  30. package/dist/services/aiService.d.ts.map +1 -1
  31. package/dist/services/aiService.js +350 -74
  32. package/dist/services/configurationWatcher.d.ts +120 -0
  33. package/dist/services/configurationWatcher.d.ts.map +1 -0
  34. package/dist/services/configurationWatcher.js +439 -0
  35. package/dist/services/fileWatcher.d.ts +69 -0
  36. package/dist/services/fileWatcher.d.ts.map +1 -0
  37. package/dist/services/fileWatcher.js +213 -0
  38. package/dist/services/hook.d.ts +91 -9
  39. package/dist/services/hook.d.ts.map +1 -1
  40. package/dist/services/hook.js +393 -43
  41. package/dist/services/jsonlHandler.d.ts +62 -0
  42. package/dist/services/jsonlHandler.d.ts.map +1 -0
  43. package/dist/services/jsonlHandler.js +257 -0
  44. package/dist/services/memory.d.ts +9 -0
  45. package/dist/services/memory.d.ts.map +1 -1
  46. package/dist/services/memory.js +81 -12
  47. package/dist/services/memoryStore.d.ts +81 -0
  48. package/dist/services/memoryStore.d.ts.map +1 -0
  49. package/dist/services/memoryStore.js +200 -0
  50. package/dist/services/session.d.ts +64 -49
  51. package/dist/services/session.d.ts.map +1 -1
  52. package/dist/services/session.js +310 -132
  53. package/dist/tools/bashTool.d.ts.map +1 -1
  54. package/dist/tools/bashTool.js +5 -4
  55. package/dist/tools/deleteFileTool.d.ts.map +1 -1
  56. package/dist/tools/deleteFileTool.js +2 -1
  57. package/dist/tools/editTool.d.ts.map +1 -1
  58. package/dist/tools/editTool.js +3 -2
  59. package/dist/tools/multiEditTool.d.ts.map +1 -1
  60. package/dist/tools/multiEditTool.js +4 -3
  61. package/dist/tools/readTool.d.ts.map +1 -1
  62. package/dist/tools/readTool.js +2 -1
  63. package/dist/tools/writeTool.d.ts.map +1 -1
  64. package/dist/tools/writeTool.js +5 -6
  65. package/dist/types/commands.d.ts +4 -0
  66. package/dist/types/commands.d.ts.map +1 -1
  67. package/dist/types/core.d.ts +35 -0
  68. package/dist/types/core.d.ts.map +1 -1
  69. package/dist/types/environment.d.ts +42 -0
  70. package/dist/types/environment.d.ts.map +1 -0
  71. package/dist/types/environment.js +21 -0
  72. package/dist/types/hooks.d.ts +8 -2
  73. package/dist/types/hooks.d.ts.map +1 -1
  74. package/dist/types/hooks.js +8 -2
  75. package/dist/types/index.d.ts +2 -0
  76. package/dist/types/index.d.ts.map +1 -1
  77. package/dist/types/index.js +2 -0
  78. package/dist/types/memoryStore.d.ts +82 -0
  79. package/dist/types/memoryStore.d.ts.map +1 -0
  80. package/dist/types/memoryStore.js +7 -0
  81. package/dist/types/messaging.d.ts +14 -2
  82. package/dist/types/messaging.d.ts.map +1 -1
  83. package/dist/types/session.d.ts +20 -0
  84. package/dist/types/session.d.ts.map +1 -0
  85. package/dist/types/session.js +7 -0
  86. package/dist/utils/bashHistory.d.ts.map +1 -1
  87. package/dist/utils/bashHistory.js +27 -26
  88. package/dist/utils/cacheControlUtils.d.ts +121 -0
  89. package/dist/utils/cacheControlUtils.d.ts.map +1 -0
  90. package/dist/utils/cacheControlUtils.js +367 -0
  91. package/dist/utils/commandPathResolver.d.ts +52 -0
  92. package/dist/utils/commandPathResolver.d.ts.map +1 -0
  93. package/dist/utils/commandPathResolver.js +145 -0
  94. package/dist/utils/configPaths.d.ts +85 -0
  95. package/dist/utils/configPaths.d.ts.map +1 -0
  96. package/dist/utils/configPaths.js +121 -0
  97. package/dist/utils/configResolver.d.ts +37 -10
  98. package/dist/utils/configResolver.d.ts.map +1 -1
  99. package/dist/utils/configResolver.js +127 -23
  100. package/dist/utils/constants.d.ts +1 -1
  101. package/dist/utils/constants.js +1 -1
  102. package/dist/utils/convertMessagesForAPI.d.ts.map +1 -1
  103. package/dist/utils/convertMessagesForAPI.js +7 -5
  104. package/dist/utils/customCommands.d.ts.map +1 -1
  105. package/dist/utils/customCommands.js +66 -21
  106. package/dist/utils/fileUtils.d.ts +15 -0
  107. package/dist/utils/fileUtils.d.ts.map +1 -0
  108. package/dist/utils/fileUtils.js +61 -0
  109. package/dist/utils/globalLogger.d.ts +102 -0
  110. package/dist/utils/globalLogger.d.ts.map +1 -0
  111. package/dist/utils/globalLogger.js +136 -0
  112. package/dist/utils/mcpUtils.d.ts.map +1 -1
  113. package/dist/utils/mcpUtils.js +25 -3
  114. package/dist/utils/messageOperations.d.ts +20 -8
  115. package/dist/utils/messageOperations.d.ts.map +1 -1
  116. package/dist/utils/messageOperations.js +25 -16
  117. package/dist/utils/pathEncoder.d.ts +104 -0
  118. package/dist/utils/pathEncoder.d.ts.map +1 -0
  119. package/dist/utils/pathEncoder.js +272 -0
  120. package/dist/utils/subagentParser.d.ts.map +1 -1
  121. package/dist/utils/subagentParser.js +2 -1
  122. package/dist/utils/tokenCalculation.d.ts +26 -0
  123. package/dist/utils/tokenCalculation.d.ts.map +1 -0
  124. package/dist/utils/tokenCalculation.js +36 -0
  125. package/package.json +6 -3
  126. package/src/agent.ts +298 -34
  127. package/src/constants/events.ts +38 -0
  128. package/src/index.ts +2 -0
  129. package/src/managers/aiManager.ts +323 -170
  130. package/src/managers/backgroundBashManager.ts +7 -6
  131. package/src/managers/hookManager.ts +83 -40
  132. package/src/managers/liveConfigManager.ts +248 -0
  133. package/src/managers/messageManager.ts +230 -63
  134. package/src/managers/slashCommandManager.ts +4 -1
  135. package/src/managers/subagentManager.ts +283 -21
  136. package/src/services/aiService.ts +474 -83
  137. package/src/services/configurationWatcher.ts +622 -0
  138. package/src/services/fileWatcher.ts +301 -0
  139. package/src/services/hook.ts +538 -47
  140. package/src/services/jsonlHandler.ts +319 -0
  141. package/src/services/memory.ts +92 -12
  142. package/src/services/memoryStore.ts +279 -0
  143. package/src/services/session.ts +381 -157
  144. package/src/tools/bashTool.ts +5 -4
  145. package/src/tools/deleteFileTool.ts +2 -1
  146. package/src/tools/editTool.ts +3 -2
  147. package/src/tools/multiEditTool.ts +4 -3
  148. package/src/tools/readTool.ts +2 -1
  149. package/src/tools/writeTool.ts +7 -6
  150. package/src/types/commands.ts +6 -0
  151. package/src/types/core.ts +44 -0
  152. package/src/types/environment.ts +60 -0
  153. package/src/types/hooks.ts +21 -8
  154. package/src/types/index.ts +2 -0
  155. package/src/types/memoryStore.ts +94 -0
  156. package/src/types/messaging.ts +14 -2
  157. package/src/types/session.ts +25 -0
  158. package/src/utils/bashHistory.ts +27 -27
  159. package/src/utils/cacheControlUtils.ts +540 -0
  160. package/src/utils/commandPathResolver.ts +189 -0
  161. package/src/utils/configPaths.ts +163 -0
  162. package/src/utils/configResolver.ts +182 -22
  163. package/src/utils/constants.ts +1 -1
  164. package/src/utils/convertMessagesForAPI.ts +7 -5
  165. package/src/utils/customCommands.ts +90 -22
  166. package/src/utils/fileUtils.ts +65 -0
  167. package/src/utils/globalLogger.ts +145 -0
  168. package/src/utils/mcpUtils.ts +34 -3
  169. package/src/utils/messageOperations.ts +42 -20
  170. package/src/utils/pathEncoder.ts +379 -0
  171. package/src/utils/subagentParser.ts +2 -1
  172. package/src/utils/tokenCalculation.ts +43 -0
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import fs from "fs";
6
6
  import { BASH_HISTORY_FILE, DATA_DIRECTORY } from "./constants.js";
7
+ import { logger } from "./globalLogger.js";
7
8
  const HISTORY_VERSION = 1;
8
9
  const MAX_HISTORY_SIZE = 1000;
9
10
  /**
@@ -15,8 +16,8 @@ const ensureDataDirectory = () => {
15
16
  fs.mkdirSync(DATA_DIRECTORY, { recursive: true });
16
17
  }
17
18
  }
18
- catch {
19
- // logger.debug("Failed to create data directory:", error);
19
+ catch (error) {
20
+ logger.debug("Failed to create data directory:", error);
20
21
  }
21
22
  };
22
23
  /**
@@ -35,7 +36,7 @@ export const loadBashHistory = () => {
35
36
  const history = JSON.parse(data);
36
37
  // Version compatibility check
37
38
  if (history.version !== HISTORY_VERSION) {
38
- // logger.debug("Bash history version mismatch, resetting history");
39
+ logger.debug("Bash history version mismatch, resetting history");
39
40
  return {
40
41
  commands: [],
41
42
  version: HISTORY_VERSION,
@@ -43,8 +44,8 @@ export const loadBashHistory = () => {
43
44
  }
44
45
  return history;
45
46
  }
46
- catch {
47
- // logger.debug("Failed to load bash history:", error);
47
+ catch (error) {
48
+ logger.debug("Failed to load bash history:", error);
48
49
  return {
49
50
  commands: [],
50
51
  version: HISTORY_VERSION,
@@ -58,7 +59,7 @@ export const saveBashHistory = (history) => {
58
59
  try {
59
60
  // Skip saving to file when in test environment
60
61
  if (process.env.NODE_ENV === "test") {
61
- // logger.debug("Skipping bash history save in test environment");
62
+ logger.debug("Skipping bash history save in test environment");
62
63
  return;
63
64
  }
64
65
  ensureDataDirectory();
@@ -69,8 +70,8 @@ export const saveBashHistory = (history) => {
69
70
  const data = JSON.stringify(history, null, 2);
70
71
  fs.writeFileSync(BASH_HISTORY_FILE, data, "utf-8");
71
72
  }
72
- catch {
73
- // logger.debug("Failed to save bash history:", error);
73
+ catch (error) {
74
+ logger.debug("Failed to save bash history:", error);
74
75
  }
75
76
  };
76
77
  /**
@@ -78,11 +79,6 @@ export const saveBashHistory = (history) => {
78
79
  */
79
80
  export const addBashCommandToHistory = (command, workdir) => {
80
81
  try {
81
- // Filter system-generated commands, do not add to history
82
- if (command.startsWith("git add . && git commit -m")) {
83
- // logger.debug("Skipping system-generated command:", { command, workdir });
84
- return;
85
- }
86
82
  const history = loadBashHistory();
87
83
  const timestamp = Date.now();
88
84
  // Check if it's a duplicate consecutive command to avoid duplicate recording
@@ -102,10 +98,10 @@ export const addBashCommandToHistory = (command, workdir) => {
102
98
  });
103
99
  }
104
100
  saveBashHistory(history);
105
- // logger.debug("Added bash command to history:", { command, workdir });
101
+ logger.debug("Added bash command to history:", { command, workdir });
106
102
  }
107
- catch {
108
- // logger.debug("Failed to add bash command to history:", error);
103
+ catch (error) {
104
+ logger.debug("Failed to add bash command to history:", error);
109
105
  }
110
106
  };
111
107
  /**
@@ -156,11 +152,16 @@ export const searchBashHistory = (query, limit = 10, workdir) => {
156
152
  // Deduplicate search results, keep latest record
157
153
  const dedupedMatches = deduplicateCommands(matches);
158
154
  const result = dedupedMatches.slice(0, limit);
159
- // logger.debug("Bash history search results:", { query, workdir: process.cwd(), originalCount: matches.length, dedupedCount: result.length });
155
+ logger.debug("Bash history search results:", {
156
+ query,
157
+ workdir: process.cwd(),
158
+ originalCount: matches.length,
159
+ dedupedCount: result.length,
160
+ });
160
161
  return result;
161
162
  }
162
- catch {
163
- // logger.debug("Failed to search bash history:", error);
163
+ catch (error) {
164
+ logger.debug("Failed to search bash history:", error);
164
165
  return [];
165
166
  }
166
167
  };
@@ -190,8 +191,8 @@ export const getRecentBashCommands = (workdir, limit = 10) => {
190
191
  const deduped = deduplicateCommands(filtered);
191
192
  return deduped.slice(-limit).reverse(); // Latest first
192
193
  }
193
- catch {
194
- // logger.debug("Failed to get recent bash commands:", error);
194
+ catch (error) {
195
+ logger.debug("Failed to get recent bash commands:", error);
195
196
  return [];
196
197
  }
197
198
  };
@@ -205,10 +206,10 @@ export const clearBashHistory = () => {
205
206
  version: HISTORY_VERSION,
206
207
  };
207
208
  saveBashHistory(history);
208
- // logger.debug("Bash history cleared");
209
+ logger.debug("Bash history cleared");
209
210
  }
210
- catch {
211
- // logger.debug("Failed to clear bash history:", error);
211
+ catch (error) {
212
+ logger.debug("Failed to clear bash history:", error);
212
213
  }
213
214
  };
214
215
  /**
@@ -225,8 +226,8 @@ export const getBashCommandStats = () => {
225
226
  workdirs,
226
227
  };
227
228
  }
228
- catch {
229
- // logger.debug("Failed to get bash command stats:", error);
229
+ catch (error) {
230
+ logger.debug("Failed to get bash command stats:", error);
230
231
  return {
231
232
  totalCommands: 0,
232
233
  uniqueCommands: 0,
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Cache Control Utilities for Claude Models
3
+ *
4
+ * This module provides utilities for adding cache_control markers to Claude models
5
+ * to optimize token usage and reduce costs. Cache control is only applied to Claude
6
+ * models and preserves backward compatibility with existing message formats.
7
+ */
8
+ import type { ChatCompletionMessageParam, ChatCompletionContentPart, ChatCompletionContentPartText, ChatCompletionFunctionTool, CompletionUsage } from "openai/resources";
9
+ /**
10
+ * Cache control directive for Claude models
11
+ */
12
+ export interface CacheControl {
13
+ type: "ephemeral";
14
+ }
15
+ /**
16
+ * Extended text content part with cache control support
17
+ */
18
+ export interface ClaudeChatCompletionContentPartText extends ChatCompletionContentPartText {
19
+ type: "text";
20
+ text: string;
21
+ cache_control?: CacheControl;
22
+ }
23
+ /**
24
+ * Extended tool definition with cache control support
25
+ */
26
+ export interface ClaudeChatCompletionFunctionTool extends ChatCompletionFunctionTool {
27
+ type: "function";
28
+ function: ChatCompletionFunctionTool["function"];
29
+ cache_control?: CacheControl;
30
+ }
31
+ /**
32
+ * Enhanced usage metrics including Claude cache information
33
+ */
34
+ export interface ClaudeUsage extends CompletionUsage {
35
+ prompt_tokens: number;
36
+ completion_tokens: number;
37
+ total_tokens: number;
38
+ cache_read_input_tokens?: number;
39
+ cache_creation_input_tokens?: number;
40
+ cache_creation?: {
41
+ ephemeral_5m_input_tokens: number;
42
+ ephemeral_1h_input_tokens: number;
43
+ };
44
+ }
45
+ /**
46
+ * Configuration for cache control application
47
+ */
48
+ export interface CacheControlConfig {
49
+ cacheSystemMessage: boolean;
50
+ cacheUserMessageCount: number;
51
+ cacheLastTool: boolean;
52
+ }
53
+ /**
54
+ * Default cache control configuration
55
+ */
56
+ export declare const DEFAULT_CACHE_CONTROL_CONFIG: CacheControlConfig;
57
+ /**
58
+ * Determines if a model supports cache control
59
+ * @param modelName - Model identifier
60
+ * @returns True if model name contains 'claude' (case-insensitive)
61
+ */
62
+ export declare function isClaudeModel(modelName: string): boolean;
63
+ /**
64
+ * Validates cache control structure
65
+ * @param control - Object to validate
66
+ * @returns True if valid cache control object
67
+ */
68
+ export declare function isValidCacheControl(control: unknown): control is CacheControl;
69
+ /**
70
+ * Adds cache control markers to message content
71
+ * @param content - Original content (string or structured)
72
+ * @param shouldCache - Whether to add cache control
73
+ * @returns Structured content with cache control markers
74
+ */
75
+ export declare function addCacheControlToContent(content: string | ChatCompletionContentPart[], shouldCache: boolean): ClaudeChatCompletionContentPartText[];
76
+ /**
77
+ * Adds cache control to the last tool in tools array
78
+ * @param tools - Array of tool definitions
79
+ * @returns Tools array with cache control on last tool
80
+ */
81
+ export declare function addCacheControlToLastTool(tools: ChatCompletionFunctionTool[]): ClaudeChatCompletionFunctionTool[];
82
+ /**
83
+ * Transforms messages for Claude cache control
84
+ * @param messages - Original OpenAI message array
85
+ * @param modelName - Model name for cache detection
86
+ * @param config - Cache control configuration
87
+ * @returns Messages with cache control markers applied
88
+ */
89
+ export declare function transformMessagesForClaudeCache(messages: ChatCompletionMessageParam[], modelName: string, config?: CacheControlConfig): ChatCompletionMessageParam[];
90
+ /**
91
+ * Extends standard usage with cache metrics
92
+ * @param standardUsage - OpenAI usage response
93
+ * @param cacheMetrics - Additional cache metrics from Claude
94
+ * @returns Extended usage with cache information
95
+ */
96
+ export declare function extendUsageWithCacheMetrics(standardUsage: CompletionUsage, cacheMetrics?: Partial<ClaudeUsage>): ClaudeUsage;
97
+ /**
98
+ * Validates Claude usage structure
99
+ * @param usage - Usage object to validate
100
+ * @returns True if usage structure is valid
101
+ */
102
+ export declare function isValidClaudeUsage(usage: unknown): usage is ClaudeUsage;
103
+ /**
104
+ * Adds cache control to the last N user messages in a conversation
105
+ * This optimizes multi-turn conversations by caching recent user context
106
+ *
107
+ * @param messages - Array of chat completion messages
108
+ * @param maxUserMessagesToCache - Maximum number of recent user messages to cache (default: 2)
109
+ * @returns Modified messages array with cache control on recent user messages
110
+ */
111
+ export declare function addCacheControlToRecentUserMessages(messages: ChatCompletionMessageParam[], maxUserMessagesToCache?: number): ChatCompletionMessageParam[];
112
+ /**
113
+ * Helper function to identify user message indices that should be cached
114
+ * Used for testing and validation purposes
115
+ *
116
+ * @param messages - Array of chat completion messages
117
+ * @param maxUserMessagesToCache - Maximum number of recent user messages to identify
118
+ * @returns Array of indices for user messages that should be cached
119
+ */
120
+ export declare function findRecentUserMessageIndices(messages: ChatCompletionMessageParam[], maxUserMessagesToCache?: number): number[];
121
+ //# sourceMappingURL=cacheControlUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cacheControlUtils.d.ts","sourceRoot":"","sources":["../../src/utils/cacheControlUtils.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,0BAA0B,EAC1B,yBAAyB,EACzB,6BAA6B,EAC7B,0BAA0B,EAC1B,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAM1B;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,WAAW,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,mCACf,SAAQ,6BAA6B;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,YAAY,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,gCACf,SAAQ,0BAA0B;IAClC,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,0BAA0B,CAAC,UAAU,CAAC,CAAC;IACjD,aAAa,CAAC,EAAE,YAAY,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,WAAY,SAAQ,eAAe;IAClD,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IAGrB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,cAAc,CAAC,EAAE;QACf,yBAAyB,EAAE,MAAM,CAAC;QAClC,yBAAyB,EAAE,MAAM,CAAC;KACnC,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,aAAa,EAAE,OAAO,CAAC;CACxB;AAMD;;GAEG;AACH,eAAO,MAAM,4BAA4B,EAAE,kBAIjC,CAAC;AAMX;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAaxD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,YAAY,CAQ7E;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,GAAG,yBAAyB,EAAE,EAC7C,WAAW,EAAE,OAAO,GACnB,mCAAmC,EAAE,CAkEvC;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,0BAA0B,EAAE,GAClC,gCAAgC,EAAE,CAwCpC;AAED;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,0BAA0B,EAAE,EACtC,SAAS,EAAE,MAAM,EACjB,MAAM,GAAE,kBAAiD,GACxD,0BAA0B,EAAE,CAkE9B;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,aAAa,EAAE,eAAe,EAC9B,YAAY,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAClC,WAAW,CAkCb;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAyCvE;AAED;;;;;;;GAOG;AACH,wBAAgB,mCAAmC,CACjD,QAAQ,EAAE,0BAA0B,EAAE,EACtC,sBAAsB,GAAE,MAAU,GACjC,0BAA0B,EAAE,CAuE9B;AAED;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,0BAA0B,EAAE,EACtC,sBAAsB,GAAE,MAAU,GACjC,MAAM,EAAE,CAqBV"}
@@ -0,0 +1,367 @@
1
+ /**
2
+ * Cache Control Utilities for Claude Models
3
+ *
4
+ * This module provides utilities for adding cache_control markers to Claude models
5
+ * to optimize token usage and reduce costs. Cache control is only applied to Claude
6
+ * models and preserves backward compatibility with existing message formats.
7
+ */
8
+ // ============================================================================
9
+ // Default Configuration
10
+ // ============================================================================
11
+ /**
12
+ * Default cache control configuration
13
+ */
14
+ export const DEFAULT_CACHE_CONTROL_CONFIG = {
15
+ cacheSystemMessage: true,
16
+ cacheUserMessageCount: 2,
17
+ cacheLastTool: true,
18
+ };
19
+ // ============================================================================
20
+ // Utility Functions (Basic Structure - to be implemented)
21
+ // ============================================================================
22
+ /**
23
+ * Determines if a model supports cache control
24
+ * @param modelName - Model identifier
25
+ * @returns True if model name contains 'claude' (case-insensitive)
26
+ */
27
+ export function isClaudeModel(modelName) {
28
+ // Handle null, undefined, and non-string inputs
29
+ if (!modelName || typeof modelName !== "string") {
30
+ return false;
31
+ }
32
+ // Handle empty strings and whitespace-only strings
33
+ const trimmed = modelName.trim();
34
+ if (trimmed.length === 0) {
35
+ return false;
36
+ }
37
+ return trimmed.toLowerCase().includes("claude");
38
+ }
39
+ /**
40
+ * Validates cache control structure
41
+ * @param control - Object to validate
42
+ * @returns True if valid cache control object
43
+ */
44
+ export function isValidCacheControl(control) {
45
+ return (control !== null &&
46
+ typeof control === "object" &&
47
+ control !== undefined &&
48
+ "type" in control &&
49
+ control.type === "ephemeral");
50
+ }
51
+ /**
52
+ * Adds cache control markers to message content
53
+ * @param content - Original content (string or structured)
54
+ * @param shouldCache - Whether to add cache control
55
+ * @returns Structured content with cache control markers
56
+ */
57
+ export function addCacheControlToContent(content, shouldCache) {
58
+ // Handle null/undefined content
59
+ if (content == null) {
60
+ return [];
61
+ }
62
+ // If shouldCache is false, return content as text parts without cache control
63
+ if (!shouldCache) {
64
+ if (typeof content === "string") {
65
+ return [{ type: "text", text: content }];
66
+ }
67
+ // Validate array input
68
+ if (!Array.isArray(content)) {
69
+ console.warn("Invalid content type for cache control transformation:", typeof content);
70
+ return [];
71
+ }
72
+ // Filter and convert only text parts with validation
73
+ return content
74
+ .filter((part) => {
75
+ if (!part || typeof part !== "object") {
76
+ return false;
77
+ }
78
+ return part.type === "text" && typeof part.text === "string";
79
+ })
80
+ .map((part) => ({ type: "text", text: part.text }));
81
+ }
82
+ // shouldCache is true - add cache control markers
83
+ if (typeof content === "string") {
84
+ // Transform string content to structured array with cache control
85
+ return [
86
+ {
87
+ type: "text",
88
+ text: content,
89
+ cache_control: { type: "ephemeral" },
90
+ },
91
+ ];
92
+ }
93
+ // Validate array input
94
+ if (!Array.isArray(content)) {
95
+ console.warn("Invalid content type for cache control transformation:", typeof content);
96
+ return [];
97
+ }
98
+ // Handle structured content - preserve existing structure, add cache control to text parts
99
+ return content
100
+ .filter((part) => {
101
+ if (!part || typeof part !== "object") {
102
+ return false;
103
+ }
104
+ return part.type === "text" && typeof part.text === "string";
105
+ })
106
+ .map((part) => ({
107
+ type: "text",
108
+ text: part.text,
109
+ cache_control: { type: "ephemeral" },
110
+ }));
111
+ }
112
+ /**
113
+ * Adds cache control to the last tool in tools array
114
+ * @param tools - Array of tool definitions
115
+ * @returns Tools array with cache control on last tool
116
+ */
117
+ export function addCacheControlToLastTool(tools) {
118
+ // Handle null, undefined, or empty arrays
119
+ if (!tools || !Array.isArray(tools) || tools.length === 0) {
120
+ return [];
121
+ }
122
+ // Validate tools structure
123
+ const validTools = tools.filter((tool) => {
124
+ if (!tool || typeof tool !== "object") {
125
+ console.warn("Invalid tool structure detected, skipping:", tool);
126
+ return false;
127
+ }
128
+ if (tool.type !== "function" || !tool.function) {
129
+ console.warn("Tool is not a function type or missing function property:", tool);
130
+ return false;
131
+ }
132
+ return true;
133
+ });
134
+ if (validTools.length === 0) {
135
+ console.warn("No valid tools found for cache control");
136
+ return [];
137
+ }
138
+ // Create a copy of the valid tools array
139
+ const result = validTools.map((tool) => ({
140
+ ...tool,
141
+ }));
142
+ // Add cache control to the last tool only
143
+ const lastIndex = result.length - 1;
144
+ result[lastIndex] = {
145
+ ...result[lastIndex],
146
+ cache_control: { type: "ephemeral" },
147
+ };
148
+ return result;
149
+ }
150
+ /**
151
+ * Transforms messages for Claude cache control
152
+ * @param messages - Original OpenAI message array
153
+ * @param modelName - Model name for cache detection
154
+ * @param config - Cache control configuration
155
+ * @returns Messages with cache control markers applied
156
+ */
157
+ export function transformMessagesForClaudeCache(messages, modelName, config = DEFAULT_CACHE_CONTROL_CONFIG) {
158
+ // Validate inputs
159
+ if (!messages || !Array.isArray(messages)) {
160
+ console.warn("Invalid messages array provided to transformMessagesForClaudeCache");
161
+ return [];
162
+ }
163
+ if (messages.length === 0) {
164
+ return [];
165
+ }
166
+ // Only apply cache control for Claude models
167
+ if (!isClaudeModel(modelName)) {
168
+ return messages;
169
+ }
170
+ // Validate config
171
+ if (!config || typeof config !== "object") {
172
+ console.warn("Invalid cache control config, using defaults");
173
+ config = DEFAULT_CACHE_CONTROL_CONFIG;
174
+ }
175
+ const result = messages.map((message, index) => {
176
+ // Validate message structure
177
+ if (!message || typeof message !== "object" || !message.role) {
178
+ console.warn("Invalid message structure at index", index, ":", message);
179
+ return message; // Return as-is to avoid breaking the flow
180
+ }
181
+ // System message: cache if enabled in config
182
+ if (message.role === "system" && config.cacheSystemMessage) {
183
+ return {
184
+ ...message,
185
+ content: addCacheControlToContent(message.content, true),
186
+ };
187
+ }
188
+ // User messages: cache last N messages based on config
189
+ if (message.role === "user" && config.cacheUserMessageCount > 0) {
190
+ const userMessageIndices = [];
191
+ messages.forEach((msg, idx) => {
192
+ if (msg.role === "user") {
193
+ userMessageIndices.push(idx);
194
+ }
195
+ });
196
+ // Check if this user message is among the last N
197
+ const isRecentUser = userMessageIndices
198
+ .slice(-config.cacheUserMessageCount)
199
+ .includes(index);
200
+ if (isRecentUser) {
201
+ return {
202
+ ...message,
203
+ content: addCacheControlToContent(message.content, true),
204
+ };
205
+ }
206
+ }
207
+ // Return message unchanged
208
+ return message;
209
+ });
210
+ return result;
211
+ }
212
+ /**
213
+ * Extends standard usage with cache metrics
214
+ * @param standardUsage - OpenAI usage response
215
+ * @param cacheMetrics - Additional cache metrics from Claude
216
+ * @returns Extended usage with cache information
217
+ */
218
+ export function extendUsageWithCacheMetrics(standardUsage, cacheMetrics) {
219
+ const baseUsage = {
220
+ prompt_tokens: standardUsage.prompt_tokens,
221
+ completion_tokens: standardUsage.completion_tokens,
222
+ total_tokens: standardUsage.total_tokens,
223
+ };
224
+ // Add cache metrics if provided
225
+ if (cacheMetrics) {
226
+ if (typeof cacheMetrics.cache_read_input_tokens === "number") {
227
+ baseUsage.cache_read_input_tokens = cacheMetrics.cache_read_input_tokens;
228
+ }
229
+ if (typeof cacheMetrics.cache_creation_input_tokens === "number") {
230
+ baseUsage.cache_creation_input_tokens =
231
+ cacheMetrics.cache_creation_input_tokens;
232
+ }
233
+ if (cacheMetrics.cache_creation &&
234
+ typeof cacheMetrics.cache_creation.ephemeral_5m_input_tokens ===
235
+ "number" &&
236
+ typeof cacheMetrics.cache_creation.ephemeral_1h_input_tokens === "number") {
237
+ baseUsage.cache_creation = {
238
+ ephemeral_5m_input_tokens: cacheMetrics.cache_creation.ephemeral_5m_input_tokens,
239
+ ephemeral_1h_input_tokens: cacheMetrics.cache_creation.ephemeral_1h_input_tokens,
240
+ };
241
+ }
242
+ }
243
+ return baseUsage;
244
+ }
245
+ /**
246
+ * Validates Claude usage structure
247
+ * @param usage - Usage object to validate
248
+ * @returns True if usage structure is valid
249
+ */
250
+ export function isValidClaudeUsage(usage) {
251
+ if (!usage || typeof usage !== "object") {
252
+ return false;
253
+ }
254
+ const usageObj = usage;
255
+ // Check required standard fields
256
+ const hasStandardFields = typeof usageObj.prompt_tokens === "number" &&
257
+ typeof usageObj.completion_tokens === "number" &&
258
+ typeof usageObj.total_tokens === "number";
259
+ if (!hasStandardFields) {
260
+ return false;
261
+ }
262
+ // Check optional cache fields
263
+ const hasCacheFields = (usageObj.cache_read_input_tokens === undefined ||
264
+ typeof usageObj.cache_read_input_tokens === "number") &&
265
+ (usageObj.cache_creation_input_tokens === undefined ||
266
+ typeof usageObj.cache_creation_input_tokens === "number");
267
+ if (!hasCacheFields) {
268
+ return false;
269
+ }
270
+ // Check cache_creation object if present
271
+ if (usageObj.cache_creation !== undefined) {
272
+ const cacheCreation = usageObj.cache_creation;
273
+ if (typeof cacheCreation !== "object" ||
274
+ typeof cacheCreation.ephemeral_5m_input_tokens !== "number" ||
275
+ typeof cacheCreation.ephemeral_1h_input_tokens !== "number") {
276
+ return false;
277
+ }
278
+ }
279
+ return true;
280
+ }
281
+ /**
282
+ * Adds cache control to the last N user messages in a conversation
283
+ * This optimizes multi-turn conversations by caching recent user context
284
+ *
285
+ * @param messages - Array of chat completion messages
286
+ * @param maxUserMessagesToCache - Maximum number of recent user messages to cache (default: 2)
287
+ * @returns Modified messages array with cache control on recent user messages
288
+ */
289
+ export function addCacheControlToRecentUserMessages(messages, maxUserMessagesToCache = 2) {
290
+ // Validate inputs
291
+ if (!messages || !Array.isArray(messages)) {
292
+ console.warn("Invalid messages array provided to addCacheControlToRecentUserMessages");
293
+ return [];
294
+ }
295
+ if (messages.length === 0 || maxUserMessagesToCache <= 0) {
296
+ return messages;
297
+ }
298
+ // Validate maxUserMessagesToCache is a reasonable number
299
+ if (maxUserMessagesToCache > 100) {
300
+ console.warn("maxUserMessagesToCache is unusually high:", maxUserMessagesToCache, "limiting to 100");
301
+ maxUserMessagesToCache = 100;
302
+ }
303
+ // Find all user message indices in reverse order (most recent first)
304
+ const userMessageIndices = [];
305
+ for (let i = messages.length - 1; i >= 0; i--) {
306
+ const message = messages[i];
307
+ // Validate message structure
308
+ if (!message || typeof message !== "object" || !message.role) {
309
+ console.warn("Invalid message at index", i, ", skipping");
310
+ continue;
311
+ }
312
+ if (message.role === "user") {
313
+ userMessageIndices.push(i);
314
+ if (userMessageIndices.length >= maxUserMessagesToCache) {
315
+ break;
316
+ }
317
+ }
318
+ }
319
+ // If no user messages found, return unchanged
320
+ if (userMessageIndices.length === 0) {
321
+ return messages;
322
+ }
323
+ // Create a copy of messages and modify the identified user messages
324
+ const modifiedMessages = [...messages];
325
+ for (const index of userMessageIndices) {
326
+ const message = modifiedMessages[index];
327
+ if (message.role === "user" && message.content != null) {
328
+ try {
329
+ modifiedMessages[index] = {
330
+ ...message,
331
+ content: addCacheControlToContent(message.content, true),
332
+ };
333
+ }
334
+ catch (error) {
335
+ console.warn("Failed to add cache control to user message at index", index, ":", error);
336
+ // Continue with original message if transformation fails
337
+ }
338
+ }
339
+ }
340
+ return modifiedMessages;
341
+ }
342
+ /**
343
+ * Helper function to identify user message indices that should be cached
344
+ * Used for testing and validation purposes
345
+ *
346
+ * @param messages - Array of chat completion messages
347
+ * @param maxUserMessagesToCache - Maximum number of recent user messages to identify
348
+ * @returns Array of indices for user messages that should be cached
349
+ */
350
+ export function findRecentUserMessageIndices(messages, maxUserMessagesToCache = 2) {
351
+ if (!Array.isArray(messages) ||
352
+ messages.length === 0 ||
353
+ maxUserMessagesToCache <= 0) {
354
+ return [];
355
+ }
356
+ const userMessageIndices = [];
357
+ for (let i = messages.length - 1; i >= 0; i--) {
358
+ if (messages[i].role === "user") {
359
+ userMessageIndices.push(i);
360
+ if (userMessageIndices.length >= maxUserMessagesToCache) {
361
+ break;
362
+ }
363
+ }
364
+ }
365
+ // Return indices in original order (not reversed)
366
+ return userMessageIndices.reverse();
367
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Command path resolver utilities for nested command discovery
3
+ * Handles conversion between file paths and command IDs with colon syntax
4
+ */
5
+ export interface CommandIdParts {
6
+ namespace?: string;
7
+ commandName: string;
8
+ isNested: boolean;
9
+ depth: number;
10
+ segments: string[];
11
+ }
12
+ /**
13
+ * Generate command ID from file path
14
+ * @param filePath - Absolute path to markdown file
15
+ * @param rootDir - Root commands directory path
16
+ * @returns Command identifier string (e.g., "openspec:apply")
17
+ * @throws Error on invalid path structure
18
+ */
19
+ export declare function generateCommandId(filePath: string, rootDir: string): string;
20
+ /**
21
+ * Parse command ID into components
22
+ * @param commandId - Command identifier (e.g., "openspec:apply")
23
+ * @returns Object with namespace and command name
24
+ * @throws Error on malformed command ID
25
+ */
26
+ export declare function parseCommandId(commandId: string): CommandIdParts;
27
+ /**
28
+ * Validate command ID format
29
+ * @param commandId - Command identifier to validate
30
+ * @returns Boolean indicating validity
31
+ */
32
+ export declare function validateCommandId(commandId: string): boolean;
33
+ /**
34
+ * Convert file path to command segments array
35
+ * @param filePath - Absolute path to markdown file
36
+ * @param rootDir - Root commands directory path
37
+ * @returns Array of path segments
38
+ */
39
+ export declare function getCommandSegments(filePath: string, rootDir: string): string[];
40
+ /**
41
+ * Get namespace from command segments
42
+ * @param segments - Command path segments
43
+ * @returns Namespace string or undefined for flat commands
44
+ */
45
+ export declare function getNamespace(segments: string[]): string | undefined;
46
+ /**
47
+ * Get command depth from segments
48
+ * @param segments - Command path segments
49
+ * @returns Depth number (0 for root, 1 for nested)
50
+ */
51
+ export declare function getDepth(segments: string[]): number;
52
+ //# sourceMappingURL=commandPathResolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commandPathResolver.d.ts","sourceRoot":"","sources":["../../src/utils/commandPathResolver.ts"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAmD3E;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,CAyChE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAU5D;AAaD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,MAAM,EAAE,CASV;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAEnE;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAEnD"}