@google/gemini-cli-core 0.7.0-nightly.20250912.68035591 → 0.7.0-preview.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/dist/index.d.ts +5 -4
  2. package/dist/index.js +5 -4
  3. package/dist/index.js.map +1 -1
  4. package/dist/src/code_assist/converter.d.ts +1 -0
  5. package/dist/src/code_assist/converter.js +1 -0
  6. package/dist/src/code_assist/converter.js.map +1 -1
  7. package/dist/src/code_assist/converter.test.js +10 -0
  8. package/dist/src/code_assist/converter.test.js.map +1 -1
  9. package/dist/src/code_assist/oauth-credential-storage.d.ts +5 -7
  10. package/dist/src/code_assist/oauth-credential-storage.js +5 -8
  11. package/dist/src/code_assist/oauth-credential-storage.js.map +1 -1
  12. package/dist/src/code_assist/oauth-credential-storage.test.js +35 -33
  13. package/dist/src/code_assist/oauth-credential-storage.test.js.map +1 -1
  14. package/dist/src/code_assist/oauth2.js +28 -2
  15. package/dist/src/code_assist/oauth2.js.map +1 -1
  16. package/dist/src/code_assist/oauth2.test.js +674 -536
  17. package/dist/src/code_assist/oauth2.test.js.map +1 -1
  18. package/dist/src/config/config.d.ts +19 -0
  19. package/dist/src/config/config.js +48 -6
  20. package/dist/src/config/config.js.map +1 -1
  21. package/dist/src/config/config.test.js +93 -1
  22. package/dist/src/config/config.test.js.map +1 -1
  23. package/dist/src/config/models.d.ts +1 -0
  24. package/dist/src/config/models.js +1 -0
  25. package/dist/src/config/models.js.map +1 -1
  26. package/dist/src/core/baseLlmClient.d.ts +1 -0
  27. package/dist/src/core/baseLlmClient.js +24 -0
  28. package/dist/src/core/baseLlmClient.js.map +1 -1
  29. package/dist/src/core/baseLlmClient.test.js +63 -0
  30. package/dist/src/core/baseLlmClient.test.js.map +1 -1
  31. package/dist/src/core/client.d.ts +3 -4
  32. package/dist/src/core/client.js +62 -145
  33. package/dist/src/core/client.js.map +1 -1
  34. package/dist/src/core/client.test.js +119 -202
  35. package/dist/src/core/client.test.js.map +1 -1
  36. package/dist/src/core/coreToolScheduler.test.js +9 -0
  37. package/dist/src/core/coreToolScheduler.test.js.map +1 -1
  38. package/dist/src/core/geminiChat.d.ts +16 -11
  39. package/dist/src/core/geminiChat.js +124 -150
  40. package/dist/src/core/geminiChat.js.map +1 -1
  41. package/dist/src/core/geminiChat.test.js +342 -204
  42. package/dist/src/core/geminiChat.test.js.map +1 -1
  43. package/dist/src/core/loggingContentGenerator.js +5 -5
  44. package/dist/src/core/loggingContentGenerator.js.map +1 -1
  45. package/dist/src/core/nonInteractiveToolExecutor.test.js +1 -0
  46. package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
  47. package/dist/src/generated/git-commit.d.ts +2 -2
  48. package/dist/src/generated/git-commit.js +2 -2
  49. package/dist/src/generated/git-commit.js.map +1 -1
  50. package/dist/src/ide/constants.d.ts +1 -0
  51. package/dist/src/ide/constants.js +1 -0
  52. package/dist/src/ide/constants.js.map +1 -1
  53. package/dist/src/ide/detect-ide.d.ts +44 -14
  54. package/dist/src/ide/detect-ide.js +29 -69
  55. package/dist/src/ide/detect-ide.js.map +1 -1
  56. package/dist/src/ide/detect-ide.test.js +29 -46
  57. package/dist/src/ide/detect-ide.test.js.map +1 -1
  58. package/dist/src/ide/ide-client.d.ts +28 -17
  59. package/dist/src/ide/ide-client.js +125 -57
  60. package/dist/src/ide/ide-client.js.map +1 -1
  61. package/dist/src/ide/ide-client.test.js +44 -10
  62. package/dist/src/ide/ide-client.test.js.map +1 -1
  63. package/dist/src/ide/ide-installer.d.ts +2 -2
  64. package/dist/src/ide/ide-installer.js +15 -11
  65. package/dist/src/ide/ide-installer.js.map +1 -1
  66. package/dist/src/ide/ide-installer.test.js +30 -12
  67. package/dist/src/ide/ide-installer.test.js.map +1 -1
  68. package/dist/src/ide/ideContext.d.ts +0 -93
  69. package/dist/src/ide/ideContext.js +0 -45
  70. package/dist/src/ide/ideContext.js.map +1 -1
  71. package/dist/src/ide/types.d.ts +141 -0
  72. package/dist/src/ide/types.js +73 -0
  73. package/dist/src/ide/types.js.map +1 -1
  74. package/dist/src/index.d.ts +4 -2
  75. package/dist/src/index.js +4 -2
  76. package/dist/src/index.js.map +1 -1
  77. package/dist/src/mcp/oauth-provider.d.ts +4 -1
  78. package/dist/src/mcp/oauth-provider.js +32 -26
  79. package/dist/src/mcp/oauth-provider.js.map +1 -1
  80. package/dist/src/mcp/oauth-token-storage.d.ts +2 -0
  81. package/dist/src/mcp/oauth-token-storage.js +25 -0
  82. package/dist/src/mcp/oauth-token-storage.js.map +1 -1
  83. package/dist/src/mcp/oauth-token-storage.test.js +251 -160
  84. package/dist/src/mcp/oauth-token-storage.test.js.map +1 -1
  85. package/dist/src/mcp/token-storage/index.d.ts +11 -0
  86. package/dist/src/mcp/token-storage/index.js +12 -0
  87. package/dist/src/mcp/token-storage/index.js.map +1 -0
  88. package/dist/src/policy/policy-engine.js +11 -2
  89. package/dist/src/policy/policy-engine.js.map +1 -1
  90. package/dist/src/policy/policy-engine.test.js +45 -0
  91. package/dist/src/policy/policy-engine.test.js.map +1 -1
  92. package/dist/src/routing/modelRouterService.js +37 -3
  93. package/dist/src/routing/modelRouterService.js.map +1 -1
  94. package/dist/src/routing/modelRouterService.test.js +37 -11
  95. package/dist/src/routing/modelRouterService.test.js.map +1 -1
  96. package/dist/src/routing/strategies/classifierStrategy.d.ts +12 -0
  97. package/dist/src/routing/strategies/classifierStrategy.js +173 -0
  98. package/dist/src/routing/strategies/classifierStrategy.js.map +1 -0
  99. package/dist/src/routing/strategies/classifierStrategy.test.d.ts +6 -0
  100. package/dist/src/routing/strategies/classifierStrategy.test.js +192 -0
  101. package/dist/src/routing/strategies/classifierStrategy.test.js.map +1 -0
  102. package/dist/src/routing/strategies/compositeStrategy.js +4 -3
  103. package/dist/src/routing/strategies/compositeStrategy.js.map +1 -1
  104. package/dist/src/routing/strategies/overrideStrategy.js +13 -12
  105. package/dist/src/routing/strategies/overrideStrategy.js.map +1 -1
  106. package/dist/src/routing/strategies/overrideStrategy.test.js +3 -2
  107. package/dist/src/routing/strategies/overrideStrategy.test.js.map +1 -1
  108. package/dist/src/services/chatRecordingService.d.ts +2 -1
  109. package/dist/src/services/chatRecordingService.js +3 -3
  110. package/dist/src/services/chatRecordingService.js.map +1 -1
  111. package/dist/src/services/chatRecordingService.test.js +8 -3
  112. package/dist/src/services/chatRecordingService.test.js.map +1 -1
  113. package/dist/src/services/gitService.js +9 -12
  114. package/dist/src/services/gitService.js.map +1 -1
  115. package/dist/src/services/gitService.test.js +10 -20
  116. package/dist/src/services/gitService.test.js.map +1 -1
  117. package/dist/src/services/loopDetectionService.js +23 -18
  118. package/dist/src/services/loopDetectionService.js.map +1 -1
  119. package/dist/src/services/loopDetectionService.test.js +27 -13
  120. package/dist/src/services/loopDetectionService.test.js.map +1 -1
  121. package/dist/src/services/shellExecutionService.js +30 -15
  122. package/dist/src/services/shellExecutionService.js.map +1 -1
  123. package/dist/src/services/shellExecutionService.test.js +43 -11
  124. package/dist/src/services/shellExecutionService.test.js.map +1 -1
  125. package/dist/src/telemetry/activity-detector.d.ts +41 -0
  126. package/dist/src/telemetry/activity-detector.js +61 -0
  127. package/dist/src/telemetry/activity-detector.js.map +1 -0
  128. package/dist/src/telemetry/activity-detector.test.d.ts +6 -0
  129. package/dist/src/telemetry/activity-detector.test.js +136 -0
  130. package/dist/src/telemetry/activity-detector.test.js.map +1 -0
  131. package/dist/src/telemetry/activity-types.d.ts +19 -0
  132. package/dist/src/telemetry/activity-types.js +21 -0
  133. package/dist/src/telemetry/activity-types.js.map +1 -0
  134. package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +14 -2
  135. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +109 -3
  136. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
  137. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +63 -5
  138. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
  139. package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +12 -1
  140. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +27 -0
  141. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
  142. package/dist/src/telemetry/config.d.ts +31 -0
  143. package/dist/src/telemetry/config.js +76 -0
  144. package/dist/src/telemetry/config.js.map +1 -0
  145. package/dist/src/telemetry/config.test.d.ts +6 -0
  146. package/dist/src/telemetry/config.test.js +124 -0
  147. package/dist/src/telemetry/config.test.js.map +1 -0
  148. package/dist/src/telemetry/constants.d.ts +9 -0
  149. package/dist/src/telemetry/constants.js +9 -0
  150. package/dist/src/telemetry/constants.js.map +1 -1
  151. package/dist/src/telemetry/gcp-exporters.d.ts +34 -0
  152. package/dist/src/telemetry/gcp-exporters.js +117 -0
  153. package/dist/src/telemetry/gcp-exporters.js.map +1 -0
  154. package/dist/src/telemetry/gcp-exporters.test.d.ts +6 -0
  155. package/dist/src/telemetry/gcp-exporters.test.js +318 -0
  156. package/dist/src/telemetry/gcp-exporters.test.js.map +1 -0
  157. package/dist/src/telemetry/index.d.ts +5 -1
  158. package/dist/src/telemetry/index.js +5 -1
  159. package/dist/src/telemetry/index.js.map +1 -1
  160. package/dist/src/telemetry/loggers.d.ts +8 -1
  161. package/dist/src/telemetry/loggers.js +111 -2
  162. package/dist/src/telemetry/loggers.js.map +1 -1
  163. package/dist/src/telemetry/loggers.test.js +207 -4
  164. package/dist/src/telemetry/loggers.test.js.map +1 -1
  165. package/dist/src/telemetry/metrics.d.ts +3 -0
  166. package/dist/src/telemetry/metrics.js +43 -1
  167. package/dist/src/telemetry/metrics.js.map +1 -1
  168. package/dist/src/telemetry/metrics.test.js +42 -0
  169. package/dist/src/telemetry/metrics.test.js.map +1 -1
  170. package/dist/src/telemetry/sdk.js +20 -2
  171. package/dist/src/telemetry/sdk.js.map +1 -1
  172. package/dist/src/telemetry/sdk.test.js +108 -0
  173. package/dist/src/telemetry/sdk.test.js.map +1 -1
  174. package/dist/src/telemetry/types.d.ts +50 -3
  175. package/dist/src/telemetry/types.js +91 -6
  176. package/dist/src/telemetry/types.js.map +1 -1
  177. package/dist/src/telemetry/uiTelemetry.d.ts +1 -1
  178. package/dist/src/telemetry/uiTelemetry.js +2 -3
  179. package/dist/src/telemetry/uiTelemetry.js.map +1 -1
  180. package/dist/src/telemetry/uiTelemetry.test.js +11 -11
  181. package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
  182. package/dist/src/tools/edit.js +4 -2
  183. package/dist/src/tools/edit.js.map +1 -1
  184. package/dist/src/tools/edit.test.js +77 -1
  185. package/dist/src/tools/edit.test.js.map +1 -1
  186. package/dist/src/tools/message-bus-integration.test.d.ts +6 -0
  187. package/dist/src/tools/message-bus-integration.test.js +183 -0
  188. package/dist/src/tools/message-bus-integration.test.js.map +1 -0
  189. package/dist/src/tools/shell.js +8 -11
  190. package/dist/src/tools/shell.js.map +1 -1
  191. package/dist/src/tools/shell.test.js +33 -37
  192. package/dist/src/tools/shell.test.js.map +1 -1
  193. package/dist/src/tools/smart-edit.d.ts +0 -1
  194. package/dist/src/tools/smart-edit.js +3 -15
  195. package/dist/src/tools/smart-edit.js.map +1 -1
  196. package/dist/src/tools/smart-edit.test.js +16 -1
  197. package/dist/src/tools/smart-edit.test.js.map +1 -1
  198. package/dist/src/tools/tool-registry.js +1 -0
  199. package/dist/src/tools/tool-registry.js.map +1 -1
  200. package/dist/src/tools/tools.d.ts +13 -4
  201. package/dist/src/tools/tools.js +101 -3
  202. package/dist/src/tools/tools.js.map +1 -1
  203. package/dist/src/tools/write-file.js +2 -2
  204. package/dist/src/tools/write-file.js.map +1 -1
  205. package/dist/src/tools/write-file.test.js +16 -10
  206. package/dist/src/tools/write-file.test.js.map +1 -1
  207. package/dist/src/tools/write-todos.d.ts +25 -0
  208. package/dist/src/tools/write-todos.js +150 -0
  209. package/dist/src/tools/write-todos.js.map +1 -0
  210. package/dist/src/tools/write-todos.test.d.ts +6 -0
  211. package/dist/src/tools/write-todos.test.js +89 -0
  212. package/dist/src/tools/write-todos.test.js.map +1 -0
  213. package/dist/src/utils/editCorrector.d.ts +7 -6
  214. package/dist/src/utils/editCorrector.js +61 -18
  215. package/dist/src/utils/editCorrector.js.map +1 -1
  216. package/dist/src/utils/editCorrector.test.js +30 -79
  217. package/dist/src/utils/editCorrector.test.js.map +1 -1
  218. package/dist/src/utils/editor.js +31 -44
  219. package/dist/src/utils/editor.js.map +1 -1
  220. package/dist/src/utils/editor.test.js +61 -75
  221. package/dist/src/utils/editor.test.js.map +1 -1
  222. package/dist/src/utils/errorParsing.js +2 -2
  223. package/dist/src/utils/errorParsing.js.map +1 -1
  224. package/dist/src/utils/errorParsing.test.js +7 -7
  225. package/dist/src/utils/errorParsing.test.js.map +1 -1
  226. package/dist/src/utils/fileUtils.test.js +17 -8
  227. package/dist/src/utils/fileUtils.test.js.map +1 -1
  228. package/dist/src/utils/memoryDiscovery.test.js +12 -6
  229. package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
  230. package/dist/src/utils/nextSpeakerChecker.d.ts +2 -2
  231. package/dist/src/utils/nextSpeakerChecker.js +8 -2
  232. package/dist/src/utils/nextSpeakerChecker.js.map +1 -1
  233. package/dist/src/utils/nextSpeakerChecker.test.js +40 -33
  234. package/dist/src/utils/nextSpeakerChecker.test.js.map +1 -1
  235. package/dist/src/utils/shell-utils.d.ts +5 -0
  236. package/dist/src/utils/shell-utils.js +23 -0
  237. package/dist/src/utils/shell-utils.js.map +1 -1
  238. package/dist/src/utils/textUtils.d.ts +5 -0
  239. package/dist/src/utils/textUtils.js +14 -0
  240. package/dist/src/utils/textUtils.js.map +1 -1
  241. package/dist/src/utils/textUtils.test.d.ts +6 -0
  242. package/dist/src/utils/textUtils.test.js +59 -0
  243. package/dist/src/utils/textUtils.test.js.map +1 -0
  244. package/dist/tsconfig.tsbuildinfo +1 -1
  245. package/package.json +5 -1
  246. package/dist/google-gemini-cli-core-0.6.0-nightly.tgz +0 -0
