wave-agent-sdk 0.18.6 → 0.19.0

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 (129) hide show
  1. package/builtin/skills/settings/ENV.md +0 -1
  2. package/builtin/skills/settings/HOOKS.md +10 -0
  3. package/builtin/skills/settings/MODELS.md +37 -0
  4. package/builtin/skills/settings/SKILLS.md +6 -4
  5. package/dist/agent.d.ts.map +1 -1
  6. package/dist/agent.js +12 -5
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +1 -0
  10. package/dist/managers/aiManager.d.ts.map +1 -1
  11. package/dist/managers/aiManager.js +31 -8
  12. package/dist/managers/cronManager.d.ts.map +1 -1
  13. package/dist/managers/cronManager.js +2 -0
  14. package/dist/managers/liveConfigManager.d.ts.map +1 -1
  15. package/dist/managers/liveConfigManager.js +0 -9
  16. package/dist/managers/mcpManager.d.ts.map +1 -1
  17. package/dist/managers/mcpManager.js +13 -3
  18. package/dist/managers/messageManager.d.ts +4 -6
  19. package/dist/managers/messageManager.d.ts.map +1 -1
  20. package/dist/managers/messageManager.js +27 -81
  21. package/dist/managers/skillManager.d.ts +1 -0
  22. package/dist/managers/skillManager.d.ts.map +1 -1
  23. package/dist/managers/skillManager.js +11 -2
  24. package/dist/managers/toolManager.d.ts.map +1 -1
  25. package/dist/managers/toolManager.js +2 -0
  26. package/dist/prompts/index.d.ts +3 -2
  27. package/dist/prompts/index.d.ts.map +1 -1
  28. package/dist/prompts/index.js +5 -1
  29. package/dist/services/aiService.d.ts.map +1 -1
  30. package/dist/services/aiService.js +13 -9
  31. package/dist/services/configurationService.d.ts.map +1 -1
  32. package/dist/services/configurationService.js +24 -9
  33. package/dist/services/hook.d.ts.map +1 -1
  34. package/dist/services/hook.js +6 -0
  35. package/dist/services/initializationService.d.ts.map +1 -1
  36. package/dist/services/initializationService.js +1 -1
  37. package/dist/services/interactionService.js +1 -1
  38. package/dist/services/reversionService.d.ts +5 -1
  39. package/dist/services/reversionService.d.ts.map +1 -1
  40. package/dist/services/reversionService.js +50 -22
  41. package/dist/services/session.d.ts +4 -13
  42. package/dist/services/session.d.ts.map +1 -1
  43. package/dist/services/session.js +59 -215
  44. package/dist/telemetry/sessionTracing.d.ts +31 -20
  45. package/dist/telemetry/sessionTracing.d.ts.map +1 -1
  46. package/dist/telemetry/sessionTracing.js +192 -62
  47. package/dist/tools/bashTool.d.ts.map +1 -1
  48. package/dist/tools/bashTool.js +16 -1
  49. package/dist/tools/readTool.d.ts.map +1 -1
  50. package/dist/tools/readTool.js +6 -7
  51. package/dist/types/config.d.ts +7 -0
  52. package/dist/types/config.d.ts.map +1 -1
  53. package/dist/types/hooks.d.ts +1 -0
  54. package/dist/types/hooks.d.ts.map +1 -1
  55. package/dist/types/messaging.d.ts +0 -1
  56. package/dist/types/messaging.d.ts.map +1 -1
  57. package/dist/types/skills.d.ts +1 -0
  58. package/dist/types/skills.d.ts.map +1 -1
  59. package/dist/types/telemetry.d.ts +16 -1
  60. package/dist/types/telemetry.d.ts.map +1 -1
  61. package/dist/utils/atomicWrite.d.ts +15 -0
  62. package/dist/utils/atomicWrite.d.ts.map +1 -0
  63. package/dist/utils/atomicWrite.js +59 -0
  64. package/dist/utils/cacheControlUtils.d.ts +3 -15
  65. package/dist/utils/cacheControlUtils.d.ts.map +1 -1
  66. package/dist/utils/cacheControlUtils.js +4 -35
  67. package/dist/utils/containerSetup.d.ts.map +1 -1
  68. package/dist/utils/containerSetup.js +5 -1
  69. package/dist/utils/convertMessagesForAPI.d.ts +9 -1
  70. package/dist/utils/convertMessagesForAPI.d.ts.map +1 -1
  71. package/dist/utils/convertMessagesForAPI.js +74 -55
  72. package/dist/utils/fileUtils.d.ts +0 -6
  73. package/dist/utils/fileUtils.d.ts.map +1 -1
  74. package/dist/utils/fileUtils.js +0 -43
  75. package/dist/utils/gitUtils.d.ts +11 -0
  76. package/dist/utils/gitUtils.d.ts.map +1 -1
  77. package/dist/utils/gitUtils.js +51 -0
  78. package/dist/utils/groupMessagesByApiRound.d.ts.map +1 -1
  79. package/dist/utils/groupMessagesByApiRound.js +7 -6
  80. package/dist/utils/mcpUtils.d.ts.map +1 -1
  81. package/dist/utils/mcpUtils.js +2 -1
  82. package/dist/utils/modelCapabilities.d.ts +22 -0
  83. package/dist/utils/modelCapabilities.d.ts.map +1 -0
  84. package/dist/utils/modelCapabilities.js +24 -0
  85. package/dist/utils/skillParser.d.ts.map +1 -1
  86. package/dist/utils/skillParser.js +3 -1
  87. package/dist/utils/tokenEstimate.d.ts +24 -0
  88. package/dist/utils/tokenEstimate.d.ts.map +1 -0
  89. package/dist/utils/tokenEstimate.js +30 -0
  90. package/dist/utils/worktreeUtils.d.ts.map +1 -1
  91. package/dist/utils/worktreeUtils.js +3 -1
  92. package/package.json +1 -1
  93. package/src/agent.ts +11 -5
  94. package/src/index.ts +1 -0
  95. package/src/managers/aiManager.ts +36 -7
  96. package/src/managers/cronManager.ts +2 -0
  97. package/src/managers/liveConfigManager.ts +0 -12
  98. package/src/managers/mcpManager.ts +16 -3
  99. package/src/managers/messageManager.ts +32 -109
  100. package/src/managers/skillManager.ts +16 -2
  101. package/src/managers/toolManager.ts +2 -0
  102. package/src/prompts/index.ts +6 -1
  103. package/src/services/aiService.ts +12 -5
  104. package/src/services/configurationService.ts +26 -24
  105. package/src/services/hook.ts +7 -0
  106. package/src/services/initializationService.ts +1 -3
  107. package/src/services/interactionService.ts +1 -1
  108. package/src/services/reversionService.ts +50 -27
  109. package/src/services/session.ts +63 -262
  110. package/src/telemetry/sessionTracing.ts +225 -73
  111. package/src/tools/bashTool.ts +23 -1
  112. package/src/tools/readTool.ts +6 -12
  113. package/src/types/config.ts +8 -0
  114. package/src/types/hooks.ts +1 -0
  115. package/src/types/messaging.ts +0 -1
  116. package/src/types/skills.ts +1 -0
  117. package/src/types/telemetry.ts +19 -1
  118. package/src/utils/atomicWrite.ts +61 -0
  119. package/src/utils/cacheControlUtils.ts +5 -38
  120. package/src/utils/containerSetup.ts +5 -1
  121. package/src/utils/convertMessagesForAPI.ts +87 -57
  122. package/src/utils/fileUtils.ts +0 -46
  123. package/src/utils/gitUtils.ts +54 -0
  124. package/src/utils/groupMessagesByApiRound.ts +7 -6
  125. package/src/utils/mcpUtils.ts +2 -1
  126. package/src/utils/modelCapabilities.ts +30 -0
  127. package/src/utils/skillParser.ts +3 -1
  128. package/src/utils/tokenEstimate.ts +34 -0
  129. package/src/utils/worktreeUtils.ts +8 -1
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Atomically write `data` to `filePath` (async).
3
+ *
4
+ * Writes to a temp file then renames it into place. On any failure the temp
5
+ * file is removed and the error is re-thrown so callers can handle it.
6
+ */
7
+ export declare function atomicWriteFile(filePath: string, data: string): Promise<void>;
8
+ /**
9
+ * Atomically write `data` to `filePath` (sync).
10
+ *
11
+ * Sync variant for callers that cannot await. Same temp-file-then-rename
12
+ * strategy as {@link atomicWriteFile}.
13
+ */
14
+ export declare function atomicWriteFileSync(filePath: string, data: string): void;
15
+ //# sourceMappingURL=atomicWrite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomicWrite.d.ts","sourceRoot":"","sources":["../../src/utils/atomicWrite.ts"],"names":[],"mappings":"AAiBA;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAaf;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAaxE"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Atomic file write utilities.
3
+ *
4
+ * Writes data to a temporary file first, then renames it to the target path.
5
+ * This prevents readers from observing a partially-written (truncated) file,
6
+ * which is critical for config files that may be read concurrently.
7
+ *
8
+ * Temp file naming: `${targetPath}.tmp.${process.pid}.${randomUUID()}`
9
+ * — avoids collisions when multiple processes write the same file.
10
+ */
11
+ import { writeFileSync, renameSync, unlinkSync, promises as fsp } from "fs";
12
+ import { randomUUID } from "crypto";
13
+ function tmpPathFor(targetPath) {
14
+ return `${targetPath}.tmp.${process.pid}.${randomUUID()}`;
15
+ }
16
+ /**
17
+ * Atomically write `data` to `filePath` (async).
18
+ *
19
+ * Writes to a temp file then renames it into place. On any failure the temp
20
+ * file is removed and the error is re-thrown so callers can handle it.
21
+ */
22
+ export async function atomicWriteFile(filePath, data) {
23
+ const tmpPath = tmpPathFor(filePath);
24
+ try {
25
+ await fsp.writeFile(tmpPath, data, "utf-8");
26
+ await fsp.rename(tmpPath, filePath);
27
+ }
28
+ catch (error) {
29
+ try {
30
+ await fsp.unlink(tmpPath);
31
+ }
32
+ catch {
33
+ // Ignore cleanup errors — the original error is more important
34
+ }
35
+ throw error;
36
+ }
37
+ }
38
+ /**
39
+ * Atomically write `data` to `filePath` (sync).
40
+ *
41
+ * Sync variant for callers that cannot await. Same temp-file-then-rename
42
+ * strategy as {@link atomicWriteFile}.
43
+ */
44
+ export function atomicWriteFileSync(filePath, data) {
45
+ const tmpPath = tmpPathFor(filePath);
46
+ try {
47
+ writeFileSync(tmpPath, data, "utf-8");
48
+ renameSync(tmpPath, filePath);
49
+ }
50
+ catch (error) {
51
+ try {
52
+ unlinkSync(tmpPath);
53
+ }
54
+ catch {
55
+ // Ignore cleanup errors — the original error is more important
56
+ }
57
+ throw error;
58
+ }
59
+ }
@@ -6,6 +6,7 @@
6
6
  * models and preserves backward compatibility with existing message formats.
