@machina.ai/cell-cli-core 1.0.13-rc9 → 1.0.21-rc1
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/package.json +12 -4
- package/dist/src/code_assist/converter.d.ts +6 -3
- package/dist/src/code_assist/converter.js +4 -2
- package/dist/src/code_assist/converter.js.map +1 -1
- package/dist/src/code_assist/converter.test.js +61 -11
- package/dist/src/code_assist/converter.test.js.map +1 -1
- package/dist/src/code_assist/oauth2.d.ts +1 -0
- package/dist/src/code_assist/oauth2.js +43 -18
- package/dist/src/code_assist/oauth2.js.map +1 -1
- package/dist/src/code_assist/oauth2.test.js +142 -9
- package/dist/src/code_assist/oauth2.test.js.map +1 -1
- package/dist/src/code_assist/server.d.ts +2 -2
- package/dist/src/code_assist/server.js +5 -5
- package/dist/src/code_assist/server.js.map +1 -1
- package/dist/src/code_assist/server.test.js +13 -10
- package/dist/src/code_assist/server.test.js.map +1 -1
- package/dist/src/code_assist/setup.js +49 -18
- package/dist/src/code_assist/setup.js.map +1 -1
- package/dist/src/code_assist/setup.test.js +115 -9
- package/dist/src/code_assist/setup.test.js.map +1 -1
- package/dist/src/config/config.d.ts +71 -13
- package/dist/src/config/config.js +154 -41
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +206 -21
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/flashFallback.test.js +19 -47
- package/dist/src/config/flashFallback.test.js.map +1 -1
- package/dist/src/config/models.d.ts +1 -0
- package/dist/src/config/models.js +1 -0
- package/dist/src/config/models.js.map +1 -1
- package/dist/src/core/client.d.ts +15 -16
- package/dist/src/core/client.js +247 -104
- package/dist/src/core/client.js.map +1 -1
- package/dist/src/core/client.test.js +798 -49
- package/dist/src/core/client.test.js.map +1 -1
- package/dist/src/core/contentGenerator.d.ts +2 -2
- package/dist/src/core/contentGenerator.js +23 -21
- package/dist/src/core/contentGenerator.js.map +1 -1
- package/dist/src/core/contentGenerator.test.js +30 -126
- package/dist/src/core/contentGenerator.test.js.map +1 -1
- package/dist/src/core/coreToolScheduler.d.ts +23 -10
- package/dist/src/core/coreToolScheduler.js +201 -77
- package/dist/src/core/coreToolScheduler.js.map +1 -1
- package/dist/src/core/coreToolScheduler.test.js +322 -94
- package/dist/src/core/coreToolScheduler.test.js.map +1 -1
- package/dist/src/core/geminiChat.d.ts +8 -6
- package/dist/src/core/geminiChat.js +49 -51
- package/dist/src/core/geminiChat.js.map +1 -1
- package/dist/src/core/geminiChat.test.js +2 -2
- package/dist/src/core/geminiChat.test.js.map +1 -1
- package/dist/src/core/geminiRequest.js +2 -37
- package/dist/src/core/geminiRequest.js.map +1 -1
- package/dist/src/core/logger.d.ts +24 -1
- package/dist/src/core/logger.js +128 -4
- package/dist/src/core/logger.js.map +1 -1
- package/dist/src/core/logger.test.js +157 -11
- package/dist/src/core/logger.test.js.map +1 -1
- package/dist/src/core/loggingContentGenerator.d.ts +25 -0
- package/dist/src/core/loggingContentGenerator.js +95 -0
- package/dist/src/core/loggingContentGenerator.js.map +1 -0
- package/dist/src/core/nonInteractiveToolExecutor.js +39 -4
- package/dist/src/core/nonInteractiveToolExecutor.js.map +1 -1
- package/dist/src/core/nonInteractiveToolExecutor.test.js +85 -62
- package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
- package/dist/src/core/prompts.js +43 -19
- package/dist/src/core/prompts.js.map +1 -1
- package/dist/src/core/prompts.test.js +118 -1
- package/dist/src/core/prompts.test.js.map +1 -1
- package/dist/src/core/subagent.d.ts +230 -0
- package/dist/src/core/subagent.js +447 -0
- package/dist/src/core/subagent.js.map +1 -0
- package/dist/src/core/subagent.test.js +515 -0
- package/dist/src/core/subagent.test.js.map +1 -0
- package/dist/src/core/tokenLimits.js +1 -0
- package/dist/src/core/tokenLimits.js.map +1 -1
- package/dist/src/core/turn.d.ts +3 -0
- package/dist/src/core/turn.js +4 -0
- package/dist/src/core/turn.js.map +1 -1
- package/dist/src/core/turn.test.js +4 -0
- package/dist/src/core/turn.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 +20 -0
- package/dist/src/ide/detect-ide.js +86 -0
- package/dist/src/ide/detect-ide.js.map +1 -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 +63 -0
- package/dist/src/ide/ide-client.js +320 -0
- package/dist/src/ide/ide-client.js.map +1 -0
- 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.d.ts +14 -0
- package/dist/src/ide/ide-installer.js +98 -0
- package/dist/src/ide/ide-installer.js.map +1 -0
- package/dist/src/ide/ide-installer.test.d.ts +6 -0
- package/dist/src/ide/ide-installer.test.js +53 -0
- package/dist/src/ide/ide-installer.test.js.map +1 -0
- package/dist/src/ide/ideContext.d.ts +374 -0
- package/dist/src/ide/ideContext.js +147 -0
- package/dist/src/ide/ideContext.js.map +1 -0
- package/dist/src/ide/ideContext.test.d.ts +6 -0
- package/dist/src/ide/ideContext.test.js +265 -0
- package/dist/src/ide/ideContext.test.js.map +1 -0
- package/dist/src/ide/process-utils.d.ts +14 -0
- package/dist/src/ide/process-utils.js +57 -0
- package/dist/src/ide/process-utils.js.map +1 -0
- package/dist/src/index.d.ts +17 -1
- package/dist/src/index.js +20 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/google-auth-provider.d.ts +23 -0
- package/dist/src/mcp/google-auth-provider.js +72 -0
- package/dist/src/mcp/google-auth-provider.js.map +1 -0
- package/dist/src/mcp/google-auth-provider.test.d.ts +6 -0
- package/dist/src/mcp/google-auth-provider.test.js +89 -0
- package/dist/src/mcp/google-auth-provider.test.js.map +1 -0
- package/dist/src/mcp/oauth-provider.d.ts +6 -2
- package/dist/src/mcp/oauth-provider.js +208 -53
- package/dist/src/mcp/oauth-provider.js.map +1 -1
- package/dist/src/mcp/oauth-provider.test.js +222 -70
- package/dist/src/mcp/oauth-provider.test.js.map +1 -1
- package/dist/src/mcp/oauth-token-storage.d.ts +3 -1
- package/dist/src/mcp/oauth-token-storage.js +3 -1
- package/dist/src/mcp/oauth-token-storage.js.map +1 -1
- package/dist/src/mcp/oauth-utils.d.ts +3 -1
- package/dist/src/mcp/oauth-utils.js +52 -14
- package/dist/src/mcp/oauth-utils.js.map +1 -1
- package/dist/src/mcp/oauth-utils.test.js +18 -3
- 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/prompts/mcp-prompts.d.ts +8 -0
- package/dist/src/prompts/mcp-prompts.js +13 -0
- package/dist/src/prompts/mcp-prompts.js.map +1 -0
- package/dist/src/prompts/prompt-registry.d.ts +34 -0
- package/dist/src/prompts/prompt-registry.js +63 -0
- package/dist/src/prompts/prompt-registry.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/fileDiscoveryService.test.js +101 -60
- package/dist/src/services/fileDiscoveryService.test.js.map +1 -1
- 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/gitService.test.js +67 -86
- package/dist/src/services/gitService.test.js.map +1 -1
- package/dist/src/services/loopDetectionService.d.ts +51 -5
- package/dist/src/services/loopDetectionService.js +152 -45
- package/dist/src/services/loopDetectionService.js.map +1 -1
- package/dist/src/services/loopDetectionService.test.js +286 -89
- package/dist/src/services/loopDetectionService.test.js.map +1 -1
- package/dist/src/services/shellExecutionService.d.ts +70 -0
- package/dist/src/services/shellExecutionService.js +175 -0
- package/dist/src/services/shellExecutionService.js.map +1 -0
- package/dist/src/services/shellExecutionService.test.d.ts +6 -0
- package/dist/src/services/shellExecutionService.test.js +282 -0
- package/dist/src/services/shellExecutionService.test.js.map +1 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +81 -9
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +356 -182
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.d.ts +17 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +342 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -0
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +19 -2
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +51 -9
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
- package/dist/src/telemetry/constants.d.ts +4 -0
- package/dist/src/telemetry/constants.js +4 -0
- package/dist/src/telemetry/constants.js.map +1 -1
- package/dist/src/telemetry/file-exporters.d.ts +28 -0
- package/dist/src/telemetry/file-exporters.js +62 -0
- package/dist/src/telemetry/file-exporters.js.map +1 -0
- 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 +6 -1
- package/dist/src/telemetry/loggers.js +87 -6
- package/dist/src/telemetry/loggers.js.map +1 -1
- package/dist/src/telemetry/loggers.test.circular.js +9 -2
- package/dist/src/telemetry/loggers.test.circular.js.map +1 -1
- package/dist/src/telemetry/loggers.test.js +51 -11
- package/dist/src/telemetry/loggers.test.js.map +1 -1
- package/dist/src/telemetry/metrics.d.ts +7 -2
- package/dist/src/telemetry/metrics.js +26 -6
- package/dist/src/telemetry/metrics.js.map +1 -1
- package/dist/src/telemetry/metrics.test.js +81 -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 +77 -30
- 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/tool-call-decision.d.ts +13 -0
- package/dist/src/telemetry/tool-call-decision.js +29 -0
- package/dist/src/telemetry/tool-call-decision.js.map +1 -0
- package/dist/src/telemetry/types.d.ts +74 -18
- package/dist/src/telemetry/types.js +110 -32
- package/dist/src/telemetry/types.js.map +1 -1
- package/dist/src/telemetry/uiTelemetry.d.ts +8 -1
- package/dist/src/telemetry/uiTelemetry.js +17 -2
- package/dist/src/telemetry/uiTelemetry.js.map +1 -1
- package/dist/src/telemetry/uiTelemetry.test.js +60 -10
- 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/mockWorkspaceContext.d.ts +13 -0
- package/dist/src/test-utils/mockWorkspaceContext.js +24 -0
- package/dist/src/test-utils/mockWorkspaceContext.js.map +1 -0
- package/dist/src/test-utils/tools.d.ts +44 -0
- package/dist/src/test-utils/tools.js +105 -0
- package/dist/src/test-utils/tools.js.map +1 -0
- package/dist/src/tools/diffOptions.d.ts +2 -0
- package/dist/src/tools/diffOptions.js +28 -0
- package/dist/src/tools/diffOptions.js.map +1 -1
- package/dist/src/tools/diffOptions.test.d.ts +6 -0
- package/dist/src/tools/diffOptions.test.js +119 -0
- package/dist/src/tools/diffOptions.test.js.map +1 -0
- package/dist/src/tools/edit.d.ts +10 -34
- package/dist/src/tools/edit.js +171 -131
- package/dist/src/tools/edit.js.map +1 -1
- package/dist/src/tools/edit.test.js +220 -43
- package/dist/src/tools/edit.test.js.map +1 -1
- package/dist/src/tools/glob.d.ts +4 -11
- package/dist/src/tools/glob.js +129 -97
- package/dist/src/tools/glob.js.map +1 -1
- package/dist/src/tools/glob.test.js +73 -17
- package/dist/src/tools/glob.test.js.map +1 -1
- package/dist/src/tools/grep.d.ts +5 -37
- package/dist/src/tools/grep.js +175 -100
- package/dist/src/tools/grep.js.map +1 -1
- package/dist/src/tools/grep.test.js +112 -28
- package/dist/src/tools/grep.test.js.map +1 -1
- package/dist/src/tools/ls.d.ts +4 -23
- package/dist/src/tools/ls.js +77 -78
- package/dist/src/tools/ls.js.map +1 -1
- package/dist/src/tools/ls.test.d.ts +6 -0
- package/dist/src/tools/ls.test.js +384 -0
- package/dist/src/tools/ls.test.js.map +1 -0
- 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 +86 -4
- package/dist/src/tools/mcp-client.js +730 -59
- package/dist/src/tools/mcp-client.js.map +1 -1
- package/dist/src/tools/mcp-client.test.js +295 -22
- 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 +163 -76
- package/dist/src/tools/mcp-tool.js.map +1 -1
- package/dist/src/tools/mcp-tool.test.js +400 -29
- package/dist/src/tools/mcp-tool.test.js.map +1 -1
- package/dist/src/tools/memoryTool.d.ts +14 -3
- package/dist/src/tools/memoryTool.js +159 -40
- package/dist/src/tools/memoryTool.js.map +1 -1
- package/dist/src/tools/memoryTool.test.js +116 -11
- package/dist/src/tools/memoryTool.test.js.map +1 -1
- package/dist/src/tools/modifiable-tool.d.ts +9 -6
- package/dist/src/tools/modifiable-tool.js +6 -3
- package/dist/src/tools/modifiable-tool.js.map +1 -1
- package/dist/src/tools/modifiable-tool.test.js +63 -74
- package/dist/src/tools/modifiable-tool.test.js.map +1 -1
- package/dist/src/tools/read-file.d.ts +4 -6
- package/dist/src/tools/read-file.js +100 -53
- package/dist/src/tools/read-file.js.map +1 -1
- package/dist/src/tools/read-file.test.js +238 -111
- package/dist/src/tools/read-file.test.js.map +1 -1
- package/dist/src/tools/read-many-files.d.ts +3 -6
- package/dist/src/tools/read-many-files.js +232 -157
- package/dist/src/tools/read-many-files.js.map +1 -1
- package/dist/src/tools/read-many-files.test.js +231 -34
- package/dist/src/tools/read-many-files.test.js.map +1 -1
- package/dist/src/tools/shell.d.ts +6 -28
- package/dist/src/tools/shell.js +249 -366
- package/dist/src/tools/shell.js.map +1 -1
- package/dist/src/tools/shell.test.js +305 -384
- package/dist/src/tools/shell.test.js.map +1 -1
- package/dist/src/tools/tool-error.d.ts +27 -0
- package/dist/src/tools/tool-error.js +32 -0
- package/dist/src/tools/tool-error.js.map +1 -0
- package/dist/src/tools/tool-registry.d.ts +38 -23
- package/dist/src/tools/tool-registry.js +127 -99
- package/dist/src/tools/tool-registry.js.map +1 -1
- package/dist/src/tools/tool-registry.test.js +37 -212
- package/dist/src/tools/tool-registry.test.js.map +1 -1
- package/dist/src/tools/tools.d.ts +145 -92
- package/dist/src/tools/tools.js +188 -61
- package/dist/src/tools/tools.js.map +1 -1
- package/dist/src/tools/tools.test.d.ts +6 -0
- package/dist/src/tools/tools.test.js +206 -0
- package/dist/src/tools/tools.test.js.map +1 -0
- 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 +20 -11
- package/dist/src/tools/write-file.js +198 -141
- package/dist/src/tools/write-file.js.map +1 -1
- package/dist/src/tools/write-file.test.js +190 -76
- package/dist/src/tools/write-file.test.js.map +1 -1
- package/dist/src/utils/bfsFileSearch.js +51 -27
- package/dist/src/utils/bfsFileSearch.js.map +1 -1
- package/dist/src/utils/bfsFileSearch.test.js +137 -136
- package/dist/src/utils/bfsFileSearch.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 +23 -24
- package/dist/src/utils/editCorrector.js.map +1 -1
- package/dist/src/utils/editor.d.ts +2 -2
- package/dist/src/utils/editor.js +23 -6
- package/dist/src/utils/editor.js.map +1 -1
- package/dist/src/utils/editor.test.js +67 -15
- package/dist/src/utils/editor.test.js.map +1 -1
- package/dist/src/utils/environmentContext.d.ts +21 -0
- package/dist/src/utils/environmentContext.js +90 -0
- package/dist/src/utils/environmentContext.js.map +1 -0
- package/dist/src/utils/environmentContext.test.d.ts +6 -0
- package/dist/src/utils/environmentContext.test.js +140 -0
- package/dist/src/utils/environmentContext.test.js.map +1 -0
- 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/errorReporting.d.ts +1 -1
- package/dist/src/utils/errorReporting.js +2 -2
- package/dist/src/utils/errorReporting.js.map +1 -1
- package/dist/src/utils/errorReporting.test.js +44 -38
- package/dist/src/utils/errorReporting.test.js.map +1 -1
- package/dist/src/utils/fileUtils.d.ts +9 -1
- package/dist/src/utils/fileUtils.js +27 -13
- package/dist/src/utils/fileUtils.js.map +1 -1
- package/dist/src/utils/fileUtils.test.js +61 -18
- package/dist/src/utils/fileUtils.test.js.map +1 -1
- package/dist/src/utils/filesearch/crawlCache.d.ts +25 -0
- package/dist/src/utils/filesearch/crawlCache.js +57 -0
- package/dist/src/utils/filesearch/crawlCache.js.map +1 -0
- package/dist/src/utils/filesearch/crawlCache.test.d.ts +6 -0
- package/dist/src/utils/filesearch/crawlCache.test.js +103 -0
- package/dist/src/utils/filesearch/crawlCache.test.js.map +1 -0
- 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 +37 -0
- package/dist/src/utils/filesearch/fileSearch.js +186 -0
- package/dist/src/utils/filesearch/fileSearch.js.map +1 -0
- package/dist/src/utils/filesearch/fileSearch.test.d.ts +6 -0
- package/dist/src/utils/filesearch/fileSearch.test.js +552 -0
- package/dist/src/utils/filesearch/fileSearch.test.js.map +1 -0
- package/dist/src/utils/filesearch/ignore.d.ts +42 -0
- package/dist/src/utils/filesearch/ignore.js +106 -0
- package/dist/src/utils/filesearch/ignore.js.map +1 -0
- package/dist/src/utils/filesearch/ignore.test.d.ts +6 -0
- package/dist/src/utils/filesearch/ignore.test.js +144 -0
- package/dist/src/utils/filesearch/ignore.test.js.map +1 -0
- package/dist/src/utils/filesearch/result-cache.d.ts +33 -0
- package/dist/src/utils/filesearch/result-cache.js +59 -0
- package/dist/src/utils/filesearch/result-cache.js.map +1 -0
- package/dist/src/utils/filesearch/result-cache.test.d.ts +6 -0
- package/dist/src/utils/filesearch/result-cache.test.js +46 -0
- package/dist/src/utils/filesearch/result-cache.test.js.map +1 -0
- package/dist/src/utils/flashFallback.integration.test.js +6 -0
- package/dist/src/utils/flashFallback.integration.test.js.map +1 -1
- package/dist/src/utils/formatters.d.ts +6 -0
- package/dist/src/utils/formatters.js +16 -0
- package/dist/src/utils/formatters.js.map +1 -0
- package/dist/src/utils/getFolderStructure.test.js +11 -13
- package/dist/src/utils/getFolderStructure.test.js.map +1 -1
- package/dist/src/utils/gitIgnoreParser.js +5 -11
- package/dist/src/utils/gitIgnoreParser.js.map +1 -1
- package/dist/src/utils/gitIgnoreParser.test.js +58 -61
- package/dist/src/utils/gitIgnoreParser.test.js.map +1 -1
- package/dist/src/utils/memoryDiscovery.d.ts +1 -1
- package/dist/src/utils/memoryDiscovery.js +76 -83
- package/dist/src/utils/memoryDiscovery.js.map +1 -1
- package/dist/src/utils/memoryDiscovery.test.js +122 -372
- package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
- package/dist/src/utils/memoryImportProcessor.d.ts +19 -12
- package/dist/src/utils/memoryImportProcessor.js +240 -85
- package/dist/src/utils/memoryImportProcessor.js.map +1 -1
- package/dist/src/utils/memoryImportProcessor.test.js +593 -51
- package/dist/src/utils/memoryImportProcessor.test.js.map +1 -1
- package/dist/src/utils/nextSpeakerChecker.js +4 -25
- package/dist/src/utils/nextSpeakerChecker.js.map +1 -1
- package/dist/src/utils/partUtils.d.ts +14 -0
- package/dist/src/utils/partUtils.js +65 -0
- package/dist/src/utils/partUtils.js.map +1 -0
- package/dist/src/utils/partUtils.test.d.ts +6 -0
- package/dist/src/utils/partUtils.test.js +130 -0
- package/dist/src/utils/partUtils.test.js.map +1 -0
- package/dist/src/utils/paths.d.ts +18 -2
- package/dist/src/utils/paths.js +39 -7
- package/dist/src/utils/paths.js.map +1 -1
- package/dist/src/utils/paths.test.d.ts +6 -0
- package/dist/src/utils/paths.test.js +225 -0
- package/dist/src/utils/paths.test.js.map +1 -0
- package/dist/src/utils/quotaErrorDetection.d.ts +1 -5
- package/dist/src/utils/quotaErrorDetection.js.map +1 -1
- package/dist/src/utils/retry.d.ts +3 -0
- package/dist/src/utils/retry.js.map +1 -1
- package/dist/src/utils/retry.test.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.d.ts +23 -0
- package/dist/src/utils/secure-browser-launcher.js +165 -0
- package/dist/src/utils/secure-browser-launcher.js.map +1 -0
- package/dist/src/utils/secure-browser-launcher.test.d.ts +6 -0
- package/dist/src/utils/secure-browser-launcher.test.js +149 -0
- package/dist/src/utils/secure-browser-launcher.test.js.map +1 -0
- package/dist/src/utils/shell-utils.d.ts +117 -0
- package/dist/src/utils/shell-utils.js +376 -0
- package/dist/src/utils/shell-utils.js.map +1 -0
- package/dist/src/utils/shell-utils.test.d.ts +6 -0
- package/dist/src/utils/shell-utils.test.js +328 -0
- package/dist/src/utils/shell-utils.test.js.map +1 -0
- package/dist/src/utils/summarizer.js +3 -32
- package/dist/src/utils/summarizer.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/textUtils.d.ts +13 -0
- package/dist/src/utils/textUtils.js +28 -0
- package/dist/src/utils/textUtils.js.map +1 -0
- 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 +66 -0
- package/dist/src/utils/workspaceContext.js +165 -0
- package/dist/src/utils/workspaceContext.js.map +1 -0
- package/dist/src/utils/workspaceContext.test.d.ts +6 -0
- package/dist/src/utils/workspaceContext.test.js +293 -0
- package/dist/src/utils/workspaceContext.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +14 -3
- package/dist/src/core/geminiRequest.test.js +0 -72
- package/dist/src/core/geminiRequest.test.js.map +0 -1
- package/dist/src/core/modelCheck.d.ts +0 -14
- package/dist/src/core/modelCheck.js +0 -62
- package/dist/src/core/modelCheck.js.map +0 -1
- package/dist/src/services/ideContext.d.ts +0 -178
- package/dist/src/services/ideContext.js +0 -105
- package/dist/src/services/ideContext.js.map +0 -1
- package/dist/src/services/ideContext.test.js +0 -111
- package/dist/src/services/ideContext.test.js.map +0 -1
- /package/dist/src/core/{geminiRequest.test.d.ts → subagent.test.d.ts} +0 -0
- /package/dist/src/{services/ideContext.test.d.ts → ide/detect-ide.test.d.ts} +0 -0
|
@@ -14,7 +14,8 @@ import { DEFAULT_GEMINI_FLASH_MODEL } from '../config/models.js';
|
|
|
14
14
|
import { FileDiscoveryService } from '../services/fileDiscoveryService.js';
|
|
15
15
|
import { setSimulate429 } from '../utils/testUtils.js';
|
|
16
16
|
import { tokenLimit } from './tokenLimits.js';
|
|
17
|
-
import { ideContext } from '../
|
|
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();
|
|
@@ -58,7 +59,7 @@ vi.mock('../telemetry/index.js', () => ({
|
|
|
58
59
|
logApiResponse: vi.fn(),
|
|
59
60
|
logApiError: vi.fn(),
|
|
60
61
|
}));
|
|
61
|
-
vi.mock('../
|
|
62
|
+
vi.mock('../ide/ideContext.js');
|
|
62
63
|
describe('findIndexAfterFraction', () => {
|
|
63
64
|
const history = [
|
|
64
65
|
{ role: 'user', parts: [{ text: 'This is the first message.' }] }, // JSON length: 66
|
|
@@ -167,14 +168,22 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
167
168
|
getQuotaErrorOccurred: vi.fn().mockReturnValue(false),
|
|
168
169
|
setQuotaErrorOccurred: vi.fn(),
|
|
169
170
|
getNoBrowser: vi.fn().mockReturnValue(false),
|
|
170
|
-
|
|
171
|
+
getUsageStatisticsEnabled: vi.fn().mockReturnValue(true),
|
|
172
|
+
getIdeModeFeature: vi.fn().mockReturnValue(false),
|
|
173
|
+
getIdeMode: vi.fn().mockReturnValue(true),
|
|
174
|
+
getDebugMode: vi.fn().mockReturnValue(false),
|
|
175
|
+
getWorkspaceContext: vi.fn().mockReturnValue({
|
|
176
|
+
getDirectories: vi.fn().mockReturnValue(['/test/dir']),
|
|
177
|
+
}),
|
|
171
178
|
getGeminiClient: vi.fn(),
|
|
179
|
+
setFallbackMode: vi.fn(),
|
|
180
|
+
getChatCompression: vi.fn().mockReturnValue(undefined),
|
|
172
181
|
};
|
|
173
182
|
const MockedConfig = vi.mocked(Config, true);
|
|
174
183
|
MockedConfig.mockImplementation(() => mockConfigObject);
|
|
175
184
|
// We can instantiate the client here since Config is mocked
|
|
176
185
|
// and the constructor will use the mocked GoogleGenAI
|
|
177
|
-
client = new GeminiClient(new Config({}));
|
|
186
|
+
client = new GeminiClient(new Config({ sessionId: 'test-session-id' }));
|
|
178
187
|
mockConfigObject.getGeminiClient.mockReturnValue(client);
|
|
179
188
|
await client.initialize(contentGeneratorConfig);
|
|
180
189
|
});
|
|
@@ -286,7 +295,7 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
286
295
|
topP: 1,
|
|
287
296
|
},
|
|
288
297
|
contents,
|
|
289
|
-
});
|
|
298
|
+
}, 'test-session-id');
|
|
290
299
|
});
|
|
291
300
|
});
|
|
292
301
|
describe('generateJson', () => {
|
|
@@ -308,34 +317,37 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
308
317
|
systemInstruction: getCoreSystemPrompt(''),
|
|
309
318
|
temperature: 0,
|
|
310
319
|
topP: 1,
|
|
311
|
-
|
|
320
|
+
responseJsonSchema: schema,
|
|
312
321
|
responseMimeType: 'application/json',
|
|
313
322
|
},
|
|
314
323
|
contents,
|
|
315
|
-
});
|
|
324
|
+
}, 'test-session-id');
|
|
316
325
|
});
|
|
317
|
-
it('
|
|
326
|
+
it('should allow overriding model and config', async () => {
|
|
318
327
|
const contents = [{ role: 'user', parts: [{ text: 'hello' }] }];
|
|
319
|
-
const schema = {
|
|
320
|
-
type: 'object',
|
|
321
|
-
properties: { foo: { type: 'string' } },
|
|
322
|
-
};
|
|
328
|
+
const schema = { type: 'string' };
|
|
323
329
|
const abortSignal = new AbortController().signal;
|
|
330
|
+
const customModel = 'custom-json-model';
|
|
331
|
+
const customConfig = { temperature: 0.9, topK: 20 };
|
|
324
332
|
const mockGenerator = {
|
|
325
333
|
countTokens: vi.fn().mockResolvedValue({ totalTokens: 1 }),
|
|
326
|
-
generateContent:
|
|
327
|
-
candidates: [
|
|
328
|
-
{
|
|
329
|
-
content: {
|
|
330
|
-
parts: [{ text: '```json\n{\n "foo": "bar"\n}\n```' }],
|
|
331
|
-
},
|
|
332
|
-
},
|
|
333
|
-
],
|
|
334
|
-
}),
|
|
334
|
+
generateContent: mockGenerateContentFn,
|
|
335
335
|
};
|
|
336
336
|
client['contentGenerator'] = mockGenerator;
|
|
337
|
-
|
|
338
|
-
expect(
|
|
337
|
+
await client.generateJson(contents, schema, abortSignal, customModel, customConfig);
|
|
338
|
+
expect(mockGenerateContentFn).toHaveBeenCalledWith({
|
|
339
|
+
model: customModel,
|
|
340
|
+
config: {
|
|
341
|
+
abortSignal,
|
|
342
|
+
systemInstruction: getCoreSystemPrompt(''),
|
|
343
|
+
temperature: 0.9,
|
|
344
|
+
topP: 1, // from default
|
|
345
|
+
topK: 20,
|
|
346
|
+
responseJsonSchema: schema,
|
|
347
|
+
responseMimeType: 'application/json',
|
|
348
|
+
},
|
|
349
|
+
contents,
|
|
350
|
+
}, 'test-session-id');
|
|
339
351
|
});
|
|
340
352
|
});
|
|
341
353
|
describe('addHistory', () => {
|
|
@@ -343,7 +355,6 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
343
355
|
const mockChat = {
|
|
344
356
|
addHistory: vi.fn(),
|
|
345
357
|
};
|
|
346
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
347
358
|
client['chat'] = mockChat;
|
|
348
359
|
const newContent = {
|
|
349
360
|
role: 'user',
|
|
@@ -409,13 +420,44 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
409
420
|
expect(result).toBeNull();
|
|
410
421
|
expect(newChat).toBe(initialChat);
|
|
411
422
|
});
|
|
412
|
-
it('
|
|
423
|
+
it('logs a telemetry event when compressing', async () => {
|
|
424
|
+
vi.spyOn(ClearcutLogger.prototype, 'logChatCompressionEvent');
|
|
413
425
|
const MOCKED_TOKEN_LIMIT = 1000;
|
|
426
|
+
const MOCKED_CONTEXT_PERCENTAGE_THRESHOLD = 0.5;
|
|
414
427
|
vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
|
|
428
|
+
vi.spyOn(client['config'], 'getChatCompression').mockReturnValue({
|
|
429
|
+
contextPercentageThreshold: MOCKED_CONTEXT_PERCENTAGE_THRESHOLD,
|
|
430
|
+
});
|
|
415
431
|
mockGetHistory.mockReturnValue([
|
|
416
432
|
{ role: 'user', parts: [{ text: '...history...' }] },
|
|
417
433
|
]);
|
|
418
|
-
const originalTokenCount =
|
|
434
|
+
const originalTokenCount = MOCKED_TOKEN_LIMIT * MOCKED_CONTEXT_PERCENTAGE_THRESHOLD;
|
|
435
|
+
const newTokenCount = 100;
|
|
436
|
+
mockCountTokens
|
|
437
|
+
.mockResolvedValueOnce({ totalTokens: originalTokenCount }) // First call for the check
|
|
438
|
+
.mockResolvedValueOnce({ totalTokens: newTokenCount }); // Second call for the new history
|
|
439
|
+
// Mock the summary response from the chat
|
|
440
|
+
mockSendMessage.mockResolvedValue({
|
|
441
|
+
role: 'model',
|
|
442
|
+
parts: [{ text: 'This is a summary.' }],
|
|
443
|
+
});
|
|
444
|
+
await client.tryCompressChat('prompt-id-3');
|
|
445
|
+
expect(ClearcutLogger.prototype.logChatCompressionEvent).toHaveBeenCalledWith(expect.objectContaining({
|
|
446
|
+
tokens_before: originalTokenCount,
|
|
447
|
+
tokens_after: newTokenCount,
|
|
448
|
+
}));
|
|
449
|
+
});
|
|
450
|
+
it('should trigger summarization if token count is at threshold with contextPercentageThreshold setting', async () => {
|
|
451
|
+
const MOCKED_TOKEN_LIMIT = 1000;
|
|
452
|
+
const MOCKED_CONTEXT_PERCENTAGE_THRESHOLD = 0.5;
|
|
453
|
+
vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
|
|
454
|
+
vi.spyOn(client['config'], 'getChatCompression').mockReturnValue({
|
|
455
|
+
contextPercentageThreshold: MOCKED_CONTEXT_PERCENTAGE_THRESHOLD,
|
|
456
|
+
});
|
|
457
|
+
mockGetHistory.mockReturnValue([
|
|
458
|
+
{ role: 'user', parts: [{ text: '...history...' }] },
|
|
459
|
+
]);
|
|
460
|
+
const originalTokenCount = MOCKED_TOKEN_LIMIT * MOCKED_CONTEXT_PERCENTAGE_THRESHOLD;
|
|
419
461
|
const newTokenCount = 100;
|
|
420
462
|
mockCountTokens
|
|
421
463
|
.mockResolvedValueOnce({ totalTokens: originalTokenCount }) // First call for the check
|
|
@@ -515,16 +557,120 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
515
557
|
});
|
|
516
558
|
});
|
|
517
559
|
describe('sendMessageStream', () => {
|
|
518
|
-
it('should include
|
|
560
|
+
it('should include editor context when ideMode is enabled', async () => {
|
|
519
561
|
// Arrange
|
|
520
|
-
vi.mocked(ideContext.
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
562
|
+
vi.mocked(ideContext.getIdeContext).mockReturnValue({
|
|
563
|
+
workspaceState: {
|
|
564
|
+
openFiles: [
|
|
565
|
+
{
|
|
566
|
+
path: '/path/to/active/file.ts',
|
|
567
|
+
timestamp: Date.now(),
|
|
568
|
+
isActive: true,
|
|
569
|
+
selectedText: 'hello',
|
|
570
|
+
cursor: { line: 5, character: 10 },
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
path: '/path/to/recent/file1.ts',
|
|
574
|
+
timestamp: Date.now(),
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
path: '/path/to/recent/file2.ts',
|
|
578
|
+
timestamp: Date.now(),
|
|
579
|
+
},
|
|
580
|
+
],
|
|
581
|
+
},
|
|
582
|
+
});
|
|
583
|
+
vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
|
|
584
|
+
const mockStream = (async function* () {
|
|
585
|
+
yield { type: 'content', value: 'Hello' };
|
|
586
|
+
})();
|
|
587
|
+
mockTurnRunFn.mockReturnValue(mockStream);
|
|
588
|
+
const mockChat = {
|
|
589
|
+
addHistory: vi.fn(),
|
|
590
|
+
getHistory: vi.fn().mockReturnValue([]),
|
|
591
|
+
};
|
|
592
|
+
client['chat'] = mockChat;
|
|
593
|
+
const mockGenerator = {
|
|
594
|
+
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
595
|
+
generateContent: mockGenerateContentFn,
|
|
596
|
+
};
|
|
597
|
+
client['contentGenerator'] = mockGenerator;
|
|
598
|
+
const initialRequest = [{ text: 'Hi' }];
|
|
599
|
+
// Act
|
|
600
|
+
const stream = client.sendMessageStream(initialRequest, new AbortController().signal, 'prompt-id-ide');
|
|
601
|
+
for await (const _ of stream) {
|
|
602
|
+
// consume stream
|
|
603
|
+
}
|
|
604
|
+
// Assert
|
|
605
|
+
expect(ideContext.getIdeContext).toHaveBeenCalled();
|
|
606
|
+
const expectedContext = `
|
|
607
|
+
Here is the user's editor context as a JSON object. This is for your information only.
|
|
608
|
+
\`\`\`json
|
|
609
|
+
${JSON.stringify({
|
|
610
|
+
activeFile: {
|
|
611
|
+
path: '/path/to/active/file.ts',
|
|
612
|
+
cursor: {
|
|
613
|
+
line: 5,
|
|
614
|
+
character: 10,
|
|
615
|
+
},
|
|
616
|
+
selectedText: 'hello',
|
|
617
|
+
},
|
|
618
|
+
otherOpenFiles: ['/path/to/recent/file1.ts', '/path/to/recent/file2.ts'],
|
|
619
|
+
}, null, 2)}
|
|
620
|
+
\`\`\`
|
|
621
|
+
`.trim();
|
|
622
|
+
const expectedRequest = [{ text: expectedContext }];
|
|
623
|
+
expect(mockChat.addHistory).toHaveBeenCalledWith({
|
|
624
|
+
role: 'user',
|
|
625
|
+
parts: expectedRequest,
|
|
626
|
+
});
|
|
627
|
+
});
|
|
628
|
+
it('should not add context if ideMode is enabled but no open files', async () => {
|
|
629
|
+
// Arrange
|
|
630
|
+
vi.mocked(ideContext.getIdeContext).mockReturnValue({
|
|
631
|
+
workspaceState: {
|
|
632
|
+
openFiles: [],
|
|
633
|
+
},
|
|
634
|
+
});
|
|
635
|
+
vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
|
|
636
|
+
const mockStream = (async function* () {
|
|
637
|
+
yield { type: 'content', value: 'Hello' };
|
|
638
|
+
})();
|
|
639
|
+
mockTurnRunFn.mockReturnValue(mockStream);
|
|
640
|
+
const mockChat = {
|
|
641
|
+
addHistory: vi.fn(),
|
|
642
|
+
getHistory: vi.fn().mockReturnValue([]),
|
|
643
|
+
};
|
|
644
|
+
client['chat'] = mockChat;
|
|
645
|
+
const mockGenerator = {
|
|
646
|
+
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
647
|
+
generateContent: mockGenerateContentFn,
|
|
648
|
+
};
|
|
649
|
+
client['contentGenerator'] = mockGenerator;
|
|
650
|
+
const initialRequest = [{ text: 'Hi' }];
|
|
651
|
+
// Act
|
|
652
|
+
const stream = client.sendMessageStream(initialRequest, new AbortController().signal, 'prompt-id-ide');
|
|
653
|
+
for await (const _ of stream) {
|
|
654
|
+
// consume stream
|
|
655
|
+
}
|
|
656
|
+
// Assert
|
|
657
|
+
expect(ideContext.getIdeContext).toHaveBeenCalled();
|
|
658
|
+
expect(mockTurnRunFn).toHaveBeenCalledWith(initialRequest, expect.any(Object));
|
|
659
|
+
});
|
|
660
|
+
it('should add context if ideMode is enabled and there is one active file', async () => {
|
|
661
|
+
// Arrange
|
|
662
|
+
vi.mocked(ideContext.getIdeContext).mockReturnValue({
|
|
663
|
+
workspaceState: {
|
|
664
|
+
openFiles: [
|
|
665
|
+
{
|
|
666
|
+
path: '/path/to/active/file.ts',
|
|
667
|
+
timestamp: Date.now(),
|
|
668
|
+
isActive: true,
|
|
669
|
+
selectedText: 'hello',
|
|
670
|
+
cursor: { line: 5, character: 10 },
|
|
671
|
+
},
|
|
672
|
+
],
|
|
673
|
+
},
|
|
528
674
|
});
|
|
529
675
|
vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
|
|
530
676
|
const mockStream = (async function* () {
|
|
@@ -548,20 +694,80 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
548
694
|
// consume stream
|
|
549
695
|
}
|
|
550
696
|
// Assert
|
|
551
|
-
expect(ideContext.
|
|
697
|
+
expect(ideContext.getIdeContext).toHaveBeenCalled();
|
|
552
698
|
const expectedContext = `
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
699
|
+
Here is the user's editor context as a JSON object. This is for your information only.
|
|
700
|
+
\`\`\`json
|
|
701
|
+
${JSON.stringify({
|
|
702
|
+
activeFile: {
|
|
703
|
+
path: '/path/to/active/file.ts',
|
|
704
|
+
cursor: {
|
|
705
|
+
line: 5,
|
|
706
|
+
character: 10,
|
|
707
|
+
},
|
|
708
|
+
selectedText: 'hello',
|
|
709
|
+
},
|
|
710
|
+
}, null, 2)}
|
|
711
|
+
\`\`\`
|
|
562
712
|
`.trim();
|
|
563
|
-
const expectedRequest = [{ text: expectedContext }
|
|
564
|
-
expect(
|
|
713
|
+
const expectedRequest = [{ text: expectedContext }];
|
|
714
|
+
expect(mockChat.addHistory).toHaveBeenCalledWith({
|
|
715
|
+
role: 'user',
|
|
716
|
+
parts: expectedRequest,
|
|
717
|
+
});
|
|
718
|
+
});
|
|
719
|
+
it('should add context if ideMode is enabled and there are open files but no active file', async () => {
|
|
720
|
+
// Arrange
|
|
721
|
+
vi.mocked(ideContext.getIdeContext).mockReturnValue({
|
|
722
|
+
workspaceState: {
|
|
723
|
+
openFiles: [
|
|
724
|
+
{
|
|
725
|
+
path: '/path/to/recent/file1.ts',
|
|
726
|
+
timestamp: Date.now(),
|
|
727
|
+
},
|
|
728
|
+
{
|
|
729
|
+
path: '/path/to/recent/file2.ts',
|
|
730
|
+
timestamp: Date.now(),
|
|
731
|
+
},
|
|
732
|
+
],
|
|
733
|
+
},
|
|
734
|
+
});
|
|
735
|
+
vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
|
|
736
|
+
const mockStream = (async function* () {
|
|
737
|
+
yield { type: 'content', value: 'Hello' };
|
|
738
|
+
})();
|
|
739
|
+
mockTurnRunFn.mockReturnValue(mockStream);
|
|
740
|
+
const mockChat = {
|
|
741
|
+
addHistory: vi.fn(),
|
|
742
|
+
getHistory: vi.fn().mockReturnValue([]),
|
|
743
|
+
};
|
|
744
|
+
client['chat'] = mockChat;
|
|
745
|
+
const mockGenerator = {
|
|
746
|
+
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
747
|
+
generateContent: mockGenerateContentFn,
|
|
748
|
+
};
|
|
749
|
+
client['contentGenerator'] = mockGenerator;
|
|
750
|
+
const initialRequest = [{ text: 'Hi' }];
|
|
751
|
+
// Act
|
|
752
|
+
const stream = client.sendMessageStream(initialRequest, new AbortController().signal, 'prompt-id-ide');
|
|
753
|
+
for await (const _ of stream) {
|
|
754
|
+
// consume stream
|
|
755
|
+
}
|
|
756
|
+
// Assert
|
|
757
|
+
expect(ideContext.getIdeContext).toHaveBeenCalled();
|
|
758
|
+
const expectedContext = `
|
|
759
|
+
Here is the user's editor context as a JSON object. This is for your information only.
|
|
760
|
+
\`\`\`json
|
|
761
|
+
${JSON.stringify({
|
|
762
|
+
otherOpenFiles: ['/path/to/recent/file1.ts', '/path/to/recent/file2.ts'],
|
|
763
|
+
}, null, 2)}
|
|
764
|
+
\`\`\`
|
|
765
|
+
`.trim();
|
|
766
|
+
const expectedRequest = [{ text: expectedContext }];
|
|
767
|
+
expect(mockChat.addHistory).toHaveBeenCalledWith({
|
|
768
|
+
role: 'user',
|
|
769
|
+
parts: expectedRequest,
|
|
770
|
+
});
|
|
565
771
|
});
|
|
566
772
|
it('should return the turn instance after the stream is complete', async () => {
|
|
567
773
|
// Arrange
|
|
@@ -764,6 +970,488 @@ Here are files the user has recently opened, with the most recent at the top:
|
|
|
764
970
|
console.log(`Infinite loop protection working: checkNextSpeaker called ${callCount} times, ` +
|
|
765
971
|
`${eventCount} events generated (properly bounded by MAX_TURNS)`);
|
|
766
972
|
});
|
|
973
|
+
describe('Editor context delta', () => {
|
|
974
|
+
const mockStream = (async function* () {
|
|
975
|
+
yield { type: 'content', value: 'Hello' };
|
|
976
|
+
})();
|
|
977
|
+
beforeEach(() => {
|
|
978
|
+
client['forceFullIdeContext'] = false; // Reset before each delta test
|
|
979
|
+
vi.spyOn(client, 'tryCompressChat').mockResolvedValue(null);
|
|
980
|
+
vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
|
|
981
|
+
mockTurnRunFn.mockReturnValue(mockStream);
|
|
982
|
+
const mockChat = {
|
|
983
|
+
addHistory: vi.fn(),
|
|
984
|
+
setHistory: vi.fn(),
|
|
985
|
+
sendMessage: vi.fn().mockResolvedValue({ text: 'summary' }),
|
|
986
|
+
// Assume history is not empty for delta checks
|
|
987
|
+
getHistory: vi
|
|
988
|
+
.fn()
|
|
989
|
+
.mockReturnValue([
|
|
990
|
+
{ role: 'user', parts: [{ text: 'previous message' }] },
|
|
991
|
+
]),
|
|
992
|
+
};
|
|
993
|
+
client['chat'] = mockChat;
|
|
994
|
+
const mockGenerator = {
|
|
995
|
+
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
996
|
+
generateContent: mockGenerateContentFn,
|
|
997
|
+
};
|
|
998
|
+
client['contentGenerator'] = mockGenerator;
|
|
999
|
+
});
|
|
1000
|
+
const testCases = [
|
|
1001
|
+
{
|
|
1002
|
+
description: 'sends delta when active file changes',
|
|
1003
|
+
previousActiveFile: {
|
|
1004
|
+
path: '/path/to/old/file.ts',
|
|
1005
|
+
cursor: { line: 5, character: 10 },
|
|
1006
|
+
selectedText: 'hello',
|
|
1007
|
+
},
|
|
1008
|
+
currentActiveFile: {
|
|
1009
|
+
path: '/path/to/active/file.ts',
|
|
1010
|
+
cursor: { line: 5, character: 10 },
|
|
1011
|
+
selectedText: 'hello',
|
|
1012
|
+
},
|
|
1013
|
+
shouldSendContext: true,
|
|
1014
|
+
},
|
|
1015
|
+
{
|
|
1016
|
+
description: 'sends delta when cursor line changes',
|
|
1017
|
+
previousActiveFile: {
|
|
1018
|
+
path: '/path/to/active/file.ts',
|
|
1019
|
+
cursor: { line: 1, character: 10 },
|
|
1020
|
+
selectedText: 'hello',
|
|
1021
|
+
},
|
|
1022
|
+
currentActiveFile: {
|
|
1023
|
+
path: '/path/to/active/file.ts',
|
|
1024
|
+
cursor: { line: 5, character: 10 },
|
|
1025
|
+
selectedText: 'hello',
|
|
1026
|
+
},
|
|
1027
|
+
shouldSendContext: true,
|
|
1028
|
+
},
|
|
1029
|
+
{
|
|
1030
|
+
description: 'sends delta when cursor character changes',
|
|
1031
|
+
previousActiveFile: {
|
|
1032
|
+
path: '/path/to/active/file.ts',
|
|
1033
|
+
cursor: { line: 5, character: 1 },
|
|
1034
|
+
selectedText: 'hello',
|
|
1035
|
+
},
|
|
1036
|
+
currentActiveFile: {
|
|
1037
|
+
path: '/path/to/active/file.ts',
|
|
1038
|
+
cursor: { line: 5, character: 10 },
|
|
1039
|
+
selectedText: 'hello',
|
|
1040
|
+
},
|
|
1041
|
+
shouldSendContext: true,
|
|
1042
|
+
},
|
|
1043
|
+
{
|
|
1044
|
+
description: 'sends delta when selected text changes',
|
|
1045
|
+
previousActiveFile: {
|
|
1046
|
+
path: '/path/to/active/file.ts',
|
|
1047
|
+
cursor: { line: 5, character: 10 },
|
|
1048
|
+
selectedText: 'world',
|
|
1049
|
+
},
|
|
1050
|
+
currentActiveFile: {
|
|
1051
|
+
path: '/path/to/active/file.ts',
|
|
1052
|
+
cursor: { line: 5, character: 10 },
|
|
1053
|
+
selectedText: 'hello',
|
|
1054
|
+
},
|
|
1055
|
+
shouldSendContext: true,
|
|
1056
|
+
},
|
|
1057
|
+
{
|
|
1058
|
+
description: 'sends delta when selected text is added',
|
|
1059
|
+
previousActiveFile: {
|
|
1060
|
+
path: '/path/to/active/file.ts',
|
|
1061
|
+
cursor: { line: 5, character: 10 },
|
|
1062
|
+
},
|
|
1063
|
+
currentActiveFile: {
|
|
1064
|
+
path: '/path/to/active/file.ts',
|
|
1065
|
+
cursor: { line: 5, character: 10 },
|
|
1066
|
+
selectedText: 'hello',
|
|
1067
|
+
},
|
|
1068
|
+
shouldSendContext: true,
|
|
1069
|
+
},
|
|
1070
|
+
{
|
|
1071
|
+
description: 'sends delta when selected text is removed',
|
|
1072
|
+
previousActiveFile: {
|
|
1073
|
+
path: '/path/to/active/file.ts',
|
|
1074
|
+
cursor: { line: 5, character: 10 },
|
|
1075
|
+
selectedText: 'hello',
|
|
1076
|
+
},
|
|
1077
|
+
currentActiveFile: {
|
|
1078
|
+
path: '/path/to/active/file.ts',
|
|
1079
|
+
cursor: { line: 5, character: 10 },
|
|
1080
|
+
},
|
|
1081
|
+
shouldSendContext: true,
|
|
1082
|
+
},
|
|
1083
|
+
{
|
|
1084
|
+
description: 'does not send context when nothing changes',
|
|
1085
|
+
previousActiveFile: {
|
|
1086
|
+
path: '/path/to/active/file.ts',
|
|
1087
|
+
cursor: { line: 5, character: 10 },
|
|
1088
|
+
selectedText: 'hello',
|
|
1089
|
+
},
|
|
1090
|
+
currentActiveFile: {
|
|
1091
|
+
path: '/path/to/active/file.ts',
|
|
1092
|
+
cursor: { line: 5, character: 10 },
|
|
1093
|
+
selectedText: 'hello',
|
|
1094
|
+
},
|
|
1095
|
+
shouldSendContext: false,
|
|
1096
|
+
},
|
|
1097
|
+
];
|
|
1098
|
+
it.each(testCases)('$description', async ({ previousActiveFile, currentActiveFile, shouldSendContext, }) => {
|
|
1099
|
+
// Setup previous context
|
|
1100
|
+
client['lastSentIdeContext'] = {
|
|
1101
|
+
workspaceState: {
|
|
1102
|
+
openFiles: [
|
|
1103
|
+
{
|
|
1104
|
+
path: previousActiveFile.path,
|
|
1105
|
+
cursor: previousActiveFile.cursor,
|
|
1106
|
+
selectedText: previousActiveFile.selectedText,
|
|
1107
|
+
isActive: true,
|
|
1108
|
+
timestamp: Date.now() - 1000,
|
|
1109
|
+
},
|
|
1110
|
+
],
|
|
1111
|
+
},
|
|
1112
|
+
};
|
|
1113
|
+
// Setup current context
|
|
1114
|
+
vi.mocked(ideContext.getIdeContext).mockReturnValue({
|
|
1115
|
+
workspaceState: {
|
|
1116
|
+
openFiles: [
|
|
1117
|
+
{ ...currentActiveFile, isActive: true, timestamp: Date.now() },
|
|
1118
|
+
],
|
|
1119
|
+
},
|
|
1120
|
+
});
|
|
1121
|
+
const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-id-delta');
|
|
1122
|
+
for await (const _ of stream) {
|
|
1123
|
+
// consume stream
|
|
1124
|
+
}
|
|
1125
|
+
const mockChat = client['chat'];
|
|
1126
|
+
if (shouldSendContext) {
|
|
1127
|
+
expect(mockChat.addHistory).toHaveBeenCalledWith(expect.objectContaining({
|
|
1128
|
+
parts: expect.arrayContaining([
|
|
1129
|
+
expect.objectContaining({
|
|
1130
|
+
text: expect.stringContaining("Here is a summary of changes in the user's editor context"),
|
|
1131
|
+
}),
|
|
1132
|
+
]),
|
|
1133
|
+
}));
|
|
1134
|
+
}
|
|
1135
|
+
else {
|
|
1136
|
+
expect(mockChat.addHistory).not.toHaveBeenCalled();
|
|
1137
|
+
}
|
|
1138
|
+
});
|
|
1139
|
+
it('sends full context when history is cleared, even if editor state is unchanged', async () => {
|
|
1140
|
+
const activeFile = {
|
|
1141
|
+
path: '/path/to/active/file.ts',
|
|
1142
|
+
cursor: { line: 5, character: 10 },
|
|
1143
|
+
selectedText: 'hello',
|
|
1144
|
+
};
|
|
1145
|
+
// Setup previous context
|
|
1146
|
+
client['lastSentIdeContext'] = {
|
|
1147
|
+
workspaceState: {
|
|
1148
|
+
openFiles: [
|
|
1149
|
+
{
|
|
1150
|
+
path: activeFile.path,
|
|
1151
|
+
cursor: activeFile.cursor,
|
|
1152
|
+
selectedText: activeFile.selectedText,
|
|
1153
|
+
isActive: true,
|
|
1154
|
+
timestamp: Date.now() - 1000,
|
|
1155
|
+
},
|
|
1156
|
+
],
|
|
1157
|
+
},
|
|
1158
|
+
};
|
|
1159
|
+
// Setup current context (same as previous)
|
|
1160
|
+
vi.mocked(ideContext.getIdeContext).mockReturnValue({
|
|
1161
|
+
workspaceState: {
|
|
1162
|
+
openFiles: [
|
|
1163
|
+
{ ...activeFile, isActive: true, timestamp: Date.now() },
|
|
1164
|
+
],
|
|
1165
|
+
},
|
|
1166
|
+
});
|
|
1167
|
+
// Make history empty
|
|
1168
|
+
const mockChat = client['chat'];
|
|
1169
|
+
mockChat.getHistory.mockReturnValue([]);
|
|
1170
|
+
const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-id-history-cleared');
|
|
1171
|
+
for await (const _ of stream) {
|
|
1172
|
+
// consume stream
|
|
1173
|
+
}
|
|
1174
|
+
expect(mockChat.addHistory).toHaveBeenCalledWith(expect.objectContaining({
|
|
1175
|
+
parts: expect.arrayContaining([
|
|
1176
|
+
expect.objectContaining({
|
|
1177
|
+
text: expect.stringContaining("Here is the user's editor context"),
|
|
1178
|
+
}),
|
|
1179
|
+
]),
|
|
1180
|
+
}));
|
|
1181
|
+
// Also verify it's the full context, not a delta.
|
|
1182
|
+
const call = mockChat.addHistory.mock.calls[0][0];
|
|
1183
|
+
const contextText = call.parts[0].text;
|
|
1184
|
+
const contextJson = JSON.parse(contextText.match(/```json\n(.*)\n```/s)[1]);
|
|
1185
|
+
expect(contextJson).toHaveProperty('activeFile');
|
|
1186
|
+
expect(contextJson.activeFile.path).toBe('/path/to/active/file.ts');
|
|
1187
|
+
});
|
|
1188
|
+
});
|
|
1189
|
+
describe('IDE context with pending tool calls', () => {
|
|
1190
|
+
let mockChat;
|
|
1191
|
+
beforeEach(() => {
|
|
1192
|
+
vi.spyOn(client, 'tryCompressChat').mockResolvedValue(null);
|
|
1193
|
+
const mockStream = (async function* () {
|
|
1194
|
+
yield { type: 'content', value: 'response' };
|
|
1195
|
+
})();
|
|
1196
|
+
mockTurnRunFn.mockReturnValue(mockStream);
|
|
1197
|
+
mockChat = {
|
|
1198
|
+
addHistory: vi.fn(),
|
|
1199
|
+
getHistory: vi.fn().mockReturnValue([]), // Default empty history
|
|
1200
|
+
setHistory: vi.fn(),
|
|
1201
|
+
sendMessage: vi.fn().mockResolvedValue({ text: 'summary' }),
|
|
1202
|
+
};
|
|
1203
|
+
client['chat'] = mockChat;
|
|
1204
|
+
const mockGenerator = {
|
|
1205
|
+
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
1206
|
+
};
|
|
1207
|
+
client['contentGenerator'] = mockGenerator;
|
|
1208
|
+
vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
|
|
1209
|
+
vi.mocked(ideContext.getIdeContext).mockReturnValue({
|
|
1210
|
+
workspaceState: {
|
|
1211
|
+
openFiles: [{ path: '/path/to/file.ts', timestamp: Date.now() }],
|
|
1212
|
+
},
|
|
1213
|
+
});
|
|
1214
|
+
});
|
|
1215
|
+
it('should NOT add IDE context when a tool call is pending', async () => {
|
|
1216
|
+
// Arrange: History ends with a functionCall from the model
|
|
1217
|
+
const historyWithPendingCall = [
|
|
1218
|
+
{ role: 'user', parts: [{ text: 'Please use a tool.' }] },
|
|
1219
|
+
{
|
|
1220
|
+
role: 'model',
|
|
1221
|
+
parts: [{ functionCall: { name: 'some_tool', args: {} } }],
|
|
1222
|
+
},
|
|
1223
|
+
];
|
|
1224
|
+
vi.mocked(mockChat.getHistory).mockReturnValue(historyWithPendingCall);
|
|
1225
|
+
// Act: Simulate sending the tool's response back
|
|
1226
|
+
const stream = client.sendMessageStream([
|
|
1227
|
+
{
|
|
1228
|
+
functionResponse: {
|
|
1229
|
+
name: 'some_tool',
|
|
1230
|
+
response: { success: true },
|
|
1231
|
+
},
|
|
1232
|
+
},
|
|
1233
|
+
], new AbortController().signal, 'prompt-id-tool-response');
|
|
1234
|
+
for await (const _ of stream) {
|
|
1235
|
+
// consume stream to complete the call
|
|
1236
|
+
}
|
|
1237
|
+
// Assert: The IDE context message should NOT have been added to the history.
|
|
1238
|
+
expect(mockChat.addHistory).not.toHaveBeenCalledWith(expect.objectContaining({
|
|
1239
|
+
parts: expect.arrayContaining([
|
|
1240
|
+
expect.objectContaining({
|
|
1241
|
+
text: expect.stringContaining("user's editor context"),
|
|
1242
|
+
}),
|
|
1243
|
+
]),
|
|
1244
|
+
}));
|
|
1245
|
+
});
|
|
1246
|
+
it('should add IDE context when no tool call is pending', async () => {
|
|
1247
|
+
// Arrange: History is normal, no pending calls
|
|
1248
|
+
const normalHistory = [
|
|
1249
|
+
{ role: 'user', parts: [{ text: 'A normal message.' }] },
|
|
1250
|
+
{ role: 'model', parts: [{ text: 'A normal response.' }] },
|
|
1251
|
+
];
|
|
1252
|
+
vi.mocked(mockChat.getHistory).mockReturnValue(normalHistory);
|
|
1253
|
+
// Act
|
|
1254
|
+
const stream = client.sendMessageStream([{ text: 'Another normal message' }], new AbortController().signal, 'prompt-id-normal');
|
|
1255
|
+
for await (const _ of stream) {
|
|
1256
|
+
// consume stream
|
|
1257
|
+
}
|
|
1258
|
+
// Assert: The IDE context message SHOULD have been added.
|
|
1259
|
+
expect(mockChat.addHistory).toHaveBeenCalledWith(expect.objectContaining({
|
|
1260
|
+
role: 'user',
|
|
1261
|
+
parts: expect.arrayContaining([
|
|
1262
|
+
expect.objectContaining({
|
|
1263
|
+
text: expect.stringContaining("user's editor context"),
|
|
1264
|
+
}),
|
|
1265
|
+
]),
|
|
1266
|
+
}));
|
|
1267
|
+
});
|
|
1268
|
+
it('should send the latest IDE context on the next message after a skipped context', async () => {
|
|
1269
|
+
// --- Step 1: A tool call is pending, context should be skipped ---
|
|
1270
|
+
// Arrange: History ends with a functionCall
|
|
1271
|
+
const historyWithPendingCall = [
|
|
1272
|
+
{ role: 'user', parts: [{ text: 'Please use a tool.' }] },
|
|
1273
|
+
{
|
|
1274
|
+
role: 'model',
|
|
1275
|
+
parts: [{ functionCall: { name: 'some_tool', args: {} } }],
|
|
1276
|
+
},
|
|
1277
|
+
];
|
|
1278
|
+
vi.mocked(mockChat.getHistory).mockReturnValue(historyWithPendingCall);
|
|
1279
|
+
// Arrange: Set the initial IDE context
|
|
1280
|
+
const initialIdeContext = {
|
|
1281
|
+
workspaceState: {
|
|
1282
|
+
openFiles: [{ path: '/path/to/fileA.ts', timestamp: Date.now() }],
|
|
1283
|
+
},
|
|
1284
|
+
};
|
|
1285
|
+
vi.mocked(ideContext.getIdeContext).mockReturnValue(initialIdeContext);
|
|
1286
|
+
// Act: Send the tool response
|
|
1287
|
+
let stream = client.sendMessageStream([
|
|
1288
|
+
{
|
|
1289
|
+
functionResponse: {
|
|
1290
|
+
name: 'some_tool',
|
|
1291
|
+
response: { success: true },
|
|
1292
|
+
},
|
|
1293
|
+
},
|
|
1294
|
+
], new AbortController().signal, 'prompt-id-tool-response');
|
|
1295
|
+
for await (const _ of stream) {
|
|
1296
|
+
/* consume */
|
|
1297
|
+
}
|
|
1298
|
+
// Assert: The initial context was NOT sent
|
|
1299
|
+
expect(mockChat.addHistory).not.toHaveBeenCalledWith(expect.objectContaining({
|
|
1300
|
+
parts: expect.arrayContaining([
|
|
1301
|
+
expect.objectContaining({
|
|
1302
|
+
text: expect.stringContaining("user's editor context"),
|
|
1303
|
+
}),
|
|
1304
|
+
]),
|
|
1305
|
+
}));
|
|
1306
|
+
// --- Step 2: A new message is sent, latest context should be included ---
|
|
1307
|
+
// Arrange: The model has responded to the tool, and the user is sending a new message.
|
|
1308
|
+
const historyAfterToolResponse = [
|
|
1309
|
+
...historyWithPendingCall,
|
|
1310
|
+
{
|
|
1311
|
+
role: 'user',
|
|
1312
|
+
parts: [
|
|
1313
|
+
{
|
|
1314
|
+
functionResponse: {
|
|
1315
|
+
name: 'some_tool',
|
|
1316
|
+
response: { success: true },
|
|
1317
|
+
},
|
|
1318
|
+
},
|
|
1319
|
+
],
|
|
1320
|
+
},
|
|
1321
|
+
{ role: 'model', parts: [{ text: 'The tool ran successfully.' }] },
|
|
1322
|
+
];
|
|
1323
|
+
vi.mocked(mockChat.getHistory).mockReturnValue(historyAfterToolResponse);
|
|
1324
|
+
vi.mocked(mockChat.addHistory).mockClear(); // Clear previous calls for the next assertion
|
|
1325
|
+
// Arrange: The IDE context has now changed
|
|
1326
|
+
const newIdeContext = {
|
|
1327
|
+
workspaceState: {
|
|
1328
|
+
openFiles: [{ path: '/path/to/fileB.ts', timestamp: Date.now() }],
|
|
1329
|
+
},
|
|
1330
|
+
};
|
|
1331
|
+
vi.mocked(ideContext.getIdeContext).mockReturnValue(newIdeContext);
|
|
1332
|
+
// Act: Send a new, regular user message
|
|
1333
|
+
stream = client.sendMessageStream([{ text: 'Thanks!' }], new AbortController().signal, 'prompt-id-final');
|
|
1334
|
+
for await (const _ of stream) {
|
|
1335
|
+
/* consume */
|
|
1336
|
+
}
|
|
1337
|
+
// Assert: The NEW context was sent as a FULL context because there was no previously sent context.
|
|
1338
|
+
const addHistoryCalls = vi.mocked(mockChat.addHistory).mock.calls;
|
|
1339
|
+
const contextCall = addHistoryCalls.find((call) => JSON.stringify(call[0]).includes("user's editor context"));
|
|
1340
|
+
expect(contextCall).toBeDefined();
|
|
1341
|
+
expect(JSON.stringify(contextCall[0])).toContain("Here is the user's editor context as a JSON object");
|
|
1342
|
+
// Check that the sent context is the new one (fileB.ts)
|
|
1343
|
+
expect(JSON.stringify(contextCall[0])).toContain('fileB.ts');
|
|
1344
|
+
// Check that the sent context is NOT the old one (fileA.ts)
|
|
1345
|
+
expect(JSON.stringify(contextCall[0])).not.toContain('fileA.ts');
|
|
1346
|
+
});
|
|
1347
|
+
it('should send a context DELTA on the next message after a skipped context', async () => {
|
|
1348
|
+
// --- Step 0: Establish an initial context ---
|
|
1349
|
+
vi.mocked(mockChat.getHistory).mockReturnValue([]); // Start with empty history
|
|
1350
|
+
const contextA = {
|
|
1351
|
+
workspaceState: {
|
|
1352
|
+
openFiles: [
|
|
1353
|
+
{
|
|
1354
|
+
path: '/path/to/fileA.ts',
|
|
1355
|
+
isActive: true,
|
|
1356
|
+
timestamp: Date.now(),
|
|
1357
|
+
},
|
|
1358
|
+
],
|
|
1359
|
+
},
|
|
1360
|
+
};
|
|
1361
|
+
vi.mocked(ideContext.getIdeContext).mockReturnValue(contextA);
|
|
1362
|
+
// Act: Send a regular message to establish the initial context
|
|
1363
|
+
let stream = client.sendMessageStream([{ text: 'Initial message' }], new AbortController().signal, 'prompt-id-initial');
|
|
1364
|
+
for await (const _ of stream) {
|
|
1365
|
+
/* consume */
|
|
1366
|
+
}
|
|
1367
|
+
// Assert: Full context for fileA.ts was sent and stored.
|
|
1368
|
+
const initialCall = vi.mocked(mockChat.addHistory).mock.calls[0][0];
|
|
1369
|
+
expect(JSON.stringify(initialCall)).toContain("user's editor context as a JSON object");
|
|
1370
|
+
expect(JSON.stringify(initialCall)).toContain('fileA.ts');
|
|
1371
|
+
// This implicitly tests that `lastSentIdeContext` is now set internally by the client.
|
|
1372
|
+
vi.mocked(mockChat.addHistory).mockClear();
|
|
1373
|
+
// --- Step 1: A tool call is pending, context should be skipped ---
|
|
1374
|
+
const historyWithPendingCall = [
|
|
1375
|
+
{ role: 'user', parts: [{ text: 'Please use a tool.' }] },
|
|
1376
|
+
{
|
|
1377
|
+
role: 'model',
|
|
1378
|
+
parts: [{ functionCall: { name: 'some_tool', args: {} } }],
|
|
1379
|
+
},
|
|
1380
|
+
];
|
|
1381
|
+
vi.mocked(mockChat.getHistory).mockReturnValue(historyWithPendingCall);
|
|
1382
|
+
// Arrange: IDE context changes, but this should be skipped
|
|
1383
|
+
const contextB = {
|
|
1384
|
+
workspaceState: {
|
|
1385
|
+
openFiles: [
|
|
1386
|
+
{
|
|
1387
|
+
path: '/path/to/fileB.ts',
|
|
1388
|
+
isActive: true,
|
|
1389
|
+
timestamp: Date.now(),
|
|
1390
|
+
},
|
|
1391
|
+
],
|
|
1392
|
+
},
|
|
1393
|
+
};
|
|
1394
|
+
vi.mocked(ideContext.getIdeContext).mockReturnValue(contextB);
|
|
1395
|
+
// Act: Send the tool response
|
|
1396
|
+
stream = client.sendMessageStream([
|
|
1397
|
+
{
|
|
1398
|
+
functionResponse: {
|
|
1399
|
+
name: 'some_tool',
|
|
1400
|
+
response: { success: true },
|
|
1401
|
+
},
|
|
1402
|
+
},
|
|
1403
|
+
], new AbortController().signal, 'prompt-id-tool-response');
|
|
1404
|
+
for await (const _ of stream) {
|
|
1405
|
+
/* consume */
|
|
1406
|
+
}
|
|
1407
|
+
// Assert: No context was sent
|
|
1408
|
+
expect(mockChat.addHistory).not.toHaveBeenCalled();
|
|
1409
|
+
// --- Step 2: A new message is sent, latest context DELTA should be included ---
|
|
1410
|
+
const historyAfterToolResponse = [
|
|
1411
|
+
...historyWithPendingCall,
|
|
1412
|
+
{
|
|
1413
|
+
role: 'user',
|
|
1414
|
+
parts: [
|
|
1415
|
+
{
|
|
1416
|
+
functionResponse: {
|
|
1417
|
+
name: 'some_tool',
|
|
1418
|
+
response: { success: true },
|
|
1419
|
+
},
|
|
1420
|
+
},
|
|
1421
|
+
],
|
|
1422
|
+
},
|
|
1423
|
+
{ role: 'model', parts: [{ text: 'The tool ran successfully.' }] },
|
|
1424
|
+
];
|
|
1425
|
+
vi.mocked(mockChat.getHistory).mockReturnValue(historyAfterToolResponse);
|
|
1426
|
+
// Arrange: The IDE context has changed again
|
|
1427
|
+
const contextC = {
|
|
1428
|
+
workspaceState: {
|
|
1429
|
+
openFiles: [
|
|
1430
|
+
// fileA is now closed, fileC is open
|
|
1431
|
+
{
|
|
1432
|
+
path: '/path/to/fileC.ts',
|
|
1433
|
+
isActive: true,
|
|
1434
|
+
timestamp: Date.now(),
|
|
1435
|
+
},
|
|
1436
|
+
],
|
|
1437
|
+
},
|
|
1438
|
+
};
|
|
1439
|
+
vi.mocked(ideContext.getIdeContext).mockReturnValue(contextC);
|
|
1440
|
+
// Act: Send a new, regular user message
|
|
1441
|
+
stream = client.sendMessageStream([{ text: 'Thanks!' }], new AbortController().signal, 'prompt-id-final');
|
|
1442
|
+
for await (const _ of stream) {
|
|
1443
|
+
/* consume */
|
|
1444
|
+
}
|
|
1445
|
+
// Assert: The DELTA context was sent
|
|
1446
|
+
const finalCall = vi.mocked(mockChat.addHistory).mock.calls[0][0];
|
|
1447
|
+
expect(JSON.stringify(finalCall)).toContain('summary of changes');
|
|
1448
|
+
// The delta should reflect fileA being closed and fileC being opened.
|
|
1449
|
+
expect(JSON.stringify(finalCall)).toContain('filesClosed');
|
|
1450
|
+
expect(JSON.stringify(finalCall)).toContain('fileA.ts');
|
|
1451
|
+
expect(JSON.stringify(finalCall)).toContain('activeFileChanged');
|
|
1452
|
+
expect(JSON.stringify(finalCall)).toContain('fileC.ts');
|
|
1453
|
+
});
|
|
1454
|
+
});
|
|
767
1455
|
});
|
|
768
1456
|
describe('generateContent', () => {
|
|
769
1457
|
it('should use current model from config for content generation', async () => {
|
|
@@ -786,7 +1474,7 @@ Here are files the user has recently opened, with the most recent at the top:
|
|
|
786
1474
|
model: currentModel,
|
|
787
1475
|
config: expect.any(Object),
|
|
788
1476
|
contents,
|
|
789
|
-
});
|
|
1477
|
+
}, 'test-session-id');
|
|
790
1478
|
});
|
|
791
1479
|
});
|
|
792
1480
|
describe('tryCompressChat', () => {
|
|
@@ -840,7 +1528,8 @@ Here are files the user has recently opened, with the most recent at the top:
|
|
|
840
1528
|
const fallbackModel = DEFAULT_GEMINI_FLASH_MODEL;
|
|
841
1529
|
// mock config been changed
|
|
842
1530
|
const currentModel = initialModel + '-changed';
|
|
843
|
-
vi.spyOn(client['config'], 'getModel')
|
|
1531
|
+
const getModelSpy = vi.spyOn(client['config'], 'getModel');
|
|
1532
|
+
getModelSpy.mockReturnValue(currentModel);
|
|
844
1533
|
const mockFallbackHandler = vi.fn().mockResolvedValue(true);
|
|
845
1534
|
client['config'].flashFallbackHandler = mockFallbackHandler;
|
|
846
1535
|
client['config'].setModel = vi.fn();
|
|
@@ -849,5 +1538,65 @@ Here are files the user has recently opened, with the most recent at the top:
|
|
|
849
1538
|
expect(mockFallbackHandler).toHaveBeenCalledWith(currentModel, fallbackModel, undefined);
|
|
850
1539
|
});
|
|
851
1540
|
});
|
|
1541
|
+
describe('setHistory', () => {
|
|
1542
|
+
it('should strip thought signatures when stripThoughts is true', () => {
|
|
1543
|
+
const mockChat = {
|
|
1544
|
+
setHistory: vi.fn(),
|
|
1545
|
+
};
|
|
1546
|
+
client['chat'] = mockChat;
|
|
1547
|
+
const historyWithThoughts = [
|
|
1548
|
+
{
|
|
1549
|
+
role: 'user',
|
|
1550
|
+
parts: [{ text: 'hello' }],
|
|
1551
|
+
},
|
|
1552
|
+
{
|
|
1553
|
+
role: 'model',
|
|
1554
|
+
parts: [
|
|
1555
|
+
{ text: 'thinking...', thoughtSignature: 'thought-123' },
|
|
1556
|
+
{
|
|
1557
|
+
functionCall: { name: 'test', args: {} },
|
|
1558
|
+
thoughtSignature: 'thought-456',
|
|
1559
|
+
},
|
|
1560
|
+
],
|
|
1561
|
+
},
|
|
1562
|
+
];
|
|
1563
|
+
client.setHistory(historyWithThoughts, { stripThoughts: true });
|
|
1564
|
+
const expectedHistory = [
|
|
1565
|
+
{
|
|
1566
|
+
role: 'user',
|
|
1567
|
+
parts: [{ text: 'hello' }],
|
|
1568
|
+
},
|
|
1569
|
+
{
|
|
1570
|
+
role: 'model',
|
|
1571
|
+
parts: [
|
|
1572
|
+
{ text: 'thinking...' },
|
|
1573
|
+
{ functionCall: { name: 'test', args: {} } },
|
|
1574
|
+
],
|
|
1575
|
+
},
|
|
1576
|
+
];
|
|
1577
|
+
expect(mockChat.setHistory).toHaveBeenCalledWith(expectedHistory);
|
|
1578
|
+
});
|
|
1579
|
+
it('should not strip thought signatures when stripThoughts is false', () => {
|
|
1580
|
+
const mockChat = {
|
|
1581
|
+
setHistory: vi.fn(),
|
|
1582
|
+
};
|
|
1583
|
+
client['chat'] = mockChat;
|
|
1584
|
+
const historyWithThoughts = [
|
|
1585
|
+
{
|
|
1586
|
+
role: 'user',
|
|
1587
|
+
parts: [{ text: 'hello' }],
|
|
1588
|
+
},
|
|
1589
|
+
{
|
|
1590
|
+
role: 'model',
|
|
1591
|
+
parts: [
|
|
1592
|
+
{ text: 'thinking...', thoughtSignature: 'thought-123' },
|
|
1593
|
+
{ text: 'ok', thoughtSignature: 'thought-456' },
|
|
1594
|
+
],
|
|
1595
|
+
},
|
|
1596
|
+
];
|
|
1597
|
+
client.setHistory(historyWithThoughts, { stripThoughts: false });
|
|
1598
|
+
expect(mockChat.setHistory).toHaveBeenCalledWith(historyWithThoughts);
|
|
1599
|
+
});
|
|
1600
|
+
});
|
|
852
1601
|
});
|
|
853
1602
|
//# sourceMappingURL=client.test.js.map
|