@@ -4,7 +4,7 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
  import { describe, it, expect, vi, afterEach } from 'vitest';
7
- import { detectIde, DetectedIde, getIdeInfo } from './detect-ide.js';
7
+ import { detectIde, IDE_DEFINITIONS } from './detect-ide.js';
8
8
  describe('detectIde', () => {
9
9
  const ideProcessInfo = { pid: 123, command: 'some/path/to/code' };
10
10
  const ideProcessInfoNoCode = { pid: 123, command: 'some/path/to/fork' };
@@ -18,92 +18,75 @@ describe('detectIde', () => {
18
18
  it('should detect Devin', () => {
19
19
  vi.stubEnv('TERM_PROGRAM', 'vscode');
20
20
  vi.stubEnv('__COG_BASHRC_SOURCED', '1');
21
- expect(detectIde(ideProcessInfo)).toBe(DetectedIde.Devin);
21
+ expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.devin);
22
22
  });
23
23
  it('should detect Replit', () => {
24
24
  vi.stubEnv('TERM_PROGRAM', 'vscode');
25
25
  vi.stubEnv('REPLIT_USER', 'testuser');
26
- expect(detectIde(ideProcessInfo)).toBe(DetectedIde.Replit);
26
+ expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.replit);
27
27
  });
28
28
  it('should detect Cursor', () => {
29
29
  vi.stubEnv('TERM_PROGRAM', 'vscode');
30
30
  vi.stubEnv('CURSOR_TRACE_ID', 'some-id');
31
- expect(detectIde(ideProcessInfo)).toBe(DetectedIde.Cursor);
31
+ expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.cursor);
32
32
  });
33
33
  it('should detect Codespaces', () => {
34
34
  vi.stubEnv('TERM_PROGRAM', 'vscode');
35
35
  vi.stubEnv('CODESPACES', 'true');
36
- expect(detectIde(ideProcessInfo)).toBe(DetectedIde.Codespaces);
36
+ expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.codespaces);
37
37
  });