7
7
  */
8
8
  import type { ChatCompletionMessageParam, ChatCompletionContentPart, ChatCompletionContentPartText, CompletionUsage } from "openai/resources";
9
+ import type { ModelCapabilities } from "../types/config.js";
9
10
  /**
10
11
  * Cache control directive for Claude models
11
12
  */
@@ -43,19 +44,6 @@ export interface ClaudeUsage extends CompletionUsage {
43
44
  };
44
45
  prompt_tokens_details?: ExtendedPromptTokensDetails;
45
46
  }
46
- /**
47
- * Determines if a model supports prompt caching
48
- * @param modelName - Model identifier
49
- * @returns True if model name matches the cache pattern (default: contains 'claude')
50
- */
51
- export declare function supportsPromptCaching(modelName: string): boolean;
52
- /**
53
- * Determines if a model supports cache control
54
- * @param modelName - Model identifier
55
- * @returns True if model name contains 'claude' (case-insensitive)
56
- * @deprecated Use supportsPromptCaching instead
57
- */
58
- export declare const isClaudeModel: typeof supportsPromptCaching;
59
47
  /**
60
48
  * Validates cache control structure
61
49
  * @param control - Object to validate
@@ -91,10 +79,10 @@ export declare function countContentBlocks(messages: ChatCompletionMessageParam[
91
79
  * Tools are marked separately via addCacheControlToLastTool (called by aiService).
92
80
  *
93
81
  * @param messages - Original OpenAI message array
94
- * @param modelName - Model name for cache detection
82
+ * @param capabilities - Declarative model capabilities for cache detection
95
83
  * @returns Messages with cache control markers applied
96
84
  */
