@google/gemini-cli-core 0.1.12 → 0.1.13-nightly.250727.3e81359c

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 (309) hide show
  1. package/README.md +30 -3
  2. package/dist/google-gemini-cli-core-0.1.13.tgz +0 -0
  3. package/dist/src/code_assist/codeAssist.js +2 -2
  4. package/dist/src/code_assist/codeAssist.js.map +1 -1
  5. package/dist/src/code_assist/oauth2.js +46 -5
  6. package/dist/src/code_assist/oauth2.js.map +1 -1
  7. package/dist/src/code_assist/oauth2.test.js +107 -10
  8. package/dist/src/code_assist/oauth2.test.js.map +1 -1
  9. package/dist/src/code_assist/server.d.ts +4 -6
  10. package/dist/src/code_assist/server.js +4 -69
  11. package/dist/src/code_assist/server.js.map +1 -1
  12. package/dist/src/code_assist/server.test.js +10 -2
  13. package/dist/src/code_assist/server.test.js.map +1 -1
  14. package/dist/src/code_assist/setup.d.ts +6 -1
  15. package/dist/src/code_assist/setup.js +4 -1
  16. package/dist/src/code_assist/setup.js.map +1 -1
  17. package/dist/src/code_assist/setup.test.js +4 -1
  18. package/dist/src/code_assist/setup.test.js.map +1 -1
  19. package/dist/src/code_assist/types.d.ts +2 -2
  20. package/dist/src/config/config.d.ts +55 -11
  21. package/dist/src/config/config.js +85 -33
  22. package/dist/src/config/config.js.map +1 -1
  23. package/dist/src/config/config.test.js +29 -26
  24. package/dist/src/config/config.test.js.map +1 -1
  25. package/dist/src/config/flashFallback.test.js +1 -1
  26. package/dist/src/config/flashFallback.test.js.map +1 -1
  27. package/dist/src/core/client.d.ts +8 -3
  28. package/dist/src/core/client.js +62 -3
  29. package/dist/src/core/client.js.map +1 -1
  30. package/dist/src/core/client.test.js +145 -37
  31. package/dist/src/core/client.test.js.map +1 -1
  32. package/dist/src/core/contentGenerator.d.ts +3 -2
  33. package/dist/src/core/contentGenerator.js +5 -4
  34. package/dist/src/core/contentGenerator.js.map +1 -1
  35. package/dist/src/core/contentGenerator.test.js +12 -5
  36. package/dist/src/core/contentGenerator.test.js.map +1 -1
  37. package/dist/src/core/coreToolScheduler.js +14 -1
  38. package/dist/src/core/coreToolScheduler.js.map +1 -1
  39. package/dist/src/core/coreToolScheduler.test.js +84 -2
  40. package/dist/src/core/coreToolScheduler.test.js.map +1 -1
  41. package/dist/src/core/geminiChat.d.ts +4 -3
  42. package/dist/src/core/geminiChat.js +8 -11
  43. package/dist/src/core/geminiChat.js.map +1 -1
  44. package/dist/src/core/geminiRequest.js +2 -37
  45. package/dist/src/core/geminiRequest.js.map +1 -1
  46. package/dist/src/core/logger.js +6 -0
  47. package/dist/src/core/logger.js.map +1 -1
  48. package/dist/src/core/logger.test.js +1 -1
  49. package/dist/src/core/logger.test.js.map +1 -1
  50. package/dist/src/core/modelCheck.d.ts +1 -1
  51. package/dist/src/core/modelCheck.js +10 -3
  52. package/dist/src/core/modelCheck.js.map +1 -1
  53. package/dist/src/core/nonInteractiveToolExecutor.test.js +8 -5
  54. package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
  55. package/dist/src/core/prompts.js +42 -18
  56. package/dist/src/core/prompts.js.map +1 -1
  57. package/dist/src/core/prompts.test.js +121 -4
  58. package/dist/src/core/prompts.test.js.map +1 -1
  59. package/dist/src/core/turn.d.ts +12 -3
  60. package/dist/src/core/turn.js +10 -0
  61. package/dist/src/core/turn.js.map +1 -1
  62. package/dist/src/core/turn.test.js +129 -0
  63. package/dist/src/core/turn.test.js.map +1 -1
  64. package/dist/src/ide/ide-client.d.ts +28 -0
  65. package/dist/src/ide/ide-client.js +88 -0
  66. package/dist/src/ide/ide-client.js.map +1 -0
  67. package/dist/src/ide/ideContext.d.ts +174 -0
  68. package/dist/src/ide/ideContext.js +101 -0
  69. package/dist/src/ide/ideContext.js.map +1 -0
  70. package/dist/src/ide/ideContext.test.js +111 -0
  71. package/dist/src/ide/ideContext.test.js.map +1 -0
  72. package/dist/src/index.d.ts +15 -0
  73. package/dist/src/index.js +17 -0
  74. package/dist/src/index.js.map +1 -1
  75. package/dist/src/mcp/google-auth-provider.d.ts +23 -0
  76. package/dist/src/mcp/google-auth-provider.js +63 -0
  77. package/dist/src/mcp/google-auth-provider.js.map +1 -0
  78. package/dist/src/mcp/google-auth-provider.test.d.ts +6 -0
  79. package/dist/src/mcp/google-auth-provider.test.js +54 -0
  80. package/dist/src/mcp/google-auth-provider.test.js.map +1 -0
  81. package/dist/src/mcp/oauth-provider.d.ts +142 -0
  82. package/dist/src/mcp/oauth-provider.js +446 -0
  83. package/dist/src/mcp/oauth-provider.js.map +1 -0
  84. package/dist/src/mcp/oauth-provider.test.d.ts +6 -0
  85. package/dist/src/mcp/oauth-provider.test.js +520 -0
  86. package/dist/src/mcp/oauth-provider.test.js.map +1 -0
  87. package/dist/src/mcp/oauth-token-storage.d.ts +81 -0
  88. package/dist/src/mcp/oauth-token-storage.js +149 -0
  89. package/dist/src/mcp/oauth-token-storage.js.map +1 -0
  90. package/dist/src/mcp/oauth-token-storage.test.d.ts +6 -0
  91. package/dist/src/mcp/oauth-token-storage.test.js +205 -0
  92. package/dist/src/mcp/oauth-token-storage.test.js.map +1 -0
  93. package/dist/src/mcp/oauth-utils.d.ts +109 -0
  94. package/dist/src/mcp/oauth-utils.js +183 -0
  95. package/dist/src/mcp/oauth-utils.js.map +1 -0
  96. package/dist/src/mcp/oauth-utils.test.d.ts +6 -0
  97. package/dist/src/mcp/oauth-utils.test.js +144 -0
  98. package/dist/src/mcp/oauth-utils.test.js.map +1 -0
  99. package/dist/src/prompts/mcp-prompts.d.ts +8 -0
  100. package/dist/src/prompts/mcp-prompts.js +13 -0
  101. package/dist/src/prompts/mcp-prompts.js.map +1 -0
  102. package/dist/src/prompts/prompt-registry.d.ts +26 -0
  103. package/dist/src/prompts/prompt-registry.js +47 -0
  104. package/dist/src/prompts/prompt-registry.js.map +1 -0
  105. package/dist/src/services/fileDiscoveryService.test.js +101 -60
  106. package/dist/src/services/fileDiscoveryService.test.js.map +1 -1
  107. package/dist/src/services/gitService.js +1 -5
  108. package/dist/src/services/gitService.js.map +1 -1
  109. package/dist/src/services/gitService.test.js +68 -92
  110. package/dist/src/services/gitService.test.js.map +1 -1
  111. package/dist/src/services/loopDetectionService.d.ts +94 -0
  112. package/dist/src/services/loopDetectionService.js +318 -0
  113. package/dist/src/services/loopDetectionService.js.map +1 -0
  114. package/dist/src/services/loopDetectionService.test.d.ts +6 -0
  115. package/dist/src/services/loopDetectionService.test.js +266 -0
  116. package/dist/src/services/loopDetectionService.test.js.map +1 -0
  117. package/dist/src/services/shellExecutionService.d.ts +70 -0
  118. package/dist/src/services/shellExecutionService.js +152 -0
  119. package/dist/src/services/shellExecutionService.js.map +1 -0
  120. package/dist/src/services/shellExecutionService.test.d.ts +6 -0
  121. package/dist/src/services/shellExecutionService.test.js +258 -0
  122. package/dist/src/services/shellExecutionService.test.js.map +1 -0
  123. package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +5 -1
  124. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +69 -4
  125. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
  126. package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +4 -1
  127. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +9 -0
  128. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
  129. package/dist/src/telemetry/constants.d.ts +1 -0
  130. package/dist/src/telemetry/constants.js +1 -0
  131. package/dist/src/telemetry/constants.js.map +1 -1
  132. package/dist/src/telemetry/file-exporters.d.ts +28 -0
  133. package/dist/src/telemetry/file-exporters.js +62 -0
  134. package/dist/src/telemetry/file-exporters.js.map +1 -0
  135. package/dist/src/telemetry/integration.test.circular.d.ts +6 -0
  136. package/dist/src/telemetry/integration.test.circular.js +53 -0
  137. package/dist/src/telemetry/integration.test.circular.js.map +1 -0
  138. package/dist/src/telemetry/loggers.d.ts +3 -1
  139. package/dist/src/telemetry/loggers.js +34 -2
  140. package/dist/src/telemetry/loggers.js.map +1 -1
  141. package/dist/src/telemetry/loggers.test.circular.d.ts +6 -0
  142. package/dist/src/telemetry/loggers.test.circular.js +100 -0
  143. package/dist/src/telemetry/loggers.test.circular.js.map +1 -0
  144. package/dist/src/telemetry/sdk.js +17 -6
  145. package/dist/src/telemetry/sdk.js.map +1 -1
  146. package/dist/src/telemetry/types.d.ts +19 -1
  147. package/dist/src/telemetry/types.js +28 -0
  148. package/dist/src/telemetry/types.js.map +1 -1
  149. package/dist/src/telemetry/uiTelemetry.d.ts +1 -0
  150. package/dist/src/telemetry/uiTelemetry.js +7 -0
  151. package/dist/src/telemetry/uiTelemetry.js.map +1 -1
  152. package/dist/src/telemetry/uiTelemetry.test.js +92 -0
  153. package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
  154. package/dist/src/tools/edit.d.ts +7 -12
  155. package/dist/src/tools/edit.js +34 -32
  156. package/dist/src/tools/edit.js.map +1 -1
  157. package/dist/src/tools/edit.test.js +12 -0
  158. package/dist/src/tools/edit.test.js.map +1 -1
  159. package/dist/src/tools/glob.d.ts +1 -14
  160. package/dist/src/tools/glob.js +13 -36
  161. package/dist/src/tools/glob.js.map +1 -1
  162. package/dist/src/tools/glob.test.js +11 -7
  163. package/dist/src/tools/glob.test.js.map +1 -1
  164. package/dist/src/tools/grep.d.ts +3 -6
  165. package/dist/src/tools/grep.js +12 -18
  166. package/dist/src/tools/grep.js.map +1 -1
  167. package/dist/src/tools/grep.test.js +9 -6
  168. package/dist/src/tools/grep.test.js.map +1 -1
  169. package/dist/src/tools/ls.d.ts +6 -14
  170. package/dist/src/tools/ls.js +47 -40
  171. package/dist/src/tools/ls.js.map +1 -1
  172. package/dist/src/tools/mcp-client.d.ts +83 -1
  173. package/dist/src/tools/mcp-client.js +613 -148
  174. package/dist/src/tools/mcp-client.js.map +1 -1
  175. package/dist/src/tools/mcp-client.test.js +211 -616
  176. package/dist/src/tools/mcp-client.test.js.map +1 -1
  177. package/dist/src/tools/mcp-tool.d.ts +11 -5
  178. package/dist/src/tools/mcp-tool.js +34 -10
  179. package/dist/src/tools/mcp-tool.js.map +1 -1
  180. package/dist/src/tools/mcp-tool.test.js +74 -24
  181. package/dist/src/tools/mcp-tool.test.js.map +1 -1
  182. package/dist/src/tools/memoryTool.js +2 -2
  183. package/dist/src/tools/memoryTool.js.map +1 -1
  184. package/dist/src/tools/modifiable-tool.test.js +51 -62
  185. package/dist/src/tools/modifiable-tool.test.js.map +1 -1
  186. package/dist/src/tools/read-file.d.ts +3 -3
  187. package/dist/src/tools/read-file.js +10 -10
  188. package/dist/src/tools/read-file.js.map +1 -1
  189. package/dist/src/tools/read-file.test.js +100 -70
  190. package/dist/src/tools/read-file.test.js.map +1 -1
  191. package/dist/src/tools/read-many-files.d.ts +6 -10
  192. package/dist/src/tools/read-many-files.js +74 -43
  193. package/dist/src/tools/read-many-files.js.map +1 -1
  194. package/dist/src/tools/read-many-files.test.js +7 -3
  195. package/dist/src/tools/read-many-files.test.js.map +1 -1
  196. package/dist/src/tools/shell.d.ts +3 -23
  197. package/dist/src/tools/shell.js +169 -293
  198. package/dist/src/tools/shell.js.map +1 -1
  199. package/dist/src/tools/shell.test.js +255 -333
  200. package/dist/src/tools/shell.test.js.map +1 -1
  201. package/dist/src/tools/tool-registry.d.ts +13 -2
  202. package/dist/src/tools/tool-registry.js +57 -10
  203. package/dist/src/tools/tool-registry.js.map +1 -1
  204. package/dist/src/tools/tool-registry.test.js +112 -41
  205. package/dist/src/tools/tool-registry.test.js.map +1 -1
  206. package/dist/src/tools/tools.d.ts +37 -2
  207. package/dist/src/tools/tools.js +25 -2
  208. package/dist/src/tools/tools.js.map +1 -1
  209. package/dist/src/tools/web-fetch.js +7 -2
  210. package/dist/src/tools/web-fetch.js.map +1 -1
  211. package/dist/src/tools/web-fetch.test.js +1 -0
  212. package/dist/src/tools/web-fetch.test.js.map +1 -1
  213. package/dist/src/tools/web-search.js +2 -2
  214. package/dist/src/tools/web-search.js.map +1 -1
  215. package/dist/src/tools/write-file.d.ts +0 -8
  216. package/dist/src/tools/write-file.js +14 -23
  217. package/dist/src/tools/write-file.js.map +1 -1
  218. package/dist/src/utils/bfsFileSearch.d.ts +2 -0
  219. package/dist/src/utils/bfsFileSearch.js +4 -1
  220. package/dist/src/utils/bfsFileSearch.js.map +1 -1
  221. package/dist/src/utils/bfsFileSearch.test.js +108 -105
  222. package/dist/src/utils/bfsFileSearch.test.js.map +1 -1
  223. package/dist/src/utils/browser.d.ts +13 -0
  224. package/dist/src/utils/browser.js +49 -0
  225. package/dist/src/utils/browser.js.map +1 -0
  226. package/dist/src/utils/editCorrector.js +4 -4
  227. package/dist/src/utils/editCorrector.js.map +1 -1
  228. package/dist/src/utils/editCorrector.test.js +1 -1
  229. package/dist/src/utils/editor.js +16 -10
  230. package/dist/src/utils/editor.js.map +1 -1
  231. package/dist/src/utils/editor.test.js +128 -28
  232. package/dist/src/utils/editor.test.js.map +1 -1
  233. package/dist/src/utils/errorReporting.d.ts +1 -1
  234. package/dist/src/utils/errorReporting.js +2 -2
  235. package/dist/src/utils/errorReporting.js.map +1 -1
  236. package/dist/src/utils/errorReporting.test.js +44 -38
  237. package/dist/src/utils/errorReporting.test.js.map +1 -1
  238. package/dist/src/utils/errors.js +4 -4
  239. package/dist/src/utils/errors.js.map +1 -1
  240. package/dist/src/utils/fileUtils.d.ts +4 -4
  241. package/dist/src/utils/fileUtils.js +33 -17
  242. package/dist/src/utils/fileUtils.js.map +1 -1
  243. package/dist/src/utils/fileUtils.test.js +37 -37
  244. package/dist/src/utils/fileUtils.test.js.map +1 -1
  245. package/dist/src/utils/formatters.d.ts +6 -0
  246. package/dist/src/utils/formatters.js +16 -0
  247. package/dist/src/utils/formatters.js.map +1 -0
  248. package/dist/src/utils/getFolderStructure.d.ts +3 -2
  249. package/dist/src/utils/getFolderStructure.js +27 -28
  250. package/dist/src/utils/getFolderStructure.js.map +1 -1
  251. package/dist/src/utils/getFolderStructure.test.js +169 -187
  252. package/dist/src/utils/getFolderStructure.test.js.map +1 -1
  253. package/dist/src/utils/gitIgnoreParser.js +4 -7
  254. package/dist/src/utils/gitIgnoreParser.js.map +1 -1
  255. package/dist/src/utils/gitIgnoreParser.test.js +70 -61
  256. package/dist/src/utils/gitIgnoreParser.test.js.map +1 -1
  257. package/dist/src/utils/memoryDiscovery.d.ts +2 -1
  258. package/dist/src/utils/memoryDiscovery.js +11 -5
  259. package/dist/src/utils/memoryDiscovery.js.map +1 -1
  260. package/dist/src/utils/memoryDiscovery.test.js +160 -371
  261. package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
  262. package/dist/src/utils/partUtils.d.ts +14 -0
  263. package/dist/src/utils/partUtils.js +65 -0
  264. package/dist/src/utils/partUtils.js.map +1 -0
  265. package/dist/src/utils/partUtils.test.d.ts +6 -0
  266. package/dist/src/utils/partUtils.test.js +130 -0
  267. package/dist/src/utils/partUtils.test.js.map +1 -0
  268. package/dist/src/utils/paths.d.ts +11 -0
  269. package/dist/src/utils/paths.js +17 -1
  270. package/dist/src/utils/paths.js.map +1 -1
  271. package/dist/src/utils/quotaErrorDetection.js +2 -11
  272. package/dist/src/utils/quotaErrorDetection.js.map +1 -1
  273. package/dist/src/utils/retry.d.ts +6 -0
  274. package/dist/src/utils/retry.js +2 -2
  275. package/dist/src/utils/retry.js.map +1 -1
  276. package/dist/src/utils/safeJsonStringify.d.ts +13 -0
  277. package/dist/src/utils/safeJsonStringify.js +25 -0
  278. package/dist/src/utils/safeJsonStringify.js.map +1 -0
  279. package/dist/src/utils/safeJsonStringify.test.d.ts +6 -0
  280. package/dist/src/utils/safeJsonStringify.test.js +61 -0
  281. package/dist/src/utils/safeJsonStringify.test.js.map +1 -0
  282. package/dist/src/utils/schemaValidator.d.ts +1 -1
  283. package/dist/src/utils/schemaValidator.js +6 -3
  284. package/dist/src/utils/schemaValidator.js.map +1 -1
  285. package/dist/src/utils/shell-utils.d.ts +44 -0
  286. package/dist/src/utils/shell-utils.js +243 -0
  287. package/dist/src/utils/shell-utils.js.map +1 -0
  288. package/dist/src/utils/shell-utils.test.d.ts +6 -0
  289. package/dist/src/utils/shell-utils.test.js +450 -0
  290. package/dist/src/utils/shell-utils.test.js.map +1 -0
  291. package/dist/src/utils/summarizer.d.ts +1 -1
  292. package/dist/src/utils/summarizer.js +11 -39
  293. package/dist/src/utils/summarizer.js.map +1 -1
  294. package/dist/src/utils/summarizer.test.js +1 -1
  295. package/dist/src/utils/systemEncoding.d.ts +40 -0
  296. package/dist/src/utils/systemEncoding.js +149 -0
  297. package/dist/src/utils/systemEncoding.js.map +1 -0
  298. package/dist/src/utils/systemEncoding.test.d.ts +6 -0
  299. package/dist/src/utils/systemEncoding.test.js +368 -0
  300. package/dist/src/utils/systemEncoding.test.js.map +1 -0
  301. package/dist/src/utils/textUtils.d.ts +13 -0
  302. package/dist/src/utils/textUtils.js +28 -0
  303. package/dist/src/utils/textUtils.js.map +1 -0
  304. package/dist/tsconfig.tsbuildinfo +1 -1
  305. package/package.json +4 -3
  306. package/dist/google-gemini-cli-core-0.1.11.tgz +0 -0
  307. package/dist/src/core/geminiRequest.test.js +0 -72
  308. package/dist/src/core/geminiRequest.test.js.map +0 -1
  309. /package/dist/src/{core/geminiRequest.test.d.ts → ide/ideContext.test.d.ts} +0 -0
