From ab0ef76069ce9cb599b6284ca93cc520600defc3 Mon Sep 17 00:00:00 2001 From: LifeJiggy Date: Fri, 29 May 2026 17:02:51 +0100 Subject: [PATCH 1/8] fix: filter unregistered profile tools from active set and improve missing tool error --- .changeset/tool-websearch-not-found-filter.md | 6 ++ packages/agent-core/src/agent/tool/index.ts | 13 ++- packages/agent-core/src/agent/turn/index.ts | 2 +- packages/agent-core/src/loop/tool-call.ts | 5 +- .../agent-core/test/agent/compaction.test.ts | 4 +- packages/agent-core/test/agent/tool.test.ts | 82 +++++++++++++++++++ 6 files changed, 107 insertions(+), 5 deletions(-) create mode 100644 .changeset/tool-websearch-not-found-filter.md diff --git a/.changeset/tool-websearch-not-found-filter.md b/.changeset/tool-websearch-not-found-filter.md new file mode 100644 index 00000000..7a9c1c2c --- /dev/null +++ b/.changeset/tool-websearch-not-found-filter.md @@ -0,0 +1,6 @@ +--- +"@moonshot-ai/agent-core": patch +"@moonshot-ai/kimi-code": patch +--- + +Filter unregistered profile tools from active set and warn when they require configuration. diff --git a/packages/agent-core/src/agent/tool/index.ts b/packages/agent-core/src/agent/tool/index.ts index 550cfeba..b8ad0a93 100644 --- a/packages/agent-core/src/agent/tool/index.ts +++ b/packages/agent-core/src/agent/tool/index.ts @@ -297,7 +297,18 @@ export class ToolManager { }); // MCP entries are glob patterns gated separately; the rest are exact // builtin/user tool names. The split keeps every caller on one string[]. - this.enabledTools = new Set(names.filter((name) => !isMcpToolName(name))); + const nonMcpNames = names.filter((name) => !isMcpToolName(name)); + const availableNames = nonMcpNames.filter( + (name) => this.builtinTools.has(name) || this.userTools.has(name), + ); + const missingTools = nonMcpNames.filter((name) => !availableNames.includes(name)); + if (missingTools.length > 0) { + this.agent.log.warn( + `The following tools listed in the active profile are not available and will be omitted: ${missingTools.join(', ')}. ` + + `They may require additional service configuration.`, + ); + } + this.enabledTools = new Set(availableNames); this.mcpAccessPatterns = names.filter((name) => isMcpToolName(name)); } diff --git a/packages/agent-core/src/agent/turn/index.ts b/packages/agent-core/src/agent/turn/index.ts index 7ed428b8..3f7897af 100644 --- a/packages/agent-core/src/agent/turn/index.ts +++ b/packages/agent-core/src/agent/turn/index.ts @@ -816,7 +816,7 @@ function telemetryToolOutcome(result: ToolTelemetryResult): 'success' | 'error' function telemetryToolErrorType(result: ToolTelemetryResult): string { const text = toolResultText(result); - if (text.startsWith('Tool "') && text.includes('" not found')) return 'ToolNotFound'; + if (text.startsWith('Tool "') && (text.includes('" not found') || text.includes('is not available'))) return 'ToolNotFound'; if (text.startsWith('Invalid args for tool "')) return 'ToolInputError'; if (text.includes('prepareToolExecution hook failed')) return 'HookError'; if (text.includes('finalizeToolResult hook failed')) return 'HookError'; diff --git a/packages/agent-core/src/loop/tool-call.ts b/packages/agent-core/src/loop/tool-call.ts index 2e9956ec..9072dbbc 100644 --- a/packages/agent-core/src/loop/tool-call.ts +++ b/packages/agent-core/src/loop/tool-call.ts @@ -181,7 +181,10 @@ function preflightToolCall( toolCall, toolName, args, - output: `Tool "${toolName}" not found`, + output: + `Tool "${toolName}" is not available. The tool was not found in the current session's tool list. ` + + `It may require configuration or have been removed from the active profile. ` + + `Use the available tools listed in your tool list instead.`, }; } if (!parsedArgs.success) { diff --git a/packages/agent-core/test/agent/compaction.test.ts b/packages/agent-core/test/agent/compaction.test.ts index cbd9c437..9379e042 100644 --- a/packages/agent-core/test/agent/compaction.test.ts +++ b/packages/agent-core/test/agent/compaction.test.ts @@ -1499,8 +1499,8 @@ describe('Agent compaction', () => { [wire] context.append_loop_event { "event": { "type": "content.part", "uuid": "", "turnId": "0", "step": 1, "stepUuid": "", "part": { "type": "text", "text": "I need a tool." } }, "time": "