@google/gemini-cli-core 0.0.3-preview.4 → 0.0.4

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 (490) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +12 -2
  3. package/dist/index.d.ts +6 -2
  4. package/dist/index.js +6 -2
  5. package/dist/index.js.map +1 -1
  6. package/dist/src/code_assist/codeAssist.d.ts +2 -0
  7. package/dist/src/code_assist/codeAssist.js +12 -0
  8. package/dist/src/code_assist/codeAssist.js.map +1 -1
  9. package/dist/src/code_assist/converter.d.ts +3 -1
  10. package/dist/src/code_assist/converter.js +2 -1
  11. package/dist/src/code_assist/converter.js.map +1 -1
  12. package/dist/src/code_assist/converter.test.js +10 -0
  13. package/dist/src/code_assist/converter.test.js.map +1 -1
  14. package/dist/src/code_assist/oauth-credential-storage.d.ts +25 -0
  15. package/dist/src/code_assist/oauth-credential-storage.js +109 -0
  16. package/dist/src/code_assist/oauth-credential-storage.js.map +1 -0
  17. package/dist/src/code_assist/oauth-credential-storage.test.js +136 -0
  18. package/dist/src/code_assist/oauth-credential-storage.test.js.map +1 -0
  19. package/dist/src/code_assist/oauth2.js +92 -29
  20. package/dist/src/code_assist/oauth2.js.map +1 -1
  21. package/dist/src/code_assist/oauth2.test.js +729 -339
  22. package/dist/src/code_assist/oauth2.test.js.map +1 -1
  23. package/dist/src/code_assist/server.d.ts +1 -1
  24. package/dist/src/code_assist/server.js +24 -1
  25. package/dist/src/code_assist/server.js.map +1 -1
  26. package/dist/src/code_assist/server.test.js +25 -0
  27. package/dist/src/code_assist/server.test.js.map +1 -1
  28. package/dist/src/code_assist/types.d.ts +17 -2
  29. package/dist/src/config/config.d.ts +72 -12
  30. package/dist/src/config/config.js +196 -64
  31. package/dist/src/config/config.js.map +1 -1
  32. package/dist/src/config/config.test.js +305 -178
  33. package/dist/src/config/config.test.js.map +1 -1
  34. package/dist/src/config/models.d.ts +16 -0
  35. package/dist/src/config/models.js +29 -0
  36. package/dist/src/config/models.js.map +1 -1
  37. package/dist/src/config/models.test.d.ts +6 -0
  38. package/dist/src/config/models.test.js +55 -0
  39. package/dist/src/config/models.test.js.map +1 -0
  40. package/dist/src/config/storage.d.ts +2 -0
  41. package/dist/src/config/storage.js +6 -1
  42. package/dist/src/config/storage.js.map +1 -1
  43. package/dist/src/config/storage.test.js +4 -0
  44. package/dist/src/config/storage.test.js.map +1 -1
  45. package/dist/src/confirmation-bus/index.d.ts +7 -0
  46. package/dist/src/confirmation-bus/index.js +8 -0
  47. package/dist/src/confirmation-bus/index.js.map +1 -0
  48. package/dist/src/confirmation-bus/message-bus.d.ts +17 -0
  49. package/dist/src/confirmation-bus/message-bus.js +81 -0
  50. package/dist/src/confirmation-bus/message-bus.js.map +1 -0
  51. package/dist/src/confirmation-bus/message-bus.test.d.ts +6 -0
  52. package/dist/src/confirmation-bus/message-bus.test.js +164 -0
  53. package/dist/src/confirmation-bus/message-bus.test.js.map +1 -0
  54. package/dist/src/confirmation-bus/types.d.ts +38 -0
  55. package/dist/src/confirmation-bus/types.js +15 -0
  56. package/dist/src/confirmation-bus/types.js.map +1 -0
  57. package/dist/src/core/baseLlmClient.d.ts +46 -0
  58. package/dist/src/core/baseLlmClient.js +112 -0
  59. package/dist/src/core/baseLlmClient.js.map +1 -0
  60. package/dist/src/core/baseLlmClient.test.d.ts +6 -0
  61. package/dist/src/core/baseLlmClient.test.js +253 -0
  62. package/dist/src/core/baseLlmClient.test.js.map +1 -0
  63. package/dist/src/core/client.d.ts +16 -21
  64. package/dist/src/core/client.js +145 -232
  65. package/dist/src/core/client.js.map +1 -1
  66. package/dist/src/core/client.test.js +393 -492
  67. package/dist/src/core/client.test.js.map +1 -1
  68. package/dist/src/core/contentGenerator.d.ts +2 -3
  69. package/dist/src/core/contentGenerator.js +0 -4
  70. package/dist/src/core/contentGenerator.js.map +1 -1
  71. package/dist/src/core/contentGenerator.test.js +1 -3
  72. package/dist/src/core/contentGenerator.test.js.map +1 -1
  73. package/dist/src/core/coreToolScheduler.d.ts +8 -3
  74. package/dist/src/core/coreToolScheduler.js +106 -5
  75. package/dist/src/core/coreToolScheduler.js.map +1 -1
  76. package/dist/src/core/coreToolScheduler.test.js +233 -5
  77. package/dist/src/core/coreToolScheduler.test.js.map +1 -1
  78. package/dist/src/core/geminiChat.d.ts +38 -32
  79. package/dist/src/core/geminiChat.js +209 -219
  80. package/dist/src/core/geminiChat.js.map +1 -1
  81. package/dist/src/core/geminiChat.test.js +674 -386
  82. package/dist/src/core/geminiChat.test.js.map +1 -1
  83. package/dist/src/core/loggingContentGenerator.js +13 -16
  84. package/dist/src/core/loggingContentGenerator.js.map +1 -1
  85. package/dist/src/core/nonInteractiveToolExecutor.test.js +59 -1
  86. package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
  87. package/dist/src/core/prompts.d.ts +5 -0
  88. package/dist/src/core/prompts.js +63 -42
  89. package/dist/src/core/prompts.js.map +1 -1
  90. package/dist/src/core/prompts.test.js +130 -1
  91. package/dist/src/core/prompts.test.js.map +1 -1
  92. package/dist/src/core/subagent.js +7 -10
  93. package/dist/src/core/subagent.js.map +1 -1
  94. package/dist/src/core/subagent.test.js +32 -22
  95. package/dist/src/core/subagent.test.js.map +1 -1
  96. package/dist/src/core/turn.d.ts +21 -5
  97. package/dist/src/core/turn.js +45 -11
  98. package/dist/src/core/turn.js.map +1 -1
  99. package/dist/src/core/turn.test.js +340 -100
  100. package/dist/src/core/turn.test.js.map +1 -1
  101. package/dist/src/fallback/handler.d.ts +7 -0
  102. package/dist/src/fallback/handler.js +51 -0
  103. package/dist/src/fallback/handler.js.map +1 -0
  104. package/dist/src/fallback/handler.test.d.ts +6 -0
  105. package/dist/src/fallback/handler.test.js +130 -0
  106. package/dist/src/fallback/handler.test.js.map +1 -0
  107. package/dist/src/fallback/types.d.ts +14 -0
  108. package/dist/src/fallback/types.js +7 -0
  109. package/dist/src/fallback/types.js.map +1 -0
  110. package/dist/src/generated/git-commit.d.ts +2 -2
  111. package/dist/src/generated/git-commit.js +2 -2
  112. package/dist/src/generated/git-commit.js.map +1 -1
  113. package/dist/src/ide/constants.d.ts +3 -0
  114. package/dist/src/ide/constants.js +3 -0
  115. package/dist/src/ide/constants.js.map +1 -1
  116. package/dist/src/ide/detect-ide.d.ts +42 -14
  117. package/dist/src/ide/detect-ide.js +22 -68
  118. package/dist/src/ide/detect-ide.js.map +1 -1
  119. package/dist/src/ide/detect-ide.test.js +11 -51
  120. package/dist/src/ide/detect-ide.test.js.map +1 -1
  121. package/dist/src/ide/ide-client.d.ts +60 -18
  122. package/dist/src/ide/ide-client.js +275 -53
  123. package/dist/src/ide/ide-client.js.map +1 -1
  124. package/dist/src/ide/ide-client.test.js +239 -6
  125. package/dist/src/ide/ide-client.test.js.map +1 -1
  126. package/dist/src/ide/ide-installer.d.ts +2 -2
  127. package/dist/src/ide/ide-installer.js +15 -11
  128. package/dist/src/ide/ide-installer.js.map +1 -1
  129. package/dist/src/ide/ide-installer.test.js +30 -12
  130. package/dist/src/ide/ide-installer.test.js.map +1 -1
  131. package/dist/src/ide/ideContext.d.ts +35 -365
  132. package/dist/src/ide/ideContext.js +60 -106
  133. package/dist/src/ide/ideContext.js.map +1 -1
  134. package/dist/src/ide/ideContext.test.js +152 -24
  135. package/dist/src/ide/ideContext.test.js.map +1 -1
  136. package/dist/src/ide/process-utils.d.ts +0 -1
  137. package/dist/src/ide/process-utils.js +43 -25
  138. package/dist/src/ide/process-utils.js.map +1 -1
  139. package/dist/src/ide/process-utils.test.js +90 -4
  140. package/dist/src/ide/process-utils.test.js.map +1 -1
  141. package/dist/src/ide/types.d.ts +486 -0
  142. package/dist/src/ide/types.js +138 -0
  143. package/dist/src/ide/types.js.map +1 -0
  144. package/dist/src/index.d.ts +10 -2
  145. package/dist/src/index.js +11 -2
  146. package/dist/src/index.js.map +1 -1
  147. package/dist/src/mcp/oauth-provider.d.ts +15 -12
  148. package/dist/src/mcp/oauth-provider.js +63 -56
  149. package/dist/src/mcp/oauth-provider.js.map +1 -1
  150. package/dist/src/mcp/oauth-provider.test.js +74 -35
  151. package/dist/src/mcp/oauth-provider.test.js.map +1 -1
  152. package/dist/src/mcp/oauth-token-storage.d.ts +14 -10
  153. package/dist/src/mcp/oauth-token-storage.js +52 -20
  154. package/dist/src/mcp/oauth-token-storage.js.map +1 -1
  155. package/dist/src/mcp/oauth-token-storage.test.js +255 -162
  156. package/dist/src/mcp/oauth-token-storage.test.js.map +1 -1
  157. package/dist/src/mcp/token-storage/base-token-storage.d.ts +1 -1
  158. package/dist/src/mcp/token-storage/base-token-storage.js +1 -1
  159. package/dist/src/mcp/token-storage/base-token-storage.js.map +1 -1
  160. package/dist/src/mcp/token-storage/base-token-storage.test.js +1 -1
  161. package/dist/src/mcp/token-storage/base-token-storage.test.js.map +1 -1
  162. package/dist/src/mcp/token-storage/file-token-storage.d.ts +24 -0
  163. package/dist/src/mcp/token-storage/file-token-storage.js +144 -0
  164. package/dist/src/mcp/token-storage/file-token-storage.js.map +1 -0
  165. package/dist/src/mcp/token-storage/file-token-storage.test.d.ts +6 -0
  166. package/dist/src/mcp/token-storage/file-token-storage.test.js +235 -0
  167. package/dist/src/mcp/token-storage/file-token-storage.test.js.map +1 -0
  168. package/dist/src/mcp/token-storage/hybrid-token-storage.d.ts +23 -0
  169. package/dist/src/mcp/token-storage/hybrid-token-storage.js +78 -0
  170. package/dist/src/mcp/token-storage/hybrid-token-storage.js.map +1 -0
  171. package/dist/src/mcp/token-storage/hybrid-token-storage.test.d.ts +6 -0
  172. package/dist/src/mcp/token-storage/hybrid-token-storage.test.js +193 -0
  173. package/dist/src/mcp/token-storage/hybrid-token-storage.test.js.map +1 -0
  174. package/dist/src/mcp/token-storage/index.d.ts +11 -0
  175. package/dist/src/mcp/token-storage/index.js +12 -0
  176. package/dist/src/mcp/token-storage/index.js.map +1 -0
  177. package/dist/src/mcp/token-storage/keychain-token-storage.d.ts +31 -0
  178. package/dist/src/mcp/token-storage/keychain-token-storage.js +190 -0
  179. package/dist/src/mcp/token-storage/keychain-token-storage.js.map +1 -0
  180. package/dist/src/mcp/token-storage/keychain-token-storage.test.d.ts +6 -0
  181. package/dist/src/mcp/token-storage/keychain-token-storage.test.js +254 -0
  182. package/dist/src/mcp/token-storage/keychain-token-storage.test.js.map +1 -0
  183. package/dist/src/mcp/token-storage/types.d.ts +4 -0
  184. package/dist/src/mcp/token-storage/types.js +5 -1
  185. package/dist/src/mcp/token-storage/types.js.map +1 -1
  186. package/dist/src/output/json-formatter.d.ts +11 -0
  187. package/dist/src/output/json-formatter.js +30 -0
  188. package/dist/src/output/json-formatter.js.map +1 -0
  189. package/dist/src/output/json-formatter.test.d.ts +6 -0
  190. package/dist/src/output/json-formatter.test.js +266 -0
  191. package/dist/src/output/json-formatter.test.js.map +1 -0
  192. package/dist/src/output/types.d.ts +20 -0
  193. package/dist/src/output/types.js +11 -0
  194. package/dist/src/output/types.js.map +1 -0
  195. package/dist/src/policy/index.d.ts +7 -0
  196. package/dist/src/policy/index.js +8 -0
  197. package/dist/src/policy/index.js.map +1 -0
  198. package/dist/src/policy/policy-engine.d.ts +30 -0
  199. package/dist/src/policy/policy-engine.js +92 -0
  200. package/dist/src/policy/policy-engine.js.map +1 -0
  201. package/dist/src/policy/policy-engine.test.d.ts +6 -0
  202. package/dist/src/policy/policy-engine.test.js +515 -0
  203. package/dist/src/policy/policy-engine.test.js.map +1 -0
  204. package/dist/src/policy/stable-stringify.d.ts +58 -0
  205. package/dist/src/policy/stable-stringify.js +122 -0
  206. package/dist/src/policy/stable-stringify.js.map +1 -0
  207. package/dist/src/policy/types.d.ts +47 -0
  208. package/dist/src/policy/types.js +12 -0
  209. package/dist/src/policy/types.js.map +1 -0
  210. package/dist/src/routing/modelRouterService.d.ts +23 -0
  211. package/dist/src/routing/modelRouterService.js +70 -0
  212. package/dist/src/routing/modelRouterService.js.map +1 -0
  213. package/dist/src/routing/modelRouterService.test.d.ts +6 -0
  214. package/dist/src/routing/modelRouterService.test.js +98 -0
  215. package/dist/src/routing/modelRouterService.test.js.map +1 -0
  216. package/dist/src/routing/routingStrategy.d.ts +62 -0
  217. package/dist/src/routing/routingStrategy.js +7 -0
  218. package/dist/src/routing/routingStrategy.js.map +1 -0
  219. package/dist/src/routing/strategies/classifierStrategy.d.ts +12 -0
  220. package/dist/src/routing/strategies/classifierStrategy.js +173 -0
  221. package/dist/src/routing/strategies/classifierStrategy.js.map +1 -0
  222. package/dist/src/routing/strategies/classifierStrategy.test.d.ts +6 -0
  223. package/dist/src/routing/strategies/classifierStrategy.test.js +192 -0
  224. package/dist/src/routing/strategies/classifierStrategy.test.js.map +1 -0
  225. package/dist/src/routing/strategies/compositeStrategy.d.ts +26 -0
  226. package/dist/src/routing/strategies/compositeStrategy.js +67 -0
  227. package/dist/src/routing/strategies/compositeStrategy.js.map +1 -0
  228. package/dist/src/routing/strategies/compositeStrategy.test.d.ts +6 -0
  229. package/dist/src/routing/strategies/compositeStrategy.test.js +123 -0
  230. package/dist/src/routing/strategies/compositeStrategy.test.js.map +1 -0
  231. package/dist/src/routing/strategies/defaultStrategy.d.ts +12 -0
  232. package/dist/src/routing/strategies/defaultStrategy.js +20 -0
  233. package/dist/src/routing/strategies/defaultStrategy.js.map +1 -0
  234. package/dist/src/routing/strategies/defaultStrategy.test.d.ts +6 -0
  235. package/dist/src/routing/strategies/defaultStrategy.test.js +26 -0
  236. package/dist/src/routing/strategies/defaultStrategy.test.js.map +1 -0
  237. package/dist/src/routing/strategies/fallbackStrategy.d.ts +12 -0
  238. package/dist/src/routing/strategies/fallbackStrategy.js +25 -0
  239. package/dist/src/routing/strategies/fallbackStrategy.js.map +1 -0
  240. package/dist/src/routing/strategies/fallbackStrategy.test.d.ts +6 -0
  241. package/dist/src/routing/strategies/fallbackStrategy.test.js +55 -0
  242. package/dist/src/routing/strategies/fallbackStrategy.test.js.map +1 -0
  243. package/dist/src/routing/strategies/overrideStrategy.d.ts +15 -0
  244. package/dist/src/routing/strategies/overrideStrategy.js +28 -0
  245. package/dist/src/routing/strategies/overrideStrategy.js.map +1 -0
  246. package/dist/src/routing/strategies/overrideStrategy.test.d.ts +6 -0
  247. package/dist/src/routing/strategies/overrideStrategy.test.js +42 -0
  248. package/dist/src/routing/strategies/overrideStrategy.test.js.map +1 -0
  249. package/dist/src/services/chatRecordingService.d.ts +7 -13
  250. package/dist/src/services/chatRecordingService.js +28 -19
  251. package/dist/src/services/chatRecordingService.js.map +1 -1
  252. package/dist/src/services/chatRecordingService.test.js +62 -20
  253. package/dist/src/services/chatRecordingService.test.js.map +1 -1
  254. package/dist/src/services/fileDiscoveryService.d.ts +10 -0
  255. package/dist/src/services/fileDiscoveryService.js +31 -17
  256. package/dist/src/services/fileDiscoveryService.js.map +1 -1
  257. package/dist/src/services/gitService.js +9 -12
  258. package/dist/src/services/gitService.js.map +1 -1
  259. package/dist/src/services/gitService.test.js +10 -20
  260. package/dist/src/services/gitService.test.js.map +1 -1
  261. package/dist/src/services/loopDetectionService.d.ts +5 -0
  262. package/dist/src/services/loopDetectionService.js +36 -20
  263. package/dist/src/services/loopDetectionService.js.map +1 -1
  264. package/dist/src/services/loopDetectionService.test.js +41 -12
  265. package/dist/src/services/loopDetectionService.test.js.map +1 -1
  266. package/dist/src/services/shellExecutionService.d.ts +34 -2
  267. package/dist/src/services/shellExecutionService.js +192 -43
  268. package/dist/src/services/shellExecutionService.js.map +1 -1
  269. package/dist/src/services/shellExecutionService.test.js +184 -55
  270. package/dist/src/services/shellExecutionService.test.js.map +1 -1
  271. package/dist/src/telemetry/activity-detector.d.ts +41 -0
  272. package/dist/src/telemetry/activity-detector.js +61 -0
  273. package/dist/src/telemetry/activity-detector.js.map +1 -0
  274. package/dist/src/telemetry/activity-detector.test.d.ts +6 -0
  275. package/dist/src/telemetry/activity-detector.test.js +136 -0
  276. package/dist/src/telemetry/activity-detector.test.js.map +1 -0
  277. package/dist/src/telemetry/activity-types.d.ts +19 -0
  278. package/dist/src/telemetry/activity-types.js +21 -0
  279. package/dist/src/telemetry/activity-types.js.map +1 -0
  280. package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +16 -2
  281. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +143 -24
  282. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
  283. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +101 -1
  284. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
  285. package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +19 -2
  286. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +48 -2
  287. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
  288. package/dist/src/telemetry/constants.d.ts +8 -0
  289. package/dist/src/telemetry/constants.js +8 -0
  290. package/dist/src/telemetry/constants.js.map +1 -1
  291. package/dist/src/telemetry/gcp-exporters.d.ts +34 -0
  292. package/dist/src/telemetry/gcp-exporters.js +117 -0
  293. package/dist/src/telemetry/gcp-exporters.js.map +1 -0
  294. package/dist/src/telemetry/gcp-exporters.test.d.ts +6 -0
  295. package/dist/src/telemetry/gcp-exporters.test.js +318 -0
  296. package/dist/src/telemetry/gcp-exporters.test.js.map +1 -0
  297. package/dist/src/telemetry/high-water-mark-tracker.d.ts +43 -0
  298. package/dist/src/telemetry/high-water-mark-tracker.js +88 -0
  299. package/dist/src/telemetry/high-water-mark-tracker.js.map +1 -0
  300. package/dist/src/telemetry/high-water-mark-tracker.test.d.ts +6 -0
  301. package/dist/src/telemetry/high-water-mark-tracker.test.js +152 -0
  302. package/dist/src/telemetry/high-water-mark-tracker.test.js.map +1 -0
  303. package/dist/src/telemetry/index.d.ts +7 -2
  304. package/dist/src/telemetry/index.js +7 -2
  305. package/dist/src/telemetry/index.js.map +1 -1
  306. package/dist/src/telemetry/loggers.d.ts +8 -1
  307. package/dist/src/telemetry/loggers.js +140 -8
  308. package/dist/src/telemetry/loggers.js.map +1 -1
  309. package/dist/src/telemetry/loggers.test.js +268 -39
  310. package/dist/src/telemetry/loggers.test.js.map +1 -1
  311. package/dist/src/telemetry/metrics.d.ts +4 -3
  312. package/dist/src/telemetry/metrics.js +33 -10
  313. package/dist/src/telemetry/metrics.js.map +1 -1
  314. package/dist/src/telemetry/metrics.test.js +47 -25
  315. package/dist/src/telemetry/metrics.test.js.map +1 -1
  316. package/dist/src/telemetry/rate-limiter.d.ts +48 -0
  317. package/dist/src/telemetry/rate-limiter.js +100 -0
  318. package/dist/src/telemetry/rate-limiter.js.map +1 -0
  319. package/dist/src/telemetry/rate-limiter.test.d.ts +6 -0
  320. package/dist/src/telemetry/rate-limiter.test.js +207 -0
  321. package/dist/src/telemetry/rate-limiter.test.js.map +1 -0
  322. package/dist/src/telemetry/sdk.js +16 -1
  323. package/dist/src/telemetry/sdk.js.map +1 -1
  324. package/dist/src/telemetry/sdk.test.js +95 -0
  325. package/dist/src/telemetry/sdk.test.js.map +1 -1
  326. package/dist/src/telemetry/types.d.ts +70 -6
  327. package/dist/src/telemetry/types.js +112 -8
  328. package/dist/src/telemetry/types.js.map +1 -1
  329. package/dist/src/telemetry/uiTelemetry.d.ts +1 -1
  330. package/dist/src/telemetry/uiTelemetry.js +6 -7
  331. package/dist/src/telemetry/uiTelemetry.js.map +1 -1
  332. package/dist/src/telemetry/uiTelemetry.test.js +15 -15
  333. package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
  334. package/dist/src/test-utils/index.d.ts +6 -0
  335. package/dist/src/test-utils/index.js +7 -0
  336. package/dist/src/test-utils/index.js.map +1 -0
  337. package/dist/src/test-utils/mock-tool.d.ts +41 -0
  338. package/dist/src/test-utils/mock-tool.js +51 -0
  339. package/dist/src/test-utils/mock-tool.js.map +1 -0
  340. package/dist/src/tools/diffOptions.js +21 -13
  341. package/dist/src/tools/diffOptions.js.map +1 -1
  342. package/dist/src/tools/diffOptions.test.js +58 -22
  343. package/dist/src/tools/diffOptions.test.js.map +1 -1
  344. package/dist/src/tools/edit.d.ts +2 -2
  345. package/dist/src/tools/edit.js +35 -44
  346. package/dist/src/tools/edit.js.map +1 -1
  347. package/dist/src/tools/edit.test.js +124 -13
  348. package/dist/src/tools/edit.test.js.map +1 -1
  349. package/dist/src/tools/glob.d.ts +5 -1
  350. package/dist/src/tools/glob.js +24 -17
  351. package/dist/src/tools/glob.js.map +1 -1
  352. package/dist/src/tools/glob.test.js +51 -0
  353. package/dist/src/tools/glob.test.js.map +1 -1
  354. package/dist/src/tools/ls.js +19 -32
  355. package/dist/src/tools/ls.js.map +1 -1
  356. package/dist/src/tools/ls.test.js +140 -280
  357. package/dist/src/tools/ls.test.js.map +1 -1
  358. package/dist/src/tools/mcp-client-manager.d.ts +5 -3
  359. package/dist/src/tools/mcp-client-manager.js +13 -4
  360. package/dist/src/tools/mcp-client-manager.js.map +1 -1
  361. package/dist/src/tools/mcp-client-manager.test.js +20 -1
  362. package/dist/src/tools/mcp-client-manager.test.js.map +1 -1
  363. package/dist/src/tools/mcp-client.d.ts +5 -5
  364. package/dist/src/tools/mcp-client.js +40 -35
  365. package/dist/src/tools/mcp-client.js.map +1 -1
  366. package/dist/src/tools/mcp-client.test.js +3 -3
  367. package/dist/src/tools/mcp-client.test.js.map +1 -1
  368. package/dist/src/tools/mcp-tool.d.ts +3 -2
  369. package/dist/src/tools/mcp-tool.js +9 -9
  370. package/dist/src/tools/mcp-tool.js.map +1 -1
  371. package/dist/src/tools/mcp-tool.test.js +28 -7
  372. package/dist/src/tools/mcp-tool.test.js.map +1 -1
  373. package/dist/src/tools/memoryTool.js +5 -33
  374. package/dist/src/tools/memoryTool.js.map +1 -1
  375. package/dist/src/tools/read-file.js +8 -3
  376. package/dist/src/tools/read-file.js.map +1 -1
  377. package/dist/src/tools/read-file.test.js +29 -0
  378. package/dist/src/tools/read-file.test.js.map +1 -1
  379. package/dist/src/tools/read-many-files.d.ts +1 -1
  380. package/dist/src/tools/read-many-files.js +18 -50
  381. package/dist/src/tools/read-many-files.js.map +1 -1
  382. package/dist/src/tools/read-many-files.test.js +4 -4
  383. package/dist/src/tools/read-many-files.test.js.map +1 -1
  384. package/dist/src/tools/ripGrep.d.ts +8 -0
  385. package/dist/src/tools/ripGrep.js +26 -1
  386. package/dist/src/tools/ripGrep.js.map +1 -1
  387. package/dist/src/tools/ripGrep.test.js +107 -5
  388. package/dist/src/tools/ripGrep.test.js.map +1 -1
  389. package/dist/src/tools/shell.d.ts +12 -2
  390. package/dist/src/tools/shell.js +20 -24
  391. package/dist/src/tools/shell.js.map +1 -1
  392. package/dist/src/tools/shell.test.js +35 -70
  393. package/dist/src/tools/shell.test.js.map +1 -1
  394. package/dist/src/tools/smart-edit.d.ts +72 -0
  395. package/dist/src/tools/smart-edit.js +594 -0
  396. package/dist/src/tools/smart-edit.js.map +1 -0
  397. package/dist/src/tools/smart-edit.test.d.ts +6 -0
  398. package/dist/src/tools/smart-edit.test.js +419 -0
  399. package/dist/src/tools/smart-edit.test.js.map +1 -0
  400. package/dist/src/tools/tool-registry.d.ts +2 -1
  401. package/dist/src/tools/tool-registry.js +6 -5
  402. package/dist/src/tools/tool-registry.js.map +1 -1
  403. package/dist/src/tools/tools.d.ts +14 -7
  404. package/dist/src/tools/tools.js +9 -2
  405. package/dist/src/tools/tools.js.map +1 -1
  406. package/dist/src/tools/web-fetch.js +4 -3
  407. package/dist/src/tools/web-fetch.js.map +1 -1
  408. package/dist/src/tools/web-search.d.ts +1 -1
  409. package/dist/src/tools/web-search.js +3 -1
  410. package/dist/src/tools/web-search.js.map +1 -1
  411. package/dist/src/tools/write-file.js +14 -19
  412. package/dist/src/tools/write-file.js.map +1 -1
  413. package/dist/src/tools/write-file.test.js +99 -19
  414. package/dist/src/tools/write-file.test.js.map +1 -1
  415. package/dist/src/utils/bfsFileSearch.js +11 -5
  416. package/dist/src/utils/bfsFileSearch.js.map +1 -1
  417. package/dist/src/utils/editCorrector.d.ts +7 -6
  418. package/dist/src/utils/editCorrector.js +61 -18
  419. package/dist/src/utils/editCorrector.js.map +1 -1
  420. package/dist/src/utils/editCorrector.test.js +30 -79
  421. package/dist/src/utils/editCorrector.test.js.map +1 -1
  422. package/dist/src/utils/editor.js +31 -44
  423. package/dist/src/utils/editor.js.map +1 -1
  424. package/dist/src/utils/editor.test.js +61 -75
  425. package/dist/src/utils/editor.test.js.map +1 -1
  426. package/dist/src/utils/errorParsing.js +2 -2
  427. package/dist/src/utils/errorParsing.js.map +1 -1
  428. package/dist/src/utils/errorParsing.test.js +7 -7
  429. package/dist/src/utils/errorParsing.test.js.map +1 -1
  430. package/dist/src/utils/errors.d.ts +6 -0
  431. package/dist/src/utils/errors.js +10 -0
  432. package/dist/src/utils/errors.js.map +1 -1
  433. package/dist/src/utils/fileUtils.d.ts +20 -3
  434. package/dist/src/utils/fileUtils.js +154 -32
  435. package/dist/src/utils/fileUtils.js.map +1 -1
  436. package/dist/src/utils/fileUtils.test.js +347 -29
  437. package/dist/src/utils/fileUtils.test.js.map +1 -1
  438. package/dist/src/utils/flashFallback.test.d.ts +6 -0
  439. package/dist/src/utils/{flashFallback.integration.test.js → flashFallback.test.js} +31 -27
  440. package/dist/src/utils/flashFallback.test.js.map +1 -0
  441. package/dist/src/utils/geminiIgnoreParser.d.ts +18 -0
  442. package/dist/src/utils/geminiIgnoreParser.js +61 -0
  443. package/dist/src/utils/geminiIgnoreParser.js.map +1 -0
  444. package/dist/src/utils/geminiIgnoreParser.test.d.ts +6 -0
  445. package/dist/src/utils/geminiIgnoreParser.test.js +50 -0
  446. package/dist/src/utils/geminiIgnoreParser.test.js.map +1 -0
  447. package/dist/src/utils/gitIgnoreParser.d.ts +3 -7
  448. package/dist/src/utils/gitIgnoreParser.js +125 -34
  449. package/dist/src/utils/gitIgnoreParser.js.map +1 -1
  450. package/dist/src/utils/gitIgnoreParser.test.js +66 -35
  451. package/dist/src/utils/gitIgnoreParser.test.js.map +1 -1
  452. package/dist/src/utils/llm-edit-fixer.d.ts +26 -0
  453. package/dist/src/utils/llm-edit-fixer.js +121 -0
  454. package/dist/src/utils/llm-edit-fixer.js.map +1 -0
  455. package/dist/src/utils/llm-edit-fixer.test.d.ts +6 -0
  456. package/dist/src/utils/llm-edit-fixer.test.js +105 -0
  457. package/dist/src/utils/llm-edit-fixer.test.js.map +1 -0
  458. package/dist/src/utils/memoryDiscovery.d.ts +5 -4
  459. package/dist/src/utils/memoryDiscovery.js +10 -9
  460. package/dist/src/utils/memoryDiscovery.js.map +1 -1
  461. package/dist/src/utils/memoryDiscovery.test.js +50 -25
  462. package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
  463. package/dist/src/utils/nextSpeakerChecker.d.ts +2 -2
  464. package/dist/src/utils/nextSpeakerChecker.js +8 -2
  465. package/dist/src/utils/nextSpeakerChecker.js.map +1 -1
  466. package/dist/src/utils/nextSpeakerChecker.test.js +75 -64
  467. package/dist/src/utils/nextSpeakerChecker.test.js.map +1 -1
  468. package/dist/src/utils/promptIdContext.d.ts +7 -0
  469. package/dist/src/utils/promptIdContext.js +8 -0
  470. package/dist/src/utils/promptIdContext.js.map +1 -0
  471. package/dist/src/utils/shell-utils.d.ts +5 -0
  472. package/dist/src/utils/shell-utils.js +23 -0
  473. package/dist/src/utils/shell-utils.js.map +1 -1
  474. package/dist/src/utils/terminalSerializer.d.ts +28 -0
  475. package/dist/src/utils/terminalSerializer.js +432 -0
  476. package/dist/src/utils/terminalSerializer.js.map +1 -0
  477. package/dist/src/utils/terminalSerializer.test.d.ts +6 -0
  478. package/dist/src/utils/terminalSerializer.test.js +176 -0
  479. package/dist/src/utils/terminalSerializer.test.js.map +1 -0
  480. package/dist/src/utils/textUtils.d.ts +5 -0
  481. package/dist/src/utils/textUtils.js +14 -0
  482. package/dist/src/utils/textUtils.js.map +1 -1
  483. package/dist/src/utils/textUtils.test.d.ts +6 -0
  484. package/dist/src/utils/textUtils.test.js +59 -0
  485. package/dist/src/utils/textUtils.test.js.map +1 -0
  486. package/dist/tsconfig.tsbuildinfo +1 -1
  487. package/package.json +9 -3
  488. package/dist/google-gemini-cli-core-0.3.0-preview.3.tgz +0 -0
  489. package/dist/src/utils/flashFallback.integration.test.js.map +0 -1
  490. /package/dist/src/{utils/flashFallback.integration.test.d.ts → code_assist/oauth-credential-storage.test.d.ts} +0 -0