@@ -0,0 +1,266 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
7
+ import { GeminiEventType, } from '../core/turn.js';
8
+ import * as loggers from '../telemetry/loggers.js';
9
+ import { LoopType } from '../telemetry/types.js';
10
+ import { LoopDetectionService } from './loopDetectionService.js';
11
+ vi.mock('../telemetry/loggers.js', () => ({
12
+ logLoopDetected: vi.fn(),
13
+ }));
14
+ const TOOL_CALL_LOOP_THRESHOLD = 5;
15
+ const CONTENT_LOOP_THRESHOLD = 10;
16
+ const CONTENT_CHUNK_SIZE = 50;
17
+ describe('LoopDetectionService', () => {
18
+ let service;
19
+ let mockConfig;
20
+ beforeEach(() => {
21
+ mockConfig = {
22
+ getTelemetryEnabled: () => true,
23
+ };
24
+ service = new LoopDetectionService(mockConfig);
25
+ vi.clearAllMocks();
26
+ });
27
+ const createToolCallRequestEvent = (name, args) => ({
28
+ type: GeminiEventType.ToolCallRequest,
29
+ value: {
30
+ name,
31
+ args,
32
+ callId: 'test-id',
33
+ isClientInitiated: false,
34
+ prompt_id: 'test-prompt-id',
35
+ },
36
+ });
37
+ const createContentEvent = (content) => ({
38
+ type: GeminiEventType.Content,
39
+ value: content,
40
+ });
41
+ describe('Tool Call Loop Detection', () => {
42
+ it(`should not detect a loop for fewer than TOOL_CALL_LOOP_THRESHOLD identical calls`, () => {
43
+ const event = createToolCallRequestEvent('testTool', { param: 'value' });
44
+ for (let i = 0; i < TOOL_CALL_LOOP_THRESHOLD - 1; i++) {
45
+ expect(service.addAndCheck(event)).toBe(false);
46
+ }
47
+ expect(loggers.logLoopDetected).not.toHaveBeenCalled();
48
+ });
49
+ it(`should detect a loop on the TOOL_CALL_LOOP_THRESHOLD-th identical call`, () => {
50
+ const event = createToolCallRequestEvent('testTool', { param: 'value' });
51
+ for (let i = 0; i < TOOL_CALL_LOOP_THRESHOLD - 1; i++) {
52
+ service.addAndCheck(event);
53
+ }
54
+ expect(service.addAndCheck(event)).toBe(true);
55
+ expect(loggers.logLoopDetected).toHaveBeenCalledTimes(1);
56
+ });
57
+ it('should detect a loop on subsequent identical calls', () => {
58
+ const event = createToolCallRequestEvent('testTool', { param: 'value' });
59
+ for (let i = 0; i < TOOL_CALL_LOOP_THRESHOLD; i++) {
60
+ service.addAndCheck(event);
61
+ }
62
+ expect(service.addAndCheck(event)).toBe(true);
63
+ expect(loggers.logLoopDetected).toHaveBeenCalledTimes(1);
64
+ });
65
+ it('should not detect a loop for different tool calls', () => {
66
+ const event1 = createToolCallRequestEvent('testTool', {
67
+ param: 'value1',
68
+ });
69
+ const event2 = createToolCallRequestEvent('testTool', {
70
+ param: 'value2',
71
+ });
72
+ const event3 = createToolCallRequestEvent('anotherTool', {
73
+ param: 'value1',
74
+ });
75
+ for (let i = 0; i < TOOL_CALL_LOOP_THRESHOLD - 2; i++) {
76
+ expect(service.addAndCheck(event1)).toBe(false);
77
+ expect(service.addAndCheck(event2)).toBe(false);
78
+ expect(service.addAndCheck(event3)).toBe(false);
79
+ }
80
+ });
81
+ it('should not reset tool call counter for other event types', () => {
82
+ const toolCallEvent = createToolCallRequestEvent('testTool', {
83
+ param: 'value',
84
+ });
85
+ const otherEvent = {
86
+ type: 'thought',
87
+ };
88
+ // Send events just below the threshold
89
+ for (let i = 0; i < TOOL_CALL_LOOP_THRESHOLD - 1; i++) {
90
+ expect(service.addAndCheck(toolCallEvent)).toBe(false);
91
+ }
92
+ // Send a different event type
93
+ expect(service.addAndCheck(otherEvent)).toBe(false);
94
+ // Send the tool call event again, which should now trigger the loop
95
+ expect(service.addAndCheck(toolCallEvent)).toBe(true);
96
+ expect(loggers.logLoopDetected).toHaveBeenCalledTimes(1);
97
+ });
98
+ });
99
+ describe('Content Loop Detection', () => {
100
+ const generateRandomString = (length) => {
101
+ let result = '';
102
+ const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
103
+ const charactersLength = characters.length;
104
+ for (let i = 0; i < length; i++) {
105
+ result += characters.charAt(Math.floor(Math.random() * charactersLength));
106
+ }
107
+ return result;
108
+ };
109
+ it('should not detect a loop for random content', () => {
110
+ service.reset('');
111
+ for (let i = 0; i < 1000; i++) {
112
+ const content = generateRandomString(10);
113
+ const isLoop = service.addAndCheck(createContentEvent(content));
114
+ expect(isLoop).toBe(false);
115
+ }
116
+ expect(loggers.logLoopDetected).not.toHaveBeenCalled();
117
+ });
118
+ it('should detect a loop when a chunk of content repeats consecutively', () => {
119
+ service.reset('');
120
+ const repeatedContent = 'a'.repeat(CONTENT_CHUNK_SIZE);
121
+ let isLoop = false;
122
+ for (let i = 0; i < CONTENT_LOOP_THRESHOLD; i++) {
123
+ for (const char of repeatedContent) {
124
+ isLoop = service.addAndCheck(createContentEvent(char));
125
+ }
126
+ }
127
+ expect(isLoop).toBe(true);
128
+ expect(loggers.logLoopDetected).toHaveBeenCalledTimes(1);
129
+ });
130
+ it('should not detect a loop if repetitions are very far apart', () => {
131
+ service.reset('');
132
+ const repeatedContent = 'b'.repeat(CONTENT_CHUNK_SIZE);
133
+ const fillerContent = generateRandomString(500);
134
+ let isLoop = false;
135
+ for (let i = 0; i < CONTENT_LOOP_THRESHOLD; i++) {
136
+ for (const char of repeatedContent) {
137
+ isLoop = service.addAndCheck(createContentEvent(char));
138
+ }
139
+ for (const char of fillerContent) {
140
+ isLoop = service.addAndCheck(createContentEvent(char));
141
+ }
142
+ }
143
+ expect(isLoop).toBe(false);
144
+ expect(loggers.logLoopDetected).not.toHaveBeenCalled();
145
+ });
146
+ });
147
+ describe('Edge Cases', () => {
148
+ it('should handle empty content', () => {
149
+ const event = createContentEvent('');
150
+ expect(service.addAndCheck(event)).toBe(false);
151
+ });
152
+ });
153
+ describe('Reset Functionality', () => {
154
+ it('tool call should reset content count', () => {
155
+ const contentEvent = createContentEvent('Some content.');
156
+ const toolEvent = createToolCallRequestEvent('testTool', {
157
+ param: 'value',
158
+ });
159
+ for (let i = 0; i < 9; i++) {
160
+ service.addAndCheck(contentEvent);
161
+ }
162
+ service.addAndCheck(toolEvent);
163
+ // Should start fresh
164
+ expect(service.addAndCheck(createContentEvent('Fresh content.'))).toBe(false);
165
+ });
166
+ });
167
+ describe('General Behavior', () => {
168
+ it('should return false for unhandled event types', () => {
169
+ const otherEvent = {
170
+ type: 'unhandled_event',
171
+ };
172
+ expect(service.addAndCheck(otherEvent)).toBe(false);
173
+ expect(service.addAndCheck(otherEvent)).toBe(false);
174
+ });
175
+ });
176
+ });
177
+ describe('LoopDetectionService LLM Checks', () => {
178
+ let service;
179
+ let mockConfig;
180
+ let mockGeminiClient;
181
+ let abortController;
182
+ beforeEach(() => {
183
+ mockGeminiClient = {
184
+ getHistory: vi.fn().mockReturnValue([]),
185
+ generateJson: vi.fn(),
186
+ };
187
+ mockConfig = {
188
+ getGeminiClient: () => mockGeminiClient,
189
+ getDebugMode: () => false,
190
+ getTelemetryEnabled: () => true,
191
+ };
192
+ service = new LoopDetectionService(mockConfig);
193
+ abortController = new AbortController();
194
+ vi.clearAllMocks();
195
+ });
196
+ afterEach(() => {
197
+ vi.restoreAllMocks();
198
+ });
199
+ const advanceTurns = async (count) => {
200
+ for (let i = 0; i < count; i++) {
201
+ await service.turnStarted(abortController.signal);
202
+ }
203
+ };
204
+ it('should not trigger LLM check before LLM_CHECK_AFTER_TURNS', async () => {
205
+ await advanceTurns(29);
206
+ expect(mockGeminiClient.generateJson).not.toHaveBeenCalled();
207
+ });
208
+ it('should trigger LLM check on the 30th turn', async () => {
209
+ mockGeminiClient.generateJson = vi
210
+ .fn()
211
+ .mockResolvedValue({ confidence: 0.1 });
212
+ await advanceTurns(30);
213
+ expect(mockGeminiClient.generateJson).toHaveBeenCalledTimes(1);
214
+ });
215
+ it('should detect a cognitive loop when confidence is high', async () => {
216
+ // First check at turn 30
217
+ mockGeminiClient.generateJson = vi
218
+ .fn()
219
+ .mockResolvedValue({ confidence: 0.85, reasoning: 'Repetitive actions' });
220
+ await advanceTurns(30);
221
+ expect(mockGeminiClient.generateJson).toHaveBeenCalledTimes(1);
222
+ // The confidence of 0.85 will result in a low interval.
223
+ // The interval will be: 5 + (15 - 5) * (1 - 0.85) = 5 + 10 * 0.15 = 6.5 -> rounded to 7
224
+ await advanceTurns(6); // advance to turn 36
225
+ mockGeminiClient.generateJson = vi
226
+ .fn()
227
+ .mockResolvedValue({ confidence: 0.95, reasoning: 'Repetitive actions' });
228
+ const finalResult = await service.turnStarted(abortController.signal); // This is turn 37
229
+ expect(finalResult).toBe(true);
230
+ expect(loggers.logLoopDetected).toHaveBeenCalledWith(mockConfig, expect.objectContaining({
231
+ 'event.name': 'loop_detected',
232
+ loop_type: LoopType.LLM_DETECTED_LOOP,
233
+ }));
234
+ });
235
+ it('should not detect a loop when confidence is low', async () => {
236
+ mockGeminiClient.generateJson = vi
237
+ .fn()
238
+ .mockResolvedValue({ confidence: 0.5, reasoning: 'Looks okay' });
239
+ await advanceTurns(30);
240
+ const result = await service.turnStarted(abortController.signal);
241
+ expect(result).toBe(false);
242
+ expect(loggers.logLoopDetected).not.toHaveBeenCalled();
243
+ });
244
+ it('should adjust the check interval based on confidence', async () => {
245
+ // Confidence is 0.0, so interval should be MAX_LLM_CHECK_INTERVAL (15)
246
+ mockGeminiClient.generateJson = vi
247
+ .fn()
248
+ .mockResolvedValue({ confidence: 0.0 });
249
+ await advanceTurns(30); // First check at turn 30
250
+ expect(mockGeminiClient.generateJson).toHaveBeenCalledTimes(1);
251
+ await advanceTurns(14); // Advance to turn 44
252
+ expect(mockGeminiClient.generateJson).toHaveBeenCalledTimes(1);
253
+ await service.turnStarted(abortController.signal); // Turn 45
254
+ expect(mockGeminiClient.generateJson).toHaveBeenCalledTimes(2);
255
+ });
256
+ it('should handle errors from generateJson gracefully', async () => {
257
+ mockGeminiClient.generateJson = vi
258
+ .fn()
259
+ .mockRejectedValue(new Error('API error'));
260
+ await advanceTurns(30);
261
+ const result = await service.turnStarted(abortController.signal);
262
+ expect(result).toBe(false);
263
+ expect(loggers.logLoopDetected).not.toHaveBeenCalled();
264
+ });
265
+ });
266
+ //# sourceMappingURL=loopDetectionService.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loopDetectionService.test.js","sourceRoot":"","sources":["../../../src/services/loopDetectionService.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ /** A structured result from a shell command execution. */
7
+ export interface ShellExecutionResult {
8
+ /** The raw, unprocessed output buffer. */
9
+ rawOutput: Buffer;
10
+ /** The combined, decoded stdout and stderr as a string. */
11
+ output: string;
12
+ /** The decoded stdout as a string. */
13
+ stdout: string;
14
+ /** The decoded stderr as a string. */
15
+ stderr: string;
16
+ /** The process exit code, or null if terminated by a signal. */
17
+ exitCode: number | null;
18
+ /** The signal that terminated the process, if any. */
19
+ signal: NodeJS.Signals | null;
20
+ /** An error object if the process failed to spawn. */
21
+ error: Error | null;
22
+ /** A boolean indicating if the command was aborted by the user. */
23
+ aborted: boolean;
24
+ /** The process ID of the spawned shell. */
25
+ pid: number | undefined;
26
+ }
27
+ /** A handle for an ongoing shell execution. */
28
+ export interface ShellExecutionHandle {
29
+ /** The process ID of the spawned shell. */
30
+ pid: number | undefined;
31
+ /** A promise that resolves with the complete execution result. */
32
+ result: Promise<ShellExecutionResult>;
33
+ }
34
+ /**
35
+ * Describes a structured event emitted during shell command execution.
36
+ */
37
+ export type ShellOutputEvent = {
38
+ /** The event contains a chunk of output data. */
39
+ type: 'data';
40
+ /** The stream from which the data originated. */
41
+ stream: 'stdout' | 'stderr';
42
+ /** The decoded string chunk. */
43
+ chunk: string;
44
+ } | {
45
+ /** Signals that the output stream has been identified as binary. */
46
+ type: 'binary_detected';
47
+ } | {
48
+ /** Provides progress updates for a binary stream. */
49
+ type: 'binary_progress';
50
+ /** The total number of bytes received so far. */
51
+ bytesReceived: number;
52
+ };
53
+ /**
54
+ * A centralized service for executing shell commands with robust process
55
+ * management, cross-platform compatibility, and streaming output capabilities.
56
+ *
57
+ */
58
+ export declare class ShellExecutionService {
59
+ /**
60
+ * Executes a shell command using `spawn`, capturing all output and lifecycle events.
61
+ *
62
+ * @param commandToExecute The exact command string to run.
63
+ * @param cwd The working directory to execute the command in.
64
+ * @param onOutputEvent A callback for streaming structured events about the execution, including data chunks and status updates.
65
+ * @param abortSignal An AbortSignal to terminate the process and its children.
66
+ * @returns An object containing the process ID (pid) and a promise that
67
+ * resolves with the complete execution result.
68
+ */
69
+ static execute(commandToExecute: string, cwd: string, onOutputEvent: (event: ShellOutputEvent) => void, abortSignal: AbortSignal): ShellExecutionHandle;
70
+ }
@@ -0,0 +1,152 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { spawn } from 'child_process';
7
+ import { TextDecoder } from 'util';
8
+ import os from 'os';
9
+ import stripAnsi from 'strip-ansi';
10
+ import { getCachedEncodingForBuffer } from '../utils/systemEncoding.js';
11
+ import { isBinary } from '../utils/textUtils.js';
12
+ const SIGKILL_TIMEOUT_MS = 200;
13
+ /**
14
+ * A centralized service for executing shell commands with robust process
15
+ * management, cross-platform compatibility, and streaming output capabilities.
16
+ *
17
+ */
18
+ export class ShellExecutionService {
19
+ /**
20
+ * Executes a shell command using `spawn`, capturing all output and lifecycle events.
21
+ *
22
+ * @param commandToExecute The exact command string to run.
23
+ * @param cwd The working directory to execute the command in.
24
+ * @param onOutputEvent A callback for streaming structured events about the execution, including data chunks and status updates.
25
+ * @param abortSignal An AbortSignal to terminate the process and its children.
26
+ * @returns An object containing the process ID (pid) and a promise that
27
+ * resolves with the complete execution result.
28
+ */
29
+ static execute(commandToExecute, cwd, onOutputEvent, abortSignal) {
30
+ const isWindows = os.platform() === 'win32';
31
+ const shell = isWindows ? 'cmd.exe' : 'bash';
32
+ const shellArgs = [isWindows ? '/c' : '-c', commandToExecute];
33
+ const child = spawn(shell, shellArgs, {
34
+ cwd,
35
+ stdio: ['ignore', 'pipe', 'pipe'],
36
+ detached: !isWindows, // Use process groups on non-Windows for robust killing
37
+ env: {
38
+ ...process.env,
39
+ GEMINI_CLI: '1',
40
+ },
41
+ });
42
+ const result = new Promise((resolve) => {
43
+ // Use decoders to handle multi-byte characters safely (for streaming output).
44
+ let stdoutDecoder = null;
45
+ let stderrDecoder = null;
46
+ let stdout = '';
47
+ let stderr = '';
48
+ const outputChunks = [];
49
+ let error = null;
50
+ let exited = false;
51
+ let isStreamingRawContent = true;
52
+ const MAX_SNIFF_SIZE = 4096;
53
+ let sniffedBytes = 0;
54
+ const handleOutput = (data, stream) => {
55
+ if (!stdoutDecoder || !stderrDecoder) {
56
+ const encoding = getCachedEncodingForBuffer(data);
57
+ try {
58
+ stdoutDecoder = new TextDecoder(encoding);
59
+ stderrDecoder = new TextDecoder(encoding);
60
+ }
61
+ catch {
62
+ // If the encoding is not supported, fall back to utf-8.
63
+ // This can happen on some platforms for certain encodings like 'utf-32le'.
64
+ stdoutDecoder = new TextDecoder('utf-8');
65
+ stderrDecoder = new TextDecoder('utf-8');
66
+ }
67
+ }
68
+ outputChunks.push(data);
69
+ // Binary detection logic. This only runs until we've made a determination.
70
+ if (isStreamingRawContent && sniffedBytes < MAX_SNIFF_SIZE) {
71
+ const sniffBuffer = Buffer.concat(outputChunks.slice(0, 20));
72
+ sniffedBytes = sniffBuffer.length;
73
+ if (isBinary(sniffBuffer)) {
74
+ // Change state to stop streaming raw content.
75
+ isStreamingRawContent = false;
76
+ onOutputEvent({ type: 'binary_detected' });
77
+ }
78
+ }
79
+ const decodedChunk = stream === 'stdout'
80
+ ? stdoutDecoder.decode(data, { stream: true })
81
+ : stderrDecoder.decode(data, { stream: true });
82
+ const strippedChunk = stripAnsi(decodedChunk);
83
+ if (stream === 'stdout') {
84
+ stdout += strippedChunk;
85
+ }
86
+ else {
87
+ stderr += strippedChunk;
88
+ }
89
+ if (isStreamingRawContent) {
90
+ onOutputEvent({ type: 'data', stream, chunk: strippedChunk });
91
+ }
92
+ else {
93
+ const totalBytes = outputChunks.reduce((sum, chunk) => sum + chunk.length, 0);
94
+ onOutputEvent({ type: 'binary_progress', bytesReceived: totalBytes });
95
+ }
96
+ };
97
+ child.stdout.on('data', (data) => handleOutput(data, 'stdout'));
98
+ child.stderr.on('data', (data) => handleOutput(data, 'stderr'));
99
+ child.on('error', (err) => {
100
+ error = err;
101
+ });
102
+ const abortHandler = async () => {
103
+ if (child.pid && !exited) {
104
+ if (isWindows) {
105
+ spawn('taskkill', ['/pid', child.pid.toString(), '/f', '/t']);
106
+ }
107
+ else {
108
+ try {
109
+ // Kill the entire process group (negative PID).
110
+ // SIGTERM first, then SIGKILL if it doesn't die.
111
+ process.kill(-child.pid, 'SIGTERM');
112
+ await new Promise((res) => setTimeout(res, SIGKILL_TIMEOUT_MS));
113
+ if (!exited) {
114
+ process.kill(-child.pid, 'SIGKILL');
115
+ }
116
+ }
117
+ catch (_e) {
118
+ // Fall back to killing just the main process if group kill fails.
119
+ if (!exited)
120
+ child.kill('SIGKILL');
121
+ }
122
+ }
123
+ }
124
+ };
125
+ abortSignal.addEventListener('abort', abortHandler, { once: true });
126
+ child.on('exit', (code, signal) => {
127
+ exited = true;
128
+ abortSignal.removeEventListener('abort', abortHandler);
129
+ if (stdoutDecoder) {
130
+ stdout += stripAnsi(stdoutDecoder.decode());
131
+ }
132
+ if (stderrDecoder) {
133
+ stderr += stripAnsi(stderrDecoder.decode());
134
+ }
135
+ const finalBuffer = Buffer.concat(outputChunks);
136
+ resolve({
137
+ rawOutput: finalBuffer,
138
+ output: stdout + (stderr ? `\n${stderr}` : ''),
139
+ stdout,
140
+ stderr,
141
+ exitCode: code,
142
+ signal,
143
+ error,
144
+ aborted: abortSignal.aborted,
145
+ pid: child.pid,
146
+ });
147
+ });
148
+ });
149
+ return { pid: child.pid, result };
150
+ }
151
+ }
152
+ //# sourceMappingURL=shellExecutionService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shellExecutionService.js","sourceRoot":"","sources":["../../../src/services/shellExecutionService.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAuD/B;;;;GAIG;AACH,MAAM,OAAO,qBAAqB;IAChC;;;;;;;;;OASG;IACH,MAAM,CAAC,OAAO,CACZ,gBAAwB,EACxB,GAAW,EACX,aAAgD,EAChD,WAAwB;QAExB,MAAM,SAAS,GAAG,EAAE,CAAC,QAAQ,EAAE,KAAK,OAAO,CAAC;QAC5C,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;QAC7C,MAAM,SAAS,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;QAE9D,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,SAAS,EAAE;YACpC,GAAG;YACH,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;YACjC,QAAQ,EAAE,CAAC,SAAS,EAAE,uDAAuD;YAC7E,GAAG,EAAE;gBACH,GAAG,OAAO,CAAC,GAAG;gBACd,UAAU,EAAE,GAAG;aAChB;SACF,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,IAAI,OAAO,CAAuB,CAAC,OAAO,EAAE,EAAE;YAC3D,8EAA8E;YAC9E,IAAI,aAAa,GAAuB,IAAI,CAAC;YAC7C,IAAI,aAAa,GAAuB,IAAI,CAAC;YAE7C,IAAI,MAAM,GAAG,EAAE,CAAC;YAChB,IAAI,MAAM,GAAG,EAAE,CAAC;YAChB,MAAM,YAAY,GAAa,EAAE,CAAC;YAClC,IAAI,KAAK,GAAiB,IAAI,CAAC;YAC/B,IAAI,MAAM,GAAG,KAAK,CAAC;YAEnB,IAAI,qBAAqB,GAAG,IAAI,CAAC;YACjC,MAAM,cAAc,GAAG,IAAI,CAAC;YAC5B,IAAI,YAAY,GAAG,CAAC,CAAC;YAErB,MAAM,YAAY,GAAG,CAAC,IAAY,EAAE,MAA2B,EAAE,EAAE;gBACjE,IAAI,CAAC,aAAa,IAAI,CAAC,aAAa,EAAE,CAAC;oBACrC,MAAM,QAAQ,GAAG,0BAA0B,CAAC,IAAI,CAAC,CAAC;oBAClD,IAAI,CAAC;wBACH,aAAa,GAAG,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC;wBAC1C,aAAa,GAAG,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC;oBAC5C,CAAC;oBAAC,MAAM,CAAC;wBACP,wDAAwD;wBACxD,2EAA2E;wBAC3E,aAAa,GAAG,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC;wBACzC,aAAa,GAAG,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC;oBAC3C,CAAC;gBACH,CAAC;gBAED,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAExB,2EAA2E;gBAC3E,IAAI,qBAAqB,IAAI,YAAY,GAAG,cAAc,EAAE,CAAC;oBAC3D,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;oBAC7D,YAAY,GAAG,WAAW,CAAC,MAAM,CAAC;oBAElC,IAAI,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;wBAC1B,8CAA8C;wBAC9C,qBAAqB,GAAG,KAAK,CAAC;wBAC9B,aAAa,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC,CAAC;oBAC7C,CAAC;gBACH,CAAC;gBAED,MAAM,YAAY,GAChB,MAAM,KAAK,QAAQ;oBACjB,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;oBAC9C,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;gBACnD,MAAM,aAAa,GAAG,SAAS,CAAC,YAAY,CAAC,CAAC;gBAE9C,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;oBACxB,MAAM,IAAI,aAAa,CAAC;gBAC1B,CAAC;qBAAM,CAAC;oBACN,MAAM,IAAI,aAAa,CAAC;gBAC1B,CAAC;gBAED,IAAI,qBAAqB,EAAE,CAAC;oBAC1B,aAAa,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC;gBAChE,CAAC;qBAAM,CAAC;oBACN,MAAM,UAAU,GAAG,YAAY,CAAC,MAAM,CACpC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,CAAC,MAAM,EAClC,CAAC,CACF,CAAC;oBACF,aAAa,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC,CAAC;gBACxE,CAAC;YACH,CAAC,CAAC;YAEF,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;YAChE,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;YAChE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBACxB,KAAK,GAAG,GAAG,CAAC;YACd,CAAC,CAAC,CAAC;YAEH,MAAM,YAAY,GAAG,KAAK,IAAI,EAAE;gBAC9B,IAAI,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;oBACzB,IAAI,SAAS,EAAE,CAAC;wBACd,KAAK,CAAC,UAAU,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;oBAChE,CAAC;yBAAM,CAAC;wBACN,IAAI,CAAC;4BACH,gDAAgD;4BAChD,iDAAiD;4BACjD,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;4BACpC,MAAM,IAAI,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC,CAAC;4BAChE,IAAI,CAAC,MAAM,EAAE,CAAC;gCACZ,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;4BACtC,CAAC;wBACH,CAAC;wBAAC,OAAO,EAAE,EAAE,CAAC;4BACZ,kEAAkE;4BAClE,IAAI,CAAC,MAAM;gCAAE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;wBACrC,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC,CAAC;YAEF,WAAW,CAAC,gBAAgB,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YAEpE,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;gBAChC,MAAM,GAAG,IAAI,CAAC;gBACd,WAAW,CAAC,mBAAmB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;gBAEvD,IAAI,aAAa,EAAE,CAAC;oBAClB,MAAM,IAAI,SAAS,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC9C,CAAC;gBACD,IAAI,aAAa,EAAE,CAAC;oBAClB,MAAM,IAAI,SAAS,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC9C,CAAC;gBAED,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;gBAEhD,OAAO,CAAC;oBACN,SAAS,EAAE,WAAW;oBACtB,MAAM,EAAE,MAAM,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC9C,MAAM;oBACN,MAAM;oBACN,QAAQ,EAAE,IAAI;oBACd,MAAM;oBACN,KAAK;oBACL,OAAO,EAAE,WAAW,CAAC,OAAO;oBAC5B,GAAG,EAAE,KAAK,CAAC,GAAG;iBACf,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC;IACpC,CAAC;CACF"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ export {};