97
- export declare function transformMessagesForExplicitCache(messages: ChatCompletionMessageParam[], modelName: string): ChatCompletionMessageParam[];
85
+ export declare function transformMessagesForExplicitCache(messages: ChatCompletionMessageParam[], capabilities?: ModelCapabilities): ChatCompletionMessageParam[];
98
86
  /**
99
87
  * Extends standard usage with cache metrics
100
88
  * Extracts cache tokens from both Claude-specific top-level fields and
@@ -1 +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,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAO1B;;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;;;GAGG;AACH,MAAM,WAAW,2BACf,SAAQ,eAAe,CAAC,mBAAmB;IAC3C,2BAA2B,CAAC,EAAE,MAAM,CAAC;CACtC;AAED;;;GAGG;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;IAGF,qBAAqB,CAAC,EAAE,2BAA2B,CAAC;CACrD;AAUD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAoBhE;AAED;;;;;GAKG;AACH,eAAO,MAAM,aAAa,8BAAwB,CAAC;AAEnD;;;;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,CAmFvC;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,0BAA0B,EAAE,GACrC,MAAM,CAYR;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iCAAiC,CAC/C,QAAQ,EAAE,0BAA0B,EAAE,EACtC,SAAS,EAAE,MAAM,GAChB,0BAA0B,EAAE,CAsF9B;AAED;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CACzC,aAAa,EAAE,eAAe,EAC9B,YAAY,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAClC,WAAW,CAiDb;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAyCvE"}
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,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAM5D;;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;;;GAGG;AACH,MAAM,WAAW,2BACf,SAAQ,eAAe,CAAC,mBAAmB;IAC3C,2BAA2B,CAAC,EAAE,MAAM,CAAC;CACtC;AAED;;;GAGG;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;IAGF,qBAAqB,CAAC,EAAE,2BAA2B,CAAC;CACrD;AAUD;;;;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,CAmFvC;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,0BAA0B,EAAE,GACrC,MAAM,CAYR;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iCAAiC,CAC/C,QAAQ,EAAE,0BAA0B,EAAE,EACtC,YAAY,CAAC,EAAE,iBAAiB,GAC/B,0BAA0B,EAAE,CAsF9B;AAED;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CACzC,aAAa,EAAE,eAAe,EAC9B,YAAY,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAClC,WAAW,CAiDb;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAyCvE"}
@@ -6,44 +6,13 @@
6
6
  * models and preserves backward compatibility with existing message formats.
7
7
  */