@@ -3,10 +3,24 @@
3
3
  * Copyright 2025 Google LLC
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
- import type { GenerateContentResponse, Content, GenerateContentConfig, SendMessageParameters, Tool } from '@google/genai';
7
- import type { ContentGenerator } from './contentGenerator.js';
6
+ import { GenerateContentResponse, type Content, type GenerateContentConfig, type SendMessageParameters, type Part, type Tool } from '@google/genai';
8
7
  import type { Config } from '../config/config.js';
9
8
  import type { StructuredError } from './turn.js';
9
+ import { ChatRecordingService } from '../services/chatRecordingService.js';
10
+ export declare enum StreamEventType {
11
+ /** A regular content chunk from the API. */
12
+ CHUNK = "chunk",
13
+ /** A signal that a retry is about to happen. The UI should discard any partial
14
+ * content from the attempt that just failed. */
15
+ RETRY = "retry"
16
+ }
17
+ export type StreamEvent = {
18
+ type: StreamEventType.CHUNK;
19
+ value: GenerateContentResponse;
20
+ } | {
21
+ type: StreamEventType.RETRY;
22
+ };
23
+ export declare function isValidNonThoughtTextPart(part: Part): boolean;
10
24
  /**
11
25
  * Custom error to signal that a stream completed without valid content,
12
26
  * which should trigger a retry.
@@ -23,38 +37,12 @@ export declare class EmptyStreamError extends Error {
23
37
  */
