Skip to content

Commit b4e52d0

Browse files
claude-code-bestglm-5-turbo
andcommitted
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>
1 parent d11b35e commit b4e52d0

3 files changed

Lines changed: 55 additions & 3 deletions

File tree

packages/builtin-tools/src/tools/ExecuteTool/ExecuteTool.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,14 @@ import {
1010
} from 'src/Tool.js'
1111
import { lazySchema } from 'src/utils/lazySchema.js'
1212
import { createUserMessage } from 'src/utils/messages.js'
13+
import {
14+
extractDiscoveredToolNames,
15+
isSearchExtraToolsEnabledOptimistic,
16+
isSearchExtraToolsToolAvailable,
17+
} from 'src/utils/searchExtraTools.js'
1318
import { DESCRIPTION, getPrompt } from './prompt.js'
1419
import { EXECUTE_TOOL_NAME } from './constants.js'
20+
import { isDeferredTool } from '../SearchExtraToolsTool/prompt.js'
1521

1622
export const inputSchema = lazySchema(() =>
1723
z.object({
@@ -74,6 +80,32 @@ export const ExecuteTool = buildTool({
7480
}
7581
}
7682

83+
// Guard: block execution of undiscovered deferred tools.
84+
// When tool search is active, deferred tools must be discovered via
85+
// SearchExtraTools first so the model has seen their schemas and knows
86+
// the correct parameters. Executing an undiscovered tool almost always
87+
// fails with parameter validation errors.
88+
if (
89+
isSearchExtraToolsEnabledOptimistic() &&
90+
isSearchExtraToolsToolAvailable(tools) &&
91+
isDeferredTool(targetTool)
92+
) {
93+
const discovered = extractDiscoveredToolNames(context.messages)
94+
if (!discovered.has(input.tool_name)) {
95+
return {
96+
data: {
97+
result: null,
98+
tool_name: input.tool_name,
99+
},
100+
newMessages: [
101+
createUserMessage({
102+
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}")`,
103+
}),
104+
],
105+
}
106+
}
107+
}
108+
77109
// Check if the target tool is currently enabled
78110
if (!targetTool.isEnabled()) {
79111
return {

packages/builtin-tools/src/tools/ExecuteTool/__tests__/ExecuteTool.runner.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ mock.module('src/utils/searchExtraTools.js', () => ({
3333
isSearchExtraToolsEnabledOptimistic: () => true,
3434
getAutoSearchExtraToolsCharThreshold: () => 100,
3535
getSearchExtraToolsMode: () => 'tst' as const,
36-
isSearchExtraToolsToolAvailable: async () => true,
36+
isSearchExtraToolsToolAvailable: () => true,
3737
isSearchExtraToolsEnabled: async () => true,
3838
isToolReferenceBlock: () => false,
39-
extractDiscoveredToolNames: () => new Set(),
39+
extractDiscoveredToolNames: () => new Set(['TestTool', 'SecretTool']),
4040
isDeferredToolsDeltaEnabled: () => false,
4141
getDeferredToolsDelta: () => null,
4242
}))
@@ -154,6 +154,26 @@ describe('ExecuteTool', () => {
154154
expect(result.newMessages).toBeDefined()
155155
})
156156

157+
test('returns error when deferred tool has not been discovered via SearchExtraTools', async () => {
158+
const mockTarget = makeMockTool('UndiscoveredTool', 'result')
159+
const ctx = makeContext([mockTarget])
160+
161+
const result = await ExecuteTool.call(
162+
{ tool_name: 'UndiscoveredTool', params: {} },
163+
ctx,
164+
async () => ({ behavior: 'allow' }),
165+
{ type: 'assistant', content: [], uuid: 'msg1' } as never,
166+
undefined,
167+
)
168+
169+
expect(result.data).toEqual({
170+
result: null,
171+
tool_name: 'UndiscoveredTool',
172+
})
173+
expect(result.newMessages).toBeDefined()
174+
expect(result.newMessages![0].content).toContain('has not been discovered')
175+
})
176+
157177
test('has correct name', () => {
158178
expect(ExecuteTool.name).toBe(EXECUTE_TOOL_NAME)
159179
})

src/services/api/claude.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1396,7 +1396,7 @@ async function* queryModel(
13961396
messagesForAPI = [
13971397
...messagesForAPI,
13981398
createUserMessage({
1399-
content: `<system-reminder>\n<available-deferred-tools>\n${deferredToolList}\n</available-deferred-tools>\nTo invoke any tool listed above, use ExecuteExtraTool with {"tool_name": "<name>", "params": {...}}. This is the ONLY way to call deferred tools — do not read source code or analyze implementation, just call ExecuteExtraTool directly.\n</system-reminder>`,
1399+
content: `<system-reminder>\n<available-deferred-tools>\n${deferredToolList}\n</available-deferred-tools>\nIMPORTANT: These tools are deferred-loading. You MUST first discover a tool via SearchExtraTools before invoking it with ExecuteExtraTool. Do NOT call ExecuteExtraTool directly — it will fail if the tool has not been discovered.\n\nSteps:\n1. SearchExtraTools("select:<tool_name>") — discover the tool and its schema\n2. ExecuteExtraTool({"tool_name": "<name>", "params": {...}}) — invoke it with correct parameters\n</system-reminder>`,
14001400
isMeta: true,
14011401
}),
14021402
]

0 commit comments

Comments
 (0)