@google/gemini-cli 0.1.19-nightly.250814.514e883a → 0.1.20
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 +1 -1
- package/dist/package.json +3 -3
- package/dist/src/acp/acp.d.ts +208 -0
- package/dist/src/{zed-integration → acp}/acp.js +44 -76
- package/dist/src/acp/acp.js.map +1 -0
- package/dist/src/acp/acpPeer.d.ts +8 -0
- package/dist/src/{zed-integration/zedIntegration.js → acp/acpPeer.js} +187 -333
- package/dist/src/acp/acpPeer.js.map +1 -0
- package/dist/src/config/config.d.ts +1 -1
- package/dist/src/config/config.js +8 -11
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/keyBindings.js +0 -4
- package/dist/src/config/keyBindings.js.map +1 -1
- package/dist/src/gemini.js +3 -6
- 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/nonInteractiveCli.js +2 -1
- package/dist/src/nonInteractiveCli.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.js +0 -2
- package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
- package/dist/src/ui/App.js +2 -8
- package/dist/src/ui/App.js.map +1 -1
- package/dist/src/ui/commands/directoryCommand.js +4 -2
- package/dist/src/ui/commands/directoryCommand.js.map +1 -1
- package/dist/src/ui/commands/mcpCommand.js +0 -4
- package/dist/src/ui/commands/mcpCommand.js.map +1 -1
- package/dist/src/ui/commands/types.d.ts +0 -1
- package/dist/src/ui/commands/types.js.map +1 -1
- package/dist/src/ui/components/InputPrompt.js +1 -7
- package/dist/src/ui/components/InputPrompt.js.map +1 -1
- package/dist/src/ui/components/shared/vim-buffer-actions.js +1 -2
- package/dist/src/ui/components/shared/vim-buffer-actions.js.map +1 -1
- package/dist/src/ui/hooks/slashCommandProcessor.js +46 -67
- package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/useFocus.d.ts +0 -4
- package/dist/src/ui/hooks/useFocus.js +4 -4
- package/dist/src/ui/hooks/useFocus.js.map +1 -1
- package/dist/src/ui/hooks/useFolderTrust.d.ts +2 -3
- package/dist/src/ui/hooks/useFolderTrust.js +9 -24
- package/dist/src/ui/hooks/useFolderTrust.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.js +2 -1
- package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
- package/dist/src/ui/hooks/useKeypress.d.ts +1 -9
- package/dist/src/ui/hooks/useKeypress.js +8 -197
- package/dist/src/ui/hooks/useKeypress.js.map +1 -1
- package/dist/src/ui/utils/errorParsing.d.ts +7 -0
- package/dist/src/ui/utils/errorParsing.js +90 -0
- package/dist/src/ui/utils/errorParsing.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/dist/src/config/trustedFolders.d.ts +0 -36
- package/dist/src/config/trustedFolders.js +0 -112
- package/dist/src/config/trustedFolders.js.map +0 -1
- package/dist/src/ui/commands/terminalSetupCommand.d.ts +0 -13
- package/dist/src/ui/commands/terminalSetupCommand.js +0 -41
- package/dist/src/ui/commands/terminalSetupCommand.js.map +0 -1
- package/dist/src/ui/hooks/useKittyKeyboardProtocol.d.ts +0 -15
- package/dist/src/ui/hooks/useKittyKeyboardProtocol.js +0 -20
- package/dist/src/ui/hooks/useKittyKeyboardProtocol.js.map +0 -1
- package/dist/src/ui/utils/kittyProtocolDetector.d.ts +0 -13
- package/dist/src/ui/utils/kittyProtocolDetector.js +0 -88
- package/dist/src/ui/utils/kittyProtocolDetector.js.map +0 -1
- package/dist/src/ui/utils/platformConstants.d.ts +0 -38
- package/dist/src/ui/utils/platformConstants.js +0 -39
- package/dist/src/ui/utils/platformConstants.js.map +0 -1
- package/dist/src/ui/utils/terminalSetup.d.ts +0 -30
- package/dist/src/ui/utils/terminalSetup.js +0 -281
- package/dist/src/ui/utils/terminalSetup.js.map +0 -1
- package/dist/src/utils/checks.d.ts +0 -19
- package/dist/src/utils/checks.js +0 -24
- package/dist/src/utils/checks.js.map +0 -1
- package/dist/src/zed-integration/acp.d.ts +0 -63
- package/dist/src/zed-integration/acp.js.map +0 -1
- package/dist/src/zed-integration/schema.d.ts +0 -11679
- package/dist/src/zed-integration/schema.js +0 -305
- package/dist/src/zed-integration/schema.js.map +0 -1
- package/dist/src/zed-integration/zedIntegration.d.ts +0 -10
- package/dist/src/zed-integration/zedIntegration.js.map +0 -1
|
@@ -3,16 +3,13 @@
|
|
|
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, } from '@google/gemini-cli-core';
|
|
7
7
|
import * as acp from './acp.js';
|
|
8
8
|
import { Readable, Writable } from 'node:stream';
|
|
9
9
|
import { SettingScope } from '../config/settings.js';
|
|
10
10
|
import * as fs from 'fs/promises';
|
|
11
11
|
import * as path from 'path';
|
|
12
|
-
|
|
13
|
-
import { randomUUID } from 'crypto';
|
|
14
|
-
import { loadCliConfig } from '../config/config.js';
|
|
15
|
-
export async function runZedIntegration(config, settings, extensions, argv) {
|
|
12
|
+
export async function runAcpPeer(config, settings) {
|
|
16
13
|
const stdout = Writable.toWeb(process.stdout);
|
|
17
14
|
const stdin = Readable.toWeb(process.stdin);
|
|
18
15
|
// Stdout is used to send messages to the client, so console.log/console.info
|
|
@@ -20,138 +17,61 @@ export async function runZedIntegration(config, settings, extensions, argv) {
|
|
|
20
17
|
console.log = console.error;
|
|
21
18
|
console.info = console.error;
|
|
22
19
|
console.debug = console.error;
|
|
23
|
-
new acp.
|
|
20
|
+
new acp.ClientConnection((client) => new GeminiAgent(config, settings, client), stdout, stdin);
|
|
24
21
|
}
|
|
25
22
|
class GeminiAgent {
|
|
26
23
|
config;
|
|
27
24
|
settings;
|
|
28
|
-
extensions;
|
|
29
|
-
argv;
|
|
30
25
|
client;
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
chat;
|
|
27
|
+
pendingSend;
|
|
28
|
+
constructor(config, settings, client) {
|
|
33
29
|
this.config = config;
|
|
34
30
|
this.settings = settings;
|
|
35
|
-
this.extensions = extensions;
|
|
36
|
-
this.argv = argv;
|
|
37
31
|
this.client = client;
|
|
38
32
|
}
|
|
39
|
-
async initialize(
|
|
40
|
-
const authMethods = [
|
|
41
|
-
{
|
|
42
|
-
id: AuthType.LOGIN_WITH_GOOGLE,
|
|
43
|
-
name: 'Log in with Google',
|
|
44
|
-
description: null,
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
id: AuthType.USE_GEMINI,
|
|
48
|
-
name: 'Use Gemini API key',
|
|
49
|
-
description: 'Requires setting the `GEMINI_API_KEY` environment variable',
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
id: AuthType.USE_VERTEX_AI,
|
|
53
|
-
name: 'Vertex AI',
|
|
54
|
-
description: null,
|
|
55
|
-
},
|
|
56
|
-
];
|
|
57
|
-
return {
|
|
58
|
-
protocolVersion: acp.PROTOCOL_VERSION,
|
|
59
|
-
authMethods,
|
|
60
|
-
agentCapabilities: {
|
|
61
|
-
loadSession: false,
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
async authenticate({ methodId }) {
|
|
66
|
-
const method = z.nativeEnum(AuthType).parse(methodId);
|
|
67
|
-
await clearCachedCredentialFile();
|
|
68
|
-
await this.config.refreshAuth(method);
|
|
69
|
-
this.settings.setValue(SettingScope.User, 'selectedAuthType', method);
|
|
70
|
-
}
|
|
71
|
-
async newSession({ cwd, mcpServers, }) {
|
|
72
|
-
const sessionId = randomUUID();
|
|
73
|
-
const config = await this.newSessionConfig(sessionId, cwd, mcpServers);
|
|
33
|
+
async initialize(_) {
|
|
74
34
|
let isAuthenticated = false;
|
|
75
35
|
if (this.settings.merged.selectedAuthType) {
|
|
76
36
|
try {
|
|
77
|
-
await config.refreshAuth(this.settings.merged.selectedAuthType);
|
|
37
|
+
await this.config.refreshAuth(this.settings.merged.selectedAuthType);
|
|
78
38
|
isAuthenticated = true;
|
|
79
39
|
}
|
|
80
|
-
catch (
|
|
81
|
-
console.error(
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
if (!isAuthenticated) {
|
|
85
|
-
throw acp.RequestError.authRequired();
|
|
86
|
-
}
|
|
87
|
-
const geminiClient = config.getGeminiClient();
|
|
88
|
-
const chat = await geminiClient.startChat();
|
|
89
|
-
const session = new Session(sessionId, chat, config, this.client);
|
|
90
|
-
this.sessions.set(sessionId, session);
|
|
91
|
-
return {
|
|
92
|
-
sessionId,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
async newSessionConfig(sessionId, cwd, mcpServers) {
|
|
96
|
-
const mergedMcpServers = { ...this.settings.merged.mcpServers };
|
|
97
|
-
for (const { command, args, env: rawEnv, name } of mcpServers) {
|
|
98
|
-
const env = {};
|
|
99
|
-
for (const { name: envName, value } of rawEnv) {
|
|
100
|
-
env[envName] = value;
|
|
40
|
+
catch (error) {
|
|
41
|
+
console.error('Failed to refresh auth:', error);
|
|
101
42
|
}
|
|
102
|
-
mergedMcpServers[name] = new MCPServerConfig(command, args, env, cwd);
|
|
103
43
|
}
|
|
104
|
-
|
|
105
|
-
const config = await loadCliConfig(settings, this.extensions, sessionId, this.argv, cwd);
|
|
106
|
-
await config.initialize();
|
|
107
|
-
return config;
|
|
44
|
+
return { protocolVersion: acp.LATEST_PROTOCOL_VERSION, isAuthenticated };
|
|
108
45
|
}
|
|
109
|
-
async
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
await session.cancelPendingPrompt();
|
|
115
|
-
}
|
|
116
|
-
async prompt(params) {
|
|
117
|
-
const session = this.sessions.get(params.sessionId);
|
|
118
|
-
if (!session) {
|
|
119
|
-
throw new Error(`Session not found: ${params.sessionId}`);
|
|
120
|
-
}
|
|
121
|
-
return session.prompt(params);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
class Session {
|
|
125
|
-
id;
|
|
126
|
-
chat;
|
|
127
|
-
config;
|
|
128
|
-
client;
|
|
129
|
-
pendingPrompt = null;
|
|
130
|
-
constructor(id, chat, config, client) {
|
|
131
|
-
this.id = id;
|
|
132
|
-
this.chat = chat;
|
|
133
|
-
this.config = config;
|
|
134
|
-
this.client = client;
|
|
46
|
+
async authenticate() {
|
|
47
|
+
await clearCachedCredentialFile();
|
|
48
|
+
await this.config.refreshAuth(AuthType.LOGIN_WITH_GOOGLE);
|
|
49
|
+
this.settings.setValue(SettingScope.User, 'selectedAuthType', AuthType.LOGIN_WITH_GOOGLE);
|
|
135
50
|
}
|
|
136
|
-
async
|
|
137
|
-
if (!this.
|
|
51
|
+
async cancelSendMessage() {
|
|
52
|
+
if (!this.pendingSend) {
|
|
138
53
|
throw new Error('Not currently generating');
|
|
139
54
|
}
|
|
140
|
-
this.
|
|
141
|
-
this.
|
|
55
|
+
this.pendingSend.abort();
|
|
56
|
+
delete this.pendingSend;
|
|
142
57
|
}
|
|
143
|
-
async
|
|
144
|
-
this.
|
|
58
|
+
async sendUserMessage(params) {
|
|
59
|
+
this.pendingSend?.abort();
|
|
145
60
|
const pendingSend = new AbortController();
|
|
146
|
-
this.
|
|
61
|
+
this.pendingSend = pendingSend;
|
|
62
|
+
if (!this.chat) {
|
|
63
|
+
const geminiClient = this.config.getGeminiClient();
|
|
64
|
+
this.chat = await geminiClient.startChat();
|
|
65
|
+
}
|
|
147
66
|
const promptId = Math.random().toString(16).slice(2);
|
|
148
67
|
const chat = this.chat;
|
|
149
|
-
const
|
|
68
|
+
const toolRegistry = await this.config.getToolRegistry();
|
|
69
|
+
const parts = await this.#resolveUserMessage(params, pendingSend.signal);
|
|
150
70
|
let nextMessage = { role: 'user', parts };
|
|
151
71
|
while (nextMessage !== null) {
|
|
152
72
|
if (pendingSend.signal.aborted) {
|
|
153
73
|
chat.addHistory(nextMessage);
|
|
154
|
-
return
|
|
74
|
+
return;
|
|
155
75
|
}
|
|
156
76
|
const functionCalls = [];
|
|
157
77
|
try {
|
|
@@ -159,12 +79,17 @@ class Session {
|
|
|
159
79
|
message: nextMessage?.parts ?? [],
|
|
160
80
|
config: {
|
|
161
81
|
abortSignal: pendingSend.signal,
|
|
82
|
+
tools: [
|
|
83
|
+
{
|
|
84
|
+
functionDeclarations: toolRegistry.getFunctionDeclarations(),
|
|
85
|
+
},
|
|
86
|
+
],
|
|
162
87
|
},
|
|
163
88
|
}, promptId);
|
|
164
89
|
nextMessage = null;
|
|
165
90
|
for await (const resp of responseStream) {
|
|
166
91
|
if (pendingSend.signal.aborted) {
|
|
167
|
-
return
|
|
92
|
+
return;
|
|
168
93
|
}
|
|
169
94
|
if (resp.candidates && resp.candidates.length > 0) {
|
|
170
95
|
const candidate = resp.candidates[0];
|
|
@@ -172,15 +97,10 @@ class Session {
|
|
|
172
97
|
if (!part.text) {
|
|
173
98
|
continue;
|
|
174
99
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
this.sendUpdate({
|
|
180
|
-
sessionUpdate: part.thought
|
|
181
|
-
? 'agent_thought_chunk'
|
|
182
|
-
: 'agent_message_chunk',
|
|
183
|
-
content,
|
|
100
|
+
this.client.streamAssistantMessageChunk({
|
|
101
|
+
chunk: part.thought
|
|
102
|
+
? { thought: part.text }
|
|
103
|
+
: { text: part.text },
|
|
184
104
|
});
|
|
185
105
|
}
|
|
186
106
|
}
|
|
@@ -198,7 +118,7 @@ class Session {
|
|
|
198
118
|
if (functionCalls.length > 0) {
|
|
199
119
|
const toolResponseParts = [];
|
|
200
120
|
for (const fc of functionCalls) {
|
|
201
|
-
const response = await this
|
|
121
|
+
const response = await this.#runTool(pendingSend.signal, promptId, fc);
|
|
202
122
|
const parts = Array.isArray(response) ? response : [response];
|
|
203
123
|
for (const part of parts) {
|
|
204
124
|
if (typeof part === 'string') {
|
|
@@ -212,16 +132,8 @@ class Session {
|
|
|
212
132
|
nextMessage = { role: 'user', parts: toolResponseParts };
|
|
213
133
|
}
|
|
214
134
|
}
|
|
215
|
-
return { stopReason: 'end_turn' };
|
|
216
|
-
}
|
|
217
|
-
async sendUpdate(update) {
|
|
218
|
-
const params = {
|
|
219
|
-
sessionId: this.id,
|
|
220
|
-
update,
|
|
221
|
-
};
|
|
222
|
-
await this.client.sessionUpdate(params);
|
|
223
135
|
}
|
|
224
|
-
async runTool(abortSignal, promptId, fc) {
|
|
136
|
+
async #runTool(abortSignal, promptId, fc) {
|
|
225
137
|
const callId = fc.id ?? `${fc.name}-${Date.now()}`;
|
|
226
138
|
const args = (fc.args ?? {});
|
|
227
139
|
const startTime = Date.now();
|
|
@@ -255,71 +167,59 @@ class Session {
|
|
|
255
167
|
if (!tool) {
|
|
256
168
|
return errorResponse(new Error(`Tool "${fc.name}" not found in registry.`));
|
|
257
169
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
const
|
|
262
|
-
if (confirmationDetails
|
|
263
|
-
content
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
status: 'pending',
|
|
276
|
-
title: invocation.getDescription(),
|
|
170
|
+
let toolCallId = undefined;
|
|
171
|
+
try {
|
|
172
|
+
const invocation = tool.build(args);
|
|
173
|
+
const confirmationDetails = await invocation.shouldConfirmExecute(abortSignal);
|
|
174
|
+
if (confirmationDetails) {
|
|
175
|
+
let content = null;
|
|
176
|
+
if (confirmationDetails.type === 'edit') {
|
|
177
|
+
content = {
|
|
178
|
+
type: 'diff',
|
|
179
|
+
path: confirmationDetails.fileName,
|
|
180
|
+
oldText: confirmationDetails.originalContent,
|
|
181
|
+
newText: confirmationDetails.newContent,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
const result = await this.client.requestToolCallConfirmation({
|
|
185
|
+
label: invocation.getDescription(),
|
|
186
|
+
icon: tool.icon,
|
|
277
187
|
content,
|
|
188
|
+
confirmation: toAcpToolCallConfirmation(confirmationDetails),
|
|
278
189
|
locations: invocation.toolLocations(),
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
case ToolConfirmationOutcome.ProceedAlwaysTool:
|
|
296
|
-
case ToolConfirmationOutcome.ModifyWithEditor:
|
|
297
|
-
break;
|
|
298
|
-
default: {
|
|
299
|
-
const resultOutcome = outcome;
|
|
300
|
-
throw new Error(`Unexpected: ${resultOutcome}`);
|
|
190
|
+
});
|
|
191
|
+
await confirmationDetails.onConfirm(toToolCallOutcome(result.outcome));
|
|
192
|
+
switch (result.outcome) {
|
|
193
|
+
case 'reject':
|
|
194
|
+
return errorResponse(new Error(`Tool "${fc.name}" not allowed to run by the user.`));
|
|
195
|
+
case 'cancel':
|
|
196
|
+
return errorResponse(new Error(`Tool "${fc.name}" was canceled by the user.`));
|
|
197
|
+
case 'allow':
|
|
198
|
+
case 'alwaysAllow':
|
|
199
|
+
case 'alwaysAllowMcpServer':
|
|
200
|
+
case 'alwaysAllowTool':
|
|
201
|
+
break;
|
|
202
|
+
default: {
|
|
203
|
+
const resultOutcome = result.outcome;
|
|
204
|
+
throw new Error(`Unexpected: ${resultOutcome}`);
|
|
205
|
+
}
|
|
301
206
|
}
|
|
207
|
+
toolCallId = result.id;
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
const result = await this.client.pushToolCall({
|
|
211
|
+
icon: tool.icon,
|
|
212
|
+
label: invocation.getDescription(),
|
|
213
|
+
locations: invocation.toolLocations(),
|
|
214
|
+
});
|
|
215
|
+
toolCallId = result.id;
|
|
302
216
|
}
|
|
303
|
-
}
|
|
304
|
-
else {
|
|
305
|
-
await this.sendUpdate({
|
|
306
|
-
sessionUpdate: 'tool_call',
|
|
307
|
-
toolCallId: callId,
|
|
308
|
-
status: 'in_progress',
|
|
309
|
-
title: invocation.getDescription(),
|
|
310
|
-
content: [],
|
|
311
|
-
locations: invocation.toolLocations(),
|
|
312
|
-
kind: tool.kind,
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
try {
|
|
316
217
|
const toolResult = await invocation.execute(abortSignal);
|
|
317
|
-
const
|
|
318
|
-
await this.
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
content: content ? [content] : [],
|
|
218
|
+
const toolCallContent = toToolCallContent(toolResult);
|
|
219
|
+
await this.client.updateToolCall({
|
|
220
|
+
toolCallId,
|
|
221
|
+
status: 'finished',
|
|
222
|
+
content: toolCallContent,
|
|
323
223
|
});
|
|
324
224
|
const durationMs = Date.now() - startTime;
|
|
325
225
|
logToolCall(this.config, {
|
|
@@ -335,47 +235,27 @@ class Session {
|
|
|
335
235
|
}
|
|
336
236
|
catch (e) {
|
|
337
237
|
const error = e instanceof Error ? e : new Error(String(e));
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
});
|
|
238
|
+
if (toolCallId) {
|
|
239
|
+
await this.client.updateToolCall({
|
|
240
|
+
toolCallId,
|
|
241
|
+
status: 'error',
|
|
242
|
+
content: { type: 'markdown', markdown: error.message },
|
|
243
|
+
});
|
|
244
|
+
}
|
|
346
245
|
return errorResponse(error);
|
|
347
246
|
}
|
|
348
247
|
}
|
|
349
|
-
async #
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
return {
|
|
356
|
-
fileData: {
|
|
357
|
-
mimeData: part.mimeType,
|
|
358
|
-
name: part.name,
|
|
359
|
-
fileUri: part.uri,
|
|
360
|
-
},
|
|
361
|
-
};
|
|
362
|
-
case 'resource': {
|
|
363
|
-
return {
|
|
364
|
-
fileData: {
|
|
365
|
-
mimeData: part.resource.mimeType,
|
|
366
|
-
name: part.resource.uri,
|
|
367
|
-
fileUri: part.resource.uri,
|
|
368
|
-
},
|
|
369
|
-
};
|
|
248
|
+
async #resolveUserMessage(message, abortSignal) {
|
|
249
|
+
const atPathCommandParts = message.chunks.filter((part) => 'path' in part);
|
|
250
|
+
if (atPathCommandParts.length === 0) {
|
|
251
|
+
return message.chunks.map((chunk) => {
|
|
252
|
+
if ('text' in chunk) {
|
|
253
|
+
return { text: chunk.text };
|
|
370
254
|
}
|
|
371
|
-
|
|
372
|
-
throw new Error(
|
|
255
|
+
else {
|
|
256
|
+
throw new Error('Unexpected chunk type');
|
|
373
257
|
}
|
|
374
|
-
}
|
|
375
|
-
});
|
|
376
|
-
const atPathCommandParts = parts.filter((part) => 'fileData' in part);
|
|
377
|
-
if (atPathCommandParts.length === 0) {
|
|
378
|
-
return parts;
|
|
258
|
+
});
|
|
379
259
|
}
|
|
380
260
|
// Get centralized file discovery service
|
|
381
261
|
const fileDiscovery = this.config.getFileService();
|
|
@@ -391,7 +271,7 @@ class Session {
|
|
|
391
271
|
throw new Error('Error: read_many_files tool not found.');
|
|
392
272
|
}
|
|
393
273
|
for (const atPathPart of atPathCommandParts) {
|
|
394
|
-
const pathName = atPathPart.
|
|
274
|
+
const pathName = atPathPart.path;
|
|
395
275
|
// Check if path should be ignored by git
|
|
396
276
|
if (fileDiscovery.shouldGitIgnoreFile(pathName)) {
|
|
397
277
|
ignoredPaths.push(pathName);
|
|
@@ -411,21 +291,21 @@ class Session {
|
|
|
411
291
|
currentPathSpec = pathName.endsWith('/')
|
|
412
292
|
? `${pathName}**`
|
|
413
293
|
: `${pathName}/**`;
|
|
414
|
-
this
|
|
294
|
+
this.#debug(`Path ${pathName} resolved to directory, using glob: ${currentPathSpec}`);
|
|
415
295
|
}
|
|
416
296
|
else {
|
|
417
|
-
this
|
|
297
|
+
this.#debug(`Path ${pathName} resolved to file: ${currentPathSpec}`);
|
|
418
298
|
}
|
|
419
299
|
resolvedSuccessfully = true;
|
|
420
300
|
}
|
|
421
301
|
else {
|
|
422
|
-
this
|
|
302
|
+
this.#debug(`Path ${pathName} is outside the project directory. Skipping.`);
|
|
423
303
|
}
|
|
424
304
|
}
|
|
425
305
|
catch (error) {
|
|
426
306
|
if (isNodeError(error) && error.code === 'ENOENT') {
|
|
427
307
|
if (this.config.getEnableRecursiveFileSearch() && globTool) {
|
|
428
|
-
this
|
|
308
|
+
this.#debug(`Path ${pathName} not found directly, attempting glob search.`);
|
|
429
309
|
try {
|
|
430
310
|
const globResult = await globTool.buildAndExecute({
|
|
431
311
|
pattern: `**/*${pathName}*`,
|
|
@@ -439,15 +319,15 @@ class Session {
|
|
|
439
319
|
if (lines.length > 1 && lines[1]) {
|
|
440
320
|
const firstMatchAbsolute = lines[1].trim();
|
|
441
321
|
currentPathSpec = path.relative(this.config.getTargetDir(), firstMatchAbsolute);
|
|
442
|
-
this
|
|
322
|
+
this.#debug(`Glob search for ${pathName} found ${firstMatchAbsolute}, using relative path: ${currentPathSpec}`);
|
|
443
323
|
resolvedSuccessfully = true;
|
|
444
324
|
}
|
|
445
325
|
else {
|
|
446
|
-
this
|
|
326
|
+
this.#debug(`Glob search for '**/*${pathName}*' did not return a usable path. Path ${pathName} will be skipped.`);
|
|
447
327
|
}
|
|
448
328
|
}
|
|
449
329
|
else {
|
|
450
|
-
this
|
|
330
|
+
this.#debug(`Glob search for '**/*${pathName}*' found no files or an error. Path ${pathName} will be skipped.`);
|
|
451
331
|
}
|
|
452
332
|
}
|
|
453
333
|
catch (globError) {
|
|
@@ -455,7 +335,7 @@ class Session {
|
|
|
455
335
|
}
|
|
456
336
|
}
|
|
457
337
|
else {
|
|
458
|
-
this
|
|
338
|
+
this.#debug(`Glob tool not found. Path ${pathName} will be skipped.`);
|
|
459
339
|
}
|
|
460
340
|
}
|
|
461
341
|
else {
|
|
@@ -470,23 +350,22 @@ class Session {
|
|
|
470
350
|
}
|
|
471
351
|
// Construct the initial part of the query for the LLM
|
|
472
352
|
let initialQueryText = '';
|
|
473
|
-
for (let i = 0; i <
|
|
474
|
-
const chunk =
|
|
353
|
+
for (let i = 0; i < message.chunks.length; i++) {
|
|
354
|
+
const chunk = message.chunks[i];
|
|
475
355
|
if ('text' in chunk) {
|
|
476
356
|
initialQueryText += chunk.text;
|
|
477
357
|
}
|
|
478
358
|
else {
|
|
479
359
|
// type === 'atPath'
|
|
480
|
-
const resolvedSpec =
|
|
360
|
+
const resolvedSpec = atPathToResolvedSpecMap.get(chunk.path);
|
|
481
361
|
if (i > 0 &&
|
|
482
362
|
initialQueryText.length > 0 &&
|
|
483
363
|
!initialQueryText.endsWith(' ') &&
|
|
484
364
|
resolvedSpec) {
|
|
485
365
|
// Add space if previous part was text and didn't end with space, or if previous was @path
|
|
486
|
-
const prevPart =
|
|
366
|
+
const prevPart = message.chunks[i - 1];
|
|
487
367
|
if ('text' in prevPart ||
|
|
488
|
-
('
|
|
489
|
-
atPathToResolvedSpecMap.has(prevPart.fileData.fileUri))) {
|
|
368
|
+
('path' in prevPart && atPathToResolvedSpecMap.has(prevPart.path))) {
|
|
490
369
|
initialQueryText += ' ';
|
|
491
370
|
}
|
|
492
371
|
}
|
|
@@ -499,12 +378,10 @@ class Session {
|
|
|
499
378
|
if (i > 0 &&
|
|
500
379
|
initialQueryText.length > 0 &&
|
|
501
380
|
!initialQueryText.endsWith(' ') &&
|
|
502
|
-
!chunk.
|
|
381
|
+
!chunk.path.startsWith(' ')) {
|
|
503
382
|
initialQueryText += ' ';
|
|
504
383
|
}
|
|
505
|
-
|
|
506
|
-
initialQueryText += `@${chunk.fileData.fileUri}`;
|
|
507
|
-
}
|
|
384
|
+
initialQueryText += `@${chunk.path}`;
|
|
508
385
|
}
|
|
509
386
|
}
|
|
510
387
|
}
|
|
@@ -512,7 +389,7 @@ class Session {
|
|
|
512
389
|
// Inform user about ignored paths
|
|
513
390
|
if (ignoredPaths.length > 0) {
|
|
514
391
|
const ignoreType = respectGitIgnore ? 'git-ignored' : 'custom-ignored';
|
|
515
|
-
this
|
|
392
|
+
this.#debug(`Ignored ${ignoredPaths.length} ${ignoreType} files: ${ignoredPaths.join(', ')}`);
|
|
516
393
|
}
|
|
517
394
|
// Fallback for lone "@" or completely invalid @-commands resulting in empty initialQueryText
|
|
518
395
|
if (pathSpecsToRead.length === 0) {
|
|
@@ -524,31 +401,23 @@ class Session {
|
|
|
524
401
|
paths: pathSpecsToRead,
|
|
525
402
|
respectGitIgnore, // Use configuration setting
|
|
526
403
|
};
|
|
527
|
-
|
|
404
|
+
let toolCallId = undefined;
|
|
528
405
|
try {
|
|
529
406
|
const invocation = readManyFilesTool.build(toolArgs);
|
|
530
|
-
await this.
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
status: 'in_progress',
|
|
534
|
-
title: invocation.getDescription(),
|
|
535
|
-
content: [],
|
|
536
|
-
locations: invocation.toolLocations(),
|
|
537
|
-
kind: readManyFilesTool.kind,
|
|
407
|
+
const toolCall = await this.client.pushToolCall({
|
|
408
|
+
icon: readManyFilesTool.icon,
|
|
409
|
+
label: invocation.getDescription(),
|
|
538
410
|
});
|
|
411
|
+
toolCallId = toolCall.id;
|
|
539
412
|
const result = await invocation.execute(abortSignal);
|
|
540
413
|
const content = toToolCallContent(result) || {
|
|
541
|
-
type: '
|
|
542
|
-
|
|
543
|
-
type: 'text',
|
|
544
|
-
text: `Successfully read: ${contentLabelsForDisplay.join(', ')}`,
|
|
545
|
-
},
|
|
414
|
+
type: 'markdown',
|
|
415
|
+
markdown: `Successfully read: ${contentLabelsForDisplay.join(', ')}`,
|
|
546
416
|
};
|
|
547
|
-
await this.
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
content: content ? [content] : [],
|
|
417
|
+
await this.client.updateToolCall({
|
|
418
|
+
toolCallId: toolCall.id,
|
|
419
|
+
status: 'finished',
|
|
420
|
+
content,
|
|
552
421
|
});
|
|
553
422
|
if (Array.isArray(result.llmContent)) {
|
|
554
423
|
const fileContentRegex = /^--- (.*?) ---\n\n([\s\S]*?)\n\n$/;
|
|
@@ -583,24 +452,20 @@ class Session {
|
|
|
583
452
|
return processedQueryParts;
|
|
584
453
|
}
|
|
585
454
|
catch (error) {
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
content: {
|
|
594
|
-
type: 'text',
|
|
595
|
-
text: `Error reading files (${contentLabelsForDisplay.join(', ')}): ${getErrorMessage(error)}`,
|
|
596
|
-
},
|
|
455
|
+
if (toolCallId) {
|
|
456
|
+
await this.client.updateToolCall({
|
|
457
|
+
toolCallId,
|
|
458
|
+
status: 'error',
|
|
459
|
+
content: {
|
|
460
|
+
type: 'markdown',
|
|
461
|
+
markdown: `Error reading files (${contentLabelsForDisplay.join(', ')}): ${getErrorMessage(error)}`,
|
|
597
462
|
},
|
|
598
|
-
|
|
599
|
-
}
|
|
463
|
+
});
|
|
464
|
+
}
|
|
600
465
|
throw error;
|
|
601
466
|
}
|
|
602
467
|
}
|
|
603
|
-
debug(msg) {
|
|
468
|
+
#debug(msg) {
|
|
604
469
|
if (this.config.getDebugMode()) {
|
|
605
470
|
console.warn(msg);
|
|
606
471
|
}
|
|
@@ -610,8 +475,8 @@ function toToolCallContent(toolResult) {
|
|
|
610
475
|
if (toolResult.returnDisplay) {
|
|
611
476
|
if (typeof toolResult.returnDisplay === 'string') {
|
|
612
477
|
return {
|
|
613
|
-
type: '
|
|
614
|
-
|
|
478
|
+
type: 'markdown',
|
|
479
|
+
markdown: toolResult.returnDisplay,
|
|
615
480
|
};
|
|
616
481
|
}
|
|
617
482
|
else {
|
|
@@ -627,65 +492,54 @@ function toToolCallContent(toolResult) {
|
|
|
627
492
|
return null;
|
|
628
493
|
}
|
|
629
494
|
}
|
|
630
|
-
|
|
631
|
-
{
|
|
632
|
-
optionId: ToolConfirmationOutcome.ProceedOnce,
|
|
633
|
-
name: 'Allow',
|
|
634
|
-
kind: 'allow_once',
|
|
635
|
-
},
|
|
636
|
-
{
|
|
637
|
-
optionId: ToolConfirmationOutcome.Cancel,
|
|
638
|
-
name: 'Reject',
|
|
639
|
-
kind: 'reject_once',
|
|
640
|
-
},
|
|
641
|
-
];
|
|
642
|
-
function toPermissionOptions(confirmation) {
|
|
643
|
-
switch (confirmation.type) {
|
|
495
|
+
function toAcpToolCallConfirmation(confirmationDetails) {
|
|
496
|
+
switch (confirmationDetails.type) {
|
|
644
497
|
case 'edit':
|
|
645
|
-
return
|
|
646
|
-
{
|
|
647
|
-
optionId: ToolConfirmationOutcome.ProceedAlways,
|
|
648
|
-
name: 'Allow All Edits',
|
|
649
|
-
kind: 'allow_always',
|
|
650
|
-
},
|
|
651
|
-
...basicPermissionOptions,
|
|
652
|
-
];
|
|
498
|
+
return { type: 'edit' };
|
|
653
499
|
case 'exec':
|
|
654
|
-
return
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
},
|
|
660
|
-
...basicPermissionOptions,
|
|
661
|
-
];
|
|
500
|
+
return {
|
|
501
|
+
type: 'execute',
|
|
502
|
+
rootCommand: confirmationDetails.rootCommand,
|
|
503
|
+
command: confirmationDetails.command,
|
|
504
|
+
};
|
|
662
505
|
case 'mcp':
|
|
663
|
-
return
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
{
|
|
670
|
-
optionId: ToolConfirmationOutcome.ProceedAlwaysTool,
|
|
671
|
-
name: `Always Allow ${confirmation.toolName}`,
|
|
672
|
-
kind: 'allow_always',
|
|
673
|
-
},
|
|
674
|
-
...basicPermissionOptions,
|
|
675
|
-
];
|
|
506
|
+
return {
|
|
507
|
+
type: 'mcp',
|
|
508
|
+
serverName: confirmationDetails.serverName,
|
|
509
|
+
toolName: confirmationDetails.toolName,
|
|
510
|
+
toolDisplayName: confirmationDetails.toolDisplayName,
|
|
511
|
+
};
|
|
676
512
|
case 'info':
|
|
677
|
-
return
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
513
|
+
return {
|
|
514
|
+
type: 'fetch',
|
|
515
|
+
urls: confirmationDetails.urls || [],
|
|
516
|
+
description: confirmationDetails.urls?.length
|
|
517
|
+
? null
|
|
518
|
+
: confirmationDetails.prompt,
|
|
519
|
+
};
|
|
520
|
+
default: {
|
|
521
|
+
const unreachable = confirmationDetails;
|
|
522
|
+
throw new Error(`Unexpected: ${unreachable}`);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
function toToolCallOutcome(outcome) {
|
|
527
|
+
switch (outcome) {
|
|
528
|
+
case 'allow':
|
|
529
|
+
return ToolConfirmationOutcome.ProceedOnce;
|
|
530
|
+
case 'alwaysAllow':
|
|
531
|
+
return ToolConfirmationOutcome.ProceedAlways;
|
|
532
|
+
case 'alwaysAllowMcpServer':
|
|
533
|
+
return ToolConfirmationOutcome.ProceedAlwaysServer;
|
|
534
|
+
case 'alwaysAllowTool':
|
|
535
|
+
return ToolConfirmationOutcome.ProceedAlwaysTool;
|
|
536
|
+
case 'reject':
|
|
537
|
+
case 'cancel':
|
|
538
|
+
return ToolConfirmationOutcome.Cancel;
|
|
685
539
|
default: {
|
|
686
|
-
const unreachable =
|
|
540
|
+
const unreachable = outcome;
|
|
687
541
|
throw new Error(`Unexpected: ${unreachable}`);
|
|
688
542
|
}
|
|
689
543
|
}
|
|
690
544
|
}
|
|
691
|
-
//# sourceMappingURL=
|
|
545
|
+
//# sourceMappingURL=acpPeer.js.map
|