24
38
  export declare class GeminiChat {
25
39
  private readonly config;
26
- private readonly contentGenerator;
27
40
  private readonly generationConfig;
28
41
  private history;
29
42
  private sendPromise;
30
- constructor(config: Config, contentGenerator: ContentGenerator, generationConfig?: GenerateContentConfig, history?: Content[]);
31
- /**
32
- * Handles falling back to Flash model when persistent 429 errors occur for OAuth users.
33
- * Uses a fallback handler if provided by the config; otherwise, returns null.
34
- */
35
- private handleFlashFallback;
43
+ private readonly chatRecordingService;
44
+ constructor(config: Config, generationConfig?: GenerateContentConfig, history?: Content[]);
36
45
  setSystemInstruction(sysInstr: string): void;
37
- /**
38
- * Sends a message to the model and returns the response.
39
- *
40
- * @remarks
41
- * This method will wait for the previous message to be processed before
42
- * sending the next message.
43
- *
44
- * @see {@link Chat#sendMessageStream} for streaming method.
45
- * @param params - parameters for sending messages within a chat session.
46
- * @returns The model's response.
47
- *
48
- * @example
49
- * ```ts
50
- * const chat = ai.chats.create({model: 'gemini-2.0-flash'});
51
- * const response = await chat.sendMessage({
52
- * message: 'Why is the sky blue?'
53
- * });
54
- * console.log(response.text);
55
- * ```
56
- */
57
- sendMessage(params: SendMessageParameters, prompt_id: string): Promise<GenerateContentResponse>;
58
46
  /**
59
47
  * Sends a message to the model and returns the response in chunks.
60
48
  *
@@ -77,7 +65,7 @@ export declare class GeminiChat {
77
65
  * }
78
66
  * ```
79
67
  */
80
- sendMessageStream(params: SendMessageParameters, prompt_id: string): Promise<AsyncGenerator<GenerateContentResponse>>;
68
+ sendMessageStream(model: string, params: SendMessageParameters, prompt_id: string): Promise<AsyncGenerator<StreamEvent>>;
81
69
  private makeApiCallAndProcessStream;
82
70
  /**
83
71
  * Returns the chat history.
@@ -112,10 +100,28 @@ export declare class GeminiChat {
112
100
  */
113
101
  addHistory(content: Content): void;
114
102
  setHistory(history: Content[]): void;
103
+ stripThoughtsFromHistory(): void;
115
104
  setTools(tools: Tool[]): void;
116
105
  maybeIncludeSchemaDepthContext(error: StructuredError): Promise<void>;
117
106
  private processStreamResponse;
118
- private recordHistory;
107
+ /**
108
+ * Gets the chat recording service instance.
109
+ */
110
+ getChatRecordingService(): ChatRecordingService;
111
+ /**
112
+ * Extracts and records thought from thought content.
113
+ */
114
+ private recordThoughtFromContent;
115
+ /**
116
+ * Truncates the chunkStream right before the second function call to a
117
+ * function that mutates state. This may involve trimming parts from a chunk
118
+ * as well as omtting some chunks altogether.
119
+ *
120
+ * We do this because it improves tool call quality if the model gets
121
+ * feedback from one mutating function call before it makes the next one.
122
+ */
123
+ private stopBeforeSecondMutator;
124
+ private isMutatorFunctionCall;
119
125
  }
