From c4dd45f8df45d4b614443264f45397e8b0d158f1 Mon Sep 17 00:00:00 2001 From: claude-code-best Date: Fri, 12 Jun 2026 17:01:01 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=98=B2=E6=AD=A2=20=20=E5=9C=A8=E6=AF=8F=E8=BD=AE=20API=20=E8=B0=83?= =?UTF-8?q?=E7=94=A8=E4=B8=AD=E9=87=8D=E5=A4=8D=E6=B3=A8=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 使用模块级 Set 缓存已注入的 deferred tool 列表,diff 后仅在有 新增工具时重新注入。根因:注入消息追加到 queryModel 的局部变量 messagesForAPI,不写入消息历史,所以每次调用都是首次。 --- src/services/api/claude.ts | 40 +++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/src/services/api/claude.ts b/src/services/api/claude.ts index 0b186a4f1..91db156e6 100644 --- a/src/services/api/claude.ts +++ b/src/services/api/claude.ts @@ -1036,6 +1036,16 @@ export function stripExcessMediaItems( }) as (UserMessage | AssistantMessage)[] } +/** + * Module-level cache of deferred-tool lines that have already been announced + * via . Because the injection is ephemeral (appended + * to a local `messagesForAPI` that is never persisted back into the caller's + * message history), we cannot scan history to detect prior injections — the + * injected message is gone after each API call. Instead we keep this Set so we + * only re-inject when new deferred tools appear (e.g. MCP server connects). + */ +const lastAnnouncedDeferredTools = new Set() + async function* queryModel( messages: Message[], systemPrompt: SystemPrompt, @@ -1385,21 +1395,33 @@ async function* queryModel( // via persisted deferred_tools_delta attachments instead of this // ephemeral prepend (which busts cache whenever the pool changes). if (useSearchExtraTools && !isDeferredToolsDeltaEnabled()) { + // Diff current deferred tools against what's already been announced in + // prior injections. Only re-inject when new + // tools appear (e.g. MCP server connects mid-session). const deferredToolList = tools .filter(t => deferredToolNames.has(t.name)) .map(formatDeferredToolLine) .sort() .join('\n') + if (deferredToolList) { - // Append to the end of the messages array (not prepend) so it - // never抢占 (CLAUDE.md) at the front. - messagesForAPI = [ - ...messagesForAPI, - createUserMessage({ - content: `\n\n${deferredToolList}\n\nIMPORTANT: The tools listed above are deferred-loading — they are NOT in your tool list. To use them, you MUST first discover a tool via SearchExtraTools, then invoke it with ExecuteExtraTool.\n\nSearchExtraTools and ExecuteExtraTool are core tools already in your tool list right now — call them directly, do NOT use Bash/Glob to find them.\n\nSteps:\n1. SearchExtraTools({"query": "select:"}) — discover the tool and its schema\n2. ExecuteExtraTool({"tool_name": "", "params": {...}}) — invoke it with correct parameters\n`, - isMeta: true, - }), - ] + const currentTools = new Set(deferredToolList.split('\n')) + const hasNewTools = [...currentTools].some( + t => !lastAnnouncedDeferredTools.has(t), + ) + + if (hasNewTools) { + lastAnnouncedDeferredTools.clear() + for (const t of currentTools) lastAnnouncedDeferredTools.add(t) + + messagesForAPI = [ + ...messagesForAPI, + createUserMessage({ + content: `\n\n${deferredToolList}\n\nIMPORTANT: The tools listed above are deferred-loading — they are NOT in your tool list. To use them, you MUST first discover a tool via SearchExtraTools, then invoke it with ExecuteExtraTool.\n\nSearchExtraTools and ExecuteExtraTool are core tools already in your tool list right now — call them directly, do NOT use Bash/Glob to find them.\n\nSteps:\n1. SearchExtraTools({"query": "select:"}) — discover the tool and its schema\n2. ExecuteExtraTool({"tool_name": "", "params": {...}}) — invoke it with correct parameters\n`, + isMeta: true, + }), + ] + } } }