mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-01 15:34:29 -07:00
Disallow redundant typecasts. (#15030)
This commit is contained in:
committed by
GitHub
parent
fcc3b2b5ec
commit
942bcfc61e
@@ -12,11 +12,7 @@ import type {
|
||||
RequestContext,
|
||||
ExecutionEventBus,
|
||||
} from '@a2a-js/sdk/server';
|
||||
import type {
|
||||
ToolCallRequestInfo,
|
||||
ServerGeminiToolCallRequestEvent,
|
||||
Config,
|
||||
} from '@google/gemini-cli-core';
|
||||
import type { ToolCallRequestInfo, Config } from '@google/gemini-cli-core';
|
||||
import {
|
||||
GeminiEventType,
|
||||
SimpleExtensionLoader,
|
||||
@@ -287,8 +283,8 @@ export class CoderAgentExecutor implements AgentExecutor {
|
||||
requestContext: RequestContext,
|
||||
eventBus: ExecutionEventBus,
|
||||
): Promise<void> {
|
||||
const userMessage = requestContext.userMessage as Message;
|
||||
const sdkTask = requestContext.task as SDKTask | undefined;
|
||||
const userMessage = requestContext.userMessage;
|
||||
const sdkTask = requestContext.task;
|
||||
|
||||
const taskId = sdkTask?.id || userMessage.taskId || uuidv4();
|
||||
const contextId: string =
|
||||
@@ -485,9 +481,7 @@ export class CoderAgentExecutor implements AgentExecutor {
|
||||
throw new Error('Execution aborted');
|
||||
}
|
||||
if (event.type === GeminiEventType.ToolCallRequest) {
|
||||
toolCallRequests.push(
|
||||
(event as ServerGeminiToolCallRequestEvent).value,
|
||||
);
|
||||
toolCallRequests.push(event.value);
|
||||
continue;
|
||||
}
|
||||
await currentTask.acceptAgentMessage(event);
|
||||
|
||||
@@ -370,12 +370,7 @@ describe('Task', () => {
|
||||
};
|
||||
|
||||
// @ts-expect-error - Calling private constructor
|
||||
task = new Task(
|
||||
'task-id',
|
||||
'context-id',
|
||||
mockConfig as Config,
|
||||
mockEventBus,
|
||||
);
|
||||
task = new Task('task-id', 'context-id', mockConfig, mockEventBus);
|
||||
|
||||
// Spy on the method we want to check calls for
|
||||
setTaskStateAndPublishUpdateSpy = vi.spyOn(
|
||||
|
||||
@@ -747,8 +747,8 @@ export class Task {
|
||||
return false;
|
||||
}
|
||||
|
||||
const callId = part.data['callId'] as string;
|
||||
const outcomeString = part.data['outcome'] as string;
|
||||
const callId = part.data['callId'];
|
||||
const outcomeString = part.data['outcome'];
|
||||
let confirmationOutcome: ToolConfirmationOutcome | undefined;
|
||||
|
||||
if (outcomeString === 'proceed_once') {
|
||||
|
||||
@@ -123,7 +123,7 @@ function resolveEnvVarsInString(value: string): string {
|
||||
return value.replace(envVarRegex, (match, varName1, varName2) => {
|
||||
const varName = varName1 || varName2;
|
||||
if (process && process.env && typeof process.env[varName] === 'string') {
|
||||
return process.env[varName]!;
|
||||
return process.env[varName];
|
||||
}
|
||||
return match;
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
type Mock,
|
||||
} from 'vitest';
|
||||
import { format } from 'node:util';
|
||||
import { type CommandModule, type Argv } from 'yargs';
|
||||
import { type Argv } from 'yargs';
|
||||
import { handleDisable, disableCommand } from './disable.js';
|
||||
import { ExtensionManager } from '../../config/extension-manager.js';
|
||||
import {
|
||||
@@ -148,7 +148,7 @@ describe('extensions disable command', () => {
|
||||
});
|
||||
|
||||
describe('disableCommand', () => {
|
||||
const command = disableCommand as CommandModule;
|
||||
const command = disableCommand;
|
||||
|
||||
it('should have correct command and describe', () => {
|
||||
expect(command.command).toBe('disable [--scope] <name>');
|
||||
|
||||
@@ -65,7 +65,7 @@ export const disableCommand: CommandModule = {
|
||||
argv.scope &&
|
||||
!Object.values(SettingScope)
|
||||
.map((s) => s.toLowerCase())
|
||||
.includes((argv.scope as string).toLowerCase())
|
||||
.includes(argv.scope.toLowerCase())
|
||||
) {
|
||||
throw new Error(
|
||||
`Invalid scope: ${argv.scope}. Please use one of ${Object.values(
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
type Mock,
|
||||
} from 'vitest';
|
||||
import { format } from 'node:util';
|
||||
import { type CommandModule, type Argv } from 'yargs';
|
||||
import { type Argv } from 'yargs';
|
||||
import { handleEnable, enableCommand } from './enable.js';
|
||||
import { ExtensionManager } from '../../config/extension-manager.js';
|
||||
import {
|
||||
@@ -137,7 +137,7 @@ describe('extensions enable command', () => {
|
||||
});
|
||||
|
||||
describe('enableCommand', () => {
|
||||
const command = enableCommand as CommandModule;
|
||||
const command = enableCommand;
|
||||
|
||||
it('should have correct command and describe', () => {
|
||||
expect(command.command).toBe('enable [--scope] <name>');
|
||||
|
||||
@@ -70,7 +70,7 @@ export const enableCommand: CommandModule = {
|
||||
argv.scope &&
|
||||
!Object.values(SettingScope)
|
||||
.map((s) => s.toLowerCase())
|
||||
.includes((argv.scope as string).toLowerCase())
|
||||
.includes(argv.scope.toLowerCase())
|
||||
) {
|
||||
throw new Error(
|
||||
`Invalid scope: ${argv.scope}. Please use one of ${Object.values(
|
||||
|
||||
@@ -4,15 +4,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {
|
||||
describe,
|
||||
it,
|
||||
expect,
|
||||
vi,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
type Mock,
|
||||
} from 'vitest';
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
||||
import { z } from 'zod';
|
||||
@@ -90,7 +82,7 @@ describe('MCP Server Example', () => {
|
||||
json: vi.fn().mockResolvedValue(mockPosts),
|
||||
});
|
||||
|
||||
const toolFn = (mockRegisterTool as Mock).mock.calls[0][2];
|
||||
const toolFn = mockRegisterTool.mock.calls[0][2];
|
||||
const result = await toolFn();
|
||||
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
@@ -109,7 +101,7 @@ describe('MCP Server Example', () => {
|
||||
|
||||
describe('poem-writer prompt implementation', () => {
|
||||
it('should generate a prompt with a title', () => {
|
||||
const promptFn = (mockRegisterPrompt as Mock).mock.calls[0][2];
|
||||
const promptFn = mockRegisterPrompt.mock.calls[0][2];
|
||||
const result = promptFn({ title: 'My Poem' });
|
||||
expect(result).toEqual({
|
||||
messages: [
|
||||
@@ -125,7 +117,7 @@ describe('MCP Server Example', () => {
|
||||
});
|
||||
|
||||
it('should generate a prompt with a title and mood', () => {
|
||||
const promptFn = (mockRegisterPrompt as Mock).mock.calls[0][2];
|
||||
const promptFn = mockRegisterPrompt.mock.calls[0][2];
|
||||
const result = promptFn({ title: 'My Poem', mood: 'sad' });
|
||||
expect(result).toEqual({
|
||||
messages: [
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
type Mock,
|
||||
} from 'vitest';
|
||||
import { format } from 'node:util';
|
||||
import { type CommandModule, type Argv } from 'yargs';
|
||||
import { type Argv } from 'yargs';
|
||||
import { handleLink, linkCommand } from './link.js';
|
||||
import { ExtensionManager } from '../../config/extension-manager.js';
|
||||
import { loadSettings, type LoadedSettings } from '../../config/settings.js';
|
||||
@@ -126,7 +126,7 @@ describe('extensions link command', () => {
|
||||
});
|
||||
|
||||
describe('linkCommand', () => {
|
||||
const command = linkCommand as CommandModule;
|
||||
const command = linkCommand;
|
||||
|
||||
it('should have correct command and describe', () => {
|
||||
expect(command.command).toBe('link <path>');
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { format } from 'node:util';
|
||||
import { type CommandModule } from 'yargs';
|
||||
import { handleList, listCommand } from './list.js';
|
||||
import { ExtensionManager } from '../../config/extension-manager.js';
|
||||
import { loadSettings, type LoadedSettings } from '../../config/settings.js';
|
||||
@@ -124,7 +123,7 @@ describe('extensions list command', () => {
|
||||
});
|
||||
|
||||
describe('listCommand', () => {
|
||||
const command = listCommand as CommandModule;
|
||||
const command = listCommand;
|
||||
|
||||
it('should have correct command and describe', () => {
|
||||
expect(command.command).toBe('list');
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
type Mock,
|
||||
} from 'vitest';
|
||||
import { format } from 'node:util';
|
||||
import { type CommandModule, type Argv } from 'yargs';
|
||||
import { type Argv } from 'yargs';
|
||||
import { handleUninstall, uninstallCommand } from './uninstall.js';
|
||||
import { ExtensionManager } from '../../config/extension-manager.js';
|
||||
import { loadSettings, type LoadedSettings } from '../../config/settings.js';
|
||||
@@ -233,7 +233,7 @@ describe('extensions uninstall command', () => {
|
||||
});
|
||||
|
||||
describe('uninstallCommand', () => {
|
||||
const command = uninstallCommand as CommandModule;
|
||||
const command = uninstallCommand;
|
||||
|
||||
it('should have correct command and describe', () => {
|
||||
expect(command.command).toBe('uninstall <names..>');
|
||||
|
||||
@@ -62,7 +62,7 @@ export const uninstallCommand: CommandModule = {
|
||||
array: true,
|
||||
})
|
||||
.check((argv) => {
|
||||
if (!argv.names || (argv.names as string[]).length === 0) {
|
||||
if (!argv.names || argv.names.length === 0) {
|
||||
throw new Error(
|
||||
'Please include at least one extension name to uninstall as a positional argument.',
|
||||
);
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
type Mock,
|
||||
} from 'vitest';
|
||||
import { format } from 'node:util';
|
||||
import { type CommandModule, type Argv } from 'yargs';
|
||||
import { type Argv } from 'yargs';
|
||||
import { handleUpdate, updateCommand } from './update.js';
|
||||
import { ExtensionManager } from '../../config/extension-manager.js';
|
||||
import { loadSettings, type LoadedSettings } from '../../config/settings.js';
|
||||
@@ -155,7 +155,7 @@ describe('extensions update command', () => {
|
||||
});
|
||||
|
||||
describe('updateCommand', () => {
|
||||
const command = updateCommand as CommandModule;
|
||||
const command = updateCommand;
|
||||
|
||||
it('should have correct command and describe', () => {
|
||||
expect(command.command).toBe('update [<name>] [--all]');
|
||||
|
||||
@@ -60,7 +60,7 @@ vi.mock('fs', async (importOriginal) => {
|
||||
if (mockPaths.has(p.toString())) {
|
||||
return { isDirectory: () => true } as unknown as import('fs').Stats;
|
||||
}
|
||||
return (actualFs as typeof import('fs')).statSync(p as unknown as string);
|
||||
return actualFs.statSync(p as unknown as string);
|
||||
}),
|
||||
realpathSync: vi.fn((p) => p),
|
||||
};
|
||||
|
||||
@@ -123,7 +123,7 @@ export class ExtensionManager extends ExtensionLoader {
|
||||
'Extensions not yet loaded, must call `loadExtensions` first',
|
||||
);
|
||||
}
|
||||
return this.loadedExtensions!;
|
||||
return this.loadedExtensions;
|
||||
}
|
||||
|
||||
async installOrUpdateExtension(
|
||||
@@ -319,7 +319,7 @@ export class ExtensionManager extends ExtensionLoader {
|
||||
|
||||
// TODO: Gracefully handle this call failing, we should back up the old
|
||||
// extension prior to overwriting it and then restore and restart it.
|
||||
extension = await this.loadExtension(destinationPath)!;
|
||||
extension = await this.loadExtension(destinationPath);
|
||||
if (!extension) {
|
||||
throw new Error(`Extension not found`);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ export async function fetchJson<T>(
|
||||
if (!res.headers.location) {
|
||||
return reject(new Error('No location header in redirect response'));
|
||||
}
|
||||
fetchJson<T>(res.headers.location!, redirectCount++)
|
||||
fetchJson<T>(res.headers.location, redirectCount++)
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
return;
|
||||
|
||||
@@ -18,7 +18,7 @@ function buildZodSchemaFromJsonSchema(def: any): z.ZodTypeAny {
|
||||
if (def.anyOf) {
|
||||
return z.union(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
def.anyOf.map((d: any) => buildZodSchemaFromJsonSchema(d)) as any,
|
||||
def.anyOf.map((d: any) => buildZodSchemaFromJsonSchema(d)),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2231,7 +2231,7 @@ describe('Settings Loading and Merging', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
mockFsExistsSync = vi.mocked(fs.existsSync);
|
||||
(mockFsExistsSync as Mock).mockReturnValue(true);
|
||||
mockFsExistsSync.mockReturnValue(true);
|
||||
mockFsReadFileSync = vi.mocked(fs.readFileSync);
|
||||
mockFsReadFileSync.mockReturnValue('{}');
|
||||
vi.mocked(isWorkspaceTrusted).mockReturnValue({
|
||||
@@ -2256,15 +2256,13 @@ describe('Settings Loading and Merging', () => {
|
||||
},
|
||||
};
|
||||
|
||||
(mockFsReadFileSync as Mock).mockImplementation(
|
||||
(p: fs.PathOrFileDescriptor) => {
|
||||
if (p === USER_SETTINGS_PATH)
|
||||
return JSON.stringify(userSettingsContent);
|
||||
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
|
||||
return JSON.stringify(workspaceSettingsContent);
|
||||
return '{}';
|
||||
},
|
||||
);
|
||||
mockFsReadFileSync.mockImplementation((p: fs.PathOrFileDescriptor) => {
|
||||
if (p === USER_SETTINGS_PATH)
|
||||
return JSON.stringify(userSettingsContent);
|
||||
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
|
||||
return JSON.stringify(workspaceSettingsContent);
|
||||
return '{}';
|
||||
});
|
||||
|
||||
const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR);
|
||||
const setValueSpy = vi.spyOn(loadedSettings, 'setValue');
|
||||
@@ -2329,15 +2327,13 @@ describe('Settings Loading and Merging', () => {
|
||||
someOtherSetting: 'value',
|
||||
};
|
||||
|
||||
(mockFsReadFileSync as Mock).mockImplementation(
|
||||
(p: fs.PathOrFileDescriptor) => {
|
||||
if (p === USER_SETTINGS_PATH)
|
||||
return JSON.stringify(userSettingsContent);
|
||||
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
|
||||
return JSON.stringify(workspaceSettingsContent);
|
||||
return '{}';
|
||||
},
|
||||
);
|
||||
mockFsReadFileSync.mockImplementation((p: fs.PathOrFileDescriptor) => {
|
||||
if (p === USER_SETTINGS_PATH)
|
||||
return JSON.stringify(userSettingsContent);
|
||||
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
|
||||
return JSON.stringify(workspaceSettingsContent);
|
||||
return '{}';
|
||||
});
|
||||
|
||||
const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR);
|
||||
const setValueSpy = vi.spyOn(loadedSettings, 'setValue');
|
||||
|
||||
@@ -33,7 +33,7 @@ describe('SettingsSchema', () => {
|
||||
];
|
||||
|
||||
expectedSettings.forEach((setting) => {
|
||||
expect(getSettingsSchema()[setting as keyof Settings]).toBeDefined();
|
||||
expect(getSettingsSchema()[setting]).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -66,9 +66,7 @@ describe('SettingsSchema', () => {
|
||||
];
|
||||
|
||||
nestedSettings.forEach((setting) => {
|
||||
const definition = getSettingsSchema()[
|
||||
setting as keyof Settings
|
||||
] as SettingDefinition;
|
||||
const definition = getSettingsSchema()[setting] as SettingDefinition;
|
||||
expect(definition.type).toBe('object');
|
||||
expect(definition.properties).toBeDefined();
|
||||
expect(typeof definition.properties).toBe('object');
|
||||
@@ -142,7 +140,7 @@ describe('SettingsSchema', () => {
|
||||
it('should have consistent default values for boolean settings', () => {
|
||||
const checkBooleanDefaults = (schema: SettingsSchema) => {
|
||||
Object.entries(schema).forEach(([, definition]) => {
|
||||
const def = definition as SettingDefinition;
|
||||
const def = definition;
|
||||
if (def.type === 'boolean') {
|
||||
// Boolean settings can have boolean or undefined defaults (for optional settings)
|
||||
expect(['boolean', 'undefined']).toContain(typeof def.default);
|
||||
|
||||
@@ -238,7 +238,7 @@ describe('FileCommandLoader', () => {
|
||||
const loader = new FileCommandLoader(mockConfig);
|
||||
const commands = await loader.loadCommands(signal);
|
||||
expect(commands).toHaveLength(1);
|
||||
expect(commands[0]!.name).toBe('gcp:pipelines:run');
|
||||
expect(commands[0].name).toBe('gcp:pipelines:run');
|
||||
});
|
||||
|
||||
it('creates namespaces from nested directories', async () => {
|
||||
|
||||
@@ -326,7 +326,7 @@ describe('chatCommand', () => {
|
||||
const fakeFiles = ['checkpoint-alpha.json', 'checkpoint-beta.json'];
|
||||
mockFs.readdir.mockImplementation(
|
||||
(async (_: string): Promise<string[]> =>
|
||||
fakeFiles as string[]) as unknown as typeof fsPromises.readdir,
|
||||
fakeFiles) as unknown as typeof fsPromises.readdir,
|
||||
);
|
||||
|
||||
mockFs.stat.mockImplementation(
|
||||
@@ -346,7 +346,7 @@ describe('chatCommand', () => {
|
||||
const date = new Date();
|
||||
mockFs.readdir.mockImplementation(
|
||||
(async (_: string): Promise<string[]> =>
|
||||
fakeFiles as string[]) as unknown as typeof fsPromises.readdir,
|
||||
fakeFiles) as unknown as typeof fsPromises.readdir,
|
||||
);
|
||||
mockFs.stat.mockImplementation((async (
|
||||
path: string,
|
||||
@@ -406,7 +406,7 @@ describe('chatCommand', () => {
|
||||
const fakeFiles = ['checkpoint-alpha.json', 'checkpoint-beta.json'];
|
||||
mockFs.readdir.mockImplementation(
|
||||
(async (_: string): Promise<string[]> =>
|
||||
fakeFiles as string[]) as unknown as typeof fsPromises.readdir,
|
||||
fakeFiles) as unknown as typeof fsPromises.readdir,
|
||||
);
|
||||
|
||||
mockFs.stat.mockImplementation(
|
||||
|
||||
@@ -110,7 +110,7 @@ describe('ideCommand', () => {
|
||||
status: core.IDEConnectionStatus.Connected,
|
||||
});
|
||||
const command = await ideCommand();
|
||||
const result = await command!.subCommands!.find(
|
||||
const result = await command.subCommands!.find(
|
||||
(c) => c.name === 'status',
|
||||
)!.action!(mockContext, '');
|
||||
expect(vi.mocked(mockIdeClient.getConnectionStatus)).toHaveBeenCalled();
|
||||
@@ -126,7 +126,7 @@ describe('ideCommand', () => {
|
||||
status: core.IDEConnectionStatus.Connecting,
|
||||
});
|
||||
const command = await ideCommand();
|
||||
const result = await command!.subCommands!.find(
|
||||
const result = await command.subCommands!.find(
|
||||
(c) => c.name === 'status',
|
||||
)!.action!(mockContext, '');
|
||||
expect(vi.mocked(mockIdeClient.getConnectionStatus)).toHaveBeenCalled();
|
||||
@@ -141,7 +141,7 @@ describe('ideCommand', () => {
|
||||
status: core.IDEConnectionStatus.Disconnected,
|
||||
});
|
||||
const command = await ideCommand();
|
||||
const result = await command!.subCommands!.find(
|
||||
const result = await command.subCommands!.find(
|
||||
(c) => c.name === 'status',
|
||||
)!.action!(mockContext, '');
|
||||
expect(vi.mocked(mockIdeClient.getConnectionStatus)).toHaveBeenCalled();
|
||||
@@ -159,7 +159,7 @@ describe('ideCommand', () => {
|
||||
details,
|
||||
});
|
||||
const command = await ideCommand();
|
||||
const result = await command!.subCommands!.find(
|
||||
const result = await command.subCommands!.find(
|
||||
(c) => c.name === 'status',
|
||||
)!.action!(mockContext, '');
|
||||
expect(vi.mocked(mockIdeClient.getConnectionStatus)).toHaveBeenCalled();
|
||||
@@ -200,7 +200,7 @@ describe('ideCommand', () => {
|
||||
status: core.IDEConnectionStatus.Connected,
|
||||
});
|
||||
|
||||
const actionPromise = command!.subCommands!.find(
|
||||
const actionPromise = command.subCommands!.find(
|
||||
(c) => c.name === 'install',
|
||||
)!.action!(mockContext, '');
|
||||
await vi.runAllTimersAsync();
|
||||
@@ -239,7 +239,7 @@ describe('ideCommand', () => {
|
||||
});
|
||||
|
||||
const command = await ideCommand();
|
||||
await command!.subCommands!.find((c) => c.name === 'install')!.action!(
|
||||
await command.subCommands!.find((c) => c.name === 'install')!.action!(
|
||||
mockContext,
|
||||
'',
|
||||
);
|
||||
|
||||
@@ -10,11 +10,7 @@ import type {
|
||||
CommandContext,
|
||||
} from './types.js';
|
||||
import { CommandKind } from './types.js';
|
||||
import type {
|
||||
DiscoveredMCPPrompt,
|
||||
DiscoveredMCPResource,
|
||||
MessageActionReturn,
|
||||
} from '@google/gemini-cli-core';
|
||||
import type { MessageActionReturn } from '@google/gemini-cli-core';
|
||||
import {
|
||||
DiscoveredMCPTool,
|
||||
getMCPDiscoveryState,
|
||||
@@ -218,25 +214,20 @@ const listAction = async (
|
||||
connectingServers.length > 0;
|
||||
|
||||
const allTools = toolRegistry.getAllTools();
|
||||
const mcpTools = allTools.filter(
|
||||
(tool) => tool instanceof DiscoveredMCPTool,
|
||||
) as DiscoveredMCPTool[];
|
||||
const mcpTools = allTools.filter((tool) => tool instanceof DiscoveredMCPTool);
|
||||
|
||||
const promptRegistry = await config.getPromptRegistry();
|
||||
const mcpPrompts = promptRegistry
|
||||
.getAllPrompts()
|
||||
.filter(
|
||||
(prompt) =>
|
||||
'serverName' in prompt &&
|
||||
serverNames.includes(prompt.serverName as string),
|
||||
) as DiscoveredMCPPrompt[];
|
||||
'serverName' in prompt && serverNames.includes(prompt.serverName),
|
||||
);
|
||||
|
||||
const resourceRegistry = config.getResourceRegistry();
|
||||
const mcpResources = resourceRegistry
|
||||
.getAllResources()
|
||||
.filter((entry) =>
|
||||
serverNames.includes(entry.serverName),
|
||||
) as DiscoveredMCPResource[];
|
||||
.filter((entry) => serverNames.includes(entry.serverName));
|
||||
|
||||
const authStatus: HistoryItemMcpStatus['authStatus'] = {};
|
||||
const tokenStorage = new MCPOAuthTokenStorage();
|
||||
@@ -269,7 +260,7 @@ const listAction = async (
|
||||
schema: tool.schema,
|
||||
})),
|
||||
prompts: mcpPrompts.map((prompt) => ({
|
||||
serverName: prompt.serverName as string,
|
||||
serverName: prompt.serverName,
|
||||
name: prompt.name,
|
||||
description: prompt.description,
|
||||
})),
|
||||
|
||||
@@ -22,7 +22,6 @@ import {
|
||||
CommandKind,
|
||||
} from './types.js';
|
||||
import type { HistoryItem } from '../types.js';
|
||||
import type { Content } from '@google/genai';
|
||||
|
||||
const HistoryItemSchema = z
|
||||
.object({
|
||||
@@ -117,9 +116,7 @@ async function restoreAction(
|
||||
} else if (action.type === 'load_history' && loadHistory) {
|
||||
loadHistory(action.history);
|
||||
if (action.clientHistory) {
|
||||
await config
|
||||
?.getGeminiClient()
|
||||
?.setHistory(action.clientHistory as Content[]);
|
||||
await config?.getGeminiClient()?.setHistory(action.clientHistory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ vi.mock('./HistoryItemDisplay.js', async () => {
|
||||
const { Text } = await vi.importActual('ink');
|
||||
return {
|
||||
HistoryItemDisplay: ({ item }: { item: { content: string } }) =>
|
||||
React.createElement(Text as unknown as React.FC, null, item.content),
|
||||
React.createElement(Text as React.FC, null, item.content),
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -326,7 +326,7 @@ describe('SettingsDialog', () => {
|
||||
|
||||
// Navigate down
|
||||
act(() => {
|
||||
stdin.write(down as string);
|
||||
stdin.write(down);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -335,7 +335,7 @@ describe('SettingsDialog', () => {
|
||||
|
||||
// Navigate up
|
||||
act(() => {
|
||||
stdin.write(up as string);
|
||||
stdin.write(up);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
|
||||
@@ -144,7 +144,7 @@ export const ToolStatsDisplay: React.FC = () => {
|
||||
|
||||
{/* Tool Rows */}
|
||||
{activeTools.map(([name, stats]) => (
|
||||
<StatRow key={name} name={name} stats={stats as ToolCallStats} />
|
||||
<StatRow key={name} name={name} stats={stats} />
|
||||
))}
|
||||
|
||||
<Box height={1} />
|
||||
|
||||
@@ -119,7 +119,7 @@ export const TodoTray: React.FC = () => {
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
return tool.resultDisplay as TodoList;
|
||||
return tool.resultDisplay;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -11,8 +11,6 @@ import { DiffRenderer } from './DiffRenderer.js';
|
||||
import { RenderInline } from '../../utils/InlineMarkdownRenderer.js';
|
||||
import type {
|
||||
ToolCallConfirmationDetails,
|
||||
ToolExecuteConfirmationDetails,
|
||||
ToolMcpConfirmationDetails,
|
||||
Config,
|
||||
} from '@google/gemini-cli-core';
|
||||
import { IdeClient, ToolConfirmationOutcome } from '@google/gemini-cli-core';
|
||||
@@ -135,8 +133,7 @@ export const ToolConfirmationMessage: React.FC<
|
||||
});
|
||||
}
|
||||
} else if (confirmationDetails.type === 'exec') {
|
||||
const executionProps =
|
||||
confirmationDetails as ToolExecuteConfirmationDetails;
|
||||
const executionProps = confirmationDetails;
|
||||
|
||||
question = `Allow execution of: '${executionProps.rootCommand}'?`;
|
||||
options.push({
|
||||
@@ -187,7 +184,7 @@ export const ToolConfirmationMessage: React.FC<
|
||||
});
|
||||
} else {
|
||||
// mcp tool confirmation
|
||||
const mcpProps = confirmationDetails as ToolMcpConfirmationDetails;
|
||||
const mcpProps = confirmationDetails;
|
||||
question = `Allow execution of MCP tool "${mcpProps.toolName}" from server "${mcpProps.serverName}"?`;
|
||||
options.push({
|
||||
label: 'Yes, allow once',
|
||||
@@ -258,8 +255,7 @@ export const ToolConfirmationMessage: React.FC<
|
||||
);
|
||||
}
|
||||
} else if (confirmationDetails.type === 'exec') {
|
||||
const executionProps =
|
||||
confirmationDetails as ToolExecuteConfirmationDetails;
|
||||
const executionProps = confirmationDetails;
|
||||
let bodyContentHeight = availableBodyContentHeight();
|
||||
if (bodyContentHeight !== undefined) {
|
||||
bodyContentHeight -= 2; // Account for padding;
|
||||
@@ -312,7 +308,7 @@ export const ToolConfirmationMessage: React.FC<
|
||||
);
|
||||
} else {
|
||||
// mcp tool confirmation
|
||||
const mcpProps = confirmationDetails as ToolMcpConfirmationDetails;
|
||||
const mcpProps = confirmationDetails;
|
||||
|
||||
bodyContent = (
|
||||
<Box flexDirection="column">
|
||||
|
||||
@@ -59,7 +59,7 @@ function findLastIndex<T>(
|
||||
predicate: (value: T, index: number, obj: T[]) => unknown,
|
||||
): number {
|
||||
for (let i = array.length - 1; i >= 0; i--) {
|
||||
if (predicate(array[i]!, i, array)) {
|
||||
if (predicate(array[i], i, array)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -192,7 +192,7 @@ function VirtualizedList<T>(
|
||||
return { index: 0, offset: 0 };
|
||||
}
|
||||
|
||||
return { index, offset: scrollTop - offsets[index]! };
|
||||
return { index, offset: scrollTop - offsets[index] };
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
@@ -433,7 +433,7 @@ export async function handleAtCommand({
|
||||
const processedQueryParts: PartListUnion = [{ text: initialQueryText }];
|
||||
|
||||
const resourcePromises = resourceAttachments.map(async (resource) => {
|
||||
const uri = resource.uri!;
|
||||
const uri = resource.uri;
|
||||
const client = mcpClientManager?.getClient(resource.serverName);
|
||||
try {
|
||||
if (!client) {
|
||||
|
||||
@@ -26,9 +26,7 @@ import { MessageType } from '../types.js';
|
||||
vi.mock('./useKeypress.js');
|
||||
|
||||
vi.mock('@google/gemini-cli-core', async () => {
|
||||
const actualServerModule = (await vi.importActual(
|
||||
'@google/gemini-cli-core',
|
||||
)) as Record<string, unknown>;
|
||||
const actualServerModule = await vi.importActual('@google/gemini-cli-core');
|
||||
return {
|
||||
...actualServerModule,
|
||||
Config: vi.fn(),
|
||||
|
||||
@@ -24,7 +24,6 @@ import { useReactToolScheduler } from './useReactToolScheduler.js';
|
||||
import type {
|
||||
Config,
|
||||
EditorType,
|
||||
GeminiClient,
|
||||
AnyToolInvocation,
|
||||
} from '@google/gemini-cli-core';
|
||||
import {
|
||||
@@ -809,8 +808,8 @@ describe('useGeminiStream', () => {
|
||||
expect(client.addHistory).toHaveBeenCalledWith({
|
||||
role: 'user',
|
||||
parts: [
|
||||
...(cancelledToolCall1.response.responseParts as Part[]),
|
||||
...(cancelledToolCall2.response.responseParts as Part[]),
|
||||
...cancelledToolCall1.response.responseParts,
|
||||
...cancelledToolCall2.response.responseParts,
|
||||
],
|
||||
});
|
||||
|
||||
@@ -2074,7 +2073,7 @@ describe('useGeminiStream', () => {
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useGeminiStream(
|
||||
mockConfig.getGeminiClient() as GeminiClient,
|
||||
mockConfig.getGeminiClient(),
|
||||
[],
|
||||
mockAddItem,
|
||||
mockConfig,
|
||||
|
||||
@@ -833,10 +833,7 @@ export const useGeminiStream = (
|
||||
);
|
||||
break;
|
||||
case ServerGeminiEventType.Finished:
|
||||
handleFinishedEvent(
|
||||
event as ServerGeminiFinishedEvent,
|
||||
userMessageTimestamp,
|
||||
);
|
||||
handleFinishedEvent(event, userMessageTimestamp);
|
||||
break;
|
||||
case ServerGeminiEventType.Citation:
|
||||
handleCitationEvent(event.value, userMessageTimestamp);
|
||||
|
||||
@@ -183,7 +183,7 @@ describe('useIncludeDirsTrust', () => {
|
||||
).props;
|
||||
expect(dialogProps.folders).toEqual(['/undefined']);
|
||||
expect(dialogProps.trustedDirs).toEqual(['/trusted']);
|
||||
expect(dialogProps.errors as string[]).toEqual([
|
||||
expect(dialogProps.errors).toEqual([
|
||||
`The following directories are explicitly untrusted and cannot be added to a trusted workspace:\n- /untrusted\nPlease use the permissions command to modify their trust level.`,
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -99,7 +99,7 @@ export function useReactToolScheduler(
|
||||
setToolCallsForDisplay((prevCalls) =>
|
||||
prevCalls.map((tc) => {
|
||||
if (tc.request.callId === toolCallId && tc.status === 'executing') {
|
||||
const executingTc = tc as TrackedExecutingToolCall;
|
||||
const executingTc = tc;
|
||||
return { ...executingTc, liveOutput: outputChunk };
|
||||
}
|
||||
return tc;
|
||||
@@ -137,7 +137,7 @@ export function useReactToolScheduler(
|
||||
...coreTc,
|
||||
responseSubmittedToGemini,
|
||||
liveOutput,
|
||||
pid: (coreTc as ExecutingToolCall).pid,
|
||||
pid: coreTc.pid,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
@@ -312,10 +312,9 @@ export function mapToDisplay(
|
||||
return {
|
||||
...baseDisplayProperties,
|
||||
status: mapCoreStatusToDisplayStatus(trackedCall.status),
|
||||
resultDisplay:
|
||||
(trackedCall as TrackedExecutingToolCall).liveOutput ?? undefined,
|
||||
resultDisplay: trackedCall.liveOutput ?? undefined,
|
||||
confirmationDetails: undefined,
|
||||
ptyId: (trackedCall as TrackedExecutingToolCall).pid,
|
||||
ptyId: trackedCall.pid,
|
||||
};
|
||||
case 'validating': // Fallthrough
|
||||
case 'scheduled':
|
||||
|
||||
@@ -106,7 +106,7 @@ const computeInitialIndex = (
|
||||
|
||||
if (initialKey !== undefined) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (items[i]!.key === initialKey && !items[i]!.disabled) {
|
||||
if (items[i].key === initialKey && !items[i].disabled) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -212,7 +212,7 @@ function areBaseItemsEqual(
|
||||
if (a.length !== b.length) return false;
|
||||
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
if (a[i]!.key !== b[i]!.key || a[i]!.disabled !== b[i]!.disabled) {
|
||||
if (a[i].key !== b[i].key || a[i].disabled !== b[i].disabled) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -283,7 +283,7 @@ export function useSelectionList<T>({
|
||||
let needsClear = false;
|
||||
|
||||
if (state.pendingHighlight && items[state.activeIndex]) {
|
||||
onHighlight?.(items[state.activeIndex]!.value);
|
||||
onHighlight?.(items[state.activeIndex].value);
|
||||
needsClear = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -231,7 +231,7 @@ describe('commandUtils', () => {
|
||||
const expected = `${ESC}]52;c;${b64}${BEL}`;
|
||||
|
||||
expect(tty.write).toHaveBeenCalledTimes(1);
|
||||
expect((tty.write as Mock).mock.calls[0][0]).toBe(expected);
|
||||
expect(tty.write.mock.calls[0][0]).toBe(expected);
|
||||
expect(tty.end).toHaveBeenCalledTimes(1); // /dev/tty closed after write
|
||||
expect(mockClipboardyWrite).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -245,7 +245,7 @@ describe('commandUtils', () => {
|
||||
|
||||
await copyToClipboard(testText);
|
||||
|
||||
const written = (tty.write as Mock).mock.calls[0][0] as string;
|
||||
const written = tty.write.mock.calls[0][0] as string;
|
||||
// Starts with tmux DCS wrapper and ends with ST
|
||||
expect(written.startsWith(`${ESC}Ptmux;`)).toBe(true);
|
||||
expect(written.endsWith(ST)).toBe(true);
|
||||
@@ -264,7 +264,7 @@ describe('commandUtils', () => {
|
||||
|
||||
await copyToClipboard(testText);
|
||||
|
||||
const written = (tty.write as Mock).mock.calls[0][0] as string;
|
||||
const written = tty.write.mock.calls[0][0] as string;
|
||||
const chunkStarts = (written.match(new RegExp(`${ESC}P`, 'g')) || [])
|
||||
.length;
|
||||
const chunkEnds = written.split(ST).length - 1;
|
||||
|
||||
@@ -240,7 +240,7 @@ describe('resolveEnvVarsInObject', () => {
|
||||
// Create circular reference
|
||||
arr.push(arr);
|
||||
|
||||
const result = resolveEnvVarsInObject(arr) as ArrayWithCircularRef;
|
||||
const result = resolveEnvVarsInObject(arr);
|
||||
|
||||
expect(result[0]).toBe('array-value');
|
||||
expect(result[1]).toBe(123);
|
||||
|
||||
@@ -25,10 +25,10 @@ export function resolveEnvVarsInString(
|
||||
return value.replace(envVarRegex, (match, varName1, varName2) => {
|
||||
const varName = varName1 || varName2;
|
||||
if (customEnv && typeof customEnv[varName] === 'string') {
|
||||
return customEnv[varName]!;
|
||||
return customEnv[varName];
|
||||
}
|
||||
if (process && process.env && typeof process.env[varName] === 'string') {
|
||||
return process.env[varName]!;
|
||||
return process.env[varName];
|
||||
}
|
||||
return match;
|
||||
});
|
||||
|
||||
@@ -52,7 +52,7 @@ export async function validateNonInteractiveAuth(
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
const authType: AuthType = effectiveAuthType as AuthType;
|
||||
const authType: AuthType = effectiveAuthType;
|
||||
|
||||
if (!useExternalAuth) {
|
||||
const err = validateAuthMethod(String(authType));
|
||||
|
||||
@@ -121,8 +121,7 @@ describe('acp', () => {
|
||||
authMethods: [],
|
||||
protocolVersion: 1,
|
||||
};
|
||||
const handler = mockConnectionConstructor.mock
|
||||
.calls[0][0]! as MethodHandler;
|
||||
const handler = mockConnectionConstructor.mock.calls[0][0];
|
||||
const result = await handler('initialize', initializeParams);
|
||||
|
||||
expect(mockAgent.initialize).toHaveBeenCalledWith(initializeParams);
|
||||
@@ -132,8 +131,7 @@ describe('acp', () => {
|
||||
it('should call agent.newSession when Connection handler receives session_new method', async () => {
|
||||
const newSessionParams = { cwd: '/tmp', mcpServers: [] };
|
||||
const newSessionResponse = { sessionId: 'session-1' };
|
||||
const handler = mockConnectionConstructor.mock
|
||||
.calls[0][0]! as MethodHandler;
|
||||
const handler = mockConnectionConstructor.mock.calls[0][0];
|
||||
const result = await handler('session/new', newSessionParams);
|
||||
|
||||
expect(mockAgent.newSession).toHaveBeenCalledWith(newSessionParams);
|
||||
@@ -147,8 +145,7 @@ describe('acp', () => {
|
||||
sessionId: 'session-1',
|
||||
};
|
||||
const loadSessionResponse = { sessionId: 'session-1' };
|
||||
const handler = mockConnectionConstructor.mock
|
||||
.calls[0][0]! as MethodHandler;
|
||||
const handler = mockConnectionConstructor.mock.calls[0][0];
|
||||
const result = await handler('session/load', loadSessionParams);
|
||||
|
||||
expect(mockAgent.loadSession).toHaveBeenCalledWith(loadSessionParams);
|
||||
@@ -162,8 +159,7 @@ describe('acp', () => {
|
||||
mcpServers: [],
|
||||
sessionId: 'session-1',
|
||||
};
|
||||
const handler = mockConnectionConstructor.mock
|
||||
.calls[0][0]! as MethodHandler;
|
||||
const handler = mockConnectionConstructor.mock.calls[0][0];
|
||||
await expect(handler('session/load', loadSessionParams)).rejects.toThrow(
|
||||
RequestError.methodNotFound().message,
|
||||
);
|
||||
@@ -173,8 +169,7 @@ describe('acp', () => {
|
||||
const authenticateParams = {
|
||||
methodId: 'test-auth-method',
|
||||
};
|
||||
const handler = mockConnectionConstructor.mock
|
||||
.calls[0][0]! as MethodHandler;
|
||||
const handler = mockConnectionConstructor.mock.calls[0][0];
|
||||
const result = await handler('authenticate', authenticateParams);
|
||||
|
||||
expect(mockAgent.authenticate).toHaveBeenCalledWith(authenticateParams);
|
||||
@@ -191,8 +186,7 @@ describe('acp', () => {
|
||||
traceId: 'trace-1',
|
||||
};
|
||||
(mockAgent.prompt as Mock).mockResolvedValue(promptResponse);
|
||||
const handler = mockConnectionConstructor.mock
|
||||
.calls[0][0]! as MethodHandler;
|
||||
const handler = mockConnectionConstructor.mock.calls[0][0];
|
||||
const result = await handler('session/prompt', promptParams);
|
||||
|
||||
expect(mockAgent.prompt).toHaveBeenCalledWith(promptParams);
|
||||
@@ -201,8 +195,7 @@ describe('acp', () => {
|
||||
|
||||
it('should call agent.cancel when Connection handler receives session_cancel method', async () => {
|
||||
const cancelParams = { sessionId: 'session-1' };
|
||||
const handler = mockConnectionConstructor.mock
|
||||
.calls[0][0]! as MethodHandler;
|
||||
const handler = mockConnectionConstructor.mock.calls[0][0];
|
||||
const result = await handler('session/cancel', cancelParams);
|
||||
|
||||
expect(mockAgent.cancel).toHaveBeenCalledWith(cancelParams);
|
||||
@@ -210,8 +203,7 @@ describe('acp', () => {
|
||||
});
|
||||
|
||||
it('should throw methodNotFound for unknown methods', async () => {
|
||||
const handler = mockConnectionConstructor.mock
|
||||
.calls[0][0]! as MethodHandler;
|
||||
const handler = mockConnectionConstructor.mock.calls[0][0];
|
||||
await expect(handler('unknown_method', {})).rejects.toThrow(
|
||||
RequestError.methodNotFound().message,
|
||||
);
|
||||
@@ -248,7 +240,7 @@ describe('acp', () => {
|
||||
const response = {
|
||||
outcome: { outcome: 'selected', optionId: 'option-1' },
|
||||
};
|
||||
(connectionInstance.sendRequest as Mock).mockResolvedValue(response);
|
||||
connectionInstance.sendRequest.mockResolvedValue(response);
|
||||
|
||||
const result = await agentSideConnection.requestPermission(params);
|
||||
expect(connectionInstance.sendRequest).toHaveBeenCalledWith(
|
||||
@@ -261,7 +253,7 @@ describe('acp', () => {
|
||||
it('should send readTextFile request via connection', async () => {
|
||||
const params = { path: '/a/b.txt', sessionId: 'session-1' };
|
||||
const response = { content: 'file content' };
|
||||
(connectionInstance.sendRequest as Mock).mockResolvedValue(response);
|
||||
connectionInstance.sendRequest.mockResolvedValue(response);
|
||||
|
||||
const result = await agentSideConnection.readTextFile(params);
|
||||
expect(connectionInstance.sendRequest).toHaveBeenCalledWith(
|
||||
@@ -278,7 +270,7 @@ describe('acp', () => {
|
||||
sessionId: 'session-1',
|
||||
};
|
||||
const response = { success: true };
|
||||
(connectionInstance.sendRequest as Mock).mockResolvedValue(response);
|
||||
connectionInstance.sendRequest.mockResolvedValue(response);
|
||||
|
||||
const result = await agentSideConnection.writeTextFile(params);
|
||||
expect(connectionInstance.sendRequest).toHaveBeenCalledWith(
|
||||
|
||||
@@ -147,7 +147,7 @@ export class Connection {
|
||||
await this.#tryCallHandler(message.method, message.params);
|
||||
} else if ('id' in message) {
|
||||
// It's a response
|
||||
this.#handleResponse(message as AnyResponse);
|
||||
this.#handleResponse(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import type { WritableStream, ReadableStream } from 'node:stream/web';
|
||||
import type { ReadableStream } from 'node:stream/web';
|
||||
|
||||
import type {
|
||||
Config,
|
||||
@@ -53,7 +53,7 @@ export async function runZedIntegration(
|
||||
argv: CliArgs,
|
||||
) {
|
||||
const { stdout: workingStdout } = createWorkingStdio();
|
||||
const stdout = Writable.toWeb(workingStdout) as WritableStream;
|
||||
const stdout = Writable.toWeb(workingStdout);
|
||||
const stdin = Readable.toWeb(process.stdin) as ReadableStream<Uint8Array>;
|
||||
|
||||
new acp.AgentSideConnection(
|
||||
@@ -355,7 +355,7 @@ export class Session {
|
||||
fc: FunctionCall,
|
||||
): Promise<Part[]> {
|
||||
const callId = fc.id ?? `${fc.name}-${Date.now()}`;
|
||||
const args = (fc.args ?? {}) as Record<string, unknown>;
|
||||
const args = fc.args ?? {};
|
||||
|
||||
const startTime = Date.now();
|
||||
|
||||
@@ -393,7 +393,7 @@ export class Session {
|
||||
}
|
||||
|
||||
const toolRegistry = this.config.getToolRegistry();
|
||||
const tool = toolRegistry.getTool(fc.name as string);
|
||||
const tool = toolRegistry.getTool(fc.name);
|
||||
|
||||
if (!tool) {
|
||||
return errorResponse(
|
||||
|
||||
@@ -317,7 +317,7 @@ describe('AgentExecutor', () => {
|
||||
onActivity,
|
||||
);
|
||||
|
||||
const agentRegistry = executor['toolRegistry'] as ToolRegistry;
|
||||
const agentRegistry = executor['toolRegistry'];
|
||||
|
||||
expect(agentRegistry).not.toBe(parentToolRegistry);
|
||||
expect(agentRegistry.getAllToolNames()).toEqual(
|
||||
@@ -754,7 +754,7 @@ describe('AgentExecutor', () => {
|
||||
expect(turn2Parts).toBeDefined();
|
||||
expect(turn2Parts).toHaveLength(1);
|
||||
|
||||
expect((turn2Parts as Part[])![0]).toEqual(
|
||||
expect((turn2Parts as Part[])[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
functionResponse: expect.objectContaining({
|
||||
name: TASK_COMPLETE_TOOL_NAME,
|
||||
@@ -944,7 +944,7 @@ describe('AgentExecutor', () => {
|
||||
const turn2Params = getMockMessageParams(1);
|
||||
const parts = turn2Params.message;
|
||||
expect(parts).toBeDefined();
|
||||
expect((parts as Part[])![0]).toEqual(
|
||||
expect((parts as Part[])[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
functionResponse: expect.objectContaining({
|
||||
id: badCallId,
|
||||
|
||||
@@ -707,7 +707,7 @@ export class AgentExecutor<TOutput extends z.ZodTypeAny> {
|
||||
|
||||
for (const [index, functionCall] of functionCalls.entries()) {
|
||||
const callId = functionCall.id ?? `${promptId}-${index}`;
|
||||
const args = (functionCall.args ?? {}) as Record<string, unknown>;
|
||||
const args = functionCall.args ?? {};
|
||||
|
||||
this.emitActivity('TOOL_CALL_START', {
|
||||
name: functionCall.name,
|
||||
@@ -918,10 +918,10 @@ export class AgentExecutor<TOutput extends z.ZodTypeAny> {
|
||||
toolNamesToLoad.push(toolRef);
|
||||
} else if (typeof toolRef === 'object' && 'schema' in toolRef) {
|
||||
// Tool instance with an explicit schema property.
|
||||
toolsList.push(toolRef.schema as FunctionDeclaration);
|
||||
toolsList.push(toolRef.schema);
|
||||
} else {
|
||||
// Raw `FunctionDeclaration` object.
|
||||
toolsList.push(toolRef as FunctionDeclaration);
|
||||
toolsList.push(toolRef);
|
||||
}
|
||||
}
|
||||
// Add schemas from tools that were registered by name.
|
||||
|
||||
@@ -43,9 +43,9 @@ describe('policyCatalog', () => {
|
||||
|
||||
it('clones policy maps so edits do not leak between calls', () => {
|
||||
const firstCall = getModelPolicyChain({ previewEnabled: false });
|
||||
firstCall[0]!.actions.terminal = 'silent';
|
||||
firstCall[0].actions.terminal = 'silent';
|
||||
const secondCall = getModelPolicyChain({ previewEnabled: false });
|
||||
expect(secondCall[0]!.actions.terminal).toBe('prompt');
|
||||
expect(secondCall[0].actions.terminal).toBe('prompt');
|
||||
});
|
||||
|
||||
it('passes when there is exactly one last-resort policy', () => {
|
||||
|
||||
@@ -827,15 +827,15 @@ describe('Server Config (config.ts)', () => {
|
||||
).ToolRegistry.prototype.registerTool;
|
||||
|
||||
// Check that registerTool was called for ShellTool
|
||||
const wasShellToolRegistered = (registerToolMock as Mock).mock.calls.some(
|
||||
const wasShellToolRegistered = registerToolMock.mock.calls.some(
|
||||
(call) => call[0] instanceof vi.mocked(ShellTool),
|
||||
);
|
||||
expect(wasShellToolRegistered).toBe(true);
|
||||
|
||||
// Check that registerTool was NOT called for ReadFileTool
|
||||
const wasReadFileToolRegistered = (
|
||||
registerToolMock as Mock
|
||||
).mock.calls.some((call) => call[0] instanceof vi.mocked(ReadFileTool));
|
||||
const wasReadFileToolRegistered = registerToolMock.mock.calls.some(
|
||||
(call) => call[0] instanceof vi.mocked(ReadFileTool),
|
||||
);
|
||||
expect(wasReadFileToolRegistered).toBe(false);
|
||||
});
|
||||
|
||||
@@ -948,9 +948,9 @@ describe('Server Config (config.ts)', () => {
|
||||
}
|
||||
).ToolRegistry.prototype.registerTool;
|
||||
|
||||
const wasShellToolRegistered = (
|
||||
registerToolMock as Mock
|
||||
).mock.calls.some((call) => call[0] instanceof vi.mocked(ShellTool));
|
||||
const wasShellToolRegistered = registerToolMock.mock.calls.some(
|
||||
(call) => call[0] instanceof vi.mocked(ShellTool),
|
||||
);
|
||||
expect(wasShellToolRegistered).toBe(true);
|
||||
});
|
||||
|
||||
@@ -968,9 +968,9 @@ describe('Server Config (config.ts)', () => {
|
||||
}
|
||||
).ToolRegistry.prototype.registerTool;
|
||||
|
||||
const wasShellToolRegistered = (
|
||||
registerToolMock as Mock
|
||||
).mock.calls.some((call) => call[0] instanceof vi.mocked(ShellTool));
|
||||
const wasShellToolRegistered = registerToolMock.mock.calls.some(
|
||||
(call) => call[0] instanceof vi.mocked(ShellTool),
|
||||
);
|
||||
expect(wasShellToolRegistered).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,7 +11,6 @@ import { PolicyDecision, getHookSource } from '../policy/types.js';
|
||||
import {
|
||||
MessageBusType,
|
||||
type Message,
|
||||
type HookExecutionRequest,
|
||||
type HookPolicyDecision,
|
||||
} from './types.js';
|
||||
import { safeJsonStringify } from '../utils/safeJsonStringify.js';
|
||||
@@ -91,7 +90,7 @@ export class MessageBus extends EventEmitter {
|
||||
}
|
||||
} else if (message.type === MessageBusType.HOOK_EXECUTION_REQUEST) {
|
||||
// Handle hook execution requests through policy evaluation
|
||||
const hookRequest = message as HookExecutionRequest;
|
||||
const hookRequest = message;
|
||||
const decision = await this.policyEngine.checkHook(hookRequest);
|
||||
|
||||
// Map decision to allow/deny for observability (ASK_USER treated as deny for hooks)
|
||||
|
||||
@@ -299,7 +299,7 @@ describe('BaseLlmClient', () => {
|
||||
// Validate the telemetry event content - find the most recent call
|
||||
const calls = vi.mocked(logMalformedJsonResponse).mock.calls;
|
||||
const lastCall = calls[calls.length - 1];
|
||||
const event = lastCall[1] as MalformedJsonResponseEvent;
|
||||
const event = lastCall[1];
|
||||
expect(event.model).toBe(defaultOptions.modelConfigKey.model);
|
||||
});
|
||||
|
||||
@@ -347,7 +347,7 @@ describe('BaseLlmClient', () => {
|
||||
expect(logMalformedJsonResponse).toHaveBeenCalled();
|
||||
const calls = vi.mocked(logMalformedJsonResponse).mock.calls;
|
||||
const lastCall = calls[calls.length - 1];
|
||||
const event = lastCall[1] as MalformedJsonResponseEvent;
|
||||
const event = lastCall[1];
|
||||
|
||||
// This is the key assertion: it should be the resolved model, not the alias
|
||||
expect(event.model).toBe(resolvedModel);
|
||||
|
||||
@@ -149,10 +149,7 @@ describe('createContentGenerator', () => {
|
||||
},
|
||||
});
|
||||
expect(generator).toEqual(
|
||||
new LoggingContentGenerator(
|
||||
(mockGenerator as GoogleGenAI).models,
|
||||
mockConfig,
|
||||
),
|
||||
new LoggingContentGenerator(mockGenerator.models, mockConfig),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -342,10 +339,7 @@ describe('createContentGenerator', () => {
|
||||
},
|
||||
});
|
||||
expect(generator).toEqual(
|
||||
new LoggingContentGenerator(
|
||||
(mockGenerator as GoogleGenAI).models,
|
||||
mockConfig,
|
||||
),
|
||||
new LoggingContentGenerator(mockGenerator.models, mockConfig),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1531,7 +1531,7 @@ describe('CoreToolScheduler request queueing', () => {
|
||||
// Capture confirmation handlers for awaiting_approval tools
|
||||
toolCalls.forEach((call) => {
|
||||
if (call.status === 'awaiting_approval') {
|
||||
const waitingCall = call as WaitingToolCall;
|
||||
const waitingCall = call;
|
||||
if (waitingCall.confirmationDetails?.onConfirm) {
|
||||
const originalHandler = pendingConfirmations.find(
|
||||
(h) => h === waitingCall.confirmationDetails.onConfirm,
|
||||
|
||||
@@ -506,7 +506,7 @@ export class CoreToolScheduler {
|
||||
// Preserve diff for cancelled edit operations
|
||||
let resultDisplay: ToolResultDisplay | undefined = undefined;
|
||||
if (currentCall.status === 'awaiting_approval') {
|
||||
const waitingCall = currentCall as WaitingToolCall;
|
||||
const waitingCall = currentCall;
|
||||
if (waitingCall.confirmationDetails.type === 'edit') {
|
||||
resultDisplay = {
|
||||
fileDiff: waitingCall.confirmationDetails.fileDiff,
|
||||
|
||||
@@ -270,9 +270,9 @@ describe('GeminiChat', () => {
|
||||
// 3. Verify history was recorded correctly
|
||||
const history = chat.getHistory();
|
||||
expect(history.length).toBe(2); // user turn + model turn
|
||||
const modelTurn = history[1]!;
|
||||
const modelTurn = history[1];
|
||||
expect(modelTurn?.parts?.length).toBe(1); // The empty part is discarded
|
||||
expect(modelTurn?.parts![0]!.functionCall).toBeDefined();
|
||||
expect(modelTurn?.parts![0].functionCall).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fail if the stream ends with an empty part and has no finishReason', async () => {
|
||||
@@ -370,9 +370,9 @@ describe('GeminiChat', () => {
|
||||
// 3. Verify history was recorded correctly with only the valid part.
|
||||
const history = chat.getHistory();
|
||||
expect(history.length).toBe(2); // user turn + model turn
|
||||
const modelTurn = history[1]!;
|
||||
const modelTurn = history[1];
|
||||
expect(modelTurn?.parts?.length).toBe(1);
|
||||
expect(modelTurn?.parts![0]!.text).toBe('Initial valid content...');
|
||||
expect(modelTurn?.parts![0].text).toBe('Initial valid content...');
|
||||
});
|
||||
|
||||
it('should consolidate subsequent text chunks after receiving an empty text chunk', async () => {
|
||||
@@ -414,9 +414,9 @@ describe('GeminiChat', () => {
|
||||
// 3. Assert: Check that the final history was correctly consolidated.
|
||||
const history = chat.getHistory();
|
||||
expect(history.length).toBe(2);
|
||||
const modelTurn = history[1]!;
|
||||
const modelTurn = history[1];
|
||||
expect(modelTurn?.parts?.length).toBe(1);
|
||||
expect(modelTurn?.parts![0]!.text).toBe('Hello World!');
|
||||
expect(modelTurn?.parts![0].text).toBe('Hello World!');
|
||||
});
|
||||
|
||||
it('should consolidate adjacent text parts that arrive in separate stream chunks', async () => {
|
||||
@@ -476,14 +476,14 @@ describe('GeminiChat', () => {
|
||||
// The history should contain the user's turn and ONE consolidated model turn.
|
||||
expect(history.length).toBe(2);
|
||||
|
||||
const modelTurn = history[1]!;
|
||||
const modelTurn = history[1];
|
||||
expect(modelTurn.role).toBe('model');
|
||||
|
||||
// The model turn should have 3 distinct parts: the merged text, the function call, and the final text.
|
||||
expect(modelTurn?.parts?.length).toBe(3);
|
||||
expect(modelTurn?.parts![0]!.text).toBe('This is the first part.');
|
||||
expect(modelTurn.parts![1]!.functionCall).toBeDefined();
|
||||
expect(modelTurn.parts![2]!.text).toBe('This is the second part.');
|
||||
expect(modelTurn?.parts![0].text).toBe('This is the first part.');
|
||||
expect(modelTurn.parts![1].functionCall).toBeDefined();
|
||||
expect(modelTurn.parts![2].text).toBe('This is the second part.');
|
||||
});
|
||||
it('should preserve text parts that stream in the same chunk as a thought', async () => {
|
||||
// 1. Mock the API to return a single chunk containing both a thought and visible text.
|
||||
@@ -525,14 +525,14 @@ describe('GeminiChat', () => {
|
||||
// The history should contain two turns: the user's message and the model's response.
|
||||
expect(history.length).toBe(2);
|
||||
|
||||
const modelTurn = history[1]!;
|
||||
const modelTurn = history[1];
|
||||
expect(modelTurn.role).toBe('model');
|
||||
|
||||
// CRUCIAL ASSERTION:
|
||||
// The buggy code would fail here, resulting in parts.length being 0.
|
||||
// The corrected code will pass, preserving the single visible text part.
|
||||
expect(modelTurn?.parts?.length).toBe(1);
|
||||
expect(modelTurn?.parts![0]!.text).toBe(
|
||||
expect(modelTurn?.parts![0].text).toBe(
|
||||
'This is the visible text that should not be lost.',
|
||||
);
|
||||
});
|
||||
@@ -1979,8 +1979,8 @@ describe('GeminiChat', () => {
|
||||
);
|
||||
|
||||
const history = chat.getHistory();
|
||||
const modelTurn = history[1]!;
|
||||
expect(modelTurn.parts![0]!.text).toBe('Success on retry');
|
||||
const modelTurn = history[1];
|
||||
expect(modelTurn.parts![0].text).toBe('Success on retry');
|
||||
});
|
||||
|
||||
it('should switch to DEFAULT_GEMINI_FLASH_MODEL and use thinkingBudget when falling back from a gemini-3 model', async () => {
|
||||
@@ -2154,11 +2154,11 @@ describe('GeminiChat', () => {
|
||||
const history = chat.getHistory();
|
||||
expect(history.length).toBe(2); // user turn + final model turn
|
||||
|
||||
const modelTurn = history[1]!;
|
||||
const modelTurn = history[1];
|
||||
// The model turn should only contain the text from the successful attempt
|
||||
expect(modelTurn!.parts![0]!.text).toBe('Successful final response');
|
||||
expect(modelTurn.parts![0].text).toBe('Successful final response');
|
||||
// It should NOT contain any text from the failed attempt
|
||||
expect(modelTurn!.parts![0]!.text).not.toContain(
|
||||
expect(modelTurn.parts![0].text).not.toContain(
|
||||
'This valid part should be discarded',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -357,9 +357,7 @@ export class GeminiChat {
|
||||
// Check if we have more attempts left.
|
||||
if (attempt < maxAttempts - 1) {
|
||||
const delayMs = INVALID_CONTENT_RETRY_OPTIONS.initialDelayMs;
|
||||
const retryType = isContentError
|
||||
? (error as InvalidStreamError).type
|
||||
: 'NETWORK_ERROR';
|
||||
const retryType = isContentError ? error.type : 'NETWORK_ERROR';
|
||||
|
||||
logContentRetry(
|
||||
this.config,
|
||||
@@ -382,11 +380,7 @@ export class GeminiChat {
|
||||
) {
|
||||
logContentRetryFailure(
|
||||
this.config,
|
||||
new ContentRetryFailureEvent(
|
||||
maxAttempts,
|
||||
(lastError as InvalidStreamError).type,
|
||||
model,
|
||||
),
|
||||
new ContentRetryFailureEvent(maxAttempts, lastError.type, model),
|
||||
);
|
||||
}
|
||||
throw lastError;
|
||||
@@ -712,7 +706,7 @@ export class GeminiChat {
|
||||
if (content.role === 'model' && content.parts) {
|
||||
const newParts = content.parts.slice();
|
||||
for (let j = 0; j < newParts.length; j++) {
|
||||
const part = newParts[j]!;
|
||||
const part = newParts[j];
|
||||
if (part.functionCall) {
|
||||
if (!part.thoughtSignature) {
|
||||
newParts[j] = {
|
||||
@@ -913,7 +907,7 @@ export class GeminiChat {
|
||||
name: call.request.name,
|
||||
args: call.request.args,
|
||||
result: call.response?.responseParts || null,
|
||||
status: call.status as 'error' | 'success' | 'cancelled',
|
||||
status: call.status,
|
||||
timestamp: new Date().toISOString(),
|
||||
resultDisplay,
|
||||
};
|
||||
|
||||
@@ -408,7 +408,7 @@ export class Logger {
|
||||
}
|
||||
|
||||
// 2. Attempt to delete the old raw path for backward compatibility.
|
||||
const oldPath = path.join(this.geminiDir!, `checkpoint-${tag}.json`);
|
||||
const oldPath = path.join(this.geminiDir, `checkpoint-${tag}.json`);
|
||||
if (newPath !== oldPath) {
|
||||
try {
|
||||
await fs.unlink(oldPath);
|
||||
|
||||
@@ -264,7 +264,7 @@ export class Turn {
|
||||
}
|
||||
|
||||
// Assuming other events are chunks with a `value` property
|
||||
const resp = streamEvent.value as GenerateContentResponse;
|
||||
const resp = streamEvent.value;
|
||||
if (!resp) continue; // Skip if there's no response body
|
||||
|
||||
this.debugResponses.push(resp);
|
||||
@@ -374,7 +374,7 @@ export class Turn {
|
||||
fnCall.id ??
|
||||
`${fnCall.name}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
||||
const name = fnCall.name || 'undefined_tool_name';
|
||||
const args = (fnCall.args || {}) as Record<string, unknown>;
|
||||
const args = fnCall.args || {};
|
||||
|
||||
const toolCallRequest: ToolCallRequestInfo = {
|
||||
callId,
|
||||
|
||||
@@ -137,7 +137,7 @@ export class HookRegistry {
|
||||
continue;
|
||||
}
|
||||
|
||||
const typedEventName = eventName as HookEventName;
|
||||
const typedEventName = eventName;
|
||||
|
||||
if (!Array.isArray(definitions)) {
|
||||
debugLogger.warn(
|
||||
|
||||
@@ -21,9 +21,9 @@ type MockChildProcessWithoutNullStreams = ChildProcessWithoutNullStreams & {
|
||||
|
||||
// Mock child_process with importOriginal for partial mocking
|
||||
vi.mock('node:child_process', async (importOriginal) => {
|
||||
const actual = (await importOriginal()) as object;
|
||||
const actual = await importOriginal();
|
||||
return {
|
||||
...actual,
|
||||
...(actual as object),
|
||||
spawn: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -21,9 +21,9 @@ type MockChildProcessWithoutNullStreams = ChildProcessWithoutNullStreams & {
|
||||
|
||||
// Mock child_process with importOriginal for partial mocking
|
||||
vi.mock('node:child_process', async (importOriginal) => {
|
||||
const actual = (await importOriginal()) as object;
|
||||
const actual = await importOriginal();
|
||||
return {
|
||||
...actual,
|
||||
...(actual as object),
|
||||
spawn: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -7,10 +7,9 @@
|
||||
import { vi } from 'vitest';
|
||||
|
||||
vi.mock('node:child_process', async (importOriginal) => {
|
||||
const actual =
|
||||
(await importOriginal()) as typeof import('node:child_process');
|
||||
const actual = await importOriginal();
|
||||
return {
|
||||
...actual,
|
||||
...(actual as object),
|
||||
execSync: vi.fn(),
|
||||
spawnSync: vi.fn(() => ({ status: 0 })),
|
||||
};
|
||||
|
||||
@@ -302,8 +302,8 @@ export class MCPOAuthProvider {
|
||||
<html>
|
||||
<body>
|
||||
<h1>Authentication Failed</h1>
|
||||
<p>Error: ${(error as string).replace(/</g, '<').replace(/>/g, '>')}</p>
|
||||
<p>${((url.searchParams.get('error_description') || '') as string).replace(/</g, '<').replace(/>/g, '>')}</p>
|
||||
<p>Error: ${error.replace(/</g, '<').replace(/>/g, '>')}</p>
|
||||
<p>${(url.searchParams.get('error_description') || '').replace(/</g, '<').replace(/>/g, '>')}</p>
|
||||
<p>You can close this window.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -519,7 +519,7 @@ export class LoopDetectionService {
|
||||
signal: AbortSignal,
|
||||
): Promise<Record<string, unknown> | null> {
|
||||
try {
|
||||
const result = (await this.config.getBaseLlmClient().generateJson({
|
||||
const result = await this.config.getBaseLlmClient().generateJson({
|
||||
modelConfigKey: { model },
|
||||
contents,
|
||||
schema: LOOP_DETECTION_SCHEMA,
|
||||
@@ -527,7 +527,7 @@ export class LoopDetectionService {
|
||||
abortSignal: signal,
|
||||
promptId: this.promptId,
|
||||
maxAttempts: 2,
|
||||
})) as Record<string, unknown>;
|
||||
});
|
||||
|
||||
if (
|
||||
result &&
|
||||
|
||||
@@ -258,10 +258,7 @@ export class ModelConfigService {
|
||||
// TODO(joshualitt): Consider knobs here, i.e. opt-in to deep merging
|
||||
// arrays on a case-by-case basis.
|
||||
if (this.isObject(accValue) && this.isObject(objValue)) {
|
||||
acc[key] = this.deepMerge(
|
||||
accValue as Record<string, unknown>,
|
||||
objValue as Record<string, unknown>,
|
||||
);
|
||||
acc[key] = this.deepMerge(accValue, objValue);
|
||||
} else {
|
||||
acc[key] = objValue;
|
||||
}
|
||||
|
||||
@@ -36,10 +36,9 @@ vi.mock('@lydell/node-pty', () => ({
|
||||
spawn: mockPtySpawn,
|
||||
}));
|
||||
vi.mock('node:child_process', async (importOriginal) => {
|
||||
const actual =
|
||||
(await importOriginal()) as typeof import('node:child_process');
|
||||
const actual = await importOriginal();
|
||||
return {
|
||||
...actual,
|
||||
...(actual as object),
|
||||
spawn: mockCpSpawn,
|
||||
};
|
||||
});
|
||||
@@ -90,7 +89,7 @@ const createMockSerializeTerminalToObjectReturnValue = (
|
||||
text: string | string[],
|
||||
): AnsiOutput => {
|
||||
const lines = Array.isArray(text) ? text : text.split('\n');
|
||||
const len = (shellExecutionConfig.terminalHeight ?? 24) as number;
|
||||
const len = shellExecutionConfig.terminalHeight ?? 24;
|
||||
const expected: AnsiOutput = Array.from({ length: len }, (_, i) => [
|
||||
{
|
||||
text: (lines[i] || '').trim(),
|
||||
@@ -108,7 +107,7 @@ const createMockSerializeTerminalToObjectReturnValue = (
|
||||
|
||||
const createExpectedAnsiOutput = (text: string | string[]): AnsiOutput => {
|
||||
const lines = Array.isArray(text) ? text : text.split('\n');
|
||||
const len = (shellExecutionConfig.terminalHeight ?? 24) as number;
|
||||
const len = shellExecutionConfig.terminalHeight ?? 24;
|
||||
const expected: AnsiOutput = Array.from({ length: len }, (_, i) => [
|
||||
{
|
||||
text: expect.stringMatching((lines[i] || '').trim()),
|
||||
@@ -419,7 +418,7 @@ describe('ShellExecutionService', () => {
|
||||
it('should write to the pty and trigger a render', async () => {
|
||||
vi.useFakeTimers();
|
||||
await simulateExecution('interactive-app', (pty) => {
|
||||
ShellExecutionService.writeToPty(pty.pid!, 'input');
|
||||
ShellExecutionService.writeToPty(pty.pid, 'input');
|
||||
pty.onExit.mock.calls[0][0]({ exitCode: 0, signal: null });
|
||||
});
|
||||
|
||||
@@ -434,7 +433,7 @@ describe('ShellExecutionService', () => {
|
||||
it('should resize the pty and the headless terminal', async () => {
|
||||
await simulateExecution('ls -l', (pty) => {
|
||||
pty.onData.mock.calls[0][0]('file1.txt\n');
|
||||
ShellExecutionService.resizePty(pty.pid!, 100, 40);
|
||||
ShellExecutionService.resizePty(pty.pid, 100, 40);
|
||||
pty.onExit.mock.calls[0][0]({ exitCode: 0, signal: null });
|
||||
});
|
||||
|
||||
@@ -448,7 +447,7 @@ describe('ShellExecutionService', () => {
|
||||
.mockReturnValue(false);
|
||||
|
||||
await simulateExecution('ls -l', (pty) => {
|
||||
ShellExecutionService.resizePty(pty.pid!, 100, 40);
|
||||
ShellExecutionService.resizePty(pty.pid, 100, 40);
|
||||
pty.onExit.mock.calls[0][0]({ exitCode: 0, signal: null });
|
||||
});
|
||||
|
||||
@@ -468,7 +467,7 @@ describe('ShellExecutionService', () => {
|
||||
// We don't expect this test to throw an error
|
||||
await expect(
|
||||
simulateExecution('ls -l', (pty) => {
|
||||
ShellExecutionService.resizePty(pty.pid!, 100, 40);
|
||||
ShellExecutionService.resizePty(pty.pid, 100, 40);
|
||||
pty.onExit.mock.calls[0][0]({ exitCode: 0, signal: null });
|
||||
}),
|
||||
).resolves.not.toThrow();
|
||||
@@ -484,7 +483,7 @@ describe('ShellExecutionService', () => {
|
||||
|
||||
await expect(
|
||||
simulateExecution('ls -l', (pty) => {
|
||||
ShellExecutionService.resizePty(pty.pid!, 100, 40);
|
||||
ShellExecutionService.resizePty(pty.pid, 100, 40);
|
||||
pty.onExit.mock.calls[0][0]({ exitCode: 0, signal: null });
|
||||
}),
|
||||
).rejects.toThrow('Some other error');
|
||||
@@ -493,7 +492,7 @@ describe('ShellExecutionService', () => {
|
||||
it('should scroll the headless terminal', async () => {
|
||||
await simulateExecution('ls -l', (pty) => {
|
||||
pty.onData.mock.calls[0][0]('file1.txt\n');
|
||||
ShellExecutionService.scrollPty(pty.pid!, 10);
|
||||
ShellExecutionService.scrollPty(pty.pid, 10);
|
||||
pty.onExit.mock.calls[0][0]({ exitCode: 0, signal: null });
|
||||
});
|
||||
|
||||
|
||||
@@ -715,9 +715,7 @@ export class ShellExecutionService {
|
||||
error,
|
||||
aborted: abortSignal.aborted,
|
||||
pid: ptyProcess.pid,
|
||||
executionMethod:
|
||||
(ptyInfo?.name as 'node-pty' | 'lydell-node-pty') ??
|
||||
'node-pty',
|
||||
executionMethod: ptyInfo?.name ?? 'node-pty',
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ describe('Global Activity Detector Functions', () => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
it('should record activity on existing detector', () => {
|
||||
const detector = getActivityDetector()!;
|
||||
const detector = getActivityDetector();
|
||||
const beforeTime = detector.getLastActivityTime();
|
||||
vi.advanceTimersByTime(100);
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ export async function resolveTelemetrySettings(options: {
|
||||
settings.enabled;
|
||||
|
||||
const rawTarget =
|
||||
(argv.telemetryTarget as string | TelemetryTarget | undefined) ??
|
||||
argv.telemetryTarget ??
|
||||
env['GEMINI_TELEMETRY_TARGET'] ??
|
||||
(settings.target as string | TelemetryTarget | undefined);
|
||||
const target = parseTelemetryTargetValue(rawTarget);
|
||||
@@ -80,7 +80,7 @@ export async function resolveTelemetrySettings(options: {
|
||||
settings.otlpEndpoint;
|
||||
|
||||
const rawProtocol =
|
||||
(argv.telemetryOtlpProtocol as string | undefined) ??
|
||||
argv.telemetryOtlpProtocol ??
|
||||
env['GEMINI_TELEMETRY_OTLP_PROTOCOL'] ??
|
||||
settings.otlpProtocol;
|
||||
const otlpProtocol = (['grpc', 'http'] as const).find(
|
||||
|
||||
@@ -101,7 +101,7 @@ describe('Telemetry Metrics', () => {
|
||||
vi.resetModules();
|
||||
vi.doMock('@opentelemetry/api', () => {
|
||||
const actualApi = originalOtelMockFactory();
|
||||
(actualApi.metrics.getMeter as Mock).mockReturnValue(mockMeterInstance);
|
||||
actualApi.metrics.getMeter.mockReturnValue(mockMeterInstance);
|
||||
return actualApi;
|
||||
});
|
||||
|
||||
|
||||
@@ -291,8 +291,7 @@ describe('StartupProfiler', () => {
|
||||
|
||||
profiler.flush(mockConfig);
|
||||
|
||||
const calls = (recordStartupPerformance as ReturnType<typeof vi.fn>).mock
|
||||
.calls;
|
||||
const calls = recordStartupPerformance.mock.calls;
|
||||
const outerCall = calls.find((call) => call[2].phase === 'outer');
|
||||
const innerCall = calls.find((call) => call[2].phase === 'inner');
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ export async function runInDevTraceSpan<R>(
|
||||
span.setAttribute('output-json', safeJsonStringify(meta.output));
|
||||
}
|
||||
for (const [key, value] of Object.entries(meta.attributes)) {
|
||||
span.setAttribute(key, value as AttributeValue);
|
||||
span.setAttribute(key, value);
|
||||
}
|
||||
if (meta.error) {
|
||||
span.setStatus({
|
||||
|
||||
@@ -15,7 +15,6 @@ import type { ApprovalMode } from '../policy/types.js';
|
||||
|
||||
import type { CompletedToolCall } from '../core/coreToolScheduler.js';
|
||||
import { DiscoveredMCPTool } from '../tools/mcp-tool.js';
|
||||
import type { FileDiff } from '../tools/tools.js';
|
||||
import { AuthType } from '../core/contentGenerator.js';
|
||||
import type { LogAttributes, LogRecord } from '@opentelemetry/api-logs';
|
||||
import {
|
||||
@@ -115,9 +114,7 @@ export class StartSessionEvent implements BaseTelemetryEvent {
|
||||
.getAllTools()
|
||||
.filter((tool) => tool instanceof DiscoveredMCPTool);
|
||||
this.mcp_tools_count = mcpTools.length;
|
||||
this.mcp_tools = mcpTools
|
||||
.map((tool) => (tool as DiscoveredMCPTool).name)
|
||||
.join(',');
|
||||
this.mcp_tools = mcpTools.map((tool) => tool.name).join(',');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,7 +296,7 @@ export class ToolCallEvent implements BaseTelemetryEvent {
|
||||
call.response.resultDisplay !== null &&
|
||||
'diffStat' in call.response.resultDisplay
|
||||
) {
|
||||
const diffStat = (call.response.resultDisplay as FileDiff).diffStat;
|
||||
const diffStat = call.response.resultDisplay.diffStat;
|
||||
if (diffStat) {
|
||||
this.metadata = {
|
||||
model_added_lines: diffStat.model_added_lines,
|
||||
|
||||
@@ -33,12 +33,12 @@ export class MockMessageBus {
|
||||
|
||||
// Capture hook-specific messages
|
||||
if (message.type === MessageBusType.HOOK_EXECUTION_REQUEST) {
|
||||
this.hookRequests.push(message as HookExecutionRequest);
|
||||
this.hookRequests.push(message);
|
||||
|
||||
// Auto-respond with success for testing
|
||||
const response: HookExecutionResponse = {
|
||||
type: MessageBusType.HOOK_EXECUTION_RESPONSE,
|
||||
correlationId: (message as HookExecutionRequest).correlationId,
|
||||
correlationId: message.correlationId,
|
||||
success: true,
|
||||
output: {
|
||||
decision: 'allow',
|
||||
|
||||
@@ -156,12 +156,12 @@ describe('EditTool', () => {
|
||||
const problematicSnippet =
|
||||
snippetMatch && snippetMatch[1] ? snippetMatch[1] : '';
|
||||
|
||||
if (((schema as any).properties as any)?.corrected_target_snippet) {
|
||||
if ((schema as any).properties?.corrected_target_snippet) {
|
||||
return Promise.resolve({
|
||||
corrected_target_snippet: problematicSnippet,
|
||||
});
|
||||
}
|
||||
if (((schema as any).properties as any)?.corrected_new_string) {
|
||||
if ((schema as any).properties?.corrected_new_string) {
|
||||
// For new_string correction, we might need more sophisticated logic,
|
||||
// but for now, returning original is a safe default if not specified by a test.
|
||||
const originalNewStringMatch = promptText.match(
|
||||
|
||||
@@ -523,7 +523,7 @@ class GrepToolInvocation extends BaseToolInvocation<
|
||||
const allMatches: GrepMatch[] = [];
|
||||
|
||||
for await (const filePath of filesStream) {
|
||||
const fileAbsolutePath = filePath as string;
|
||||
const fileAbsolutePath = filePath;
|
||||
try {
|
||||
const content = await fsPromises.readFile(fileAbsolutePath, 'utf8');
|
||||
const lines = content.split(/\r?\n/);
|
||||
|
||||
@@ -184,11 +184,11 @@ export async function modifyWithEditor<ToolParams>(
|
||||
overrides !== undefined && 'proposedContent' in overrides;
|
||||
|
||||
const currentContent = hasCurrentOverride
|
||||
? (overrides!.currentContent ?? '')
|
||||
? (overrides.currentContent ?? '')
|
||||
: await modifyContext.getCurrentContent(originalParams);
|
||||
|
||||
const proposedContent = hasProposedOverride
|
||||
? (overrides!.proposedContent ?? '')
|
||||
? (overrides.proposedContent ?? '')
|
||||
: await modifyContext.getProposedContent(originalParams);
|
||||
|
||||
const { oldPath, newPath, dirPath } = createTempFilesForModify(
|
||||
|
||||
@@ -16,7 +16,6 @@ import type { Config } from '../config/config.js';
|
||||
import { FileDiscoveryService } from '../services/fileDiscoveryService.js';
|
||||
import { StandardFileSystemService } from '../services/fileSystemService.js';
|
||||
import { createMockWorkspaceContext } from '../test-utils/mockWorkspaceContext.js';
|
||||
import type { ToolInvocation, ToolResult } from './tools.js';
|
||||
import { WorkspaceContext } from '../utils/workspaceContext.js';
|
||||
|
||||
vi.mock('../telemetry/loggers.js', () => ({
|
||||
@@ -72,10 +71,7 @@ describe('ReadFileTool', () => {
|
||||
};
|
||||
const result = tool.build(params);
|
||||
expect(typeof result).not.toBe('string');
|
||||
const invocation = result as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
>;
|
||||
const invocation = result;
|
||||
expect(invocation.toolLocations()[0].path).toBe(
|
||||
path.join(tempRootDir, 'test.txt'),
|
||||
);
|
||||
@@ -146,11 +142,9 @@ describe('ReadFileTool', () => {
|
||||
};
|
||||
const invocation = tool.build(params);
|
||||
expect(typeof invocation).not.toBe('string');
|
||||
expect(
|
||||
(
|
||||
invocation as ToolInvocation<ReadFileToolParams, ToolResult>
|
||||
).getDescription(),
|
||||
).toBe(path.join('sub', 'dir', 'file.txt'));
|
||||
expect(invocation.getDescription()).toBe(
|
||||
path.join('sub', 'dir', 'file.txt'),
|
||||
);
|
||||
});
|
||||
|
||||
it('should return shortened path when file path is deep', () => {
|
||||
@@ -170,9 +164,7 @@ describe('ReadFileTool', () => {
|
||||
const params: ReadFileToolParams = { file_path: deepPath };
|
||||
const invocation = tool.build(params);
|
||||
expect(typeof invocation).not.toBe('string');
|
||||
const desc = (
|
||||
invocation as ToolInvocation<ReadFileToolParams, ToolResult>
|
||||
).getDescription();
|
||||
const desc = invocation.getDescription();
|
||||
expect(desc).toContain('...');
|
||||
expect(desc).toContain('file.txt');
|
||||
});
|
||||
@@ -184,22 +176,16 @@ describe('ReadFileTool', () => {
|
||||
};
|
||||
const invocation = tool.build(params);
|
||||
expect(typeof invocation).not.toBe('string');
|
||||
expect(
|
||||
(
|
||||
invocation as ToolInvocation<ReadFileToolParams, ToolResult>
|
||||
).getDescription(),
|
||||
).toBe(path.join('sub', 'dir', 'file.txt'));
|
||||
expect(invocation.getDescription()).toBe(
|
||||
path.join('sub', 'dir', 'file.txt'),
|
||||
);
|
||||
});
|
||||
|
||||
it('should return . if path is the root directory', () => {
|
||||
const params: ReadFileToolParams = { file_path: tempRootDir };
|
||||
const invocation = tool.build(params);
|
||||
expect(typeof invocation).not.toBe('string');
|
||||
expect(
|
||||
(
|
||||
invocation as ToolInvocation<ReadFileToolParams, ToolResult>
|
||||
).getDescription(),
|
||||
).toBe('.');
|
||||
expect(invocation.getDescription()).toBe('.');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -209,10 +195,7 @@ describe('ReadFileTool', () => {
|
||||
const fileContent = 'This is a test file.';
|
||||
await fsp.writeFile(filePath, fileContent, 'utf-8');
|
||||
const params: ReadFileToolParams = { file_path: 'textfile.txt' };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
>;
|
||||
const invocation = tool.build(params);
|
||||
|
||||
expect(await invocation.execute(abortSignal)).toEqual({
|
||||
llmContent: fileContent,
|
||||
@@ -223,10 +206,7 @@ describe('ReadFileTool', () => {
|
||||
it('should return error if file does not exist', async () => {
|
||||
const filePath = path.join(tempRootDir, 'nonexistent.txt');
|
||||
const params: ReadFileToolParams = { file_path: filePath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
>;
|
||||
const invocation = tool.build(params);
|
||||
|
||||
const result = await invocation.execute(abortSignal);
|
||||
expect(result).toEqual({
|
||||
@@ -245,10 +225,7 @@ describe('ReadFileTool', () => {
|
||||
const fileContent = 'This is a test file.';
|
||||
await fsp.writeFile(filePath, fileContent, 'utf-8');
|
||||
const params: ReadFileToolParams = { file_path: filePath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
>;
|
||||
const invocation = tool.build(params);
|
||||
|
||||
expect(await invocation.execute(abortSignal)).toEqual({
|
||||
llmContent: fileContent,
|
||||
@@ -260,10 +237,7 @@ describe('ReadFileTool', () => {
|
||||
const dirPath = path.join(tempRootDir, 'directory');
|
||||
await fsp.mkdir(dirPath);
|
||||
const params: ReadFileToolParams = { file_path: dirPath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
>;
|
||||
const invocation = tool.build(params);
|
||||
|
||||
const result = await invocation.execute(abortSignal);
|
||||
expect(result).toEqual({
|
||||
@@ -283,10 +257,7 @@ describe('ReadFileTool', () => {
|
||||
const largeContent = 'x'.repeat(21 * 1024 * 1024);
|
||||
await fsp.writeFile(filePath, largeContent, 'utf-8');
|
||||
const params: ReadFileToolParams = { file_path: filePath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
>;
|
||||
const invocation = tool.build(params);
|
||||
|
||||
const result = await invocation.execute(abortSignal);
|
||||
expect(result).toHaveProperty('error');
|
||||
@@ -302,10 +273,7 @@ describe('ReadFileTool', () => {
|
||||
const fileContent = `Short line\n${longLine}\nAnother short line`;
|
||||
await fsp.writeFile(filePath, fileContent, 'utf-8');
|
||||
const params: ReadFileToolParams = { file_path: filePath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
>;
|
||||
const invocation = tool.build(params);
|
||||
|
||||
const result = await invocation.execute(abortSignal);
|
||||
expect(result.llmContent).toContain(
|
||||
@@ -323,10 +291,7 @@ describe('ReadFileTool', () => {
|
||||
]);
|
||||
await fsp.writeFile(imagePath, pngHeader);
|
||||
const params: ReadFileToolParams = { file_path: imagePath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
>;
|
||||
const invocation = tool.build(params);
|
||||
|
||||
const result = await invocation.execute(abortSignal);
|
||||
expect(result.llmContent).toEqual({
|
||||
@@ -344,10 +309,7 @@ describe('ReadFileTool', () => {
|
||||
const pdfHeader = Buffer.from('%PDF-1.4');
|
||||
await fsp.writeFile(pdfPath, pdfHeader);
|
||||
const params: ReadFileToolParams = { file_path: pdfPath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
>;
|
||||
const invocation = tool.build(params);
|
||||
|
||||
const result = await invocation.execute(abortSignal);
|
||||
expect(result.llmContent).toEqual({
|
||||
@@ -365,10 +327,7 @@ describe('ReadFileTool', () => {
|
||||
const binaryData = Buffer.from([0x00, 0xff, 0x00, 0xff]);
|
||||
await fsp.writeFile(binPath, binaryData);
|
||||
const params: ReadFileToolParams = { file_path: binPath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
>;
|
||||
const invocation = tool.build(params);
|
||||
|
||||
const result = await invocation.execute(abortSignal);
|
||||
expect(result.llmContent).toBe(
|
||||
@@ -382,10 +341,7 @@ describe('ReadFileTool', () => {
|
||||
const svgContent = '<svg><circle cx="50" cy="50" r="40"/></svg>';
|
||||
await fsp.writeFile(svgPath, svgContent, 'utf-8');
|
||||
const params: ReadFileToolParams = { file_path: svgPath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
>;
|
||||
const invocation = tool.build(params);
|
||||
|
||||
const result = await invocation.execute(abortSignal);
|
||||
expect(result.llmContent).toBe(svgContent);
|
||||
@@ -398,10 +354,7 @@ describe('ReadFileTool', () => {
|
||||
const largeContent = '<svg>' + 'x'.repeat(1024 * 1024 + 1) + '</svg>';
|
||||
await fsp.writeFile(svgPath, largeContent, 'utf-8');
|
||||
const params: ReadFileToolParams = { file_path: svgPath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
>;
|
||||
const invocation = tool.build(params);
|
||||
|
||||
const result = await invocation.execute(abortSignal);
|
||||
expect(result.llmContent).toBe(
|
||||
@@ -416,10 +369,7 @@ describe('ReadFileTool', () => {
|
||||
const emptyPath = path.join(tempRootDir, 'empty.txt');
|
||||
await fsp.writeFile(emptyPath, '', 'utf-8');
|
||||
const params: ReadFileToolParams = { file_path: emptyPath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
>;
|
||||
const invocation = tool.build(params);
|
||||
|
||||
const result = await invocation.execute(abortSignal);
|
||||
expect(result.llmContent).toBe('');
|
||||
@@ -437,10 +387,7 @@ describe('ReadFileTool', () => {
|
||||
offset: 5, // Start from line 6
|
||||
limit: 3,
|
||||
};
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
>;
|
||||
const invocation = tool.build(params);
|
||||
|
||||
const result = await invocation.execute(abortSignal);
|
||||
expect(result.llmContent).toContain(
|
||||
@@ -465,10 +412,7 @@ describe('ReadFileTool', () => {
|
||||
await fsp.writeFile(tempFilePath, tempFileContent, 'utf-8');
|
||||
|
||||
const params: ReadFileToolParams = { file_path: tempFilePath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
>;
|
||||
const invocation = tool.build(params);
|
||||
|
||||
const result = await invocation.execute(abortSignal);
|
||||
expect(result.llmContent).toBe(tempFileContent);
|
||||
|
||||
@@ -147,12 +147,12 @@ describe('SmartEditTool', () => {
|
||||
const problematicSnippet =
|
||||
snippetMatch && snippetMatch[1] ? snippetMatch[1] : '';
|
||||
|
||||
if (((schema as any).properties as any)?.corrected_target_snippet) {
|
||||
if ((schema as any).properties?.corrected_target_snippet) {
|
||||
return Promise.resolve({
|
||||
corrected_target_snippet: problematicSnippet,
|
||||
});
|
||||
}
|
||||
if (((schema as any).properties as any)?.corrected_new_string) {
|
||||
if ((schema as any).properties?.corrected_new_string) {
|
||||
const originalNewStringMatch = promptText.match(
|
||||
/original_new_string \(what was intended to replace original_old_string\):\n```\n([\s\S]*?)\n```/,
|
||||
);
|
||||
|
||||
@@ -536,7 +536,7 @@ describe('WriteFileTool', () => {
|
||||
expect(confirmation.onConfirm).toBeDefined();
|
||||
|
||||
// Call `onConfirm` to trigger the logic that updates the content
|
||||
await confirmation.onConfirm!(ToolConfirmationOutcome.ProceedOnce);
|
||||
await confirmation.onConfirm(ToolConfirmationOutcome.ProceedOnce);
|
||||
|
||||
// Now, check if the original `params` object (captured by the invocation) was modified
|
||||
expect(invocation.params.content).toBe('ide-modified-content');
|
||||
|
||||
@@ -48,7 +48,7 @@ export function getToolCallDataSchema(historyItemSchema?: z.ZodTypeAny) {
|
||||
export function generateCheckpointFileName(
|
||||
toolCall: ToolCallRequestInfo,
|
||||
): string | null {
|
||||
const toolArgs = toolCall.args as Record<string, unknown>;
|
||||
const toolArgs = toolCall.args;
|
||||
const toolFilePath = toolArgs['file_path'] as string;
|
||||
|
||||
if (!toolFilePath) {
|
||||
|
||||
@@ -40,7 +40,7 @@ export const read = (key: string): string[] | undefined => crawlCache.get(key);
|
||||
export const write = (key: string, results: string[], ttlMs: number): void => {
|
||||
// Clear any existing timer for this key to prevent premature deletion
|
||||
if (cacheTimers.has(key)) {
|
||||
clearTimeout(cacheTimers.get(key)!);
|
||||
clearTimeout(cacheTimers.get(key));
|
||||
}
|
||||
|
||||
// Store the new data
|
||||
|
||||
@@ -129,7 +129,7 @@ class RecursiveFileSearch implements FileSearch {
|
||||
|
||||
let filteredCandidates;
|
||||
const { files: candidates, isExactMatch } =
|
||||
await this.resultCache!.get(pattern);
|
||||
await this.resultCache.get(pattern);
|
||||
|
||||
if (isExactMatch) {
|
||||
// Use the cached result.
|
||||
@@ -151,7 +151,7 @@ class RecursiveFileSearch implements FileSearch {
|
||||
}
|
||||
|
||||
if (shouldCache) {
|
||||
this.resultCache!.set(pattern, filteredCandidates);
|
||||
this.resultCache.set(pattern, filteredCandidates);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ export function doesToolInvocationMatch(
|
||||
if (isTool(toolOrToolName)) {
|
||||
toolNames = [toolOrToolName.name, toolOrToolName.constructor.name];
|
||||
} else {
|
||||
toolNames = [toolOrToolName as string];
|
||||
toolNames = [toolOrToolName];
|
||||
}
|
||||
|
||||
if (toolNames.some((name) => SHELL_TOOL_NAMES.includes(name))) {
|
||||
|
||||
@@ -66,12 +66,12 @@ async function create(dir: string, structure: FileSystemStructure) {
|
||||
if (typeof item === 'string') {
|
||||
await fs.writeFile(path.join(newPath, item), '');
|
||||
} else {
|
||||
await create(newPath, item as FileSystemStructure);
|
||||
await create(newPath, item);
|
||||
}
|
||||
}
|
||||
} else if (typeof content === 'object' && content !== null) {
|
||||
await fs.mkdir(newPath, { recursive: true });
|
||||
await create(newPath, content as FileSystemStructure);
|
||||
await create(newPath, content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user