@office-ai/aioncli-core 0.1.21 → 0.2.3
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/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/src/code_assist/converter.d.ts +3 -2
- package/dist/src/code_assist/converter.js +1 -0
- package/dist/src/code_assist/converter.js.map +1 -1
- package/dist/src/code_assist/oauth2.d.ts +2 -0
- package/dist/src/code_assist/oauth2.js +47 -25
- package/dist/src/code_assist/oauth2.js.map +1 -1
- package/dist/src/code_assist/oauth2.test.js +99 -8
- package/dist/src/code_assist/oauth2.test.js.map +1 -1
- package/dist/src/code_assist/server.js +1 -1
- package/dist/src/code_assist/server.js.map +1 -1
- package/dist/src/code_assist/setup.js +48 -17
- package/dist/src/code_assist/setup.js.map +1 -1
- package/dist/src/code_assist/setup.test.js +114 -8
- package/dist/src/code_assist/setup.test.js.map +1 -1
- package/dist/src/config/config.d.ts +27 -8
- package/dist/src/config/config.js +56 -25
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +109 -1
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/core/client.d.ts +18 -15
- package/dist/src/core/client.js +275 -63
- package/dist/src/core/client.js.map +1 -1
- package/dist/src/core/client.test.js +631 -36
- package/dist/src/core/client.test.js.map +1 -1
- package/dist/src/core/contentGenerator.js +20 -12
- package/dist/src/core/contentGenerator.js.map +1 -1
- package/dist/src/core/contentGenerator.test.js +39 -15
- package/dist/src/core/contentGenerator.test.js.map +1 -1
- package/dist/src/core/coreToolScheduler.d.ts +2 -1
- package/dist/src/core/coreToolScheduler.js +26 -4
- package/dist/src/core/coreToolScheduler.js.map +1 -1
- package/dist/src/core/coreToolScheduler.test.js +230 -71
- package/dist/src/core/coreToolScheduler.test.js.map +1 -1
- package/dist/src/core/geminiChat.d.ts +11 -1
- package/dist/src/core/geminiChat.js +85 -11
- package/dist/src/core/geminiChat.js.map +1 -1
- package/dist/src/core/logger.d.ts +22 -1
- package/dist/src/core/logger.js +103 -17
- package/dist/src/core/logger.js.map +1 -1
- package/dist/src/core/logger.test.js +86 -20
- package/dist/src/core/logger.test.js.map +1 -1
- package/dist/src/core/loggingContentGenerator.d.ts +1 -0
- package/dist/src/core/loggingContentGenerator.js +7 -1
- package/dist/src/core/loggingContentGenerator.js.map +1 -1
- package/dist/src/core/nonInteractiveToolExecutor.d.ts +2 -2
- package/dist/src/core/nonInteractiveToolExecutor.js +11 -3
- package/dist/src/core/nonInteractiveToolExecutor.js.map +1 -1
- package/dist/src/core/nonInteractiveToolExecutor.test.js +95 -46
- package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
- package/dist/src/core/openaiContentGenerator.js +71 -25
- package/dist/src/core/openaiContentGenerator.js.map +1 -1
- package/dist/src/core/openaiContentGenerator.test.js +1 -1
- package/dist/src/core/openaiContentGenerator.test.js.map +1 -1
- package/dist/src/core/prompts.js +4 -4
- package/dist/src/core/prompts.js.map +1 -1
- package/dist/src/core/subagent.js +5 -5
- package/dist/src/core/subagent.js.map +1 -1
- package/dist/src/core/subagent.test.js +3 -3
- package/dist/src/core/subagent.test.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +7 -0
- package/dist/src/generated/git-commit.js +10 -0
- package/dist/src/generated/git-commit.js.map +1 -0
- package/dist/src/ide/constants.d.ts +6 -0
- package/dist/src/ide/constants.js +7 -0
- package/dist/src/ide/constants.js.map +1 -0
- package/dist/src/ide/detect-ide.d.ts +12 -2
- package/dist/src/ide/detect-ide.js +64 -5
- package/dist/src/ide/detect-ide.js.map +1 -1
- package/dist/src/ide/detect-ide.test.d.ts +6 -0
- package/dist/src/ide/detect-ide.test.js +65 -0
- package/dist/src/ide/detect-ide.test.js.map +1 -0
- package/dist/src/ide/ide-client.d.ts +9 -1
- package/dist/src/ide/ide-client.js +113 -30
- package/dist/src/ide/ide-client.js.map +1 -1
- package/dist/src/ide/ide-client.test.d.ts +6 -0
- package/dist/src/ide/ide-client.test.js +43 -0
- package/dist/src/ide/ide-client.test.js.map +1 -0
- package/dist/src/ide/ide-installer.js +23 -34
- package/dist/src/ide/ide-installer.js.map +1 -1
- package/dist/src/ide/ide-installer.test.js +6 -8
- package/dist/src/ide/ide-installer.test.js.map +1 -1
- package/dist/src/ide/ideContext.d.ts +42 -42
- package/dist/src/ide/process-utils.d.ts +19 -0
- package/dist/src/ide/process-utils.js +140 -0
- package/dist/src/ide/process-utils.js.map +1 -0
- package/dist/src/index.d.ts +6 -1
- package/dist/src/index.js +6 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/google-auth-provider.js +9 -0
- package/dist/src/mcp/google-auth-provider.js.map +1 -1
- package/dist/src/mcp/google-auth-provider.test.js +45 -10
- package/dist/src/mcp/google-auth-provider.test.js.map +1 -1
- package/dist/src/mcp/oauth-provider.d.ts +0 -1
- package/dist/src/mcp/oauth-provider.js +176 -59
- package/dist/src/mcp/oauth-provider.js.map +1 -1
- package/dist/src/mcp/oauth-provider.test.js +132 -62
- package/dist/src/mcp/oauth-provider.test.js.map +1 -1
- package/dist/src/mcp/oauth-utils.d.ts +3 -1
- package/dist/src/mcp/oauth-utils.js +50 -12
- package/dist/src/mcp/oauth-utils.js.map +1 -1
- package/dist/src/mcp/oauth-utils.test.js +17 -2
- package/dist/src/mcp/oauth-utils.test.js.map +1 -1
- package/dist/src/mocks/msw.d.ts +6 -0
- package/dist/src/mocks/msw.js +8 -0
- package/dist/src/mocks/msw.js.map +1 -0
- package/dist/src/services/chatRecordingService.d.ts +150 -0
- package/dist/src/services/chatRecordingService.js +318 -0
- package/dist/src/services/chatRecordingService.js.map +1 -0
- package/dist/src/services/chatRecordingService.test.d.ts +6 -0
- package/dist/src/services/chatRecordingService.test.js +288 -0
- package/dist/src/services/chatRecordingService.test.js.map +1 -0
- package/dist/src/services/fileSystemService.d.ts +31 -0
- package/dist/src/services/fileSystemService.js +18 -0
- package/dist/src/services/fileSystemService.js.map +1 -0
- package/dist/src/services/fileSystemService.test.d.ts +6 -0
- package/dist/src/services/fileSystemService.test.js +41 -0
- package/dist/src/services/fileSystemService.test.js.map +1 -0
- package/dist/src/services/loopDetectionService.js +9 -10
- package/dist/src/services/loopDetectionService.js.map +1 -1
- package/dist/src/services/loopDetectionService.test.js +52 -0
- package/dist/src/services/loopDetectionService.test.js.map +1 -1
- package/dist/src/services/shellExecutionService.d.ts +8 -10
- package/dist/src/services/shellExecutionService.js +289 -133
- package/dist/src/services/shellExecutionService.js.map +1 -1
- package/dist/src/services/shellExecutionService.test.js +274 -30
- package/dist/src/services/shellExecutionService.test.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +61 -17
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +211 -233
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.d.ts +11 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +250 -94
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +9 -2
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +19 -9
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
- package/dist/src/telemetry/constants.d.ts +1 -0
- package/dist/src/telemetry/constants.js +1 -0
- package/dist/src/telemetry/constants.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/integration.test.circular.js +1 -0
- package/dist/src/telemetry/integration.test.circular.js.map +1 -1
- package/dist/src/telemetry/loggers.d.ts +3 -1
- package/dist/src/telemetry/loggers.js +39 -6
- package/dist/src/telemetry/loggers.js.map +1 -1
- package/dist/src/telemetry/loggers.test.js +37 -7
- package/dist/src/telemetry/loggers.test.js.map +1 -1
- package/dist/src/telemetry/metrics.d.ts +5 -1
- package/dist/src/telemetry/metrics.js +23 -9
- package/dist/src/telemetry/metrics.js.map +1 -1
- package/dist/src/telemetry/metrics.test.js +31 -1
- package/dist/src/telemetry/metrics.test.js.map +1 -1
- package/dist/src/telemetry/sdk.d.ts +1 -1
- package/dist/src/telemetry/sdk.js +80 -44
- package/dist/src/telemetry/sdk.js.map +1 -1
- package/dist/src/telemetry/sdk.test.d.ts +6 -0
- package/dist/src/telemetry/sdk.test.js +82 -0
- package/dist/src/telemetry/sdk.test.js.map +1 -0
- package/dist/src/telemetry/telemetry.test.js +2 -2
- package/dist/src/telemetry/telemetry.test.js.map +1 -1
- package/dist/src/telemetry/types.d.ts +41 -14
- package/dist/src/telemetry/types.js +52 -23
- package/dist/src/telemetry/types.js.map +1 -1
- package/dist/src/telemetry/uiTelemetry.d.ts +4 -0
- package/dist/src/telemetry/uiTelemetry.js +14 -1
- package/dist/src/telemetry/uiTelemetry.js.map +1 -1
- package/dist/src/telemetry/uiTelemetry.test.js +45 -9
- package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
- package/dist/src/test-utils/config.d.ts +16 -0
- package/dist/src/test-utils/config.js +32 -0
- package/dist/src/test-utils/config.js.map +1 -0
- package/dist/src/test-utils/tools.d.ts +29 -8
- package/dist/src/test-utils/tools.js +80 -16
- package/dist/src/test-utils/tools.js.map +1 -1
- package/dist/src/tools/edit.d.ts +1 -1
- package/dist/src/tools/edit.js +23 -19
- package/dist/src/tools/edit.js.map +1 -1
- package/dist/src/tools/edit.test.js +11 -3
- package/dist/src/tools/edit.test.js.map +1 -1
- package/dist/src/tools/glob.d.ts +1 -1
- package/dist/src/tools/glob.js +15 -16
- package/dist/src/tools/glob.js.map +1 -1
- package/dist/src/tools/glob.test.js +20 -0
- package/dist/src/tools/glob.test.js.map +1 -1
- package/dist/src/tools/grep.d.ts +1 -1
- package/dist/src/tools/grep.js +7 -13
- package/dist/src/tools/grep.js.map +1 -1
- package/dist/src/tools/ls.d.ts +4 -23
- package/dist/src/tools/ls.js +77 -79
- package/dist/src/tools/ls.js.map +1 -1
- package/dist/src/tools/ls.test.js +62 -34
- package/dist/src/tools/ls.test.js.map +1 -1
- package/dist/src/tools/mcp-client-manager.d.ts +38 -0
- package/dist/src/tools/mcp-client-manager.js +74 -0
- package/dist/src/tools/mcp-client-manager.js.map +1 -0
- package/dist/src/tools/mcp-client-manager.test.d.ts +6 -0
- package/dist/src/tools/mcp-client-manager.test.js +39 -0
- package/dist/src/tools/mcp-client-manager.test.js.map +1 -0
- package/dist/src/tools/mcp-client.d.ts +43 -0
- package/dist/src/tools/mcp-client.js +157 -11
- package/dist/src/tools/mcp-client.js.map +1 -1
- package/dist/src/tools/mcp-client.test.js +62 -276
- package/dist/src/tools/mcp-client.test.js.map +1 -1
- package/dist/src/tools/mcp-tool.d.ts +6 -13
- package/dist/src/tools/mcp-tool.js +62 -34
- package/dist/src/tools/mcp-tool.js.map +1 -1
- package/dist/src/tools/mcp-tool.test.js +118 -59
- package/dist/src/tools/mcp-tool.test.js.map +1 -1
- package/dist/src/tools/memoryTool.d.ts +9 -13
- package/dist/src/tools/memoryTool.js +122 -121
- package/dist/src/tools/memoryTool.js.map +1 -1
- package/dist/src/tools/memoryTool.test.js +38 -18
- package/dist/src/tools/memoryTool.test.js.map +1 -1
- package/dist/src/tools/read-file.d.ts +1 -1
- package/dist/src/tools/read-file.js +20 -18
- package/dist/src/tools/read-file.js.map +1 -1
- package/dist/src/tools/read-file.test.js +8 -0
- package/dist/src/tools/read-file.test.js.map +1 -1
- package/dist/src/tools/read-many-files.d.ts +3 -5
- package/dist/src/tools/read-many-files.js +121 -105
- package/dist/src/tools/read-many-files.js.map +1 -1
- package/dist/src/tools/read-many-files.test.js +94 -37
- package/dist/src/tools/read-many-files.test.js.map +1 -1
- package/dist/src/tools/shell.d.ts +4 -6
- package/dist/src/tools/shell.js +120 -124
- package/dist/src/tools/shell.js.map +1 -1
- package/dist/src/tools/shell.test.js +63 -65
- package/dist/src/tools/shell.test.js.map +1 -1
- package/dist/src/tools/tool-error.d.ts +1 -0
- package/dist/src/tools/tool-error.js +1 -0
- package/dist/src/tools/tool-error.js.map +1 -1
- package/dist/src/tools/tool-registry.d.ts +14 -18
- package/dist/src/tools/tool-registry.js +73 -106
- package/dist/src/tools/tool-registry.js.map +1 -1
- package/dist/src/tools/tool-registry.test.js +24 -192
- package/dist/src/tools/tool-registry.test.js.map +1 -1
- package/dist/src/tools/tools.d.ts +33 -89
- package/dist/src/tools/tools.js +76 -119
- package/dist/src/tools/tools.js.map +1 -1
- package/dist/src/tools/tools.test.js +91 -2
- package/dist/src/tools/tools.test.js.map +1 -1
- package/dist/src/tools/web-fetch.d.ts +4 -7
- package/dist/src/tools/web-fetch.js +58 -64
- package/dist/src/tools/web-fetch.js.map +1 -1
- package/dist/src/tools/web-fetch.test.js +8 -4
- package/dist/src/tools/web-fetch.test.js.map +1 -1
- package/dist/src/tools/web-search.d.ts +4 -5
- package/dist/src/tools/web-search.js +47 -51
- package/dist/src/tools/web-search.js.map +1 -1
- package/dist/src/tools/web-search.test.d.ts +6 -0
- package/dist/src/tools/web-search.test.js +139 -0
- package/dist/src/tools/web-search.test.js.map +1 -0
- package/dist/src/tools/write-file.d.ts +15 -10
- package/dist/src/tools/write-file.js +134 -145
- package/dist/src/tools/write-file.js.map +1 -1
- package/dist/src/tools/write-file.test.js +82 -127
- package/dist/src/tools/write-file.test.js.map +1 -1
- package/dist/src/utils/browser.js +4 -3
- package/dist/src/utils/browser.js.map +1 -1
- package/dist/src/utils/editCorrector.js +21 -22
- package/dist/src/utils/editCorrector.js.map +1 -1
- package/dist/src/utils/editor.js +1 -1
- package/dist/src/utils/editor.js.map +1 -1
- package/dist/src/utils/editor.test.js +10 -10
- package/dist/src/utils/editor.test.js.map +1 -1
- package/dist/src/utils/environmentContext.js +2 -2
- package/dist/src/utils/environmentContext.js.map +1 -1
- package/dist/src/utils/environmentContext.test.js +3 -2
- package/dist/src/utils/environmentContext.test.js.map +1 -1
- package/dist/src/utils/errorParsing.d.ts +8 -0
- package/dist/src/utils/errorParsing.js +93 -0
- package/dist/src/utils/errorParsing.js.map +1 -0
- package/dist/src/utils/errorParsing.test.d.ts +6 -0
- package/dist/src/utils/errorParsing.test.js +172 -0
- package/dist/src/utils/errorParsing.test.js.map +1 -0
- package/dist/src/utils/fileUtils.d.ts +2 -1
- package/dist/src/utils/fileUtils.js +3 -3
- package/dist/src/utils/fileUtils.js.map +1 -1
- package/dist/src/utils/fileUtils.test.js +18 -17
- package/dist/src/utils/fileUtils.test.js.map +1 -1
- package/dist/src/utils/filesearch/crawler.d.ts +15 -0
- package/dist/src/utils/filesearch/crawler.js +50 -0
- package/dist/src/utils/filesearch/crawler.js.map +1 -0
- package/dist/src/utils/filesearch/crawler.test.d.ts +6 -0
- package/dist/src/utils/filesearch/crawler.test.js +468 -0
- package/dist/src/utils/filesearch/crawler.test.js.map +1 -0
- package/dist/src/utils/filesearch/fileSearch.d.ts +9 -53
- package/dist/src/utils/filesearch/fileSearch.js +63 -118
- package/dist/src/utils/filesearch/fileSearch.js.map +1 -1
- package/dist/src/utils/filesearch/fileSearch.test.js +95 -197
- package/dist/src/utils/filesearch/fileSearch.test.js.map +1 -1
- package/dist/src/utils/filesearch/ignore.d.ts +7 -0
- package/dist/src/utils/filesearch/ignore.js +25 -0
- package/dist/src/utils/filesearch/ignore.js.map +1 -1
- package/dist/src/utils/filesearch/ignore.test.js +89 -2
- package/dist/src/utils/filesearch/ignore.test.js.map +1 -1
- package/dist/src/utils/filesearch/result-cache.d.ts +1 -2
- package/dist/src/utils/filesearch/result-cache.js +1 -3
- package/dist/src/utils/filesearch/result-cache.js.map +1 -1
- package/dist/src/utils/filesearch/result-cache.test.js +3 -4
- package/dist/src/utils/filesearch/result-cache.test.js.map +1 -1
- package/dist/src/utils/getPty.d.ts +19 -0
- package/dist/src/utils/getPty.js +23 -0
- package/dist/src/utils/getPty.js.map +1 -0
- package/dist/src/utils/memoryDiscovery.js +3 -3
- package/dist/src/utils/memoryDiscovery.js.map +1 -1
- package/dist/src/utils/memoryDiscovery.test.js +3 -2
- package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
- package/dist/src/utils/memoryImportProcessor.js +3 -7
- package/dist/src/utils/memoryImportProcessor.js.map +1 -1
- package/dist/src/utils/memoryImportProcessor.test.js +17 -20
- package/dist/src/utils/memoryImportProcessor.test.js.map +1 -1
- package/dist/src/utils/nextSpeakerChecker.js +3 -4
- package/dist/src/utils/nextSpeakerChecker.js.map +1 -1
- package/dist/src/utils/paths.d.ts +7 -0
- package/dist/src/utils/paths.js +15 -0
- package/dist/src/utils/paths.js.map +1 -1
- package/dist/src/utils/paths.test.js +74 -2
- package/dist/src/utils/paths.test.js.map +1 -1
- package/dist/src/utils/quotaErrorDetection.d.ts +1 -5
- package/dist/src/utils/quotaErrorDetection.js.map +1 -1
- package/dist/src/utils/retry.js +40 -0
- package/dist/src/utils/retry.js.map +1 -1
- package/dist/src/utils/schemaValidator.d.ts +1 -8
- package/dist/src/utils/schemaValidator.js +1 -32
- package/dist/src/utils/schemaValidator.js.map +1 -1
- package/dist/src/utils/secure-browser-launcher.js +4 -3
- package/dist/src/utils/secure-browser-launcher.js.map +1 -1
- package/dist/src/utils/shell-utils.d.ts +39 -0
- package/dist/src/utils/shell-utils.js +72 -2
- package/dist/src/utils/shell-utils.js.map +1 -1
- package/dist/src/utils/shell-utils.test.js +132 -4
- package/dist/src/utils/shell-utils.test.js.map +1 -1
- package/dist/src/utils/systemEncoding.js +1 -1
- package/dist/src/utils/systemEncoding.js.map +1 -1
- package/dist/src/utils/systemEncoding.test.js +23 -23
- package/dist/src/utils/systemEncoding.test.js.map +1 -1
- package/dist/src/utils/user_account.js +58 -48
- package/dist/src/utils/user_account.js.map +1 -1
- package/dist/src/utils/user_account.test.js +76 -9
- package/dist/src/utils/user_account.test.js.map +1 -1
- package/dist/src/utils/workspaceContext.d.ts +22 -7
- package/dist/src/utils/workspaceContext.js +81 -55
- package/dist/src/utils/workspaceContext.js.map +1 -1
- package/dist/src/utils/workspaceContext.test.js +221 -137
- package/dist/src/utils/workspaceContext.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +21 -8
|
@@ -15,6 +15,7 @@ import { FileDiscoveryService } from '../services/fileDiscoveryService.js';
|
|
|
15
15
|
import { setSimulate429 } from '../utils/testUtils.js';
|
|
16
16
|
import { tokenLimit } from './tokenLimits.js';
|
|
17
17
|
import { ideContext } from '../ide/ideContext.js';
|
|
18
|
+
import { ClearcutLogger } from '../telemetry/clearcut-logger/clearcut-logger.js';
|
|
18
19
|
// --- Mocks ---
|
|
19
20
|
const mockChatCreateFn = vi.fn();
|
|
20
21
|
const mockGenerateContentFn = vi.fn();
|
|
@@ -151,7 +152,7 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
151
152
|
getContentGeneratorConfig: vi
|
|
152
153
|
.fn()
|
|
153
154
|
.mockReturnValue(contentGeneratorConfig),
|
|
154
|
-
getToolRegistry: vi.fn().
|
|
155
|
+
getToolRegistry: vi.fn().mockReturnValue(mockToolRegistry),
|
|
155
156
|
getModel: vi.fn().mockReturnValue('test-model'),
|
|
156
157
|
getEmbeddingModel: vi.fn().mockReturnValue('test-embedding-model'),
|
|
157
158
|
getApiKey: vi.fn().mockReturnValue('test-key'),
|
|
@@ -170,12 +171,14 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
170
171
|
getUsageStatisticsEnabled: vi.fn().mockReturnValue(true),
|
|
171
172
|
getIdeModeFeature: vi.fn().mockReturnValue(false),
|
|
172
173
|
getIdeMode: vi.fn().mockReturnValue(true),
|
|
174
|
+
getDebugMode: vi.fn().mockReturnValue(false),
|
|
173
175
|
getWorkspaceContext: vi.fn().mockReturnValue({
|
|
174
176
|
getDirectories: vi.fn().mockReturnValue(['/test/dir']),
|
|
175
177
|
}),
|
|
176
178
|
getGeminiClient: vi.fn(),
|
|
177
179
|
setFallbackMode: vi.fn(),
|
|
178
180
|
getChatCompression: vi.fn().mockReturnValue(undefined),
|
|
181
|
+
getSkipNextSpeakerCheck: vi.fn().mockReturnValue(false),
|
|
179
182
|
};
|
|
180
183
|
const MockedConfig = vi.mocked(Config, true);
|
|
181
184
|
MockedConfig.mockImplementation(() => mockConfigObject);
|
|
@@ -315,7 +318,7 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
315
318
|
systemInstruction: getCoreSystemPrompt(''),
|
|
316
319
|
temperature: 0,
|
|
317
320
|
topP: 1,
|
|
318
|
-
|
|
321
|
+
responseJsonSchema: schema,
|
|
319
322
|
responseMimeType: 'application/json',
|
|
320
323
|
},
|
|
321
324
|
contents,
|
|
@@ -341,7 +344,7 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
341
344
|
temperature: 0.9,
|
|
342
345
|
topP: 1, // from default
|
|
343
346
|
topK: 20,
|
|
344
|
-
|
|
347
|
+
responseJsonSchema: schema,
|
|
345
348
|
responseMimeType: 'application/json',
|
|
346
349
|
},
|
|
347
350
|
contents,
|
|
@@ -353,7 +356,6 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
353
356
|
const mockChat = {
|
|
354
357
|
addHistory: vi.fn(),
|
|
355
358
|
};
|
|
356
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
357
359
|
client['chat'] = mockChat;
|
|
358
360
|
const newContent = {
|
|
359
361
|
role: 'user',
|
|
@@ -419,6 +421,33 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
419
421
|
expect(result).toBeNull();
|
|
420
422
|
expect(newChat).toBe(initialChat);
|
|
421
423
|
});
|
|
424
|
+
it('logs a telemetry event when compressing', async () => {
|
|
425
|
+
vi.spyOn(ClearcutLogger.prototype, 'logChatCompressionEvent');
|
|
426
|
+
const MOCKED_TOKEN_LIMIT = 1000;
|
|
427
|
+
const MOCKED_CONTEXT_PERCENTAGE_THRESHOLD = 0.5;
|
|
428
|
+
vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
|
|
429
|
+
vi.spyOn(client['config'], 'getChatCompression').mockReturnValue({
|
|
430
|
+
contextPercentageThreshold: MOCKED_CONTEXT_PERCENTAGE_THRESHOLD,
|
|
431
|
+
});
|
|
432
|
+
mockGetHistory.mockReturnValue([
|
|
433
|
+
{ role: 'user', parts: [{ text: '...history...' }] },
|
|
434
|
+
]);
|
|
435
|
+
const originalTokenCount = MOCKED_TOKEN_LIMIT * MOCKED_CONTEXT_PERCENTAGE_THRESHOLD;
|
|
436
|
+
const newTokenCount = 100;
|
|
437
|
+
mockCountTokens
|
|
438
|
+
.mockResolvedValueOnce({ totalTokens: originalTokenCount }) // First call for the check
|
|
439
|
+
.mockResolvedValueOnce({ totalTokens: newTokenCount }); // Second call for the new history
|
|
440
|
+
// Mock the summary response from the chat
|
|
441
|
+
mockSendMessage.mockResolvedValue({
|
|
442
|
+
role: 'model',
|
|
443
|
+
parts: [{ text: 'This is a summary.' }],
|
|
444
|
+
});
|
|
445
|
+
await client.tryCompressChat('prompt-id-3');
|
|
446
|
+
expect(ClearcutLogger.prototype.logChatCompressionEvent).toHaveBeenCalledWith(expect.objectContaining({
|
|
447
|
+
tokens_before: originalTokenCount,
|
|
448
|
+
tokens_after: newTokenCount,
|
|
449
|
+
}));
|
|
450
|
+
});
|
|
422
451
|
it('should trigger summarization if token count is at threshold with contextPercentageThreshold setting', async () => {
|
|
423
452
|
const MOCKED_TOKEN_LIMIT = 1000;
|
|
424
453
|
const MOCKED_CONTEXT_PERCENTAGE_THRESHOLD = 0.5;
|
|
@@ -529,7 +558,7 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
529
558
|
});
|
|
530
559
|
});
|
|
531
560
|
describe('sendMessageStream', () => {
|
|
532
|
-
it('should include
|
|
561
|
+
it('should include editor context when ideMode is enabled', async () => {
|
|
533
562
|
// Arrange
|
|
534
563
|
vi.mocked(ideContext.getIdeContext).mockReturnValue({
|
|
535
564
|
workspaceState: {
|
|
@@ -552,7 +581,7 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
552
581
|
],
|
|
553
582
|
},
|
|
554
583
|
});
|
|
555
|
-
vi.spyOn(client['config'], '
|
|
584
|
+
vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
|
|
556
585
|
const mockStream = (async function* () {
|
|
557
586
|
yield { type: 'content', value: 'Hello' };
|
|
558
587
|
})();
|
|
@@ -576,27 +605,35 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
576
605
|
// Assert
|
|
577
606
|
expect(ideContext.getIdeContext).toHaveBeenCalled();
|
|
578
607
|
const expectedContext = `
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
608
|
+
Here is the user's editor context as a JSON object. This is for your information only.
|
|
609
|
+
\`\`\`json
|
|
610
|
+
${JSON.stringify({
|
|
611
|
+
activeFile: {
|
|
612
|
+
path: '/path/to/active/file.ts',
|
|
613
|
+
cursor: {
|
|
614
|
+
line: 5,
|
|
615
|
+
character: 10,
|
|
616
|
+
},
|
|
617
|
+
selectedText: 'hello',
|
|
618
|
+
},
|
|
619
|
+
otherOpenFiles: ['/path/to/recent/file1.ts', '/path/to/recent/file2.ts'],
|
|
620
|
+
}, null, 2)}
|
|
621
|
+
\`\`\`
|
|
588
622
|
`.trim();
|
|
589
|
-
const expectedRequest = [{ text: expectedContext }
|
|
590
|
-
expect(
|
|
623
|
+
const expectedRequest = [{ text: expectedContext }];
|
|
624
|
+
expect(mockChat.addHistory).toHaveBeenCalledWith({
|
|
625
|
+
role: 'user',
|
|
626
|
+
parts: expectedRequest,
|
|
627
|
+
});
|
|
591
628
|
});
|
|
592
|
-
it('should not add context if
|
|
629
|
+
it('should not add context if ideMode is enabled but no open files', async () => {
|
|
593
630
|
// Arrange
|
|
594
631
|
vi.mocked(ideContext.getIdeContext).mockReturnValue({
|
|
595
632
|
workspaceState: {
|
|
596
633
|
openFiles: [],
|
|
597
634
|
},
|
|
598
635
|
});
|
|
599
|
-
vi.spyOn(client['config'], '
|
|
636
|
+
vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
|
|
600
637
|
const mockStream = (async function* () {
|
|
601
638
|
yield { type: 'content', value: 'Hello' };
|
|
602
639
|
})();
|
|
@@ -621,7 +658,7 @@ Here are some other files the user has open, with the most recent at the top:
|
|
|
621
658
|
expect(ideContext.getIdeContext).toHaveBeenCalled();
|
|
622
659
|
expect(mockTurnRunFn).toHaveBeenCalledWith(initialRequest, expect.any(Object));
|
|
623
660
|
});
|
|
624
|
-
it('should add context if
|
|
661
|
+
it('should add context if ideMode is enabled and there is one active file', async () => {
|
|
625
662
|
// Arrange
|
|
626
663
|
vi.mocked(ideContext.getIdeContext).mockReturnValue({
|
|
627
664
|
workspaceState: {
|
|
@@ -636,7 +673,7 @@ Here are some other files the user has open, with the most recent at the top:
|
|
|
636
673
|
],
|
|
637
674
|
},
|
|
638
675
|
});
|
|
639
|
-
vi.spyOn(client['config'], '
|
|
676
|
+
vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
|
|
640
677
|
const mockStream = (async function* () {
|
|
641
678
|
yield { type: 'content', value: 'Hello' };
|
|
642
679
|
})();
|
|
@@ -660,17 +697,27 @@ Here are some other files the user has open, with the most recent at the top:
|
|
|
660
697
|
// Assert
|
|
661
698
|
expect(ideContext.getIdeContext).toHaveBeenCalled();
|
|
662
699
|
const expectedContext = `
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
700
|
+
Here is the user's editor context as a JSON object. This is for your information only.
|
|
701
|
+
\`\`\`json
|
|
702
|
+
${JSON.stringify({
|
|
703
|
+
activeFile: {
|
|
704
|
+
path: '/path/to/active/file.ts',
|
|
705
|
+
cursor: {
|
|
706
|
+
line: 5,
|
|
707
|
+
character: 10,
|
|
708
|
+
},
|
|
709
|
+
selectedText: 'hello',
|
|
710
|
+
},
|
|
711
|
+
}, null, 2)}
|
|
712
|
+
\`\`\`
|
|
669
713
|
`.trim();
|
|
670
|
-
const expectedRequest = [{ text: expectedContext }
|
|
671
|
-
expect(
|
|
714
|
+
const expectedRequest = [{ text: expectedContext }];
|
|
715
|
+
expect(mockChat.addHistory).toHaveBeenCalledWith({
|
|
716
|
+
role: 'user',
|
|
717
|
+
parts: expectedRequest,
|
|
718
|
+
});
|
|
672
719
|
});
|
|
673
|
-
it('should add context if
|
|
720
|
+
it('should add context if ideMode is enabled and there are open files but no active file', async () => {
|
|
674
721
|
// Arrange
|
|
675
722
|
vi.mocked(ideContext.getIdeContext).mockReturnValue({
|
|
676
723
|
workspaceState: {
|
|
@@ -686,7 +733,7 @@ This is the selected text in the file:
|
|
|
686
733
|
],
|
|
687
734
|
},
|
|
688
735
|
});
|
|
689
|
-
vi.spyOn(client['config'], '
|
|
736
|
+
vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
|
|
690
737
|
const mockStream = (async function* () {
|
|
691
738
|
yield { type: 'content', value: 'Hello' };
|
|
692
739
|
})();
|
|
@@ -710,12 +757,18 @@ This is the selected text in the file:
|
|
|
710
757
|
// Assert
|
|
711
758
|
expect(ideContext.getIdeContext).toHaveBeenCalled();
|
|
712
759
|
const expectedContext = `
|
|
713
|
-
Here
|
|
714
|
-
|
|
715
|
-
|
|
760
|
+
Here is the user's editor context as a JSON object. This is for your information only.
|
|
761
|
+
\`\`\`json
|
|
762
|
+
${JSON.stringify({
|
|
763
|
+
otherOpenFiles: ['/path/to/recent/file1.ts', '/path/to/recent/file2.ts'],
|
|
764
|
+
}, null, 2)}
|
|
765
|
+
\`\`\`
|
|
716
766
|
`.trim();
|
|
717
|
-
const expectedRequest = [{ text: expectedContext }
|
|
718
|
-
expect(
|
|
767
|
+
const expectedRequest = [{ text: expectedContext }];
|
|
768
|
+
expect(mockChat.addHistory).toHaveBeenCalledWith({
|
|
769
|
+
role: 'user',
|
|
770
|
+
parts: expectedRequest,
|
|
771
|
+
});
|
|
719
772
|
});
|
|
720
773
|
it('should return the turn instance after the stream is complete', async () => {
|
|
721
774
|
// Arrange
|
|
@@ -918,6 +971,488 @@ Here are some files the user has open, with the most recent at the top:
|
|
|
918
971
|
console.log(`Infinite loop protection working: checkNextSpeaker called ${callCount} times, ` +
|
|
919
972
|
`${eventCount} events generated (properly bounded by MAX_TURNS)`);
|
|
920
973
|
});
|
|
974
|
+
describe('Editor context delta', () => {
|
|
975
|
+
const mockStream = (async function* () {
|
|
976
|
+
yield { type: 'content', value: 'Hello' };
|
|
977
|
+
})();
|
|
978
|
+
beforeEach(() => {
|
|
979
|
+
client['forceFullIdeContext'] = false; // Reset before each delta test
|
|
980
|
+
vi.spyOn(client, 'tryCompressChat').mockResolvedValue(null);
|
|
981
|
+
vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
|
|
982
|
+
mockTurnRunFn.mockReturnValue(mockStream);
|
|
983
|
+
const mockChat = {
|
|
984
|
+
addHistory: vi.fn(),
|
|
985
|
+
setHistory: vi.fn(),
|
|
986
|
+
sendMessage: vi.fn().mockResolvedValue({ text: 'summary' }),
|
|
987
|
+
// Assume history is not empty for delta checks
|
|
988
|
+
getHistory: vi
|
|
989
|
+
.fn()
|
|
990
|
+
.mockReturnValue([
|
|
991
|
+
{ role: 'user', parts: [{ text: 'previous message' }] },
|
|
992
|
+
]),
|
|
993
|
+
};
|
|
994
|
+
client['chat'] = mockChat;
|
|
995
|
+
const mockGenerator = {
|
|
996
|
+
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
997
|
+
generateContent: mockGenerateContentFn,
|
|
998
|
+
};
|
|
999
|
+
client['contentGenerator'] = mockGenerator;
|
|
1000
|
+
});
|
|
1001
|
+
const testCases = [
|
|
1002
|
+
{
|
|
1003
|
+
description: 'sends delta when active file changes',
|
|
1004
|
+
previousActiveFile: {
|
|
1005
|
+
path: '/path/to/old/file.ts',
|
|
1006
|
+
cursor: { line: 5, character: 10 },
|
|
1007
|
+
selectedText: 'hello',
|
|
1008
|
+
},
|
|
1009
|
+
currentActiveFile: {
|
|
1010
|
+
path: '/path/to/active/file.ts',
|
|
1011
|
+
cursor: { line: 5, character: 10 },
|
|
1012
|
+
selectedText: 'hello',
|
|
1013
|
+
},
|
|
1014
|
+
shouldSendContext: true,
|
|
1015
|
+
},
|
|
1016
|
+
{
|
|
1017
|
+
description: 'sends delta when cursor line changes',
|
|
1018
|
+
previousActiveFile: {
|
|
1019
|
+
path: '/path/to/active/file.ts',
|
|
1020
|
+
cursor: { line: 1, character: 10 },
|
|
1021
|
+
selectedText: 'hello',
|
|
1022
|
+
},
|
|
1023
|
+
currentActiveFile: {
|
|
1024
|
+
path: '/path/to/active/file.ts',
|
|
1025
|
+
cursor: { line: 5, character: 10 },
|
|
1026
|
+
selectedText: 'hello',
|
|
1027
|
+
},
|
|
1028
|
+
shouldSendContext: true,
|
|
1029
|
+
},
|
|
1030
|
+
{
|
|
1031
|
+
description: 'sends delta when cursor character changes',
|
|
1032
|
+
previousActiveFile: {
|
|
1033
|
+
path: '/path/to/active/file.ts',
|
|
1034
|
+
cursor: { line: 5, character: 1 },
|
|
1035
|
+
selectedText: 'hello',
|
|
1036
|
+
},
|
|
1037
|
+
currentActiveFile: {
|
|
1038
|
+
path: '/path/to/active/file.ts',
|
|
1039
|
+
cursor: { line: 5, character: 10 },
|
|
1040
|
+
selectedText: 'hello',
|
|
1041
|
+
},
|
|
1042
|
+
shouldSendContext: true,
|
|
1043
|
+
},
|
|
1044
|
+
{
|
|
1045
|
+
description: 'sends delta when selected text changes',
|
|
1046
|
+
previousActiveFile: {
|
|
1047
|
+
path: '/path/to/active/file.ts',
|
|
1048
|
+
cursor: { line: 5, character: 10 },
|
|
1049
|
+
selectedText: 'world',
|
|
1050
|
+
},
|
|
1051
|
+
currentActiveFile: {
|
|
1052
|
+
path: '/path/to/active/file.ts',
|
|
1053
|
+
cursor: { line: 5, character: 10 },
|
|
1054
|
+
selectedText: 'hello',
|
|
1055
|
+
},
|
|
1056
|
+
shouldSendContext: true,
|
|
1057
|
+
},
|
|
1058
|
+
{
|
|
1059
|
+
description: 'sends delta when selected text is added',
|
|
1060
|
+
previousActiveFile: {
|
|
1061
|
+
path: '/path/to/active/file.ts',
|
|
1062
|
+
cursor: { line: 5, character: 10 },
|
|
1063
|
+
},
|
|
1064
|
+
currentActiveFile: {
|
|
1065
|
+
path: '/path/to/active/file.ts',
|
|
1066
|
+
cursor: { line: 5, character: 10 },
|
|
1067
|
+
selectedText: 'hello',
|
|
1068
|
+
},
|
|
1069
|
+
shouldSendContext: true,
|
|
1070
|
+
},
|
|
1071
|
+
{
|
|
1072
|
+
description: 'sends delta when selected text is removed',
|
|
1073
|
+
previousActiveFile: {
|
|
1074
|
+
path: '/path/to/active/file.ts',
|
|
1075
|
+
cursor: { line: 5, character: 10 },
|
|
1076
|
+
selectedText: 'hello',
|
|
1077
|
+
},
|
|
1078
|
+
currentActiveFile: {
|
|
1079
|
+
path: '/path/to/active/file.ts',
|
|
1080
|
+
cursor: { line: 5, character: 10 },
|
|
1081
|
+
},
|
|
1082
|
+
shouldSendContext: true,
|
|
1083
|
+
},
|
|
1084
|
+
{
|
|
1085
|
+
description: 'does not send context when nothing changes',
|
|
1086
|
+
previousActiveFile: {
|
|
1087
|
+
path: '/path/to/active/file.ts',
|
|
1088
|
+
cursor: { line: 5, character: 10 },
|
|
1089
|
+
selectedText: 'hello',
|
|
1090
|
+
},
|
|
1091
|
+
currentActiveFile: {
|
|
1092
|
+
path: '/path/to/active/file.ts',
|
|
1093
|
+
cursor: { line: 5, character: 10 },
|
|
1094
|
+
selectedText: 'hello',
|
|
1095
|
+
},
|
|
1096
|
+
shouldSendContext: false,
|
|
1097
|
+
},
|
|
1098
|
+
];
|
|
1099
|
+
it.each(testCases)('$description', async ({ previousActiveFile, currentActiveFile, shouldSendContext, }) => {
|
|
1100
|
+
// Setup previous context
|
|
1101
|
+
client['lastSentIdeContext'] = {
|
|
1102
|
+
workspaceState: {
|
|
1103
|
+
openFiles: [
|
|
1104
|
+
{
|
|
1105
|
+
path: previousActiveFile.path,
|
|
1106
|
+
cursor: previousActiveFile.cursor,
|
|
1107
|
+
selectedText: previousActiveFile.selectedText,
|
|
1108
|
+
isActive: true,
|
|
1109
|
+
timestamp: Date.now() - 1000,
|
|
1110
|
+
},
|
|
1111
|
+
],
|
|
1112
|
+
},
|
|
1113
|
+
};
|
|
1114
|
+
// Setup current context
|
|
1115
|
+
vi.mocked(ideContext.getIdeContext).mockReturnValue({
|
|
1116
|
+
workspaceState: {
|
|
1117
|
+
openFiles: [
|
|
1118
|
+
{ ...currentActiveFile, isActive: true, timestamp: Date.now() },
|
|
1119
|
+
],
|
|
1120
|
+
},
|
|
1121
|
+
});
|
|
1122
|
+
const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-id-delta');
|
|
1123
|
+
for await (const _ of stream) {
|
|
1124
|
+
// consume stream
|
|
1125
|
+
}
|
|
1126
|
+
const mockChat = client['chat'];
|
|
1127
|
+
if (shouldSendContext) {
|
|
1128
|
+
expect(mockChat.addHistory).toHaveBeenCalledWith(expect.objectContaining({
|
|
1129
|
+
parts: expect.arrayContaining([
|
|
1130
|
+
expect.objectContaining({
|
|
1131
|
+
text: expect.stringContaining("Here is a summary of changes in the user's editor context"),
|
|
1132
|
+
}),
|
|
1133
|
+
]),
|
|
1134
|
+
}));
|
|
1135
|
+
}
|
|
1136
|
+
else {
|
|
1137
|
+
expect(mockChat.addHistory).not.toHaveBeenCalled();
|
|
1138
|
+
}
|
|
1139
|
+
});
|
|
1140
|
+
it('sends full context when history is cleared, even if editor state is unchanged', async () => {
|
|
1141
|
+
const activeFile = {
|
|
1142
|
+
path: '/path/to/active/file.ts',
|
|
1143
|
+
cursor: { line: 5, character: 10 },
|
|
1144
|
+
selectedText: 'hello',
|
|
1145
|
+
};
|
|
1146
|
+
// Setup previous context
|
|
1147
|
+
client['lastSentIdeContext'] = {
|
|
1148
|
+
workspaceState: {
|
|
1149
|
+
openFiles: [
|
|
1150
|
+
{
|
|
1151
|
+
path: activeFile.path,
|
|
1152
|
+
cursor: activeFile.cursor,
|
|
1153
|
+
selectedText: activeFile.selectedText,
|
|
1154
|
+
isActive: true,
|
|
1155
|
+
timestamp: Date.now() - 1000,
|
|
1156
|
+
},
|
|
1157
|
+
],
|
|
1158
|
+
},
|
|
1159
|
+
};
|
|
1160
|
+
// Setup current context (same as previous)
|
|
1161
|
+
vi.mocked(ideContext.getIdeContext).mockReturnValue({
|
|
1162
|
+
workspaceState: {
|
|
1163
|
+
openFiles: [
|
|
1164
|
+
{ ...activeFile, isActive: true, timestamp: Date.now() },
|
|
1165
|
+
],
|
|
1166
|
+
},
|
|
1167
|
+
});
|
|
1168
|
+
// Make history empty
|
|
1169
|
+
const mockChat = client['chat'];
|
|
1170
|
+
mockChat.getHistory.mockReturnValue([]);
|
|
1171
|
+
const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-id-history-cleared');
|
|
1172
|
+
for await (const _ of stream) {
|
|
1173
|
+
// consume stream
|
|
1174
|
+
}
|
|
1175
|
+
expect(mockChat.addHistory).toHaveBeenCalledWith(expect.objectContaining({
|
|
1176
|
+
parts: expect.arrayContaining([
|
|
1177
|
+
expect.objectContaining({
|
|
1178
|
+
text: expect.stringContaining("Here is the user's editor context"),
|
|
1179
|
+
}),
|
|
1180
|
+
]),
|
|
1181
|
+
}));
|
|
1182
|
+
// Also verify it's the full context, not a delta.
|
|
1183
|
+
const call = mockChat.addHistory.mock.calls[0][0];
|
|
1184
|
+
const contextText = call.parts[0].text;
|
|
1185
|
+
const contextJson = JSON.parse(contextText.match(/```json\n(.*)\n```/s)[1]);
|
|
1186
|
+
expect(contextJson).toHaveProperty('activeFile');
|
|
1187
|
+
expect(contextJson.activeFile.path).toBe('/path/to/active/file.ts');
|
|
1188
|
+
});
|
|
1189
|
+
});
|
|
1190
|
+
describe('IDE context with pending tool calls', () => {
|
|
1191
|
+
let mockChat;
|
|
1192
|
+
beforeEach(() => {
|
|
1193
|
+
vi.spyOn(client, 'tryCompressChat').mockResolvedValue(null);
|
|
1194
|
+
const mockStream = (async function* () {
|
|
1195
|
+
yield { type: 'content', value: 'response' };
|
|
1196
|
+
})();
|
|
1197
|
+
mockTurnRunFn.mockReturnValue(mockStream);
|
|
1198
|
+
mockChat = {
|
|
1199
|
+
addHistory: vi.fn(),
|
|
1200
|
+
getHistory: vi.fn().mockReturnValue([]), // Default empty history
|
|
1201
|
+
setHistory: vi.fn(),
|
|
1202
|
+
sendMessage: vi.fn().mockResolvedValue({ text: 'summary' }),
|
|
1203
|
+
};
|
|
1204
|
+
client['chat'] = mockChat;
|
|
1205
|
+
const mockGenerator = {
|
|
1206
|
+
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
1207
|
+
};
|
|
1208
|
+
client['contentGenerator'] = mockGenerator;
|
|
1209
|
+
vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
|
|
1210
|
+
vi.mocked(ideContext.getIdeContext).mockReturnValue({
|
|
1211
|
+
workspaceState: {
|
|
1212
|
+
openFiles: [{ path: '/path/to/file.ts', timestamp: Date.now() }],
|
|
1213
|
+
},
|
|
1214
|
+
});
|
|
1215
|
+
});
|
|
1216
|
+
it('should NOT add IDE context when a tool call is pending', async () => {
|
|
1217
|
+
// Arrange: History ends with a functionCall from the model
|
|
1218
|
+
const historyWithPendingCall = [
|
|
1219
|
+
{ role: 'user', parts: [{ text: 'Please use a tool.' }] },
|
|
1220
|
+
{
|
|
1221
|
+
role: 'model',
|
|
1222
|
+
parts: [{ functionCall: { name: 'some_tool', args: {} } }],
|
|
1223
|
+
},
|
|
1224
|
+
];
|
|
1225
|
+
vi.mocked(mockChat.getHistory).mockReturnValue(historyWithPendingCall);
|
|
1226
|
+
// Act: Simulate sending the tool's response back
|
|
1227
|
+
const stream = client.sendMessageStream([
|
|
1228
|
+
{
|
|
1229
|
+
functionResponse: {
|
|
1230
|
+
name: 'some_tool',
|
|
1231
|
+
response: { success: true },
|
|
1232
|
+
},
|
|
1233
|
+
},
|
|
1234
|
+
], new AbortController().signal, 'prompt-id-tool-response');
|
|
1235
|
+
for await (const _ of stream) {
|
|
1236
|
+
// consume stream to complete the call
|
|
1237
|
+
}
|
|
1238
|
+
// Assert: The IDE context message should NOT have been added to the history.
|
|
1239
|
+
expect(mockChat.addHistory).not.toHaveBeenCalledWith(expect.objectContaining({
|
|
1240
|
+
parts: expect.arrayContaining([
|
|
1241
|
+
expect.objectContaining({
|
|
1242
|
+
text: expect.stringContaining("user's editor context"),
|
|
1243
|
+
}),
|
|
1244
|
+
]),
|
|
1245
|
+
}));
|
|
1246
|
+
});
|
|
1247
|
+
it('should add IDE context when no tool call is pending', async () => {
|
|
1248
|
+
// Arrange: History is normal, no pending calls
|
|
1249
|
+
const normalHistory = [
|
|
1250
|
+
{ role: 'user', parts: [{ text: 'A normal message.' }] },
|
|
1251
|
+
{ role: 'model', parts: [{ text: 'A normal response.' }] },
|
|
1252
|
+
];
|
|
1253
|
+
vi.mocked(mockChat.getHistory).mockReturnValue(normalHistory);
|
|
1254
|
+
// Act
|
|
1255
|
+
const stream = client.sendMessageStream([{ text: 'Another normal message' }], new AbortController().signal, 'prompt-id-normal');
|
|
1256
|
+
for await (const _ of stream) {
|
|
1257
|
+
// consume stream
|
|
1258
|
+
}
|
|
1259
|
+
// Assert: The IDE context message SHOULD have been added.
|
|
1260
|
+
expect(mockChat.addHistory).toHaveBeenCalledWith(expect.objectContaining({
|
|
1261
|
+
role: 'user',
|
|
1262
|
+
parts: expect.arrayContaining([
|
|
1263
|
+
expect.objectContaining({
|
|
1264
|
+
text: expect.stringContaining("user's editor context"),
|
|
1265
|
+
}),
|
|
1266
|
+
]),
|
|
1267
|
+
}));
|
|
1268
|
+
});
|
|
1269
|
+
it('should send the latest IDE context on the next message after a skipped context', async () => {
|
|
1270
|
+
// --- Step 1: A tool call is pending, context should be skipped ---
|
|
1271
|
+
// Arrange: History ends with a functionCall
|
|
1272
|
+
const historyWithPendingCall = [
|
|
1273
|
+
{ role: 'user', parts: [{ text: 'Please use a tool.' }] },
|
|
1274
|
+
{
|
|
1275
|
+
role: 'model',
|
|
1276
|
+
parts: [{ functionCall: { name: 'some_tool', args: {} } }],
|
|
1277
|
+
},
|
|
1278
|
+
];
|
|
1279
|
+
vi.mocked(mockChat.getHistory).mockReturnValue(historyWithPendingCall);
|
|
1280
|
+
// Arrange: Set the initial IDE context
|
|
1281
|
+
const initialIdeContext = {
|
|
1282
|
+
workspaceState: {
|
|
1283
|
+
openFiles: [{ path: '/path/to/fileA.ts', timestamp: Date.now() }],
|
|
1284
|
+
},
|
|
1285
|
+
};
|
|
1286
|
+
vi.mocked(ideContext.getIdeContext).mockReturnValue(initialIdeContext);
|
|
1287
|
+
// Act: Send the tool response
|
|
1288
|
+
let stream = client.sendMessageStream([
|
|
1289
|
+
{
|
|
1290
|
+
functionResponse: {
|
|
1291
|
+
name: 'some_tool',
|
|
1292
|
+
response: { success: true },
|
|
1293
|
+
},
|
|
1294
|
+
},
|
|
1295
|
+
], new AbortController().signal, 'prompt-id-tool-response');
|
|
1296
|
+
for await (const _ of stream) {
|
|
1297
|
+
/* consume */
|
|
1298
|
+
}
|
|
1299
|
+
// Assert: The initial context was NOT sent
|
|
1300
|
+
expect(mockChat.addHistory).not.toHaveBeenCalledWith(expect.objectContaining({
|
|
1301
|
+
parts: expect.arrayContaining([
|
|
1302
|
+
expect.objectContaining({
|
|
1303
|
+
text: expect.stringContaining("user's editor context"),
|
|
1304
|
+
}),
|
|
1305
|
+
]),
|
|
1306
|
+
}));
|
|
1307
|
+
// --- Step 2: A new message is sent, latest context should be included ---
|
|
1308
|
+
// Arrange: The model has responded to the tool, and the user is sending a new message.
|
|
1309
|
+
const historyAfterToolResponse = [
|
|
1310
|
+
...historyWithPendingCall,
|
|
1311
|
+
{
|
|
1312
|
+
role: 'user',
|
|
1313
|
+
parts: [
|
|
1314
|
+
{
|
|
1315
|
+
functionResponse: {
|
|
1316
|
+
name: 'some_tool',
|
|
1317
|
+
response: { success: true },
|
|
1318
|
+
},
|
|
1319
|
+
},
|
|
1320
|
+
],
|
|
1321
|
+
},
|
|
1322
|
+
{ role: 'model', parts: [{ text: 'The tool ran successfully.' }] },
|
|
1323
|
+
];
|
|
1324
|
+
vi.mocked(mockChat.getHistory).mockReturnValue(historyAfterToolResponse);
|
|
1325
|
+
vi.mocked(mockChat.addHistory).mockClear(); // Clear previous calls for the next assertion
|
|
1326
|
+
// Arrange: The IDE context has now changed
|
|
1327
|
+
const newIdeContext = {
|
|
1328
|
+
workspaceState: {
|
|
1329
|
+
openFiles: [{ path: '/path/to/fileB.ts', timestamp: Date.now() }],
|
|
1330
|
+
},
|
|
1331
|
+
};
|
|
1332
|
+
vi.mocked(ideContext.getIdeContext).mockReturnValue(newIdeContext);
|
|
1333
|
+
// Act: Send a new, regular user message
|
|
1334
|
+
stream = client.sendMessageStream([{ text: 'Thanks!' }], new AbortController().signal, 'prompt-id-final');
|
|
1335
|
+
for await (const _ of stream) {
|
|
1336
|
+
/* consume */
|
|
1337
|
+
}
|
|
1338
|
+
// Assert: The NEW context was sent as a FULL context because there was no previously sent context.
|
|
1339
|
+
const addHistoryCalls = vi.mocked(mockChat.addHistory).mock.calls;
|
|
1340
|
+
const contextCall = addHistoryCalls.find((call) => JSON.stringify(call[0]).includes("user's editor context"));
|
|
1341
|
+
expect(contextCall).toBeDefined();
|
|
1342
|
+
expect(JSON.stringify(contextCall[0])).toContain("Here is the user's editor context as a JSON object");
|
|
1343
|
+
// Check that the sent context is the new one (fileB.ts)
|
|
1344
|
+
expect(JSON.stringify(contextCall[0])).toContain('fileB.ts');
|
|
1345
|
+
// Check that the sent context is NOT the old one (fileA.ts)
|
|
1346
|
+
expect(JSON.stringify(contextCall[0])).not.toContain('fileA.ts');
|
|
1347
|
+
});
|
|
1348
|
+
it('should send a context DELTA on the next message after a skipped context', async () => {
|
|
1349
|
+
// --- Step 0: Establish an initial context ---
|
|
1350
|
+
vi.mocked(mockChat.getHistory).mockReturnValue([]); // Start with empty history
|
|
1351
|
+
const contextA = {
|
|
1352
|
+
workspaceState: {
|
|
1353
|
+
openFiles: [
|
|
1354
|
+
{
|
|
1355
|
+
path: '/path/to/fileA.ts',
|
|
1356
|
+
isActive: true,
|
|
1357
|
+
timestamp: Date.now(),
|
|
1358
|
+
},
|
|
1359
|
+
],
|
|
1360
|
+
},
|
|
1361
|
+
};
|
|
1362
|
+
vi.mocked(ideContext.getIdeContext).mockReturnValue(contextA);
|
|
1363
|
+
// Act: Send a regular message to establish the initial context
|
|
1364
|
+
let stream = client.sendMessageStream([{ text: 'Initial message' }], new AbortController().signal, 'prompt-id-initial');
|
|
1365
|
+
for await (const _ of stream) {
|
|
1366
|
+
/* consume */
|
|
1367
|
+
}
|
|
1368
|
+
// Assert: Full context for fileA.ts was sent and stored.
|
|
1369
|
+
const initialCall = vi.mocked(mockChat.addHistory).mock.calls[0][0];
|
|
1370
|
+
expect(JSON.stringify(initialCall)).toContain("user's editor context as a JSON object");
|
|
1371
|
+
expect(JSON.stringify(initialCall)).toContain('fileA.ts');
|
|
1372
|
+
// This implicitly tests that `lastSentIdeContext` is now set internally by the client.
|
|
1373
|
+
vi.mocked(mockChat.addHistory).mockClear();
|
|
1374
|
+
// --- Step 1: A tool call is pending, context should be skipped ---
|
|
1375
|
+
const historyWithPendingCall = [
|
|
1376
|
+
{ role: 'user', parts: [{ text: 'Please use a tool.' }] },
|
|
1377
|
+
{
|
|
1378
|
+
role: 'model',
|
|
1379
|
+
parts: [{ functionCall: { name: 'some_tool', args: {} } }],
|
|
1380
|
+
},
|
|
1381
|
+
];
|
|
1382
|
+
vi.mocked(mockChat.getHistory).mockReturnValue(historyWithPendingCall);
|
|
1383
|
+
// Arrange: IDE context changes, but this should be skipped
|
|
1384
|
+
const contextB = {
|
|
1385
|
+
workspaceState: {
|
|
1386
|
+
openFiles: [
|
|
1387
|
+
{
|
|
1388
|
+
path: '/path/to/fileB.ts',
|
|
1389
|
+
isActive: true,
|
|
1390
|
+
timestamp: Date.now(),
|
|
1391
|
+
},
|
|
1392
|
+
],
|
|
1393
|
+
},
|
|
1394
|
+
};
|
|
1395
|
+
vi.mocked(ideContext.getIdeContext).mockReturnValue(contextB);
|
|
1396
|
+
// Act: Send the tool response
|
|
1397
|
+
stream = client.sendMessageStream([
|
|
1398
|
+
{
|
|
1399
|
+
functionResponse: {
|
|
1400
|
+
name: 'some_tool',
|
|
1401
|
+
response: { success: true },
|
|
1402
|
+
},
|
|
1403
|
+
},
|
|
1404
|
+
], new AbortController().signal, 'prompt-id-tool-response');
|
|
1405
|
+
for await (const _ of stream) {
|
|
1406
|
+
/* consume */
|
|
1407
|
+
}
|
|
1408
|
+
// Assert: No context was sent
|
|
1409
|
+
expect(mockChat.addHistory).not.toHaveBeenCalled();
|
|
1410
|
+
// --- Step 2: A new message is sent, latest context DELTA should be included ---
|
|
1411
|
+
const historyAfterToolResponse = [
|
|
1412
|
+
...historyWithPendingCall,
|
|
1413
|
+
{
|
|
1414
|
+
role: 'user',
|
|
1415
|
+
parts: [
|
|
1416
|
+
{
|
|
1417
|
+
functionResponse: {
|
|
1418
|
+
name: 'some_tool',
|
|
1419
|
+
response: { success: true },
|
|
1420
|
+
},
|
|
1421
|
+
},
|
|
1422
|
+
],
|
|
1423
|
+
},
|
|
1424
|
+
{ role: 'model', parts: [{ text: 'The tool ran successfully.' }] },
|
|
1425
|
+
];
|
|
1426
|
+
vi.mocked(mockChat.getHistory).mockReturnValue(historyAfterToolResponse);
|
|
1427
|
+
// Arrange: The IDE context has changed again
|
|
1428
|
+
const contextC = {
|
|
1429
|
+
workspaceState: {
|
|
1430
|
+
openFiles: [
|
|
1431
|
+
// fileA is now closed, fileC is open
|
|
1432
|
+
{
|
|
1433
|
+
path: '/path/to/fileC.ts',
|
|
1434
|
+
isActive: true,
|
|
1435
|
+
timestamp: Date.now(),
|
|
1436
|
+
},
|
|
1437
|
+
],
|
|
1438
|
+
},
|
|
1439
|
+
};
|
|
1440
|
+
vi.mocked(ideContext.getIdeContext).mockReturnValue(contextC);
|
|
1441
|
+
// Act: Send a new, regular user message
|
|
1442
|
+
stream = client.sendMessageStream([{ text: 'Thanks!' }], new AbortController().signal, 'prompt-id-final');
|
|
1443
|
+
for await (const _ of stream) {
|
|
1444
|
+
/* consume */
|
|
1445
|
+
}
|
|
1446
|
+
// Assert: The DELTA context was sent
|
|
1447
|
+
const finalCall = vi.mocked(mockChat.addHistory).mock.calls[0][0];
|
|
1448
|
+
expect(JSON.stringify(finalCall)).toContain('summary of changes');
|
|
1449
|
+
// The delta should reflect fileA being closed and fileC being opened.
|
|
1450
|
+
expect(JSON.stringify(finalCall)).toContain('filesClosed');
|
|
1451
|
+
expect(JSON.stringify(finalCall)).toContain('fileA.ts');
|
|
1452
|
+
expect(JSON.stringify(finalCall)).toContain('activeFileChanged');
|
|
1453
|
+
expect(JSON.stringify(finalCall)).toContain('fileC.ts');
|
|
1454
|
+
});
|
|
1455
|
+
});
|
|
921
1456
|
});
|
|
922
1457
|
describe('generateContent', () => {
|
|
923
1458
|
it('should use current model from config for content generation', async () => {
|
|
@@ -1004,5 +1539,65 @@ Here are some files the user has open, with the most recent at the top:
|
|
|
1004
1539
|
expect(mockFallbackHandler).toHaveBeenCalledWith(currentModel, fallbackModel, undefined);
|
|
1005
1540
|
});
|
|
1006
1541
|
});
|
|
1542
|
+
describe('setHistory', () => {
|
|
1543
|
+
it('should strip thought signatures when stripThoughts is true', () => {
|
|
1544
|
+
const mockChat = {
|
|
1545
|
+
setHistory: vi.fn(),
|
|
1546
|
+
};
|
|
1547
|
+
client['chat'] = mockChat;
|
|
1548
|
+
const historyWithThoughts = [
|
|
1549
|
+
{
|
|
1550
|
+
role: 'user',
|
|
1551
|
+
parts: [{ text: 'hello' }],
|
|
1552
|
+
},
|
|
1553
|
+
{
|
|
1554
|
+
role: 'model',
|
|
1555
|
+
parts: [
|
|
1556
|
+
{ text: 'thinking...', thoughtSignature: 'thought-123' },
|
|
1557
|
+
{
|
|
1558
|
+
functionCall: { name: 'test', args: {} },
|
|
1559
|
+
thoughtSignature: 'thought-456',
|
|
1560
|
+
},
|
|
1561
|
+
],
|
|
1562
|
+
},
|
|
1563
|
+
];
|
|
1564
|
+
client.setHistory(historyWithThoughts, { stripThoughts: true });
|
|
1565
|
+
const expectedHistory = [
|
|
1566
|
+
{
|
|
1567
|
+
role: 'user',
|
|
1568
|
+
parts: [{ text: 'hello' }],
|
|
1569
|
+
},
|
|
1570
|
+
{
|
|
1571
|
+
role: 'model',
|
|
1572
|
+
parts: [
|
|
1573
|
+
{ text: 'thinking...' },
|
|
1574
|
+
{ functionCall: { name: 'test', args: {} } },
|
|
1575
|
+
],
|
|
1576
|
+
},
|
|
1577
|
+
];
|
|
1578
|
+
expect(mockChat.setHistory).toHaveBeenCalledWith(expectedHistory);
|
|
1579
|
+
});
|
|
1580
|
+
it('should not strip thought signatures when stripThoughts is false', () => {
|
|
1581
|
+
const mockChat = {
|
|
1582
|
+
setHistory: vi.fn(),
|
|
1583
|
+
};
|
|
1584
|
+
client['chat'] = mockChat;
|
|
1585
|
+
const historyWithThoughts = [
|
|
1586
|
+
{
|
|
1587
|
+
role: 'user',
|
|
1588
|
+
parts: [{ text: 'hello' }],
|
|
1589
|
+
},
|
|
1590
|
+
{
|
|
1591
|
+
role: 'model',
|
|
1592
|
+
parts: [
|
|
1593
|
+
{ text: 'thinking...', thoughtSignature: 'thought-123' },
|
|
1594
|
+
{ text: 'ok', thoughtSignature: 'thought-456' },
|
|
1595
|
+
],
|
|
1596
|
+
},
|
|
1597
|
+
];
|
|
1598
|
+
client.setHistory(historyWithThoughts, { stripThoughts: false });
|
|
1599
|
+
expect(mockChat.setHistory).toHaveBeenCalledWith(historyWithThoughts);
|
|
1600
|
+
});
|
|
1601
|
+
});
|
|
1007
1602
|
});
|
|
1008
1603
|
//# sourceMappingURL=client.test.js.map
|