38
38
  it('should detect Cloud Shell via EDITOR_IN_CLOUD_SHELL', () => {
39
39
  vi.stubEnv('TERM_PROGRAM', 'vscode');
40
40
  vi.stubEnv('EDITOR_IN_CLOUD_SHELL', 'true');
41
- expect(detectIde(ideProcessInfo)).toBe(DetectedIde.CloudShell);
41
+ expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.cloudshell);
42
42
  });
43
43
  it('should detect Cloud Shell via CLOUD_SHELL', () => {
44
44
  vi.stubEnv('TERM_PROGRAM', 'vscode');
45
45
  vi.stubEnv('CLOUD_SHELL', 'true');
46
- expect(detectIde(ideProcessInfo)).toBe(DetectedIde.CloudShell);
46
+ expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.cloudshell);
47
47
  });
48
48
  it('should detect Trae', () => {
49
49
  vi.stubEnv('TERM_PROGRAM', 'vscode');
50
50
  vi.stubEnv('TERM_PRODUCT', 'Trae');
51
- expect(detectIde(ideProcessInfo)).toBe(DetectedIde.Trae);
51
+ expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.trae);
52
52
  });
53
53
  it('should detect Firebase Studio via MONOSPACE_ENV', () => {
54
54
  vi.stubEnv('TERM_PROGRAM', 'vscode');
55
55
  vi.stubEnv('MONOSPACE_ENV', 'true');
56
- expect(detectIde(ideProcessInfo)).toBe(DetectedIde.FirebaseStudio);
56
+ expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.firebasestudio);
57
57
  });