8
8
  import { logger } from "./globalLogger.js";
9
+ import { supportsPromptCaching } from "./modelCapabilities.js";
9
10
  // ============================================================================
10
11
  // Default Configuration
11
12
  // ============================================================================
12
13
  // ============================================================================
13
14
  // Utility Functions (Basic Structure - to be implemented)
14
15
  // ============================================================================
15
- /**
16
- * Determines if a model supports prompt caching
17
- * @param modelName - Model identifier
18
- * @returns True if model name matches the cache pattern (default: contains 'claude')
19
- */
20
- export function supportsPromptCaching(modelName) {
21
- // Handle null, undefined, and non-string inputs
22
- if (!modelName || typeof modelName !== "string") {
23
- return false;
24
- }
25
- // Handle empty strings and whitespace-only strings
26
- const trimmed = modelName.trim();
27
- if (trimmed.length === 0) {
28
- return false;
29
- }
30
- const cachePattern = process.env.WAVE_PROMPT_CACHE_REGEX || "claude";
31
- try {
32
- const regex = new RegExp(cachePattern, "i");
33
- return regex.test(trimmed);
34
- }
35
- catch {
36
- // If regex is invalid, fall back to simple includes check with default
37
- return trimmed.toLowerCase().includes("claude");
38
- }
39
- }
40
- /**
41
- * Determines if a model supports cache control
42
- * @param modelName - Model identifier
43
- * @returns True if model name contains 'claude' (case-insensitive)
44
- * @deprecated Use supportsPromptCaching instead
45
- */
46
- export const isClaudeModel = supportsPromptCaching;
47
16
  /**
48
17
  * Validates cache control structure
49
18
  * @param control - Object to validate
@@ -165,10 +134,10 @@ export function countContentBlocks(messages) {
165
134
  * Tools are marked separately via addCacheControlToLastTool (called by aiService).
166
135
  *
167
136
  * @param messages - Original OpenAI message array
168
- * @param modelName - Model name for cache detection
137
+ * @param capabilities - Declarative model capabilities for cache detection
169
138
  * @returns Messages with cache control markers applied
170
139
  */
