@machina.ai/cell-cli-core 1.16.0-rc2 → 1.19.4-rc1

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 (277) hide show
  1. package/dist/package.json +6 -4
  2. package/dist/src/agents/codebase-investigator.js +1 -1
  3. package/dist/src/agents/executor.js +12 -21
  4. package/dist/src/agents/executor.js.map +1 -1
  5. package/dist/src/agents/executor.test.js +53 -57
  6. package/dist/src/agents/executor.test.js.map +1 -1
  7. package/dist/src/agents/registry.d.ts +4 -0
  8. package/dist/src/agents/registry.js +23 -0
  9. package/dist/src/agents/registry.js.map +1 -1
  10. package/dist/src/agents/registry.test.js +30 -16
  11. package/dist/src/agents/registry.test.js.map +1 -1
  12. package/dist/src/availability/modelAvailabilityService.d.ts +34 -0
  13. package/dist/src/availability/modelAvailabilityService.js +84 -0
  14. package/dist/src/availability/modelAvailabilityService.js.map +1 -0
  15. package/dist/src/availability/modelAvailabilityService.test.d.ts +6 -0
  16. package/dist/src/availability/modelAvailabilityService.test.js +140 -0
  17. package/dist/src/availability/modelAvailabilityService.test.js.map +1 -0
  18. package/dist/src/availability/modelPolicy.d.ts +42 -0
  19. package/dist/src/availability/modelPolicy.js +7 -0
  20. package/dist/src/availability/modelPolicy.js.map +1 -0
  21. package/dist/src/availability/policyCatalog.d.ts +20 -0
  22. package/dist/src/availability/policyCatalog.js +83 -0
  23. package/dist/src/availability/policyCatalog.js.map +1 -0
  24. package/dist/src/availability/policyCatalog.test.d.ts +6 -0
  25. package/dist/src/availability/policyCatalog.test.js +70 -0
  26. package/dist/src/availability/policyCatalog.test.js.map +1 -0
  27. package/dist/src/code_assist/codeAssist.js +1 -1
  28. package/dist/src/code_assist/codeAssist.test.js +3 -3
  29. package/dist/src/code_assist/oauth-credential-storage.test.js +1 -0
  30. package/dist/src/code_assist/oauth-credential-storage.test.js.map +1 -1
  31. package/dist/src/code_assist/oauth2.js +106 -53
  32. package/dist/src/code_assist/oauth2.js.map +1 -1
  33. package/dist/src/code_assist/oauth2.test.js +25 -12
  34. package/dist/src/code_assist/oauth2.test.js.map +1 -1
  35. package/dist/src/config/config.d.ts +18 -5
  36. package/dist/src/config/config.js +51 -19
  37. package/dist/src/config/config.js.map +1 -1
  38. package/dist/src/config/config.test.js +14 -79
  39. package/dist/src/config/config.test.js.map +1 -1
  40. package/dist/src/config/defaultModelConfigs.js +56 -4
  41. package/dist/src/config/defaultModelConfigs.js.map +1 -1
  42. package/dist/src/confirmation-bus/message-bus.d.ts +6 -0
  43. package/dist/src/confirmation-bus/message-bus.js +63 -2
  44. package/dist/src/confirmation-bus/message-bus.js.map +1 -1
  45. package/dist/src/confirmation-bus/types.d.ts +25 -2
  46. package/dist/src/confirmation-bus/types.js +3 -0
  47. package/dist/src/confirmation-bus/types.js.map +1 -1
  48. package/dist/src/core/AuthenticatedContentGenerator.d.ts +20 -0
  49. package/dist/src/core/AuthenticatedContentGenerator.js +115 -0
  50. package/dist/src/core/AuthenticatedContentGenerator.js.map +1 -0
  51. package/dist/src/core/baseLlmClient.d.ts +27 -1
  52. package/dist/src/core/baseLlmClient.js +66 -45
  53. package/dist/src/core/baseLlmClient.js.map +1 -1
  54. package/dist/src/core/baseLlmClient.test.js +94 -6
  55. package/dist/src/core/baseLlmClient.test.js.map +1 -1
  56. package/dist/src/core/client.d.ts +1 -2
  57. package/dist/src/core/client.js +89 -29
  58. package/dist/src/core/client.js.map +1 -1
  59. package/dist/src/core/client.test.js +82 -41
  60. package/dist/src/core/client.test.js.map +1 -1
  61. package/dist/src/core/clientHookTriggers.d.ts +36 -0
  62. package/dist/src/core/clientHookTriggers.js +76 -0
  63. package/dist/src/core/clientHookTriggers.js.map +1 -0
  64. package/dist/src/core/contentGenerator.d.ts +2 -1
  65. package/dist/src/core/contentGenerator.js +12 -5
  66. package/dist/src/core/contentGenerator.js.map +1 -1
  67. package/dist/src/core/contentGenerator.test.js +119 -1
  68. package/dist/src/core/contentGenerator.test.js.map +1 -1
  69. package/dist/src/core/coreToolScheduler.d.ts +0 -2
  70. package/dist/src/core/coreToolScheduler.js +1 -3
  71. package/dist/src/core/coreToolScheduler.js.map +1 -1
  72. package/dist/src/core/coreToolScheduler.test.js +3 -18
  73. package/dist/src/core/coreToolScheduler.test.js.map +1 -1
  74. package/dist/src/core/geminiChat.d.ts +10 -5
  75. package/dist/src/core/geminiChat.js +57 -22
  76. package/dist/src/core/geminiChat.js.map +1 -1
  77. package/dist/src/core/geminiChat.test.js +188 -55
  78. package/dist/src/core/geminiChat.test.js.map +1 -1
  79. package/dist/src/core/logger.test.js +12 -11
  80. package/dist/src/core/logger.test.js.map +1 -1
  81. package/dist/src/core/nonInteractiveToolExecutor.js +6 -2
  82. package/dist/src/core/nonInteractiveToolExecutor.js.map +1 -1
  83. package/dist/src/core/nonInteractiveToolExecutor.test.js +10 -2
  84. package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
  85. package/dist/src/core/prompts.js +21 -6
  86. package/dist/src/core/prompts.js.map +1 -1
  87. package/dist/src/core/prompts.test.js +79 -93
  88. package/dist/src/core/prompts.test.js.map +1 -1
  89. package/dist/src/core/turn.d.ts +14 -3
  90. package/dist/src/core/turn.js +13 -7
  91. package/dist/src/core/turn.js.map +1 -1
  92. package/dist/src/core/turn.test.js +73 -139
  93. package/dist/src/core/turn.test.js.map +1 -1
  94. package/dist/src/fallback/handler.js +10 -5
  95. package/dist/src/fallback/handler.js.map +1 -1
  96. package/dist/src/fallback/handler.test.js +79 -17
  97. package/dist/src/fallback/handler.test.js.map +1 -1
  98. package/dist/src/generated/git-commit.d.ts +2 -2
  99. package/dist/src/generated/git-commit.js +2 -2
  100. package/dist/src/hooks/hookEventHandler.d.ts +109 -0
  101. package/dist/src/hooks/hookEventHandler.js +486 -0
  102. package/dist/src/hooks/hookEventHandler.js.map +1 -0
  103. package/dist/src/hooks/hookEventHandler.test.d.ts +6 -0
  104. package/dist/src/hooks/hookEventHandler.test.js +384 -0
  105. package/dist/src/hooks/hookEventHandler.test.js.map +1 -0
  106. package/dist/src/hooks/hookSystem.d.ts +48 -0
  107. package/dist/src/hooks/hookSystem.js +83 -0
  108. package/dist/src/hooks/hookSystem.js.map +1 -0
  109. package/dist/src/hooks/hookSystem.test.d.ts +6 -0
  110. package/dist/src/hooks/hookSystem.test.js +213 -0
  111. package/dist/src/hooks/hookSystem.test.js.map +1 -0
  112. package/dist/src/hooks/index.d.ts +15 -0
  113. package/dist/src/hooks/index.js +15 -0
  114. package/dist/src/hooks/index.js.map +1 -0
  115. package/dist/src/index.d.ts +4 -0
  116. package/dist/src/index.js +6 -0
  117. package/dist/src/index.js.map +1 -1
  118. package/dist/src/mcp/oauth-provider.test.js +1 -0
  119. package/dist/src/mcp/oauth-provider.test.js.map +1 -1
  120. package/dist/src/policy/policy-engine.d.ts +17 -1
  121. package/dist/src/policy/policy-engine.js +90 -1
  122. package/dist/src/policy/policy-engine.js.map +1 -1
  123. package/dist/src/policy/policy-engine.test.js +222 -0
  124. package/dist/src/policy/policy-engine.test.js.map +1 -1
  125. package/dist/src/policy/types.d.ts +51 -1
  126. package/dist/src/policy/types.js +21 -0
  127. package/dist/src/policy/types.js.map +1 -1
  128. package/dist/src/prompts/mcp-prompts.test.js +0 -1
  129. package/dist/src/prompts/mcp-prompts.test.js.map +1 -1
  130. package/dist/src/prompts/prompt-registry.test.js +0 -15
  131. package/dist/src/prompts/prompt-registry.test.js.map +1 -1
  132. package/dist/src/routing/modelRouterService.js +1 -1
  133. package/dist/src/routing/modelRouterService.js.map +1 -1
  134. package/dist/src/routing/strategies/classifierStrategy.test.js +4 -3
  135. package/dist/src/routing/strategies/classifierStrategy.test.js.map +1 -1
  136. package/dist/src/services/chatCompressionService.d.ts +2 -1
  137. package/dist/src/services/chatCompressionService.js +23 -7
  138. package/dist/src/services/chatCompressionService.js.map +1 -1
  139. package/dist/src/services/chatCompressionService.test.js +27 -32
  140. package/dist/src/services/chatCompressionService.test.js.map +1 -1
  141. package/dist/src/services/chatRecordingService.d.ts +1 -1
  142. package/dist/src/services/gitService.js +3 -1
  143. package/dist/src/services/gitService.js.map +1 -1
  144. package/dist/src/services/gitService.test.js +10 -0
  145. package/dist/src/services/gitService.test.js.map +1 -1
  146. package/dist/src/services/modelConfig.integration.test.js +34 -0
  147. package/dist/src/services/modelConfig.integration.test.js.map +1 -1
  148. package/dist/src/services/modelConfigService.d.ts +3 -0
  149. package/dist/src/services/modelConfigService.js +12 -3
  150. package/dist/src/services/modelConfigService.js.map +1 -1
  151. package/dist/src/services/modelConfigService.test.js +133 -0
  152. package/dist/src/services/modelConfigService.test.js.map +1 -1
  153. package/dist/src/services/shellExecutionService.d.ts +2 -0
  154. package/dist/src/services/shellExecutionService.js +35 -6
  155. package/dist/src/services/shellExecutionService.js.map +1 -1
  156. package/dist/src/services/shellExecutionService.test.js +78 -1
  157. package/dist/src/services/shellExecutionService.test.js.map +1 -1
  158. package/dist/src/services/test-data/resolved-aliases.golden.json +57 -4
  159. package/dist/src/telemetry/activity-monitor.test.js +4 -1
  160. package/dist/src/telemetry/activity-monitor.test.js.map +1 -1
  161. package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +2 -1
  162. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +97 -51
  163. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
  164. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +24 -16
  165. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
  166. package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +2 -0
  167. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +5 -1
  168. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
  169. package/dist/src/telemetry/loggers.d.ts +2 -1
  170. package/dist/src/telemetry/loggers.js +14 -3
  171. package/dist/src/telemetry/loggers.js.map +1 -1
  172. package/dist/src/telemetry/loggers.test.js +2 -5
  173. package/dist/src/telemetry/loggers.test.js.map +1 -1
  174. package/dist/src/telemetry/metrics.d.ts +26 -8
  175. package/dist/src/telemetry/metrics.js +37 -11
  176. package/dist/src/telemetry/metrics.js.map +1 -1
  177. package/dist/src/telemetry/metrics.test.js +61 -39
  178. package/dist/src/telemetry/metrics.test.js.map +1 -1
  179. package/dist/src/telemetry/sanitize.d.ts +25 -0
  180. package/dist/src/telemetry/sanitize.js +48 -0
  181. package/dist/src/telemetry/sanitize.js.map +1 -0
  182. package/dist/src/telemetry/sanitize.test.d.ts +6 -0
  183. package/dist/src/telemetry/sanitize.test.js +279 -0
  184. package/dist/src/telemetry/sanitize.test.js.map +1 -0
  185. package/dist/src/telemetry/semantic.js +1 -1
  186. package/dist/src/telemetry/semantic.js.map +1 -1
  187. package/dist/src/telemetry/types.d.ts +19 -0
  188. package/dist/src/telemetry/types.js +65 -0
  189. package/dist/src/telemetry/types.js.map +1 -1
  190. package/dist/src/test-utils/config.d.ts +1 -1
  191. package/dist/src/test-utils/config.js +1 -1
  192. package/dist/src/test-utils/mock-message-bus.d.ts +60 -0
  193. package/dist/src/test-utils/mock-message-bus.js +131 -0
  194. package/dist/src/test-utils/mock-message-bus.js.map +1 -0
  195. package/dist/src/test-utils/mock-tool.js +1 -1
  196. package/dist/src/test-utils/mock-tool.js.map +1 -1
  197. package/dist/src/tools/edit.test.js +203 -200
  198. package/dist/src/tools/edit.test.js.map +1 -1
  199. package/dist/src/tools/mcp-client.d.ts +4 -2
  200. package/dist/src/tools/mcp-client.js +226 -114
  201. package/dist/src/tools/mcp-client.js.map +1 -1
  202. package/dist/src/tools/mcp-client.test.js +128 -54
  203. package/dist/src/tools/mcp-client.test.js.map +1 -1
  204. package/dist/src/tools/mcp-tool.test.js +186 -273
  205. package/dist/src/tools/mcp-tool.test.js.map +1 -1
  206. package/dist/src/tools/modifiable-tool.d.ts +1 -1
  207. package/dist/src/tools/modifiable-tool.js +2 -2
  208. package/dist/src/tools/modifiable-tool.js.map +1 -1
  209. package/dist/src/tools/modifiable-tool.test.js +12 -11
  210. package/dist/src/tools/modifiable-tool.test.js.map +1 -1
  211. package/dist/src/tools/ripGrep.test.js +84 -126
  212. package/dist/src/tools/ripGrep.test.js.map +1 -1
  213. package/dist/src/tools/smart-edit.test.js +93 -112
  214. package/dist/src/tools/smart-edit.test.js.map +1 -1
  215. package/dist/src/tools/tool-registry.test.js +59 -90
  216. package/dist/src/tools/tool-registry.test.js.map +1 -1
  217. package/dist/src/tools/web-fetch.test.js +121 -179
  218. package/dist/src/tools/web-fetch.test.js.map +1 -1
  219. package/dist/src/tools/write-file.test.js +105 -116
  220. package/dist/src/tools/write-file.test.js.map +1 -1
  221. package/dist/src/tools/write-todos.d.ts +1 -1
  222. package/dist/src/tools/write-todos.js +1 -1
  223. package/dist/src/utils/customHeaderUtils.d.ts +9 -0
  224. package/dist/src/utils/customHeaderUtils.js +34 -0
  225. package/dist/src/utils/customHeaderUtils.js.map +1 -0
  226. package/dist/src/utils/customHeaderUtils.test.d.ts +6 -0
  227. package/dist/src/utils/customHeaderUtils.test.js +77 -0
  228. package/dist/src/utils/customHeaderUtils.test.js.map +1 -0
  229. package/dist/src/utils/debugLogger.test.js +27 -25
  230. package/dist/src/utils/debugLogger.test.js.map +1 -1
  231. package/dist/src/utils/editor.d.ts +1 -1
  232. package/dist/src/utils/editor.js +3 -2
  233. package/dist/src/utils/editor.js.map +1 -1
  234. package/dist/src/utils/editor.test.js +7 -46
  235. package/dist/src/utils/editor.test.js.map +1 -1
  236. package/dist/src/utils/events.d.ts +33 -3
  237. package/dist/src/utils/events.js +35 -15
  238. package/dist/src/utils/events.js.map +1 -1
  239. package/dist/src/utils/events.test.js +86 -5
  240. package/dist/src/utils/events.test.js.map +1 -1
  241. package/dist/src/utils/filesearch/fileSearch.js +1 -1
  242. package/dist/src/utils/filesearch/fileSearch.js.map +1 -1
  243. package/dist/src/utils/installationManager.test.js +2 -1
  244. package/dist/src/utils/installationManager.test.js.map +1 -1
  245. package/dist/src/utils/llm-edit-fixer.test.js +2 -1
  246. package/dist/src/utils/llm-edit-fixer.test.js.map +1 -1
  247. package/dist/src/utils/memoryDiscovery.test.js +3 -2
  248. package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
  249. package/dist/src/utils/memoryImportProcessor.js +1 -1
  250. package/dist/src/utils/memoryImportProcessor.js.map +1 -1
  251. package/dist/src/utils/memoryImportProcessor.test.js +8 -14
  252. package/dist/src/utils/memoryImportProcessor.test.js.map +1 -1
  253. package/dist/src/utils/nextSpeakerChecker.test.js +3 -1
  254. package/dist/src/utils/nextSpeakerChecker.test.js.map +1 -1
  255. package/dist/src/utils/package.d.ts +14 -0
  256. package/dist/src/utils/package.js +15 -2
  257. package/dist/src/utils/package.js.map +1 -1
  258. package/dist/src/utils/schemaValidator.d.ts +1 -1
  259. package/dist/src/utils/schemaValidator.js +1 -1
  260. package/dist/src/utils/shell-utils.js +1 -1
  261. package/dist/src/utils/stdio.d.ts +32 -0
  262. package/dist/src/utils/stdio.js +85 -0
  263. package/dist/src/utils/stdio.js.map +1 -0
  264. package/dist/src/utils/stdio.test.d.ts +6 -0
  265. package/dist/src/utils/stdio.test.js +47 -0
  266. package/dist/src/utils/stdio.test.js.map +1 -0
  267. package/dist/src/utils/systemEncoding.test.js +2 -1
  268. package/dist/src/utils/systemEncoding.test.js.map +1 -1
  269. package/dist/src/utils/terminal.d.ts +14 -0
  270. package/dist/src/utils/terminal.js +38 -0
  271. package/dist/src/utils/terminal.js.map +1 -0
  272. package/dist/src/utils/userAccountManager.test.js +7 -6
  273. package/dist/src/utils/userAccountManager.test.js.map +1 -1
  274. package/dist/src/utils/workspaceContext.test.js +5 -4
  275. package/dist/src/utils/workspaceContext.test.js.map +1 -1
  276. package/dist/tsconfig.tsbuildinfo +1 -1
  277. package/package.json +6 -4
