# Changelog
## v2.7.7 (2026-03-23)
### 🩺 降级日志与可观测性增强
- **新增 `degraded` 状态**:将“工具看起来可用但未真正调用”、“`max_tokens` 截断且未自动续写”、“模型自述上一步输出被截断、正在补写”等情况从 `success` 中拆分出来
- **补充 `statusReason` / `issueTags`**:日志落盘、统计接口、Vue 日志页和旧版 `/logs` 页面均可显示降级原因并单独筛选
- **修复 Anthropic 工具统计失真**:`/v1/messages` 路径会正确写入 `toolCallsDetected`,不再出现 `stop_reason=tool_use` 但统计为 0 的情况
### ✂️ 长 Write/Edit 截断恢复修复
- **新增语义级截断检测**:即使 `json action` 代码块本身已经闭合,只要大负载 `Write/Edit` 参数尾部明显半截,也会继续判定为需要续写
- **OpenAI 流式长工具调用恢复**:OpenAI 兼容流式路径现在至少会尝试 1 次内部续写,修复长 `Write` 被截断后无法恢复完整多帧 `tool_calls` 的回归
- **补充回归测试**:新增并整理 `unit-handler-truncation.mjs` 与 `unit-openai-stream-truncation.mjs`,覆盖长 `Write/Edit` 截断、自愈补写和 OpenAI 流式恢复场景
---
## v2.7.5 (2026-03-19)
### 🏗️ 常量集中管理
- **新增 `constants.ts`**:将 `REFUSAL_PATTERNS`(50+ 条拒绝检测规则)、`IDENTITY_PROBE_PATTERNS`、`TOOL_CAPABILITY_PATTERNS`、`CLAUDE_IDENTITY_RESPONSE`、`CLAUDE_TOOLS_RESPONSE` 及自定义拒绝规则逻辑从 `handler.ts` 提取到独立文件
- **提升可维护性**:贡献者修改内置规则时只需查看 `constants.ts`,无需翻阅 2000 行的 handler 业务逻辑
- **`isRefusal()` 函数统一导出**:内置规则 + 自定义规则合并检测,所有调用点自动生效
### 🔧 自定义拒绝检测规则
- **`config.yaml` 新增 `refusal_patterns` 字段**:用户可添加自定义正则匹配规则,追加到内置列表之后(不替换),匹配到即触发重试逻辑
- **无效正则容错**:无效的正则表达式自动退化为字面量匹配,不会导致服务报错
- **缓存编译**:自定义规则只在配置变更时重新编译 RegExp,运行时零开销
- **热重载支持**:修改后下一次请求即生效
### 🔀 响应内容清洗开关
- **`config.yaml` 新增 `sanitize_response` 字段**:控制 `sanitizeResponse()` 函数(将 Cursor 身份引用替换为 Claude),**默认关闭**
- **环境变量支持**:`SANITIZE_RESPONSE=true` 可覆盖配置文件
- **零开销设计**:关闭时函数入口直接返回原文本,无正则计算
- **热重载支持**:修改配置后立即生效
---
## v2.7.4 (2026-03-18)
### 🛡️ 截断安全 — 防止损坏的工具调用
- **截断时跳过工具解析**:当响应被截断(`stop_reason=max_tokens`)时,不再尝试解析不完整的 `json action` 块,避免生成损坏的工具调用(如写入半截文件)
- **纯文本回退**:截断响应中的不完整工具块被自动剥离,剩余文本作为纯文本返回,由客户端(Claude Code)原生续写
- **默认禁用代理续写**:`maxAutoContinue` 默认值改为 `0`,让 Claude Code 原生处理续写(体验更好、进度可见),配置同步更新至 `config.yaml`、`config.yaml.example`、`docker-compose.yml`
### 🧹 提示词注入防御增强
- **身份声明清除**:自动剥离系统提示词中的 Claude Code / Anthropic 身份声明(`You are Claude Code`、`I'm Claude, made by Anthropic` 等),防止模型将其判定为 prompt injection 并拒绝服务
- **流式热身窗口扩大**:混合流式模式的 `warmupChars` 从 96 增至 300 字符,确保拒绝检测完成前不释放任何文本给客户端
### 📊 日志查看器增强
- **提示词对比视图**:「💬 提示词」tab 重命名为「💬 提示词对比」,分区展示原始请求 vs 转换后的 Cursor 消息
- **转换摘要面板**:顶部新增 6 格摘要(原始工具数 → Cursor 工具数 0、工具指令占用字符数、消息数变化、总上下文大小)
- **工具去向提示**:当有工具时显示黄色提示「Cursor API 不支持原生 tools 参数,N 个工具已转换为文本指令嵌入 user #1」
- **标题提取优化**:通用 XML 标签清除(覆盖所有注入标签)+ 清除 `Respond with the appropriate action` 引导语
---
## v2.7.2 (2026-03-17)
### 🖥️ 日志查看器全面升级
- **前端重构为独立静态文件**:`logs.html` / `logs.css` / `logs.js` 分离到 `public/` 目录,告别单文件嵌入,更易维护
- **🌙 日/夜主题切换**:一键切换明暗主题(☀️/🌙),自动检测系统偏好,选择持久化到 `localStorage`
- **暗色主题完整适配**:深蓝渐变背景,所有 UI 元素(标签、状态灯、代码块、JSON 高亮)均有独立暗色配色
- **标题提取修复**:过滤 `...` 注入内容和 Claude Code `"First, think step by step..."` 引导语,确保标题显示用户真实提问
- **登录页同步更新**:独立 `login.html`,视觉风格与日志页一致
### 🧹 工程化改进
- **移除 `WELL_KNOWN_TOOLS` 白名单**:所有工具统一保留描述(截取前 50 字符),简化逻辑
- **`config.yaml` 停止追踪**:含敏感 token 的配置文件加入 `.gitignore`,不再上传
- **新增 `config.yaml.example`**:配置模板,安全默认值,用户只需 `cp config.yaml.example config.yaml`
- **`.gitignore` 清理**:去除重复条目,排除开发截图文件
- **Thinking 默认关闭**:`thinking.enabled` 默认值改为 `false`
- **Express v5 兼容**:修复 `path-to-regexp` 通配符路由报错,改用 `express.static` 中间件
- **CSS 兼容性**:补充标准 `background-clip` 属性
### 📝 README 大幅更新
- 新增日志查看器功能介绍(特性列表 + 鉴权说明)
- 新增配置项速查表格
- 新增环境变量参考表
- 项目结构补充 `public/` 目录说明
- 配置说明改为引导用户从 `config.yaml.example` 复制
---
## v2.7.1 (2026-03-16)
### 🗜️ 智能历史压缩算法
- **修复 JSON Action 块截断**:之前朴素的 `substring` 截断会切断 `` ```json action `` 代码块,产生未闭合标记和不完整 JSON,严重误导模型。现在对包含工具调用的 assistant 消息,提取工具名生成摘要(如 `[Executed: Write, Read]`),不再做子串截断
- **工具结果头尾保留**:工具结果截断从"只保留头部"改为 **60% 头 + 40% 尾**,确保错误信息、stack trace 等末尾关键内容不丢失
- **修复非工具模式偏移量**:few-shot 消息跳过偏移量从硬编码 `+2` 改为动态计算 `hasTools ? 2 : 0`,修复非工具模式下前2条消息无法参与压缩的问题
- **自然边界截断**:普通文本在换行符处截断,避免切断单词或代码
### ⚙️ 可配置压缩系统
- 新增 `compression` 配置段(config.yaml),支持:
- `enabled`:压缩开关(`true`/`false`),关闭后所有消息原样保留
- `level`:压缩级别 1-3(轻度/中等/激进),每级预设不同的保留消息数和字符限制
- `keep_recent`:高级选项,覆盖级别预设的保留消息数
- `early_msg_max_chars`:高级选项,覆盖级别预设的早期消息字符上限
- 支持环境变量 `COMPRESSION_ENABLED` / `COMPRESSION_LEVEL`,方便 Docker 部署
### 🔐 日志查看器鉴权
- 配置了 `auth_tokens` 后,访问 `/logs` 及所有 `/api/logs*` 端点需要验证身份
- 精美的登录页面,输入 token 后通过 `/api/stats` 验证有效性
- Token 存入 `localStorage`,刷新页面无需重新输入
- 支持 query 参数 `?token=xxx`、`Authorization` header、`x-api-key` 三种传入方式
- 页面右上角显示退出按钮,清除缓存并跳回登录页
- 未配置 `auth_tokens` 时保持完全开放(向后兼容)
### 🧠 Thinking 拒绝误判修复
- **修复 thinking 触发拒绝检测**:模型的 `` 内容中包含反思性语言(如 "haven't given a specific task"),被拒绝检测正则误判为拒绝响应
- 拒绝检测现在先剥离 `` 标签内容,仅对实际输出文本进行检测
- 流式和非流式路径均已修复
### 🧠 OpenAI 格式 Thinking 默认启用
- OpenAI Chat Completions 协议不再依赖模型名包含 `thinking` 或传入 `reasoning_effort` 才启用
- 所有 OpenAI 格式请求默认启用 thinking,确保 Claude Code 等客户端始终获得推理内容
---
## v2.7.0 (2026-03-16)
### 🔐 API Token 鉴权
- **公网部署安全**:新增 `auth_tokens` 配置项,支持 Bearer token 鉴权
- 支持多 token(数组格式)、环境变量 `AUTH_TOKEN`、`x-api-key` 头
- 未配置时全部放行(向后兼容),GET 请求和 /health 端点无需鉴权
- 启动 banner 显示鉴权状态
### 🧠 Thinking 支持(客户端驱动)
- **Anthropic 协议**:请求体传 `thinking.type = "enabled"` 即启用
- **OpenAI 协议**:模型名含 `thinking` 或传 `reasoning_effort` 参数即启用
- 系统提示词注入 `` 引导,模型输出自动提取
- Anthropic 返回 `thinking` content block,OpenAI 返回 `reasoning_content` 字段
- 提取在拒绝检测之前执行,防止 thinking 内容触发误判
- 未启用时仍会剥离 thinking 标签(防误判),但内容不返回
### 🔧 已知工具跳过描述(已在 v2.7.2 移除)
- `WELL_KNOWN_TOOLS` 集合中的 17 个常用工具(Read、Write、Bash 等)不再生成描述文本
- 减少约 30% 工具指令输入,节省上下文空间
### 📊 动态工具结果预算
- `getToolResultBudget()` 替代固定 15K 限制
- 根据当前上下文大小动态调整:小上下文 20K → 大上下文 8K
- `setCurrentContextChars()` 跟踪实际上下文字符数
### 🛡️ isTruncated 重写
- 重新实现截断检测逻辑,正确处理工具调用 JSON 中的反引号
- 优先检查 `` ```json action`` 代码块,避免 JSON 字符串值内的反引号导致误判
- 消除因误判导致的无限重试
### 📦 response_format 支持
- `OpenAIChatRequest` 新增 `response_format` 字段(`json_object` / `json_schema`)
- JSON 格式请求自动追加格式指令到最后一条用户消息
- `stripMarkdownJsonWrapper()` 自动剥离响应中的 markdown 代码块包装
- 流式和非流式路径均支持
### 🧹 计费头清除
- 自动清除系统提示词中的 `x-anthropic-billing-header`
- 防止模型将其判定为恶意伪造并触发注入警告
### 🌐 Vision 独立代理
- 新增 `vision.proxy` 配置项,图片分析 API 单独走代理
- Cursor API 保持直连(国内可用),不因代理影响响应速度
- 未配置时回退到全局 `proxy`
### 🛡️ 新增拒绝模式
- 补充 4 个 Cursor 新拒绝措辞:`isn't something I can help with`、`not something I can help with`、`scoped to answering questions about Cursor`、`falls outside`
---
## v2.5.6 (2026-03-12)
### 🗜️ 渐进式历史压缩
- **新策略**:保留最近 6 条消息完整不动,仅压缩早期消息中超过 2000 字符的文本部分
- 不删除任何消息(保留完整对话结构),只截短单条消息的超长文本
- 兼顾上下文完整性与输出空间,替代之前被移除的全删式智能压缩
- 工具描述截断从 200 → **80 字符**(Schema 已包含参数信息,短描述节省输入体积)
- 工具结果截断从 30000 → **15000 字符**(为输出留更多空间)
### 🔧 续写智能去重
- **问题**:模型续写时经常重复截断点附近的内容,拼接后出现重复段落
- **新增 `deduplicateContinuation()`**:在原内容尾部和续写头部之间搜索最长重叠,自动移除重复部分
- 支持字符级精确匹配和行级模糊匹配两种去重策略
- 去重后无新内容时自动停止续写(防止无限循环)
- 流式和非流式路径均已集成
### ⚡ 非流式截断续写(与流式路径对齐)
- **问题**:非流式模式下 Write 大文件等长输出被截断后,Claude Code 直接收到不完整的工具调用 JSON,导致 `tool_use` 退化为纯文本
- **修复**:非流式路径新增内部截断续写(最多 6 次),与流式路径逻辑完全对齐
- 新增 `tool_choice=any` 强制重试(非流式):模型未输出工具调用时自动追加强制消息重试
- 新增极短响应重试(非流式):响应 < 10 字符时自动重试
### 📊 Token 估算优化
- 提取 `estimateInputTokens()` 为独立函数,Anthropic 和 OpenAI handler 共用
- 估算比例从 1/4 调整为 **1/3**(更适合中英文混合和代码场景)+ 10% 安全边距
- 新增工具定义的 token 估算(每个压缩工具签名 ~200 chars + 1000 chars 指令开销)
- 替代之前 `input_tokens: 100` 的硬编码占位符
### 🛡️ JSON 解析器加固
- **反斜杠计数精确化**:`tolerantParse` 和 `parseToolCalls` 中的字符串状态跟踪从 `escaped` 布尔标志改为**反向计数连续反斜杠**,正确处理 `\\\"` (未转义) vs `\\\\\\\"` (已转义) 等边界情况
- **新增第五层逆向贪婪提取**:当所有 JSON 修复手段失败时,对 Write/Edit 等工具的 `content`/`command`/`text` 等大值字段进行逆向贪婪提取,从 JSON 末尾向前搜索值的结束引号
- 小值字段(`file_path`、`path` 等)仍用精确正则提取
---
## v2.5.5 (2026-03-12)
### 🐛 修复长响应误判为拒绝
- **问题**:工具模式下,模型输出长文本(如 8654 字符的深度分析报告),正文中碰巧包含 `无法提供...信息`、`工具调用场景`、`即报错` 等拒绝检测关键词,导致整个响应被替换为无意义的引导文本 `"I understand the request..."`,进而 Claude Code 陷入死循环
- **修复策略**:
- 截断响应(`stop_reason=max_tokens`)完全跳过拒绝检测 — 8654 字符的响应不可能是拒绝
- 长响应(≥ 500 字符)仅检查**前 300 字符**是否包含拒绝模式 — 拒绝一定在开头
- 短响应(< 500 字符)保持全文检测 — 真正的拒绝回复通常很短
- 流式和非流式处理均已修复
### 🔇 减少 tolerantParse 日志噪音
- 模型输出中的普通 JSON 代码块(如含正则 `[\s\S]*?` 的代码示例)不再打印 `error` 级别日志
- 仅当内容包含 `"tool"` / `"name"` 键(疑似工具调用)时才报 error,其余降为 `warn` 级别
---
## v2.5.4 (2026-03-11)
### 🌐 内网代理支持 (Issue #17)
- **修复 `fetch failed`**:Node.js 原生 `fetch()` 不读取 `HTTP_PROXY` / `HTTPS_PROXY` 环境变量,内网用户设置这些变量后请求仍然直连失败
- **新增 `proxy-agent.ts`**:使用 `undici.ProxyAgent` 作为 fetch dispatcher,所有外发请求(Cursor API、Vision API)均可通过 HTTP 代理转发
- **配置方式**:在 `config.yaml` 中设置 `proxy` 字段,或通过 `PROXY` 环境变量指定(支持 `http://用户名:密码@代理:端口` 格式)
- **单元测试**:新增 16 个测试用例覆盖代理模块的核心逻辑
---
## v2.5.3 (2026-03-11)
### 🗜️ Schema 压缩 — 根治截断问题
- **根本原因定位**:90 个工具的完整 JSON Schema 占用 ~135,000 chars,导致 Cursor API 输出预算仅 ~3,000 chars,Write/Edit 工具的 content 参数被严重截断
- **compactSchema() 压缩**:将完整 JSON Schema 转为紧凑类型签名(如 `{file_path!: string, encoding?: utf-8|base64}`),输入体积降至 ~15,000 chars
- **工具描述截断**:每个工具描述最多 200 chars,避免个别工具(如 Agent)的超长描述浪费 token
- **效果**:输出预算从 ~3k 提升到 ~8k+ chars,Write 工具可一次写入完整文件
### 🔧 JSON-String-Aware 解析器
- **修复致命 Bug**:旧的 lazy regex `/```json[\s\S]*?```/g` 会在 JSON 字符串值内部的 ``` 处提前闭合,导致 Write/Edit 工具的 content 参数(如含 markdown 代码块的文档)被截断为仅前几行
- **新实现**:手动扫描器跟踪 JSON 字符串状态(`"` 配对 + `\` 转义),只在字符串外部匹配闭合 ```
- **截断恢复**:无闭合 ``` 的代码块也能通过 tolerantParse 恢复工具调用
### ⚠️ 续写机制重写
- **修复空响应问题**:旧实现只追加 assistant 消息,Cursor API 看到最后是 assistant 的消息后返回空响应
- **新实现**:每次续写添加 user 引导消息 + 最后 300 chars 上下文锚点
- **防膨胀**:每次基于原始消息快照重建,而非累积消息
- **MAX_AUTO_CONTINUE** 从 4 提升至 6
---
## v2.5.2 (2026-03-11)
### 🗜️ 移除上下文智能压缩 (Reverted)
移除上一版本引入的“智能压缩替裁剪”功能。
- **原因**:Claude Code等Agent非常依赖完整的工具调用历史(尤其是 `Read` 和 `Bash` 的具体输出)来决定下一步行动。将 `Action output` 压缩为 `[30000 chars...]` 以及将历史命令压缩为 `[System Note...]` 会导致大模型“失忆”,进而在多轮对话中陷入死循环、产生幻觉,甚至复读 `[Called Bash...]` 等错误格式。
- **替代方案**:通过新增的 `isTruncated` 自动检测并返回 `stop_reason: "max_tokens"`,已经能有效解决需要频繁点“继续”按钮的问题,因此粗暴的历史压缩不再被需要。
### ⚠️ 截断无缝续写 (Internal Auto-Continue)
- **Proxy-Side 无缝拼接**:彻底解决大文件编辑(如 `Write` 工具写了几万字)时被 API 截断,导致 JSON 解析失败、变为普通文本从而丢失工具调用的致命问题!
- **自动检测与请求**:当模型输出触发截断(如代码块/XML未闭合),Proxy 将在 **底层直接自动重试续写**,无需任何额外交互。
- **防止工具调用退化为文本**:由于 Anthropic API 会在不同消息间打断工具调用块,造成 Claude Code 将 `{"tool": "Write", ...}` 降级为屏幕上的纯文本并崩溃停顿(Crunched 几分钟)。现在,Proxy 会内部拼接 2-4 次请求,始终将一个完整未截断的 JSON 动作一次性抛给 Claude Code,极大提高了多轮复杂任务的成功率!
### 🔧 工具参数容错 (tool-fixer)
- **移除隐式重命名 `file_path` 为 `path` 行动**:修复 Claude Code 2.1.71 中 `Read` 工具因为必需参数 `file_path` 被强制丢弃而陷入请求验证失败死循环的问题。
- **新增第四层正则兜底**:当模型生成的 JSON 工具调用包含未转义双引号(如代码内容参数)导致标准解析和控制字符修复均失败时,使用正则提取 `tool` 名称和 `parameters` 字段
- 解决 `SyntaxError: Expected ',' or '}'` at position 5384 等长参数解析崩溃问题
### 🛡️ 拒绝 Fallback 优化
- 工具模式下拒绝时返回极短文本 `"Let me proceed with the task."`,避免 Claude Code 误判为任务完成
---
## v2.5.0 (2026-03-10)
- OpenAI Responses API (`/v1/responses`) 支持 Cursor IDE Agent 模式
- 跨协议防御对齐(Anthropic + OpenAI handler 共享拒绝检测和重试逻辑)
- 统一图片预处理管道(OCR/Vision API)