From f17353e05bc998544824076d93b2a8875019a87a Mon Sep 17 00:00:00 2001 From: chinadoiphin Date: Thu, 19 Mar 2026 22:12:14 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20Cursor=20=E8=BA=AB?= =?UTF-8?q?=E4=BB=BD=E6=B3=84=E6=BC=8F=20=E2=80=94=20=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E5=A3=B0=E7=A7=B0=E5=8F=AA=E6=9C=89=20read=5Ffile/read=5Fdir?= =?UTF-8?q?=20=E5=B7=A5=E5=85=B7=20(#68)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题:模型回复"在当前环境中我只有读取 Cursor 文档的工具 (read_file / read_dir),无法访问你的本地文件系统", 暴露了 Cursor 文档助手身份。 修复: 1. handleDirectTextStream 的 warmupChars 从 96 → 300, 与工具模式对齐,确保拒绝检测窗口覆盖完整的中文拒绝句式 2. constants.ts 新增 9 条中文拒绝检测规则,覆盖 "只有读取 Cursor/文档的工具"、"无法访问本地文件"、 "无法执行命令"、"需要在 Claude Code CLI 环境" 等新措辞 3. sanitizeResponse 新增 5 条清洗规则作为最后防线 --- src/constants.ts | 10 ++++++++++ src/handler.ts | 8 ++++++++ 2 files changed, 18 insertions(+) diff --git a/src/constants.ts b/src/constants.ts index 9273942..8963ef0 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -119,6 +119,16 @@ export const REFUSAL_PATTERNS: RegExp[] = [ /只能用.*?read_file/i, /无法调用.*?工具/, /(?:仅限于|仅用于).*?(?:查阅|浏览).*?(?:文档|docs)/, + // ── 中文: 工具可用性声明 (2026-03 新增) ── + /只有.*?读取.*?Cursor.*?工具/, + /只有.*?读取.*?文档的工具/, + /无法访问.*?本地文件/, + /无法.*?执行命令/, + /需要在.*?Claude\s*Code/i, + /需要.*?CLI.*?环境/i, + /当前环境.*?只有.*?工具/, + /只有.*?read_file.*?read_dir/i, + /只有.*?read_dir.*?read_file/i, // ── 中文: Cursor 中文界面拒绝措辞 (2026-03 批次) ── /只能回答.*(?:Cursor|编辑器).*(?:相关|有关)/, diff --git a/src/handler.ts b/src/handler.ts index d1ad03d..7acd92e 100644 --- a/src/handler.ts +++ b/src/handler.ts @@ -250,6 +250,12 @@ export function sanitizeResponse(text: string): string { result = result.replace(/\*\*`?read_dir`?\*\*[^\n]*\n(?:[^\n]*\n){0,3}/gi, ''); result = result.replace(/\d+\.\s*\*\*`?read_(?:file|dir)`?\*\*[^\n]*/gi, ''); result = result.replace(/[⚠注意].*?(?:不是|并非|无法).*?(?:本地文件|代码库|执行代码)[^。\n]*[。]?\s*/g, ''); + // 中文: "只有读取 Cursor 文档的工具" / "无法访问本地文件系统" 等新措辞清洗 + result = result.replace(/[^。\n]*只有.*?读取.*?(?:Cursor|文档).*?工具[^。\n]*[。]?\s*/g, ''); + result = result.replace(/[^。\n]*无法访问.*?本地文件[^。\n]*[。]?\s*/g, ''); + result = result.replace(/[^。\n]*无法.*?执行命令[^。\n]*[。]?\s*/g, ''); + result = result.replace(/[^。\n]*需要在.*?Claude\s*Code[^。\n]*[。]?\s*/gi, ''); + result = result.replace(/[^。\n]*当前环境.*?只有.*?工具[^。\n]*[。]?\s*/g, ''); // === Hallucination about accidentally calling Cursor internal tools === // "I accidentally called the Cursor documentation read_dir tool." -> remove entire sentence result = result.replace(/[^\n.!?]*(?:accidentally|mistakenly|keep|sorry|apologies|apologize)[^\n.!?]*(?:called|calling|used|using)[^\n.!?]*Cursor[^\n.!?]*tool[^\n.!?]*[.!?]\s*/gi, ''); @@ -786,6 +792,7 @@ async function handleDirectTextStream( let finalVisibleText = ''; let finalThinkingContent = ''; let streamer = createIncrementalTextStreamer({ + warmupChars: 300, // ★ 与工具模式对齐:前 300 chars 不释放,确保拒绝检测完成后再流 transform: sanitizeResponse, isBlockedPrefix: (text) => isRefusal(text.substring(0, 300)), }); @@ -802,6 +809,7 @@ async function handleDirectTextStream( let leadingResolved = false; let thinkingContent = ''; const attemptStreamer = createIncrementalTextStreamer({ + warmupChars: 300, // ★ 与工具模式对齐 transform: sanitizeResponse, isBlockedPrefix: (text) => isRefusal(text.substring(0, 300)), });