171
- export function transformMessagesForExplicitCache(messages, modelName) {
140
+ export function transformMessagesForExplicitCache(messages, capabilities) {
172
141
  // Validate inputs
173
142
  if (!messages || !Array.isArray(messages)) {
174
143
  logger.warn("Invalid messages array provided to transformMessagesForExplicitCache");
@@ -178,7 +147,7 @@ export function transformMessagesForExplicitCache(messages, modelName) {
178
147
  return [];
179
148
  }
180
149
  // Only apply cache control for models that support prompt caching
181
- if (!supportsPromptCaching(modelName)) {
150
+ if (!supportsPromptCaching(capabilities)) {
182
151
  return messages;
183
152
  }
184
153
  // Find first system message index
@@ -1 +1 @@
1
- {"version":3,"file":"containerSetup.d.ts","sourceRoot":"","sources":["../../src/utils/containerSetup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AA0B3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAM3E,OAAO,KAAK,EAAE,YAAY,EAAmB,MAAM,mBAAmB,CAAC;AACvE,OAAO,KAAK,EACV,cAAc,EACd,KAAK,EACL,IAAI,EACJ,cAAc,EAEf,MAAM,mBAAmB,CAAC;AAM3B,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB,EAAE,oBAAoB,CAAC;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;IAGhB,uBAAuB,EAAE,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,IAAI,CAAC;IAC3D,aAAa,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;IACvC,sBAAsB,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACvD,wBAAwB,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACzD,iBAAiB,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAClD,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,0BAA0B,GACvC,SAAS,CA8SX"}
1
+ {"version":3,"file":"containerSetup.d.ts","sourceRoot":"","sources":["../../src/utils/containerSetup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AA0B3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAM3E,OAAO,KAAK,EAAE,YAAY,EAAmB,MAAM,mBAAmB,CAAC;AACvE,OAAO,KAAK,EACV,cAAc,EACd,KAAK,EACL,IAAI,EACJ,cAAc,EAEf,MAAM,mBAAmB,CAAC;AAM3B,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB,EAAE,oBAAoB,CAAC;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;IAGhB,uBAAuB,EAAE,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,IAAI,CAAC;IAC3D,aAAa,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;IACvC,sBAAsB,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACvD,wBAAwB,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACzD,iBAAiB,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAClD,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,0BAA0B,GACvC,SAAS,CAkTX"}
@@ -139,7 +139,11 @@ export function setupAgentContainer(setupOptions) {
139
139
  });
140
140
  container.register("SkillManager", skillManager);
141
141
  const rootSessionId = messageManager.getRootSessionId();
142
- container.register("ReversionService", new ReversionService(rootSessionId));
142
+ const reversionService = new ReversionService(rootSessionId);
143
+ container.register("ReversionService", reversionService);
144
+ reversionService.cleanupOldSessions(30).catch((error) => {
145
+ logger.error("Failed to cleanup old file history:", error);
146
+ });
143
147
  const reversionManager = new ReversionManager(container);
144
148
  container.register("ReversionManager", reversionManager);
145
149
  const canUseToolWithPermissionRequest = options.canUseTool
@@ -1,10 +1,18 @@
1
1
  import type { Message } from "../types/index.js";
2
2
  import { ChatCompletionMessageParam } from "openai/resources.js";
3
+ /**
4
+ * Options for converting messages to API format.
5
+ */
6
+ export interface ConvertMessagesOptions {
7
+ /** When false, images are stripped and replaced with text placeholders. */
8
+ supportsVision?: boolean;
9
+ }
3
10
  /**
4
11
  * Convert message format to API call format, stopping when a compacted message is encountered.
5
12
  * Messages with no meaningful content or tool calls are filtered out.
6
13
  * @param messages Message list
14
+ * @param options Optional conversion options (e.g. supportsVision)
7
15
  * @returns Converted API message format list
8
16
  */
9
- export declare function convertMessagesForAPI(messages: Message[]): ChatCompletionMessageParam[];
17
+ export declare function convertMessagesForAPI(messages: Message[], options?: ConvertMessagesOptions): ChatCompletionMessageParam[];
10
18
  //# sourceMappingURL=convertMessagesForAPI.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"convertMessagesForAPI.d.ts","sourceRoot":"","sources":["../../src/utils/convertMessagesForAPI.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAKjD,OAAO,EAEL,0BAA0B,EAC3B,MAAM,qBAAqB,CAAC;AAiC7B;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,OAAO,EAAE,GAClB,0BAA0B,EAAE,CAsP9B"}
1
+ {"version":3,"file":"convertMessagesForAPI.d.ts","sourceRoot":"","sources":["../../src/utils/convertMessagesForAPI.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAKjD,OAAO,EAEL,0BAA0B,EAC3B,MAAM,qBAAqB,CAAC;AAiC7B;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,2EAA2E;IAC3E,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,0BAA0B,EAAE,CA0Q9B"}
@@ -36,9 +36,11 @@ function safeToolArguments(args) {
36
36
  * Convert message format to API call format, stopping when a compacted message is encountered.
37
37
  * Messages with no meaningful content or tool calls are filtered out.
38
38
  * @param messages Message list
39
+ * @param options Optional conversion options (e.g. supportsVision)
39
40
  * @returns Converted API message format list
40
41
  */
41
- export function convertMessagesForAPI(messages) {
42
+ export function convertMessagesForAPI(messages, options) {
43
+ const supportsVision = options?.supportsVision !== false;
42
44
  const recentMessages = [];
43
45
  const startIndex = messages.length - 1;
44
46
  for (let i = startIndex; i >= 0; i--) {
@@ -66,49 +68,57 @@ export function convertMessagesForAPI(messages) {
66
68
  const toolBlocks = message.blocks.filter((block) => block.type === "tool");
67
69
  const completedToolIds = new Set(); // Record completed tool IDs
68
70
  if (toolBlocks.length > 0) {
71
+ // Collect image user messages to place after all tool messages
72
+ const imageUserMessages = [];
69
73
  toolBlocks.forEach((toolBlock) => {
70
74
  // Only add completed tool blocks (i.e., stage is 'end')
71
75
  if (toolBlock.id && toolBlock.stage === "end") {
72
76
  completedToolIds.add(toolBlock.id);
73
- // Check for image data
77
+ // Always add a proper tool message to satisfy tool_call_id pairing
78
+ recentMessages.unshift({
79
+ tool_call_id: toolBlock.id,
80
+ role: "tool",
81
+ content: stripAnsiColors(toolBlock.result || ""),
82
+ });
83
+ // If there are images, also prepare a user message with image content
74
84
  if (toolBlock.images && toolBlock.images.length > 0) {
75
- // If there is an image, create a user message instead of a tool message
76
- const contentParts = [];
77
- // Add tool result as text
78
- const toolResultText = `Tool result for ${toolBlock.name || "unknown tool"}:\n${stripAnsiColors(toolBlock.result || "")}`;
79
- contentParts.push({
80
- type: "text",
81
- text: toolResultText,
82
- });
83
- // Add image
84
- toolBlock.images.forEach((image) => {
85
- const imageUrl = image.data.startsWith("data:")
86
- ? image.data
87
- : `data:${image.mediaType || "image/png"};base64,${image.data}`;
88
- contentParts.push({
89
- type: "image_url",
90
- image_url: {
91
- url: imageUrl,
92
- detail: "auto",
93
- },
85
+ if (supportsVision) {
86
+ const contentParts = [];
87
+ toolBlock.images.forEach((image) => {
88
+ const imageUrl = image.data.startsWith("data:")
89
+ ? image.data
90
+ : `data:${image.mediaType || "image/png"};base64,${image.data}`;
91
+ contentParts.push({
92
+ type: "image_url",
93
+ image_url: {
94
+ url: imageUrl,
95
+ detail: "auto",
96
+ },
97
+ });
94
98
  });
95
- });
96
- // Add user message
97
- recentMessages.unshift({
98
- role: "user",
99
- content: contentParts,
100
- });
101
- }
102
- else {
103
- // Normal tool message
104
- recentMessages.unshift({
105
- tool_call_id: toolBlock.id,
106
- role: "tool",
107
- content: stripAnsiColors(toolBlock.result || ""),
108
- });
99
+ imageUserMessages.push({
100
+ role: "user",
101
+ content: contentParts,
102
+ });
103
+ }
104
+ else {
105
+ // Non-vision model: replace images with a text placeholder
106
+ imageUserMessages.push({
107
+ role: "user",
108
+ content: "[Tool returned an image, but the current model does not support image recognition]",
109
+ });
110
+ }
109
111
  }
110
112
  }
111
113
  });
114
+ // Insert image user messages after all tool messages but before the
115
+ // assistant message (which will be unshifted next). Since tool messages
116
+ // were unshifted to the front, we splice images right after them.
117
+ if (imageUserMessages.length > 0) {
118
+ // Tool messages are at indices 0..N-1, existing messages start at N
119
+ const toolMsgCount = completedToolIds.size;
120
+ recentMessages.splice(toolMsgCount, 0, ...imageUserMessages);
121
+ }
112
122
  }
113
123
  // Construct the content of the assistant message
114
124
  let content = "";
@@ -180,28 +190,37 @@ export function convertMessagesForAPI(messages) {
180
190
  if (block.type === "image" &&
181
191
  block.imageUrls &&
182
192
  block.imageUrls.length > 0) {
183
- block.imageUrls.forEach((imageUrl) => {
184
- // Check if it's already base64, convert if not
185
- let finalImageUrl = imageUrl;
186
- if (!imageUrl.startsWith("data:image/")) {
187
- // If it's a file path, it needs to be converted to base64
188
- try {
189
- finalImageUrl = convertImageToBase64(imageUrl);
190
- }
191
- catch (error) {
192
- logger.error("Failed to convert image path to base64:", imageUrl, error);
193
- // Skip this image, do not add to content
194
- return;
195
- }
196
- }
193
+ if (!supportsVision) {
194
+ // Non-vision model: replace images with a text placeholder
197
195
  contentParts.push({
198
- type: "image_url",
199
- image_url: {
200
- url: finalImageUrl,
201
- detail: "auto",
202
- },
196
+ type: "text",
197
+ text: "[User shared an image, but the current model does not support image recognition]",
203
198
  });
204
- });
199
+ }
200
+ else {
201
+ block.imageUrls.forEach((imageUrl) => {
202
+ // Check if it's already base64, convert if not
203
+ let finalImageUrl = imageUrl;
204
+ if (!imageUrl.startsWith("data:image/")) {
205
+ // If it's a file path, it needs to be converted to base64
206
+ try {
207
+ finalImageUrl = convertImageToBase64(imageUrl);
208
+ }
209
+ catch (error) {
210
+ logger.error("Failed to convert image path to base64:", imageUrl, error);
211
+ // Skip this image, do not add to content
212
+ return;
213
+ }
214
+ }
215
+ contentParts.push({
216
+ type: "image_url",
217
+ image_url: {
218
+ url: finalImageUrl,
219
+ detail: "auto",
220
+ },
221
+ });
222
+ });
223
+ }
205
224
  }
206
225
  // If there is a tool block in user message, add its result
207
226
  if (block.type === "tool" && block.stage === "end" && block.result) {
@@ -26,12 +26,6 @@ export declare function readFirstNLines(filePath: string, maxLines: number): Pro
26
26
  * @return {Promise<string>} - The last non-empty line of the file, or an empty string if no non-empty lines found.
27
27
  */
28
28
  export declare function getLastLine(filePath: string, minLength?: number): Promise<string>;
29
- /**
30
- * Ensures that a pattern is present in the global git ignore file.
31
- *
32
- * @param {string} pattern - The pattern to add to global git ignore.
33
- */
34
- export declare function ensureGlobalGitIgnore(pattern: string): Promise<void>;
35
29
  /**
36
30
  * Suggests similar paths if a file is not found.
37
31
  */
@@ -1 +1 @@
1
- {"version":3,"file":"fileUtils.d.ts","sourceRoot":"","sources":["../../src/utils/fileUtils.ts"],"names":[],"mappings":"AAOA;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAwBrE;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,EAAE,CAAC,CA4BnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,SAAS,SAAI,GACZ,OAAO,CAAC,MAAM,CAAC,CA8DjB;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqC1E;AA2BD;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,EAAE,CAAC,CAwBnB;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGxB"}
1
+ {"version":3,"file":"fileUtils.d.ts","sourceRoot":"","sources":["../../src/utils/fileUtils.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAwBrE;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,EAAE,CAAC,CA4BnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,SAAS,SAAI,GACZ,OAAO,CAAC,MAAM,CAAC,CA8DjB;AA2BD;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,EAAE,CAAC,CAwBnB;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGxB"}
@@ -1,8 +1,6 @@
1
1
  import fs from "node:fs/promises";
2
2
  import { createReadStream } from "node:fs";
3
3
  import path from "node:path";
4
- import { execSync } from "node:child_process";
5
- import { homedir } from "node:os";
6
4
  import { glob } from "glob";
7
5
  /**
8
6
  * Reads the first line of a file efficiently using Node.js readline.
@@ -138,47 +136,6 @@ export async function getLastLine(filePath, minLength = 1) {
138
136
  }
139
137
  }
140
138
  }
141
- /**
142
- * Ensures that a pattern is present in the global git ignore file.
143
- *
144
- * @param {string} pattern - The pattern to add to global git ignore.
145
- */
146
- export async function ensureGlobalGitIgnore(pattern) {
147
- try {
148
- let globalIgnorePath;
149
- try {
150
- globalIgnorePath = execSync("git config --get core.excludesfile", {
151
- encoding: "utf8",
152
- }).trim();
153
- }
154
- catch {
155
- // If not set, use default paths
156
- const xdgConfigHome = process.env.XDG_CONFIG_HOME || path.join(homedir(), ".config");
157
- globalIgnorePath = path.join(xdgConfigHome, "git", "ignore");
158
- }
159
- if (!globalIgnorePath)
160
- return;
161
- // Ensure directory exists
162
- await fs.mkdir(path.dirname(globalIgnorePath), { recursive: true });
163
- let content = "";
164
- try {
165
- content = await fs.readFile(globalIgnorePath, "utf8");
166
- }
167
- catch {
168
- // File doesn't exist
169
- }
170
- const lines = content.split("\n").map((line) => line.trim());
171
- if (!lines.includes(pattern)) {
172
- const newContent = content.endsWith("\n") || content === ""
173
- ? `${content}${pattern}\n`
174
- : `${content}\n${pattern}\n`;
175
- await fs.writeFile(globalIgnorePath, newContent, "utf8");
176
- }
177
- }
178
- catch {
179
- // Ignore errors
180
- }
181
- }
182
139
  /**
183
140
  * Simple Levenshtein distance implementation
184
141
  */
@@ -45,6 +45,17 @@ export declare function resolveGitDir(cwd: string): string | null;
45
45
  * @returns Default remote branch name
46
46
  */
47
47
  export declare function getDefaultRemoteBranch(cwd: string): string;
48
+ /**
49
+ * Ensure that Wave runtime files are excluded from git status by writing
50
+ * patterns to `.git/info/exclude` (per-repo, not global).
51
+ *
52
+ * Idempotent: if the marker `# wave-runtime` is already present in the
53
+ * exclude file, it skips writing. A module-level Set provides additional
54
+ * per-process dedup to avoid redundant file reads.
55
+ *
56
+ * @param cwd Working directory to start searching from
57
+ */
58
+ export declare function ensureWaveRuntimeFilesExcluded(cwd: string): void;
48
59
  /**
49
60
  * Check if there are uncommitted changes in the working directory
50
61
  * @param cwd Working directory
@@ -1 +1 @@
1
- {"version":3,"file":"gitUtils.d.ts","sourceRoot":"","sources":["../../src/utils/gitUtils.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAevD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAUlD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAWnD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAetD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkCxD;AAwDD;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA4B1D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAW1D;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAYvE"}
1
+ {"version":3,"file":"gitUtils.d.ts","sourceRoot":"","sources":["../../src/utils/gitUtils.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAevD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAUlD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAWnD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAetD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkCxD;AAwDD;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA4B1D;AAQD;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAoChE;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAW1D;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAYvE"}
@@ -216,6 +216,57 @@ export function getDefaultRemoteBranch(cwd) {
216
216
  // 4. Hardcoded fallback
217
217
  return "main";
218
218
  }
219
+ /**
220
+ * Module-level set tracking git dirs already processed in this process,
221
+ * to avoid redundant I/O.
222
+ */
223
+ const excludedGitDirs = new Set();
224
+ /**
225
+ * Ensure that Wave runtime files are excluded from git status by writing
226
+ * patterns to `.git/info/exclude` (per-repo, not global).
227
+ *
228
+ * Idempotent: if the marker `# wave-runtime` is already present in the
229
+ * exclude file, it skips writing. A module-level Set provides additional
230
+ * per-process dedup to avoid redundant file reads.
231
+ *
232
+ * @param cwd Working directory to start searching from
233
+ */
234
+ export function ensureWaveRuntimeFilesExcluded(cwd) {
235
+ try {
236
+ const gitDir = resolveGitDir(cwd);
237
+ if (!gitDir)
238
+ return;
239
+ if (excludedGitDirs.has(gitDir))
240
+ return;
241
+ const excludePath = path.join(gitDir, "info", "exclude");
242
+ let content = "";
243
+ try {
244
+ content = fsSync.readFileSync(excludePath, "utf8");
245
+ }
246
+ catch {
247
+ // File doesn't exist yet
248
+ }
249
+ const marker = "# wave-runtime";
250
+ if (content.includes(marker)) {
251
+ excludedGitDirs.add(gitDir);
252
+ return;
253
+ }
254
+ const block = [
255
+ marker,
256
+ "**/.wave/scheduled_tasks.lock",
257
+ "**/.wave/scheduled_tasks.json",
258
+ "**/.wave/worktrees/",
259
+ "**/.wave/settings.local.json",
260
+ "",
261
+ ].join("\n");
262
+ fsSync.mkdirSync(path.join(gitDir, "info"), { recursive: true });
263
+ fsSync.appendFileSync(excludePath, block);
264
+ excludedGitDirs.add(gitDir);
265
+ }
266
+ catch {
267
+ // Best-effort: ignore all errors
268
+ }
269
+ }
219
270
  /**
220
271
  * Check if there are uncommitted changes in the working directory
221
272
  * @param cwd Working directory
@@ -1 +1 @@
1
- {"version":3,"file":"groupMessagesByApiRound.d.ts","sourceRoot":"","sources":["../../src/utils/groupMessagesByApiRound.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,QAAQ,EAAE,CA8DvE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,OAAO,EAAE,EACnB,UAAU,EAAE,MAAM,GACjB,OAAO,EAAE,CAIX"}
1
+ {"version":3,"file":"groupMessagesByApiRound.d.ts","sourceRoot":"","sources":["../../src/utils/groupMessagesByApiRound.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAGjD,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,QAAQ,EAAE,CA8DvE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,OAAO,EAAE,EACnB,UAAU,EAAE,MAAM,GACjB,OAAO,EAAE,CAIX"}
@@ -1,3 +1,4 @@
1
+ import { estimateTokens as estimateStrTokens } from "./tokenEstimate.js";
1
2
  /**
2
3
  * Groups messages into "API rounds" — each round corresponds to one API
3
4
  * call-response cycle. This is critical because in agentic sessions with a
@@ -74,24 +75,24 @@ export function getLastApiRounds(messages, roundCount) {
74
75
  return lastRounds.flatMap((r) => r.messages);
75
76
  }
76
77
  /**
77
- * Roughly estimate token count from character count (~4 chars per token).
78
+ * Estimate token count from message blocks using CJK-aware estimation.
78
79
  */
79
80
  function estimateTokens(messages) {
80
- let chars = 0;
81
+ let combined = "";
81
82
  for (const msg of messages) {
82
83
  for (const block of msg.blocks) {
83
84
  if ("content" in block && typeof block.content === "string") {
84
- chars += block.content.length;
85
+ combined += block.content;
85
86
  }
86
87
  if (block.type === "tool" &&
87
88
  block.parameters &&
88
89
  typeof block.parameters === "string") {
89
- chars += block.parameters.length;
90
+ combined += block.parameters;
90
91
  }
91
92
  if (block.type === "tool" && block.result) {
92
- chars += block.result.length;
93
+ combined += block.result;
93
94
  }
94
95
  }
95
96
  }
96
- return Math.ceil(chars / 4);
97
+ return estimateStrTokens(combined);
97
98
  }
@@ -1 +1 @@
1
- {"version":3,"file":"mcpUtils.d.ts","sourceRoot":"","sources":["../../src/utils/mcpUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,KAAK,EAAE,UAAU,EAAc,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAgDlE;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,GACjB,0BAA0B,CAgB5B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,CACX,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,WAAW,KAClB,OAAO,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACtD,CAAC,GACD,UAAU,CAiCZ;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,eAAe,EAAE,GACzB,eAAe,GAAG,SAAS,CAK7B"}
1
+ {"version":3,"file":"mcpUtils.d.ts","sourceRoot":"","sources":["../../src/utils/mcpUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,KAAK,EAAE,UAAU,EAAc,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAgDlE;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,GACjB,0BAA0B,CAgB5B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,CACX,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,WAAW,KAClB,OAAO,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACtD,CAAC,GACD,UAAU,CAiCZ;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,eAAe,EAAE,GACzB,eAAe,GAAG,SAAS,CAM7B"}
@@ -84,5 +84,6 @@ export function createMcpToolPlugin(mcpTool, serverName, executeTool) {
84
84
  * Find which server a tool belongs to
85
85
  */
86
86
  export function findToolServer(toolName, servers) {
87
- return servers.find((s) => s.status === "connected" && s.tools?.some((t) => t.name === toolName));
87
+ return servers.find((s) => (s.status === "connected" || s.status === "reconnecting") &&
88
+ s.tools?.some((t) => t.name === toolName));
88
89
  }