@google/gemini-cli-core 0.17.0-nightly.20251116.e650a4ee5 → 0.17.0-preview.0
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 +2 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/src/code_assist/experiments/flagNames.d.ts +3 -0
- package/dist/src/code_assist/experiments/flagNames.js +3 -0
- package/dist/src/code_assist/experiments/flagNames.js.map +1 -1
- package/dist/src/config/config.d.ts +13 -0
- package/dist/src/config/config.js +44 -1
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +77 -0
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/models.d.ts +22 -1
- package/dist/src/config/models.js +48 -5
- package/dist/src/config/models.js.map +1 -1
- package/dist/src/config/models.test.js +71 -10
- package/dist/src/config/models.test.js.map +1 -1
- package/dist/src/core/client.d.ts +0 -1
- package/dist/src/core/client.js +5 -12
- package/dist/src/core/client.js.map +1 -1
- package/dist/src/core/client.test.js +5 -16
- package/dist/src/core/client.test.js.map +1 -1
- package/dist/src/core/geminiChat.d.ts +2 -0
- package/dist/src/core/geminiChat.js +91 -12
- package/dist/src/core/geminiChat.js.map +1 -1
- package/dist/src/core/geminiChat.test.js +253 -18
- package/dist/src/core/geminiChat.test.js.map +1 -1
- package/dist/src/fallback/handler.js +52 -5
- package/dist/src/fallback/handler.js.map +1 -1
- package/dist/src/fallback/handler.test.js +64 -4
- package/dist/src/fallback/handler.test.js.map +1 -1
- package/dist/src/fallback/types.d.ts +1 -1
- 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/ide/detect-ide.d.ts +4 -0
- package/dist/src/ide/detect-ide.js +4 -0
- package/dist/src/ide/detect-ide.js.map +1 -1
- package/dist/src/ide/detect-ide.test.js +5 -0
- package/dist/src/ide/detect-ide.test.js.map +1 -1
- package/dist/src/ide/ide-client.d.ts +3 -1
- package/dist/src/ide/ide-client.js +5 -4
- package/dist/src/ide/ide-client.js.map +1 -1
- package/dist/src/ide/ide-installer.js +65 -20
- package/dist/src/ide/ide-installer.js.map +1 -1
- package/dist/src/ide/ide-installer.test.js +41 -0
- package/dist/src/ide/ide-installer.test.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/routing/modelRouterService.js +15 -0
- package/dist/src/routing/modelRouterService.js.map +1 -1
- package/dist/src/routing/modelRouterService.test.js +62 -0
- package/dist/src/routing/modelRouterService.test.js.map +1 -1
- package/dist/src/routing/strategies/classifierStrategy.d.ts +1 -1
- package/dist/src/routing/strategies/classifierStrategy.js +4 -4
- package/dist/src/routing/strategies/classifierStrategy.js.map +1 -1
- package/dist/src/routing/strategies/classifierStrategy.test.js +1 -0
- package/dist/src/routing/strategies/classifierStrategy.test.js.map +1 -1
- package/dist/src/routing/strategies/fallbackStrategy.js +1 -1
- package/dist/src/routing/strategies/fallbackStrategy.js.map +1 -1
- package/dist/src/routing/strategies/fallbackStrategy.test.js +4 -0
- package/dist/src/routing/strategies/fallbackStrategy.test.js.map +1 -1
- package/dist/src/routing/strategies/overrideStrategy.js +2 -2
- package/dist/src/routing/strategies/overrideStrategy.js.map +1 -1
- package/dist/src/routing/strategies/overrideStrategy.test.js +3 -0
- package/dist/src/routing/strategies/overrideStrategy.test.js.map +1 -1
- package/dist/src/tools/edit.test.js +203 -200
- package/dist/src/tools/edit.test.js.map +1 -1
- package/dist/src/tools/mcp-client.d.ts +4 -2
- package/dist/src/tools/mcp-client.js +76 -22
- package/dist/src/tools/mcp-client.js.map +1 -1
- package/dist/src/tools/mcp-client.test.js +111 -42
- package/dist/src/tools/mcp-client.test.js.map +1 -1
- package/dist/src/tools/mcp-tool.test.js +186 -273
- package/dist/src/tools/mcp-tool.test.js.map +1 -1
- package/dist/src/tools/ripGrep.test.js +84 -126
- package/dist/src/tools/ripGrep.test.js.map +1 -1
- package/dist/src/tools/smart-edit.test.js +92 -111
- package/dist/src/tools/smart-edit.test.js.map +1 -1
- package/dist/src/tools/tool-registry.test.js +59 -90
- package/dist/src/tools/tool-registry.test.js.map +1 -1
- package/dist/src/tools/web-fetch.test.js +121 -179
- package/dist/src/tools/web-fetch.test.js.map +1 -1
- package/dist/src/tools/write-file.test.js +105 -116
- package/dist/src/tools/write-file.test.js.map +1 -1
- package/dist/src/utils/editor.d.ts +3 -1
- package/dist/src/utils/editor.js +18 -1
- package/dist/src/utils/editor.js.map +1 -1
- package/dist/src/utils/editor.test.js +11 -0
- package/dist/src/utils/editor.test.js.map +1 -1
- package/dist/src/utils/flashFallback.test.js +2 -2
- package/dist/src/utils/flashFallback.test.js.map +1 -1
- package/dist/src/utils/googleQuotaErrors.d.ts +2 -1
- package/dist/src/utils/googleQuotaErrors.js +20 -12
- package/dist/src/utils/googleQuotaErrors.js.map +1 -1
- 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/retry.d.ts +0 -9
- package/dist/src/utils/retry.js +24 -28
- package/dist/src/utils/retry.js.map +1 -1
- package/dist/src/utils/retry.test.js +51 -0
- package/dist/src/utils/retry.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/dist/google-gemini-cli-core-0.17.0-nightly.20251114.0fcbff506.tgz +0 -0
|
@@ -17,6 +17,14 @@ const mockCallableToolInstance = {
|
|
|
17
17
|
callTool: mockCallTool,
|
|
18
18
|
// Add other methods if DiscoveredMCPTool starts using them
|
|
19
19
|
};
|
|
20
|
+
const createSdkResponse = (toolName, response) => [
|
|
21
|
+
{
|
|
22
|
+
functionResponse: {
|
|
23
|
+
name: toolName,
|
|
24
|
+
response,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
];
|
|
20
28
|
describe('generateValidName', () => {
|
|
21
29
|
it('should return a valid name for a simple function', () => {
|
|
22
30
|
expect(generateValidName('myFunction')).toBe('myFunction');
|
|
@@ -30,14 +38,12 @@ describe('generateValidName', () => {
|
|
|
30
38
|
it('should handle names with only invalid characters', () => {
|
|
31
39
|
expect(generateValidName('!@#$%^&*()')).toBe('__________');
|
|
32
40
|
});
|
|
33
|
-
it(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
it('should handle names that are longer than 64 characters', () => {
|
|
40
|
-
expect(generateValidName('a'.repeat(80)).length).toBe(63);
|
|
41
|
+
it.each([
|
|
42
|
+
{ length: 63, expected: 63, description: 'exactly 63 characters' },
|
|
43
|
+
{ length: 64, expected: 63, description: 'exactly 64 characters' },
|
|
44
|
+
{ length: 80, expected: 63, description: 'longer than 64 characters' },
|
|
45
|
+
])('should handle names that are $description long', ({ length, expected }) => {
|
|
46
|
+
expect(generateValidName('a'.repeat(length)).length).toBe(expected);
|
|
41
47
|
});
|
|
42
48
|
});
|
|
43
49
|
describe('DiscoveredMCPTool', () => {
|
|
@@ -189,20 +195,9 @@ describe('DiscoveredMCPTool', () => {
|
|
|
189
195
|
it('should handle a simple text response correctly', async () => {
|
|
190
196
|
const params = { param: 'test' };
|
|
191
197
|
const successMessage = 'This is a success message.';
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
{
|
|
196
|
-
functionResponse: {
|
|
197
|
-
name: serverToolName,
|
|
198
|
-
response: {
|
|
199
|
-
// The `content` array contains MCP ContentBlocks.
|
|
200
|
-
content: [{ type: 'text', text: successMessage }],
|
|
201
|
-
},
|
|
202
|
-
},
|
|
203
|
-
},
|
|
204
|
-
];
|
|
205
|
-
mockCallTool.mockResolvedValue(sdkResponse);
|
|
198
|
+
mockCallTool.mockResolvedValue(createSdkResponse(serverToolName, {
|
|
199
|
+
content: [{ type: 'text', text: successMessage }],
|
|
200
|
+
}));
|
|
206
201
|
const invocation = tool.build(params);
|
|
207
202
|
const toolResult = await invocation.execute(new AbortController().signal);
|
|
208
203
|
// 1. Assert that the llmContent sent to the scheduler is a clean Part array.
|
|
@@ -216,23 +211,15 @@ describe('DiscoveredMCPTool', () => {
|
|
|
216
211
|
});
|
|
217
212
|
it('should handle an AudioBlock response', async () => {
|
|
218
213
|
const params = { param: 'play' };
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
{
|
|
226
|
-
type: 'audio',
|
|
227
|
-
data: 'BASE64_AUDIO_DATA',
|
|
228
|
-
mimeType: 'audio/mp3',
|
|
229
|
-
},
|
|
230
|
-
],
|
|
231
|
-
},
|
|
214
|
+
mockCallTool.mockResolvedValue(createSdkResponse(serverToolName, {
|
|
215
|
+
content: [
|
|
216
|
+
{
|
|
217
|
+
type: 'audio',
|
|
218
|
+
data: 'BASE64_AUDIO_DATA',
|
|
219
|
+
mimeType: 'audio/mp3',
|
|
232
220
|
},
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
mockCallTool.mockResolvedValue(sdkResponse);
|
|
221
|
+
],
|
|
222
|
+
}));
|
|
236
223
|
const invocation = tool.build(params);
|
|
237
224
|
const toolResult = await invocation.execute(new AbortController().signal);
|
|
238
225
|
expect(toolResult.llmContent).toEqual([
|
|
@@ -250,24 +237,16 @@ describe('DiscoveredMCPTool', () => {
|
|
|
250
237
|
});
|
|
251
238
|
it('should handle a ResourceLinkBlock response', async () => {
|
|
252
239
|
const params = { param: 'get' };
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
type: 'resource_link',
|
|
261
|
-
uri: 'file:///path/to/thing',
|
|
262
|
-
name: 'resource-name',
|
|
263
|
-
title: 'My Resource',
|
|
264
|
-
},
|
|
265
|
-
],
|
|
266
|
-
},
|
|
240
|
+
mockCallTool.mockResolvedValue(createSdkResponse(serverToolName, {
|
|
241
|
+
content: [
|
|
242
|
+
{
|
|
243
|
+
type: 'resource_link',
|
|
244
|
+
uri: 'file:///path/to/thing',
|
|
245
|
+
name: 'resource-name',
|
|
246
|
+
title: 'My Resource',
|
|
267
247
|
},
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
mockCallTool.mockResolvedValue(sdkResponse);
|
|
248
|
+
],
|
|
249
|
+
}));
|
|
271
250
|
const invocation = tool.build(params);
|
|
272
251
|
const toolResult = await invocation.execute(new AbortController().signal);
|
|
273
252
|
expect(toolResult.llmContent).toEqual([
|
|
@@ -279,26 +258,18 @@ describe('DiscoveredMCPTool', () => {
|
|
|
279
258
|
});
|
|
280
259
|
it('should handle an embedded text ResourceBlock response', async () => {
|
|
281
260
|
const params = { param: 'get' };
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
resource: {
|
|
291
|
-
uri: 'file:///path/to/text.txt',
|
|
292
|
-
text: 'This is the text content.',
|
|
293
|
-
mimeType: 'text/plain',
|
|
294
|
-
},
|
|
295
|
-
},
|
|
296
|
-
],
|
|
261
|
+
mockCallTool.mockResolvedValue(createSdkResponse(serverToolName, {
|
|
262
|
+
content: [
|
|
263
|
+
{
|
|
264
|
+
type: 'resource',
|
|
265
|
+
resource: {
|
|
266
|
+
uri: 'file:///path/to/text.txt',
|
|
267
|
+
text: 'This is the text content.',
|
|
268
|
+
mimeType: 'text/plain',
|
|
297
269
|
},
|
|
298
270
|
},
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
mockCallTool.mockResolvedValue(sdkResponse);
|
|
271
|
+
],
|
|
272
|
+
}));
|
|
302
273
|
const invocation = tool.build(params);
|
|
303
274
|
const toolResult = await invocation.execute(new AbortController().signal);
|
|
304
275
|
expect(toolResult.llmContent).toEqual([
|
|
@@ -308,26 +279,18 @@ describe('DiscoveredMCPTool', () => {
|
|
|
308
279
|
});
|
|
309
280
|
it('should handle an embedded binary ResourceBlock response', async () => {
|
|
310
281
|
const params = { param: 'get' };
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
resource: {
|
|
320
|
-
uri: 'file:///path/to/data.bin',
|
|
321
|
-
blob: 'BASE64_BINARY_DATA',
|
|
322
|
-
mimeType: 'application/octet-stream',
|
|
323
|
-
},
|
|
324
|
-
},
|
|
325
|
-
],
|
|
282
|
+
mockCallTool.mockResolvedValue(createSdkResponse(serverToolName, {
|
|
283
|
+
content: [
|
|
284
|
+
{
|
|
285
|
+
type: 'resource',
|
|
286
|
+
resource: {
|
|
287
|
+
uri: 'file:///path/to/data.bin',
|
|
288
|
+
blob: 'BASE64_BINARY_DATA',
|
|
289
|
+
mimeType: 'application/octet-stream',
|
|
326
290
|
},
|
|
327
291
|
},
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
mockCallTool.mockResolvedValue(sdkResponse);
|
|
292
|
+
],
|
|
293
|
+
}));
|
|
331
294
|
const invocation = tool.build(params);
|
|
332
295
|
const toolResult = await invocation.execute(new AbortController().signal);
|
|
333
296
|
expect(toolResult.llmContent).toEqual([
|
|
@@ -345,25 +308,17 @@ describe('DiscoveredMCPTool', () => {
|
|
|
345
308
|
});
|
|
346
309
|
it('should handle a mix of content block types', async () => {
|
|
347
310
|
const params = { param: 'complex' };
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
{
|
|
356
|
-
type: 'image',
|
|
357
|
-
data: 'BASE64_IMAGE_DATA',
|
|
358
|
-
mimeType: 'image/jpeg',
|
|
359
|
-
},
|
|
360
|
-
{ type: 'text', text: 'Second part.' },
|
|
361
|
-
],
|
|
362
|
-
},
|
|
311
|
+
mockCallTool.mockResolvedValue(createSdkResponse(serverToolName, {
|
|
312
|
+
content: [
|
|
313
|
+
{ type: 'text', text: 'First part.' },
|
|
314
|
+
{
|
|
315
|
+
type: 'image',
|
|
316
|
+
data: 'BASE64_IMAGE_DATA',
|
|
317
|
+
mimeType: 'image/jpeg',
|
|
363
318
|
},
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
319
|
+
{ type: 'text', text: 'Second part.' },
|
|
320
|
+
],
|
|
321
|
+
}));
|
|
367
322
|
const invocation = tool.build(params);
|
|
368
323
|
const toolResult = await invocation.execute(new AbortController().signal);
|
|
369
324
|
expect(toolResult.llmContent).toEqual([
|
|
@@ -383,20 +338,12 @@ describe('DiscoveredMCPTool', () => {
|
|
|
383
338
|
});
|
|
384
339
|
it('should ignore unknown content block types', async () => {
|
|
385
340
|
const params = { param: 'test' };
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
{ type: 'text', text: 'Valid part.' },
|
|
393
|
-
{ type: 'future_block', data: 'some-data' },
|
|
394
|
-
],
|
|
395
|
-
},
|
|
396
|
-
},
|
|
397
|
-
},
|
|
398
|
-
];
|
|
399
|
-
mockCallTool.mockResolvedValue(sdkResponse);
|
|
341
|
+
mockCallTool.mockResolvedValue(createSdkResponse(serverToolName, {
|
|
342
|
+
content: [
|
|
343
|
+
{ type: 'text', text: 'Valid part.' },
|
|
344
|
+
{ type: 'future_block', data: 'some-data' },
|
|
345
|
+
],
|
|
346
|
+
}));
|
|
400
347
|
const invocation = tool.build(params);
|
|
401
348
|
const toolResult = await invocation.execute(new AbortController().signal);
|
|
402
349
|
expect(toolResult.llmContent).toEqual([{ text: 'Valid part.' }]);
|
|
@@ -404,38 +351,30 @@ describe('DiscoveredMCPTool', () => {
|
|
|
404
351
|
});
|
|
405
352
|
it('should handle a complex mix of content block types', async () => {
|
|
406
353
|
const params = { param: 'super-complex' };
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
resource: {
|
|
423
|
-
uri: 'file:///path/to/text.txt',
|
|
424
|
-
text: 'Embedded text content.',
|
|
425
|
-
mimeType: 'text/plain',
|
|
426
|
-
},
|
|
427
|
-
},
|
|
428
|
-
{
|
|
429
|
-
type: 'image',
|
|
430
|
-
data: 'BASE64_IMAGE_DATA',
|
|
431
|
-
mimeType: 'image/jpeg',
|
|
432
|
-
},
|
|
433
|
-
],
|
|
354
|
+
mockCallTool.mockResolvedValue(createSdkResponse(serverToolName, {
|
|
355
|
+
content: [
|
|
356
|
+
{ type: 'text', text: 'Here is a resource.' },
|
|
357
|
+
{
|
|
358
|
+
type: 'resource_link',
|
|
359
|
+
uri: 'file:///path/to/resource',
|
|
360
|
+
name: 'resource-name',
|
|
361
|
+
title: 'My Resource',
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
type: 'resource',
|
|
365
|
+
resource: {
|
|
366
|
+
uri: 'file:///path/to/text.txt',
|
|
367
|
+
text: 'Embedded text content.',
|
|
368
|
+
mimeType: 'text/plain',
|
|
434
369
|
},
|
|
435
370
|
},
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
371
|
+
{
|
|
372
|
+
type: 'image',
|
|
373
|
+
data: 'BASE64_IMAGE_DATA',
|
|
374
|
+
mimeType: 'image/jpeg',
|
|
375
|
+
},
|
|
376
|
+
],
|
|
377
|
+
}));
|
|
439
378
|
const invocation = tool.build(params);
|
|
440
379
|
const toolResult = await invocation.execute(new AbortController().signal);
|
|
441
380
|
expect(toolResult.llmContent).toEqual([
|
|
@@ -457,6 +396,8 @@ describe('DiscoveredMCPTool', () => {
|
|
|
457
396
|
expect(toolResult.returnDisplay).toBe('Here is a resource.\n[Link to My Resource: file:///path/to/resource]\nEmbedded text content.\n[Image: image/jpeg]');
|
|
458
397
|
});
|
|
459
398
|
describe('AbortSignal support', () => {
|
|
399
|
+
const MOCK_TOOL_DELAY = 1000;
|
|
400
|
+
const ABORT_DELAY = 50;
|
|
460
401
|
it('should abort immediately if signal is already aborted', async () => {
|
|
461
402
|
const params = { param: 'test' };
|
|
462
403
|
const controller = new AbortController();
|
|
@@ -482,28 +423,20 @@ describe('DiscoveredMCPTool', () => {
|
|
|
482
423
|
},
|
|
483
424
|
},
|
|
484
425
|
]);
|
|
485
|
-
},
|
|
426
|
+
}, MOCK_TOOL_DELAY);
|
|
486
427
|
}));
|
|
487
428
|
const invocation = tool.build(params);
|
|
488
429
|
const promise = invocation.execute(controller.signal);
|
|
489
430
|
// Abort after a short delay to simulate cancellation during execution
|
|
490
|
-
setTimeout(() => controller.abort(),
|
|
431
|
+
setTimeout(() => controller.abort(), ABORT_DELAY);
|
|
491
432
|
await expect(promise).rejects.toThrow('Tool call aborted');
|
|
492
433
|
});
|
|
493
434
|
it('should complete successfully if not aborted', async () => {
|
|
494
435
|
const params = { param: 'test' };
|
|
495
436
|
const controller = new AbortController();
|
|
496
|
-
|
|
497
|
-
{
|
|
498
|
-
|
|
499
|
-
name: serverToolName,
|
|
500
|
-
response: {
|
|
501
|
-
content: [{ type: 'text', text: 'Success' }],
|
|
502
|
-
},
|
|
503
|
-
},
|
|
504
|
-
},
|
|
505
|
-
];
|
|
506
|
-
mockCallTool.mockResolvedValue(successResponse);
|
|
437
|
+
mockCallTool.mockResolvedValue(createSdkResponse(serverToolName, {
|
|
438
|
+
content: [{ type: 'text', text: 'Success' }],
|
|
439
|
+
}));
|
|
507
440
|
const invocation = tool.build(params);
|
|
508
441
|
const result = await invocation.execute(controller.signal);
|
|
509
442
|
expect(result.llmContent).toEqual([{ text: 'Success' }]);
|
|
@@ -515,15 +448,7 @@ describe('DiscoveredMCPTool', () => {
|
|
|
515
448
|
it('should handle tool error even when abort signal is provided', async () => {
|
|
516
449
|
const params = { param: 'test' };
|
|
517
450
|
const controller = new AbortController();
|
|
518
|
-
|
|
519
|
-
{
|
|
520
|
-
functionResponse: {
|
|
521
|
-
name: serverToolName,
|
|
522
|
-
response: { error: { isError: true } },
|
|
523
|
-
},
|
|
524
|
-
},
|
|
525
|
-
];
|
|
526
|
-
mockCallTool.mockResolvedValue(errorResponse);
|
|
451
|
+
mockCallTool.mockResolvedValue(createSdkResponse(serverToolName, { error: { isError: true } }));
|
|
527
452
|
const invocation = tool.build(params);
|
|
528
453
|
const result = await invocation.execute(controller.signal);
|
|
529
454
|
expect(result.error?.type).toBe(ToolErrorType.MCP_TOOL_ERROR);
|
|
@@ -537,38 +462,40 @@ describe('DiscoveredMCPTool', () => {
|
|
|
537
462
|
const invocation = tool.build(params);
|
|
538
463
|
await expect(invocation.execute(controller.signal)).rejects.toThrow(expectedError);
|
|
539
464
|
});
|
|
540
|
-
it(
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
response: {
|
|
548
|
-
content: [{ type: 'text', text: 'Success' }],
|
|
549
|
-
},
|
|
550
|
-
},
|
|
465
|
+
it.each([
|
|
466
|
+
{
|
|
467
|
+
name: 'successful completion',
|
|
468
|
+
setup: () => {
|
|
469
|
+
mockCallTool.mockResolvedValue(createSdkResponse(serverToolName, {
|
|
470
|
+
content: [{ type: 'text', text: 'Success' }],
|
|
471
|
+
}));
|
|
551
472
|
},
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
473
|
+
expectError: false,
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
name: 'error',
|
|
477
|
+
setup: () => {
|
|
478
|
+
mockCallTool.mockRejectedValue(new Error('Tool execution failed'));
|
|
479
|
+
},
|
|
480
|
+
expectError: true,
|
|
481
|
+
},
|
|
482
|
+
])('should cleanup event listeners properly on $name', async ({ setup, expectError }) => {
|
|
560
483
|
const params = { param: 'test' };
|
|
561
484
|
const controller = new AbortController();
|
|
562
|
-
|
|
563
|
-
mockCallTool.mockRejectedValue(expectedError);
|
|
485
|
+
setup();
|
|
564
486
|
const invocation = tool.build(params);
|
|
565
|
-
|
|
566
|
-
|
|
487
|
+
if (expectError) {
|
|
488
|
+
try {
|
|
489
|
+
await invocation.execute(controller.signal);
|
|
490
|
+
}
|
|
491
|
+
catch (_error) {
|
|
492
|
+
// Expected error
|
|
493
|
+
}
|
|
567
494
|
}
|
|
568
|
-
|
|
569
|
-
|
|
495
|
+
else {
|
|
496
|
+
await invocation.execute(controller.signal);
|
|
570
497
|
}
|
|
571
|
-
// Verify cleanup by aborting after
|
|
498
|
+
// Verify cleanup by aborting after execution
|
|
572
499
|
controller.abort();
|
|
573
500
|
expect(controller.signal.aborted).toBe(true);
|
|
574
501
|
});
|
|
@@ -609,22 +536,32 @@ describe('DiscoveredMCPTool', () => {
|
|
|
609
536
|
throw new Error('Confirmation details not in expected format or was false');
|
|
610
537
|
}
|
|
611
538
|
});
|
|
612
|
-
it(
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
539
|
+
it.each([
|
|
540
|
+
{
|
|
541
|
+
outcome: ToolConfirmationOutcome.ProceedAlwaysServer,
|
|
542
|
+
description: 'add server to allowlist on ProceedAlwaysServer',
|
|
543
|
+
shouldAddServer: true,
|
|
544
|
+
shouldAddTool: false,
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
outcome: ToolConfirmationOutcome.ProceedAlwaysTool,
|
|
548
|
+
description: 'add tool to allowlist on ProceedAlwaysTool',
|
|
549
|
+
shouldAddServer: false,
|
|
550
|
+
shouldAddTool: true,
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
outcome: ToolConfirmationOutcome.Cancel,
|
|
554
|
+
description: 'handle Cancel confirmation outcome',
|
|
555
|
+
shouldAddServer: false,
|
|
556
|
+
shouldAddTool: false,
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
outcome: ToolConfirmationOutcome.ProceedOnce,
|
|
560
|
+
description: 'handle ProceedOnce confirmation outcome',
|
|
561
|
+
shouldAddServer: false,
|
|
562
|
+
shouldAddTool: false,
|
|
563
|
+
},
|
|
564
|
+
])('should $description', async ({ outcome, shouldAddServer, shouldAddTool }) => {
|
|
628
565
|
const toolAllowlistKey = `${serverName}.${serverToolName}`;
|
|
629
566
|
const invocation = tool.build({ param: 'mock' });
|
|
630
567
|
const confirmation = await invocation.shouldConfirmExecute(new AbortController().signal);
|
|
@@ -633,42 +570,9 @@ describe('DiscoveredMCPTool', () => {
|
|
|
633
570
|
typeof confirmation === 'object' &&
|
|
634
571
|
'onConfirm' in confirmation &&
|
|
635
572
|
typeof confirmation.onConfirm === 'function') {
|
|
636
|
-
await confirmation.onConfirm(
|
|
637
|
-
expect(invocation.constructor.allowlist.has(
|
|
638
|
-
|
|
639
|
-
else {
|
|
640
|
-
throw new Error('Confirmation details or onConfirm not in expected format');
|
|
641
|
-
}
|
|
642
|
-
});
|
|
643
|
-
it('should handle Cancel confirmation outcome', async () => {
|
|
644
|
-
const invocation = tool.build({ param: 'mock' });
|
|
645
|
-
const confirmation = await invocation.shouldConfirmExecute(new AbortController().signal);
|
|
646
|
-
expect(confirmation).not.toBe(false);
|
|
647
|
-
if (confirmation &&
|
|
648
|
-
typeof confirmation === 'object' &&
|
|
649
|
-
'onConfirm' in confirmation &&
|
|
650
|
-
typeof confirmation.onConfirm === 'function') {
|
|
651
|
-
// Cancel should not add anything to allowlist
|
|
652
|
-
await confirmation.onConfirm(ToolConfirmationOutcome.Cancel);
|
|
653
|
-
expect(invocation.constructor.allowlist.has(serverName)).toBe(false);
|
|
654
|
-
expect(invocation.constructor.allowlist.has(`${serverName}.${serverToolName}`)).toBe(false);
|
|
655
|
-
}
|
|
656
|
-
else {
|
|
657
|
-
throw new Error('Confirmation details or onConfirm not in expected format');
|
|
658
|
-
}
|
|
659
|
-
});
|
|
660
|
-
it('should handle ProceedOnce confirmation outcome', async () => {
|
|
661
|
-
const invocation = tool.build({ param: 'mock' });
|
|
662
|
-
const confirmation = await invocation.shouldConfirmExecute(new AbortController().signal);
|
|
663
|
-
expect(confirmation).not.toBe(false);
|
|
664
|
-
if (confirmation &&
|
|
665
|
-
typeof confirmation === 'object' &&
|
|
666
|
-
'onConfirm' in confirmation &&
|
|
667
|
-
typeof confirmation.onConfirm === 'function') {
|
|
668
|
-
// ProceedOnce should not add anything to allowlist
|
|
669
|
-
await confirmation.onConfirm(ToolConfirmationOutcome.ProceedOnce);
|
|
670
|
-
expect(invocation.constructor.allowlist.has(serverName)).toBe(false);
|
|
671
|
-
expect(invocation.constructor.allowlist.has(`${serverName}.${serverToolName}`)).toBe(false);
|
|
573
|
+
await confirmation.onConfirm(outcome);
|
|
574
|
+
expect(invocation.constructor.allowlist.has(serverName)).toBe(shouldAddServer);
|
|
575
|
+
expect(invocation.constructor.allowlist.has(toolAllowlistKey)).toBe(shouldAddTool);
|
|
672
576
|
}
|
|
673
577
|
else {
|
|
674
578
|
throw new Error('Confirmation details or onConfirm not in expected format');
|
|
@@ -679,27 +583,36 @@ describe('DiscoveredMCPTool', () => {
|
|
|
679
583
|
const mockConfig = (isTrusted) => ({
|
|
680
584
|
isTrustedFolder: () => isTrusted,
|
|
681
585
|
});
|
|
682
|
-
it(
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
586
|
+
it.each([
|
|
587
|
+
{
|
|
588
|
+
trust: true,
|
|
589
|
+
isTrusted: true,
|
|
590
|
+
shouldConfirm: false,
|
|
591
|
+
description: 'return false if trust is true and folder is trusted',
|
|
592
|
+
},
|
|
593
|
+
{
|
|
594
|
+
trust: true,
|
|
595
|
+
isTrusted: false,
|
|
596
|
+
shouldConfirm: true,
|
|
597
|
+
description: 'return confirmation details if trust is true but folder is not trusted',
|
|
598
|
+
},
|
|
599
|
+
{
|
|
600
|
+
trust: false,
|
|
601
|
+
isTrusted: true,
|
|
602
|
+
shouldConfirm: true,
|
|
603
|
+
description: 'return confirmation details if trust is false, even if folder is trusted',
|
|
604
|
+
},
|
|
605
|
+
])('should $description', async ({ trust, isTrusted, shouldConfirm }) => {
|
|
606
|
+
const testTool = new DiscoveredMCPTool(mockCallableToolInstance, serverName, serverToolName, baseDescription, inputSchema, trust, undefined, mockConfig(isTrusted));
|
|
607
|
+
const invocation = testTool.build({ param: 'mock' });
|
|
700
608
|
const confirmation = await invocation.shouldConfirmExecute(new AbortController().signal);
|
|
701
|
-
|
|
702
|
-
|
|
609
|
+
if (shouldConfirm) {
|
|
610
|
+
expect(confirmation).not.toBe(false);
|
|
611
|
+
expect(confirmation).toHaveProperty('type', 'mcp');
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
expect(confirmation).toBe(false);
|
|
615
|
+
}
|
|
703
616
|
});
|
|
704
617
|
});
|
|
705
618
|
describe('DiscoveredMCPToolInvocation', () => {
|