@@ -4,10 +4,10 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
7
- import { ApiError } from '@google/genai';
7
+ import { ApiError, ThinkingLevel } from '@google/genai';
8
8
  import { GeminiChat, InvalidStreamError, StreamEventType, SYNTHETIC_THOUGHT_SIGNATURE, } from './geminiChat.js';
9
9
  import { setSimulate429 } from '../utils/testUtils.js';
10
- import { DEFAULT_GEMINI_FLASH_MODEL, DEFAULT_GEMINI_MODEL, PREVIEW_GEMINI_MODEL, } from '../config/models.js';
10
+ import { DEFAULT_GEMINI_FLASH_MODEL, DEFAULT_GEMINI_MODEL, DEFAULT_THINKING_MODE, PREVIEW_GEMINI_MODEL, } from '../config/models.js';
11
11
  import { AuthType } from './contentGenerator.js';
12
12
  import { TerminalQuotaError } from '../utils/googleQuotaErrors.js';
13
13
  import { retryWithBackoff } from '../utils/retry.js';
@@ -65,7 +65,6 @@ describe('GeminiChat', () => {
65
65
  let mockContentGenerator;
66
66
  let chat;
67
67
  let mockConfig;
68
- const config = {};
69
68
  beforeEach(() => {
70
69
  vi.clearAllMocks();
71
70
  vi.mocked(uiTelemetryService.setLastPromptTokenCount).mockClear();
@@ -104,6 +103,24 @@ describe('GeminiChat', () => {
104
103
  }),
105
104
  getContentGenerator: vi.fn().mockReturnValue(mockContentGenerator),
106
105
  getRetryFetchErrors: vi.fn().mockReturnValue(false),
106
+ modelConfigService: {
107
+ getResolvedConfig: vi.fn().mockImplementation((modelConfigKey) => {
108
+ const thinkingConfig = modelConfigKey.model.startsWith('gemini-3')
109
+ ? {
110
+ thinkingLevel: ThinkingLevel.HIGH,
111
+ }
112
+ : {
113
+ thinkingBudget: DEFAULT_THINKING_MODE,
114
+ };
115
+ return {
116
+ model: modelConfigKey.model,
117
+ generateContentConfig: {
118
+ temperature: 0,
119
+ thinkingConfig,
120
+ },
121
+ };
122
+ }),
123
+ },
107
124
  isPreviewModelBypassMode: vi.fn().mockReturnValue(false),
108
125
  setPreviewModelBypassMode: vi.fn(),
109
126
  isPreviewModelFallbackMode: vi.fn().mockReturnValue(false),
@@ -113,7 +130,7 @@ describe('GeminiChat', () => {
113
130
  // Disable 429 simulation for tests
114
131
  setSimulate429(false);
115
132
  // Reset history for each test by creating a new instance
116
- chat = new GeminiChat(mockConfig, config, []);
133
+ chat = new GeminiChat(mockConfig);
117
134
  });
118
135
  afterEach(() => {
119
136
  vi.restoreAllMocks();
@@ -125,12 +142,12 @@ describe('GeminiChat', () => {
125
142
  { role: 'user', parts: [{ text: 'Hello' }] },
126
143
  { role: 'model', parts: [{ text: 'Hi there' }] },
127
144
  ];
128
- const chatWithHistory = new GeminiChat(mockConfig, config, history);
145
+ const chatWithHistory = new GeminiChat(mockConfig, '', [], history);
129
146
  const estimatedTokens = Math.ceil(JSON.stringify(history).length / 4);
130
147
  expect(chatWithHistory.getLastPromptTokenCount()).toBe(estimatedTokens);
131
148
  });
132
149
  it('should initialize lastPromptTokenCount for empty history', () => {
133
- const chatEmpty = new GeminiChat(mockConfig, config, []);
150
+ const chatEmpty = new GeminiChat(mockConfig);
134
151
  expect(chatEmpty.getLastPromptTokenCount()).toBe(Math.ceil(JSON.stringify([]).length / 4));
135
152
  });
136
153
  });
@@ -163,7 +180,7 @@ describe('GeminiChat', () => {
163
180
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(streamWithToolCall);
164
181
  // 2. Action & Assert: The stream processing should complete without throwing an error
165
182
  // because the presence of a tool call makes the empty final chunk acceptable.
166
- const stream = await chat.sendMessageStream('test-model', { message: 'test message' }, 'prompt-id-tool-call-empty-end');
183
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-tool-call-empty-end', new AbortController().signal);
167
184
  await expect((async () => {
168
185
  for await (const _ of stream) {
169
186
  /* consume stream */
@@ -203,7 +220,7 @@ describe('GeminiChat', () => {
203
220
  })();
204
221
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(streamWithNoFinish);
205
222
  // 2. Action & Assert: The stream should fail because there's no finish reason.
206
- const stream = await chat.sendMessageStream('gemini-2.0-flash', { message: 'test message' }, 'prompt-id-no-finish-empty-end');
223
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test message', 'prompt-id-no-finish-empty-end', new AbortController().signal);
207
224
  await expect((async () => {
208
225
  for await (const _ of stream) {
209
226
  /* consume stream */
@@ -238,7 +255,7 @@ describe('GeminiChat', () => {
238
255
  })();
239
256
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(streamWithInvalidEnd);
240
257
  // 2. Action & Assert: The stream should complete without throwing an error.
241
- const stream = await chat.sendMessageStream('test-model', { message: 'test message' }, 'prompt-id-valid-then-invalid-end');
258
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-valid-then-invalid-end', new AbortController().signal);
242
259
  await expect((async () => {
243
260
  for await (const _ of stream) {
244
261
  /* consume stream */
@@ -273,7 +290,7 @@ describe('GeminiChat', () => {
273
290
  })();
274
291
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(multiChunkStream);
275
292
  // 2. Action: Send a message and consume the stream.
276
- const stream = await chat.sendMessageStream('test-model', { message: 'test message' }, 'prompt-id-empty-chunk-consolidation');
293
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-empty-chunk-consolidation', new AbortController().signal);
277
294
  for await (const _ of stream) {
278
295
  // Consume the stream
279
296
  }
@@ -321,7 +338,7 @@ describe('GeminiChat', () => {
321
338
  })();
322
339
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(multiChunkStream);
323
340
  // 2. Action: Send a message and consume the stream.
324
- const stream = await chat.sendMessageStream('test-model', { message: 'test message' }, 'prompt-id-multi-chunk');
341
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-multi-chunk', new AbortController().signal);
325
342
  for await (const _ of stream) {
326
343
  // Consume the stream to trigger history recording.
327
344
  }
@@ -357,7 +374,7 @@ describe('GeminiChat', () => {
357
374
  })();
358
375
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(mixedContentStream);
359
376
  // 2. Action: Send a message and fully consume the stream to trigger history recording.
360
- const stream = await chat.sendMessageStream('test-model', { message: 'test message' }, 'prompt-id-mixed-chunk');
377
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-mixed-chunk', new AbortController().signal);
361
378
  for await (const _ of stream) {
362
379
  // This loop consumes the stream.
363
380
  }
@@ -385,7 +402,7 @@ describe('GeminiChat', () => {
385
402
  ],
386
403
  };
387
404
  })());
388
- const stream = await chat.sendMessageStream(PREVIEW_GEMINI_MODEL, { message: 'test' }, 'prompt-id-fast-retry');
405
+ const stream = await chat.sendMessageStream({ model: PREVIEW_GEMINI_MODEL }, 'test', 'prompt-id-fast-retry', new AbortController().signal);
389
406
  for await (const _ of stream) {
390
407
  // consume stream
391
408
  }
@@ -405,7 +422,7 @@ describe('GeminiChat', () => {
405
422
  ],
406
423
  };
407
424
  })());
408
- const stream = await chat.sendMessageStream(DEFAULT_GEMINI_FLASH_MODEL, { message: 'test' }, 'prompt-id-normal-retry');
425
+ const stream = await chat.sendMessageStream({ model: DEFAULT_GEMINI_FLASH_MODEL }, 'test', 'prompt-id-normal-retry', new AbortController().signal);
409
426
  for await (const _ of stream) {
410
427
  // consume stream
411
428
  }
@@ -437,7 +454,7 @@ describe('GeminiChat', () => {
437
454
  }));
438
455
  // ACT
439
456
  const consumeStream = async () => {
440
- const stream = await chat.sendMessageStream(PREVIEW_GEMINI_MODEL, { message: 'test' }, 'prompt-id-bypass');
457
+ const stream = await chat.sendMessageStream({ model: PREVIEW_GEMINI_MODEL }, 'test', 'prompt-id-bypass', new AbortController().signal);
441
458
  // Consume the stream to trigger execution
442
459
  for await (const _ of stream) {
443
460
  // do nothing
@@ -484,14 +501,12 @@ describe('GeminiChat', () => {
484
501
  })();
485
502
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(emptyStreamResponse);
486
503
  // 3. Action: Send the function response back to the model and consume the stream.
487
- const stream = await chat.sendMessageStream('gemini-2.0-flash', {
488
- message: {
489
- functionResponse: {
490
- name: 'find_restaurant',
491
- response: { name: 'Vesuvio' },
492
- },
504
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, {
505
+ functionResponse: {
506
+ name: 'find_restaurant',
507
+ response: { name: 'Vesuvio' },
493
508
  },
494
- }, 'prompt-id-stream-1');
509
+ }, 'prompt-id-stream-1', new AbortController().signal);
495
510
  // 4. Assert: The stream processing should throw an InvalidStreamError.
496
511
  await expect((async () => {
497
512
  for await (const _ of stream) {
@@ -522,7 +537,7 @@ describe('GeminiChat', () => {
522
537
  };
523
538
  })();
524
539
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(streamWithToolCall);
525
- const stream = await chat.sendMessageStream('test-model', { message: 'test' }, 'prompt-id-1');
540
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-1', new AbortController().signal);
526
541
  // Should not throw an error
527
542
  await expect((async () => {
528
543
  for await (const _ of stream) {
@@ -546,7 +561,7 @@ describe('GeminiChat', () => {
546
561
  };
547
562
  })();
548
563
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(streamWithoutFinishReason);
549
- const stream = await chat.sendMessageStream('gemini-2.0-flash', { message: 'test' }, 'prompt-id-1');
564
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test message', 'prompt-id-1', new AbortController().signal);
550
565
  await expect((async () => {
551
566
  for await (const _ of stream) {
552
567
  // consume stream
@@ -569,7 +584,7 @@ describe('GeminiChat', () => {
569
584
  };
570
585
  })();
571
586
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(streamWithEmptyResponse);
572
- const stream = await chat.sendMessageStream('gemini-2.0-flash', { message: 'test' }, 'prompt-id-1');
587
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test message', 'prompt-id-1', new AbortController().signal);
573
588
  await expect((async () => {
574
589
  for await (const _ of stream) {
575
590
  // consume stream
@@ -592,7 +607,7 @@ describe('GeminiChat', () => {
592
607
  };
593
608
  })();
594
609
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(validStream);
595
- const stream = await chat.sendMessageStream('test-model', { message: 'test' }, 'prompt-id-1');
610
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-1', new AbortController().signal);
596
611
  // Should not throw an error
597
612
  await expect((async () => {
598
613
  for await (const _ of stream) {
@@ -616,7 +631,7 @@ describe('GeminiChat', () => {
616
631
  };
617
632
  })();
618
633
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(streamWithMalformedFunctionCall);
619
- const stream = await chat.sendMessageStream('gemini-2.5-pro', { message: 'test' }, 'prompt-id-malformed');
634
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.5-pro' }, 'test', 'prompt-id-malformed', new AbortController().signal);
620
635
  // Should throw an error
621
636
  await expect((async () => {
622
637
  for await (const _ of stream) {
@@ -650,7 +665,7 @@ describe('GeminiChat', () => {
650
665
  };
651
666
  })());
652
667
  // 2. Send a message
653
- const stream = await chat.sendMessageStream('gemini-2.5-pro', { message: 'test retry' }, 'prompt-id-retry-malformed');
668
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.5-pro' }, 'test retry', 'prompt-id-retry-malformed', new AbortController().signal);
654
669
  const events = [];
655
670
  for await (const event of stream) {
656
671
  events.push(event);
@@ -688,7 +703,7 @@ describe('GeminiChat', () => {
688
703
  };
689
704
  })();
690
705
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(response);
691
- const stream = await chat.sendMessageStream('test-model', { message: 'hello' }, 'prompt-id-1');
706
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'hello', 'prompt-id-1', new AbortController().signal);
692
707
  for await (const _ of stream) {
693
708
  // consume stream
694
709
  }
@@ -700,9 +715,69 @@ describe('GeminiChat', () => {
700
715
  parts: [{ text: 'hello' }],
701
716
  },
702
717
  ],
703
- config: {},
718
+ config: {
719
+ systemInstruction: '',
720
+ tools: [],
721
+ temperature: 0,
722
+ thinkingConfig: {
723
+ thinkingBudget: DEFAULT_THINKING_MODE,
724
+ },
725
+ abortSignal: expect.any(AbortSignal),
726
+ },
704
727
  }, 'prompt-id-1');
705
728
  });
729
+ it('should use thinkingLevel and remove thinkingBudget for gemini-3 models', async () => {
730
+ const response = (async function* () {
731
+ yield {
732
+ candidates: [
733
+ {
734
+ content: { parts: [{ text: 'response' }], role: 'model' },
735
+ finishReason: 'STOP',
736
+ },
737
+ ],
738
+ };
739
+ })();
740
+ vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(response);
741
+ const stream = await chat.sendMessageStream({ model: 'gemini-3-test-only-model-string-for-testing' }, 'hello', 'prompt-id-thinking-level', new AbortController().signal);
742
+ for await (const _ of stream) {
743
+ // consume stream
744
+ }
745
+ expect(mockContentGenerator.generateContentStream).toHaveBeenCalledWith(expect.objectContaining({
746
+ model: 'gemini-3-test-only-model-string-for-testing',
747
+ config: expect.objectContaining({
748
+ thinkingConfig: {
749
+ thinkingBudget: undefined,
750
+ thinkingLevel: ThinkingLevel.HIGH,
751
+ },
752
+ }),
753
+ }), 'prompt-id-thinking-level');
754
+ });
755
+ it('should use thinkingBudget and remove thinkingLevel for non-gemini-3 models', async () => {
756
+ const response = (async function* () {
757
+ yield {
758
+ candidates: [
759
+ {
760
+ content: { parts: [{ text: 'response' }], role: 'model' },
761
+ finishReason: 'STOP',
762
+ },
763
+ ],
764
+ };
765
+ })();
766
+ vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(response);
767
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'hello', 'prompt-id-thinking-budget', new AbortController().signal);
768
+ for await (const _ of stream) {
769
+ // consume stream
770
+ }
771
+ expect(mockContentGenerator.generateContentStream).toHaveBeenCalledWith(expect.objectContaining({
772
+ model: 'gemini-2.0-flash',
773
+ config: expect.objectContaining({
774
+ thinkingConfig: {
775
+ thinkingBudget: DEFAULT_THINKING_MODE,
776
+ thinkingLevel: undefined,
777
+ },
778
+ }),
779
+ }), 'prompt-id-thinking-budget');
780
+ });
706
781
  });
707
782
  describe('addHistory', () => {
708
783
  it('should add a new content item to the history', () => {
@@ -740,7 +815,7 @@ describe('GeminiChat', () => {
740
815
  candidates: [{ content: { parts: [{ text: '' }] } }],
741
816
  };
742
817
  })());
743
- const stream = await chat.sendMessageStream('gemini-1.5-pro', { message: 'test' }, 'prompt-id-no-retry');
818
+ const stream = await chat.sendMessageStream({ model: 'gemini-1.5-pro' }, 'test', 'prompt-id-no-retry', new AbortController().signal);
744
819
  await expect((async () => {
745
820
  for await (const _ of stream) {
746
821
  // Must loop to trigger the internal logic that throws.
@@ -773,7 +848,7 @@ describe('GeminiChat', () => {
773
848
  };
774
849
  })());
775
850
  // ACT: Send a message and collect all events from the stream.
776
- const stream = await chat.sendMessageStream('gemini-2.0-flash', { message: 'test' }, 'prompt-id-yield-retry');
851
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test message', 'prompt-id-yield-retry', new AbortController().signal);
777
852
  const events = [];
778
853
  for await (const event of stream) {
779
854
  events.push(event);
@@ -805,7 +880,7 @@ describe('GeminiChat', () => {
805
880
  ],
806
881
  };
807
882
  })());
808
- const stream = await chat.sendMessageStream('gemini-2.0-flash', { message: 'test' }, 'prompt-id-retry-success');
883
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test', 'prompt-id-retry-success', new AbortController().signal);
809
884
  const chunks = [];
810
885
  for await (const chunk of stream) {
811
886
  chunks.push(chunk);
@@ -856,7 +931,7 @@ describe('GeminiChat', () => {
856
931
  ],
857
932
  };
858
933
  })());
859
- const stream = await chat.sendMessageStream('gemini-2.0-flash', { message: 'test', config: { temperature: 0.5 } }, 'prompt-id-retry-temperature');
934
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test message', 'prompt-id-retry-temperature', new AbortController().signal);
860
935
  for await (const _ of stream) {
861
936
  // consume stream
862
937
  }
@@ -864,7 +939,7 @@ describe('GeminiChat', () => {
864
939
  // First call should have original temperature
865
940
  expect(mockContentGenerator.generateContentStream).toHaveBeenNthCalledWith(1, expect.objectContaining({
866
941
  config: expect.objectContaining({
867
- temperature: 0.5,
942
+ temperature: 0,
868
943
  }),
869
944
  }), 'prompt-id-retry-temperature');
870
945
  // Second call (retry) should have temperature 1
@@ -887,7 +962,7 @@ describe('GeminiChat', () => {
887
962
  ],
888
963
  };
889
964
  })());
890
- const stream = await chat.sendMessageStream('gemini-2.0-flash', { message: 'test' }, 'prompt-id-retry-fail');
965
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test', 'prompt-id-retry-fail', new AbortController().signal);
891
966
  await expect(async () => {
892
967
  for await (const _ of stream) {
893
968
  // Must loop to trigger the internal logic that throws.
@@ -936,7 +1011,7 @@ describe('GeminiChat', () => {
936
1011
  it('should not retry on 400 Bad Request errors', async () => {
937
1012
  const error400 = new ApiError({ message: 'Bad Request', status: 400 });
938
1013
  vi.mocked(mockContentGenerator.generateContentStream).mockRejectedValue(error400);
939
- const stream = await chat.sendMessageStream('gemini-2.0-flash', { message: 'test' }, 'prompt-id-400');
1014
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test message', 'prompt-id-400', new AbortController().signal);
940
1015
  await expect((async () => {
941
1016
  for await (const _ of stream) {
942
1017
  /* consume stream */
@@ -959,7 +1034,7 @@ describe('GeminiChat', () => {
959
1034
  ],
960
1035
  };
961
1036
  })());
962
- const stream = await chat.sendMessageStream('test-model', { message: 'test' }, 'prompt-id-429-retry');
1037
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-429-retry', new AbortController().signal);
963
1038
  const events = [];
964
1039
  for await (const event of stream) {
965
1040
  events.push(event);
@@ -988,7 +1063,7 @@ describe('GeminiChat', () => {
988
1063
  ],
989
1064
  };
990
1065
  })());
991
- const stream = await chat.sendMessageStream('test-model', { message: 'test' }, 'prompt-id-500-retry');
1066
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-500-retry', new AbortController().signal);
992
1067
  const events = [];
993
1068
  for await (const event of stream) {
994
1069
  events.push(event);
@@ -1024,7 +1099,7 @@ describe('GeminiChat', () => {
1024
1099
  throw error;
1025
1100
  }
1026
1101
  });
1027
- const stream = await chat.sendMessageStream('test-model', { message: 'test' }, 'prompt-id-fetch-error-retry');
1102
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-fetch-error-retry', new AbortController().signal);
1028
1103
  const events = [];
1029
1104
  for await (const event of stream) {
1030
1105
  events.push(event);
@@ -1067,7 +1142,7 @@ describe('GeminiChat', () => {
1067
1142
  };
1068
1143
  })());
1069
1144
  // 3. Send a new message
1070
- const stream = await chat.sendMessageStream('gemini-2.0-flash', { message: 'Second question' }, 'prompt-id-retry-existing');
1145
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'Second question', 'prompt-id-retry-existing', new AbortController().signal);
1071
1146
  for await (const _ of stream) {
1072
1147
  // consume stream
1073
1148
  }
@@ -1119,7 +1194,7 @@ describe('GeminiChat', () => {
1119
1194
  };
1120
1195
  })());
1121
1196
  // 2. Call the method and consume the stream.
1122
- const stream = await chat.sendMessageStream('gemini-2.0-flash', { message: 'test empty stream' }, 'prompt-id-empty-stream');
1197
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test empty stream', 'prompt-id-empty-stream', new AbortController().signal);
1123
1198
  const chunks = [];
1124
1199
  for await (const chunk of stream) {
1125
1200
  chunks.push(chunk);
@@ -1180,11 +1255,11 @@ describe('GeminiChat', () => {
1180
1255
  .mockResolvedValueOnce(firstStreamGenerator)
1181
1256
  .mockResolvedValueOnce(secondStreamGenerator);
1182
1257
  // 3. Start the first stream and consume only the first chunk to pause it
1183
- const firstStream = await chat.sendMessageStream('test-model', { message: 'first' }, 'prompt-1');
1258
+ const firstStream = await chat.sendMessageStream({ model: 'test-model' }, 'first', 'prompt-1', new AbortController().signal);
1184
1259
  const firstStreamIterator = firstStream[Symbol.asyncIterator]();
1185
1260
  await firstStreamIterator.next();
1186
1261
  // 4. While the first stream is paused, start the second call. It will block.
1187
- const secondStreamPromise = chat.sendMessageStream('test-model', { message: 'second' }, 'prompt-2');
1262
+ const secondStreamPromise = chat.sendMessageStream({ model: 'test-model' }, 'second', 'prompt-2', new AbortController().signal);
1188
1263
  // 5. Assert that only one API call has been made so far.
1189
1264
  expect(mockContentGenerator.generateContentStream).toHaveBeenCalledTimes(1);
1190
1265
  // 6. Unblock and fully consume the first stream to completion.
@@ -1224,7 +1299,7 @@ describe('GeminiChat', () => {
1224
1299
  vi.mocked(mockContentGenerator.generateContentStream).mockImplementation(async () => (async function* () {
1225
1300
  yield mockResponse;
1226
1301
  })());
1227
- const stream = await chat.sendMessageStream('test-model', { message: 'test' }, 'prompt-id-res3');
1302
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-res3', new AbortController().signal);
1228
1303
  for await (const _ of stream) {
1229
1304
  // consume stream
1230
1305
  }
@@ -1287,7 +1362,7 @@ describe('GeminiChat', () => {
1287
1362
  isInFallbackModeSpy.mockReturnValue(true);
1288
1363
  return true; // Signal retry
1289
1364
  });
1290
- const stream = await chat.sendMessageStream('test-model', { message: 'trigger 429' }, 'prompt-id-fb1');
1365
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'trigger 429', 'prompt-id-fb1', new AbortController().signal);
1291
1366
  // Consume stream to trigger logic
1292
1367
  for await (const _ of stream) {
1293
1368
  // no-op
@@ -1299,11 +1374,69 @@ describe('GeminiChat', () => {
1299
1374
  const modelTurn = history[1];
1300
1375
  expect(modelTurn.parts[0].text).toBe('Success on retry');
1301
1376
  });
1377
+ it('should switch to DEFAULT_GEMINI_FLASH_MODEL and use thinkingBudget when falling back from a gemini-3 model', async () => {
1378
+ // ARRANGE
1379
+ const authType = AuthType.LOGIN_WITH_GOOGLE;
1380
+ vi.mocked(mockConfig.getContentGeneratorConfig).mockReturnValue({
1381
+ authType,
1382
+ });
1383
+ // Initial state: Not in fallback mode
1384
+ const isInFallbackModeSpy = vi.spyOn(mockConfig, 'isInFallbackMode');
1385
+ isInFallbackModeSpy.mockReturnValue(false);
1386
+ // Mock API calls:
1387
+ // 1. Fails with 429 (simulating gemini-3 failure)
1388
+ // 2. Succeeds (simulating fallback success)
1389
+ vi.mocked(mockContentGenerator.generateContentStream)
1390
+ .mockRejectedValueOnce(error429)
1391
+ .mockResolvedValueOnce((async function* () {
1392
+ yield {
1393
+ candidates: [
1394
+ {
1395
+ content: { parts: [{ text: 'Fallback success' }] },
1396
+ finishReason: 'STOP',
1397
+ },
1398
+ ],
1399
+ };
1400
+ })());
1401
+ // Mock handleFallback to enable fallback mode and signal retry
1402
+ mockHandleFallback.mockImplementation(async () => {
1403
+ isInFallbackModeSpy.mockReturnValue(true); // Next call will see fallback mode = true
1404
+ return true;
1405
+ });
1406
+ // ACT
1407
+ const stream = await chat.sendMessageStream({ model: 'gemini-3-test-model' }, // Start with a gemini-3 model
1408
+ 'test fallback thinking', 'prompt-id-fb3', new AbortController().signal);
1409
+ for await (const _ of stream) {
1410
+ // consume stream
1411
+ }
1412
+ // ASSERT
1413
+ expect(mockContentGenerator.generateContentStream).toHaveBeenCalledTimes(2);
1414
+ // First call: gemini-3 model, thinkingLevel set
1415
+ expect(mockContentGenerator.generateContentStream).toHaveBeenNthCalledWith(1, expect.objectContaining({
1416
+ model: 'gemini-3-test-model',
1417
+ config: expect.objectContaining({
1418
+ thinkingConfig: {
1419
+ thinkingBudget: undefined,
1420
+ thinkingLevel: ThinkingLevel.HIGH,
1421
+ },
1422
+ }),
1423
+ }), 'prompt-id-fb3');
1424
+ // Second call: DEFAULT_GEMINI_FLASH_MODEL (due to fallback), thinkingBudget set (due to fix)
1425
+ expect(mockContentGenerator.generateContentStream).toHaveBeenNthCalledWith(2, expect.objectContaining({
1426
+ model: DEFAULT_GEMINI_FLASH_MODEL,
1427
+ config: expect.objectContaining({
1428
+ thinkingConfig: {
1429
+ thinkingBudget: DEFAULT_THINKING_MODE,
1430
+ thinkingLevel: undefined,
1431
+ },
1432
+ }),
1433
+ }), 'prompt-id-fb3');
1434
+ });
1302
1435
  it('should stop retrying if handleFallback returns false (e.g., auth intent)', async () => {
1303
1436
  vi.mocked(mockConfig.getModel).mockReturnValue('gemini-pro');
1304
1437
  vi.mocked(mockContentGenerator.generateContentStream).mockRejectedValue(error429);
1305
1438
  mockHandleFallback.mockResolvedValue(false);
1306
- const stream = await chat.sendMessageStream('gemini-2.0-flash', { message: 'test stop' }, 'prompt-id-fb2');
1439
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test stop', 'prompt-id-fb2', new AbortController().signal);
1307
1440
  await expect((async () => {
1308
1441
  for await (const _ of stream) {
1309
1442
  /* consume stream */
@@ -1347,7 +1480,7 @@ describe('GeminiChat', () => {
1347
1480
  };
1348
1481
  })());
1349
1482
  // Send a message and consume the stream
1350
- const stream = await chat.sendMessageStream('gemini-2.0-flash', { message: 'test' }, 'prompt-id-discard-test');
1483
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test message', 'prompt-id-discard-test', new AbortController().signal);
1351
1484
  const events = [];
1352
1485
  for await (const event of stream) {
1353
1486
  events.push(event);
@@ -1411,7 +1544,7 @@ describe('GeminiChat', () => {
1411
1544
  };
1412
1545
  })();
1413
1546
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(stream);
1414
- await chat.sendMessageStream('test-model', { message: 'test' }, 'prompt-id-preview-model-reset');
1547
+ await chat.sendMessageStream({ model: 'test-model' }, 'test', 'prompt-id-preview-model-reset', new AbortController().signal);
1415
1548
  expect(mockConfig.setPreviewModelBypassMode).toHaveBeenCalledWith(false);
1416
1549
  });
1417
1550
  it('should reset previewModelFallbackMode to false upon successful Preview Model usage', async () => {
@@ -1426,7 +1559,7 @@ describe('GeminiChat', () => {
1426
1559
  };
1427
1560
  })();
1428
1561
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(stream);
1429
- const resultStream = await chat.sendMessageStream(PREVIEW_GEMINI_MODEL, { message: 'test' }, 'prompt-id-preview-model-healing');
1562
+ const resultStream = await chat.sendMessageStream({ model: PREVIEW_GEMINI_MODEL }, 'test', 'prompt-id-preview-model-healing', new AbortController().signal);
1430
1563
  for await (const _ of resultStream) {
1431
1564
  // consume stream
1432
1565
  }
@@ -1446,7 +1579,7 @@ describe('GeminiChat', () => {
1446
1579
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(stream);
1447
1580
  // Simulate bypass mode being active (downgrade happened)
1448
1581
  vi.mocked(mockConfig.isPreviewModelBypassMode).mockReturnValue(true);
1449
- const resultStream = await chat.sendMessageStream(PREVIEW_GEMINI_MODEL, { message: 'test' }, 'prompt-id-bypass-no-healing');
1582
+ const resultStream = await chat.sendMessageStream({ model: PREVIEW_GEMINI_MODEL }, 'test', 'prompt-id-bypass-no-healing', new AbortController().signal);
1450
1583
  for await (const _ of resultStream) {
1451
1584
  // consume stream
1452
1585
  }
@@ -1455,7 +1588,7 @@ describe('GeminiChat', () => {
1455
1588
  });
1456
1589
  describe('ensureActiveLoopHasThoughtSignatures', () => {
1457
1590
  it('should add thoughtSignature to the first functionCall in each model turn of the active loop', () => {
1458
- const chat = new GeminiChat(mockConfig, {}, []);
1591
+ const chat = new GeminiChat(mockConfig, '', [], []);
1459
1592
  const history = [
1460
1593
  { role: 'user', parts: [{ text: 'Old message' }] },
1461
1594
  {
@@ -1504,7 +1637,7 @@ describe('GeminiChat', () => {
1504
1637
  expect(newContents[5]?.parts?.[1]).not.toHaveProperty('thoughtSignature');
1505
1638
  });
1506
1639
  it('should not modify contents if there is no user text message', () => {
1507
- const chat = new GeminiChat(mockConfig, {}, []);
1640
+ const chat = new GeminiChat(mockConfig, '', [], []);
1508
1641
  const history = [
1509
1642
  {
1510
1643
  role: 'user',
@@ -1520,13 +1653,13 @@ describe('GeminiChat', () => {
1520
1653
  expect(newContents[1]?.parts?.[0]).not.toHaveProperty('thoughtSignature');
1521
1654
  });
1522
1655
  it('should handle an empty history', () => {
1523
- const chat = new GeminiChat(mockConfig, {}, []);
1656
+ const chat = new GeminiChat(mockConfig, '', []);
1524
1657
  const history = [];
1525
1658
  const newContents = chat.ensureActiveLoopHasThoughtSignatures(history);
1526
1659
  expect(newContents).toEqual([]);
1527
1660
  });
1528
1661
  it('should handle history with only a user message', () => {
1529
- const chat = new GeminiChat(mockConfig, {}, []);
1662
+ const chat = new GeminiChat(mockConfig, '', []);
1530
1663
  const history = [{ role: 'user', parts: [{ text: 'Hello' }] }];
1531
1664
  const newContents = chat.ensureActiveLoopHasThoughtSignatures(history);
1532
1665
  expect(newContents).toEqual(history);