@vybestack/llxprt-code 0.1.23 → 0.2.2-nightly.250908.7b895396
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/README.md +21 -17
- package/dist/package.json +4 -3
- package/dist/src/auth/anthropic-oauth-provider.d.ts +50 -3
- package/dist/src/auth/anthropic-oauth-provider.js +287 -63
- package/dist/src/auth/anthropic-oauth-provider.js.map +1 -1
- package/dist/src/auth/gemini-oauth-provider.d.ts +34 -3
- package/dist/src/auth/gemini-oauth-provider.js +308 -14
- package/dist/src/auth/gemini-oauth-provider.js.map +1 -1
- package/dist/src/auth/migration.d.ts +26 -0
- package/dist/src/auth/migration.js +54 -0
- package/dist/src/auth/migration.js.map +1 -0
- package/dist/src/auth/oauth-manager.d.ts +24 -0
- package/dist/src/auth/oauth-manager.js +179 -14
- package/dist/src/auth/oauth-manager.js.map +1 -1
- package/dist/src/auth/oauth-manager.spec.js +10 -8
- package/dist/src/auth/oauth-manager.spec.js.map +1 -1
- package/dist/src/auth/qwen-oauth-provider.d.ts +59 -3
- package/dist/src/auth/qwen-oauth-provider.js +263 -41
- package/dist/src/auth/qwen-oauth-provider.js.map +1 -1
- package/dist/src/config/config.js +11 -10
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/extension.d.ts +1 -1
- package/dist/src/config/extension.js +1 -1
- package/dist/src/gemini.js +33 -11
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +1 -1
- package/dist/src/generated/git-commit.js +1 -1
- package/dist/src/integration-tests/base-url-behavior.integration.test.js +4 -1
- package/dist/src/integration-tests/base-url-behavior.integration.test.js.map +1 -1
- package/dist/src/integration-tests/compression-settings-apply.integration.test.js +85 -332
- package/dist/src/integration-tests/compression-settings-apply.integration.test.js.map +1 -1
- package/dist/src/integration-tests/ephemeral-settings.integration.test.js +5 -16
- package/dist/src/integration-tests/ephemeral-settings.integration.test.js.map +1 -1
- package/dist/src/integration-tests/model-params-isolation.integration.test.js +8 -2
- package/dist/src/integration-tests/model-params-isolation.integration.test.js.map +1 -1
- package/dist/src/integration-tests/modelParams.integration.test.js +4 -1
- package/dist/src/integration-tests/modelParams.integration.test.js.map +1 -1
- package/dist/src/integration-tests/provider-switching.integration.test.js +4 -1
- package/dist/src/integration-tests/provider-switching.integration.test.js.map +1 -1
- package/dist/src/integration-tests/retry-settings.integration.test.d.ts +6 -0
- package/dist/src/integration-tests/retry-settings.integration.test.js +56 -0
- package/dist/src/integration-tests/retry-settings.integration.test.js.map +1 -0
- package/dist/src/providers/index.d.ts +1 -1
- package/dist/src/providers/logging/LoggingProviderWrapper.test.js +58 -47
- package/dist/src/providers/logging/LoggingProviderWrapper.test.js.map +1 -1
- package/dist/src/providers/logging/multi-provider-logging.integration.test.js +130 -68
- package/dist/src/providers/logging/multi-provider-logging.integration.test.js.map +1 -1
- package/dist/src/providers/logging/performance.test.js +39 -16
- package/dist/src/providers/logging/performance.test.js.map +1 -1
- package/dist/src/providers/provider-gemini-switching.test.js +4 -1
- package/dist/src/providers/provider-gemini-switching.test.js.map +1 -1
- package/dist/src/providers/provider-switching.integration.test.js +12 -3
- package/dist/src/providers/provider-switching.integration.test.js.map +1 -1
- package/dist/src/providers/providerConfigUtils.js +2 -4
- package/dist/src/providers/providerConfigUtils.js.map +1 -1
- package/dist/src/providers/providerManagerInstance.js +24 -49
- package/dist/src/providers/providerManagerInstance.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.js +4 -0
- package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
- package/dist/src/storage/ConversationStorage.test.js +10 -7
- package/dist/src/storage/ConversationStorage.test.js.map +1 -1
- package/dist/src/test-utils/mockCommandContext.js +2 -0
- package/dist/src/test-utils/mockCommandContext.js.map +1 -1
- package/dist/src/ui/App.e2e.test.d.ts +6 -0
- package/dist/src/ui/App.e2e.test.js +37 -0
- package/dist/src/ui/App.e2e.test.js.map +1 -0
- package/dist/src/ui/App.js +51 -4
- package/dist/src/ui/App.js.map +1 -1
- package/dist/src/ui/commands/authCommand.d.ts +9 -1
- package/dist/src/ui/commands/authCommand.js +109 -31
- package/dist/src/ui/commands/authCommand.js.map +1 -1
- package/dist/src/ui/commands/clearCommand.js +2 -0
- package/dist/src/ui/commands/clearCommand.js.map +1 -1
- package/dist/src/ui/commands/ideCommand.js +26 -0
- package/dist/src/ui/commands/ideCommand.js.map +1 -1
- package/dist/src/ui/commands/keyCommand.js +16 -54
- package/dist/src/ui/commands/keyCommand.js.map +1 -1
- package/dist/src/ui/commands/keyCommand.test.js +3 -3
- package/dist/src/ui/commands/keyCommand.test.js.map +1 -1
- package/dist/src/ui/commands/keyfileCommand.js +42 -32
- package/dist/src/ui/commands/keyfileCommand.js.map +1 -1
- package/dist/src/ui/commands/logoutCommand.d.ts +7 -0
- package/dist/src/ui/commands/logoutCommand.js +70 -0
- package/dist/src/ui/commands/logoutCommand.js.map +1 -0
- package/dist/src/ui/commands/modelCommand.js +16 -0
- package/dist/src/ui/commands/modelCommand.js.map +1 -1
- package/dist/src/ui/commands/profileCommand.js +31 -25
- package/dist/src/ui/commands/profileCommand.js.map +1 -1
- package/dist/src/ui/commands/profileCommand.test.js +15 -16
- package/dist/src/ui/commands/profileCommand.test.js.map +1 -1
- package/dist/src/ui/commands/providerCommand.js +10 -7
- package/dist/src/ui/commands/providerCommand.js.map +1 -1
- package/dist/src/ui/commands/setCommand.js +43 -19
- package/dist/src/ui/commands/setCommand.js.map +1 -1
- package/dist/src/ui/commands/setCommand.test.js +5 -7
- package/dist/src/ui/commands/setCommand.test.js.map +1 -1
- package/dist/src/ui/commands/setupGithubCommand.d.ts +2 -0
- package/dist/src/ui/commands/setupGithubCommand.js +123 -23
- package/dist/src/ui/commands/setupGithubCommand.js.map +1 -1
- package/dist/src/ui/commands/setupGithubCommand.test.js +94 -1
- package/dist/src/ui/commands/setupGithubCommand.test.js.map +1 -1
- package/dist/src/ui/commands/statusCommand.d.ts +7 -0
- package/dist/src/ui/commands/statusCommand.js +71 -0
- package/dist/src/ui/commands/statusCommand.js.map +1 -0
- package/dist/src/ui/commands/types.d.ts +1 -0
- package/dist/src/ui/commands/types.js.map +1 -1
- package/dist/src/ui/components/ContextIndicator.ui.test.js +19 -46
- package/dist/src/ui/components/ContextIndicator.ui.test.js.map +1 -1
- package/dist/src/ui/components/Footer.d.ts +1 -1
- package/dist/src/ui/components/Footer.js +7 -7
- package/dist/src/ui/components/Footer.js.map +1 -1
- package/dist/src/ui/components/Footer.responsive.test.js +28 -25
- package/dist/src/ui/components/Footer.responsive.test.js.map +1 -1
- package/dist/src/ui/components/OAuthCodeDialog.d.ts +5 -0
- package/dist/src/ui/components/OAuthCodeDialog.js +31 -1
- package/dist/src/ui/components/OAuthCodeDialog.js.map +1 -1
- package/dist/src/ui/components/OAuthCodeDialog.test.d.ts +6 -0
- package/dist/src/ui/components/OAuthCodeDialog.test.js +60 -0
- package/dist/src/ui/components/OAuthCodeDialog.test.js.map +1 -0
- package/dist/src/ui/contexts/KeypressContext.js +23 -1
- package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
- package/dist/src/ui/contexts/KeypressContext.test.js +58 -2
- package/dist/src/ui/contexts/KeypressContext.test.js.map +1 -1
- package/dist/src/ui/contexts/SessionContext.d.ts +2 -0
- package/dist/src/ui/contexts/SessionContext.js +9 -1
- package/dist/src/ui/contexts/SessionContext.js.map +1 -1
- package/dist/src/ui/contexts/TodoContext.js +2 -2
- package/dist/src/ui/contexts/TodoContext.js.map +1 -1
- package/dist/src/ui/hooks/atCommandProcessor.js +0 -2
- package/dist/src/ui/hooks/atCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/atCommandProcessor.test.js +21 -6
- package/dist/src/ui/hooks/atCommandProcessor.test.js.map +1 -1
- package/dist/src/ui/hooks/slashCommandProcessor.js +2 -0
- package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/useAuthCommand.js +6 -5
- package/dist/src/ui/hooks/useAuthCommand.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.js +47 -63
- package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
- package/dist/src/ui/hooks/useProviderDialog.js +2 -5
- package/dist/src/ui/hooks/useProviderDialog.js.map +1 -1
- package/dist/src/ui/hooks/useToolScheduler.test.js +60 -24
- package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
- package/dist/src/ui/utils/platformConstants.d.ts +2 -0
- package/dist/src/ui/utils/platformConstants.js +2 -0
- package/dist/src/ui/utils/platformConstants.js.map +1 -1
- package/dist/src/ui/utils/secureInputHandler.js +18 -7
- package/dist/src/ui/utils/secureInputHandler.js.map +1 -1
- package/dist/src/ui/utils/secureInputHandler.test.js +20 -0
- package/dist/src/ui/utils/secureInputHandler.test.js.map +1 -1
- package/dist/src/utils/privacy/ConversationDataRedactor.d.ts +3 -3
- package/dist/src/utils/privacy/ConversationDataRedactor.js +16 -12
- package/dist/src/utils/privacy/ConversationDataRedactor.js.map +1 -1
- package/dist/src/utils/privacy/ConversationDataRedactor.test.js +115 -72
- package/dist/src/utils/privacy/ConversationDataRedactor.test.js.map +1 -1
- package/dist/src/validateNonInterActiveAuth.js +8 -17
- package/dist/src/validateNonInterActiveAuth.js.map +1 -1
- package/dist/src/zed-integration/schema.d.ts +194 -91
- package/dist/src/zed-integration/schema.js +7 -1
- package/dist/src/zed-integration/schema.js.map +1 -1
- package/dist/src/zed-integration/zedIntegration.d.ts +1 -3
- package/dist/src/zed-integration/zedIntegration.js +454 -198
- package/dist/src/zed-integration/zedIntegration.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -3
@@ -3,7 +3,7 @@
|
|
3
3
|
* Copyright 2025 Google LLC
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
5
5
|
*/
|
6
|
-
import { AuthType, logToolCall, convertToFunctionResponse, ToolConfirmationOutcome, clearCachedCredentialFile, isNodeError, getErrorMessage, isWithinRoot, getErrorStatus,
|
6
|
+
import { AuthType, logToolCall, convertToFunctionResponse, ToolConfirmationOutcome, clearCachedCredentialFile, isNodeError, getErrorMessage, isWithinRoot, getErrorStatus, DiscoveredMCPTool, DebugLogger, getFunctionCalls, EmojiFilter, } from '@vybestack/llxprt-code-core';
|
7
7
|
import * as acp from './acp.js';
|
8
8
|
import { AcpFileSystemService } from './fileSystemService.js';
|
9
9
|
import { Readable, Writable } from 'node:stream';
|
@@ -11,32 +11,43 @@ import { SettingScope } from '../config/settings.js';
|
|
11
11
|
import * as fs from 'fs/promises';
|
12
12
|
import * as path from 'path';
|
13
13
|
import { z } from 'zod';
|
14
|
+
import os from 'os';
|
14
15
|
import { randomUUID } from 'crypto';
|
15
|
-
|
16
|
-
|
16
|
+
export async function runZedIntegration(config, settings) {
|
17
|
+
const logger = new DebugLogger('llxprt:zed-integration');
|
18
|
+
logger.debug(() => 'Starting Zed integration');
|
17
19
|
const stdout = Writable.toWeb(process.stdout);
|
18
20
|
const stdin = Readable.toWeb(process.stdin);
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
logger.debug(() => 'Streams created');
|
22
|
+
try {
|
23
|
+
new acp.AgentSideConnection((client) => {
|
24
|
+
logger.debug(() => 'Creating GeminiAgent');
|
25
|
+
return new GeminiAgent(config, settings, client);
|
26
|
+
}, stdout, stdin);
|
27
|
+
logger.debug(() => 'AgentSideConnection created successfully');
|
28
|
+
}
|
29
|
+
catch (e) {
|
30
|
+
logger.debug(() => `ERROR: Failed to create AgentSideConnection: ${e}`);
|
31
|
+
throw e;
|
32
|
+
}
|
33
|
+
logger.debug(() => 'Zed integration ready, waiting for messages');
|
34
|
+
// Keep the process alive - the Connection's #receive method will handle messages
|
35
|
+
await new Promise(() => {
|
36
|
+
// This promise never resolves, keeping the process alive
|
37
|
+
});
|
25
38
|
}
|
26
39
|
class GeminiAgent {
|
27
40
|
config;
|
28
41
|
settings;
|
29
|
-
extensions;
|
30
|
-
argv;
|
31
42
|
client;
|
32
43
|
sessions = new Map();
|
33
44
|
clientCapabilities;
|
34
|
-
|
45
|
+
logger;
|
46
|
+
constructor(config, settings, client) {
|
35
47
|
this.config = config;
|
36
48
|
this.settings = settings;
|
37
|
-
this.extensions = extensions;
|
38
|
-
this.argv = argv;
|
39
49
|
this.client = client;
|
50
|
+
this.logger = new DebugLogger('llxprt:zed-integration');
|
40
51
|
}
|
41
52
|
async initialize(args) {
|
42
53
|
this.clientCapabilities = args.clientCapabilities;
|
@@ -62,6 +73,11 @@ class GeminiAgent {
|
|
62
73
|
authMethods,
|
63
74
|
agentCapabilities: {
|
64
75
|
loadSession: false,
|
76
|
+
promptCapabilities: {
|
77
|
+
image: true,
|
78
|
+
audio: true,
|
79
|
+
embeddedContext: true,
|
80
|
+
},
|
65
81
|
},
|
66
82
|
};
|
67
83
|
}
|
@@ -71,47 +87,176 @@ class GeminiAgent {
|
|
71
87
|
await this.config.refreshAuth(method);
|
72
88
|
this.settings.setValue(SettingScope.User, 'selectedAuthType', method);
|
73
89
|
}
|
74
|
-
async newSession({ cwd, mcpServers, }) {
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
90
|
+
async newSession({ cwd: _cwd, mcpServers: _mcpServers, }) {
|
91
|
+
try {
|
92
|
+
const sessionId = randomUUID();
|
93
|
+
// Use the existing config that was passed to runZedIntegration
|
94
|
+
const sessionConfig = this.config;
|
95
|
+
this.logger.debug(() => `newSession - creating session ${sessionId}`);
|
96
|
+
if (this.clientCapabilities?.fs) {
|
97
|
+
const acpFileSystemService = new AcpFileSystemService(this.client, sessionId, this.clientCapabilities.fs, sessionConfig.getFileSystemService());
|
98
|
+
sessionConfig.setFileSystemService(acpFileSystemService);
|
99
|
+
}
|
100
|
+
// Try to get the client and check if it's properly initialized
|
101
|
+
let geminiClient = sessionConfig.getGeminiClient();
|
102
|
+
const hasContentGeneratorConfig = sessionConfig.getContentGeneratorConfig() !== undefined;
|
103
|
+
this.logger.debug(() => `GeminiClient exists: ${!!geminiClient}, ContentGeneratorConfig exists: ${hasContentGeneratorConfig}`);
|
104
|
+
if (!geminiClient || !hasContentGeneratorConfig) {
|
105
|
+
this.logger.debug(() => 'GeminiClient not available - attempting auto-authentication');
|
106
|
+
// Auto-authenticate based on available configuration
|
107
|
+
const providerManager = sessionConfig.getProviderManager();
|
108
|
+
// Debug provider state
|
109
|
+
if (providerManager) {
|
110
|
+
this.logger.debug(() => `ProviderManager exists: ${providerManager.hasActiveProvider() ? 'has active provider' : 'no active provider'}`);
|
111
|
+
this.logger.debug(() => `Active provider name: ${providerManager.getActiveProviderName() || 'none'}`);
|
112
|
+
}
|
113
|
+
else {
|
114
|
+
this.logger.debug(() => 'No ProviderManager available');
|
115
|
+
}
|
116
|
+
// Check for provider from config (loaded from profile or CLI)
|
117
|
+
const configProvider = sessionConfig.getProvider();
|
118
|
+
if (configProvider && providerManager) {
|
119
|
+
this.logger.debug(() => `Config has provider: ${configProvider}`);
|
120
|
+
// Ensure provider is activated
|
121
|
+
if (!providerManager.hasActiveProvider()) {
|
122
|
+
this.logger.debug(() => `Activating provider: ${configProvider}`);
|
123
|
+
await providerManager.setActiveProvider(configProvider);
|
124
|
+
// Apply ephemeral settings from profile to the provider
|
125
|
+
const activeProvider = providerManager.getActiveProvider();
|
126
|
+
if (activeProvider) {
|
127
|
+
const authKey = sessionConfig.getEphemeralSetting('auth-key');
|
128
|
+
const authKeyfile = sessionConfig.getEphemeralSetting('auth-keyfile');
|
129
|
+
const baseUrl = sessionConfig.getEphemeralSetting('base-url');
|
130
|
+
// Apply auth settings from profile
|
131
|
+
if (authKey && activeProvider.setApiKey) {
|
132
|
+
this.logger.debug(() => 'Setting API key from profile');
|
133
|
+
activeProvider.setApiKey(authKey);
|
134
|
+
}
|
135
|
+
else if (authKeyfile && activeProvider.setApiKey) {
|
136
|
+
// Load API key from file
|
137
|
+
try {
|
138
|
+
const apiKey = (await fs.readFile(authKeyfile.replace(/^~/, os.homedir()), 'utf-8')).trim();
|
139
|
+
if (apiKey) {
|
140
|
+
this.logger.debug(() => 'Setting API key from keyfile');
|
141
|
+
activeProvider.setApiKey(apiKey);
|
142
|
+
}
|
143
|
+
}
|
144
|
+
catch (error) {
|
145
|
+
this.logger.debug(() => `ERROR: Failed to load keyfile ${authKeyfile}: ${error}`);
|
146
|
+
}
|
147
|
+
}
|
148
|
+
// Apply base URL if specified
|
149
|
+
if (baseUrl && baseUrl !== 'none' && activeProvider.setBaseUrl) {
|
150
|
+
this.logger.debug(() => `Setting base URL: ${baseUrl}`);
|
151
|
+
activeProvider.setBaseUrl(baseUrl);
|
152
|
+
}
|
153
|
+
// Apply profile model params if loaded
|
154
|
+
const configWithProfile = sessionConfig;
|
155
|
+
if (configWithProfile._profileModelParams &&
|
156
|
+
'setModelParams' in activeProvider &&
|
157
|
+
activeProvider.setModelParams) {
|
158
|
+
this.logger.debug(() => 'Setting model params from profile');
|
159
|
+
activeProvider.setModelParams(configWithProfile._profileModelParams);
|
160
|
+
}
|
161
|
+
}
|
162
|
+
}
|
163
|
+
}
|
164
|
+
if (providerManager && providerManager.hasActiveProvider()) {
|
165
|
+
// Use provider-based auth if a provider is configured
|
166
|
+
this.logger.debug(() => `Auto-authenticating with provider: ${providerManager.getActiveProviderName()}`);
|
167
|
+
// Ensure provider manager is set on config before refreshAuth
|
168
|
+
// This is crucial for createContentGeneratorConfig to include the provider manager
|
169
|
+
if (!sessionConfig.getProviderManager()) {
|
170
|
+
this.logger.debug(() => 'Setting provider manager on config');
|
171
|
+
sessionConfig.providerManager = providerManager;
|
172
|
+
// Ensure serverToolsProvider (Gemini) has config set BEFORE refreshAuth
|
173
|
+
// This is critical for web search to work properly
|
174
|
+
const serverToolsProvider = providerManager.getServerToolsProvider();
|
175
|
+
if (serverToolsProvider &&
|
176
|
+
serverToolsProvider.name === 'gemini' &&
|
177
|
+
serverToolsProvider.setConfig) {
|
178
|
+
this.logger.debug(() => 'Setting config on serverToolsProvider for web search (before auth)');
|
179
|
+
serverToolsProvider.setConfig(sessionConfig);
|
180
|
+
}
|
181
|
+
}
|
182
|
+
await sessionConfig.refreshAuth(AuthType.USE_PROVIDER);
|
183
|
+
// After refreshAuth, verify ContentGeneratorConfig was created with provider manager
|
184
|
+
const contentGenConfig = sessionConfig.getContentGeneratorConfig();
|
185
|
+
if (contentGenConfig && !contentGenConfig.providerManager) {
|
186
|
+
this.logger.debug(() => 'Adding provider manager to ContentGeneratorConfig');
|
187
|
+
contentGenConfig.providerManager = providerManager;
|
188
|
+
}
|
189
|
+
}
|
190
|
+
else if (process.env.GEMINI_API_KEY) {
|
191
|
+
// Use API key if available
|
192
|
+
this.logger.debug(() => 'Auto-authenticating with GEMINI_API_KEY');
|
193
|
+
await sessionConfig.refreshAuth(AuthType.USE_GEMINI);
|
194
|
+
}
|
195
|
+
else {
|
196
|
+
// Try OAuth as last resort (this might open a browser)
|
197
|
+
this.logger.debug(() => 'Auto-authenticating with OAuth');
|
198
|
+
await sessionConfig.refreshAuth(AuthType.LOGIN_WITH_GOOGLE);
|
199
|
+
}
|
200
|
+
geminiClient = sessionConfig.getGeminiClient();
|
201
|
+
if (!geminiClient) {
|
202
|
+
throw new Error('Failed to authenticate. Please ensure valid credentials are available.');
|
203
|
+
}
|
204
|
+
}
|
205
|
+
this.logger.debug(() => 'Successfully obtained GeminiClient');
|
206
|
+
// Verify ContentGeneratorConfig was created properly
|
207
|
+
let contentGenConfig;
|
79
208
|
try {
|
80
|
-
|
81
|
-
|
209
|
+
contentGenConfig = sessionConfig.getContentGeneratorConfig();
|
210
|
+
this.logger.debug(() => `ContentGeneratorConfig exists: ${!!contentGenConfig}`);
|
211
|
+
if (contentGenConfig) {
|
212
|
+
this.logger.debug(() => `ContentGeneratorConfig has providerManager: ${!!contentGenConfig.providerManager}`);
|
213
|
+
this.logger.debug(() => `ContentGeneratorConfig authType: ${contentGenConfig.authType}`);
|
214
|
+
}
|
82
215
|
}
|
83
|
-
catch (
|
84
|
-
|
216
|
+
catch (error) {
|
217
|
+
this.logger.debug(() => `Failed to get ContentGeneratorConfig: ${error}`);
|
218
|
+
throw new Error('Content generator config not created after authentication. Please check your credentials.');
|
85
219
|
}
|
86
|
-
|
87
|
-
|
88
|
-
throw acp.RequestError.authRequired();
|
89
|
-
}
|
90
|
-
if (this.clientCapabilities?.fs) {
|
91
|
-
const acpFileSystemService = new AcpFileSystemService(this.client, sessionId, this.clientCapabilities.fs, config.getFileSystemService());
|
92
|
-
config.setFileSystemService(acpFileSystemService);
|
93
|
-
}
|
94
|
-
const geminiClient = config.getGeminiClient();
|
95
|
-
const chat = await geminiClient.startChat();
|
96
|
-
const session = new Session(sessionId, chat, config, this.client);
|
97
|
-
this.sessions.set(sessionId, session);
|
98
|
-
return {
|
99
|
-
sessionId,
|
100
|
-
};
|
101
|
-
}
|
102
|
-
async newSessionConfig(sessionId, cwd, mcpServers) {
|
103
|
-
const mergedMcpServers = { ...this.settings.merged.mcpServers };
|
104
|
-
for (const { command, args, env: rawEnv, name } of mcpServers) {
|
105
|
-
const env = {};
|
106
|
-
for (const { name: envName, value } of rawEnv) {
|
107
|
-
env[envName] = value;
|
220
|
+
if (!contentGenConfig) {
|
221
|
+
throw new Error('Content generator config not created after authentication.');
|
108
222
|
}
|
109
|
-
|
223
|
+
let chat;
|
224
|
+
try {
|
225
|
+
chat = await geminiClient.startChat();
|
226
|
+
}
|
227
|
+
catch (error) {
|
228
|
+
this.logger.debug(() => `Error starting chat: ${error}`);
|
229
|
+
// If startChat fails due to missing config, try to authenticate now
|
230
|
+
if (error instanceof Error &&
|
231
|
+
error.message.includes('Content generator config')) {
|
232
|
+
this.logger.debug(() => 'Attempting late authentication due to missing config');
|
233
|
+
const providerManager = sessionConfig.getProviderManager();
|
234
|
+
if (providerManager && providerManager.hasActiveProvider()) {
|
235
|
+
await sessionConfig.refreshAuth(AuthType.USE_PROVIDER);
|
236
|
+
}
|
237
|
+
else if (process.env.GEMINI_API_KEY) {
|
238
|
+
await sessionConfig.refreshAuth(AuthType.USE_GEMINI);
|
239
|
+
}
|
240
|
+
else {
|
241
|
+
await sessionConfig.refreshAuth(AuthType.LOGIN_WITH_GOOGLE);
|
242
|
+
}
|
243
|
+
// Try again after auth
|
244
|
+
chat = await geminiClient.startChat();
|
245
|
+
}
|
246
|
+
else {
|
247
|
+
throw error;
|
248
|
+
}
|
249
|
+
}
|
250
|
+
const session = new Session(sessionId, chat, sessionConfig, this.client);
|
251
|
+
this.sessions.set(sessionId, session);
|
252
|
+
return {
|
253
|
+
sessionId,
|
254
|
+
};
|
255
|
+
}
|
256
|
+
catch (error) {
|
257
|
+
this.logger.debug(() => `ERROR in newSession: ${error}`);
|
258
|
+
throw error;
|
110
259
|
}
|
111
|
-
const settings = { ...this.settings.merged, mcpServers: mergedMcpServers };
|
112
|
-
const config = await loadCliConfig(settings, this.extensions, sessionId, this.argv, cwd);
|
113
|
-
await config.initialize();
|
114
|
-
return config;
|
115
260
|
}
|
116
261
|
async cancel(params) {
|
117
262
|
const session = this.sessions.get(params.sessionId);
|
@@ -134,11 +279,16 @@ class Session {
|
|
134
279
|
config;
|
135
280
|
client;
|
136
281
|
pendingPrompt = null;
|
282
|
+
emojiFilter;
|
137
283
|
constructor(id, chat, config, client) {
|
138
284
|
this.id = id;
|
139
285
|
this.chat = chat;
|
140
286
|
this.config = config;
|
141
287
|
this.client = client;
|
288
|
+
// Initialize emoji filter from settings
|
289
|
+
const emojiFilterMode = this.config.getEphemeralSetting('emojifilter') || 'auto';
|
290
|
+
const filterConfig = { mode: emojiFilterMode };
|
291
|
+
this.emojiFilter = new EmojiFilter(filterConfig);
|
142
292
|
}
|
143
293
|
async cancelPendingPrompt() {
|
144
294
|
if (!this.pendingPrompt) {
|
@@ -156,10 +306,6 @@ class Session {
|
|
156
306
|
const parts = await this.#resolvePrompt(params.prompt, pendingSend.signal);
|
157
307
|
let nextMessage = { role: 'user', parts };
|
158
308
|
while (nextMessage !== null) {
|
159
|
-
if (pendingSend.signal.aborted) {
|
160
|
-
chat.addHistory(nextMessage);
|
161
|
-
return { stopReason: 'cancelled' };
|
162
|
-
}
|
163
309
|
const functionCalls = [];
|
164
310
|
try {
|
165
311
|
const responseStream = await chat.sendMessageStream({
|
@@ -171,7 +317,9 @@ class Session {
|
|
171
317
|
nextMessage = null;
|
172
318
|
for await (const resp of responseStream) {
|
173
319
|
if (pendingSend.signal.aborted) {
|
174
|
-
|
320
|
+
// Let the stream processing complete naturally to handle cancellation properly
|
321
|
+
// Don't return early here - let the tool pipeline handle cleanup
|
322
|
+
break;
|
175
323
|
}
|
176
324
|
if (resp.candidates && resp.candidates.length > 0) {
|
177
325
|
const candidate = resp.candidates[0];
|
@@ -179,9 +327,27 @@ class Session {
|
|
179
327
|
if (!part.text) {
|
180
328
|
continue;
|
181
329
|
}
|
330
|
+
// Filter the content through emoji filter
|
331
|
+
const filterResult = this.emojiFilter.filterStreamChunk(part.text);
|
332
|
+
if (filterResult.blocked) {
|
333
|
+
// In error mode: inject error feedback to model for retry
|
334
|
+
this.sendUpdate({
|
335
|
+
sessionUpdate: 'agent_message_chunk',
|
336
|
+
content: {
|
337
|
+
type: 'text',
|
338
|
+
text: '[Error: Response blocked due to emoji detection]',
|
339
|
+
},
|
340
|
+
});
|
341
|
+
// Add system feedback to be sent with next tool response
|
342
|
+
// This could be done by queueing feedback similar to TUI implementation
|
343
|
+
continue;
|
344
|
+
}
|
345
|
+
const filteredText = typeof filterResult.filtered === 'string'
|
346
|
+
? filterResult.filtered
|
347
|
+
: '';
|
182
348
|
const content = {
|
183
349
|
type: 'text',
|
184
|
-
text:
|
350
|
+
text: filteredText,
|
185
351
|
};
|
186
352
|
this.sendUpdate({
|
187
353
|
sessionUpdate: part.thought
|
@@ -191,8 +357,10 @@ class Session {
|
|
191
357
|
});
|
192
358
|
}
|
193
359
|
}
|
194
|
-
|
195
|
-
|
360
|
+
// Extract function calls from the response using the proper utility
|
361
|
+
const respFunctionCalls = getFunctionCalls(resp);
|
362
|
+
if (respFunctionCalls && respFunctionCalls.length > 0) {
|
363
|
+
functionCalls.push(...respFunctionCalls);
|
196
364
|
}
|
197
365
|
}
|
198
366
|
}
|
@@ -200,11 +368,31 @@ class Session {
|
|
200
368
|
if (getErrorStatus(error) === 429) {
|
201
369
|
throw new acp.RequestError(429, 'Rate limit exceeded. Try again later.');
|
202
370
|
}
|
203
|
-
|
371
|
+
// If this is an abort error due to cancellation, handle it gracefully
|
372
|
+
if (pendingSend.signal.aborted &&
|
373
|
+
isNodeError(error) &&
|
374
|
+
error.name === 'AbortError') {
|
375
|
+
// Don't throw - let the cancellation be handled below
|
376
|
+
}
|
377
|
+
else {
|
378
|
+
throw error;
|
379
|
+
}
|
380
|
+
}
|
381
|
+
// Check for cancellation after stream processing but before tool execution
|
382
|
+
if (pendingSend.signal.aborted) {
|
383
|
+
// Return cancellation without adding to conversation history
|
384
|
+
// The conversation state should remain clean for proper context handling
|
385
|
+
return { stopReason: 'cancelled' };
|
204
386
|
}
|
205
387
|
if (functionCalls.length > 0) {
|
206
388
|
const toolResponseParts = [];
|
207
389
|
for (const fc of functionCalls) {
|
390
|
+
// Check for cancellation before each tool execution
|
391
|
+
if (pendingSend.signal.aborted) {
|
392
|
+
// Return cancellation without polluting conversation history
|
393
|
+
// Tool execution cancellation should be handled by the tool execution system
|
394
|
+
return { stopReason: 'cancelled' };
|
395
|
+
}
|
208
396
|
const response = await this.runTool(pendingSend.signal, promptId, fc);
|
209
397
|
const parts = Array.isArray(response) ? response : [response];
|
210
398
|
for (const part of parts) {
|
@@ -216,7 +404,14 @@ class Session {
|
|
216
404
|
}
|
217
405
|
}
|
218
406
|
}
|
219
|
-
|
407
|
+
// For multiple tool responses, send them all together as the TUI does
|
408
|
+
// This ensures proper conversation history structure for providers like Anthropic
|
409
|
+
if (toolResponseParts.length > 0) {
|
410
|
+
nextMessage = { role: 'user', parts: toolResponseParts };
|
411
|
+
}
|
412
|
+
else {
|
413
|
+
nextMessage = null;
|
414
|
+
}
|
220
415
|
}
|
221
416
|
}
|
222
417
|
return { stopReason: 'end_turn' };
|
@@ -247,7 +442,15 @@ class Session {
|
|
247
442
|
? 'mcp'
|
248
443
|
: 'native',
|
249
444
|
});
|
445
|
+
// Return paired function call and function response for proper conversation history
|
250
446
|
return [
|
447
|
+
{
|
448
|
+
functionCall: {
|
449
|
+
id: callId,
|
450
|
+
name: fc.name ?? '',
|
451
|
+
args,
|
452
|
+
},
|
453
|
+
},
|
251
454
|
{
|
252
455
|
functionResponse: {
|
253
456
|
id: callId,
|
@@ -265,64 +468,64 @@ class Session {
|
|
265
468
|
if (!tool) {
|
266
469
|
return errorResponse(new Error(`Tool "${fc.name}" not found in registry.`));
|
267
470
|
}
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
471
|
+
try {
|
472
|
+
const invocation = tool.build(args);
|
473
|
+
const confirmationDetails = await invocation.shouldConfirmExecute(abortSignal);
|
474
|
+
if (confirmationDetails) {
|
475
|
+
const content = [];
|
476
|
+
if (confirmationDetails.type === 'edit') {
|
477
|
+
content.push({
|
478
|
+
type: 'diff',
|
479
|
+
path: confirmationDetails.fileName,
|
480
|
+
oldText: confirmationDetails.originalContent,
|
481
|
+
newText: confirmationDetails.newContent,
|
482
|
+
});
|
483
|
+
}
|
484
|
+
const params = {
|
485
|
+
sessionId: this.id,
|
486
|
+
options: toPermissionOptions(confirmationDetails),
|
487
|
+
toolCall: {
|
488
|
+
toolCallId: callId,
|
489
|
+
status: 'pending',
|
490
|
+
title: invocation.getDescription(),
|
491
|
+
content,
|
492
|
+
locations: invocation.toolLocations(),
|
493
|
+
kind: tool.kind,
|
494
|
+
},
|
495
|
+
};
|
496
|
+
const output = await this.client.requestPermission(params);
|
497
|
+
const outcome = output.outcome.outcome === 'cancelled'
|
498
|
+
? ToolConfirmationOutcome.Cancel
|
499
|
+
: z
|
500
|
+
.nativeEnum(ToolConfirmationOutcome)
|
501
|
+
.parse(output.outcome.optionId);
|
502
|
+
await confirmationDetails.onConfirm(outcome);
|
503
|
+
switch (outcome) {
|
504
|
+
case ToolConfirmationOutcome.Cancel:
|
505
|
+
return errorResponse(new Error(`Tool "${fc.name}" was canceled by the user.`));
|
506
|
+
case ToolConfirmationOutcome.ProceedOnce:
|
507
|
+
case ToolConfirmationOutcome.ProceedAlways:
|
508
|
+
case ToolConfirmationOutcome.ProceedAlwaysServer:
|
509
|
+
case ToolConfirmationOutcome.ProceedAlwaysTool:
|
510
|
+
case ToolConfirmationOutcome.ModifyWithEditor:
|
511
|
+
break;
|
512
|
+
default: {
|
513
|
+
const resultOutcome = outcome;
|
514
|
+
throw new Error(`Unexpected: ${resultOutcome}`);
|
515
|
+
}
|
516
|
+
}
|
279
517
|
}
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
toolCall: {
|
518
|
+
else {
|
519
|
+
await this.sendUpdate({
|
520
|
+
sessionUpdate: 'tool_call',
|
284
521
|
toolCallId: callId,
|
285
|
-
status: '
|
522
|
+
status: 'in_progress',
|
286
523
|
title: invocation.getDescription(),
|
287
|
-
content,
|
524
|
+
content: [],
|
288
525
|
locations: invocation.toolLocations(),
|
289
526
|
kind: tool.kind,
|
290
|
-
}
|
291
|
-
};
|
292
|
-
const output = await this.client.requestPermission(params);
|
293
|
-
const outcome = output.outcome.outcome === 'cancelled'
|
294
|
-
? ToolConfirmationOutcome.Cancel
|
295
|
-
: z
|
296
|
-
.nativeEnum(ToolConfirmationOutcome)
|
297
|
-
.parse(output.outcome.optionId);
|
298
|
-
await confirmationDetails.onConfirm(outcome);
|
299
|
-
switch (outcome) {
|
300
|
-
case ToolConfirmationOutcome.Cancel:
|
301
|
-
return errorResponse(new Error(`Tool "${fc.name}" was canceled by the user.`));
|
302
|
-
case ToolConfirmationOutcome.ProceedOnce:
|
303
|
-
case ToolConfirmationOutcome.ProceedAlways:
|
304
|
-
case ToolConfirmationOutcome.ProceedAlwaysServer:
|
305
|
-
case ToolConfirmationOutcome.ProceedAlwaysTool:
|
306
|
-
case ToolConfirmationOutcome.ModifyWithEditor:
|
307
|
-
break;
|
308
|
-
default: {
|
309
|
-
const resultOutcome = outcome;
|
310
|
-
throw new Error(`Unexpected: ${resultOutcome}`);
|
311
|
-
}
|
527
|
+
});
|
312
528
|
}
|
313
|
-
}
|
314
|
-
else {
|
315
|
-
await this.sendUpdate({
|
316
|
-
sessionUpdate: 'tool_call',
|
317
|
-
toolCallId: callId,
|
318
|
-
status: 'in_progress',
|
319
|
-
title: invocation.getDescription(),
|
320
|
-
content: [],
|
321
|
-
locations: invocation.toolLocations(),
|
322
|
-
kind: tool.kind,
|
323
|
-
});
|
324
|
-
}
|
325
|
-
try {
|
326
529
|
const toolResult = await invocation.execute(abortSignal);
|
327
530
|
const content = toToolCallContent(toolResult);
|
328
531
|
await this.sendUpdate({
|
@@ -344,7 +547,21 @@ class Session {
|
|
344
547
|
? 'mcp'
|
345
548
|
: 'native',
|
346
549
|
});
|
347
|
-
|
550
|
+
// Return paired function call and function response like the TUI does
|
551
|
+
// This ensures proper conversation history for providers that need it (like Anthropic)
|
552
|
+
const functionResponseParts = convertToFunctionResponse(fc.name, callId, toolResult.llmContent);
|
553
|
+
return [
|
554
|
+
{
|
555
|
+
functionCall: {
|
556
|
+
id: callId,
|
557
|
+
name: fc.name,
|
558
|
+
args,
|
559
|
+
},
|
560
|
+
},
|
561
|
+
...(Array.isArray(functionResponseParts)
|
562
|
+
? functionResponseParts
|
563
|
+
: [functionResponseParts]),
|
564
|
+
];
|
348
565
|
}
|
349
566
|
catch (e) {
|
350
567
|
const error = e instanceof Error ? e : new Error(String(e));
|
@@ -360,41 +577,53 @@ class Session {
|
|
360
577
|
}
|
361
578
|
}
|
362
579
|
async #resolvePrompt(message, abortSignal) {
|
580
|
+
const FILE_URI_SCHEME = 'file://';
|
581
|
+
const embeddedContext = [];
|
363
582
|
const parts = message.map((part) => {
|
364
583
|
switch (part.type) {
|
365
584
|
case 'text':
|
366
585
|
return { text: part.text };
|
367
|
-
case '
|
586
|
+
case 'image':
|
587
|
+
case 'audio':
|
368
588
|
return {
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
fileUri: part.uri,
|
589
|
+
inlineData: {
|
590
|
+
mimeType: part.mimeType,
|
591
|
+
data: part.data,
|
373
592
|
},
|
374
593
|
};
|
594
|
+
case 'resource_link': {
|
595
|
+
if (part.uri.startsWith(FILE_URI_SCHEME)) {
|
596
|
+
return {
|
597
|
+
fileData: {
|
598
|
+
mimeData: part.mimeType,
|
599
|
+
name: part.name,
|
600
|
+
fileUri: part.uri.slice(FILE_URI_SCHEME.length),
|
601
|
+
},
|
602
|
+
};
|
603
|
+
}
|
604
|
+
else {
|
605
|
+
return { text: `@${part.uri}` };
|
606
|
+
}
|
607
|
+
}
|
375
608
|
case 'resource': {
|
376
|
-
|
377
|
-
|
378
|
-
mimeData: part.resource.mimeType,
|
379
|
-
name: part.resource.uri,
|
380
|
-
fileUri: part.resource.uri,
|
381
|
-
},
|
382
|
-
};
|
609
|
+
embeddedContext.push(part.resource);
|
610
|
+
return { text: `@${part.resource.uri}` };
|
383
611
|
}
|
384
612
|
default: {
|
385
|
-
|
613
|
+
const unreachable = part;
|
614
|
+
throw new Error(`Unexpected chunk type: '${unreachable}'`);
|
386
615
|
}
|
387
616
|
}
|
388
617
|
});
|
389
618
|
const atPathCommandParts = parts.filter((part) => 'fileData' in part);
|
390
|
-
if (atPathCommandParts.length === 0) {
|
619
|
+
if (atPathCommandParts.length === 0 && embeddedContext.length === 0) {
|
391
620
|
return parts;
|
392
621
|
}
|
622
|
+
const atPathToResolvedSpecMap = new Map();
|
393
623
|
// Get centralized file discovery service
|
394
624
|
const fileDiscovery = this.config.getFileService();
|
395
625
|
const respectGitIgnore = this.config.getFileFilteringRespectGitIgnore();
|
396
626
|
const pathSpecsToRead = [];
|
397
|
-
const atPathToResolvedSpecMap = new Map();
|
398
627
|
const contentLabelsForDisplay = [];
|
399
628
|
const ignoredPaths = [];
|
400
629
|
const toolRegistry = this.config.getToolRegistry();
|
@@ -527,91 +756,115 @@ class Session {
|
|
527
756
|
const ignoreType = respectGitIgnore ? 'git-ignored' : 'custom-ignored';
|
528
757
|
this.debug(`Ignored ${ignoredPaths.length} ${ignoreType} files: ${ignoredPaths.join(', ')}`);
|
529
758
|
}
|
530
|
-
|
531
|
-
if (pathSpecsToRead.length === 0) {
|
759
|
+
const processedQueryParts = [{ text: initialQueryText }];
|
760
|
+
if (pathSpecsToRead.length === 0 && embeddedContext.length === 0) {
|
761
|
+
// Fallback for lone "@" or completely invalid @-commands resulting in empty initialQueryText
|
532
762
|
console.warn('No valid file paths found in @ commands to read.');
|
533
763
|
return [{ text: initialQueryText }];
|
534
764
|
}
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
};
|
540
|
-
const callId = `${readManyFilesTool.name}-${Date.now()}`;
|
541
|
-
try {
|
542
|
-
const invocation = readManyFilesTool.build(toolArgs);
|
543
|
-
await this.sendUpdate({
|
544
|
-
sessionUpdate: 'tool_call',
|
545
|
-
toolCallId: callId,
|
546
|
-
status: 'in_progress',
|
547
|
-
title: invocation.getDescription(),
|
548
|
-
content: [],
|
549
|
-
locations: invocation.toolLocations(),
|
550
|
-
kind: readManyFilesTool.kind,
|
551
|
-
});
|
552
|
-
const result = await invocation.execute(abortSignal);
|
553
|
-
const content = toToolCallContent(result) || {
|
554
|
-
type: 'content',
|
555
|
-
content: {
|
556
|
-
type: 'text',
|
557
|
-
text: `Successfully read: ${contentLabelsForDisplay.join(', ')}`,
|
558
|
-
},
|
765
|
+
if (pathSpecsToRead.length > 0) {
|
766
|
+
const toolArgs = {
|
767
|
+
paths: pathSpecsToRead,
|
768
|
+
respectGitIgnore, // Use configuration setting
|
559
769
|
};
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
770
|
+
const callId = `${readManyFilesTool.name}-${Date.now()}`;
|
771
|
+
try {
|
772
|
+
const invocation = readManyFilesTool.build(toolArgs);
|
773
|
+
await this.sendUpdate({
|
774
|
+
sessionUpdate: 'tool_call',
|
775
|
+
toolCallId: callId,
|
776
|
+
status: 'in_progress',
|
777
|
+
title: invocation.getDescription(),
|
778
|
+
content: [],
|
779
|
+
locations: invocation.toolLocations(),
|
780
|
+
kind: readManyFilesTool.kind,
|
570
781
|
});
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
782
|
+
const result = await invocation.execute(abortSignal);
|
783
|
+
const content = toToolCallContent(result) || {
|
784
|
+
type: 'content',
|
785
|
+
content: {
|
786
|
+
type: 'text',
|
787
|
+
text: `Successfully read: ${contentLabelsForDisplay.join(', ')}`,
|
788
|
+
},
|
789
|
+
};
|
790
|
+
await this.sendUpdate({
|
791
|
+
sessionUpdate: 'tool_call_update',
|
792
|
+
toolCallId: callId,
|
793
|
+
status: 'completed',
|
794
|
+
content: content ? [content] : [],
|
795
|
+
});
|
796
|
+
if (Array.isArray(result.llmContent)) {
|
797
|
+
const fileContentRegex = /^--- (.*?) ---\n\n([\s\S]*?)\n\n$/;
|
798
|
+
processedQueryParts.push({
|
799
|
+
text: '\n--- Content from referenced files ---',
|
800
|
+
});
|
801
|
+
for (const part of result.llmContent) {
|
802
|
+
if (typeof part === 'string') {
|
803
|
+
const match = fileContentRegex.exec(part);
|
804
|
+
if (match) {
|
805
|
+
const filePathSpecInContent = match[1]; // This is a resolved pathSpec
|
806
|
+
const fileActualContent = match[2].trim();
|
807
|
+
processedQueryParts.push({
|
808
|
+
text: `\nContent from @${filePathSpecInContent}:\n`,
|
809
|
+
});
|
810
|
+
processedQueryParts.push({ text: fileActualContent });
|
811
|
+
}
|
812
|
+
else {
|
813
|
+
processedQueryParts.push({ text: part });
|
814
|
+
}
|
581
815
|
}
|
582
816
|
else {
|
583
|
-
|
817
|
+
// part is a Part object.
|
818
|
+
processedQueryParts.push(part);
|
584
819
|
}
|
585
820
|
}
|
586
|
-
else {
|
587
|
-
// part is a Part object.
|
588
|
-
processedQueryParts.push(part);
|
589
|
-
}
|
590
821
|
}
|
591
|
-
|
822
|
+
else {
|
823
|
+
console.warn('read_many_files tool returned no content or empty content.');
|
824
|
+
}
|
592
825
|
}
|
593
|
-
|
594
|
-
|
826
|
+
catch (error) {
|
827
|
+
await this.sendUpdate({
|
828
|
+
sessionUpdate: 'tool_call_update',
|
829
|
+
toolCallId: callId,
|
830
|
+
status: 'failed',
|
831
|
+
content: [
|
832
|
+
{
|
833
|
+
type: 'content',
|
834
|
+
content: {
|
835
|
+
type: 'text',
|
836
|
+
text: `Error reading files (${contentLabelsForDisplay.join(', ')}): ${getErrorMessage(error)}`,
|
837
|
+
},
|
838
|
+
},
|
839
|
+
],
|
840
|
+
});
|
841
|
+
throw error;
|
595
842
|
}
|
596
|
-
return processedQueryParts;
|
597
843
|
}
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
toolCallId: callId,
|
602
|
-
status: 'failed',
|
603
|
-
content: [
|
604
|
-
{
|
605
|
-
type: 'content',
|
606
|
-
content: {
|
607
|
-
type: 'text',
|
608
|
-
text: `Error reading files (${contentLabelsForDisplay.join(', ')}): ${getErrorMessage(error)}`,
|
609
|
-
},
|
610
|
-
},
|
611
|
-
],
|
844
|
+
if (embeddedContext.length > 0) {
|
845
|
+
processedQueryParts.push({
|
846
|
+
text: '\n--- Content from referenced context ---',
|
612
847
|
});
|
613
|
-
|
848
|
+
for (const contextPart of embeddedContext) {
|
849
|
+
processedQueryParts.push({
|
850
|
+
text: `\nContent from @${contextPart.uri}:\n`,
|
851
|
+
});
|
852
|
+
if ('text' in contextPart) {
|
853
|
+
processedQueryParts.push({
|
854
|
+
text: contextPart.text,
|
855
|
+
});
|
856
|
+
}
|
857
|
+
else {
|
858
|
+
processedQueryParts.push({
|
859
|
+
inlineData: {
|
860
|
+
mimeType: contextPart.mimeType ?? 'application/octet-stream',
|
861
|
+
data: contextPart.blob,
|
862
|
+
},
|
863
|
+
});
|
864
|
+
}
|
865
|
+
}
|
614
866
|
}
|
867
|
+
return processedQueryParts;
|
615
868
|
}
|
616
869
|
debug(msg) {
|
617
870
|
if (this.config.getDebugMode()) {
|
@@ -620,6 +873,9 @@ class Session {
|
|
620
873
|
}
|
621
874
|
}
|
622
875
|
function toToolCallContent(toolResult) {
|
876
|
+
if (toolResult.error?.message) {
|
877
|
+
throw new Error(toolResult.error.message);
|
878
|
+
}
|
623
879
|
if (toolResult.returnDisplay) {
|
624
880
|
if (typeof toolResult.returnDisplay === 'string') {
|
625
881
|
return {
|