@google/gemini-cli-core 0.5.0-preview.1 → 0.7.0-nightly.20250912.68035591
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.
- package/LICENSE +2 -2
- package/README.md +12 -2
- package/dist/google-gemini-cli-core-0.6.0-nightly.tgz +0 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/src/config/config.d.ts +24 -1
- package/dist/src/config/config.js +58 -15
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +11 -15
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/models.d.ts +14 -0
- package/dist/src/config/models.js +26 -0
- package/dist/src/config/models.js.map +1 -1
- package/dist/src/config/models.test.d.ts +6 -0
- package/dist/src/config/models.test.js +55 -0
- package/dist/src/config/models.test.js.map +1 -0
- package/dist/src/confirmation-bus/index.d.ts +7 -0
- package/dist/src/confirmation-bus/index.js +8 -0
- package/dist/src/confirmation-bus/index.js.map +1 -0
- package/dist/src/confirmation-bus/message-bus.d.ts +17 -0
- package/dist/src/confirmation-bus/message-bus.js +81 -0
- package/dist/src/confirmation-bus/message-bus.js.map +1 -0
- package/dist/src/confirmation-bus/message-bus.test.d.ts +6 -0
- package/dist/src/confirmation-bus/message-bus.test.js +164 -0
- package/dist/src/confirmation-bus/message-bus.test.js.map +1 -0
- package/dist/src/confirmation-bus/types.d.ts +38 -0
- package/dist/src/confirmation-bus/types.js +15 -0
- package/dist/src/confirmation-bus/types.js.map +1 -0
- package/dist/src/core/client.d.ts +4 -1
- package/dist/src/core/client.js +46 -18
- package/dist/src/core/client.js.map +1 -1
- package/dist/src/core/client.test.js +156 -46
- package/dist/src/core/client.test.js.map +1 -1
- package/dist/src/core/contentGenerator.d.ts +0 -1
- package/dist/src/core/contentGenerator.js +0 -4
- package/dist/src/core/contentGenerator.js.map +1 -1
- package/dist/src/core/contentGenerator.test.js +0 -3
- package/dist/src/core/contentGenerator.test.js.map +1 -1
- package/dist/src/core/coreToolScheduler.d.ts +4 -3
- package/dist/src/core/coreToolScheduler.js +42 -5
- package/dist/src/core/coreToolScheduler.js.map +1 -1
- package/dist/src/core/coreToolScheduler.test.js +34 -0
- package/dist/src/core/coreToolScheduler.test.js.map +1 -1
- package/dist/src/core/geminiChat.d.ts +1 -22
- package/dist/src/core/geminiChat.js +15 -135
- package/dist/src/core/geminiChat.js.map +1 -1
- package/dist/src/core/geminiChat.test.js +59 -312
- package/dist/src/core/geminiChat.test.js.map +1 -1
- package/dist/src/core/nonInteractiveToolExecutor.test.js +48 -0
- package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
- package/dist/src/core/subagent.js +1 -1
- package/dist/src/core/subagent.js.map +1 -1
- package/dist/src/core/subagent.test.js +9 -8
- package/dist/src/core/subagent.test.js.map +1 -1
- package/dist/src/core/turn.d.ts +2 -1
- package/dist/src/core/turn.js +2 -2
- package/dist/src/core/turn.js.map +1 -1
- package/dist/src/core/turn.test.js +18 -18
- package/dist/src/core/turn.test.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/generated/git-commit.js.map +1 -1
- package/dist/src/ide/ide-client.d.ts +27 -0
- package/dist/src/ide/ide-client.js +85 -5
- package/dist/src/ide/ide-client.js.map +1 -1
- package/dist/src/ide/ide-client.test.js +53 -0
- package/dist/src/ide/ide-client.test.js.map +1 -1
- package/dist/src/ide/ideContext.d.ts +34 -20
- package/dist/src/ide/ideContext.js +20 -33
- package/dist/src/ide/ideContext.js.map +1 -1
- package/dist/src/ide/ideContext.test.js +37 -39
- package/dist/src/ide/ideContext.test.js.map +1 -1
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.js +3 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/output/json-formatter.d.ts +11 -0
- package/dist/src/output/json-formatter.js +30 -0
- package/dist/src/output/json-formatter.js.map +1 -0
- package/dist/src/output/json-formatter.test.d.ts +6 -0
- package/dist/src/output/json-formatter.test.js +266 -0
- package/dist/src/output/json-formatter.test.js.map +1 -0
- package/dist/src/output/types.d.ts +20 -0
- package/dist/src/output/types.js +11 -0
- package/dist/src/output/types.js.map +1 -0
- package/dist/src/policy/index.d.ts +7 -0
- package/dist/src/policy/index.js +8 -0
- package/dist/src/policy/index.js.map +1 -0
- package/dist/src/policy/policy-engine.d.ts +30 -0
- package/dist/src/policy/policy-engine.js +83 -0
- package/dist/src/policy/policy-engine.js.map +1 -0
- package/dist/src/policy/policy-engine.test.d.ts +6 -0
- package/dist/src/policy/policy-engine.test.js +470 -0
- package/dist/src/policy/policy-engine.test.js.map +1 -0
- package/dist/src/policy/stable-stringify.d.ts +58 -0
- package/dist/src/policy/stable-stringify.js +122 -0
- package/dist/src/policy/stable-stringify.js.map +1 -0
- package/dist/src/policy/types.d.ts +47 -0
- package/dist/src/policy/types.js +12 -0
- package/dist/src/policy/types.js.map +1 -0
- package/dist/src/routing/modelRouterService.d.ts +23 -0
- package/dist/src/routing/modelRouterService.js +36 -0
- package/dist/src/routing/modelRouterService.js.map +1 -0
- package/dist/src/routing/modelRouterService.test.d.ts +6 -0
- package/dist/src/routing/modelRouterService.test.js +72 -0
- package/dist/src/routing/modelRouterService.test.js.map +1 -0
- package/dist/src/routing/routingStrategy.d.ts +62 -0
- package/dist/src/routing/routingStrategy.js +7 -0
- package/dist/src/routing/routingStrategy.js.map +1 -0
- package/dist/src/routing/strategies/compositeStrategy.d.ts +26 -0
- package/dist/src/routing/strategies/compositeStrategy.js +67 -0
- package/dist/src/routing/strategies/compositeStrategy.js.map +1 -0
- package/dist/src/routing/strategies/compositeStrategy.test.d.ts +6 -0
- package/dist/src/routing/strategies/compositeStrategy.test.js +123 -0
- package/dist/src/routing/strategies/compositeStrategy.test.js.map +1 -0
- package/dist/src/routing/strategies/defaultStrategy.d.ts +12 -0
- package/dist/src/routing/strategies/defaultStrategy.js +20 -0
- package/dist/src/routing/strategies/defaultStrategy.js.map +1 -0
- package/dist/src/routing/strategies/defaultStrategy.test.d.ts +6 -0
- package/dist/src/routing/strategies/defaultStrategy.test.js +26 -0
- package/dist/src/routing/strategies/defaultStrategy.test.js.map +1 -0
- package/dist/src/routing/strategies/fallbackStrategy.d.ts +12 -0
- package/dist/src/routing/strategies/fallbackStrategy.js +25 -0
- package/dist/src/routing/strategies/fallbackStrategy.js.map +1 -0
- package/dist/src/routing/strategies/fallbackStrategy.test.d.ts +6 -0
- package/dist/src/routing/strategies/fallbackStrategy.test.js +55 -0
- package/dist/src/routing/strategies/fallbackStrategy.test.js.map +1 -0
- package/dist/src/routing/strategies/overrideStrategy.d.ts +15 -0
- package/dist/src/routing/strategies/overrideStrategy.js +27 -0
- package/dist/src/routing/strategies/overrideStrategy.js.map +1 -0
- package/dist/src/routing/strategies/overrideStrategy.test.d.ts +6 -0
- package/dist/src/routing/strategies/overrideStrategy.test.js +41 -0
- package/dist/src/routing/strategies/overrideStrategy.test.js.map +1 -0
- package/dist/src/services/fileDiscoveryService.d.ts +10 -0
- package/dist/src/services/fileDiscoveryService.js +31 -17
- package/dist/src/services/fileDiscoveryService.js.map +1 -1
- package/dist/src/services/loopDetectionService.d.ts +5 -0
- package/dist/src/services/loopDetectionService.js +13 -2
- package/dist/src/services/loopDetectionService.js.map +1 -1
- package/dist/src/services/loopDetectionService.test.js +15 -0
- package/dist/src/services/loopDetectionService.test.js.map +1 -1
- package/dist/src/services/shellExecutionService.d.ts +34 -2
- package/dist/src/services/shellExecutionService.js +177 -43
- package/dist/src/services/shellExecutionService.js.map +1 -1
- package/dist/src/services/shellExecutionService.test.js +153 -56
- package/dist/src/services/shellExecutionService.test.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +4 -2
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +31 -4
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +6 -2
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +14 -2
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
- package/dist/src/telemetry/index.d.ts +2 -2
- package/dist/src/telemetry/index.js +2 -2
- package/dist/src/telemetry/index.js.map +1 -1
- package/dist/src/telemetry/loggers.d.ts +2 -1
- package/dist/src/telemetry/loggers.js +20 -5
- package/dist/src/telemetry/loggers.js.map +1 -1
- package/dist/src/telemetry/loggers.test.js +50 -35
- package/dist/src/telemetry/loggers.test.js.map +1 -1
- package/dist/src/telemetry/metrics.d.ts +1 -1
- package/dist/src/telemetry/metrics.js +2 -2
- package/dist/src/telemetry/metrics.js.map +1 -1
- package/dist/src/telemetry/types.d.ts +23 -3
- package/dist/src/telemetry/types.js +25 -3
- package/dist/src/telemetry/types.js.map +1 -1
- package/dist/src/tools/edit.js +2 -3
- package/dist/src/tools/edit.js.map +1 -1
- package/dist/src/tools/edit.test.js +2 -8
- package/dist/src/tools/edit.test.js.map +1 -1
- package/dist/src/tools/glob.d.ts +5 -1
- package/dist/src/tools/glob.js +24 -17
- package/dist/src/tools/glob.js.map +1 -1
- package/dist/src/tools/glob.test.js +51 -0
- package/dist/src/tools/glob.test.js.map +1 -1
- package/dist/src/tools/ls.js +19 -32
- package/dist/src/tools/ls.js.map +1 -1
- package/dist/src/tools/ls.test.js +140 -280
- package/dist/src/tools/ls.test.js.map +1 -1
- package/dist/src/tools/read-many-files.d.ts +1 -1
- package/dist/src/tools/read-many-files.js +17 -49
- package/dist/src/tools/read-many-files.js.map +1 -1
- package/dist/src/tools/ripGrep.d.ts +4 -0
- package/dist/src/tools/ripGrep.js +11 -1
- package/dist/src/tools/ripGrep.js.map +1 -1
- package/dist/src/tools/ripGrep.test.js +51 -1
- package/dist/src/tools/ripGrep.test.js.map +1 -1
- package/dist/src/tools/shell.d.ts +12 -2
- package/dist/src/tools/shell.js +12 -16
- package/dist/src/tools/shell.js.map +1 -1
- package/dist/src/tools/shell.test.js +2 -33
- package/dist/src/tools/shell.test.js.map +1 -1
- package/dist/src/tools/smart-edit.js +2 -3
- package/dist/src/tools/smart-edit.js.map +1 -1
- package/dist/src/tools/smart-edit.test.js +2 -8
- package/dist/src/tools/smart-edit.test.js.map +1 -1
- package/dist/src/tools/tools.d.ts +6 -4
- package/dist/src/tools/tools.js +2 -2
- package/dist/src/tools/tools.js.map +1 -1
- package/dist/src/tools/write-file.js +2 -3
- package/dist/src/tools/write-file.js.map +1 -1
- package/dist/src/tools/write-file.test.js +78 -0
- package/dist/src/tools/write-file.test.js.map +1 -1
- package/dist/src/utils/bfsFileSearch.js +11 -5
- package/dist/src/utils/bfsFileSearch.js.map +1 -1
- package/dist/src/utils/errors.d.ts +6 -0
- package/dist/src/utils/errors.js +10 -0
- package/dist/src/utils/errors.js.map +1 -1
- package/dist/src/utils/geminiIgnoreParser.d.ts +18 -0
- package/dist/src/utils/geminiIgnoreParser.js +61 -0
- package/dist/src/utils/geminiIgnoreParser.js.map +1 -0
- package/dist/src/utils/geminiIgnoreParser.test.d.ts +6 -0
- package/dist/src/utils/geminiIgnoreParser.test.js +50 -0
- package/dist/src/utils/geminiIgnoreParser.test.js.map +1 -0
- package/dist/src/utils/gitIgnoreParser.d.ts +3 -9
- package/dist/src/utils/gitIgnoreParser.js +60 -69
- package/dist/src/utils/gitIgnoreParser.js.map +1 -1
- package/dist/src/utils/gitIgnoreParser.test.js +18 -53
- package/dist/src/utils/gitIgnoreParser.test.js.map +1 -1
- package/dist/src/utils/terminalSerializer.d.ts +28 -0
- package/dist/src/utils/terminalSerializer.js +432 -0
- package/dist/src/utils/terminalSerializer.js.map +1 -0
- package/dist/src/utils/terminalSerializer.test.d.ts +6 -0
- package/dist/src/utils/terminalSerializer.test.js +176 -0
- package/dist/src/utils/terminalSerializer.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/dist/google-gemini-cli-core-0.5.0-preview.tgz +0 -0
- package/dist/src/utils/ide-trust.d.ts +0 -10
- package/dist/src/utils/ide-trust.js +0 -14
- package/dist/src/utils/ide-trust.js.map +0 -1
|
@@ -13,7 +13,7 @@ import { DEFAULT_GEMINI_FLASH_MODEL } from '../config/models.js';
|
|
|
13
13
|
import { FileDiscoveryService } from '../services/fileDiscoveryService.js';
|
|
14
14
|
import { setSimulate429 } from '../utils/testUtils.js';
|
|
15
15
|
import { tokenLimit } from './tokenLimits.js';
|
|
16
|
-
import {
|
|
16
|
+
import { ideContextStore } from '../ide/ideContext.js';
|
|
17
17
|
import { ClearcutLogger } from '../telemetry/clearcut-logger/clearcut-logger.js';
|
|
18
18
|
// Mock fs module to prevent actual file system operations during tests
|
|
19
19
|
const mockFileSystem = new Map();
|
|
@@ -108,22 +108,22 @@ describe('findIndexAfterFraction', () => {
|
|
|
108
108
|
// 0: 66
|
|
109
109
|
// 1: 66 + 68 = 134
|
|
110
110
|
// 2: 134 + 66 = 200
|
|
111
|
-
// 200 >= 166.5, so index is
|
|
112
|
-
expect(findIndexAfterFraction(history, 0.5)).toBe(
|
|
111
|
+
// 200 >= 166.5, so index is 3
|
|
112
|
+
expect(findIndexAfterFraction(history, 0.5)).toBe(3);
|
|
113
113
|
});
|
|
114
114
|
it('should handle a fraction that results in the last index', () => {
|
|
115
115
|
// 333 * 0.9 = 299.7
|
|
116
116
|
// ...
|
|
117
117
|
// 3: 200 + 68 = 268
|
|
118
118
|
// 4: 268 + 65 = 333
|
|
119
|
-
// 333 >= 299.7, so index is
|
|
120
|
-
expect(findIndexAfterFraction(history, 0.9)).toBe(
|
|
119
|
+
// 333 >= 299.7, so index is 5
|
|
120
|
+
expect(findIndexAfterFraction(history, 0.9)).toBe(5);
|
|
121
121
|
});
|
|
122
122
|
it('should handle an empty history', () => {
|
|
123
123
|
expect(findIndexAfterFraction([], 0.5)).toBe(0);
|
|
124
124
|
});
|
|
125
125
|
it('should handle a history with only one item', () => {
|
|
126
|
-
expect(findIndexAfterFraction(history.slice(0, 1), 0.5)).toBe(
|
|
126
|
+
expect(findIndexAfterFraction(history.slice(0, 1), 0.5)).toBe(1);
|
|
127
127
|
});
|
|
128
128
|
it('should handle history with weird parts', () => {
|
|
129
129
|
const historyWithEmptyParts = [
|
|
@@ -131,7 +131,7 @@ describe('findIndexAfterFraction', () => {
|
|
|
131
131
|
{ role: 'model', parts: [{ fileData: { fileUri: 'derp' } }] },
|
|
132
132
|
{ role: 'user', parts: [{ text: 'Message 2' }] },
|
|
133
133
|
];
|
|
134
|
-
expect(findIndexAfterFraction(historyWithEmptyParts, 0.5)).toBe(
|
|
134
|
+
expect(findIndexAfterFraction(historyWithEmptyParts, 0.5)).toBe(2);
|
|
135
135
|
});
|
|
136
136
|
});
|
|
137
137
|
describe('isThinkingSupported', () => {
|
|
@@ -176,7 +176,7 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
176
176
|
mockContentGenerator = {
|
|
177
177
|
generateContent: mockGenerateContentFn,
|
|
178
178
|
generateContentStream: vi.fn(),
|
|
179
|
-
countTokens: vi.fn(),
|
|
179
|
+
countTokens: vi.fn().mockResolvedValue({ totalTokens: 100 }),
|
|
180
180
|
embedContent: vi.fn(),
|
|
181
181
|
batchEmbedContents: vi.fn(),
|
|
182
182
|
};
|
|
@@ -189,7 +189,6 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
189
189
|
};
|
|
190
190
|
const fileService = new FileDiscoveryService('/test/dir');
|
|
191
191
|
const contentGeneratorConfig = {
|
|
192
|
-
model: 'test-model',
|
|
193
192
|
apiKey: 'test-key',
|
|
194
193
|
vertexai: false,
|
|
195
194
|
authType: AuthType.USE_GEMINI,
|
|
@@ -222,6 +221,9 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
222
221
|
getDirectories: vi.fn().mockReturnValue(['/test/dir']),
|
|
223
222
|
}),
|
|
224
223
|
getGeminiClient: vi.fn(),
|
|
224
|
+
getModelRouterService: vi.fn().mockReturnValue({
|
|
225
|
+
route: vi.fn().mockResolvedValue({ model: 'default-routed-model' }),
|
|
226
|
+
}),
|
|
225
227
|
isInFallbackMode: vi.fn().mockReturnValue(false),
|
|
226
228
|
setFallbackMode: vi.fn(),
|
|
227
229
|
getChatCompression: vi.fn().mockReturnValue(undefined),
|
|
@@ -400,7 +402,6 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
400
402
|
});
|
|
401
403
|
});
|
|
402
404
|
describe('tryCompressChat', () => {
|
|
403
|
-
const mockSendMessage = vi.fn();
|
|
404
405
|
const mockGetHistory = vi.fn();
|
|
405
406
|
beforeEach(() => {
|
|
406
407
|
vi.mock('./tokenLimits', () => ({
|
|
@@ -410,7 +411,6 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
410
411
|
getHistory: mockGetHistory,
|
|
411
412
|
addHistory: vi.fn(),
|
|
412
413
|
setHistory: vi.fn(),
|
|
413
|
-
sendMessage: mockSendMessage,
|
|
414
414
|
};
|
|
415
415
|
});
|
|
416
416
|
function setup({ chatHistory = [
|
|
@@ -420,7 +420,6 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
420
420
|
const mockChat = {
|
|
421
421
|
getHistory: vi.fn().mockReturnValue(chatHistory),
|
|
422
422
|
setHistory: vi.fn(),
|
|
423
|
-
sendMessage: vi.fn().mockResolvedValue({ text: 'Summary' }),
|
|
424
423
|
};
|
|
425
424
|
vi.mocked(mockContentGenerator.countTokens)
|
|
426
425
|
.mockResolvedValueOnce({ totalTokens: 1000 })
|
|
@@ -527,9 +526,15 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
527
526
|
.mockResolvedValueOnce({ totalTokens: originalTokenCount }) // First call for the check
|
|
528
527
|
.mockResolvedValueOnce({ totalTokens: newTokenCount }); // Second call for the new history
|
|
529
528
|
// Mock the summary response from the chat
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
529
|
+
mockGenerateContentFn.mockResolvedValue({
|
|
530
|
+
candidates: [
|
|
531
|
+
{
|
|
532
|
+
content: {
|
|
533
|
+
role: 'model',
|
|
534
|
+
parts: [{ text: 'This is a summary.' }],
|
|
535
|
+
},
|
|
536
|
+
},
|
|
537
|
+
],
|
|
533
538
|
});
|
|
534
539
|
await client.tryCompressChat('prompt-id-3');
|
|
535
540
|
expect(ClearcutLogger.prototype.logChatCompressionEvent).toHaveBeenCalledWith(expect.objectContaining({
|
|
@@ -553,15 +558,21 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
553
558
|
.mockResolvedValueOnce({ totalTokens: originalTokenCount }) // First call for the check
|
|
554
559
|
.mockResolvedValueOnce({ totalTokens: newTokenCount }); // Second call for the new history
|
|
555
560
|
// Mock the summary response from the chat
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
561
|
+
mockGenerateContentFn.mockResolvedValue({
|
|
562
|
+
candidates: [
|
|
563
|
+
{
|
|
564
|
+
content: {
|
|
565
|
+
role: 'model',
|
|
566
|
+
parts: [{ text: 'This is a summary.' }],
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
],
|
|
559
570
|
});
|
|
560
571
|
const initialChat = client.getChat();
|
|
561
572
|
const result = await client.tryCompressChat('prompt-id-3');
|
|
562
573
|
const newChat = client.getChat();
|
|
563
574
|
expect(tokenLimit).toHaveBeenCalled();
|
|
564
|
-
expect(
|
|
575
|
+
expect(mockGenerateContentFn).toHaveBeenCalled();
|
|
565
576
|
// Assert that summarization happened and returned the correct stats
|
|
566
577
|
expect(result).toEqual({
|
|
567
578
|
compressionStatus: CompressionStatus.COMPRESSED,
|
|
@@ -598,15 +609,21 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
598
609
|
.mockResolvedValueOnce({ totalTokens: originalTokenCount }) // First call for the check
|
|
599
610
|
.mockResolvedValueOnce({ totalTokens: newTokenCount }); // Second call for the new history
|
|
600
611
|
// Mock the summary response from the chat
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
612
|
+
mockGenerateContentFn.mockResolvedValue({
|
|
613
|
+
candidates: [
|
|
614
|
+
{
|
|
615
|
+
content: {
|
|
616
|
+
role: 'model',
|
|
617
|
+
parts: [{ text: 'This is a summary.' }],
|
|
618
|
+
},
|
|
619
|
+
},
|
|
620
|
+
],
|
|
604
621
|
});
|
|
605
622
|
const initialChat = client.getChat();
|
|
606
623
|
const result = await client.tryCompressChat('prompt-id-3');
|
|
607
624
|
const newChat = client.getChat();
|
|
608
625
|
expect(tokenLimit).toHaveBeenCalled();
|
|
609
|
-
expect(
|
|
626
|
+
expect(mockGenerateContentFn).toHaveBeenCalled();
|
|
610
627
|
// Assert that summarization happened and returned the correct stats
|
|
611
628
|
expect(result).toEqual({
|
|
612
629
|
compressionStatus: CompressionStatus.COMPRESSED,
|
|
@@ -632,14 +649,20 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
632
649
|
.mockResolvedValueOnce({ totalTokens: originalTokenCount })
|
|
633
650
|
.mockResolvedValueOnce({ totalTokens: newTokenCount });
|
|
634
651
|
// Mock the summary response from the chat
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
652
|
+
mockGenerateContentFn.mockResolvedValue({
|
|
653
|
+
candidates: [
|
|
654
|
+
{
|
|
655
|
+
content: {
|
|
656
|
+
role: 'model',
|
|
657
|
+
parts: [{ text: 'This is a summary.' }],
|
|
658
|
+
},
|
|
659
|
+
},
|
|
660
|
+
],
|
|
638
661
|
});
|
|
639
662
|
const initialChat = client.getChat();
|
|
640
663
|
const result = await client.tryCompressChat('prompt-id-1', true); // force = true
|
|
641
664
|
const newChat = client.getChat();
|
|
642
|
-
expect(
|
|
665
|
+
expect(mockGenerateContentFn).toHaveBeenCalled();
|
|
643
666
|
expect(result).toEqual({
|
|
644
667
|
compressionStatus: CompressionStatus.COMPRESSED,
|
|
645
668
|
originalTokenCount,
|
|
@@ -740,7 +763,7 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
740
763
|
});
|
|
741
764
|
it('should include editor context when ideMode is enabled', async () => {
|
|
742
765
|
// Arrange
|
|
743
|
-
vi.mocked(
|
|
766
|
+
vi.mocked(ideContextStore.get).mockReturnValue({
|
|
744
767
|
workspaceState: {
|
|
745
768
|
openFiles: [
|
|
746
769
|
{
|
|
@@ -777,7 +800,7 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
777
800
|
// consume stream
|
|
778
801
|
}
|
|
779
802
|
// Assert
|
|
780
|
-
expect(
|
|
803
|
+
expect(ideContextStore.get).toHaveBeenCalled();
|
|
781
804
|
const expectedContext = `
|
|
782
805
|
Here is the user's editor context as a JSON object. This is for your information only.
|
|
783
806
|
\`\`\`json
|
|
@@ -802,7 +825,7 @@ ${JSON.stringify({
|
|
|
802
825
|
});
|
|
803
826
|
it('should not add context if ideMode is enabled but no open files', async () => {
|
|
804
827
|
// Arrange
|
|
805
|
-
vi.mocked(
|
|
828
|
+
vi.mocked(ideContextStore.get).mockReturnValue({
|
|
806
829
|
workspaceState: {
|
|
807
830
|
openFiles: [],
|
|
808
831
|
},
|
|
@@ -824,12 +847,16 @@ ${JSON.stringify({
|
|
|
824
847
|
// consume stream
|
|
825
848
|
}
|
|
826
849
|
// Assert
|
|
827
|
-
expect(
|
|
828
|
-
|
|
850
|
+
expect(ideContextStore.get).toHaveBeenCalled();
|
|
851
|
+
// The `turn.run` method is now called with the model name as the first
|
|
852
|
+
// argument. We use `expect.any(String)` because this test is
|
|
853
|
+
// concerned with the IDE context logic, not the model routing,
|
|
854
|
+
// which is tested in its own dedicated suite.
|
|
855
|
+
expect(mockTurnRunFn).toHaveBeenCalledWith(expect.any(String), initialRequest, expect.any(Object));
|
|
829
856
|
});
|
|
830
857
|
it('should add context if ideMode is enabled and there is one active file', async () => {
|
|
831
858
|
// Arrange
|
|
832
|
-
vi.mocked(
|
|
859
|
+
vi.mocked(ideContextStore.get).mockReturnValue({
|
|
833
860
|
workspaceState: {
|
|
834
861
|
openFiles: [
|
|
835
862
|
{
|
|
@@ -859,7 +886,7 @@ ${JSON.stringify({
|
|
|
859
886
|
// consume stream
|
|
860
887
|
}
|
|
861
888
|
// Assert
|
|
862
|
-
expect(
|
|
889
|
+
expect(ideContextStore.get).toHaveBeenCalled();
|
|
863
890
|
const expectedContext = `
|
|
864
891
|
Here is the user's editor context as a JSON object. This is for your information only.
|
|
865
892
|
\`\`\`json
|
|
@@ -883,7 +910,7 @@ ${JSON.stringify({
|
|
|
883
910
|
});
|
|
884
911
|
it('should add context if ideMode is enabled and there are open files but no active file', async () => {
|
|
885
912
|
// Arrange
|
|
886
|
-
vi.mocked(
|
|
913
|
+
vi.mocked(ideContextStore.get).mockReturnValue({
|
|
887
914
|
workspaceState: {
|
|
888
915
|
openFiles: [
|
|
889
916
|
{
|
|
@@ -914,7 +941,7 @@ ${JSON.stringify({
|
|
|
914
941
|
// consume stream
|
|
915
942
|
}
|
|
916
943
|
// Assert
|
|
917
|
-
expect(
|
|
944
|
+
expect(ideContextStore.get).toHaveBeenCalled();
|
|
918
945
|
const expectedContext = `
|
|
919
946
|
Here is the user's editor context as a JSON object. This is for your information only.
|
|
920
947
|
\`\`\`json
|
|
@@ -1110,6 +1137,91 @@ ${JSON.stringify({
|
|
|
1110
1137
|
console.log(`Infinite loop protection working: checkNextSpeaker called ${callCount} times, ` +
|
|
1111
1138
|
`${eventCount} events generated (properly bounded by MAX_TURNS)`);
|
|
1112
1139
|
});
|
|
1140
|
+
describe('Model Routing', () => {
|
|
1141
|
+
let mockRouterService;
|
|
1142
|
+
beforeEach(() => {
|
|
1143
|
+
mockRouterService = {
|
|
1144
|
+
route: vi
|
|
1145
|
+
.fn()
|
|
1146
|
+
.mockResolvedValue({ model: 'routed-model', reason: 'test' }),
|
|
1147
|
+
};
|
|
1148
|
+
vi.mocked(mockConfig.getModelRouterService).mockReturnValue(mockRouterService);
|
|
1149
|
+
mockTurnRunFn.mockReturnValue((async function* () {
|
|
1150
|
+
yield { type: 'content', value: 'Hello' };
|
|
1151
|
+
})());
|
|
1152
|
+
});
|
|
1153
|
+
it('should use the model router service to select a model on the first turn', async () => {
|
|
1154
|
+
const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-1');
|
|
1155
|
+
await fromAsync(stream); // consume stream
|
|
1156
|
+
expect(mockConfig.getModelRouterService).toHaveBeenCalled();
|
|
1157
|
+
expect(mockRouterService.route).toHaveBeenCalled();
|
|
1158
|
+
expect(mockTurnRunFn).toHaveBeenCalledWith('routed-model', // The model from the router
|
|
1159
|
+
[{ text: 'Hi' }], expect.any(Object));
|
|
1160
|
+
});
|
|
1161
|
+
it('should use the same model for subsequent turns in the same prompt (stickiness)', async () => {
|
|
1162
|
+
// First turn
|
|
1163
|
+
let stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-1');
|
|
1164
|
+
await fromAsync(stream);
|
|
1165
|
+
expect(mockRouterService.route).toHaveBeenCalledTimes(1);
|
|
1166
|
+
expect(mockTurnRunFn).toHaveBeenCalledWith('routed-model', [{ text: 'Hi' }], expect.any(Object));
|
|
1167
|
+
// Second turn
|
|
1168
|
+
stream = client.sendMessageStream([{ text: 'Continue' }], new AbortController().signal, 'prompt-1');
|
|
1169
|
+
await fromAsync(stream);
|
|
1170
|
+
// Router should not be called again
|
|
1171
|
+
expect(mockRouterService.route).toHaveBeenCalledTimes(1);
|
|
1172
|
+
// Should stick to the first model
|
|
1173
|
+
expect(mockTurnRunFn).toHaveBeenCalledWith('routed-model', [{ text: 'Continue' }], expect.any(Object));
|
|
1174
|
+
});
|
|
1175
|
+
it('should reset the sticky model and re-route when the prompt_id changes', async () => {
|
|
1176
|
+
// First prompt
|
|
1177
|
+
let stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-1');
|
|
1178
|
+
await fromAsync(stream);
|
|
1179
|
+
expect(mockRouterService.route).toHaveBeenCalledTimes(1);
|
|
1180
|
+
expect(mockTurnRunFn).toHaveBeenCalledWith('routed-model', [{ text: 'Hi' }], expect.any(Object));
|
|
1181
|
+
// New prompt
|
|
1182
|
+
mockRouterService.route.mockResolvedValue({
|
|
1183
|
+
model: 'new-routed-model',
|
|
1184
|
+
reason: 'test',
|
|
1185
|
+
});
|
|
1186
|
+
stream = client.sendMessageStream([{ text: 'A new topic' }], new AbortController().signal, 'prompt-2');
|
|
1187
|
+
await fromAsync(stream);
|
|
1188
|
+
// Router should be called again for the new prompt
|
|
1189
|
+
expect(mockRouterService.route).toHaveBeenCalledTimes(2);
|
|
1190
|
+
// Should use the newly routed model
|
|
1191
|
+
expect(mockTurnRunFn).toHaveBeenCalledWith('new-routed-model', [{ text: 'A new topic' }], expect.any(Object));
|
|
1192
|
+
});
|
|
1193
|
+
it('should use the fallback model and bypass routing when in fallback mode', async () => {
|
|
1194
|
+
vi.mocked(mockConfig.isInFallbackMode).mockReturnValue(true);
|
|
1195
|
+
mockRouterService.route.mockResolvedValue({
|
|
1196
|
+
model: DEFAULT_GEMINI_FLASH_MODEL,
|
|
1197
|
+
reason: 'fallback',
|
|
1198
|
+
});
|
|
1199
|
+
const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-1');
|
|
1200
|
+
await fromAsync(stream);
|
|
1201
|
+
expect(mockTurnRunFn).toHaveBeenCalledWith(DEFAULT_GEMINI_FLASH_MODEL, [{ text: 'Hi' }], expect.any(Object));
|
|
1202
|
+
});
|
|
1203
|
+
it('should stick to the fallback model for the entire sequence even if fallback mode ends', async () => {
|
|
1204
|
+
// Start the sequence in fallback mode
|
|
1205
|
+
vi.mocked(mockConfig.isInFallbackMode).mockReturnValue(true);
|
|
1206
|
+
mockRouterService.route.mockResolvedValue({
|
|
1207
|
+
model: DEFAULT_GEMINI_FLASH_MODEL,
|
|
1208
|
+
reason: 'fallback',
|
|
1209
|
+
});
|
|
1210
|
+
let stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-fallback-stickiness');
|
|
1211
|
+
await fromAsync(stream);
|
|
1212
|
+
// First call should use fallback model
|
|
1213
|
+
expect(mockTurnRunFn).toHaveBeenCalledWith(DEFAULT_GEMINI_FLASH_MODEL, [{ text: 'Hi' }], expect.any(Object));
|
|
1214
|
+
// End fallback mode
|
|
1215
|
+
vi.mocked(mockConfig.isInFallbackMode).mockReturnValue(false);
|
|
1216
|
+
// Second call in the same sequence
|
|
1217
|
+
stream = client.sendMessageStream([{ text: 'Continue' }], new AbortController().signal, 'prompt-fallback-stickiness');
|
|
1218
|
+
await fromAsync(stream);
|
|
1219
|
+
// Router should still not be called, and it should stick to the fallback model
|
|
1220
|
+
expect(mockTurnRunFn).toHaveBeenCalledTimes(2); // Ensure it was called again
|
|
1221
|
+
expect(mockTurnRunFn).toHaveBeenLastCalledWith(DEFAULT_GEMINI_FLASH_MODEL, // Still the fallback model
|
|
1222
|
+
[{ text: 'Continue' }], expect.any(Object));
|
|
1223
|
+
});
|
|
1224
|
+
});
|
|
1113
1225
|
describe('Editor context delta', () => {
|
|
1114
1226
|
const mockStream = (async function* () {
|
|
1115
1227
|
yield { type: 'content', value: 'Hello' };
|
|
@@ -1126,7 +1238,6 @@ ${JSON.stringify({
|
|
|
1126
1238
|
const mockChat = {
|
|
1127
1239
|
addHistory: vi.fn(),
|
|
1128
1240
|
setHistory: vi.fn(),
|
|
1129
|
-
sendMessage: vi.fn().mockResolvedValue({ text: 'summary' }),
|
|
1130
1241
|
// Assume history is not empty for delta checks
|
|
1131
1242
|
getHistory: vi
|
|
1132
1243
|
.fn()
|
|
@@ -1250,7 +1361,7 @@ ${JSON.stringify({
|
|
|
1250
1361
|
},
|
|
1251
1362
|
};
|
|
1252
1363
|
// Setup current context
|
|
1253
|
-
vi.mocked(
|
|
1364
|
+
vi.mocked(ideContextStore.get).mockReturnValue({
|
|
1254
1365
|
workspaceState: {
|
|
1255
1366
|
openFiles: [
|
|
1256
1367
|
{ ...currentActiveFile, isActive: true, timestamp: Date.now() },
|
|
@@ -1296,7 +1407,7 @@ ${JSON.stringify({
|
|
|
1296
1407
|
},
|
|
1297
1408
|
};
|
|
1298
1409
|
// Setup current context (same as previous)
|
|
1299
|
-
vi.mocked(
|
|
1410
|
+
vi.mocked(ideContextStore.get).mockReturnValue({
|
|
1300
1411
|
workspaceState: {
|
|
1301
1412
|
openFiles: [
|
|
1302
1413
|
{ ...activeFile, isActive: true, timestamp: Date.now() },
|
|
@@ -1341,11 +1452,10 @@ ${JSON.stringify({
|
|
|
1341
1452
|
addHistory: vi.fn(),
|
|
1342
1453
|
getHistory: vi.fn().mockReturnValue([]), // Default empty history
|
|
1343
1454
|
setHistory: vi.fn(),
|
|
1344
|
-
sendMessage: vi.fn().mockResolvedValue({ text: 'summary' }),
|
|
1345
1455
|
};
|
|
1346
1456
|
client['chat'] = mockChat;
|
|
1347
1457
|
vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
|
|
1348
|
-
vi.mocked(
|
|
1458
|
+
vi.mocked(ideContextStore.get).mockReturnValue({
|
|
1349
1459
|
workspaceState: {
|
|
1350
1460
|
openFiles: [{ path: '/path/to/file.ts', timestamp: Date.now() }],
|
|
1351
1461
|
},
|
|
@@ -1421,7 +1531,7 @@ ${JSON.stringify({
|
|
|
1421
1531
|
openFiles: [{ path: '/path/to/fileA.ts', timestamp: Date.now() }],
|
|
1422
1532
|
},
|
|
1423
1533
|
};
|
|
1424
|
-
vi.mocked(
|
|
1534
|
+
vi.mocked(ideContextStore.get).mockReturnValue(initialIdeContext);
|
|
1425
1535
|
// Act: Send the tool response
|
|
1426
1536
|
let stream = client.sendMessageStream([
|
|
1427
1537
|
{
|
|
@@ -1467,7 +1577,7 @@ ${JSON.stringify({
|
|
|
1467
1577
|
openFiles: [{ path: '/path/to/fileB.ts', timestamp: Date.now() }],
|
|
1468
1578
|
},
|
|
1469
1579
|
};
|
|
1470
|
-
vi.mocked(
|
|
1580
|
+
vi.mocked(ideContextStore.get).mockReturnValue(newIdeContext);
|
|
1471
1581
|
// Act: Send a new, regular user message
|
|
1472
1582
|
stream = client.sendMessageStream([{ text: 'Thanks!' }], new AbortController().signal, 'prompt-id-final');
|
|
1473
1583
|
for await (const _ of stream) {
|
|
@@ -1497,7 +1607,7 @@ ${JSON.stringify({
|
|
|
1497
1607
|
],
|
|
1498
1608
|
},
|
|
1499
1609
|
};
|
|
1500
|
-
vi.mocked(
|
|
1610
|
+
vi.mocked(ideContextStore.get).mockReturnValue(contextA);
|
|
1501
1611
|
// Act: Send a regular message to establish the initial context
|
|
1502
1612
|
let stream = client.sendMessageStream([{ text: 'Initial message' }], new AbortController().signal, 'prompt-id-initial');
|
|
1503
1613
|
for await (const _ of stream) {
|
|
@@ -1530,7 +1640,7 @@ ${JSON.stringify({
|
|
|
1530
1640
|
],
|
|
1531
1641
|
},
|
|
1532
1642
|
};
|
|
1533
|
-
vi.mocked(
|
|
1643
|
+
vi.mocked(ideContextStore.get).mockReturnValue(contextB);
|
|
1534
1644
|
// Act: Send the tool response
|
|
1535
1645
|
stream = client.sendMessageStream([
|
|
1536
1646
|
{
|
|
@@ -1575,7 +1685,7 @@ ${JSON.stringify({
|
|
|
1575
1685
|
],
|
|
1576
1686
|
},
|
|
1577
1687
|
};
|
|
1578
|
-
vi.mocked(
|
|
1688
|
+
vi.mocked(ideContextStore.get).mockReturnValue(contextC);
|
|
1579
1689
|
// Act: Send a new, regular user message
|
|
1580
1690
|
stream = client.sendMessageStream([{ text: 'Thanks!' }], new AbortController().signal, 'prompt-id-final');
|
|
1581
1691
|
for await (const _ of stream) {
|