58
58
  it('should detect VSCode when no other IDE is detected and command includes "code"', () => {
59
59
  vi.stubEnv('TERM_PROGRAM', 'vscode');
60
60
  vi.stubEnv('MONOSPACE_ENV', '');
61
- expect(detectIde(ideProcessInfo)).toBe(DetectedIde.VSCode);
61
+ expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.vscode);
62
62
  });
63
63
  it('should detect VSCodeFork when no other IDE is detected and command does not include "code"', () => {
64
64
  vi.stubEnv('TERM_PROGRAM', 'vscode');
65
65
  vi.stubEnv('MONOSPACE_ENV', '');
66
- expect(detectIde(ideProcessInfoNoCode)).toBe(DetectedIde.VSCodeFork);
67
- });
68
- it('should prioritize other IDEs over VSCode detection', () => {
69
- vi.stubEnv('TERM_PROGRAM', 'vscode');
70
- vi.stubEnv('REPLIT_USER', 'testuser');
71
- expect(detectIde(ideProcessInfo)).toBe(DetectedIde.Replit);
66
+ expect(detectIde(ideProcessInfoNoCode)).toBe(IDE_DEFINITIONS.vscodefork);
72
67
  });
73
68
  });
74
- describe('getIdeInfo', () => {
75
- it('should return correct info for Devin', () => {
76
- expect(getIdeInfo(DetectedIde.Devin)).toEqual({ displayName: 'Devin' });
77
- });
78
- it('should return correct info for Replit', () => {
79
- expect(getIdeInfo(DetectedIde.Replit)).toEqual({ displayName: 'Replit' });
80
- });
81
- it('should return correct info for Cursor', () => {
82
- expect(getIdeInfo(DetectedIde.Cursor)).toEqual({ displayName: 'Cursor' });
83
- });
84
- it('should return correct info for CloudShell', () => {
85
- expect(getIdeInfo(DetectedIde.CloudShell)).toEqual({
86
- displayName: 'Cloud Shell',
87
- });
88
- });
89
- it('should return correct info for Codespaces', () => {
90
- expect(getIdeInfo(DetectedIde.Codespaces)).toEqual({
91
- displayName: 'GitHub Codespaces',
92
- });
93
- });
94
- it('should return correct info for FirebaseStudio', () => {
95
- expect(getIdeInfo(DetectedIde.FirebaseStudio)).toEqual({
96
- displayName: 'Firebase Studio',
97
- });
69
+ describe('detectIde with ideInfoFromFile', () => {
70
+ const ideProcessInfo = { pid: 123, command: 'some/path/to/code' };
71
+ afterEach(() => {
72
+ vi.unstubAllEnvs();
98
73
  });
99
- it('should return correct info for Trae', () => {
100
- expect(getIdeInfo(DetectedIde.Trae)).toEqual({ displayName: 'Trae' });
74
+ it('should use the name and displayName from the file', () => {
75
+ const ideInfoFromFile = {
76
+ name: 'custom-ide',
77
+ displayName: 'Custom IDE',
78
+ };
79
+ expect(detectIde(ideProcessInfo, ideInfoFromFile)).toEqual(ideInfoFromFile);
101
80
  });
102
- it('should return correct info for VSCode', () => {
103
- expect(getIdeInfo(DetectedIde.VSCode)).toEqual({ displayName: 'VS Code' });
81
+ it('should fall back to env detection if name is missing', () => {
82
+ const ideInfoFromFile = { displayName: 'Custom IDE' };
83
+ vi.stubEnv('TERM_PROGRAM', 'vscode');
84
+ expect(detectIde(ideProcessInfo, ideInfoFromFile)).toBe(IDE_DEFINITIONS.vscode);
104
85
  });
105
- it('should return correct info for VSCodeFork', () => {
106
- expect(getIdeInfo(DetectedIde.VSCodeFork)).toEqual({ displayName: 'IDE' });
86
+ it('should fall back to env detection if displayName is missing', () => {
87
+ const ideInfoFromFile = { name: 'custom-ide' };
88
+ vi.stubEnv('TERM_PROGRAM', 'vscode');
89
+ expect(detectIde(ideProcessInfo, ideInfoFromFile)).toBe(IDE_DEFINITIONS.vscode);
107
90
  });
108
91
  });
109
92
  //# sourceMappingURL=detect-ide.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"detect-ide.test.js","sourceRoot":"","sources":["../../../src/ide/detect-ide.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"detect-ide.test.js","sourceRoot":"","sources":["../../../src/ide/detect-ide.test.ts"],"names":[],"mappings":"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"}
@@ -3,8 +3,14 @@
3
3
  * Copyright 2025 Google LLC
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
- import { type DetectedIde } from '../ide/detect-ide.js';
7
- import { type DiffUpdateResult } from './ideContext.js';
6
+ import { type IdeInfo } from '../ide/detect-ide.js';
7
+ export type DiffUpdateResult = {
8
+ status: 'accepted';
9
+ content?: string;
10
+ } | {
11
+ status: 'rejected';
12
+ content: undefined;
13
+ };
8
14
  export type IDEConnectionState = {
9
15
  status: IDEConnectionStatus;
10
16
  details?: string;
@@ -22,8 +28,9 @@ export declare class IdeClient {
22
28
  private client;
23
29
  private state;
24
30
  private currentIde;
25
- private currentIdeDisplayName;
26
31
  private ideProcessInfo;
32
+ private connectionConfig;
33
+ private authToken;
27
34
  private diffResponses;
28
35
  private statusListeners;
29
36
  private trustChangeListeners;
@@ -42,20 +49,24 @@ export declare class IdeClient {
42
49
  removeTrustChangeListener(listener: (isTrusted: boolean) => void): void;
43
50
  connect(): Promise<void>;
44
51
  /**
45
- * A diff is accepted with any modifications if the user performs one of the
46
- * following actions:
47
- * - Clicks the checkbox icon in the IDE to accept
48
- * - Runs `command+shift+p` > "Gemini CLI: Accept Diff in IDE" to accept
49
- * - Selects "accept" in the CLI UI
50
- * - Saves the file via `ctrl/command+s`
52
+ * Opens a diff view in the IDE, allowing the user to review and accept or
53
+ * reject changes.
54
+ *
55
+ * This method sends a request to the IDE to display a diff between the
56
+ * current content of a file and the new content provided. It then waits for
57
+ * a notification from the IDE indicating that the user has either accepted
58
+ * (potentially with manual edits) or rejected the diff.
59
+ *
60
+ * A mutex ensures that only one diff view can be open at a time to prevent
61
+ * race conditions.
51
62
  *
52
- * A diff is rejected if the user performs one of the following actions:
53
- * - Clicks the "x" icon in the IDE
54
- * - Runs "Gemini CLI: Close Diff in IDE"
55
- * - Selects "no" in the CLI UI
56
- * - Closes the file
63
+ * @param filePath The absolute path to the file to be diffed.
64
+ * @param newContent The proposed new content for the file.
65
+ * @returns A promise that resolves with a `DiffUpdateResult`, indicating
66
+ * whether the diff was 'accepted' or 'rejected' and including the final
67
+ * content if accepted.
57
68
  */
58
- openDiff(filePath: string, newContent?: string): Promise<DiffUpdateResult>;
69
+ openDiff(filePath: string, newContent: string): Promise<DiffUpdateResult>;
59
70
  /**
60
71
  * Acquires a lock to ensure sequential execution of critical sections.
61
72
  *
@@ -79,13 +90,13 @@ export declare class IdeClient {
79
90
  }): Promise<string | undefined>;
80
91
  resolveDiffFromCli(filePath: string, outcome: 'accepted' | 'rejected'): Promise<void>;
81
92
  disconnect(): Promise<void>;
82
- getCurrentIde(): DetectedIde | undefined;
93
+ getCurrentIde(): IdeInfo | undefined;
83
94
  getConnectionStatus(): IDEConnectionState;
84
95
  getDetectedIdeDisplayName(): string | undefined;
85
96
  isDiffingEnabled(): boolean;
86
97
  private discoverTools;
87
98
  private setState;
88
- static validateWorkspacePath(ideWorkspacePath: string | undefined, currentIdeDisplayName: string | undefined, cwd: string): {
99
+ static validateWorkspacePath(ideWorkspacePath: string | undefined, cwd: string): {
89
100
  isValid: boolean;
90
101
  error?: string;
91
102
  };
@@ -5,17 +5,19 @@
5
5
  */
6
6
  import * as fs from 'node:fs';
7
7
  import { isSubpath } from '../utils/paths.js';
8
- import { detectIde, getIdeInfo } from '../ide/detect-ide.js';
9
- import { ideContextStore, IdeDiffAcceptedNotificationSchema, IdeDiffClosedNotificationSchema, CloseDiffResponseSchema, } from './ideContext.js';
10
- import { IdeContextNotificationSchema } from './types.js';
8
+ import { detectIde } from '../ide/detect-ide.js';
9
+ import { ideContextStore } from './ideContext.js';
10
+ import { IdeContextNotificationSchema, IdeDiffAcceptedNotificationSchema, IdeDiffClosedNotificationSchema, IdeDiffRejectedNotificationSchema, } from './types.js';
11
11
  import { getIdeProcessInfo } from './process-utils.js';
12
12
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
13
13
  import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
14
14
  import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
15
+ import { CallToolResultSchema } from '@modelcontextprotocol/sdk/types.js';
15
16
  import * as os from 'node:os';
16
17
  import * as path from 'node:path';
17
18
  import { EnvHttpProxyAgent } from 'undici';
18
19
  import { ListToolsResultSchema } from '@modelcontextprotocol/sdk/types.js';
20
+ import { IDE_REQUEST_TIMEOUT_MS } from './constants.js';
19
21
  const logger = {
20
22
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
23
  debug: (...args) => console.debug('[DEBUG] [IDEClient]', ...args),
@@ -49,8 +51,9 @@ export class IdeClient {
49
51
  details: 'IDE integration is currently disabled. To enable it, run /ide enable.',
50
52
  };
51
53
  currentIde;
52
- currentIdeDisplayName;
53
54
  ideProcessInfo;
55
+ connectionConfig;
56
+ authToken;
54
57
  diffResponses = new Map();
55
58
  statusListeners = new Set();
56
59
  trustChangeListeners = new Set();
@@ -67,10 +70,8 @@ export class IdeClient {
67
70
  IdeClient.instancePromise = (async () => {
68
71
  const client = new IdeClient();
69
72
  client.ideProcessInfo = await getIdeProcessInfo();
70
- client.currentIde = detectIde(client.ideProcessInfo);
71
- if (client.currentIde) {
72
- client.currentIdeDisplayName = getIdeInfo(client.currentIde).displayName;
73
- }
73
+ client.connectionConfig = await client.getConnectionConfigFromFile();
74
+ client.currentIde = detectIde(client.ideProcessInfo, client.connectionConfig?.ideInfo);
74
75
  return client;
75
76
  })();
76
77
  }
@@ -89,28 +90,31 @@ export class IdeClient {
89
90
  this.trustChangeListeners.delete(listener);
90
91
  }
91
92
  async connect() {
92
- if (!this.currentIde || !this.currentIdeDisplayName) {
93
+ if (!this.currentIde) {
93
94
  this.setState(IDEConnectionStatus.Disconnected, `IDE integration is not supported in your current environment. To use this feature, run Gemini CLI in one of these supported IDEs: VS Code or VS Code forks`, false);
94
95
  return;
95
96
  }
96
97
  this.setState(IDEConnectionStatus.Connecting);
97
- const configFromFile = await this.getConnectionConfigFromFile();
98
- const workspacePath = configFromFile?.workspacePath ??
98
+ this.connectionConfig = await this.getConnectionConfigFromFile();
99
+ if (this.connectionConfig?.authToken) {
100
+ this.authToken = this.connectionConfig.authToken;
101
+ }
102
+ const workspacePath = this.connectionConfig?.workspacePath ??
99
103
  process.env['GEMINI_CLI_IDE_WORKSPACE_PATH'];
100
- const { isValid, error } = IdeClient.validateWorkspacePath(workspacePath, this.currentIdeDisplayName, process.cwd());
104
+ const { isValid, error } = IdeClient.validateWorkspacePath(workspacePath, process.cwd());
101
105
  if (!isValid) {
102
106
  this.setState(IDEConnectionStatus.Disconnected, error, true);
103
107
  return;
104
108
  }
105
- if (configFromFile) {
106
- if (configFromFile.port) {
107
- const connected = await this.establishHttpConnection(configFromFile.port);
109
+ if (this.connectionConfig) {
110
+ if (this.connectionConfig.port) {
111
+ const connected = await this.establishHttpConnection(this.connectionConfig.port);
108
112
  if (connected) {
109
113
  return;
110
114
  }
111
115
  }
112
- if (configFromFile.stdio) {
113
- const connected = await this.establishStdioConnection(configFromFile.stdio);
116
+ if (this.connectionConfig.stdio) {
117
+ const connected = await this.establishStdioConnection(this.connectionConfig.stdio);
114
118
  if (connected) {
115
119
  return;
116
120
  }
@@ -130,21 +134,25 @@ export class IdeClient {
130
134
  return;
131
135
  }
132
136
  }
133
- this.setState(IDEConnectionStatus.Disconnected, `Failed to connect to IDE companion extension in ${this.currentIdeDisplayName}. Please ensure the extension is running. To install the extension, run /ide install.`, true);
137
+ this.setState(IDEConnectionStatus.Disconnected, `Failed to connect to IDE companion extension in ${this.currentIde.displayName}. Please ensure the extension is running. To install the extension, run /ide install.`, true);
134
138
  }
135
139
  /**
136
- * A diff is accepted with any modifications if the user performs one of the
137
- * following actions:
138
- * - Clicks the checkbox icon in the IDE to accept
139
- * - Runs `command+shift+p` > "Gemini CLI: Accept Diff in IDE" to accept
140
- * - Selects "accept" in the CLI UI
141
- * - Saves the file via `ctrl/command+s`
140
+ * Opens a diff view in the IDE, allowing the user to review and accept or
141
+ * reject changes.
142
+ *
143
+ * This method sends a request to the IDE to display a diff between the
144
+ * current content of a file and the new content provided. It then waits for
145
+ * a notification from the IDE indicating that the user has either accepted
146
+ * (potentially with manual edits) or rejected the diff.
147
+ *
148
+ * A mutex ensures that only one diff view can be open at a time to prevent
149
+ * race conditions.
142
150
  *
143
- * A diff is rejected if the user performs one of the following actions:
144
- * - Clicks the "x" icon in the IDE
145
- * - Runs "Gemini CLI: Close Diff in IDE"
146
- * - Selects "no" in the CLI UI
147
- * - Closes the file
151
+ * @param filePath The absolute path to the file to be diffed.
152
+ * @param newContent The proposed new content for the file.
153
+ * @returns A promise that resolves with a `DiffUpdateResult`, indicating
154
+ * whether the diff was 'accepted' or 'rejected' and including the final
155
+ * content if accepted.
148
156
  */
149
157
  async openDiff(filePath, newContent) {
150
158
  const release = await this.acquireMutex();
@@ -155,15 +163,27 @@ export class IdeClient {
155
163
  }
156
164
  this.diffResponses.set(filePath, resolve);
157
165
  this.client
158
- .callTool({
159
- name: `openDiff`,
160
- arguments: {
161
- filePath,
162
- newContent,
166
+ .request({
167
+ method: 'tools/call',
168
+ params: {
169
+ name: `openDiff`,
170
+ arguments: {
171
+ filePath,
172
+ newContent,
173
+ },
163
174
  },
175
+ }, CallToolResultSchema, { timeout: IDE_REQUEST_TIMEOUT_MS })
176
+ .then((parsedResultData) => {
177
+ if (parsedResultData.isError) {
178
+ const textPart = parsedResultData.content.find((part) => part.type === 'text');
179
+ const errorMessage = textPart?.text ?? `Tool 'openDiff' reported an error.`;
180
+ logger.debug(`Request for openDiff ${filePath} failed with isError:`, errorMessage);
181
+ this.diffResponses.delete(filePath);
182
+ reject(new Error(errorMessage));
183
+ }
164
184
  })
165
185
  .catch((err) => {
166
- logger.debug(`callTool for ${filePath} failed:`, err);
186
+ logger.debug(`Request for openDiff ${filePath} failed:`, err);
167
187
  this.diffResponses.delete(filePath);
168
188
  reject(err);
169
189
  });
@@ -200,22 +220,48 @@ export class IdeClient {
200
220
  }
201
221
  async closeDiff(filePath, options) {
202
222
  try {
203
- const result = await this.client?.callTool({
204
- name: `closeDiff`,
205
- arguments: {
206
- filePath,
207
- suppressNotification: options?.suppressNotification,
223
+ if (!this.client) {
224
+ return undefined;
225
+ }
226
+ const resultData = await this.client.request({
227
+ method: 'tools/call',
228
+ params: {
229
+ name: `closeDiff`,
230
+ arguments: {
231
+ filePath,
232
+ suppressNotification: options?.suppressNotification,
233
+ },
208
234
  },
209
- });
210
- if (result) {
211
- const parsed = CloseDiffResponseSchema.parse(result);
212
- return parsed.content;
235
+ }, CallToolResultSchema, { timeout: IDE_REQUEST_TIMEOUT_MS });
236
+ if (!resultData) {
237
+ return undefined;
238
+ }
239
+ if (resultData.isError) {
240
+ const textPart = resultData.content.find((part) => part.type === 'text');
241
+ const errorMessage = textPart?.text ?? `Tool 'closeDiff' reported an error.`;
242
+ logger.debug(`Request for closeDiff ${filePath} failed with isError:`, errorMessage);
243
+ return undefined;
244
+ }
245
+ const textPart = resultData.content.find((part) => part.type === 'text');
246
+ if (textPart?.text) {
247
+ try {
248
+ const parsedJson = JSON.parse(textPart.text);
249
+ if (parsedJson && typeof parsedJson.content === 'string') {
250
+ return parsedJson.content;
251
+ }
252
+ if (parsedJson && parsedJson.content === null) {
253
+ return undefined;
254
+ }
255
+ }
256
+ catch (_e) {
257
+ logger.debug(`Invalid JSON in closeDiff response for ${filePath}:`, textPart.text);
258
+ }
213
259
  }
214
260
  }
215
261
  catch (err) {
216
- logger.debug(`callTool for ${filePath} failed:`, err);
262
+ logger.debug(`Request for closeDiff ${filePath} failed:`, err);
217
263
  }
218
- return;
264
+ return undefined;
219
265
  }
220
266
  // Closes the diff. Instead of waiting for a notification,
221
267
  // manually resolves the diff resolver as the desired outcome.
@@ -254,7 +300,7 @@ export class IdeClient {
254
300
  return this.state;
255
301
  }
256
302
  getDetectedIdeDisplayName() {
257
- return this.currentIdeDisplayName;
303
+ return this.currentIde?.displayName;
258
304
  }
259
305
  isDiffingEnabled() {
260
306
  return (!!this.client &&
@@ -316,17 +362,17 @@ export class IdeClient {
316
362
  ideContextStore.clear();
317
363
  }
318
364
  }
319
- static validateWorkspacePath(ideWorkspacePath, currentIdeDisplayName, cwd) {
365
+ static validateWorkspacePath(ideWorkspacePath, cwd) {
320
366
  if (ideWorkspacePath === undefined) {
321
367
  return {
322
368
  isValid: false,
323
- error: `Failed to connect to IDE companion extension in ${currentIdeDisplayName}. Please ensure the extension is running. To install the extension, run /ide install.`,
369
+ error: `Failed to connect to IDE companion extension. Please ensure the extension is running. To install the extension, run /ide install.`,
324
370
  };
325
371
  }
326
372
  if (ideWorkspacePath === '') {
327
373
  return {
328
374
  isValid: false,
329
- error: `To use this feature, please open a workspace folder in ${currentIdeDisplayName} and try again.`,
375
+ error: `To use this feature, please open a workspace folder in your IDE and try again.`,
330
376
  };
331
377
  }
332
378
  const ideWorkspacePaths = ideWorkspacePath.split(path.delimiter);
@@ -338,7 +384,7 @@ export class IdeClient {
338
384
  if (!isWithinWorkspace) {
339
385
  return {
340
386
  isValid: false,
341
- error: `Directory mismatch. Gemini CLI is running in a different location than the open workspace in ${currentIdeDisplayName}. Please run the CLI from one of the following directories: ${ideWorkspacePaths.join(', ')}`,
387
+ error: `Directory mismatch. Gemini CLI is running in a different location than the open workspace in the IDE. Please run the CLI from one of the following directories: ${ideWorkspacePaths.join(', ')}`,
342
388
  };
343
389
  }
344
390
  return { isValid: true };
@@ -389,7 +435,7 @@ export class IdeClient {
389
435
  // windows are open, multiple files matching the pattern are expected to
390
436
  // exist.
391
437
  }
392
- const portFileDir = path.join(os.tmpdir(), '.gemini', 'ide');
438
+ const portFileDir = path.join(os.tmpdir(), 'gemini', 'ide');
393
439
  let portFiles;
394
440
  try {
395
441
  portFiles = await fs.promises.readdir(portFileDir);
@@ -398,6 +444,9 @@ export class IdeClient {
398
444
  logger.debug('Failed to read IDE connection directory:', e);
399
445
  return undefined;
400
446
  }
447
+ if (!portFiles) {
448
+ return undefined;
449
+ }
401
450
  const fileRegex = new RegExp(`^gemini-ide-server-${this.ideProcessInfo.pid}-\\d+\\.json$`);
402
451
  const matchingFiles = portFiles
403
452
  .filter((file) => fileRegex.test(file))
@@ -426,7 +475,7 @@ export class IdeClient {
426
475
  if (!content) {
427
476
  return false;
428
477
  }
429
- const { isValid } = IdeClient.validateWorkspacePath(content.workspacePath, this.currentIdeDisplayName, process.cwd());
478
+ const { isValid } = IdeClient.validateWorkspacePath(content.workspacePath, process.cwd());
430
479
  return isValid;
431
480
  });
432
481
  if (validWorkspaces.length === 0) {
@@ -437,7 +486,7 @@ export class IdeClient {
437
486
  }
438
487
  const portFromEnv = this.getPortFromEnv();
439
488
  if (portFromEnv) {
440
- const matchingPort = validWorkspaces.find((content) => content.port === portFromEnv);
489
+ const matchingPort = validWorkspaces.find((content) => String(content.port) === portFromEnv);
441
490
  if (matchingPort) {
442
491
  return matchingPort;
443
492
  }
@@ -462,7 +511,7 @@ export class IdeClient {
462
511
  return new Response(response.body, {
463
512
  status: response.status,
464
513
  statusText: response.statusText,
465
- headers: response.headers,
514
+ headers: [...response.headers.entries()],
466
515
  });
467
516
  };
468
517
  }
@@ -480,10 +529,11 @@ export class IdeClient {
480
529
  }
481
530
  });
482
531
  this.client.onerror = (_error) => {
483
- this.setState(IDEConnectionStatus.Disconnected, `IDE connection error. The connection was lost unexpectedly. Please try reconnecting by running /ide enable`, true);
532
+ const errorMessage = _error instanceof Error ? _error.message : `_error`;
533
+ this.setState(IDEConnectionStatus.Disconnected, `IDE connection error. The connection was lost unexpectedly. Please try reconnecting by running /ide enable\n${errorMessage}`, true);
484
534
  };
485
535
  this.client.onclose = () => {
486
- this.setState(IDEConnectionStatus.Disconnected, `IDE connection error. The connection was lost unexpectedly. Please try reconnecting by running /ide enable`, true);
536
+ this.setState(IDEConnectionStatus.Disconnected, `IDE connection closed. To reconnect, run /ide enable.`, true);
487
537
  };
488
538
  this.client.setNotificationHandler(IdeDiffAcceptedNotificationSchema, (notification) => {
489
539
  const { filePath, content } = notification.params;
@@ -496,6 +546,19 @@ export class IdeClient {
496
546
  logger.debug(`No resolver found for ${filePath}`);
497
547
  }
498
548
  });
549
+ this.client.setNotificationHandler(IdeDiffRejectedNotificationSchema, (notification) => {
550
+ const { filePath } = notification.params;
551
+ const resolver = this.diffResponses.get(filePath);
552
+ if (resolver) {
553
+ resolver({ status: 'rejected', content: undefined });
554
+ this.diffResponses.delete(filePath);
555
+ }
556
+ else {
557
+ logger.debug(`No resolver found for ${filePath}`);
558
+ }
559
+ });
560
+ // For backwards compatability. Newer extension versions will only send
561
+ // IdeDiffRejectedNotificationSchema.
499
562
  this.client.setNotificationHandler(IdeDiffClosedNotificationSchema, (notification) => {
500
563
  const { filePath } = notification.params;
501
564
  const resolver = this.diffResponses.get(filePath);
@@ -519,6 +582,11 @@ export class IdeClient {
519
582
  });
520
583
  transport = new StreamableHTTPClientTransport(new URL(`http://${getIdeServerHost()}:${port}/mcp`), {
521
584
  fetch: this.createProxyAwareFetch(),
585
+ requestInit: {
586
+ headers: this.authToken
587
+ ? { Authorization: `Bearer ${this.authToken}` }
588
+ : {},
589
+ },
522
590
  });
523
591
  await this.client.connect(transport);
524
592
  this.registerClientHandlers();