120
126
  /** Visible for Testing */
121
127
  export declare function isSchemaDepthError(errorMessage: string): boolean;
@@ -3,13 +3,29 @@
3
3
  * Copyright 2025 Google LLC
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
+ // DISCLAIMER: This is a copied version of https://github.com/googleapis/js-genai/blob/main/src/chats.ts with the intention of working around a key bug
7
+ // where function responses are not treated as "valid" responses: https://b.corp.google.com/issues/420354090
8
+ import { GenerateContentResponse, FinishReason, } from '@google/genai';
9
+ import { toParts } from '../code_assist/converter.js';
6
10
  import { createUserContent } from '@google/genai';
7
11
  import { retryWithBackoff } from '../utils/retry.js';
8
- import { AuthType } from './contentGenerator.js';
9
- import { DEFAULT_GEMINI_FLASH_MODEL } from '../config/models.js';
10
- import { hasCycleInSchema } from '../tools/tools.js';
12
+ import { DEFAULT_GEMINI_FLASH_MODEL, getEffectiveModel, } from '../config/models.js';
13
+ import { hasCycleInSchema, MUTATOR_KINDS } from '../tools/tools.js';
11
14
  import { logContentRetry, logContentRetryFailure, logInvalidChunk, } from '../telemetry/loggers.js';
