@office-ai/aioncli-core 0.2.3 → 0.18.4
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 +16 -3
- package/dist/index.js +15 -3
- package/dist/index.js.map +1 -1
- package/dist/src/agents/codebase-investigator.d.ts +46 -0
- package/dist/src/agents/codebase-investigator.js +132 -0
- package/dist/src/agents/codebase-investigator.js.map +1 -0
- package/dist/src/agents/codebase-investigator.test.js +35 -0
- package/dist/src/agents/codebase-investigator.test.js.map +1 -0
- package/dist/src/agents/executor.d.ts +114 -0
- package/dist/src/agents/executor.js +779 -0
- package/dist/src/agents/executor.js.map +1 -0
- package/dist/src/agents/executor.test.js +1362 -0
- package/dist/src/agents/executor.test.js.map +1 -0
- package/dist/src/agents/invocation.d.ts +46 -0
- package/dist/src/agents/invocation.js +102 -0
- package/dist/src/agents/invocation.js.map +1 -0
- package/dist/src/agents/invocation.test.js +215 -0
- package/dist/src/agents/invocation.test.js.map +1 -0
- package/dist/src/agents/registry.d.ts +40 -0
- package/dist/src/agents/registry.js +105 -0
- package/dist/src/agents/registry.js.map +1 -0
- package/dist/src/agents/registry.test.d.ts +6 -0
- package/dist/src/agents/registry.test.js +160 -0
- package/dist/src/agents/registry.test.js.map +1 -0
- package/dist/src/agents/schema-utils.d.ts +39 -0
- package/dist/src/agents/schema-utils.js +57 -0
- package/dist/src/agents/schema-utils.js.map +1 -0
- package/dist/src/agents/schema-utils.test.d.ts +6 -0
- package/dist/src/agents/schema-utils.test.js +144 -0
- package/dist/src/agents/schema-utils.test.js.map +1 -0
- package/dist/src/agents/subagent-tool-wrapper.d.ts +38 -0
- package/dist/src/agents/subagent-tool-wrapper.js +48 -0
- package/dist/src/agents/subagent-tool-wrapper.js.map +1 -0
- package/dist/src/agents/subagent-tool-wrapper.test.d.ts +6 -0
- package/dist/src/agents/subagent-tool-wrapper.test.js +110 -0
- package/dist/src/agents/subagent-tool-wrapper.test.js.map +1 -0
- package/dist/src/agents/types.d.ts +146 -0
- package/dist/src/agents/types.js +19 -0
- package/dist/src/agents/types.js.map +1 -0
- package/dist/src/agents/utils.d.ts +15 -0
- package/dist/src/agents/utils.js +29 -0
- package/dist/src/agents/utils.js.map +1 -0
- package/dist/src/agents/utils.test.d.ts +6 -0
- package/dist/src/agents/utils.test.js +87 -0
- package/dist/src/agents/utils.test.js.map +1 -0
- package/dist/src/code_assist/codeAssist.d.ts +6 -3
- package/dist/src/code_assist/codeAssist.js +13 -1
- package/dist/src/code_assist/codeAssist.js.map +1 -1
- package/dist/src/code_assist/codeAssist.test.d.ts +6 -0
- package/dist/src/code_assist/codeAssist.test.js +99 -0
- package/dist/src/code_assist/codeAssist.test.js.map +1 -0
- package/dist/src/code_assist/converter.d.ts +5 -1
- package/dist/src/code_assist/converter.js +39 -5
- package/dist/src/code_assist/converter.js.map +1 -1
- package/dist/src/code_assist/converter.test.js +112 -0
- package/dist/src/code_assist/converter.test.js.map +1 -1
- package/dist/src/code_assist/experiments/client_metadata.d.ts +12 -0
- package/dist/src/code_assist/experiments/client_metadata.js +50 -0
- package/dist/src/code_assist/experiments/client_metadata.js.map +1 -0
- package/dist/src/code_assist/experiments/client_metadata.test.d.ts +6 -0
- package/dist/src/code_assist/experiments/client_metadata.test.js +99 -0
- package/dist/src/code_assist/experiments/client_metadata.test.js.map +1 -0
- package/dist/src/code_assist/experiments/experiments.d.ts +17 -0
- package/dist/src/code_assist/experiments/experiments.js +36 -0
- package/dist/src/code_assist/experiments/experiments.js.map +1 -0
- package/dist/src/code_assist/experiments/experiments.test.d.ts +6 -0
- package/dist/src/code_assist/experiments/experiments.test.js +92 -0
- package/dist/src/code_assist/experiments/experiments.test.js.map +1 -0
- package/dist/src/code_assist/experiments/flagNames.d.ts +13 -0
- package/dist/src/code_assist/experiments/flagNames.js +13 -0
- package/dist/src/code_assist/experiments/flagNames.js.map +1 -0
- package/dist/src/code_assist/experiments/types.d.ts +35 -0
- package/dist/src/code_assist/experiments/types.js +7 -0
- package/dist/src/code_assist/experiments/types.js.map +1 -0
- package/dist/src/code_assist/oauth-credential-storage.d.ts +25 -0
- package/dist/src/code_assist/oauth-credential-storage.js +110 -0
- package/dist/src/code_assist/oauth-credential-storage.js.map +1 -0
- package/dist/src/code_assist/oauth-credential-storage.test.d.ts +6 -0
- package/dist/src/code_assist/oauth-credential-storage.test.js +198 -0
- package/dist/src/code_assist/oauth-credential-storage.test.js.map +1 -0
- package/dist/src/code_assist/oauth2.d.ts +3 -3
- package/dist/src/code_assist/oauth2.js +252 -125
- package/dist/src/code_assist/oauth2.js.map +1 -1
- package/dist/src/code_assist/oauth2.test.js +788 -350
- package/dist/src/code_assist/oauth2.test.js.map +1 -1
- package/dist/src/code_assist/server.d.ts +8 -6
- package/dist/src/code_assist/server.js +41 -10
- package/dist/src/code_assist/server.js.map +1 -1
- package/dist/src/code_assist/server.test.js +151 -28
- package/dist/src/code_assist/server.test.js.map +1 -1
- package/dist/src/code_assist/setup.d.ts +2 -2
- package/dist/src/code_assist/setup.js +5 -3
- package/dist/src/code_assist/setup.js.map +1 -1
- package/dist/src/code_assist/setup.test.js.map +1 -1
- package/dist/src/code_assist/types.d.ts +18 -3
- package/dist/src/code_assist/types.js.map +1 -1
- package/dist/src/commands/extensions.d.ts +7 -0
- package/dist/src/commands/extensions.js +9 -0
- package/dist/src/commands/extensions.js.map +1 -0
- package/dist/src/commands/extensions.test.d.ts +6 -0
- package/dist/src/commands/extensions.test.js +19 -0
- package/dist/src/commands/extensions.test.js.map +1 -0
- package/dist/src/config/config.d.ts +282 -60
- package/dist/src/config/config.js +677 -129
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +1020 -146
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/constants.d.ts +11 -0
- package/dist/src/config/constants.js +16 -0
- package/dist/src/config/constants.js.map +1 -0
- package/dist/src/config/defaultModelConfigs.d.ts +7 -0
- package/dist/src/config/defaultModelConfigs.js +185 -0
- package/dist/src/config/defaultModelConfigs.js.map +1 -0
- package/dist/src/config/models.d.ts +37 -0
- package/dist/src/config/models.js +72 -0
- package/dist/src/config/models.js.map +1 -1
- package/dist/src/config/models.test.d.ts +6 -0
- package/dist/src/config/models.test.js +116 -0
- package/dist/src/config/models.test.js.map +1 -0
- package/dist/src/config/storage.d.ts +36 -0
- package/dist/src/config/storage.js +115 -0
- package/dist/src/config/storage.js.map +1 -0
- package/dist/src/config/storage.test.d.ts +6 -0
- package/dist/src/config/storage.test.js +48 -0
- package/dist/src/config/storage.test.js.map +1 -0
- package/dist/src/confirmation-bus/index.d.ts +7 -0
- package/dist/src/confirmation-bus/index.js +8 -0
- package/dist/src/confirmation-bus/index.js.map +1 -0
- package/dist/src/confirmation-bus/message-bus.d.ts +18 -0
- package/dist/src/confirmation-bus/message-bus.js +87 -0
- package/dist/src/confirmation-bus/message-bus.js.map +1 -0
- package/dist/src/confirmation-bus/message-bus.test.d.ts +6 -0
- package/dist/src/confirmation-bus/message-bus.test.js +170 -0
- package/dist/src/confirmation-bus/message-bus.test.js.map +1 -0
- package/dist/src/confirmation-bus/types.d.ts +49 -0
- package/dist/src/confirmation-bus/types.js +16 -0
- package/dist/src/confirmation-bus/types.js.map +1 -0
- package/dist/src/core/apiKeyCredentialStorage.d.ts +17 -0
- package/dist/src/core/apiKeyCredentialStorage.js +64 -0
- package/dist/src/core/apiKeyCredentialStorage.js.map +1 -0
- package/dist/src/core/apiKeyCredentialStorage.test.d.ts +6 -0
- package/dist/src/core/apiKeyCredentialStorage.test.js +71 -0
- package/dist/src/core/apiKeyCredentialStorage.test.js.map +1 -0
- package/dist/src/core/baseLlmClient.d.ts +50 -0
- package/dist/src/core/baseLlmClient.js +185 -0
- package/dist/src/core/baseLlmClient.js.map +1 -0
- package/dist/src/core/baseLlmClient.test.d.ts +6 -0
- package/dist/src/core/baseLlmClient.test.js +311 -0
- package/dist/src/core/baseLlmClient.test.js.map +1 -0
- package/dist/src/core/client.d.ts +30 -42
- package/dist/src/core/client.js +178 -477
- package/dist/src/core/client.js.map +1 -1
- package/dist/src/core/client.test.js +739 -617
- package/dist/src/core/client.test.js.map +1 -1
- package/dist/src/core/contentGenerator.d.ts +7 -6
- package/dist/src/core/contentGenerator.js +59 -45
- package/dist/src/core/contentGenerator.js.map +1 -1
- package/dist/src/core/contentGenerator.test.js +50 -4
- package/dist/src/core/contentGenerator.test.js.map +1 -1
- package/dist/src/core/coreToolScheduler.d.ts +28 -11
- package/dist/src/core/coreToolScheduler.js +493 -161
- package/dist/src/core/coreToolScheduler.js.map +1 -1
- package/dist/src/core/coreToolScheduler.test.js +995 -163
- package/dist/src/core/coreToolScheduler.test.js.map +1 -1
- package/dist/src/core/fakeContentGenerator.d.ts +33 -0
- package/dist/src/core/fakeContentGenerator.js +58 -0
- package/dist/src/core/fakeContentGenerator.js.map +1 -0
- package/dist/src/core/fakeContentGenerator.test.d.ts +6 -0
- package/dist/src/core/fakeContentGenerator.test.js +127 -0
- package/dist/src/core/fakeContentGenerator.test.js.map +1 -0
- package/dist/src/core/geminiChat.d.ts +61 -55
- package/dist/src/core/geminiChat.js +388 -366
- package/dist/src/core/geminiChat.js.map +1 -1
- package/dist/src/core/geminiChat.test.js +1600 -355
- package/dist/src/core/geminiChat.test.js.map +1 -1
- package/dist/src/core/geminiRequest.js +1 -0
- package/dist/src/core/geminiRequest.js.map +1 -1
- package/dist/src/core/logger.d.ts +11 -4
- package/dist/src/core/logger.js +39 -30
- package/dist/src/core/logger.js.map +1 -1
- package/dist/src/core/logger.test.js +62 -45
- package/dist/src/core/logger.test.js.map +1 -1
- package/dist/src/core/loggingContentGenerator.d.ts +4 -3
- package/dist/src/core/loggingContentGenerator.js +116 -37
- package/dist/src/core/loggingContentGenerator.js.map +1 -1
- package/dist/src/core/loggingContentGenerator.test.d.ts +6 -0
- package/dist/src/core/loggingContentGenerator.test.js +180 -0
- package/dist/src/core/loggingContentGenerator.test.js.map +1 -0
- package/dist/src/core/nonInteractiveToolExecutor.d.ts +4 -5
- package/dist/src/core/nonInteractiveToolExecutor.js +17 -120
- package/dist/src/core/nonInteractiveToolExecutor.js.map +1 -1
- package/dist/src/core/nonInteractiveToolExecutor.test.js +168 -84
- package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
- package/dist/src/core/openaiContentGenerator.d.ts +4 -3
- package/dist/src/core/openaiContentGenerator.js +49 -19
- package/dist/src/core/openaiContentGenerator.js.map +1 -1
- package/dist/src/core/openaiContentGenerator.test.js +1 -0
- package/dist/src/core/openaiContentGenerator.test.js.map +1 -1
- package/dist/src/core/prompts.d.ts +7 -1
- package/dist/src/core/prompts.js +198 -195
- package/dist/src/core/prompts.js.map +1 -1
- package/dist/src/core/prompts.test.js +172 -104
- package/dist/src/core/prompts.test.js.map +1 -1
- package/dist/src/core/recordingContentGenerator.d.ts +18 -0
- package/dist/src/core/recordingContentGenerator.js +77 -0
- package/dist/src/core/recordingContentGenerator.js.map +1 -0
- package/dist/src/core/recordingContentGenerator.test.d.ts +6 -0
- package/dist/src/core/recordingContentGenerator.test.js +101 -0
- package/dist/src/core/recordingContentGenerator.test.js.map +1 -0
- package/dist/src/core/subagent.d.ts +24 -18
- package/dist/src/core/subagent.js +125 -90
- package/dist/src/core/subagent.js.map +1 -1
- package/dist/src/core/subagent.test.js +59 -44
- package/dist/src/core/subagent.test.js.map +1 -1
- package/dist/src/core/tokenLimits.test.d.ts +6 -0
- package/dist/src/core/tokenLimits.test.js +26 -0
- package/dist/src/core/tokenLimits.test.js.map +1 -0
- package/dist/src/core/turn.d.ts +57 -13
- package/dist/src/core/turn.js +79 -35
- package/dist/src/core/turn.js.map +1 -1
- package/dist/src/core/turn.test.js +373 -120
- package/dist/src/core/turn.test.js.map +1 -1
- package/dist/src/fallback/handler.d.ts +7 -0
- package/dist/src/fallback/handler.js +181 -0
- package/dist/src/fallback/handler.js.map +1 -0
- package/dist/src/fallback/handler.test.d.ts +6 -0
- package/dist/src/fallback/handler.test.js +245 -0
- package/dist/src/fallback/handler.test.js.map +1 -0
- package/dist/src/fallback/types.d.ts +14 -0
- package/dist/src/fallback/types.js +7 -0
- package/dist/src/fallback/types.js.map +1 -0
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/generated/git-commit.js.map +1 -1
- package/dist/src/hooks/hookAggregator.d.ts +68 -0
- package/dist/src/hooks/hookAggregator.js +262 -0
- package/dist/src/hooks/hookAggregator.js.map +1 -0
- package/dist/src/hooks/hookAggregator.test.d.ts +6 -0
- package/dist/src/hooks/hookAggregator.test.js +387 -0
- package/dist/src/hooks/hookAggregator.test.js.map +1 -0
- package/dist/src/hooks/hookPlanner.d.ts +46 -0
- package/dist/src/hooks/hookPlanner.js +108 -0
- package/dist/src/hooks/hookPlanner.js.map +1 -0
- package/dist/src/hooks/hookPlanner.test.d.ts +6 -0
- package/dist/src/hooks/hookPlanner.test.js +255 -0
- package/dist/src/hooks/hookPlanner.test.js.map +1 -0
- package/dist/src/hooks/hookRegistry.d.ts +87 -0
- package/dist/src/hooks/hookRegistry.js +198 -0
- package/dist/src/hooks/hookRegistry.js.map +1 -0
- package/dist/src/hooks/hookRegistry.test.d.ts +6 -0
- package/dist/src/hooks/hookRegistry.test.js +341 -0
- package/dist/src/hooks/hookRegistry.test.js.map +1 -0
- package/dist/src/hooks/hookRunner.d.ts +42 -0
- package/dist/src/hooks/hookRunner.js +272 -0
- package/dist/src/hooks/hookRunner.js.map +1 -0
- package/dist/src/hooks/hookRunner.test.d.ts +6 -0
- package/dist/src/hooks/hookRunner.test.js +468 -0
- package/dist/src/hooks/hookRunner.test.js.map +1 -0
- package/dist/src/hooks/hookTranslator.d.ts +113 -0
- package/dist/src/hooks/hookTranslator.js +232 -0
- package/dist/src/hooks/hookTranslator.js.map +1 -0
- package/dist/src/hooks/hookTranslator.test.d.ts +6 -0
- package/dist/src/hooks/hookTranslator.test.js +192 -0
- package/dist/src/hooks/hookTranslator.test.js.map +1 -0
- package/dist/src/hooks/types.d.ts +384 -0
- package/dist/src/hooks/types.js +284 -0
- package/dist/src/hooks/types.js.map +1 -0
- package/dist/src/hooks/types.test.d.ts +6 -0
- package/dist/src/hooks/types.test.js +313 -0
- package/dist/src/hooks/types.test.js.map +1 -0
- package/dist/src/ide/constants.d.ts +3 -0
- package/dist/src/ide/constants.js +3 -0
- package/dist/src/ide/constants.js.map +1 -1
- package/dist/src/ide/detect-ide.d.ts +52 -12
- package/dist/src/ide/detect-ide.js +51 -65
- package/dist/src/ide/detect-ide.js.map +1 -1
- package/dist/src/ide/detect-ide.test.js +95 -52
- package/dist/src/ide/detect-ide.test.js.map +1 -1
- package/dist/src/ide/ide-client.d.ts +72 -24
- package/dist/src/ide/ide-client.js +380 -84
- package/dist/src/ide/ide-client.js.map +1 -1
- package/dist/src/ide/ide-client.test.js +539 -35
- package/dist/src/ide/ide-client.test.js.map +1 -1
- package/dist/src/ide/ide-installer.d.ts +2 -2
- package/dist/src/ide/ide-installer.js +93 -35
- package/dist/src/ide/ide-installer.js.map +1 -1
- package/dist/src/ide/ide-installer.test.js +157 -26
- package/dist/src/ide/ide-installer.test.js.map +1 -1
- package/dist/src/ide/ideContext.d.ts +35 -365
- package/dist/src/ide/ideContext.js +60 -106
- package/dist/src/ide/ideContext.js.map +1 -1
- package/dist/src/ide/ideContext.test.js +152 -24
- package/dist/src/ide/ideContext.test.js.map +1 -1
- package/dist/src/ide/process-utils.d.ts +7 -5
- package/dist/src/ide/process-utils.js +108 -67
- package/dist/src/ide/process-utils.js.map +1 -1
- package/dist/src/ide/process-utils.test.d.ts +6 -0
- package/dist/src/ide/process-utils.test.js +151 -0
- package/dist/src/ide/process-utils.test.js.map +1 -0
- package/dist/src/ide/types.d.ts +486 -0
- package/dist/src/ide/types.js +138 -0
- package/dist/src/ide/types.js.map +1 -0
- package/dist/src/index.d.ts +41 -2
- package/dist/src/index.js +44 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/google-auth-provider.d.ts +5 -3
- package/dist/src/mcp/google-auth-provider.js +21 -3
- package/dist/src/mcp/google-auth-provider.js.map +1 -1
- package/dist/src/mcp/google-auth-provider.test.js +42 -9
- package/dist/src/mcp/google-auth-provider.test.js.map +1 -1
- package/dist/src/mcp/oauth-provider.d.ts +25 -18
- package/dist/src/mcp/oauth-provider.js +194 -97
- package/dist/src/mcp/oauth-provider.js.map +1 -1
- package/dist/src/mcp/oauth-provider.test.js +581 -39
- package/dist/src/mcp/oauth-provider.test.js.map +1 -1
- package/dist/src/mcp/oauth-token-storage.d.ts +14 -32
- package/dist/src/mcp/oauth-token-storage.js +58 -28
- package/dist/src/mcp/oauth-token-storage.js.map +1 -1
- package/dist/src/mcp/oauth-token-storage.test.js +262 -162
- package/dist/src/mcp/oauth-token-storage.test.js.map +1 -1
- package/dist/src/mcp/oauth-utils.d.ts +16 -1
- package/dist/src/mcp/oauth-utils.js +68 -33
- package/dist/src/mcp/oauth-utils.js.map +1 -1
- package/dist/src/mcp/oauth-utils.test.js +86 -3
- package/dist/src/mcp/oauth-utils.test.js.map +1 -1
- package/dist/src/mcp/sa-impersonation-provider.d.ts +27 -0
- package/dist/src/mcp/sa-impersonation-provider.js +113 -0
- package/dist/src/mcp/sa-impersonation-provider.js.map +1 -0
- package/dist/src/mcp/sa-impersonation-provider.test.d.ts +6 -0
- package/dist/src/mcp/sa-impersonation-provider.test.js +117 -0
- package/dist/src/mcp/sa-impersonation-provider.test.js.map +1 -0
- package/dist/src/mcp/token-storage/base-token-storage.d.ts +19 -0
- package/dist/src/mcp/token-storage/base-token-storage.js +36 -0
- package/dist/src/mcp/token-storage/base-token-storage.js.map +1 -0
- package/dist/src/mcp/token-storage/base-token-storage.test.d.ts +6 -0
- package/dist/src/mcp/token-storage/base-token-storage.test.js +151 -0
- package/dist/src/mcp/token-storage/base-token-storage.test.js.map +1 -0
- package/dist/src/mcp/token-storage/file-token-storage.d.ts +24 -0
- package/dist/src/mcp/token-storage/file-token-storage.js +145 -0
- package/dist/src/mcp/token-storage/file-token-storage.js.map +1 -0
- package/dist/src/mcp/token-storage/file-token-storage.test.d.ts +6 -0
- package/dist/src/mcp/token-storage/file-token-storage.test.js +238 -0
- package/dist/src/mcp/token-storage/file-token-storage.test.js.map +1 -0
- package/dist/src/mcp/token-storage/hybrid-token-storage.d.ts +23 -0
- package/dist/src/mcp/token-storage/hybrid-token-storage.js +78 -0
- package/dist/src/mcp/token-storage/hybrid-token-storage.js.map +1 -0
- package/dist/src/mcp/token-storage/hybrid-token-storage.test.d.ts +6 -0
- package/dist/src/mcp/token-storage/hybrid-token-storage.test.js +193 -0
- package/dist/src/mcp/token-storage/hybrid-token-storage.test.js.map +1 -0
- package/dist/src/mcp/token-storage/index.d.ts +11 -0
- package/dist/src/mcp/token-storage/index.js +12 -0
- package/dist/src/mcp/token-storage/index.js.map +1 -0
- package/dist/src/mcp/token-storage/keychain-token-storage.d.ts +35 -0
- package/dist/src/mcp/token-storage/keychain-token-storage.js +246 -0
- package/dist/src/mcp/token-storage/keychain-token-storage.js.map +1 -0
- package/dist/src/mcp/token-storage/keychain-token-storage.test.d.ts +6 -0
- package/dist/src/mcp/token-storage/keychain-token-storage.test.js +305 -0
- package/dist/src/mcp/token-storage/keychain-token-storage.test.js.map +1 -0
- package/dist/src/mcp/token-storage/types.d.ts +44 -0
- package/dist/src/mcp/token-storage/types.js +11 -0
- package/dist/src/mcp/token-storage/types.js.map +1 -0
- package/dist/src/output/json-formatter.d.ts +11 -0
- package/dist/src/output/json-formatter.js +30 -0
- package/dist/src/output/json-formatter.js.map +1 -0
- package/dist/src/output/json-formatter.test.d.ts +6 -0
- package/dist/src/output/json-formatter.test.js +266 -0
- package/dist/src/output/json-formatter.test.js.map +1 -0
- package/dist/src/output/stream-json-formatter.d.ts +32 -0
- package/dist/src/output/stream-json-formatter.js +52 -0
- package/dist/src/output/stream-json-formatter.js.map +1 -0
- package/dist/src/output/stream-json-formatter.test.d.ts +6 -0
- package/dist/src/output/stream-json-formatter.test.js +479 -0
- package/dist/src/output/stream-json-formatter.test.js.map +1 -0
- package/dist/src/output/types.d.ts +82 -0
- package/dist/src/output/types.js +22 -0
- package/dist/src/output/types.js.map +1 -0
- package/dist/src/policy/config.d.ts +31 -0
- package/dist/src/policy/config.js +199 -0
- package/dist/src/policy/config.js.map +1 -0
- package/dist/src/policy/config.test.d.ts +6 -0
- package/dist/src/policy/config.test.js +538 -0
- package/dist/src/policy/config.test.js.map +1 -0
- package/dist/src/policy/index.d.ts +9 -0
- package/dist/src/policy/index.js +10 -0
- package/dist/src/policy/index.js.map +1 -0
- package/dist/src/policy/policies/discovered.toml +8 -0
- package/dist/src/policy/policies/read-only.toml +56 -0
- package/dist/src/policy/policies/write.toml +73 -0
- package/dist/src/policy/policies/yolo.toml +31 -0
- package/dist/src/policy/policy-engine.d.ts +39 -0
- package/dist/src/policy/policy-engine.js +158 -0
- package/dist/src/policy/policy-engine.js.map +1 -0
- package/dist/src/policy/policy-engine.test.d.ts +6 -0
- package/dist/src/policy/policy-engine.test.js +899 -0
- package/dist/src/policy/policy-engine.test.js.map +1 -0
- package/dist/src/policy/stable-stringify.d.ts +58 -0
- package/dist/src/policy/stable-stringify.js +122 -0
- package/dist/src/policy/stable-stringify.js.map +1 -0
- package/dist/src/policy/toml-loader.d.ts +47 -0
- package/dist/src/policy/toml-loader.js +411 -0
- package/dist/src/policy/toml-loader.js.map +1 -0
- package/dist/src/policy/toml-loader.test.d.ts +6 -0
- package/dist/src/policy/toml-loader.test.js +376 -0
- package/dist/src/policy/toml-loader.test.js.map +1 -0
- package/dist/src/policy/types.d.ts +130 -0
- package/dist/src/policy/types.js +22 -0
- package/dist/src/policy/types.js.map +1 -0
- package/dist/src/prompts/mcp-prompts.d.ts +2 -2
- package/dist/src/prompts/mcp-prompts.test.d.ts +6 -0
- package/dist/src/prompts/mcp-prompts.test.js +39 -0
- package/dist/src/prompts/mcp-prompts.test.js.map +1 -0
- package/dist/src/prompts/prompt-registry.d.ts +1 -1
- package/dist/src/prompts/prompt-registry.js +2 -1
- package/dist/src/prompts/prompt-registry.js.map +1 -1
- package/dist/src/prompts/prompt-registry.test.d.ts +6 -0
- package/dist/src/prompts/prompt-registry.test.js +96 -0
- package/dist/src/prompts/prompt-registry.test.js.map +1 -0
- package/dist/src/routing/modelRouterService.d.ts +23 -0
- package/dist/src/routing/modelRouterService.js +85 -0
- package/dist/src/routing/modelRouterService.js.map +1 -0
- package/dist/src/routing/modelRouterService.test.d.ts +6 -0
- package/dist/src/routing/modelRouterService.test.js +160 -0
- package/dist/src/routing/modelRouterService.test.js.map +1 -0
- package/dist/src/routing/routingStrategy.d.ts +62 -0
- package/dist/src/routing/routingStrategy.js +7 -0
- package/dist/src/routing/routingStrategy.js.map +1 -0
- package/dist/src/routing/strategies/classifierStrategy.d.ts +12 -0
- package/dist/src/routing/strategies/classifierStrategy.js +166 -0
- package/dist/src/routing/strategies/classifierStrategy.js.map +1 -0
- package/dist/src/routing/strategies/classifierStrategy.test.d.ts +6 -0
- package/dist/src/routing/strategies/classifierStrategy.test.js +196 -0
- package/dist/src/routing/strategies/classifierStrategy.test.js.map +1 -0
- package/dist/src/routing/strategies/compositeStrategy.d.ts +26 -0
- package/dist/src/routing/strategies/compositeStrategy.js +68 -0
- package/dist/src/routing/strategies/compositeStrategy.js.map +1 -0
- package/dist/src/routing/strategies/compositeStrategy.test.d.ts +6 -0
- package/dist/src/routing/strategies/compositeStrategy.test.js +123 -0
- package/dist/src/routing/strategies/compositeStrategy.test.js.map +1 -0
- package/dist/src/routing/strategies/defaultStrategy.d.ts +12 -0
- package/dist/src/routing/strategies/defaultStrategy.js +20 -0
- package/dist/src/routing/strategies/defaultStrategy.js.map +1 -0
- package/dist/src/routing/strategies/defaultStrategy.test.d.ts +6 -0
- package/dist/src/routing/strategies/defaultStrategy.test.js +26 -0
- package/dist/src/routing/strategies/defaultStrategy.test.js.map +1 -0
- package/dist/src/routing/strategies/fallbackStrategy.d.ts +12 -0
- package/dist/src/routing/strategies/fallbackStrategy.js +25 -0
- package/dist/src/routing/strategies/fallbackStrategy.js.map +1 -0
- package/dist/src/routing/strategies/fallbackStrategy.test.d.ts +6 -0
- package/dist/src/routing/strategies/fallbackStrategy.test.js +59 -0
- package/dist/src/routing/strategies/fallbackStrategy.test.js.map +1 -0
- package/dist/src/routing/strategies/overrideStrategy.d.ts +15 -0
- package/dist/src/routing/strategies/overrideStrategy.js +28 -0
- package/dist/src/routing/strategies/overrideStrategy.js.map +1 -0
- package/dist/src/routing/strategies/overrideStrategy.test.d.ts +6 -0
- package/dist/src/routing/strategies/overrideStrategy.test.js +45 -0
- package/dist/src/routing/strategies/overrideStrategy.test.js.map +1 -0
- package/dist/src/safety/built-in.d.ts +21 -0
- package/dist/src/safety/built-in.js +106 -0
- package/dist/src/safety/built-in.js.map +1 -0
- package/dist/src/safety/built-in.test.d.ts +6 -0
- package/dist/src/safety/built-in.test.js +199 -0
- package/dist/src/safety/built-in.test.js.map +1 -0
- package/dist/src/safety/checker-runner.d.ts +48 -0
- package/dist/src/safety/checker-runner.js +208 -0
- package/dist/src/safety/checker-runner.js.map +1 -0
- package/dist/src/safety/checker-runner.test.d.ts +6 -0
- package/dist/src/safety/checker-runner.test.js +238 -0
- package/dist/src/safety/checker-runner.test.js.map +1 -0
- package/dist/src/safety/context-builder.d.ts +23 -0
- package/dist/src/safety/context-builder.js +47 -0
- package/dist/src/safety/context-builder.js.map +1 -0
- package/dist/src/safety/context-builder.test.d.ts +6 -0
- package/dist/src/safety/context-builder.test.js +49 -0
- package/dist/src/safety/context-builder.test.js.map +1 -0
- package/dist/src/safety/protocol.d.ts +88 -0
- package/dist/src/safety/protocol.js +15 -0
- package/dist/src/safety/protocol.js.map +1 -0
- package/dist/src/safety/registry.d.ts +26 -0
- package/dist/src/safety/registry.js +65 -0
- package/dist/src/safety/registry.js.map +1 -0
- package/dist/src/safety/registry.test.d.ts +6 -0
- package/dist/src/safety/registry.test.js +31 -0
- package/dist/src/safety/registry.test.js.map +1 -0
- package/dist/src/services/chatCompressionService.d.ts +32 -0
- package/dist/src/services/chatCompressionService.js +162 -0
- package/dist/src/services/chatCompressionService.js.map +1 -0
- package/dist/src/services/chatCompressionService.test.d.ts +6 -0
- package/dist/src/services/chatCompressionService.test.js +210 -0
- package/dist/src/services/chatCompressionService.test.js.map +1 -0
- package/dist/src/services/chatRecordingService.d.ts +10 -15
- package/dist/src/services/chatRecordingService.js +43 -29
- package/dist/src/services/chatRecordingService.js.map +1 -1
- package/dist/src/services/chatRecordingService.test.js +69 -25
- package/dist/src/services/chatRecordingService.test.js.map +1 -1
- package/dist/src/services/fileDiscoveryService.d.ts +8 -10
- package/dist/src/services/fileDiscoveryService.js +31 -53
- package/dist/src/services/fileDiscoveryService.js.map +1 -1
- package/dist/src/services/fileDiscoveryService.test.js +94 -14
- package/dist/src/services/fileDiscoveryService.test.js.map +1 -1
- package/dist/src/services/fileSystemService.d.ts +9 -0
- package/dist/src/services/fileSystemService.js +12 -1
- package/dist/src/services/fileSystemService.js.map +1 -1
- package/dist/src/services/fileSystemService.test.js +1 -1
- package/dist/src/services/fileSystemService.test.js.map +1 -1
- package/dist/src/services/gitService.d.ts +3 -1
- package/dist/src/services/gitService.js +30 -24
- package/dist/src/services/gitService.js.map +1 -1
- package/dist/src/services/gitService.test.js +30 -37
- package/dist/src/services/gitService.test.js.map +1 -1
- package/dist/src/services/loopDetectionService.d.ts +12 -3
- package/dist/src/services/loopDetectionService.js +148 -55
- package/dist/src/services/loopDetectionService.js.map +1 -1
- package/dist/src/services/loopDetectionService.test.js +280 -21
- package/dist/src/services/loopDetectionService.test.js.map +1 -1
- package/dist/src/services/modelConfig.golden.test.d.ts +6 -0
- package/dist/src/services/modelConfig.golden.test.js +42 -0
- package/dist/src/services/modelConfig.golden.test.js.map +1 -0
- package/dist/src/services/modelConfig.integration.test.d.ts +6 -0
- package/dist/src/services/modelConfig.integration.test.js +247 -0
- package/dist/src/services/modelConfig.integration.test.js.map +1 -0
- package/dist/src/services/modelConfigService.d.ts +48 -0
- package/dist/src/services/modelConfigService.js +151 -0
- package/dist/src/services/modelConfigService.js.map +1 -0
- package/dist/src/services/modelConfigService.test.d.ts +6 -0
- package/dist/src/services/modelConfigService.test.js +531 -0
- package/dist/src/services/modelConfigService.test.js.map +1 -0
- package/dist/src/services/shellExecutionService.d.ts +37 -2
- package/dist/src/services/shellExecutionService.js +361 -67
- package/dist/src/services/shellExecutionService.js.map +1 -1
- package/dist/src/services/shellExecutionService.test.js +333 -71
- package/dist/src/services/shellExecutionService.test.js.map +1 -1
- package/dist/src/services/test-data/resolved-aliases.golden.json +202 -0
- package/dist/src/telemetry/activity-detector.d.ts +41 -0
- package/dist/src/telemetry/activity-detector.js +61 -0
- package/dist/src/telemetry/activity-detector.js.map +1 -0
- package/dist/src/telemetry/activity-detector.test.d.ts +6 -0
- package/dist/src/telemetry/activity-detector.test.js +136 -0
- package/dist/src/telemetry/activity-detector.test.js.map +1 -0
- package/dist/src/telemetry/activity-monitor.d.ts +116 -0
- package/dist/src/telemetry/activity-monitor.js +209 -0
- package/dist/src/telemetry/activity-monitor.js.map +1 -0
- package/dist/src/telemetry/activity-monitor.test.d.ts +6 -0
- package/dist/src/telemetry/activity-monitor.test.js +251 -0
- package/dist/src/telemetry/activity-monitor.test.js.map +1 -0
- package/dist/src/telemetry/activity-types.d.ts +19 -0
- package/dist/src/telemetry/activity-types.js +21 -0
- package/dist/src/telemetry/activity-types.js.map +1 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +53 -5
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +581 -56
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.d.ts +2 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +467 -31
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +75 -4
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +182 -6
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
- package/dist/src/telemetry/config.d.ts +31 -0
- package/dist/src/telemetry/config.js +76 -0
- package/dist/src/telemetry/config.js.map +1 -0
- package/dist/src/telemetry/config.test.d.ts +6 -0
- package/dist/src/telemetry/config.test.js +124 -0
- package/dist/src/telemetry/config.test.js.map +1 -0
- package/dist/src/telemetry/constants.d.ts +0 -18
- package/dist/src/telemetry/constants.js +0 -18
- package/dist/src/telemetry/constants.js.map +1 -1
- package/dist/src/telemetry/file-exporters.d.ts +5 -4
- package/dist/src/telemetry/file-exporters.js +1 -1
- package/dist/src/telemetry/file-exporters.js.map +1 -1
- package/dist/src/telemetry/gcp-exporters.d.ts +34 -0
- package/dist/src/telemetry/gcp-exporters.js +116 -0
- package/dist/src/telemetry/gcp-exporters.js.map +1 -0
- package/dist/src/telemetry/gcp-exporters.test.d.ts +6 -0
- package/dist/src/telemetry/gcp-exporters.test.js +318 -0
- package/dist/src/telemetry/gcp-exporters.test.js.map +1 -0
- package/dist/src/telemetry/high-water-mark-tracker.d.ts +43 -0
- package/dist/src/telemetry/high-water-mark-tracker.js +88 -0
- package/dist/src/telemetry/high-water-mark-tracker.js.map +1 -0
- package/dist/src/telemetry/high-water-mark-tracker.test.d.ts +6 -0
- package/dist/src/telemetry/high-water-mark-tracker.test.js +152 -0
- package/dist/src/telemetry/high-water-mark-tracker.test.js.map +1 -0
- package/dist/src/telemetry/index.d.ts +16 -2
- package/dist/src/telemetry/index.js +25 -2
- package/dist/src/telemetry/index.js.map +1 -1
- package/dist/src/telemetry/loggers.d.ts +25 -3
- package/dist/src/telemetry/loggers.js +333 -154
- package/dist/src/telemetry/loggers.js.map +1 -1
- package/dist/src/telemetry/loggers.test.circular.js +3 -4
- package/dist/src/telemetry/loggers.test.circular.js.map +1 -1
- package/dist/src/telemetry/loggers.test.js +868 -63
- package/dist/src/telemetry/loggers.test.js.map +1 -1
- package/dist/src/telemetry/memory-monitor.d.ts +149 -0
- package/dist/src/telemetry/memory-monitor.js +335 -0
- package/dist/src/telemetry/memory-monitor.js.map +1 -0
- package/dist/src/telemetry/memory-monitor.test.d.ts +6 -0
- package/dist/src/telemetry/memory-monitor.test.js +472 -0
- package/dist/src/telemetry/memory-monitor.test.js.map +1 -0
- package/dist/src/telemetry/metrics.d.ts +498 -11
- package/dist/src/telemetry/metrics.js +729 -84
- package/dist/src/telemetry/metrics.js.map +1 -1
- package/dist/src/telemetry/metrics.test.js +964 -101
- package/dist/src/telemetry/metrics.test.js.map +1 -1
- package/dist/src/telemetry/rate-limiter.d.ts +48 -0
- package/dist/src/telemetry/rate-limiter.js +100 -0
- package/dist/src/telemetry/rate-limiter.js.map +1 -0
- package/dist/src/telemetry/rate-limiter.test.d.ts +6 -0
- package/dist/src/telemetry/rate-limiter.test.js +207 -0
- package/dist/src/telemetry/rate-limiter.test.js.map +1 -0
- package/dist/src/telemetry/sdk.d.ts +1 -1
- package/dist/src/telemetry/sdk.js +23 -4
- package/dist/src/telemetry/sdk.js.map +1 -1
- package/dist/src/telemetry/sdk.test.js +108 -0
- package/dist/src/telemetry/sdk.test.js.map +1 -1
- package/dist/src/telemetry/semantic.d.ts +82 -0
- package/dist/src/telemetry/semantic.js +269 -0
- package/dist/src/telemetry/semantic.js.map +1 -0
- package/dist/src/telemetry/semantic.test.d.ts +6 -0
- package/dist/src/telemetry/semantic.test.js +387 -0
- package/dist/src/telemetry/semantic.test.js.map +1 -0
- package/dist/src/telemetry/telemetry-utils.d.ts +6 -0
- package/dist/src/telemetry/telemetry-utils.js +14 -0
- package/dist/src/telemetry/telemetry-utils.js.map +1 -0
- package/dist/src/telemetry/telemetry-utils.test.d.ts +6 -0
- package/dist/src/telemetry/telemetry-utils.test.js +41 -0
- package/dist/src/telemetry/telemetry-utils.test.js.map +1 -0
- package/dist/src/telemetry/telemetryAttributes.d.ts +8 -0
- package/dist/src/telemetry/telemetryAttributes.js +19 -0
- package/dist/src/telemetry/telemetryAttributes.js.map +1 -0
- package/dist/src/telemetry/trace.d.ts +46 -0
- package/dist/src/telemetry/trace.js +121 -0
- package/dist/src/telemetry/trace.js.map +1 -0
- package/dist/src/telemetry/types.d.ts +351 -25
- package/dist/src/telemetry/types.js +1071 -63
- package/dist/src/telemetry/types.js.map +1 -1
- package/dist/src/telemetry/uiTelemetry.d.ts +4 -4
- package/dist/src/telemetry/uiTelemetry.js +14 -15
- package/dist/src/telemetry/uiTelemetry.js.map +1 -1
- package/dist/src/telemetry/uiTelemetry.test.js +122 -96
- package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
- package/dist/src/test-utils/config.d.ts +3 -2
- package/dist/src/test-utils/config.js +1 -1
- package/dist/src/test-utils/config.js.map +1 -1
- package/dist/src/test-utils/index.d.ts +6 -0
- package/dist/src/test-utils/index.js +7 -0
- package/dist/src/test-utils/index.js.map +1 -0
- package/dist/src/test-utils/mock-tool.d.ts +66 -0
- package/dist/src/test-utils/{tools.js → mock-tool.js} +45 -29
- package/dist/src/test-utils/mock-tool.js.map +1 -0
- package/dist/src/test-utils/mockWorkspaceContext.d.ts +1 -1
- package/dist/src/tools/base-tool-invocation.test.d.ts +6 -0
- package/dist/src/tools/base-tool-invocation.test.js +85 -0
- package/dist/src/tools/base-tool-invocation.test.js.map +1 -0
- package/dist/src/tools/diffOptions.d.ts +1 -1
- package/dist/src/tools/diffOptions.js +21 -13
- package/dist/src/tools/diffOptions.js.map +1 -1
- package/dist/src/tools/diffOptions.test.js +58 -22
- package/dist/src/tools/diffOptions.test.js.map +1 -1
- package/dist/src/tools/edit.d.ts +10 -8
- package/dist/src/tools/edit.js +100 -79
- package/dist/src/tools/edit.js.map +1 -1
- package/dist/src/tools/edit.test.js +429 -163
- package/dist/src/tools/edit.test.js.map +1 -1
- package/dist/src/tools/glob.d.ts +11 -5
- package/dist/src/tools/glob.js +58 -42
- package/dist/src/tools/glob.js.map +1 -1
- package/dist/src/tools/glob.test.js +249 -166
- package/dist/src/tools/glob.test.js.map +1 -1
- package/dist/src/tools/grep.d.ts +7 -5
- package/dist/src/tools/grep.js +66 -40
- package/dist/src/tools/grep.js.map +1 -1
- package/dist/src/tools/grep.test.js +41 -15
- package/dist/src/tools/grep.test.js.map +1 -1
- package/dist/src/tools/ls.d.ts +7 -5
- package/dist/src/tools/ls.js +54 -68
- package/dist/src/tools/ls.js.map +1 -1
- package/dist/src/tools/ls.test.js +150 -293
- package/dist/src/tools/ls.test.js.map +1 -1
- package/dist/src/tools/mcp-client-manager.d.ts +52 -12
- package/dist/src/tools/mcp-client-manager.js +196 -27
- package/dist/src/tools/mcp-client-manager.js.map +1 -1
- package/dist/src/tools/mcp-client-manager.test.js +139 -13
- package/dist/src/tools/mcp-client-manager.test.js.map +1 -1
- package/dist/src/tools/mcp-client.d.ts +28 -24
- package/dist/src/tools/mcp-client.js +338 -351
- package/dist/src/tools/mcp-client.js.map +1 -1
- package/dist/src/tools/mcp-client.test.js +380 -193
- package/dist/src/tools/mcp-client.test.js.map +1 -1
- package/dist/src/tools/mcp-tool.d.ts +11 -5
- package/dist/src/tools/mcp-tool.js +66 -17
- package/dist/src/tools/mcp-tool.js.map +1 -1
- package/dist/src/tools/mcp-tool.test.js +278 -211
- package/dist/src/tools/mcp-tool.test.js.map +1 -1
- package/dist/src/tools/memoryTool.d.ts +10 -7
- package/dist/src/tools/memoryTool.js +28 -49
- package/dist/src/tools/memoryTool.js.map +1 -1
- package/dist/src/tools/memoryTool.test.js +26 -13
- package/dist/src/tools/memoryTool.test.js.map +1 -1
- package/dist/src/tools/message-bus-integration.test.d.ts +6 -0
- package/dist/src/tools/message-bus-integration.test.js +196 -0
- package/dist/src/tools/message-bus-integration.test.js.map +1 -0
- package/dist/src/tools/modifiable-tool.d.ts +7 -3
- package/dist/src/tools/modifiable-tool.js +41 -19
- package/dist/src/tools/modifiable-tool.js.map +1 -1
- package/dist/src/tools/modifiable-tool.test.js +70 -35
- package/dist/src/tools/modifiable-tool.test.js.map +1 -1
- package/dist/src/tools/read-file.d.ts +9 -7
- package/dist/src/tools/read-file.js +39 -60
- package/dist/src/tools/read-file.js.map +1 -1
- package/dist/src/tools/read-file.test.js +103 -36
- package/dist/src/tools/read-file.test.js.map +1 -1
- package/dist/src/tools/read-many-files.d.ts +9 -14
- package/dist/src/tools/read-many-files.js +71 -155
- package/dist/src/tools/read-many-files.js.map +1 -1
- package/dist/src/tools/read-many-files.test.js +98 -44
- package/dist/src/tools/read-many-files.test.js.map +1 -1
- package/dist/src/tools/ripGrep.d.ts +73 -0
- package/dist/src/tools/ripGrep.js +395 -0
- package/dist/src/tools/ripGrep.js.map +1 -0
- package/dist/src/tools/ripGrep.test.d.ts +6 -0
- package/dist/src/tools/ripGrep.test.js +1305 -0
- package/dist/src/tools/ripGrep.test.js.map +1 -0
- package/dist/src/tools/shell.d.ts +19 -6
- package/dist/src/tools/shell.js +92 -56
- package/dist/src/tools/shell.js.map +1 -1
- package/dist/src/tools/shell.test.js +176 -103
- package/dist/src/tools/shell.test.js.map +1 -1
- package/dist/src/tools/smart-edit.d.ts +78 -0
- package/dist/src/tools/smart-edit.js +717 -0
- package/dist/src/tools/smart-edit.js.map +1 -0
- package/dist/src/tools/smart-edit.test.d.ts +6 -0
- package/dist/src/tools/smart-edit.test.js +592 -0
- package/dist/src/tools/smart-edit.test.js.map +1 -0
- package/dist/src/tools/tool-error.d.ts +39 -1
- package/dist/src/tools/tool-error.js +54 -0
- package/dist/src/tools/tool-error.js.map +1 -1
- package/dist/src/tools/tool-names.d.ts +17 -0
- package/dist/src/tools/tool-names.js +21 -0
- package/dist/src/tools/tool-names.js.map +1 -0
- package/dist/src/tools/tool-registry.d.ts +38 -20
- package/dist/src/tools/tool-registry.js +134 -77
- package/dist/src/tools/tool-registry.js.map +1 -1
- package/dist/src/tools/tool-registry.test.js +218 -58
- package/dist/src/tools/tool-registry.test.js.map +1 -1
- package/dist/src/tools/tools.d.ts +49 -17
- package/dist/src/tools/tools.js +150 -8
- package/dist/src/tools/tools.js.map +1 -1
- package/dist/src/tools/tools.test.js +1 -2
- package/dist/src/tools/tools.test.js.map +1 -1
- package/dist/src/tools/web-fetch.d.ts +14 -5
- package/dist/src/tools/web-fetch.js +90 -44
- package/dist/src/tools/web-fetch.js.map +1 -1
- package/dist/src/tools/web-fetch.test.js +388 -20
- package/dist/src/tools/web-fetch.test.js.map +1 -1
- package/dist/src/tools/web-search.d.ts +8 -6
- package/dist/src/tools/web-search.js +40 -15
- package/dist/src/tools/web-search.js.map +1 -1
- package/dist/src/tools/web-search.test.js +75 -1
- package/dist/src/tools/web-search.test.js.map +1 -1
- package/dist/src/tools/write-file.d.ts +7 -5
- package/dist/src/tools/write-file.js +54 -54
- package/dist/src/tools/write-file.js.map +1 -1
- package/dist/src/tools/write-file.test.js +237 -146
- package/dist/src/tools/write-file.test.js.map +1 -1
- package/dist/src/tools/write-todos.d.ts +50 -0
- package/dist/src/tools/write-todos.js +193 -0
- package/dist/src/tools/write-todos.js.map +1 -0
- package/dist/src/tools/write-todos.test.d.ts +6 -0
- package/dist/src/tools/write-todos.test.js +89 -0
- package/dist/src/tools/write-todos.test.js.map +1 -0
- package/dist/src/utils/bfsFileSearch.d.ts +2 -2
- package/dist/src/utils/bfsFileSearch.js +16 -9
- package/dist/src/utils/bfsFileSearch.js.map +1 -1
- package/dist/src/utils/bfsFileSearch.test.js +3 -3
- package/dist/src/utils/bfsFileSearch.test.js.map +1 -1
- package/dist/src/utils/channel.d.ts +19 -0
- package/dist/src/utils/channel.js +49 -0
- package/dist/src/utils/channel.js.map +1 -0
- package/dist/src/utils/channel.test.d.ts +6 -0
- package/dist/src/utils/channel.test.js +170 -0
- package/dist/src/utils/channel.test.js.map +1 -0
- package/dist/src/utils/debugLogger.d.ts +25 -0
- package/dist/src/utils/debugLogger.js +33 -0
- package/dist/src/utils/debugLogger.js.map +1 -0
- package/dist/src/utils/debugLogger.test.d.ts +6 -0
- package/dist/src/utils/debugLogger.test.js +69 -0
- package/dist/src/utils/debugLogger.test.js.map +1 -0
- package/dist/src/utils/delay.d.ts +16 -0
- package/dist/src/utils/delay.js +43 -0
- package/dist/src/utils/delay.js.map +1 -0
- package/dist/src/utils/delay.test.d.ts +6 -0
- package/dist/src/utils/delay.test.js +88 -0
- package/dist/src/utils/delay.test.js.map +1 -0
- package/dist/src/utils/editCorrector.d.ts +9 -8
- package/dist/src/utils/editCorrector.js +62 -34
- package/dist/src/utils/editCorrector.js.map +1 -1
- package/dist/src/utils/editCorrector.test.js +52 -87
- package/dist/src/utils/editCorrector.test.js.map +1 -1
- package/dist/src/utils/editor.d.ts +4 -2
- package/dist/src/utils/editor.js +56 -55
- package/dist/src/utils/editor.js.map +1 -1
- package/dist/src/utils/editor.test.js +47 -88
- package/dist/src/utils/editor.test.js.map +1 -1
- package/dist/src/utils/environmentContext.d.ts +3 -2
- package/dist/src/utils/environmentContext.js +20 -33
- package/dist/src/utils/environmentContext.js.map +1 -1
- package/dist/src/utils/environmentContext.test.js +6 -34
- package/dist/src/utils/environmentContext.test.js.map +1 -1
- package/dist/src/utils/errorParsing.d.ts +1 -1
- package/dist/src/utils/errorParsing.js +5 -33
- package/dist/src/utils/errorParsing.js.map +1 -1
- package/dist/src/utils/errorParsing.test.js +0 -88
- package/dist/src/utils/errorParsing.test.js.map +1 -1
- package/dist/src/utils/errorReporting.d.ts +1 -1
- package/dist/src/utils/errors.d.ts +28 -0
- package/dist/src/utils/errors.js +48 -0
- package/dist/src/utils/errors.js.map +1 -1
- package/dist/src/utils/events.d.ts +121 -0
- package/dist/src/utils/events.js +84 -0
- package/dist/src/utils/events.js.map +1 -0
- package/dist/src/utils/events.test.d.ts +6 -0
- package/dist/src/utils/events.test.js +212 -0
- package/dist/src/utils/events.test.js.map +1 -0
- package/dist/src/utils/extensionLoader.d.ts +86 -0
- package/dist/src/utils/extensionLoader.js +208 -0
- package/dist/src/utils/extensionLoader.js.map +1 -0
- package/dist/src/utils/extensionLoader.test.d.ts +6 -0
- package/dist/src/utils/extensionLoader.test.js +154 -0
- package/dist/src/utils/extensionLoader.test.js.map +1 -0
- package/dist/src/utils/fetch.d.ts +1 -0
- package/dist/src/utils/fetch.js +5 -1
- package/dist/src/utils/fetch.js.map +1 -1
- package/dist/src/utils/fileUtils.d.ts +28 -12
- package/dist/src/utils/fileUtils.js +204 -81
- package/dist/src/utils/fileUtils.js.map +1 -1
- package/dist/src/utils/fileUtils.test.js +426 -82
- package/dist/src/utils/fileUtils.test.js.map +1 -1
- package/dist/src/utils/filesearch/crawler.d.ts +1 -1
- package/dist/src/utils/filesearch/crawler.test.js +2 -2
- package/dist/src/utils/filesearch/crawler.test.js.map +1 -1
- package/dist/src/utils/filesearch/fileSearch.d.ts +1 -0
- package/dist/src/utils/filesearch/fileSearch.js +14 -9
- package/dist/src/utils/filesearch/fileSearch.js.map +1 -1
- package/dist/src/utils/filesearch/fileSearch.test.js +90 -0
- package/dist/src/utils/filesearch/fileSearch.test.js.map +1 -1
- package/dist/src/utils/flashFallback.test.d.ts +6 -0
- package/dist/src/utils/flashFallback.test.js +103 -0
- package/dist/src/utils/flashFallback.test.js.map +1 -0
- package/dist/src/utils/formatters.d.ts +1 -0
- package/dist/src/utils/formatters.js +2 -1
- package/dist/src/utils/formatters.js.map +1 -1
- package/dist/src/utils/formatters.test.d.ts +6 -0
- package/dist/src/utils/formatters.test.js +26 -0
- package/dist/src/utils/formatters.test.js.map +1 -0
- package/dist/src/utils/geminiIgnoreParser.d.ts +18 -0
- package/dist/src/utils/geminiIgnoreParser.js +61 -0
- package/dist/src/utils/geminiIgnoreParser.js.map +1 -0
- package/dist/src/utils/geminiIgnoreParser.test.d.ts +6 -0
- package/dist/src/utils/geminiIgnoreParser.test.js +50 -0
- package/dist/src/utils/geminiIgnoreParser.test.js.map +1 -0
- package/dist/src/utils/generateContentResponseUtilities.d.ts +1 -2
- package/dist/src/utils/generateContentResponseUtilities.js +1 -13
- package/dist/src/utils/generateContentResponseUtilities.js.map +1 -1
- package/dist/src/utils/generateContentResponseUtilities.test.js +2 -40
- package/dist/src/utils/generateContentResponseUtilities.test.js.map +1 -1
- package/dist/src/utils/getFolderStructure.d.ts +2 -2
- package/dist/src/utils/getFolderStructure.js +12 -20
- package/dist/src/utils/getFolderStructure.js.map +1 -1
- package/dist/src/utils/getFolderStructure.test.js +11 -10
- package/dist/src/utils/getFolderStructure.test.js.map +1 -1
- package/dist/src/utils/gitIgnoreParser.d.ts +7 -8
- package/dist/src/utils/gitIgnoreParser.js +145 -36
- package/dist/src/utils/gitIgnoreParser.js.map +1 -1
- package/dist/src/utils/gitIgnoreParser.test.js +127 -38
- package/dist/src/utils/gitIgnoreParser.test.js.map +1 -1
- package/dist/src/utils/gitUtils.js +2 -2
- package/dist/src/utils/gitUtils.js.map +1 -1
- package/dist/src/utils/googleErrors.d.ts +104 -0
- package/dist/src/utils/googleErrors.js +152 -0
- package/dist/src/utils/googleErrors.js.map +1 -0
- package/dist/src/utils/googleErrors.test.d.ts +6 -0
- package/dist/src/utils/googleErrors.test.js +301 -0
- package/dist/src/utils/googleErrors.test.js.map +1 -0
- package/dist/src/utils/googleQuotaErrors.d.ts +37 -0
- package/dist/src/utils/googleQuotaErrors.js +157 -0
- package/dist/src/utils/googleQuotaErrors.js.map +1 -0
- package/dist/src/utils/googleQuotaErrors.test.d.ts +6 -0
- package/dist/src/utils/googleQuotaErrors.test.js +311 -0
- package/dist/src/utils/googleQuotaErrors.test.js.map +1 -0
- package/dist/src/utils/httpErrors.d.ts +18 -0
- package/dist/src/utils/httpErrors.js +36 -0
- package/dist/src/utils/httpErrors.js.map +1 -0
- package/dist/src/utils/ignorePatterns.d.ts +103 -0
- package/dist/src/utils/ignorePatterns.js +220 -0
- package/dist/src/utils/ignorePatterns.js.map +1 -0
- package/dist/src/utils/ignorePatterns.test.d.ts +6 -0
- package/dist/src/utils/ignorePatterns.test.js +246 -0
- package/dist/src/utils/ignorePatterns.test.js.map +1 -0
- package/dist/src/utils/installationManager.d.ts +16 -0
- package/dist/src/utils/installationManager.js +51 -0
- package/dist/src/utils/installationManager.js.map +1 -0
- package/dist/src/utils/installationManager.test.d.ts +6 -0
- package/dist/src/utils/installationManager.test.js +85 -0
- package/dist/src/utils/installationManager.test.js.map +1 -0
- package/dist/src/utils/language-detection.d.ts +6 -0
- package/dist/src/utils/language-detection.js +101 -0
- package/dist/src/utils/language-detection.js.map +1 -0
- package/dist/src/utils/llm-edit-fixer.d.ts +26 -0
- package/dist/src/utils/llm-edit-fixer.js +155 -0
- package/dist/src/utils/llm-edit-fixer.js.map +1 -0
- package/dist/src/utils/llm-edit-fixer.test.d.ts +6 -0
- package/dist/src/utils/llm-edit-fixer.test.js +223 -0
- package/dist/src/utils/llm-edit-fixer.test.js.map +1 -0
- package/dist/src/utils/memoryDiscovery.d.ts +26 -6
- package/dist/src/utils/memoryDiscovery.js +239 -40
- package/dist/src/utils/memoryDiscovery.js.map +1 -1
- package/dist/src/utils/memoryDiscovery.test.js +365 -44
- package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
- package/dist/src/utils/memoryImportProcessor.js +19 -25
- package/dist/src/utils/memoryImportProcessor.js.map +1 -1
- package/dist/src/utils/memoryImportProcessor.test.js +24 -155
- package/dist/src/utils/memoryImportProcessor.test.js.map +1 -1
- package/dist/src/utils/messageInspectors.d.ts +1 -1
- package/dist/src/utils/nextSpeakerChecker.d.ts +3 -3
- package/dist/src/utils/nextSpeakerChecker.js +10 -4
- package/dist/src/utils/nextSpeakerChecker.js.map +1 -1
- package/dist/src/utils/nextSpeakerChecker.test.js +85 -66
- package/dist/src/utils/nextSpeakerChecker.test.js.map +1 -1
- package/dist/src/utils/package.d.ts +12 -0
- package/dist/src/utils/package.js +15 -0
- package/dist/src/utils/package.js.map +1 -0
- package/dist/src/utils/partUtils.d.ts +22 -1
- package/dist/src/utils/partUtils.js +68 -0
- package/dist/src/utils/partUtils.js.map +1 -1
- package/dist/src/utils/partUtils.test.js +112 -1
- package/dist/src/utils/partUtils.test.js.map +1 -1
- package/dist/src/utils/pathCorrector.d.ts +25 -0
- package/dist/src/utils/pathCorrector.js +33 -0
- package/dist/src/utils/pathCorrector.js.map +1 -0
- package/dist/src/utils/pathCorrector.test.d.ts +6 -0
- package/dist/src/utils/pathCorrector.test.js +83 -0
- package/dist/src/utils/pathCorrector.test.js.map +1 -0
- package/dist/src/utils/pathReader.d.ts +17 -0
- package/dist/src/utils/pathReader.js +92 -0
- package/dist/src/utils/pathReader.js.map +1 -0
- package/dist/src/utils/pathReader.test.d.ts +6 -0
- package/dist/src/utils/pathReader.test.js +406 -0
- package/dist/src/utils/pathReader.test.js.map +1 -0
- package/dist/src/utils/paths.d.ts +1 -18
- package/dist/src/utils/paths.js +133 -57
- package/dist/src/utils/paths.js.map +1 -1
- package/dist/src/utils/paths.test.js +200 -68
- package/dist/src/utils/paths.test.js.map +1 -1
- package/dist/src/utils/promptIdContext.d.ts +7 -0
- package/dist/src/utils/promptIdContext.js +8 -0
- package/dist/src/utils/promptIdContext.js.map +1 -0
- package/dist/src/utils/quotaErrorDetection.d.ts +1 -3
- package/dist/src/utils/quotaErrorDetection.js +0 -46
- package/dist/src/utils/quotaErrorDetection.js.map +1 -1
- package/dist/src/utils/retry.d.ts +5 -10
- package/dist/src/utils/retry.js +114 -197
- package/dist/src/utils/retry.js.map +1 -1
- package/dist/src/utils/retry.test.js +196 -130
- package/dist/src/utils/retry.test.js.map +1 -1
- package/dist/src/utils/safeJsonStringify.d.ts +4 -4
- package/dist/src/utils/safeJsonStringify.js +31 -7
- package/dist/src/utils/safeJsonStringify.js.map +1 -1
- package/dist/src/utils/schemaValidator.js +15 -1
- package/dist/src/utils/schemaValidator.js.map +1 -1
- package/dist/src/utils/schemaValidator.test.d.ts +6 -0
- package/dist/src/utils/schemaValidator.test.js +113 -0
- package/dist/src/utils/schemaValidator.test.js.map +1 -0
- package/dist/src/utils/session.js +1 -1
- package/dist/src/utils/session.js.map +1 -1
- package/dist/src/utils/shell-utils.d.ts +21 -3
- package/dist/src/utils/shell-utils.js +427 -159
- package/dist/src/utils/shell-utils.js.map +1 -1
- package/dist/src/utils/shell-utils.test.js +250 -59
- package/dist/src/utils/shell-utils.test.js.map +1 -1
- package/dist/src/utils/stdio.d.ts +32 -0
- package/dist/src/utils/stdio.js +85 -0
- package/dist/src/utils/stdio.js.map +1 -0
- package/dist/src/utils/stdio.test.d.ts +6 -0
- package/dist/src/utils/stdio.test.js +47 -0
- package/dist/src/utils/stdio.test.js.map +1 -0
- package/dist/src/utils/summarizer.d.ts +6 -4
- package/dist/src/utils/summarizer.js +8 -9
- package/dist/src/utils/summarizer.js.map +1 -1
- package/dist/src/utils/summarizer.test.js +32 -12
- package/dist/src/utils/summarizer.test.js.map +1 -1
- package/dist/src/utils/systemEncoding.js +7 -6
- package/dist/src/utils/systemEncoding.js.map +1 -1
- package/dist/src/utils/systemEncoding.test.js +4 -3
- package/dist/src/utils/systemEncoding.test.js.map +1 -1
- package/dist/src/utils/terminal.d.ts +14 -0
- package/dist/src/utils/terminal.js +38 -0
- package/dist/src/utils/terminal.js.map +1 -0
- package/dist/src/utils/terminalSerializer.d.ts +25 -0
- package/dist/src/utils/terminalSerializer.js +432 -0
- package/dist/src/utils/terminalSerializer.js.map +1 -0
- package/dist/src/utils/terminalSerializer.test.d.ts +6 -0
- package/dist/src/utils/terminalSerializer.test.js +176 -0
- package/dist/src/utils/terminalSerializer.test.js.map +1 -0
- package/dist/src/utils/textUtils.d.ts +5 -0
- package/dist/src/utils/textUtils.js +14 -0
- package/dist/src/utils/textUtils.js.map +1 -1
- package/dist/src/utils/textUtils.test.d.ts +6 -0
- package/dist/src/utils/textUtils.test.js +59 -0
- package/dist/src/utils/textUtils.test.js.map +1 -0
- package/dist/src/utils/thoughtUtils.d.ts +21 -0
- package/dist/src/utils/thoughtUtils.js +39 -0
- package/dist/src/utils/thoughtUtils.js.map +1 -0
- package/dist/src/utils/thoughtUtils.test.d.ts +6 -0
- package/dist/src/utils/thoughtUtils.test.js +78 -0
- package/dist/src/utils/thoughtUtils.test.js.map +1 -0
- package/dist/src/utils/tool-utils.d.ts +19 -0
- package/dist/src/utils/tool-utils.js +67 -0
- package/dist/src/utils/tool-utils.js.map +1 -0
- package/dist/src/utils/tool-utils.test.d.ts +6 -0
- package/dist/src/utils/tool-utils.test.js +69 -0
- package/dist/src/utils/tool-utils.test.js.map +1 -0
- package/dist/src/utils/userAccountManager.d.ts +20 -0
- package/dist/src/utils/userAccountManager.js +115 -0
- package/dist/src/utils/userAccountManager.js.map +1 -0
- package/dist/src/utils/userAccountManager.test.d.ts +6 -0
- package/dist/src/utils/{user_account.test.js → userAccountManager.test.js} +41 -36
- package/dist/src/utils/userAccountManager.test.js.map +1 -0
- package/dist/src/utils/workspaceContext.d.ts +4 -3
- package/dist/src/utils/workspaceContext.js +23 -17
- package/dist/src/utils/workspaceContext.js.map +1 -1
- package/dist/src/utils/workspaceContext.test.js +45 -19
- package/dist/src/utils/workspaceContext.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +24 -10
- 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/test-utils/tools.d.ts +0 -44
- package/dist/src/test-utils/tools.js.map +0 -1
- package/dist/src/utils/flashFallback.integration.test.js +0 -118
- package/dist/src/utils/flashFallback.integration.test.js.map +0 -1
- package/dist/src/utils/user_account.d.ts +0 -9
- package/dist/src/utils/user_account.js +0 -109
- package/dist/src/utils/user_account.js.map +0 -1
- package/dist/src/utils/user_account.test.js.map +0 -1
- package/dist/src/utils/user_id.d.ts +0 -11
- package/dist/src/utils/user_id.js +0 -49
- package/dist/src/utils/user_id.js.map +0 -1
- package/dist/src/utils/user_id.test.js +0 -21
- package/dist/src/utils/user_id.test.js.map +0 -1
- /package/dist/src/{utils/flashFallback.integration.test.d.ts → agents/codebase-investigator.test.d.ts} +0 -0
- /package/dist/src/{utils/user_account.test.d.ts → agents/executor.test.d.ts} +0 -0
- /package/dist/src/{utils/user_id.test.d.ts → agents/invocation.test.d.ts} +0 -0
|
@@ -3,26 +3,48 @@
|
|
|
3
3
|
* Copyright 2025 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { GeminiEventType, Turn } from './turn.js';
|
|
6
|
+
import { describe, it, expect, vi, beforeEach, afterEach, } from 'vitest';
|
|
7
|
+
import { GeminiClient } from './client.js';
|
|
8
|
+
import { AuthType, } from './contentGenerator.js';
|
|
9
|
+
import {} from './geminiChat.js';
|
|
10
|
+
import { CompressionStatus, GeminiEventType, Turn, } from './turn.js';
|
|
12
11
|
import { getCoreSystemPrompt } from './prompts.js';
|
|
13
12
|
import { DEFAULT_GEMINI_FLASH_MODEL } from '../config/models.js';
|
|
14
13
|
import { FileDiscoveryService } from '../services/fileDiscoveryService.js';
|
|
15
14
|
import { setSimulate429 } from '../utils/testUtils.js';
|
|
16
15
|
import { tokenLimit } from './tokenLimits.js';
|
|
17
|
-
import {
|
|
16
|
+
import { ideContextStore } from '../ide/ideContext.js';
|
|
17
|
+
import { uiTelemetryService } from '../telemetry/uiTelemetry.js';
|
|
18
|
+
import { ChatCompressionService } from '../services/chatCompressionService.js';
|
|
18
19
|
import { ClearcutLogger } from '../telemetry/clearcut-logger/clearcut-logger.js';
|
|
20
|
+
vi.mock('../services/chatCompressionService.js');
|
|
21
|
+
// Mock fs module to prevent actual file system operations during tests
|
|
22
|
+
const mockFileSystem = new Map();
|
|
23
|
+
vi.mock('node:fs', () => {
|
|
24
|
+
const fsModule = {
|
|
25
|
+
mkdirSync: vi.fn(),
|
|
26
|
+
writeFileSync: vi.fn((path, data) => {
|
|
27
|
+
mockFileSystem.set(path, data);
|
|
28
|
+
}),
|
|
29
|
+
readFileSync: vi.fn((path) => {
|
|
30
|
+
if (mockFileSystem.has(path)) {
|
|
31
|
+
return mockFileSystem.get(path);
|
|
32
|
+
}
|
|
33
|
+
throw Object.assign(new Error('ENOENT: no such file or directory'), {
|
|
34
|
+
code: 'ENOENT',
|
|
35
|
+
});
|
|
36
|
+
}),
|
|
37
|
+
existsSync: vi.fn((path) => mockFileSystem.has(path)),
|
|
38
|
+
};
|
|
39
|
+
return {
|
|
40
|
+
default: fsModule,
|
|
41
|
+
...fsModule,
|
|
42
|
+
};
|
|
43
|
+
});
|
|
19
44
|
// --- Mocks ---
|
|
20
|
-
const mockChatCreateFn = vi.fn();
|
|
21
|
-
const mockGenerateContentFn = vi.fn();
|
|
22
|
-
const mockEmbedContentFn = vi.fn();
|
|
23
45
|
const mockTurnRunFn = vi.fn();
|
|
24
|
-
vi.mock('
|
|
25
|
-
|
|
46
|
+
vi.mock('./turn', async (importOriginal) => {
|
|
47
|
+
const actual = await importOriginal();
|
|
26
48
|
// Define a mock class that has the same shape as the real Turn
|
|
27
49
|
class MockTurn {
|
|
28
50
|
pendingToolCalls = [];
|
|
@@ -34,11 +56,8 @@ vi.mock('./turn', () => {
|
|
|
34
56
|
}
|
|
35
57
|
// Export the mock class as 'Turn'
|
|
36
58
|
return {
|
|
59
|
+
...actual,
|
|
37
60
|
Turn: MockTurn,
|
|
38
|
-
GeminiEventType: {
|
|
39
|
-
MaxSessionTurns: 'MaxSessionTurns',
|
|
40
|
-
ChatCompressed: 'ChatCompressed',
|
|
41
|
-
},
|
|
42
61
|
};
|
|
43
62
|
});
|
|
44
63
|
vi.mock('../config/config.js');
|
|
@@ -60,80 +79,51 @@ vi.mock('../telemetry/index.js', () => ({
|
|
|
60
79
|
logApiError: vi.fn(),
|
|
61
80
|
}));
|
|
62
81
|
vi.mock('../ide/ideContext.js');
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
// 1: 66 + 68 = 134
|
|
82
|
-
// 2: 134 + 66 = 200
|
|
83
|
-
// 200 >= 166.5, so index is 2
|
|
84
|
-
expect(findIndexAfterFraction(history, 0.5)).toBe(2);
|
|
85
|
-
});
|
|
86
|
-
it('should handle a fraction that results in the last index', () => {
|
|
87
|
-
// 333 * 0.9 = 299.7
|
|
88
|
-
// ...
|
|
89
|
-
// 3: 200 + 68 = 268
|
|
90
|
-
// 4: 268 + 65 = 333
|
|
91
|
-
// 333 >= 299.7, so index is 4
|
|
92
|
-
expect(findIndexAfterFraction(history, 0.9)).toBe(4);
|
|
93
|
-
});
|
|
94
|
-
it('should handle an empty history', () => {
|
|
95
|
-
expect(findIndexAfterFraction([], 0.5)).toBe(0);
|
|
96
|
-
});
|
|
97
|
-
it('should handle a history with only one item', () => {
|
|
98
|
-
expect(findIndexAfterFraction(history.slice(0, 1), 0.5)).toBe(0);
|
|
99
|
-
});
|
|
100
|
-
it('should handle history with weird parts', () => {
|
|
101
|
-
const historyWithEmptyParts = [
|
|
102
|
-
{ role: 'user', parts: [{ text: 'Message 1' }] },
|
|
103
|
-
{ role: 'model', parts: [{ fileData: { fileUri: 'derp' } }] },
|
|
104
|
-
{ role: 'user', parts: [{ text: 'Message 2' }] },
|
|
105
|
-
];
|
|
106
|
-
expect(findIndexAfterFraction(historyWithEmptyParts, 0.5)).toBe(1);
|
|
107
|
-
});
|
|
108
|
-
});
|
|
82
|
+
vi.mock('../telemetry/uiTelemetry.js', () => ({
|
|
83
|
+
uiTelemetryService: {
|
|
84
|
+
setLastPromptTokenCount: vi.fn(),
|
|
85
|
+
getLastPromptTokenCount: vi.fn(),
|
|
86
|
+
},
|
|
87
|
+
}));
|
|
88
|
+
/**
|
|
89
|
+
* Array.fromAsync ponyfill, which will be available in es 2024.
|
|
90
|
+
*
|
|
91
|
+
* Buffers an async generator into an array and returns the result.
|
|
92
|
+
*/
|
|
93
|
+
async function fromAsync(promise) {
|
|
94
|
+
const results = [];
|
|
95
|
+
for await (const result of promise) {
|
|
96
|
+
results.push(result);
|
|
97
|
+
}
|
|
98
|
+
return results;
|
|
99
|
+
}
|
|
109
100
|
describe('Gemini Client (client.ts)', () => {
|
|
101
|
+
let mockContentGenerator;
|
|
102
|
+
let mockConfig;
|
|
110
103
|
let client;
|
|
104
|
+
let mockGenerateContentFn;
|
|
111
105
|
beforeEach(async () => {
|
|
112
106
|
vi.resetAllMocks();
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
embedContent: mockEmbedContentFn,
|
|
123
|
-
},
|
|
124
|
-
};
|
|
125
|
-
return mock;
|
|
107
|
+
ClearcutLogger.clearInstance();
|
|
108
|
+
vi.mocked(uiTelemetryService.setLastPromptTokenCount).mockClear();
|
|
109
|
+
vi.mocked(ChatCompressionService.prototype.compress).mockResolvedValue({
|
|
110
|
+
newHistory: null,
|
|
111
|
+
info: {
|
|
112
|
+
originalTokenCount: 0,
|
|
113
|
+
newTokenCount: 0,
|
|
114
|
+
compressionStatus: CompressionStatus.NOOP,
|
|
115
|
+
},
|
|
126
116
|
});
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
candidates: [
|
|
130
|
-
{
|
|
131
|
-
content: {
|
|
132
|
-
parts: [{ text: '{"key": "value"}' }],
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
],
|
|
117
|
+
mockGenerateContentFn = vi.fn().mockResolvedValue({
|
|
118
|
+
candidates: [{ content: { parts: [{ text: '{"key": "value"}' }] } }],
|
|
136
119
|
});
|
|
120
|
+
// Disable 429 simulation for tests
|
|
121
|
+
setSimulate429(false);
|
|
122
|
+
mockContentGenerator = {
|
|
123
|
+
generateContent: mockGenerateContentFn,
|
|
124
|
+
generateContentStream: vi.fn(),
|
|
125
|
+
batchEmbedContents: vi.fn(),
|
|
126
|
+
};
|
|
137
127
|
// Because the GeminiClient constructor kicks off an async process (startChat)
|
|
138
128
|
// that depends on a fully-formed Config object, we need to mock the
|
|
139
129
|
// entire implementation of Config for these tests.
|
|
@@ -143,12 +133,11 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
143
133
|
};
|
|
144
134
|
const fileService = new FileDiscoveryService('/test/dir');
|
|
145
135
|
const contentGeneratorConfig = {
|
|
146
|
-
model: 'test-model',
|
|
147
136
|
apiKey: 'test-key',
|
|
148
137
|
vertexai: false,
|
|
149
138
|
authType: AuthType.USE_GEMINI,
|
|
150
139
|
};
|
|
151
|
-
|
|
140
|
+
mockConfig = {
|
|
152
141
|
getContentGeneratorConfig: vi
|
|
153
142
|
.fn()
|
|
154
143
|
.mockReturnValue(contentGeneratorConfig),
|
|
@@ -159,7 +148,6 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
159
148
|
getVertexAI: vi.fn().mockReturnValue(false),
|
|
160
149
|
getUserAgent: vi.fn().mockReturnValue('test-agent'),
|
|
161
150
|
getUserMemory: vi.fn().mockReturnValue(''),
|
|
162
|
-
getFullContext: vi.fn().mockReturnValue(false),
|
|
163
151
|
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
|
164
152
|
getProxy: vi.fn().mockReturnValue(undefined),
|
|
165
153
|
getWorkingDir: vi.fn().mockReturnValue('/test/dir'),
|
|
@@ -172,185 +160,54 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
172
160
|
getIdeModeFeature: vi.fn().mockReturnValue(false),
|
|
173
161
|
getIdeMode: vi.fn().mockReturnValue(true),
|
|
174
162
|
getDebugMode: vi.fn().mockReturnValue(false),
|
|
163
|
+
getPreviewFeatures: vi.fn().mockReturnValue(false),
|
|
175
164
|
getWorkspaceContext: vi.fn().mockReturnValue({
|
|
176
165
|
getDirectories: vi.fn().mockReturnValue(['/test/dir']),
|
|
177
166
|
}),
|
|
178
167
|
getGeminiClient: vi.fn(),
|
|
168
|
+
getModelRouterService: vi.fn().mockReturnValue({
|
|
169
|
+
route: vi.fn().mockResolvedValue({ model: 'default-routed-model' }),
|
|
170
|
+
}),
|
|
171
|
+
isInFallbackMode: vi.fn().mockReturnValue(false),
|
|
179
172
|
setFallbackMode: vi.fn(),
|
|
180
173
|
getChatCompression: vi.fn().mockReturnValue(undefined),
|
|
181
174
|
getSkipNextSpeakerCheck: vi.fn().mockReturnValue(false),
|
|
175
|
+
getUseSmartEdit: vi.fn().mockReturnValue(false),
|
|
176
|
+
getUseModelRouter: vi.fn().mockReturnValue(false),
|
|
177
|
+
getShowModelInfoInChat: vi.fn().mockReturnValue(false),
|
|
178
|
+
getContinueOnFailedApiCall: vi.fn(),
|
|
179
|
+
getProjectRoot: vi.fn().mockReturnValue('/test/project/root'),
|
|
180
|
+
storage: {
|
|
181
|
+
getProjectTempDir: vi.fn().mockReturnValue('/test/temp'),
|
|
182
|
+
},
|
|
183
|
+
getContentGenerator: vi.fn().mockReturnValue(mockContentGenerator),
|
|
184
|
+
getBaseLlmClient: vi.fn().mockReturnValue({
|
|
185
|
+
generateJson: vi.fn().mockResolvedValue({
|
|
186
|
+
next_speaker: 'user',
|
|
187
|
+
reasoning: 'test',
|
|
188
|
+
}),
|
|
189
|
+
}),
|
|
190
|
+
modelConfigService: {
|
|
191
|
+
getResolvedConfig(modelConfigKey) {
|
|
192
|
+
return {
|
|
193
|
+
model: modelConfigKey.model,
|
|
194
|
+
generateContentConfig: {
|
|
195
|
+
temperature: 0,
|
|
196
|
+
topP: 1,
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
isInteractive: vi.fn().mockReturnValue(false),
|
|
182
202
|
};
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
client = new GeminiClient(new Config({ sessionId: 'test-session-id' }));
|
|
188
|
-
mockConfigObject.getGeminiClient.mockReturnValue(client);
|
|
189
|
-
await client.initialize(contentGeneratorConfig);
|
|
203
|
+
client = new GeminiClient(mockConfig);
|
|
204
|
+
await client.initialize();
|
|
205
|
+
vi.mocked(mockConfig.getGeminiClient).mockReturnValue(client);
|
|
206
|
+
vi.mocked(uiTelemetryService.setLastPromptTokenCount).mockClear();
|
|
190
207
|
});
|
|
191
208
|
afterEach(() => {
|
|
192
209
|
vi.restoreAllMocks();
|
|
193
210
|
});
|
|
194
|
-
// NOTE: The following tests for startChat were removed due to persistent issues with
|
|
195
|
-
// the @google/genai mock. Specifically, the mockChatCreateFn (representing instance.chats.create)
|
|
196
|
-
// was not being detected as called by the GeminiClient instance.
|
|
197
|
-
// This likely points to a subtle issue in how the GoogleGenerativeAI class constructor
|
|
198
|
-
// and its instance methods are mocked and then used by the class under test.
|
|
199
|
-
// For future debugging, ensure that the `this.client` in `GeminiClient` (which is an
|
|
200
|
-
// instance of the mocked GoogleGenerativeAI) correctly has its `chats.create` method
|
|
201
|
-
// pointing to `mockChatCreateFn`.
|
|
202
|
-
// it('startChat should call getCoreSystemPrompt with userMemory and pass to chats.create', async () => { ... });
|
|
203
|
-
// it('startChat should call getCoreSystemPrompt with empty string if userMemory is empty', async () => { ... });
|
|
204
|
-
// NOTE: The following tests for generateJson were removed due to persistent issues with
|
|
205
|
-
// the @google/genai mock, similar to the startChat tests. The mockGenerateContentFn
|
|
206
|
-
// (representing instance.models.generateContent) was not being detected as called, or the mock
|
|
207
|
-
// was not preventing an actual API call (leading to API key errors).
|
|
208
|
-
// For future debugging, ensure `this.client.models.generateContent` in `GeminiClient` correctly
|
|
209
|
-
// uses the `mockGenerateContentFn`.
|
|
210
|
-
// it('generateJson should call getCoreSystemPrompt with userMemory and pass to generateContent', async () => { ... });
|
|
211
|
-
// it('generateJson should call getCoreSystemPrompt with empty string if userMemory is empty', async () => { ... });
|
|
212
|
-
describe('generateEmbedding', () => {
|
|
213
|
-
const texts = ['hello world', 'goodbye world'];
|
|
214
|
-
const testEmbeddingModel = 'test-embedding-model';
|
|
215
|
-
it('should call embedContent with correct parameters and return embeddings', async () => {
|
|
216
|
-
const mockEmbeddings = [
|
|
217
|
-
[0.1, 0.2, 0.3],
|
|
218
|
-
[0.4, 0.5, 0.6],
|
|
219
|
-
];
|
|
220
|
-
const mockResponse = {
|
|
221
|
-
embeddings: [
|
|
222
|
-
{ values: mockEmbeddings[0] },
|
|
223
|
-
{ values: mockEmbeddings[1] },
|
|
224
|
-
],
|
|
225
|
-
};
|
|
226
|
-
mockEmbedContentFn.mockResolvedValue(mockResponse);
|
|
227
|
-
const result = await client.generateEmbedding(texts);
|
|
228
|
-
expect(mockEmbedContentFn).toHaveBeenCalledTimes(1);
|
|
229
|
-
expect(mockEmbedContentFn).toHaveBeenCalledWith({
|
|
230
|
-
model: testEmbeddingModel,
|
|
231
|
-
contents: texts,
|
|
232
|
-
});
|
|
233
|
-
expect(result).toEqual(mockEmbeddings);
|
|
234
|
-
});
|
|
235
|
-
it('should return an empty array if an empty array is passed', async () => {
|
|
236
|
-
const result = await client.generateEmbedding([]);
|
|
237
|
-
expect(result).toEqual([]);
|
|
238
|
-
expect(mockEmbedContentFn).not.toHaveBeenCalled();
|
|
239
|
-
});
|
|
240
|
-
it('should throw an error if API response has no embeddings array', async () => {
|
|
241
|
-
mockEmbedContentFn.mockResolvedValue({}); // No `embeddings` key
|
|
242
|
-
await expect(client.generateEmbedding(texts)).rejects.toThrow('No embeddings found in API response.');
|
|
243
|
-
});
|
|
244
|
-
it('should throw an error if API response has an empty embeddings array', async () => {
|
|
245
|
-
const mockResponse = {
|
|
246
|
-
embeddings: [],
|
|
247
|
-
};
|
|
248
|
-
mockEmbedContentFn.mockResolvedValue(mockResponse);
|
|
249
|
-
await expect(client.generateEmbedding(texts)).rejects.toThrow('No embeddings found in API response.');
|
|
250
|
-
});
|
|
251
|
-
it('should throw an error if API returns a mismatched number of embeddings', async () => {
|
|
252
|
-
const mockResponse = {
|
|
253
|
-
embeddings: [{ values: [1, 2, 3] }], // Only one for two texts
|
|
254
|
-
};
|
|
255
|
-
mockEmbedContentFn.mockResolvedValue(mockResponse);
|
|
256
|
-
await expect(client.generateEmbedding(texts)).rejects.toThrow('API returned a mismatched number of embeddings. Expected 2, got 1.');
|
|
257
|
-
});
|
|
258
|
-
it('should throw an error if any embedding has nullish values', async () => {
|
|
259
|
-
const mockResponse = {
|
|
260
|
-
embeddings: [{ values: [1, 2, 3] }, { values: undefined }], // Second one is bad
|
|
261
|
-
};
|
|
262
|
-
mockEmbedContentFn.mockResolvedValue(mockResponse);
|
|
263
|
-
await expect(client.generateEmbedding(texts)).rejects.toThrow('API returned an empty embedding for input text at index 1: "goodbye world"');
|
|
264
|
-
});
|
|
265
|
-
it('should throw an error if any embedding has an empty values array', async () => {
|
|
266
|
-
const mockResponse = {
|
|
267
|
-
embeddings: [{ values: [] }, { values: [1, 2, 3] }], // First one is bad
|
|
268
|
-
};
|
|
269
|
-
mockEmbedContentFn.mockResolvedValue(mockResponse);
|
|
270
|
-
await expect(client.generateEmbedding(texts)).rejects.toThrow('API returned an empty embedding for input text at index 0: "hello world"');
|
|
271
|
-
});
|
|
272
|
-
it('should propagate errors from the API call', async () => {
|
|
273
|
-
const apiError = new Error('API Failure');
|
|
274
|
-
mockEmbedContentFn.mockRejectedValue(apiError);
|
|
275
|
-
await expect(client.generateEmbedding(texts)).rejects.toThrow('API Failure');
|
|
276
|
-
});
|
|
277
|
-
});
|
|
278
|
-
describe('generateContent', () => {
|
|
279
|
-
it('should call generateContent with the correct parameters', async () => {
|
|
280
|
-
const contents = [{ role: 'user', parts: [{ text: 'hello' }] }];
|
|
281
|
-
const generationConfig = { temperature: 0.5 };
|
|
282
|
-
const abortSignal = new AbortController().signal;
|
|
283
|
-
// Mock countTokens
|
|
284
|
-
const mockGenerator = {
|
|
285
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 1 }),
|
|
286
|
-
generateContent: mockGenerateContentFn,
|
|
287
|
-
};
|
|
288
|
-
client['contentGenerator'] = mockGenerator;
|
|
289
|
-
await client.generateContent(contents, generationConfig, abortSignal);
|
|
290
|
-
expect(mockGenerateContentFn).toHaveBeenCalledWith({
|
|
291
|
-
model: 'test-model',
|
|
292
|
-
config: {
|
|
293
|
-
abortSignal,
|
|
294
|
-
systemInstruction: getCoreSystemPrompt(''),
|
|
295
|
-
temperature: 0.5,
|
|
296
|
-
topP: 1,
|
|
297
|
-
},
|
|
298
|
-
contents,
|
|
299
|
-
}, 'test-session-id');
|
|
300
|
-
});
|
|
301
|
-
});
|
|
302
|
-
describe('generateJson', () => {
|
|
303
|
-
it('should call generateContent with the correct parameters', async () => {
|
|
304
|
-
const contents = [{ role: 'user', parts: [{ text: 'hello' }] }];
|
|
305
|
-
const schema = { type: 'string' };
|
|
306
|
-
const abortSignal = new AbortController().signal;
|
|
307
|
-
// Mock countTokens
|
|
308
|
-
const mockGenerator = {
|
|
309
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 1 }),
|
|
310
|
-
generateContent: mockGenerateContentFn,
|
|
311
|
-
};
|
|
312
|
-
client['contentGenerator'] = mockGenerator;
|
|
313
|
-
await client.generateJson(contents, schema, abortSignal);
|
|
314
|
-
expect(mockGenerateContentFn).toHaveBeenCalledWith({
|
|
315
|
-
model: 'test-model', // Should use current model from config
|
|
316
|
-
config: {
|
|
317
|
-
abortSignal,
|
|
318
|
-
systemInstruction: getCoreSystemPrompt(''),
|
|
319
|
-
temperature: 0,
|
|
320
|
-
topP: 1,
|
|
321
|
-
responseJsonSchema: schema,
|
|
322
|
-
responseMimeType: 'application/json',
|
|
323
|
-
},
|
|
324
|
-
contents,
|
|
325
|
-
}, 'test-session-id');
|
|
326
|
-
});
|
|
327
|
-
it('should allow overriding model and config', async () => {
|
|
328
|
-
const contents = [{ role: 'user', parts: [{ text: 'hello' }] }];
|
|
329
|
-
const schema = { type: 'string' };
|
|
330
|
-
const abortSignal = new AbortController().signal;
|
|
331
|
-
const customModel = 'custom-json-model';
|
|
332
|
-
const customConfig = { temperature: 0.9, topK: 20 };
|
|
333
|
-
const mockGenerator = {
|
|
334
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 1 }),
|
|
335
|
-
generateContent: mockGenerateContentFn,
|
|
336
|
-
};
|
|
337
|
-
client['contentGenerator'] = mockGenerator;
|
|
338
|
-
await client.generateJson(contents, schema, abortSignal, customModel, customConfig);
|
|
339
|
-
expect(mockGenerateContentFn).toHaveBeenCalledWith({
|
|
340
|
-
model: customModel,
|
|
341
|
-
config: {
|
|
342
|
-
abortSignal,
|
|
343
|
-
systemInstruction: getCoreSystemPrompt(''),
|
|
344
|
-
temperature: 0.9,
|
|
345
|
-
topP: 1, // from default
|
|
346
|
-
topK: 20,
|
|
347
|
-
responseJsonSchema: schema,
|
|
348
|
-
responseMimeType: 'application/json',
|
|
349
|
-
},
|
|
350
|
-
contents,
|
|
351
|
-
}, 'test-session-id');
|
|
352
|
-
});
|
|
353
|
-
});
|
|
354
211
|
describe('addHistory', () => {
|
|
355
212
|
it('should call chat.addHistory with the provided content', async () => {
|
|
356
213
|
const mockChat = {
|
|
@@ -387,180 +244,232 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
387
244
|
expect(JSON.stringify(newHistory)).not.toContain('some old message');
|
|
388
245
|
});
|
|
389
246
|
});
|
|
247
|
+
describe('startChat', () => {
|
|
248
|
+
it('should include environment context when resuming a session', async () => {
|
|
249
|
+
const extraHistory = [
|
|
250
|
+
{ role: 'user', parts: [{ text: 'Old message' }] },
|
|
251
|
+
{ role: 'model', parts: [{ text: 'Old response' }] },
|
|
252
|
+
];
|
|
253
|
+
const chat = await client.startChat(extraHistory);
|
|
254
|
+
const history = chat.getHistory();
|
|
255
|
+
// The first message should be the environment context
|
|
256
|
+
expect(history[0].role).toBe('user');
|
|
257
|
+
expect(history[0].parts?.[0]?.text).toContain('This is the Gemini CLI');
|
|
258
|
+
expect(history[0].parts?.[0]?.text).toContain("The project's temporary directory is:");
|
|
259
|
+
// The subsequent messages should be the extra history
|
|
260
|
+
expect(history[1]).toEqual(extraHistory[0]);
|
|
261
|
+
expect(history[2]).toEqual(extraHistory[1]);
|
|
262
|
+
});
|
|
263
|
+
});
|
|
390
264
|
describe('tryCompressChat', () => {
|
|
391
|
-
const mockCountTokens = vi.fn();
|
|
392
|
-
const mockSendMessage = vi.fn();
|
|
393
265
|
const mockGetHistory = vi.fn();
|
|
394
266
|
beforeEach(() => {
|
|
395
267
|
vi.mock('./tokenLimits', () => ({
|
|
396
268
|
tokenLimit: vi.fn(),
|
|
397
269
|
}));
|
|
398
|
-
client['contentGenerator'] = {
|
|
399
|
-
countTokens: mockCountTokens,
|
|
400
|
-
};
|
|
401
270
|
client['chat'] = {
|
|
402
271
|
getHistory: mockGetHistory,
|
|
403
272
|
addHistory: vi.fn(),
|
|
404
273
|
setHistory: vi.fn(),
|
|
405
|
-
|
|
274
|
+
getLastPromptTokenCount: vi.fn(),
|
|
406
275
|
};
|
|
407
276
|
});
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
277
|
+
function setup({ chatHistory = [
|
|
278
|
+
{ role: 'user', parts: [{ text: 'Long conversation' }] },
|
|
279
|
+
{ role: 'model', parts: [{ text: 'Long response' }] },
|
|
280
|
+
], originalTokenCount = 1000, newTokenCount = 500, compressionStatus = CompressionStatus.COMPRESSED, } = {}) {
|
|
281
|
+
const mockOriginalChat = {
|
|
282
|
+
getHistory: vi.fn((_curated) => chatHistory),
|
|
283
|
+
setHistory: vi.fn(),
|
|
284
|
+
getLastPromptTokenCount: vi.fn().mockReturnValue(originalTokenCount),
|
|
285
|
+
};
|
|
286
|
+
client['chat'] = mockOriginalChat;
|
|
287
|
+
vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
|
|
288
|
+
const newHistory = [
|
|
289
|
+
{ role: 'user', parts: [{ text: 'Summary' }] },
|
|
290
|
+
{ role: 'model', parts: [{ text: 'Got it' }] },
|
|
291
|
+
];
|
|
292
|
+
vi.mocked(ChatCompressionService.prototype.compress).mockResolvedValue({
|
|
293
|
+
newHistory: compressionStatus === CompressionStatus.COMPRESSED
|
|
294
|
+
? newHistory
|
|
295
|
+
: null,
|
|
296
|
+
info: {
|
|
297
|
+
originalTokenCount,
|
|
298
|
+
newTokenCount,
|
|
299
|
+
compressionStatus,
|
|
300
|
+
},
|
|
431
301
|
});
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
.
|
|
439
|
-
.
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
302
|
+
const mockNewChat = {
|
|
303
|
+
getHistory: vi.fn().mockReturnValue(newHistory),
|
|
304
|
+
setHistory: vi.fn(),
|
|
305
|
+
getLastPromptTokenCount: vi.fn().mockReturnValue(newTokenCount),
|
|
306
|
+
};
|
|
307
|
+
client['startChat'] = vi
|
|
308
|
+
.fn()
|
|
309
|
+
.mockResolvedValue(mockNewChat);
|
|
310
|
+
return {
|
|
311
|
+
client,
|
|
312
|
+
mockOriginalChat,
|
|
313
|
+
mockNewChat,
|
|
314
|
+
estimatedNewTokenCount: newTokenCount,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
describe('when compression inflates the token count', () => {
|
|
318
|
+
it('allows compression to be forced/manual after a failure', async () => {
|
|
319
|
+
// Call 1 (Fails): Setup with inflated tokens
|
|
320
|
+
setup({
|
|
321
|
+
originalTokenCount: 100,
|
|
322
|
+
newTokenCount: 200,
|
|
323
|
+
compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
|
|
324
|
+
});
|
|
325
|
+
await client.tryCompressChat('prompt-id-4', false); // Fails
|
|
326
|
+
// Call 2 (Forced): Re-setup with compressed tokens
|
|
327
|
+
const { estimatedNewTokenCount: compressedTokenCount } = setup({
|
|
328
|
+
originalTokenCount: 100,
|
|
329
|
+
newTokenCount: 50,
|
|
330
|
+
compressionStatus: CompressionStatus.COMPRESSED,
|
|
331
|
+
});
|
|
332
|
+
const result = await client.tryCompressChat('prompt-id-4', true); // Forced
|
|
333
|
+
expect(result).toEqual({
|
|
334
|
+
compressionStatus: CompressionStatus.COMPRESSED,
|
|
335
|
+
newTokenCount: compressedTokenCount,
|
|
336
|
+
originalTokenCount: 100,
|
|
337
|
+
});
|
|
444
338
|
});
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
339
|
+
it('yields the result even if the compression inflated the tokens', async () => {
|
|
340
|
+
const { client, estimatedNewTokenCount } = setup({
|
|
341
|
+
originalTokenCount: 100,
|
|
342
|
+
newTokenCount: 200,
|
|
343
|
+
compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
|
|
344
|
+
});
|
|
345
|
+
const result = await client.tryCompressChat('prompt-id-4', false);
|
|
346
|
+
expect(result).toEqual({
|
|
347
|
+
compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
|
|
348
|
+
newTokenCount: estimatedNewTokenCount,
|
|
349
|
+
originalTokenCount: 100,
|
|
350
|
+
});
|
|
351
|
+
// IMPORTANT: The change in client.ts means setLastPromptTokenCount is NOT called on failure
|
|
352
|
+
expect(uiTelemetryService.setLastPromptTokenCount).not.toHaveBeenCalled();
|
|
457
353
|
});
|
|
458
|
-
|
|
459
|
-
{
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
.
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
mockSendMessage.mockResolvedValue({
|
|
468
|
-
role: 'model',
|
|
469
|
-
parts: [{ text: 'This is a summary.' }],
|
|
354
|
+
it('does not manipulate the source chat', async () => {
|
|
355
|
+
const { client, mockOriginalChat } = setup({
|
|
356
|
+
originalTokenCount: 100,
|
|
357
|
+
newTokenCount: 200,
|
|
358
|
+
compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
|
|
359
|
+
});
|
|
360
|
+
await client.tryCompressChat('prompt-id-4', false);
|
|
361
|
+
// On failure, the chat should NOT be replaced
|
|
362
|
+
expect(client['chat']).toBe(mockOriginalChat);
|
|
470
363
|
});
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
364
|
+
it.skip('will not attempt to compress context after a failure', async () => {
|
|
365
|
+
const { client } = setup({
|
|
366
|
+
originalTokenCount: 100,
|
|
367
|
+
newTokenCount: 200,
|
|
368
|
+
compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
|
|
369
|
+
});
|
|
370
|
+
await client.tryCompressChat('prompt-id-4', false); // This fails and sets hasFailedCompressionAttempt = true
|
|
371
|
+
// Mock the next call to return NOOP
|
|
372
|
+
vi.mocked(ChatCompressionService.prototype.compress).mockResolvedValueOnce({
|
|
373
|
+
newHistory: null,
|
|
374
|
+
info: {
|
|
375
|
+
originalTokenCount: 0,
|
|
376
|
+
newTokenCount: 0,
|
|
377
|
+
compressionStatus: CompressionStatus.NOOP,
|
|
378
|
+
},
|
|
379
|
+
});
|
|
380
|
+
// This call should now be a NOOP
|
|
381
|
+
const result = await client.tryCompressChat('prompt-id-5', false);
|
|
382
|
+
expect(result.compressionStatus).toBe(CompressionStatus.NOOP);
|
|
383
|
+
expect(ChatCompressionService.prototype.compress).toHaveBeenCalledTimes(2);
|
|
384
|
+
expect(ChatCompressionService.prototype.compress).toHaveBeenLastCalledWith(expect.anything(), 'prompt-id-5', false, expect.anything(), expect.anything(), true);
|
|
480
385
|
});
|
|
481
|
-
// Assert that the chat was reset
|
|
482
|
-
expect(newChat).not.toBe(initialChat);
|
|
483
386
|
});
|
|
484
|
-
it('should not
|
|
387
|
+
it('should not trigger summarization if token count is below threshold', async () => {
|
|
485
388
|
const MOCKED_TOKEN_LIMIT = 1000;
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
{ role: 'model', parts: [{ text: '...history 6...' }] },
|
|
494
|
-
{ role: 'user', parts: [{ text: '...history 7...' }] },
|
|
495
|
-
{ role: 'model', parts: [{ text: '...history 8...' }] },
|
|
496
|
-
// Normally we would break here, but we have a function response.
|
|
497
|
-
{
|
|
498
|
-
role: 'user',
|
|
499
|
-
parts: [{ functionResponse: { name: '...history 8...' } }],
|
|
389
|
+
const originalTokenCount = MOCKED_TOKEN_LIMIT * 0.699;
|
|
390
|
+
vi.mocked(ChatCompressionService.prototype.compress).mockResolvedValue({
|
|
391
|
+
newHistory: null,
|
|
392
|
+
info: {
|
|
393
|
+
originalTokenCount,
|
|
394
|
+
newTokenCount: originalTokenCount,
|
|
395
|
+
compressionStatus: CompressionStatus.NOOP,
|
|
500
396
|
},
|
|
501
|
-
{ role: 'model', parts: [{ text: '...history 10...' }] },
|
|
502
|
-
// Instead we will break here.
|
|
503
|
-
{ role: 'user', parts: [{ text: '...history 10...' }] },
|
|
504
|
-
]);
|
|
505
|
-
const originalTokenCount = 1000 * 0.7;
|
|
506
|
-
const newTokenCount = 100;
|
|
507
|
-
mockCountTokens
|
|
508
|
-
.mockResolvedValueOnce({ totalTokens: originalTokenCount }) // First call for the check
|
|
509
|
-
.mockResolvedValueOnce({ totalTokens: newTokenCount }); // Second call for the new history
|
|
510
|
-
// Mock the summary response from the chat
|
|
511
|
-
mockSendMessage.mockResolvedValue({
|
|
512
|
-
role: 'model',
|
|
513
|
-
parts: [{ text: 'This is a summary.' }],
|
|
514
397
|
});
|
|
515
398
|
const initialChat = client.getChat();
|
|
516
|
-
const result = await client.tryCompressChat('prompt-id-
|
|
399
|
+
const result = await client.tryCompressChat('prompt-id-2', false);
|
|
517
400
|
const newChat = client.getChat();
|
|
518
|
-
expect(tokenLimit).toHaveBeenCalled();
|
|
519
|
-
expect(mockSendMessage).toHaveBeenCalled();
|
|
520
|
-
// Assert that summarization happened and returned the correct stats
|
|
521
401
|
expect(result).toEqual({
|
|
402
|
+
compressionStatus: CompressionStatus.NOOP,
|
|
403
|
+
newTokenCount: originalTokenCount,
|
|
522
404
|
originalTokenCount,
|
|
523
|
-
newTokenCount,
|
|
524
405
|
});
|
|
525
|
-
|
|
526
|
-
expect(newChat).not.toBe(initialChat);
|
|
527
|
-
// 1. standard start context message
|
|
528
|
-
// 2. standard canned user start message
|
|
529
|
-
// 3. compressed summary message
|
|
530
|
-
// 4. standard canned user summary message
|
|
531
|
-
// 5. The last user message (not the last 3 because that would start with a function response)
|
|
532
|
-
expect(newChat.getHistory().length).toEqual(5);
|
|
406
|
+
expect(newChat).toBe(initialChat);
|
|
533
407
|
});
|
|
534
|
-
it('should
|
|
535
|
-
|
|
536
|
-
{ role: 'user', parts: [{ text: '
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
mockCountTokens
|
|
541
|
-
.mockResolvedValueOnce({ totalTokens: originalTokenCount })
|
|
542
|
-
.mockResolvedValueOnce({ totalTokens: newTokenCount });
|
|
543
|
-
// Mock the summary response from the chat
|
|
544
|
-
mockSendMessage.mockResolvedValue({
|
|
545
|
-
role: 'model',
|
|
546
|
-
parts: [{ text: 'This is a summary.' }],
|
|
408
|
+
it('should return NOOP if history is too short to compress', async () => {
|
|
409
|
+
const { client } = setup({
|
|
410
|
+
chatHistory: [{ role: 'user', parts: [{ text: 'hi' }] }],
|
|
411
|
+
originalTokenCount: 50,
|
|
412
|
+
newTokenCount: 50,
|
|
413
|
+
compressionStatus: CompressionStatus.NOOP,
|
|
547
414
|
});
|
|
548
|
-
const
|
|
549
|
-
const result = await client.tryCompressChat('prompt-id-1', true); // force = true
|
|
550
|
-
const newChat = client.getChat();
|
|
551
|
-
expect(mockSendMessage).toHaveBeenCalled();
|
|
415
|
+
const result = await client.tryCompressChat('prompt-id-noop', false);
|
|
552
416
|
expect(result).toEqual({
|
|
553
|
-
|
|
554
|
-
|
|
417
|
+
compressionStatus: CompressionStatus.NOOP,
|
|
418
|
+
originalTokenCount: 50,
|
|
419
|
+
newTokenCount: 50,
|
|
555
420
|
});
|
|
556
|
-
// Assert that the chat was reset
|
|
557
|
-
expect(newChat).not.toBe(initialChat);
|
|
558
421
|
});
|
|
559
422
|
});
|
|
560
423
|
describe('sendMessageStream', () => {
|
|
424
|
+
it('emits a compression event when the context was automatically compressed', async () => {
|
|
425
|
+
// Arrange
|
|
426
|
+
mockTurnRunFn.mockReturnValue((async function* () {
|
|
427
|
+
yield { type: 'content', value: 'Hello' };
|
|
428
|
+
})());
|
|
429
|
+
const compressionInfo = {
|
|
430
|
+
compressionStatus: CompressionStatus.COMPRESSED,
|
|
431
|
+
originalTokenCount: 1000,
|
|
432
|
+
newTokenCount: 500,
|
|
433
|
+
};
|
|
434
|
+
vi.spyOn(client, 'tryCompressChat').mockResolvedValueOnce(compressionInfo);
|
|
435
|
+
// Act
|
|
436
|
+
const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-id-1');
|
|
437
|
+
const events = await fromAsync(stream);
|
|
438
|
+
// Assert
|
|
439
|
+
expect(events).toContainEqual({
|
|
440
|
+
type: GeminiEventType.ChatCompressed,
|
|
441
|
+
value: compressionInfo,
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
it.each([
|
|
445
|
+
{
|
|
446
|
+
compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
|
|
447
|
+
},
|
|
448
|
+
{ compressionStatus: CompressionStatus.NOOP },
|
|
449
|
+
])('does not emit a compression event when the status is $compressionStatus', async ({ compressionStatus }) => {
|
|
450
|
+
// Arrange
|
|
451
|
+
const mockStream = (async function* () {
|
|
452
|
+
yield { type: 'content', value: 'Hello' };
|
|
453
|
+
})();
|
|
454
|
+
mockTurnRunFn.mockReturnValue(mockStream);
|
|
455
|
+
const compressionInfo = {
|
|
456
|
+
compressionStatus,
|
|
457
|
+
originalTokenCount: 1000,
|
|
458
|
+
newTokenCount: 500,
|
|
459
|
+
};
|
|
460
|
+
vi.spyOn(client, 'tryCompressChat').mockResolvedValueOnce(compressionInfo);
|
|
461
|
+
// Act
|
|
462
|
+
const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-id-1');
|
|
463
|
+
const events = await fromAsync(stream);
|
|
464
|
+
// Assert
|
|
465
|
+
expect(events).not.toContainEqual({
|
|
466
|
+
type: GeminiEventType.ChatCompressed,
|
|
467
|
+
value: expect.anything(),
|
|
468
|
+
});
|
|
469
|
+
});
|
|
561
470
|
it('should include editor context when ideMode is enabled', async () => {
|
|
562
471
|
// Arrange
|
|
563
|
-
vi.mocked(
|
|
472
|
+
vi.mocked(ideContextStore.get).mockReturnValue({
|
|
564
473
|
workspaceState: {
|
|
565
474
|
openFiles: [
|
|
566
475
|
{
|
|
@@ -581,21 +490,21 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
581
490
|
],
|
|
582
491
|
},
|
|
583
492
|
});
|
|
584
|
-
vi.
|
|
585
|
-
|
|
493
|
+
vi.mocked(mockConfig.getIdeMode).mockReturnValue(true);
|
|
494
|
+
vi.spyOn(client, 'tryCompressChat').mockResolvedValue({
|
|
495
|
+
originalTokenCount: 0,
|
|
496
|
+
newTokenCount: 0,
|
|
497
|
+
compressionStatus: CompressionStatus.COMPRESSED,
|
|
498
|
+
});
|
|
499
|
+
mockTurnRunFn.mockReturnValue((async function* () {
|
|
586
500
|
yield { type: 'content', value: 'Hello' };
|
|
587
|
-
})();
|
|
588
|
-
mockTurnRunFn.mockReturnValue(mockStream);
|
|
501
|
+
})());
|
|
589
502
|
const mockChat = {
|
|
590
503
|
addHistory: vi.fn(),
|
|
591
504
|
getHistory: vi.fn().mockReturnValue([]),
|
|
505
|
+
getLastPromptTokenCount: vi.fn(),
|
|
592
506
|
};
|
|
593
507
|
client['chat'] = mockChat;
|
|
594
|
-
const mockGenerator = {
|
|
595
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
596
|
-
generateContent: mockGenerateContentFn,
|
|
597
|
-
};
|
|
598
|
-
client['contentGenerator'] = mockGenerator;
|
|
599
508
|
const initialRequest = [{ text: 'Hi' }];
|
|
600
509
|
// Act
|
|
601
510
|
const stream = client.sendMessageStream(initialRequest, new AbortController().signal, 'prompt-id-ide');
|
|
@@ -603,7 +512,7 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
603
512
|
// consume stream
|
|
604
513
|
}
|
|
605
514
|
// Assert
|
|
606
|
-
expect(
|
|
515
|
+
expect(ideContextStore.get).toHaveBeenCalled();
|
|
607
516
|
const expectedContext = `
|
|
608
517
|
Here is the user's editor context as a JSON object. This is for your information only.
|
|
609
518
|
\`\`\`json
|
|
@@ -628,7 +537,7 @@ ${JSON.stringify({
|
|
|
628
537
|
});
|
|
629
538
|
it('should not add context if ideMode is enabled but no open files', async () => {
|
|
630
539
|
// Arrange
|
|
631
|
-
vi.mocked(
|
|
540
|
+
vi.mocked(ideContextStore.get).mockReturnValue({
|
|
632
541
|
workspaceState: {
|
|
633
542
|
openFiles: [],
|
|
634
543
|
},
|
|
@@ -641,13 +550,9 @@ ${JSON.stringify({
|
|
|
641
550
|
const mockChat = {
|
|
642
551
|
addHistory: vi.fn(),
|
|
643
552
|
getHistory: vi.fn().mockReturnValue([]),
|
|
553
|
+
getLastPromptTokenCount: vi.fn(),
|
|
644
554
|
};
|
|
645
555
|
client['chat'] = mockChat;
|
|
646
|
-
const mockGenerator = {
|
|
647
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
648
|
-
generateContent: mockGenerateContentFn,
|
|
649
|
-
};
|
|
650
|
-
client['contentGenerator'] = mockGenerator;
|
|
651
556
|
const initialRequest = [{ text: 'Hi' }];
|
|
652
557
|
// Act
|
|
653
558
|
const stream = client.sendMessageStream(initialRequest, new AbortController().signal, 'prompt-id-ide');
|
|
@@ -655,12 +560,12 @@ ${JSON.stringify({
|
|
|
655
560
|
// consume stream
|
|
656
561
|
}
|
|
657
562
|
// Assert
|
|
658
|
-
expect(
|
|
659
|
-
expect(mockTurnRunFn).toHaveBeenCalledWith(initialRequest, expect.any(
|
|
563
|
+
expect(ideContextStore.get).toHaveBeenCalled();
|
|
564
|
+
expect(mockTurnRunFn).toHaveBeenCalledWith({ model: 'default-routed-model' }, initialRequest, expect.any(AbortSignal));
|
|
660
565
|
});
|
|
661
566
|
it('should add context if ideMode is enabled and there is one active file', async () => {
|
|
662
567
|
// Arrange
|
|
663
|
-
vi.mocked(
|
|
568
|
+
vi.mocked(ideContextStore.get).mockReturnValue({
|
|
664
569
|
workspaceState: {
|
|
665
570
|
openFiles: [
|
|
666
571
|
{
|
|
@@ -674,6 +579,11 @@ ${JSON.stringify({
|
|
|
674
579
|
},
|
|
675
580
|
});
|
|
676
581
|
vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
|
|
582
|
+
vi.spyOn(client, 'tryCompressChat').mockResolvedValue({
|
|
583
|
+
originalTokenCount: 0,
|
|
584
|
+
newTokenCount: 0,
|
|
585
|
+
compressionStatus: CompressionStatus.COMPRESSED,
|
|
586
|
+
});
|
|
677
587
|
const mockStream = (async function* () {
|
|
678
588
|
yield { type: 'content', value: 'Hello' };
|
|
679
589
|
})();
|
|
@@ -681,13 +591,9 @@ ${JSON.stringify({
|
|
|
681
591
|
const mockChat = {
|
|
682
592
|
addHistory: vi.fn(),
|
|
683
593
|
getHistory: vi.fn().mockReturnValue([]),
|
|
594
|
+
getLastPromptTokenCount: vi.fn(),
|
|
684
595
|
};
|
|
685
596
|
client['chat'] = mockChat;
|
|
686
|
-
const mockGenerator = {
|
|
687
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
688
|
-
generateContent: mockGenerateContentFn,
|
|
689
|
-
};
|
|
690
|
-
client['contentGenerator'] = mockGenerator;
|
|
691
597
|
const initialRequest = [{ text: 'Hi' }];
|
|
692
598
|
// Act
|
|
693
599
|
const stream = client.sendMessageStream(initialRequest, new AbortController().signal, 'prompt-id-ide');
|
|
@@ -695,7 +601,7 @@ ${JSON.stringify({
|
|
|
695
601
|
// consume stream
|
|
696
602
|
}
|
|
697
603
|
// Assert
|
|
698
|
-
expect(
|
|
604
|
+
expect(ideContextStore.get).toHaveBeenCalled();
|
|
699
605
|
const expectedContext = `
|
|
700
606
|
Here is the user's editor context as a JSON object. This is for your information only.
|
|
701
607
|
\`\`\`json
|
|
@@ -719,7 +625,7 @@ ${JSON.stringify({
|
|
|
719
625
|
});
|
|
720
626
|
it('should add context if ideMode is enabled and there are open files but no active file', async () => {
|
|
721
627
|
// Arrange
|
|
722
|
-
vi.mocked(
|
|
628
|
+
vi.mocked(ideContextStore.get).mockReturnValue({
|
|
723
629
|
workspaceState: {
|
|
724
630
|
openFiles: [
|
|
725
631
|
{
|
|
@@ -734,6 +640,11 @@ ${JSON.stringify({
|
|
|
734
640
|
},
|
|
735
641
|
});
|
|
736
642
|
vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
|
|
643
|
+
vi.spyOn(client, 'tryCompressChat').mockResolvedValue({
|
|
644
|
+
originalTokenCount: 0,
|
|
645
|
+
newTokenCount: 0,
|
|
646
|
+
compressionStatus: CompressionStatus.COMPRESSED,
|
|
647
|
+
});
|
|
737
648
|
const mockStream = (async function* () {
|
|
738
649
|
yield { type: 'content', value: 'Hello' };
|
|
739
650
|
})();
|
|
@@ -741,13 +652,9 @@ ${JSON.stringify({
|
|
|
741
652
|
const mockChat = {
|
|
742
653
|
addHistory: vi.fn(),
|
|
743
654
|
getHistory: vi.fn().mockReturnValue([]),
|
|
655
|
+
getLastPromptTokenCount: vi.fn(),
|
|
744
656
|
};
|
|
745
657
|
client['chat'] = mockChat;
|
|
746
|
-
const mockGenerator = {
|
|
747
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
748
|
-
generateContent: mockGenerateContentFn,
|
|
749
|
-
};
|
|
750
|
-
client['contentGenerator'] = mockGenerator;
|
|
751
658
|
const initialRequest = [{ text: 'Hi' }];
|
|
752
659
|
// Act
|
|
753
660
|
const stream = client.sendMessageStream(initialRequest, new AbortController().signal, 'prompt-id-ide');
|
|
@@ -755,7 +662,7 @@ ${JSON.stringify({
|
|
|
755
662
|
// consume stream
|
|
756
663
|
}
|
|
757
664
|
// Assert
|
|
758
|
-
expect(
|
|
665
|
+
expect(ideContextStore.get).toHaveBeenCalled();
|
|
759
666
|
const expectedContext = `
|
|
760
667
|
Here is the user's editor context as a JSON object. This is for your information only.
|
|
761
668
|
\`\`\`json
|
|
@@ -779,13 +686,9 @@ ${JSON.stringify({
|
|
|
779
686
|
const mockChat = {
|
|
780
687
|
addHistory: vi.fn(),
|
|
781
688
|
getHistory: vi.fn().mockReturnValue([]),
|
|
689
|
+
getLastPromptTokenCount: vi.fn(),
|
|
782
690
|
};
|
|
783
691
|
client['chat'] = mockChat;
|
|
784
|
-
const mockGenerator = {
|
|
785
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
786
|
-
generateContent: mockGenerateContentFn,
|
|
787
|
-
};
|
|
788
|
-
client['contentGenerator'] = mockGenerator;
|
|
789
692
|
// Act
|
|
790
693
|
const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-id-1');
|
|
791
694
|
// Consume the stream manually to get the final return value.
|
|
@@ -801,6 +704,7 @@ ${JSON.stringify({
|
|
|
801
704
|
expect(finalResult).toBeInstanceOf(Turn);
|
|
802
705
|
});
|
|
803
706
|
it('should stop infinite loop after MAX_TURNS when nextSpeaker always returns model', async () => {
|
|
707
|
+
vi.spyOn(client['config'], 'getContinueOnFailedApiCall').mockReturnValue(true);
|
|
804
708
|
// Get the mocked checkNextSpeaker function and configure it to trigger infinite loop
|
|
805
709
|
const { checkNextSpeaker } = await import('../utils/nextSpeakerChecker.js');
|
|
806
710
|
const mockCheckNextSpeaker = vi.mocked(checkNextSpeaker);
|
|
@@ -816,13 +720,9 @@ ${JSON.stringify({
|
|
|
816
720
|
const mockChat = {
|
|
817
721
|
addHistory: vi.fn(),
|
|
818
722
|
getHistory: vi.fn().mockReturnValue([]),
|
|
723
|
+
getLastPromptTokenCount: vi.fn(),
|
|
819
724
|
};
|
|
820
725
|
client['chat'] = mockChat;
|
|
821
|
-
const mockGenerator = {
|
|
822
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
823
|
-
generateContent: mockGenerateContentFn,
|
|
824
|
-
};
|
|
825
|
-
client['contentGenerator'] = mockGenerator;
|
|
826
726
|
// Use a signal that never gets aborted
|
|
827
727
|
const abortController = new AbortController();
|
|
828
728
|
const signal = abortController.signal;
|
|
@@ -847,27 +747,10 @@ ${JSON.stringify({
|
|
|
847
747
|
}
|
|
848
748
|
// Assert
|
|
849
749
|
expect(finalResult).toBeInstanceOf(Turn);
|
|
850
|
-
// Debug: Check how many times checkNextSpeaker was called
|
|
851
|
-
const callCount = mockCheckNextSpeaker.mock.calls.length;
|
|
852
750
|
// If infinite loop protection is working, checkNextSpeaker should be called many times
|
|
853
751
|
// but stop at MAX_TURNS (100). Since each recursive call should trigger checkNextSpeaker,
|
|
854
752
|
// we expect it to be called multiple times before hitting the limit
|
|
855
753
|
expect(mockCheckNextSpeaker).toHaveBeenCalled();
|
|
856
|
-
// The test should demonstrate that the infinite loop protection works:
|
|
857
|
-
// - If checkNextSpeaker is called many times (close to MAX_TURNS), it shows the loop was happening
|
|
858
|
-
// - If it's only called once, the recursive behavior might not be triggered
|
|
859
|
-
if (callCount === 0) {
|
|
860
|
-
throw new Error('checkNextSpeaker was never called - the recursive condition was not met');
|
|
861
|
-
}
|
|
862
|
-
else if (callCount === 1) {
|
|
863
|
-
// This might be expected behavior if the turn has pending tool calls or other conditions prevent recursion
|
|
864
|
-
console.log('checkNextSpeaker called only once - no infinite loop occurred');
|
|
865
|
-
}
|
|
866
|
-
else {
|
|
867
|
-
console.log(`checkNextSpeaker called ${callCount} times - infinite loop protection worked`);
|
|
868
|
-
// If called multiple times, we expect it to be stopped before MAX_TURNS
|
|
869
|
-
expect(callCount).toBeLessThanOrEqual(100); // Should not exceed MAX_TURNS
|
|
870
|
-
}
|
|
871
754
|
// The stream should produce events and eventually terminate
|
|
872
755
|
expect(eventCount).toBeGreaterThanOrEqual(1);
|
|
873
756
|
expect(eventCount).toBeLessThan(200); // Should not exceed our safety limit
|
|
@@ -883,13 +766,9 @@ ${JSON.stringify({
|
|
|
883
766
|
const mockChat = {
|
|
884
767
|
addHistory: vi.fn(),
|
|
885
768
|
getHistory: vi.fn().mockReturnValue([]),
|
|
769
|
+
getLastPromptTokenCount: vi.fn(),
|
|
886
770
|
};
|
|
887
771
|
client['chat'] = mockChat;
|
|
888
|
-
const mockGenerator = {
|
|
889
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
890
|
-
generateContent: mockGenerateContentFn,
|
|
891
|
-
};
|
|
892
|
-
client['contentGenerator'] = mockGenerator;
|
|
893
772
|
// Act & Assert
|
|
894
773
|
// Run up to the limit
|
|
895
774
|
for (let i = 0; i < MAX_SESSION_TURNS; i++) {
|
|
@@ -926,13 +805,9 @@ ${JSON.stringify({
|
|
|
926
805
|
const mockChat = {
|
|
927
806
|
addHistory: vi.fn(),
|
|
928
807
|
getHistory: vi.fn().mockReturnValue([]),
|
|
808
|
+
getLastPromptTokenCount: vi.fn(),
|
|
929
809
|
};
|
|
930
810
|
client['chat'] = mockChat;
|
|
931
|
-
const mockGenerator = {
|
|
932
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
933
|
-
generateContent: mockGenerateContentFn,
|
|
934
|
-
};
|
|
935
|
-
client['contentGenerator'] = mockGenerator;
|
|
936
811
|
// Use a signal that never gets aborted
|
|
937
812
|
const abortController = new AbortController();
|
|
938
813
|
const signal = abortController.signal;
|
|
@@ -958,9 +833,8 @@ ${JSON.stringify({
|
|
|
958
833
|
}
|
|
959
834
|
}
|
|
960
835
|
}
|
|
961
|
-
catch (
|
|
836
|
+
catch (_) {
|
|
962
837
|
// If the test framework times out, that also demonstrates the infinite loop
|
|
963
|
-
console.error('Test timed out or errored:', error);
|
|
964
838
|
}
|
|
965
839
|
// Assert that the fix works - the loop should stop at MAX_TURNS
|
|
966
840
|
const callCount = mockCheckNextSpeaker.mock.calls.length;
|
|
@@ -968,8 +842,272 @@ ${JSON.stringify({
|
|
|
968
842
|
// the loop should stop at MAX_TURNS (100)
|
|
969
843
|
expect(callCount).toBeLessThanOrEqual(100); // Should not exceed MAX_TURNS
|
|
970
844
|
expect(eventCount).toBeLessThanOrEqual(200); // Should have reasonable number of events
|
|
971
|
-
|
|
972
|
-
|
|
845
|
+
});
|
|
846
|
+
it('should yield ContextWindowWillOverflow when the context window is about to overflow', async () => {
|
|
847
|
+
// Arrange
|
|
848
|
+
const MOCKED_TOKEN_LIMIT = 1000;
|
|
849
|
+
vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
|
|
850
|
+
// Set last prompt token count
|
|
851
|
+
const lastPromptTokenCount = 900;
|
|
852
|
+
const mockChat = {
|
|
853
|
+
getLastPromptTokenCount: vi.fn().mockReturnValue(lastPromptTokenCount),
|
|
854
|
+
getHistory: vi.fn().mockReturnValue([]),
|
|
855
|
+
};
|
|
856
|
+
client['chat'] = mockChat;
|
|
857
|
+
// Remaining = 100. Threshold (95%) = 95.
|
|
858
|
+
// We need a request > 95 tokens.
|
|
859
|
+
// A string of length 400 is roughly 100 tokens.
|
|
860
|
+
const longText = 'a'.repeat(400);
|
|
861
|
+
const request = [{ text: longText }];
|
|
862
|
+
const estimatedRequestTokenCount = Math.floor(JSON.stringify(request).length / 4);
|
|
863
|
+
const remainingTokenCount = MOCKED_TOKEN_LIMIT - lastPromptTokenCount;
|
|
864
|
+
// Mock tryCompressChat to not compress
|
|
865
|
+
vi.spyOn(client, 'tryCompressChat').mockResolvedValue({
|
|
866
|
+
originalTokenCount: lastPromptTokenCount,
|
|
867
|
+
newTokenCount: lastPromptTokenCount,
|
|
868
|
+
compressionStatus: CompressionStatus.NOOP,
|
|
869
|
+
});
|
|
870
|
+
// Act
|
|
871
|
+
const stream = client.sendMessageStream(request, new AbortController().signal, 'prompt-id-overflow');
|
|
872
|
+
const events = await fromAsync(stream);
|
|
873
|
+
// Assert
|
|
874
|
+
expect(events).toContainEqual({
|
|
875
|
+
type: GeminiEventType.ContextWindowWillOverflow,
|
|
876
|
+
value: {
|
|
877
|
+
estimatedRequestTokenCount,
|
|
878
|
+
remainingTokenCount,
|
|
879
|
+
},
|
|
880
|
+
});
|
|
881
|
+
// Ensure turn.run is not called
|
|
882
|
+
expect(mockTurnRunFn).not.toHaveBeenCalled();
|
|
883
|
+
});
|
|
884
|
+
it("should use the sticky model's token limit for the overflow check", async () => {
|
|
885
|
+
// Arrange
|
|
886
|
+
const STICKY_MODEL = 'gemini-1.5-flash';
|
|
887
|
+
const STICKY_MODEL_LIMIT = 1000;
|
|
888
|
+
const CONFIG_MODEL_LIMIT = 2000;
|
|
889
|
+
// Set up token limits
|
|
890
|
+
vi.mocked(tokenLimit).mockImplementation((model) => {
|
|
891
|
+
if (model === STICKY_MODEL)
|
|
892
|
+
return STICKY_MODEL_LIMIT;
|
|
893
|
+
return CONFIG_MODEL_LIMIT;
|
|
894
|
+
});
|
|
895
|
+
// Set the sticky model
|
|
896
|
+
client['currentSequenceModel'] = STICKY_MODEL;
|
|
897
|
+
// Set token count
|
|
898
|
+
const lastPromptTokenCount = 900;
|
|
899
|
+
const mockChat = {
|
|
900
|
+
getLastPromptTokenCount: vi.fn().mockReturnValue(lastPromptTokenCount),
|
|
901
|
+
getHistory: vi.fn().mockReturnValue([]),
|
|
902
|
+
};
|
|
903
|
+
client['chat'] = mockChat;
|
|
904
|
+
// Remaining (sticky) = 100. Threshold (95%) = 95.
|
|
905
|
+
// We need a request > 95 tokens.
|
|
906
|
+
const longText = 'a'.repeat(400);
|
|
907
|
+
const request = [{ text: longText }];
|
|
908
|
+
const estimatedRequestTokenCount = Math.floor(JSON.stringify(request).length / 4);
|
|
909
|
+
const remainingTokenCount = STICKY_MODEL_LIMIT - lastPromptTokenCount;
|
|
910
|
+
vi.spyOn(client, 'tryCompressChat').mockResolvedValue({
|
|
911
|
+
originalTokenCount: lastPromptTokenCount,
|
|
912
|
+
newTokenCount: lastPromptTokenCount,
|
|
913
|
+
compressionStatus: CompressionStatus.NOOP,
|
|
914
|
+
});
|
|
915
|
+
// Act
|
|
916
|
+
const stream = client.sendMessageStream(request, new AbortController().signal, 'test-session-id');
|
|
917
|
+
const events = await fromAsync(stream);
|
|
918
|
+
// Assert
|
|
919
|
+
// Should overflow based on the sticky model's limit
|
|
920
|
+
expect(events).toContainEqual({
|
|
921
|
+
type: GeminiEventType.ContextWindowWillOverflow,
|
|
922
|
+
value: {
|
|
923
|
+
estimatedRequestTokenCount,
|
|
924
|
+
remainingTokenCount,
|
|
925
|
+
},
|
|
926
|
+
});
|
|
927
|
+
expect(tokenLimit).toHaveBeenCalledWith(STICKY_MODEL);
|
|
928
|
+
expect(mockTurnRunFn).not.toHaveBeenCalled();
|
|
929
|
+
});
|
|
930
|
+
describe('Model Routing', () => {
|
|
931
|
+
let mockRouterService;
|
|
932
|
+
beforeEach(() => {
|
|
933
|
+
mockRouterService = {
|
|
934
|
+
route: vi
|
|
935
|
+
.fn()
|
|
936
|
+
.mockResolvedValue({ model: 'routed-model', reason: 'test' }),
|
|
937
|
+
};
|
|
938
|
+
vi.mocked(mockConfig.getModelRouterService).mockReturnValue(mockRouterService);
|
|
939
|
+
mockTurnRunFn.mockReturnValue((async function* () {
|
|
940
|
+
yield { type: 'content', value: 'Hello' };
|
|
941
|
+
})());
|
|
942
|
+
const mockChat = {
|
|
943
|
+
addHistory: vi.fn(),
|
|
944
|
+
getHistory: vi.fn().mockReturnValue([]),
|
|
945
|
+
getLastPromptTokenCount: vi.fn(),
|
|
946
|
+
};
|
|
947
|
+
client['chat'] = mockChat;
|
|
948
|
+
});
|
|
949
|
+
it('should use the model router service to select a model on the first turn', async () => {
|
|
950
|
+
const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-1');
|
|
951
|
+
await fromAsync(stream); // consume stream
|
|
952
|
+
expect(mockConfig.getModelRouterService).toHaveBeenCalled();
|
|
953
|
+
expect(mockRouterService.route).toHaveBeenCalled();
|
|
954
|
+
expect(mockTurnRunFn).toHaveBeenCalledWith({ model: 'routed-model' }, [{ text: 'Hi' }], expect.any(AbortSignal));
|
|
955
|
+
});
|
|
956
|
+
it('should use the same model for subsequent turns in the same prompt (stickiness)', async () => {
|
|
957
|
+
// First turn
|
|
958
|
+
let stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-1');
|
|
959
|
+
await fromAsync(stream);
|
|
960
|
+
expect(mockRouterService.route).toHaveBeenCalledTimes(1);
|
|
961
|
+
expect(mockTurnRunFn).toHaveBeenCalledWith({ model: 'routed-model' }, [{ text: 'Hi' }], expect.any(AbortSignal));
|
|
962
|
+
// Second turn
|
|
963
|
+
stream = client.sendMessageStream([{ text: 'Continue' }], new AbortController().signal, 'prompt-1');
|
|
964
|
+
await fromAsync(stream);
|
|
965
|
+
// Router should not be called again
|
|
966
|
+
expect(mockRouterService.route).toHaveBeenCalledTimes(1);
|
|
967
|
+
// Should stick to the first model
|
|
968
|
+
expect(mockTurnRunFn).toHaveBeenCalledWith({ model: 'routed-model' }, [{ text: 'Continue' }], expect.any(AbortSignal));
|
|
969
|
+
});
|
|
970
|
+
it('should reset the sticky model and re-route when the prompt_id changes', async () => {
|
|
971
|
+
// First prompt
|
|
972
|
+
let stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-1');
|
|
973
|
+
await fromAsync(stream);
|
|
974
|
+
expect(mockRouterService.route).toHaveBeenCalledTimes(1);
|
|
975
|
+
expect(mockTurnRunFn).toHaveBeenCalledWith({ model: 'routed-model' }, [{ text: 'Hi' }], expect.any(AbortSignal));
|
|
976
|
+
// New prompt
|
|
977
|
+
mockRouterService.route.mockResolvedValue({
|
|
978
|
+
model: 'new-routed-model',
|
|
979
|
+
reason: 'test',
|
|
980
|
+
});
|
|
981
|
+
stream = client.sendMessageStream([{ text: 'A new topic' }], new AbortController().signal, 'prompt-2');
|
|
982
|
+
await fromAsync(stream);
|
|
983
|
+
// Router should be called again for the new prompt
|
|
984
|
+
expect(mockRouterService.route).toHaveBeenCalledTimes(2);
|
|
985
|
+
// Should use the newly routed model
|
|
986
|
+
expect(mockTurnRunFn).toHaveBeenCalledWith({ model: 'new-routed-model' }, [{ text: 'A new topic' }], expect.any(AbortSignal));
|
|
987
|
+
});
|
|
988
|
+
it('should use the fallback model and bypass routing when in fallback mode', async () => {
|
|
989
|
+
vi.mocked(mockConfig.isInFallbackMode).mockReturnValue(true);
|
|
990
|
+
mockRouterService.route.mockResolvedValue({
|
|
991
|
+
model: DEFAULT_GEMINI_FLASH_MODEL,
|
|
992
|
+
reason: 'fallback',
|
|
993
|
+
});
|
|
994
|
+
const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-1');
|
|
995
|
+
await fromAsync(stream);
|
|
996
|
+
expect(mockTurnRunFn).toHaveBeenCalledWith({ model: DEFAULT_GEMINI_FLASH_MODEL }, [{ text: 'Hi' }], expect.any(AbortSignal));
|
|
997
|
+
});
|
|
998
|
+
it('should stick to the fallback model for the entire sequence even if fallback mode ends', async () => {
|
|
999
|
+
// Start the sequence in fallback mode
|
|
1000
|
+
vi.mocked(mockConfig.isInFallbackMode).mockReturnValue(true);
|
|
1001
|
+
mockRouterService.route.mockResolvedValue({
|
|
1002
|
+
model: DEFAULT_GEMINI_FLASH_MODEL,
|
|
1003
|
+
reason: 'fallback',
|
|
1004
|
+
});
|
|
1005
|
+
let stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-fallback-stickiness');
|
|
1006
|
+
await fromAsync(stream);
|
|
1007
|
+
// First call should use fallback model
|
|
1008
|
+
expect(mockTurnRunFn).toHaveBeenCalledWith({ model: DEFAULT_GEMINI_FLASH_MODEL }, [{ text: 'Hi' }], expect.any(AbortSignal));
|
|
1009
|
+
// End fallback mode
|
|
1010
|
+
vi.mocked(mockConfig.isInFallbackMode).mockReturnValue(false);
|
|
1011
|
+
// Second call in the same sequence
|
|
1012
|
+
stream = client.sendMessageStream([{ text: 'Continue' }], new AbortController().signal, 'prompt-fallback-stickiness');
|
|
1013
|
+
await fromAsync(stream);
|
|
1014
|
+
// Router should still not be called, and it should stick to the fallback model
|
|
1015
|
+
expect(mockTurnRunFn).toHaveBeenCalledTimes(2); // Ensure it was called again
|
|
1016
|
+
expect(mockTurnRunFn).toHaveBeenLastCalledWith({ model: DEFAULT_GEMINI_FLASH_MODEL }, // Still the fallback model
|
|
1017
|
+
[{ text: 'Continue' }], expect.any(AbortSignal));
|
|
1018
|
+
});
|
|
1019
|
+
});
|
|
1020
|
+
it('should recursively call sendMessageStream with "Please continue." when InvalidStream event is received', async () => {
|
|
1021
|
+
vi.spyOn(client['config'], 'getContinueOnFailedApiCall').mockReturnValue(true);
|
|
1022
|
+
// Arrange
|
|
1023
|
+
const mockStream1 = (async function* () {
|
|
1024
|
+
yield { type: GeminiEventType.InvalidStream };
|
|
1025
|
+
})();
|
|
1026
|
+
const mockStream2 = (async function* () {
|
|
1027
|
+
yield { type: GeminiEventType.Content, value: 'Continued content' };
|
|
1028
|
+
})();
|
|
1029
|
+
mockTurnRunFn
|
|
1030
|
+
.mockReturnValueOnce(mockStream1)
|
|
1031
|
+
.mockReturnValueOnce(mockStream2);
|
|
1032
|
+
const mockChat = {
|
|
1033
|
+
addHistory: vi.fn(),
|
|
1034
|
+
getHistory: vi.fn().mockReturnValue([]),
|
|
1035
|
+
getLastPromptTokenCount: vi.fn(),
|
|
1036
|
+
};
|
|
1037
|
+
client['chat'] = mockChat;
|
|
1038
|
+
const initialRequest = [{ text: 'Hi' }];
|
|
1039
|
+
const promptId = 'prompt-id-invalid-stream';
|
|
1040
|
+
const signal = new AbortController().signal;
|
|
1041
|
+
// Act
|
|
1042
|
+
const stream = client.sendMessageStream(initialRequest, signal, promptId);
|
|
1043
|
+
const events = await fromAsync(stream);
|
|
1044
|
+
// Assert
|
|
1045
|
+
expect(events).toEqual([
|
|
1046
|
+
{ type: GeminiEventType.ModelInfo, value: 'default-routed-model' },
|
|
1047
|
+
{ type: GeminiEventType.InvalidStream },
|
|
1048
|
+
{ type: GeminiEventType.Content, value: 'Continued content' },
|
|
1049
|
+
]);
|
|
1050
|
+
// Verify that turn.run was called twice
|
|
1051
|
+
expect(mockTurnRunFn).toHaveBeenCalledTimes(2);
|
|
1052
|
+
// First call with original request
|
|
1053
|
+
expect(mockTurnRunFn).toHaveBeenNthCalledWith(1, { model: 'default-routed-model' }, initialRequest, expect.any(AbortSignal));
|
|
1054
|
+
// Second call with "Please continue."
|
|
1055
|
+
expect(mockTurnRunFn).toHaveBeenNthCalledWith(2, { model: 'default-routed-model' }, [{ text: 'System: Please continue.' }], expect.any(AbortSignal));
|
|
1056
|
+
});
|
|
1057
|
+
it('should not recursively call sendMessageStream with "Please continue." when InvalidStream event is received and flag is false', async () => {
|
|
1058
|
+
vi.spyOn(client['config'], 'getContinueOnFailedApiCall').mockReturnValue(false);
|
|
1059
|
+
// Arrange
|
|
1060
|
+
const mockStream1 = (async function* () {
|
|
1061
|
+
yield { type: GeminiEventType.InvalidStream };
|
|
1062
|
+
})();
|
|
1063
|
+
mockTurnRunFn.mockReturnValueOnce(mockStream1);
|
|
1064
|
+
const mockChat = {
|
|
1065
|
+
addHistory: vi.fn(),
|
|
1066
|
+
getHistory: vi.fn().mockReturnValue([]),
|
|
1067
|
+
getLastPromptTokenCount: vi.fn(),
|
|
1068
|
+
};
|
|
1069
|
+
client['chat'] = mockChat;
|
|
1070
|
+
const initialRequest = [{ text: 'Hi' }];
|
|
1071
|
+
const promptId = 'prompt-id-invalid-stream';
|
|
1072
|
+
const signal = new AbortController().signal;
|
|
1073
|
+
// Act
|
|
1074
|
+
const stream = client.sendMessageStream(initialRequest, signal, promptId);
|
|
1075
|
+
const events = await fromAsync(stream);
|
|
1076
|
+
// Assert
|
|
1077
|
+
expect(events).toEqual([
|
|
1078
|
+
{ type: GeminiEventType.ModelInfo, value: 'default-routed-model' },
|
|
1079
|
+
{ type: GeminiEventType.InvalidStream },
|
|
1080
|
+
]);
|
|
1081
|
+
// Verify that turn.run was called only once
|
|
1082
|
+
expect(mockTurnRunFn).toHaveBeenCalledTimes(1);
|
|
1083
|
+
});
|
|
1084
|
+
it('should stop recursing after one retry when InvalidStream events are repeatedly received', async () => {
|
|
1085
|
+
vi.spyOn(client['config'], 'getContinueOnFailedApiCall').mockReturnValue(true);
|
|
1086
|
+
// Arrange
|
|
1087
|
+
// Always return a new invalid stream
|
|
1088
|
+
mockTurnRunFn.mockImplementation(() => (async function* () {
|
|
1089
|
+
yield { type: GeminiEventType.InvalidStream };
|
|
1090
|
+
})());
|
|
1091
|
+
const mockChat = {
|
|
1092
|
+
addHistory: vi.fn(),
|
|
1093
|
+
getHistory: vi.fn().mockReturnValue([]),
|
|
1094
|
+
getLastPromptTokenCount: vi.fn(),
|
|
1095
|
+
};
|
|
1096
|
+
client['chat'] = mockChat;
|
|
1097
|
+
const initialRequest = [{ text: 'Hi' }];
|
|
1098
|
+
const promptId = 'prompt-id-infinite-invalid-stream';
|
|
1099
|
+
const signal = new AbortController().signal;
|
|
1100
|
+
// Act
|
|
1101
|
+
const stream = client.sendMessageStream(initialRequest, signal, promptId);
|
|
1102
|
+
const events = await fromAsync(stream);
|
|
1103
|
+
// Assert
|
|
1104
|
+
// We expect 3 events (model_info + original + 1 retry)
|
|
1105
|
+
expect(events.length).toBe(3);
|
|
1106
|
+
expect(events
|
|
1107
|
+
.filter((e) => e.type !== GeminiEventType.ModelInfo)
|
|
1108
|
+
.every((e) => e.type === GeminiEventType.InvalidStream)).toBe(true);
|
|
1109
|
+
// Verify that turn.run was called twice
|
|
1110
|
+
expect(mockTurnRunFn).toHaveBeenCalledTimes(2);
|
|
973
1111
|
});
|
|
974
1112
|
describe('Editor context delta', () => {
|
|
975
1113
|
const mockStream = (async function* () {
|
|
@@ -977,26 +1115,25 @@ ${JSON.stringify({
|
|
|
977
1115
|
})();
|
|
978
1116
|
beforeEach(() => {
|
|
979
1117
|
client['forceFullIdeContext'] = false; // Reset before each delta test
|
|
980
|
-
vi.spyOn(client, 'tryCompressChat').mockResolvedValue(
|
|
1118
|
+
vi.spyOn(client, 'tryCompressChat').mockResolvedValue({
|
|
1119
|
+
originalTokenCount: 0,
|
|
1120
|
+
newTokenCount: 0,
|
|
1121
|
+
compressionStatus: CompressionStatus.COMPRESSED,
|
|
1122
|
+
});
|
|
981
1123
|
vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
|
|
982
1124
|
mockTurnRunFn.mockReturnValue(mockStream);
|
|
983
1125
|
const mockChat = {
|
|
984
1126
|
addHistory: vi.fn(),
|
|
985
1127
|
setHistory: vi.fn(),
|
|
986
|
-
sendMessage: vi.fn().mockResolvedValue({ text: 'summary' }),
|
|
987
1128
|
// Assume history is not empty for delta checks
|
|
988
1129
|
getHistory: vi
|
|
989
1130
|
.fn()
|
|
990
1131
|
.mockReturnValue([
|
|
991
1132
|
{ role: 'user', parts: [{ text: 'previous message' }] },
|
|
992
1133
|
]),
|
|
1134
|
+
getLastPromptTokenCount: vi.fn(),
|
|
993
1135
|
};
|
|
994
1136
|
client['chat'] = mockChat;
|
|
995
|
-
const mockGenerator = {
|
|
996
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
997
|
-
generateContent: mockGenerateContentFn,
|
|
998
|
-
};
|
|
999
|
-
client['contentGenerator'] = mockGenerator;
|
|
1000
1137
|
});
|
|
1001
1138
|
const testCases = [
|
|
1002
1139
|
{
|
|
@@ -1112,10 +1249,14 @@ ${JSON.stringify({
|
|
|
1112
1249
|
},
|
|
1113
1250
|
};
|
|
1114
1251
|
// Setup current context
|
|
1115
|
-
vi.mocked(
|
|
1252
|
+
vi.mocked(ideContextStore.get).mockReturnValue({
|
|
1116
1253
|
workspaceState: {
|
|
1117
1254
|
openFiles: [
|
|
1118
|
-
{
|
|
1255
|
+
{
|
|
1256
|
+
...currentActiveFile,
|
|
1257
|
+
isActive: true,
|
|
1258
|
+
timestamp: Date.now(),
|
|
1259
|
+
},
|
|
1119
1260
|
],
|
|
1120
1261
|
},
|
|
1121
1262
|
});
|
|
@@ -1158,7 +1299,7 @@ ${JSON.stringify({
|
|
|
1158
1299
|
},
|
|
1159
1300
|
};
|
|
1160
1301
|
// Setup current context (same as previous)
|
|
1161
|
-
vi.mocked(
|
|
1302
|
+
vi.mocked(ideContextStore.get).mockReturnValue({
|
|
1162
1303
|
workspaceState: {
|
|
1163
1304
|
openFiles: [
|
|
1164
1305
|
{ ...activeFile, isActive: true, timestamp: Date.now() },
|
|
@@ -1190,7 +1331,11 @@ ${JSON.stringify({
|
|
|
1190
1331
|
describe('IDE context with pending tool calls', () => {
|
|
1191
1332
|
let mockChat;
|
|
1192
1333
|
beforeEach(() => {
|
|
1193
|
-
vi.spyOn(client, 'tryCompressChat').mockResolvedValue(
|
|
1334
|
+
vi.spyOn(client, 'tryCompressChat').mockResolvedValue({
|
|
1335
|
+
originalTokenCount: 0,
|
|
1336
|
+
newTokenCount: 0,
|
|
1337
|
+
compressionStatus: CompressionStatus.COMPRESSED,
|
|
1338
|
+
});
|
|
1194
1339
|
const mockStream = (async function* () {
|
|
1195
1340
|
yield { type: 'content', value: 'response' };
|
|
1196
1341
|
})();
|
|
@@ -1199,15 +1344,11 @@ ${JSON.stringify({
|
|
|
1199
1344
|
addHistory: vi.fn(),
|
|
1200
1345
|
getHistory: vi.fn().mockReturnValue([]), // Default empty history
|
|
1201
1346
|
setHistory: vi.fn(),
|
|
1202
|
-
|
|
1347
|
+
getLastPromptTokenCount: vi.fn(),
|
|
1203
1348
|
};
|
|
1204
1349
|
client['chat'] = mockChat;
|
|
1205
|
-
const mockGenerator = {
|
|
1206
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
1207
|
-
};
|
|
1208
|
-
client['contentGenerator'] = mockGenerator;
|
|
1209
1350
|
vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
|
|
1210
|
-
vi.mocked(
|
|
1351
|
+
vi.mocked(ideContextStore.get).mockReturnValue({
|
|
1211
1352
|
workspaceState: {
|
|
1212
1353
|
openFiles: [{ path: '/path/to/file.ts', timestamp: Date.now() }],
|
|
1213
1354
|
},
|
|
@@ -1283,7 +1424,7 @@ ${JSON.stringify({
|
|
|
1283
1424
|
openFiles: [{ path: '/path/to/fileA.ts', timestamp: Date.now() }],
|
|
1284
1425
|
},
|
|
1285
1426
|
};
|
|
1286
|
-
vi.mocked(
|
|
1427
|
+
vi.mocked(ideContextStore.get).mockReturnValue(initialIdeContext);
|
|
1287
1428
|
// Act: Send the tool response
|
|
1288
1429
|
let stream = client.sendMessageStream([
|
|
1289
1430
|
{
|
|
@@ -1329,7 +1470,7 @@ ${JSON.stringify({
|
|
|
1329
1470
|
openFiles: [{ path: '/path/to/fileB.ts', timestamp: Date.now() }],
|
|
1330
1471
|
},
|
|
1331
1472
|
};
|
|
1332
|
-
vi.mocked(
|
|
1473
|
+
vi.mocked(ideContextStore.get).mockReturnValue(newIdeContext);
|
|
1333
1474
|
// Act: Send a new, regular user message
|
|
1334
1475
|
stream = client.sendMessageStream([{ text: 'Thanks!' }], new AbortController().signal, 'prompt-id-final');
|
|
1335
1476
|
for await (const _ of stream) {
|
|
@@ -1359,7 +1500,7 @@ ${JSON.stringify({
|
|
|
1359
1500
|
],
|
|
1360
1501
|
},
|
|
1361
1502
|
};
|
|
1362
|
-
vi.mocked(
|
|
1503
|
+
vi.mocked(ideContextStore.get).mockReturnValue(contextA);
|
|
1363
1504
|
// Act: Send a regular message to establish the initial context
|
|
1364
1505
|
let stream = client.sendMessageStream([{ text: 'Initial message' }], new AbortController().signal, 'prompt-id-initial');
|
|
1365
1506
|
for await (const _ of stream) {
|
|
@@ -1392,7 +1533,7 @@ ${JSON.stringify({
|
|
|
1392
1533
|
],
|
|
1393
1534
|
},
|
|
1394
1535
|
};
|
|
1395
|
-
vi.mocked(
|
|
1536
|
+
vi.mocked(ideContextStore.get).mockReturnValue(contextB);
|
|
1396
1537
|
// Act: Send the tool response
|
|
1397
1538
|
stream = client.sendMessageStream([
|
|
1398
1539
|
{
|
|
@@ -1437,7 +1578,7 @@ ${JSON.stringify({
|
|
|
1437
1578
|
],
|
|
1438
1579
|
},
|
|
1439
1580
|
};
|
|
1440
|
-
vi.mocked(
|
|
1581
|
+
vi.mocked(ideContextStore.get).mockReturnValue(contextC);
|
|
1441
1582
|
// Act: Send a new, regular user message
|
|
1442
1583
|
stream = client.sendMessageStream([{ text: 'Thanks!' }], new AbortController().signal, 'prompt-id-final');
|
|
1443
1584
|
for await (const _ of stream) {
|
|
@@ -1453,150 +1594,131 @@ ${JSON.stringify({
|
|
|
1453
1594
|
expect(JSON.stringify(finalCall)).toContain('fileC.ts');
|
|
1454
1595
|
});
|
|
1455
1596
|
});
|
|
1597
|
+
it('should not call checkNextSpeaker when turn.run() yields an error', async () => {
|
|
1598
|
+
// Arrange
|
|
1599
|
+
const { checkNextSpeaker } = await import('../utils/nextSpeakerChecker.js');
|
|
1600
|
+
const mockCheckNextSpeaker = vi.mocked(checkNextSpeaker);
|
|
1601
|
+
const mockStream = (async function* () {
|
|
1602
|
+
yield {
|
|
1603
|
+
type: GeminiEventType.Error,
|
|
1604
|
+
value: { error: { message: 'test error' } },
|
|
1605
|
+
};
|
|
1606
|
+
})();
|
|
1607
|
+
mockTurnRunFn.mockReturnValue(mockStream);
|
|
1608
|
+
const mockChat = {
|
|
1609
|
+
addHistory: vi.fn(),
|
|
1610
|
+
getHistory: vi.fn().mockReturnValue([]),
|
|
1611
|
+
getLastPromptTokenCount: vi.fn(),
|
|
1612
|
+
};
|
|
1613
|
+
client['chat'] = mockChat;
|
|
1614
|
+
// Act
|
|
1615
|
+
const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-id-error');
|
|
1616
|
+
for await (const _ of stream) {
|
|
1617
|
+
// consume stream
|
|
1618
|
+
}
|
|
1619
|
+
// Assert
|
|
1620
|
+
expect(mockCheckNextSpeaker).not.toHaveBeenCalled();
|
|
1621
|
+
});
|
|
1622
|
+
it('should not call checkNextSpeaker when turn.run() yields a value then an error', async () => {
|
|
1623
|
+
// Arrange
|
|
1624
|
+
const { checkNextSpeaker } = await import('../utils/nextSpeakerChecker.js');
|
|
1625
|
+
const mockCheckNextSpeaker = vi.mocked(checkNextSpeaker);
|
|
1626
|
+
const mockStream = (async function* () {
|
|
1627
|
+
yield { type: GeminiEventType.Content, value: 'some content' };
|
|
1628
|
+
yield {
|
|
1629
|
+
type: GeminiEventType.Error,
|
|
1630
|
+
value: { error: { message: 'test error' } },
|
|
1631
|
+
};
|
|
1632
|
+
})();
|
|
1633
|
+
mockTurnRunFn.mockReturnValue(mockStream);
|
|
1634
|
+
const mockChat = {
|
|
1635
|
+
addHistory: vi.fn(),
|
|
1636
|
+
getHistory: vi.fn().mockReturnValue([]),
|
|
1637
|
+
getLastPromptTokenCount: vi.fn(),
|
|
1638
|
+
};
|
|
1639
|
+
client['chat'] = mockChat;
|
|
1640
|
+
// Act
|
|
1641
|
+
const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-id-error');
|
|
1642
|
+
for await (const _ of stream) {
|
|
1643
|
+
// consume stream
|
|
1644
|
+
}
|
|
1645
|
+
// Assert
|
|
1646
|
+
expect(mockCheckNextSpeaker).not.toHaveBeenCalled();
|
|
1647
|
+
});
|
|
1648
|
+
it('should abort linked signal when loop is detected', async () => {
|
|
1649
|
+
// Arrange
|
|
1650
|
+
vi.spyOn(client['loopDetector'], 'turnStarted').mockResolvedValue(false);
|
|
1651
|
+
vi.spyOn(client['loopDetector'], 'addAndCheck')
|
|
1652
|
+
.mockReturnValueOnce(false)
|
|
1653
|
+
.mockReturnValueOnce(true);
|
|
1654
|
+
let capturedSignal;
|
|
1655
|
+
mockTurnRunFn.mockImplementation((_modelConfigKey, _request, signal) => {
|
|
1656
|
+
capturedSignal = signal;
|
|
1657
|
+
return (async function* () {
|
|
1658
|
+
yield { type: 'content', value: 'First event' };
|
|
1659
|
+
yield { type: 'content', value: 'Second event' };
|
|
1660
|
+
})();
|
|
1661
|
+
});
|
|
1662
|
+
const mockChat = {
|
|
1663
|
+
addHistory: vi.fn(),
|
|
1664
|
+
getHistory: vi.fn().mockReturnValue([]),
|
|
1665
|
+
getLastPromptTokenCount: vi.fn(),
|
|
1666
|
+
};
|
|
1667
|
+
client['chat'] = mockChat;
|
|
1668
|
+
// Act
|
|
1669
|
+
const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-id-loop');
|
|
1670
|
+
const events = [];
|
|
1671
|
+
for await (const event of stream) {
|
|
1672
|
+
events.push(event);
|
|
1673
|
+
}
|
|
1674
|
+
// Assert
|
|
1675
|
+
expect(events).toContainEqual({ type: GeminiEventType.LoopDetected });
|
|
1676
|
+
expect(capturedSignal.aborted).toBe(true);
|
|
1677
|
+
});
|
|
1456
1678
|
});
|
|
1457
1679
|
describe('generateContent', () => {
|
|
1680
|
+
it('should call generateContent with the correct parameters', async () => {
|
|
1681
|
+
const contents = [{ role: 'user', parts: [{ text: 'hello' }] }];
|
|
1682
|
+
const abortSignal = new AbortController().signal;
|
|
1683
|
+
await client.generateContent({ model: DEFAULT_GEMINI_FLASH_MODEL }, contents, abortSignal);
|
|
1684
|
+
expect(mockContentGenerator.generateContent).toHaveBeenCalledWith({
|
|
1685
|
+
model: DEFAULT_GEMINI_FLASH_MODEL,
|
|
1686
|
+
config: {
|
|
1687
|
+
abortSignal,
|
|
1688
|
+
systemInstruction: getCoreSystemPrompt({}, ''),
|
|
1689
|
+
temperature: 0,
|
|
1690
|
+
topP: 1,
|
|
1691
|
+
},
|
|
1692
|
+
contents,
|
|
1693
|
+
}, 'test-session-id');
|
|
1694
|
+
});
|
|
1458
1695
|
it('should use current model from config for content generation', async () => {
|
|
1459
1696
|
const initialModel = client['config'].getModel();
|
|
1460
1697
|
const contents = [{ role: 'user', parts: [{ text: 'test' }] }];
|
|
1461
1698
|
const currentModel = initialModel + '-changed';
|
|
1462
1699
|
vi.spyOn(client['config'], 'getModel').mockReturnValueOnce(currentModel);
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
generateContent: mockGenerateContentFn,
|
|
1466
|
-
};
|
|
1467
|
-
client['contentGenerator'] = mockGenerator;
|
|
1468
|
-
await client.generateContent(contents, {}, new AbortController().signal);
|
|
1469
|
-
expect(mockGenerateContentFn).not.toHaveBeenCalledWith({
|
|
1700
|
+
await client.generateContent({ model: DEFAULT_GEMINI_FLASH_MODEL }, contents, new AbortController().signal);
|
|
1701
|
+
expect(mockContentGenerator.generateContent).not.toHaveBeenCalledWith({
|
|
1470
1702
|
model: initialModel,
|
|
1471
1703
|
config: expect.any(Object),
|
|
1472
1704
|
contents,
|
|
1473
1705
|
});
|
|
1474
|
-
expect(
|
|
1475
|
-
model:
|
|
1706
|
+
expect(mockContentGenerator.generateContent).toHaveBeenCalledWith({
|
|
1707
|
+
model: DEFAULT_GEMINI_FLASH_MODEL,
|
|
1476
1708
|
config: expect.any(Object),
|
|
1477
1709
|
contents,
|
|
1478
1710
|
}, 'test-session-id');
|
|
1479
1711
|
});
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
const
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
{ role: 'user', parts: [{ text: 'Long conversation' }] },
|
|
1491
|
-
{ role: 'model', parts: [{ text: 'Long response' }] },
|
|
1492
|
-
];
|
|
1493
|
-
const mockChat = {
|
|
1494
|
-
getHistory: vi.fn().mockReturnValue(mockChatHistory),
|
|
1495
|
-
setHistory: vi.fn(),
|
|
1496
|
-
sendMessage: mockSendMessage,
|
|
1497
|
-
};
|
|
1498
|
-
const mockGenerator = {
|
|
1499
|
-
countTokens: mockCountTokens,
|
|
1500
|
-
};
|
|
1501
|
-
// mock the model has been changed between calls of `countTokens`
|
|
1502
|
-
const firstCurrentModel = initialModel + '-changed-1';
|
|
1503
|
-
const secondCurrentModel = initialModel + '-changed-2';
|
|
1504
|
-
vi.spyOn(client['config'], 'getModel')
|
|
1505
|
-
.mockReturnValueOnce(firstCurrentModel)
|
|
1506
|
-
.mockReturnValueOnce(secondCurrentModel);
|
|
1507
|
-
client['chat'] = mockChat;
|
|
1508
|
-
client['contentGenerator'] = mockGenerator;
|
|
1509
|
-
client['startChat'] = vi.fn().mockResolvedValue(mockChat);
|
|
1510
|
-
const result = await client.tryCompressChat('prompt-id-4', true);
|
|
1511
|
-
expect(mockCountTokens).toHaveBeenCalledTimes(2);
|
|
1512
|
-
expect(mockCountTokens).toHaveBeenNthCalledWith(1, {
|
|
1513
|
-
model: firstCurrentModel,
|
|
1514
|
-
contents: mockChatHistory,
|
|
1515
|
-
});
|
|
1516
|
-
expect(mockCountTokens).toHaveBeenNthCalledWith(2, {
|
|
1517
|
-
model: secondCurrentModel,
|
|
1518
|
-
contents: expect.any(Array),
|
|
1519
|
-
});
|
|
1520
|
-
expect(result).toEqual({
|
|
1521
|
-
originalTokenCount: 100000,
|
|
1522
|
-
newTokenCount: 5000,
|
|
1523
|
-
});
|
|
1524
|
-
});
|
|
1525
|
-
});
|
|
1526
|
-
describe('handleFlashFallback', () => {
|
|
1527
|
-
it('should use current model from config when checking for fallback', async () => {
|
|
1528
|
-
const initialModel = client['config'].getModel();
|
|
1529
|
-
const fallbackModel = DEFAULT_GEMINI_FLASH_MODEL;
|
|
1530
|
-
// mock config been changed
|
|
1531
|
-
const currentModel = initialModel + '-changed';
|
|
1532
|
-
const getModelSpy = vi.spyOn(client['config'], 'getModel');
|
|
1533
|
-
getModelSpy.mockReturnValue(currentModel);
|
|
1534
|
-
const mockFallbackHandler = vi.fn().mockResolvedValue(true);
|
|
1535
|
-
client['config'].flashFallbackHandler = mockFallbackHandler;
|
|
1536
|
-
client['config'].setModel = vi.fn();
|
|
1537
|
-
const result = await client['handleFlashFallback'](AuthType.LOGIN_WITH_GOOGLE);
|
|
1538
|
-
expect(result).toBe(fallbackModel);
|
|
1539
|
-
expect(mockFallbackHandler).toHaveBeenCalledWith(currentModel, fallbackModel, undefined);
|
|
1540
|
-
});
|
|
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);
|
|
1712
|
+
it('should use the Flash model when fallback mode is active', async () => {
|
|
1713
|
+
const contents = [{ role: 'user', parts: [{ text: 'hello' }] }];
|
|
1714
|
+
const abortSignal = new AbortController().signal;
|
|
1715
|
+
const requestedModel = 'gemini-2.5-pro'; // A non-flash model
|
|
1716
|
+
// Mock config to be in fallback mode
|
|
1717
|
+
vi.spyOn(client['config'], 'isInFallbackMode').mockReturnValue(true);
|
|
1718
|
+
await client.generateContent({ model: requestedModel }, contents, abortSignal);
|
|
1719
|
+
expect(mockGenerateContentFn).toHaveBeenCalledWith(expect.objectContaining({
|
|
1720
|
+
model: DEFAULT_GEMINI_FLASH_MODEL,
|
|
1721
|
+
}), 'test-session-id');
|
|
1600
1722
|
});
|
|
1601
1723
|
});
|
|
1602
1724
|
});
|