mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-15 12:55:51 +00:00
fix: 拦截 ExecuteExtraTool 直接调用未搜索的延迟工具
模型在未通过 SearchExtraTools 发现工具的情况下直接调用 ExecuteExtraTool, 因不知道工具 schema 导致参数错误(如 libraryName: undefined)。 双重修复: 1. ExecuteTool.call() 添加服务端拦截:检查目标 deferred 工具是否已被发现 2. 更新 <available-deferred-tools> 系统提示:要求先搜索再执行 Co-Authored-By: glm-5-turbo <zai-org@claude-code-best.win>
This commit is contained in:
@@ -10,8 +10,14 @@ import {
|
||||
} from 'src/Tool.js'
|
||||
import { lazySchema } from 'src/utils/lazySchema.js'
|
||||
import { createUserMessage } from 'src/utils/messages.js'
|
||||
import {
|
||||
extractDiscoveredToolNames,
|
||||
isSearchExtraToolsEnabledOptimistic,
|
||||
isSearchExtraToolsToolAvailable,
|
||||
} from 'src/utils/searchExtraTools.js'
|
||||
import { DESCRIPTION, getPrompt } from './prompt.js'
|
||||
import { EXECUTE_TOOL_NAME } from './constants.js'
|
||||
import { isDeferredTool } from '../SearchExtraToolsTool/prompt.js'
|
||||
|
||||
export const inputSchema = lazySchema(() =>
|
||||
z.object({
|
||||
@@ -74,6 +80,32 @@ export const ExecuteTool = buildTool({
|
||||
}
|
||||
}
|
||||
|
||||
// Guard: block execution of undiscovered deferred tools.
|
||||
// When tool search is active, deferred tools must be discovered via
|
||||
// SearchExtraTools first so the model has seen their schemas and knows
|
||||
// the correct parameters. Executing an undiscovered tool almost always
|
||||
// fails with parameter validation errors.
|
||||
if (
|
||||
isSearchExtraToolsEnabledOptimistic() &&
|
||||
isSearchExtraToolsToolAvailable(tools) &&
|
||||
isDeferredTool(targetTool)
|
||||
) {
|
||||
const discovered = extractDiscoveredToolNames(context.messages)
|
||||
if (!discovered.has(input.tool_name)) {
|
||||
return {
|
||||
data: {
|
||||
result: null,
|
||||
tool_name: input.tool_name,
|
||||
},
|
||||
newMessages: [
|
||||
createUserMessage({
|
||||
content: `Tool "${input.tool_name}" has not been discovered yet. You must first use SearchExtraTools to discover this tool before executing it.\n\nUsage: SearchExtraTools("select:${input.tool_name}")`,
|
||||
}),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the target tool is currently enabled
|
||||
if (!targetTool.isEnabled()) {
|
||||
return {
|
||||
|
||||
@@ -33,10 +33,10 @@ mock.module('src/utils/searchExtraTools.js', () => ({
|
||||
isSearchExtraToolsEnabledOptimistic: () => true,
|
||||
getAutoSearchExtraToolsCharThreshold: () => 100,
|
||||
getSearchExtraToolsMode: () => 'tst' as const,
|
||||
isSearchExtraToolsToolAvailable: async () => true,
|
||||
isSearchExtraToolsToolAvailable: () => true,
|
||||
isSearchExtraToolsEnabled: async () => true,
|
||||
isToolReferenceBlock: () => false,
|
||||
extractDiscoveredToolNames: () => new Set(),
|
||||
extractDiscoveredToolNames: () => new Set(['TestTool', 'SecretTool']),
|
||||
isDeferredToolsDeltaEnabled: () => false,
|
||||
getDeferredToolsDelta: () => null,
|
||||
}))
|
||||
@@ -154,6 +154,26 @@ describe('ExecuteTool', () => {
|
||||
expect(result.newMessages).toBeDefined()
|
||||
})
|
||||
|
||||
test('returns error when deferred tool has not been discovered via SearchExtraTools', async () => {
|
||||
const mockTarget = makeMockTool('UndiscoveredTool', 'result')
|
||||
const ctx = makeContext([mockTarget])
|
||||
|
||||
const result = await ExecuteTool.call(
|
||||
{ tool_name: 'UndiscoveredTool', params: {} },
|
||||
ctx,
|
||||
async () => ({ behavior: 'allow' }),
|
||||
{ type: 'assistant', content: [], uuid: 'msg1' } as never,
|
||||
undefined,
|
||||
)
|
||||
|
||||
expect(result.data).toEqual({
|
||||
result: null,
|
||||
tool_name: 'UndiscoveredTool',
|
||||
})
|
||||
expect(result.newMessages).toBeDefined()
|
||||
expect(result.newMessages![0].content).toContain('has not been discovered')
|
||||
})
|
||||
|
||||
test('has correct name', () => {
|
||||
expect(ExecuteTool.name).toBe(EXECUTE_TOOL_NAME)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user