15
+ import { ChatRecordingService } from '../services/chatRecordingService.js';
12
16
  import { ContentRetryEvent, ContentRetryFailureEvent, InvalidChunkEvent, } from '../telemetry/types.js';
17
+ import { handleFallback } from '../fallback/handler.js';
18
+ import { isFunctionResponse } from '../utils/messageInspectors.js';
19
+ import { partListUnionToString } from './geminiRequest.js';
20
+ import { uiTelemetryService } from '../telemetry/uiTelemetry.js';
21
+ export var StreamEventType;
22
+ (function (StreamEventType) {
23
+ /** A regular content chunk from the API. */
24
+ StreamEventType["CHUNK"] = "chunk";
25
+ /** A signal that a retry is about to happen. The UI should discard any partial
26
+ * content from the attempt that just failed. */
27
+ StreamEventType["RETRY"] = "retry";
28
+ })(StreamEventType || (StreamEventType = {}));
13
29
  const INVALID_CONTENT_RETRY_OPTIONS = {
14
30
  maxAttempts: 3, // 1 initial call + 2 retries
15
31
  initialDelayMs: 500,
@@ -27,6 +43,16 @@ function isValidResponse(response) {
27
43
  }
28
44
  return isValidContent(content);
29
45
  }
46
+ export function isValidNonThoughtTextPart(part) {
47
+ return (typeof part.text === 'string' &&
48
+ !part.thought &&
49
+ // Technically, the model should never generate parts that have text and
50
+ // any of these but we don't trust them so check anyways.
51
+ !part.functionCall &&
52
+ !part.functionResponse &&
53
+ !part.inlineData &&
54
+ !part.fileData);
55
+ }
30
56
  function isValidContent(content) {
31
57
  if (content.parts === undefined || content.parts.length === 0) {
32
58
  return false;
@@ -110,141 +136,23 @@ export class EmptyStreamError extends Error {
110
136
  */
111
137
  export class GeminiChat {
112
138
  config;
113
- contentGenerator;
114
139
  generationConfig;
115
140
  history;
116
141
  // A promise to represent the current state of the message being sent to the
117
142
  // model.
118
143
  sendPromise = Promise.resolve();
119
- constructor(config, contentGenerator, generationConfig = {}, history = []) {
144
+ chatRecordingService;
145
+ constructor(config, generationConfig = {}, history = []) {
120
146
  this.config = config;
121
- this.contentGenerator = contentGenerator;
122
147
  this.generationConfig = generationConfig;
123
148
  this.history = history;
124
149
  validateHistory(history);
125
- }
126
- /**
127
- * Handles falling back to Flash model when persistent 429 errors occur for OAuth users.
128
- * Uses a fallback handler if provided by the config; otherwise, returns null.
129
- */
130
- async handleFlashFallback(authType, error) {
131
- // Only handle fallback for OAuth users
132
- if (authType !== AuthType.LOGIN_WITH_GOOGLE) {
133
- return null;
134
- }
135
- const currentModel = this.config.getModel();
136
- const fallbackModel = DEFAULT_GEMINI_FLASH_MODEL;
137
- // Don't fallback if already using Flash model
138
- if (currentModel === fallbackModel) {
139
- return null;
140
- }
141
- // Check if config has a fallback handler (set by CLI package)
142
- const fallbackHandler = this.config.flashFallbackHandler;
143
- if (typeof fallbackHandler === 'function') {
144
- try {
145
- const accepted = await fallbackHandler(currentModel, fallbackModel, error);
146
- if (accepted !== false && accepted !== null) {
147
- this.config.setModel(fallbackModel);
148
- this.config.setFallbackMode(true);
149
- return fallbackModel;
150
- }
151
- // Check if the model was switched manually in the handler
152
- if (this.config.getModel() === fallbackModel) {
153
- return null; // Model was switched but don't continue with current prompt
154
- }
155
- }
156
- catch (error) {
157
- console.warn('Flash fallback handler failed:', error);
158
- }
159
- }
160
- return null;
150
+ this.chatRecordingService = new ChatRecordingService(config);
151
+ this.chatRecordingService.initialize();
161
152
  }
162
153
  setSystemInstruction(sysInstr) {
163
154
  this.generationConfig.systemInstruction = sysInstr;
164
155
  }
165
- /**
166
- * Sends a message to the model and returns the response.
167
- *
168
- * @remarks
169
- * This method will wait for the previous message to be processed before
170
- * sending the next message.
171
- *
172
- * @see {@link Chat#sendMessageStream} for streaming method.
173
- * @param params - parameters for sending messages within a chat session.
174
- * @returns The model's response.
175
- *
176
- * @example
177
- * ```ts
178
- * const chat = ai.chats.create({model: 'gemini-2.0-flash'});
179
- * const response = await chat.sendMessage({
180
- * message: 'Why is the sky blue?'
181
- * });
182
- * console.log(response.text);
183
- * ```
184
- */
185
- async sendMessage(params, prompt_id) {
186
- await this.sendPromise;
187
- const userContent = createUserContent(params.message);
188
- const requestContents = this.getHistory(true).concat(userContent);
189
- let response;
190
- try {
191
- const apiCall = () => {
192
- const modelToUse = this.config.getModel() || DEFAULT_GEMINI_FLASH_MODEL;
193
- // Prevent Flash model calls immediately after quota error
194
- if (this.config.getQuotaErrorOccurred() &&
195
- modelToUse === DEFAULT_GEMINI_FLASH_MODEL) {
196
- throw new Error('Please submit a new query to continue with the Flash model.');
197
- }
198
- return this.contentGenerator.generateContent({
199
- model: modelToUse,
200
- contents: requestContents,
201
- config: { ...this.generationConfig, ...params.config },
202
- }, prompt_id);
203
- };
204
- response = await retryWithBackoff(apiCall, {
205
- shouldRetry: (error) => {
206
- // Check for known error messages and codes.
207
- if (error instanceof Error && error.message) {
208
- if (isSchemaDepthError(error.message))
209
- return false;
210
- if (error.message.includes('429'))
211
- return true;
212
- if (error.message.match(/5\d{2}/))
213
- return true;
214
- }
215
- return false; // Don't retry other errors by default
216
- },
217
- onPersistent429: async (authType, error) => await this.handleFlashFallback(authType, error),
218
- authType: this.config.getContentGeneratorConfig()?.authType,
219
- });
220
- this.sendPromise = (async () => {
221
- const outputContent = response.candidates?.[0]?.content;
222
- const modelOutput = outputContent ? [outputContent] : [];
223
- // Because the AFC input contains the entire curated chat history in
224
- // addition to the new user input, we need to truncate the AFC history
225
- // to deduplicate the existing chat history.
226
- const fullAutomaticFunctionCallingHistory = response.automaticFunctionCallingHistory;
227
- const index = this.getHistory(true).length;
228
- let automaticFunctionCallingHistory = [];
229
- if (fullAutomaticFunctionCallingHistory != null) {
230
- automaticFunctionCallingHistory =
231
- fullAutomaticFunctionCallingHistory.slice(index) ?? [];
232
- }
233
- this.recordHistory(userContent, modelOutput, automaticFunctionCallingHistory);
234
- })();
235
- await this.sendPromise.catch((error) => {
236
- // Resets sendPromise to avoid subsequent calls failing
237
- this.sendPromise = Promise.resolve();
238
- // Re-throw the error so the caller knows something went wrong.
239
- throw error;
240
- });
241
- return response;
242
- }
243
- catch (error) {
244
- this.sendPromise = Promise.resolve();
245
- throw error;
246
- }
247
- }
248
156
  /**
249
157
  * Sends a message to the model and returns the response in chunks.
250
158
  *
@@ -267,7 +175,7 @@ export class GeminiChat {
267
175
  * }
268
176
  * ```
269
177
  */
270
- async sendMessageStream(params, prompt_id) {
178
+ async sendMessageStream(model, params, prompt_id) {
271
179
  await this.sendPromise;
272
180
  let streamDoneResolver;
273
181
  const streamDonePromise = new Promise((resolve) => {
@@ -275,6 +183,19 @@ export class GeminiChat {
275
183
  });
276
184
  this.sendPromise = streamDonePromise;
277
185
  const userContent = createUserContent(params.message);
186
+ // Record user input - capture complete message with all parts (text, files, images, etc.)
187
+ // but skip recording function responses (tool call results) as they should be stored in tool call records
188
+ if (!isFunctionResponse(userContent)) {
189
+ const userMessage = Array.isArray(params.message)
190
+ ? params.message
191
+ : [params.message];
192
+ const userMessageContent = partListUnionToString(toParts(userMessage));
193
+ this.chatRecordingService.recordMessage({
194
+ model,
195
+ type: 'user',
196
+ content: userMessageContent,
197
+ });
198
+ }
278
199
  // Add user content to history ONCE before any attempts.
279
200
  this.history.push(userContent);
280
201
  const requestContents = this.getHistory(true);
@@ -285,9 +206,12 @@ export class GeminiChat {
285
206
  let lastError = new Error('Request failed after all retries.');
286
207
  for (let attempt = 0; attempt < INVALID_CONTENT_RETRY_OPTIONS.maxAttempts; attempt++) {
287
208
  try {
288
- const stream = await self.makeApiCallAndProcessStream(requestContents, params, prompt_id, userContent);
209
+ if (attempt > 0) {
210
+ yield { type: StreamEventType.RETRY };
211
+ }
212
+ const stream = await self.makeApiCallAndProcessStream(model, requestContents, params, prompt_id);
289
213
  for await (const chunk of stream) {
290
- yield chunk;
214
+ yield { type: StreamEventType.CHUNK, value: chunk };
291
215
  }
292
216
  lastError = null;
293
217
  break;
@@ -323,19 +247,20 @@ export class GeminiChat {
323
247
  }
324
248
  })();
325
249
  }
326
- async makeApiCallAndProcessStream(requestContents, params, prompt_id, userContent) {
250
+ async makeApiCallAndProcessStream(model, requestContents, params, prompt_id) {
327
251
  const apiCall = () => {
328
- const modelToUse = this.config.getModel();
252
+ const modelToUse = getEffectiveModel(this.config.isInFallbackMode(), model);
329
253
  if (this.config.getQuotaErrorOccurred() &&
330
254
  modelToUse === DEFAULT_GEMINI_FLASH_MODEL) {
331
255
  throw new Error('Please submit a new query to continue with the Flash model.');
332
256
  }
333
- return this.contentGenerator.generateContentStream({
257
+ return this.config.getContentGenerator().generateContentStream({
334
258
  model: modelToUse,
335
259
  contents: requestContents,
336
260
  config: { ...this.generationConfig, ...params.config },
337
261
  }, prompt_id);
338
262
  };
263
+ const onPersistent429Callback = async (authType, error) => await handleFallback(this.config, model, authType, error);
339
264
  const streamResponse = await retryWithBackoff(apiCall, {
340
265
  shouldRetry: (error) => {
341
266
  if (error instanceof Error && error.message) {
@@ -348,10 +273,10 @@ export class GeminiChat {
348
273
  }
349
274
  return false;
350
275
  },
351
- onPersistent429: async (authType, error) => await this.handleFlashFallback(authType, error),
276
+ onPersistent429: onPersistent429Callback,
352
277
  authType: this.config.getContentGeneratorConfig()?.authType,
353
278
  });
354
- return this.processStreamResponse(streamResponse, userContent);
279
+ return this.processStreamResponse(model, streamResponse);
355
280
  }
356
281
  /**
357
282
  * Returns the chat history.
@@ -399,6 +324,22 @@ export class GeminiChat {
399
324
  setHistory(history) {
400
325
  this.history = history;
401
326
  }
327
+ stripThoughtsFromHistory() {
328
+ this.history = this.history.map((content) => {
329
+ const newContent = { ...content };
330
+ if (newContent.parts) {
331
+ newContent.parts = newContent.parts.map((part) => {
332
+ if (part && typeof part === 'object' && 'thoughtSignature' in part) {
333
+ const newPart = { ...part };
334
+ delete newPart.thoughtSignature;
335
+ return newPart;
336
+ }
337
+ return part;
338
+ });
339
+ }
340
+ return newContent;
341
+ });
342
+ }
402
343
  setTools(tools) {
403
344
  this.generationConfig.tools = tools;
404
345
  }
@@ -424,117 +365,166 @@ export class GeminiChat {
424
365
  }
425
366
  }
426
367
  }
427
- async *processStreamResponse(streamResponse, userInput) {
368
+ async *processStreamResponse(model, streamResponse) {
428
369
  const modelResponseParts = [];
429
- let isStreamInvalid = false;
430
370
  let hasReceivedAnyChunk = false;
431
- for await (const chunk of streamResponse) {
371
+ let hasReceivedValidChunk = false;
372
+ let hasToolCall = false;
373
+ let lastChunk = null;
374
+ let lastChunkIsInvalid = false;
375
+ for await (const chunk of this.stopBeforeSecondMutator(streamResponse)) {
432
376
  hasReceivedAnyChunk = true;
377
+ lastChunk = chunk;
433
378
  if (isValidResponse(chunk)) {
379
+ hasReceivedValidChunk = true;
380
+ lastChunkIsInvalid = false;
434
381
  const content = chunk.candidates?.[0]?.content;
435
382
  if (content?.parts) {
436
- modelResponseParts.push(...content.parts);
383
+ if (content.parts.some((part) => part.thought)) {
384
+ // Record thoughts
385
+ this.recordThoughtFromContent(content);
386
+ }
387
+ if (content.parts.some((part) => part.functionCall)) {
388
+ hasToolCall = true;
389
+ }
390
+ modelResponseParts.push(...content.parts.filter((part) => !part.thought));
437
391
  }
438
392
  }
439
393
  else {
440
394
  logInvalidChunk(this.config, new InvalidChunkEvent('Invalid chunk received from stream.'));
441
- isStreamInvalid = true;
395
+ lastChunkIsInvalid = true;
442
396
  }
443
- yield chunk;
397
+ // Record token usage if this chunk has usageMetadata
398
+ if (chunk.usageMetadata) {
399
+ this.chatRecordingService.recordMessageTokens(chunk.usageMetadata);
400
+ if (chunk.usageMetadata.promptTokenCount !== undefined) {
401
+ uiTelemetryService.setLastPromptTokenCount(chunk.usageMetadata.promptTokenCount);
402
+ }
403
+ }
404
+ yield chunk; // Yield every chunk to the UI immediately.
444
405
  }
445
- if (isStreamInvalid || !hasReceivedAnyChunk) {
446
- throw new EmptyStreamError('Model stream was invalid or completed without valid content.');
406
+ if (!hasReceivedAnyChunk) {
407
+ throw new EmptyStreamError('Model stream completed without any chunks.');
447
408
  }
448
- // Bundle all streamed parts into a single Content object
449
- const modelOutput = modelResponseParts.length > 0
450
- ? [{ role: 'model', parts: modelResponseParts }]
451
- : [];
452
- // Pass the raw, bundled data to the new, robust recordHistory
453
- this.recordHistory(userInput, modelOutput);
454
- }
455
- recordHistory(userInput, modelOutput, automaticFunctionCallingHistory) {
456
- // Part 1: Handle the user's turn.
457
- if (automaticFunctionCallingHistory &&
458
- automaticFunctionCallingHistory.length > 0) {
459
- this.history.push(...extractCuratedHistory(automaticFunctionCallingHistory));
409
+ const hasFinishReason = lastChunk?.candidates?.some((candidate) => candidate.finishReason);
410
+ // Stream validation logic: A stream is considered successful if:
411
+ // 1. There's a tool call (tool calls can end without explicit finish reasons), OR
412
+ // 2. There's a finish reason AND the last chunk is valid (or we haven't received any valid chunks)
413
+ //
414
+ // We throw an error only when there's no tool call AND:
415
+ // - No finish reason, OR
416
+ // - Last chunk is invalid after receiving valid content
417
+ if (!hasToolCall &&
418
+ (!hasFinishReason || (lastChunkIsInvalid && !hasReceivedValidChunk))) {
419
+ throw new EmptyStreamError('Model stream ended with an invalid chunk or missing finish reason.');
460
420
  }
461
- else {
462
- if (this.history.length === 0 ||
463
- this.history[this.history.length - 1] !== userInput) {
464
- const lastTurn = this.history[this.history.length - 1];
465
- // The only time we don't push is if it's the *exact same* object,
466
- // which happens in streaming where we add it preemptively.
467
- if (lastTurn !== userInput) {
468
- if (lastTurn?.role === 'user') {
469
- // This is an invalid sequence.
470
- throw new Error('Cannot add a user turn after another user turn.');
471
- }
472
- this.history.push(userInput);
473
- }
421
+ // Record model response text from the collected parts
422
+ if (modelResponseParts.length > 0) {
423
+ const responseText = modelResponseParts
424
+ .filter((part) => part.text)
425
+ .map((part) => part.text)
426
+ .join('');
427
+ if (responseText.trim()) {
428
+ this.chatRecordingService.recordMessage({
429
+ model,
430
+ type: 'gemini',
431
+ content: responseText,
432
+ });
474
433
  }
475
434
  }
476
- // Part 2: Process the model output into a final, consolidated list of turns.
477
- const finalModelTurns = [];
478
- for (const content of modelOutput) {
479
- // A. Preserve malformed content that has no 'parts' array.
480
- if (!content.parts) {
481
- finalModelTurns.push(content);
482
- continue;
483
- }
484
- // B. Filter out 'thought' parts.
485
- const visibleParts = content.parts.filter((part) => !part.thought);
486
- const newTurn = { ...content, parts: visibleParts };
487
- const lastTurnInFinal = finalModelTurns[finalModelTurns.length - 1];
488
- // Consolidate this new turn with the PREVIOUS turn if they are adjacent model turns.
489
- if (lastTurnInFinal &&
490
- lastTurnInFinal.role === 'model' &&
491
- newTurn.role === 'model' &&
492
- lastTurnInFinal.parts && // SAFETY CHECK: Ensure the destination has a parts array.
493
- newTurn.parts) {
494
- lastTurnInFinal.parts.push(...newTurn.parts);
435
+ // String thoughts and consolidate text parts.
436
+ const consolidatedParts = [];
437
+ for (const part of modelResponseParts) {
438
+ const lastPart = consolidatedParts[consolidatedParts.length - 1];
439
+ if (lastPart?.text &&
440
+ isValidNonThoughtTextPart(lastPart) &&
441
+ isValidNonThoughtTextPart(part)) {
442
+ lastPart.text += part.text;
495
443
  }
496
444
  else {
497
- finalModelTurns.push(newTurn);
445
+ consolidatedParts.push(part);
498
446
  }
499
447
  }
500
- // Part 3: Add the processed model turns to the history, with one final consolidation pass.
501
- if (finalModelTurns.length > 0) {
502
- // Re-consolidate parts within any turns that were merged in the previous step.
503
- for (const turn of finalModelTurns) {
504
- if (turn.parts && turn.parts.length > 1) {
505
- const consolidatedParts = [];
506
- for (const part of turn.parts) {
507
- const lastPart = consolidatedParts[consolidatedParts.length - 1];
508
- if (lastPart &&
509
- // Ensure lastPart is a pure text part
510
- typeof lastPart.text === 'string' &&
511
- !lastPart.functionCall &&
512
- !lastPart.functionResponse &&
513
- !lastPart.inlineData &&
514
- !lastPart.fileData &&
515
- !lastPart.thought &&
516
- // Ensure current part is a pure text part
517
- typeof part.text === 'string' &&
518
- !part.functionCall &&
519
- !part.functionResponse &&
520
- !part.inlineData &&
521
- !part.fileData &&
522
- !part.thought) {
523
- lastPart.text += part.text;
524
- }
525
- else {
526
- consolidatedParts.push({ ...part });
527
- }
448
+ this.history.push({ role: 'model', parts: consolidatedParts });
449
+ }
450
+ /**
451
+ * Gets the chat recording service instance.
452
+ */
453
+ getChatRecordingService() {
454
+ return this.chatRecordingService;
455
+ }
456
+ /**
457
+ * Extracts and records thought from thought content.
458
+ */
459
+ recordThoughtFromContent(content) {
460
+ if (!content.parts || content.parts.length === 0) {
461
+ return;
462
+ }
463
+ const thoughtPart = content.parts[0];
464
+ if (thoughtPart.text) {
465
+ // Extract subject and description using the same logic as turn.ts
466
+ const rawText = thoughtPart.text;
467
+ const subjectStringMatches = rawText.match(/\*\*(.*?)\*\*/s);
468
+ const subject = subjectStringMatches
469
+ ? subjectStringMatches[1].trim()
470
+ : '';
471
+ const description = rawText.replace(/\*\*(.*?)\*\*/s, '').trim();
472
+ this.chatRecordingService.recordThought({
473
+ subject,
474
+ description,
475
+ });
476
+ }
477
+ }
478
+ /**
479
+ * Truncates the chunkStream right before the second function call to a
480
+ * function that mutates state. This may involve trimming parts from a chunk
481
+ * as well as omtting some chunks altogether.
482
+ *
483
+ * We do this because it improves tool call quality if the model gets
484
+ * feedback from one mutating function call before it makes the next one.
485
+ */
486
+ async *stopBeforeSecondMutator(chunkStream) {
487
+ let foundMutatorFunctionCall = false;
488
+ for await (const chunk of chunkStream) {
489
+ const candidate = chunk.candidates?.[0];
490
+ const content = candidate?.content;
491
+ if (!candidate || !content?.parts) {
492
+ yield chunk;
493
+ continue;
494
+ }
495
+ const truncatedParts = [];
496
+ for (const part of content.parts) {
497
+ if (this.isMutatorFunctionCall(part)) {
498
+ if (foundMutatorFunctionCall) {
499
+ // This is the second mutator call.
500
+ // Truncate and return immedaitely.
501
+ const newChunk = new GenerateContentResponse();
502
+ newChunk.candidates = [
503
+ {
504
+ ...candidate,
505
+ content: {
506
+ ...content,
507
+ parts: truncatedParts,
508
+ },
509
+ finishReason: FinishReason.STOP,
510
+ },
511
+ ];
512
+ yield newChunk;
513
+ return;
528
514
  }
529
- turn.parts = consolidatedParts;
515
+ foundMutatorFunctionCall = true;
530
516
  }
517
+ truncatedParts.push(part);
531
518
  }
532
- this.history.push(...finalModelTurns);
519
+ yield chunk;
533
520
  }
534
- else {
535
- // If, after all processing, there's NO model output, add the placeholder.
536
- this.history.push({ role: 'model', parts: [] });
521
+ }
522
+ isMutatorFunctionCall(part) {
523
+ if (!part?.functionCall?.name) {
524
+ return false;
537
525
  }
526
+ const tool = this.config.getToolRegistry().getTool(part.functionCall.name);
527
+ return !!tool && MUTATOR_KINDS.includes(tool.kind);
538
528
  }
539
529
  }
540
530
  /** Visible for Testing */