diff --git a/src/main/presenter/builtInToolsPresenter/base.ts b/src/main/presenter/builtInToolsPresenter/base.ts index 8b8973d4d67f85293900051c81daa6b89580dd6c..e990873a7c7674cd283cf4e914d79011a17fd135 100644 --- a/src/main/presenter/builtInToolsPresenter/base.ts +++ b/src/main/presenter/builtInToolsPresenter/base.ts @@ -2,6 +2,7 @@ import { MCPToolResponse } from '@shared/presenter' export interface BuiltInToolDefinition { name: string + alisa: string[] description: string parameters: { type: string diff --git a/src/main/presenter/builtInToolsPresenter/executeCommandTool.ts b/src/main/presenter/builtInToolsPresenter/executeCommandTool.ts index 8ee5ffedd87a92e19fa0e4f62ce5e39668ab2125..bd142d25488311f7d5c5d41fecd281c31352e504 100644 --- a/src/main/presenter/builtInToolsPresenter/executeCommandTool.ts +++ b/src/main/presenter/builtInToolsPresenter/executeCommandTool.ts @@ -8,6 +8,7 @@ const execAsync = promisify(execCallback) export const executeCommandTool: BuiltInToolDefinition = { name: 'execute_command', + alisa: ['execute_shell'], description: "Requests the execution of a command line instruction and returns the standard output and standard error. Use this tool only when needing to run commands related to the system itself; it cannot be used for network requests or accessing external websites. That is, use it only when there is a clear need, and carefully evaluate the potential side effects of the command. You must tailor the command to the user's system and clearly explain its function. For command chaining, use the appropriate chaining syntax for the user's shell. By default, it runs in the current working directory; other directories or shells can be specified as needed.This tool can only be run on the local system where it is installed. When performing operations such as package installation and uninstallation, it is necessary to first determine the correct package management command.", parameters: { diff --git a/src/main/presenter/builtInToolsPresenter/index.ts b/src/main/presenter/builtInToolsPresenter/index.ts index 298b35af2fe95ff57d6627355d69c4321fdd7a97..4b1f824452f7eec8417325f6d99d55c384717e14 100644 --- a/src/main/presenter/builtInToolsPresenter/index.ts +++ b/src/main/presenter/builtInToolsPresenter/index.ts @@ -14,7 +14,7 @@ import { listFilesTool, executeListFilesTool } from './listFilesTool' import { executeCommandTool, executeCommandToolHandler } from './executeCommandTool' import { useA2AServerTool, executeUseA2AServerToolHandler } from './useA2AServerTool' -export const BUILT_IN_TOOL_SERVER_NAME = 'polymind-builtin' +export const BUILT_IN_TOOL_SERVER_NAME = 'PolyMind-built-in' export const BUILT_IN_TOOL_SERVER_DESCRIPTION = 'PolyMind built-in tools' export const builtInTools: Record = { @@ -248,7 +248,10 @@ export class BuiltInToolsPresenter implements IBuiltInToolsPresenter { required?: string[] } - return { + const aliases = + (builtInTools[tool.name as keyof typeof builtInTools]?.alisa as string[] | undefined) || [] + + const definition: MCPToolDefinition = { type: 'function', function: { name: tool.name, @@ -265,6 +268,11 @@ export class BuiltInToolsPresenter implements IBuiltInToolsPresenter { description: BUILT_IN_TOOL_SERVER_DESCRIPTION } } + + // Attach optional alias list for downstream selection logic + ;(definition.function as any).alisa = aliases + + return definition } private parseToolArguments(argumentsText: string): Record { diff --git a/src/main/presenter/builtInToolsPresenter/listFilesTool.ts b/src/main/presenter/builtInToolsPresenter/listFilesTool.ts index 9a27dc3b09957f231793b1d95803b640b871168c..4903c952a5b6a1d77fd8cb79597a652bb045f171 100644 --- a/src/main/presenter/builtInToolsPresenter/listFilesTool.ts +++ b/src/main/presenter/builtInToolsPresenter/listFilesTool.ts @@ -4,6 +4,7 @@ import { BuiltInToolDefinition, BuiltInToolResponse, buildRawData } from './base export const listFilesTool: BuiltInToolDefinition = { name: 'list_files', + alisa: ['list_directory'], description: 'equests to list the names of files and subdirectories in a specified directory. Use this tool when you need to understand the directory structure or confirm the existence of files. By default, it only lists the current level, recursion can be enabled if necessary.', parameters: { diff --git a/src/main/presenter/builtInToolsPresenter/readFileTool.ts b/src/main/presenter/builtInToolsPresenter/readFileTool.ts index 0d1df1744c5c0b6c70dba007d05c6b6c5a03bb14..b39f4b3d294a7646434d40c27bd2eedeb29b94a7 100644 --- a/src/main/presenter/builtInToolsPresenter/readFileTool.ts +++ b/src/main/presenter/builtInToolsPresenter/readFileTool.ts @@ -4,6 +4,7 @@ import { BuiltInToolDefinition, BuiltInToolResponse, buildRawData } from './base export const readFileTool: BuiltInToolDefinition = { name: 'read_file', + alisa: [], description: 'Requests to read the content of a file at a specified path. Use this tool when you need to inspect an existing file whose content you are unaware of, such as analyzing code, viewing a text file, or extracting information from a configuration file.The output content will have line numbers prefixed to each line (e.g., "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. It can automatically extract raw text from PDF and DOCX files. It might not work for other types of binary files as it returns the raw content as a string.', parameters: { diff --git a/src/main/presenter/builtInToolsPresenter/writeFileTool.ts b/src/main/presenter/builtInToolsPresenter/writeFileTool.ts index 18e7269e5c09a55926ecd65128ca81ba7af15a38..f8e97b38175db32b2e05fbf97d2b105441ed1544 100644 --- a/src/main/presenter/builtInToolsPresenter/writeFileTool.ts +++ b/src/main/presenter/builtInToolsPresenter/writeFileTool.ts @@ -4,6 +4,7 @@ import { BuiltInToolDefinition, BuiltInToolResponse, buildRawData } from './base export const writeFileTool: BuiltInToolDefinition = { name: 'write_file', + alisa: [], description: 'Requests to write the complete content to a file at a specified path. If the file already exists, it will be overwritten with the provided content. If the file does not exist, a new file will be created. This tool automatically creates all necessary directories required to write the file.', parameters: { diff --git a/src/main/presenter/llmProviderPresenter/index.ts b/src/main/presenter/llmProviderPresenter/index.ts index 8851e477723b15b3fb462c469dea3631c5367c48..9a97d7d7bbf88a5e46796c5ac6d7b03b35127620 100644 --- a/src/main/presenter/llmProviderPresenter/index.ts +++ b/src/main/presenter/llmProviderPresenter/index.ts @@ -801,17 +801,7 @@ export class LLMProviderPresenter implements ILlmProviderPresenter { try { console.log(`[Agent Loop] Iteration ${toolCallCount + 1} for event: ${eventId}`) - let availableTools: MCPToolDefinition[] = [] - const useBuiltInToolsEnabled = this.configPresenter.getUseBuiltInToolsEnabled() - const [mcpTools, builtInTools] = await Promise.all([ - presenter.mcpPresenter.getAllToolDefinitions(enabledMcpTools), - presenter.builtInToolsPresenter.getBuiltInToolDefinitions( - useBuiltInToolsEnabled, - currentAgent - ) - ]) - availableTools = [...mcpTools, ...builtInTools] - + const availableTools = await this.getAvailableTools(enabledMcpTools, currentAgent) const canExecute = this.canExecuteImmediately(providerId) if (!canExecute) { const config = this.getProviderRateLimitConfig(providerId) @@ -1752,6 +1742,37 @@ export class LLMProviderPresenter implements ILlmProviderPresenter { return state?.config || this.DEFAULT_RATE_LIMIT_CONFIG } + private async getAvailableTools( + enabledMcpTools: string[] | undefined, + currentAgent?: Agent | null + ): Promise { + const useBuiltInToolsEnabled = this.configPresenter.getUseBuiltInToolsEnabled() + const [mcpTools, builtInTools] = await Promise.all([ + presenter.mcpPresenter.getAllToolDefinitions(enabledMcpTools), + presenter.builtInToolsPresenter.getBuiltInToolDefinitions( + useBuiltInToolsEnabled, + currentAgent + ) + ]) + + // Collect built-in tool names and aliases to prefer them when functionality overlaps + const builtInNamesAndAliases = new Set() + for (const tool of builtInTools) { + builtInNamesAndAliases.add(tool.function.name) + const aliases = (tool.function as any).alisa + if (Array.isArray(aliases)) { + for (const alias of aliases) { + builtInNamesAndAliases.add(alias) + } + } + } + // Drop MCP tools whose names match any built-in name or alias + const filteredMcpTools = mcpTools.filter( + (tool) => !builtInNamesAndAliases.has(tool.function.name) + ) + return [...builtInTools, ...filteredMcpTools] + } + private canExecuteImmediately(providerId: string): boolean { const state = this.providerRateLimitStates.get(providerId) if (!state || !state.config.enabled) {