docs/HISTORY-LEDGER-PRS.md
Rama master · réplica de lectura del repositorio original.
# WindsurfAPI 记账本 v2 —— 69 个 PR 逐条精细账
数据源:`/tmp/ledger-prs.txt`(69 行原始数据)、`/tmp/ledger-out-A/B/C/D.md`(v1 深度分析)、`/tmp/ledger-gitrefs.txt`(git log --grep=#n 引用记录)、`gh pr view`(补 #251/#253)。评级「台账」为 `/tmp/ledger-grades.txt` 已定权重;「自判」为台账外判断;「未评级」指废案或维护方自审。排序按 PR 编号。
---
## 全量表格(69 条)
| PR | 作者 | 创建→合并 | 干了什么(含关键技术点) | 修了什么 bug | 评级 | 合并后后续修复(git log --grep=#n) |
|---|---|---|---|---|---|---|
| 1 | dd373156 | 04-17→04-20 | 把 `MODEL_TIER_ACCESS.pro` 对 `ALL_MODEL_KEYS` 的模块加载期快照引用改成 getter,每次访问重新枚举 live `MODELS` 字典 | `mergeCloudModels()` 启动后动态加入的五个 `claude-opus-4-7-*` 模型永远不在冻结数组里,pro 账号请求全部 403 `model_not_entitled` | B+(台账) | 无(gitrefs `#1` 段全为 wire field 前缀噪音;一行 getter 改完即止) |
| 13 | colin1112a | CLOSED | 系统性安全/正确性审查,一次提交 15 条 CRITICAL/HIGH/MEDIUM 修复:XSS 转义、shell 注入防护、10MB body 上限、auth 路由顺序、SSE 日志改 fetch+header 传密码、gRPC 双回调保护、LS pool 并发竞态、HTTP/2 16MB frame 上限、varint BigInt 等 | 多类真实缺陷(未授权访问 /apiKey 泄露 / OOM DoS / 并发双回调),但改动面过大(12 文件)未合并 | B+(台账) | 无直接记录(#13 段命中 wire field 噪音);多项被后续独立重做——#80 的「SSE 迁 fetch」与「getAccountList 不返完整 apiKey」正对应其中两条 |
| 20 | motto1 | 04-21→04-22 | 修复 Dashboard 邮箱密码登录——逆向补出 Auth1 四步登录链路(`/_devin-auth/connections` → `/_devin-auth/password/login` → WindsurfPostAuth → GetOneTimeAuthToken),旧 Firebase 保留为兜底;顺带加邮箱/密码批量导入 + 剪贴板读取 | 官网可登录但 Dashboard 登录失败(旧 Firebase 路径已不是主链路) | A(台账) | 无缺陷修复;`f18b2ad` OTP 验证码登录与 #20 登录链路同域,属功能演进 |
| 26 | youfak | 04-22→04-22 | 新增 Dockerfile / docker-compose.yml / .dockerignore / .gitattributes,加 DATA_DIR 配置把 accounts.json / stats.json / logs/ 等持久化文件收敛到可挂载数据目录 | Windows 宿主构建 CRLF 导致容器内 `set: pipefail` 报错;Dashboard 重启 LS 失败会把整个 Node 进程打崩 | A+(台账) | 无(9f1de4b 合并即止) |
| 36 | baily-zhang | 04-22→04-23 | Cascade 会话复用扛住 Claude Code 的 system prompt 抖动——fingerprint 只 hash user + tool_result 稳定轮,system prompt 改 opt-in;历史重放 budget 可配置(1m 模型默认更大);加 LS_DATA_DIR 覆盖 | #24「胡言乱语」爆发后的第一块拼图——老 fingerprint 把动态 system prompt hash 进去导致永不命中 | S(台账) | 无直接修复;`e078f35`(#61 的 commit,body 关联 #36)——#61 在 #36 基础上深化 fingerprint 策略,属体系延续 |
| 43 | smeinecke | 04-23→04-24 | 14 个 commit 把 dashboard 全部硬编码中文换成 `I18n.t()` 调用,补齐 en.json / zh-CN.json 缺键,加 check-i18n.js 自动扫描 JS 里的 key 防缺键回归 | Dashboard 中英双语切换是半成品(大量 UI 字符串无英文) | A(台账) | 无缺陷修复;i18n 基础设施被 #88/#89/#92 持续复用扩展 |
| 44 | aict666 | 04-24→04-24 | `updateCapability` 成功 chat 后不再无条件 `inferTier()` 覆写 account.tier——GetUserStatus 已给出权威 tier 时跳过回退 | Pro/Trial 账号只要调一次 gemini-2.5-flash / gpt-4o-mini 就被悄悄降级成 free,Dashboard 显示 FREE、后续请求按 free 限 | S+(台账) | 无(c8a86c5 合并即止) |
| 45 | baily-zhang | 04-24→04-24 | 把 stepOffset / generatorOffset 随 conversation pool entry 持久化,复用 cascade 时从上次 offset 继续拉,不再每次从 step 0 重放 | 复用 turn 时旧 assistant text 被重放(alpha → alphabeta → alphabetabeta),usage 也累加成整条 cascade 总量 | S(台账) | 无直接缺陷修复;#36/#45/#61 构成 cascade 复用机制修复链 |
| 50 | aict666 | CLOSED-采纳 | 禁用 tool-emulated 请求(含 Claude Code tool_use/tool_result 轮)的 Cascade 复用,普通非工具聊天轮保持启用,加复用资格测试 | 工具模拟轮复用时的上下文污染/重放风险(与 #45 同期问题的前置防御) | B+(自判;被 `2ae9d15` 正式 Merge 采纳) | `2ae9d15` 采纳;后被 #61 演进成「窄范围启用 + 严格账号绑定」的更精细方案,说明 #50 是临时闸门而非终态 |
| 51 | aict666 | 04-24→04-24 | 重写 TOOL_PROTOCOL_HEADER——删掉 IGNORE any earlier / For THIS request only / 栅栏标题等越狱措辞,改成中性的一句话,保留 `<tool_call>` 协议合同与 parser | Opus 4.7 走 /v1/messages 时完全不 emit tool_call,直接回「prompt-injection attempt」——三段越狱措辞被 Opus 自己的 injection detector 反杀 | S+(台账) | 无;#53/#54 同一战线继续(同族后续) |
| 53 | aict666 | 04-24→04-24 | redact marker 从 `(internal path redacted)` 换成 `redacted internal path`(无 shell 元字符 / 无路径形状),加两条 banned regex 锁死 marker 形状 | 模型把 marker 塞进 `cd (internal path redacted) && git branch -a`,zsh 把 `(…)` 当 glob-qualifier 报 `unknown file attribute: i`——根因精确到 zsh glob qualifier 词法 | S+(台账) | `2557fb8 fix(#24)` 采纳;#54 把 marker 再升到第 6 代(U+2026)并加 banned-shape regex |
| 54 | aict666 | 04-24→04-25 | 一个 PR 铲掉三个叠加 regression——tool preamble 从 ~1600 字符瘦身到 ~330 字符(去掉 `### Tool` / parameters schema 等 Claude Code system prompt 指纹形态);redact marker 升到第 6 代单字符 `…`;neutralizeCascadeIdentity 加 5 个新 pattern 封死 Cascade 自称口吻。Claude Code 工具流整体对齐 Anthropic 原生行为(tool_use → tool_result ID 关联、stream 事件序列、参数 streaming 拼接) | Opus 4.7 判「pasted system prompt」拒答、redacted 路径被当参数浪费 turn、Cascade 身份泄漏 | S+(台账);作者累积升 MR | `ae4067c` 采纳;无缺陷修复,与 #61(Opus 多模态)互补 |
| 58 | abwuge | 04-25→04-25 | 两处单行修——nginx upstream `resolve` 动态解析补 `zone` 指令;src/config.js 顶部补 `import { join } from 'path'` | docker-compose 起来后所有 windsurf 容器持续 Restart(nginx 拒绝启动 + Node ReferenceError 进程崩),新人首贡解了部署死锁 | B+(台账) | `9dc019e` 采纳;无后续修复 |
| 61 | baily-zhang | 04-25→04-25 | Opus 4.7 多模态五件事——图片/二进制 block 历史只留占位符不嵌 base64;长 system prompt 压缩去 billing header;禁用 user-message tool fallback;窄范围 cascade reuse + 严格账号绑定;fingerprint 不再 hash 图片 base64 | Claude Code + 图片 + tools 时上下文指数膨胀,触发 Opus 4.7 prompt-injection 拒答(多模态调用从不可用救回来) | S+(台账);作者累积升 LR | `e078f35 fix: prevent Opus 4.7 context bloat (#61)`;无缺陷修复 |
| 62 | baily-zhang | 04-25→04-25 | 修正 Credits 文案里过度转义的 `Codeium\'s` / `Cascade\'s`,新增 dashboard inline script 语法回归测试(V8 静态解析) | 单引号 JS 字符串里转义错误导致整个 dashboard 主脚本 parse fail——页面能开但 stats / accounts 全空白 | S+(台账) | `4d51b5a fix(dashboard): prevent credits syntax crash (#62)`;无后续修复 |
| 64 | baily-zhang | 04-25→04-25 | 新增 isCascadeTransportError() 识别 pending stream has been canceled / ECONNRESET / ERR_HTTP2 等,命中即 resetCascadeTransportState() 清坏会话;不再吞 warmupCascade() transport 失败;统一归类 upstream_transient_error 走瞬态退避 | 重客户端在 InitializeCascadePanelState 收到 cancel 后持续 502——Cascade/LS 的 HTTP/2 transport 会话进坏状态且被后续请求继续复用 | S(台账) | `54e78ad fix(#64): recover from canceled cascade panel transport`;无后续修复 |
| 65 | abwuge | 04-25→04-25 | 新增 `.github/workflows/release.yml`——push v* tag 时自动构建 multi-arch Docker 镜像推 GHCR + 创建 GitHub Release(含 changelog);docker-compose 默认 image 改指 ghcr.io/dwgx/windsurf-api:latest | 项目缺 CI/CD 发布链(此前每次发版手工构建镜像 + 手工建 Release);顺带免去用户 clone 后 docker compose build | A(台账) | **有**——`ca0df42 ci: fix release workflow image name + restore docker-compose build option`(workflow 镜像名写错被修)。此后 v2.0.6~2.0.11 全按该 workflow 跑 |
| 72 | sandleft | CLOSED-采纳 | Codex Responses API 四种非 function tool(custom / namespace / web_search / tool_search)兼容——namespace 用 `__` 扁平化展开成 function、custom 转单 string `input`、web_search/tool_search 给 `query`;回程用 `__response_tool` metadata 还原类型;流式 tool name 后到 chunk 兜住 | 反代对四种工具直接 throw `Unsupported Responses tool type` 把整个请求干 500,codex / codex exec 无法使用 | S(台账) | review 点出的 tool_choice 未翻译 / 流式 metadata 透传不全 / 0 测试三问题全部补齐(+4 条最小测试);cherry-pick 时维护方叠了 UNBRIDGED 集合(file_search / computer_use_preview / mcp 静默 drop,web_search_preview 走翻译,fallback warn-and-drop 不再 throw);gitrefs 仅见 `5e8c067` credits |
| 73 | Yuuqq | CLOSED-采纳(部分,7 处中 3 处) | Windows 部署发现的 7 个 bug——fileURLToPath 替代手撸 file:// 转换、OPTIONS 真返 204 + 显式 CORS 头、META_TAG_NAMES 正则 escape、LS health watchdog、HTTP 明文 SEC 警告、Windows langserver 路径等 | Windows 上 sanitize 全部失效(URL pathname 是 /e:/path 形式)、OPTIONS fall-through 到 JSON body、用户消息 tag 名注入元字符破坏 strip 表 | B+(台账) | `5a4eae9 fix: tighten Windows repo-root resolution, OPTIONS 204, and pool regex escape` 采纳三处(BUG-3 / BUG-8 / BUG-7);其余四处因与现网部署有交互待验证未并 |
| 74 | baily-zhang | CLOSED-未采纳 | 把 tool preamble 预算决策抽成独立 helper 并改顺序——full <= soft 用完整版;full > soft 先试 compact fallback;只有 full 和 compact 都超 hard cap 才 400。新增两条回归测试覆盖 fallback / reject 分界 | 预算逻辑回归——full > hard cap 时直接拒(哪怕 compact 只有 1~2KB),Claude Code 首轮 20+ 工具在入口直接 400「74KB > 47KB」 | B+(自判;gitrefs 空,与 #54 的 preamble 330 字符瘦身功能重叠,疑被该方案覆盖而搁置) | 无 |
| 80 | RogueBinary(台账署名 ZLin98) | CLOSED-采纳(部分,安全方向三件) | 16 文件 748 行安全加固大改——LS env allowlist(只白名单 HOME/PATH/LANG/TMPDIR/proxy/SSL trust,不再喂整份 process.env)、跨平台 workspace 重置(弃 execSync POSIX shell 改 Node fs)、Dashboard auth 收紧(移除 ?pwd= query fallback)、HOST 默认收紧、feature flag 默认全关、getAccountList 不返完整 apiKey 等 | AWS_SECRET / GITHUB_TOKEN / CI secret 经 LS 子进程环境泄漏、Windows 上 workspace 重置直接炸、password 进 URL 访问日志与浏览器历史 | A(台账) | `0b2d051 chore: bump 2.0.12 — 安全加固 cherry-pick`——最稳三件(env allowlist / 跨平台重置 / dashboard auth)cherry-pick 进 v2.0.12,作者署名经 Co-authored-by trailer 保留;其余 6 项因行为变化/UX 倒退未并但思路合理 |
| 88 | smeinecke | 04-28→04-29 | 新加 validateHostFormat() helper(镜像 resolvePublicAddresses 结构但去掉私有段拒绝),ALLOW_PRIVATE_PROXY_HOSTS=1 时 testProxy() 切到它;config.js + .env.example + setup.sh + 中英 README 全接通 | 本地跑 192.168.x.x / 10.x.x.x / localhost 私有代理的 Dashboard 测代理过不了 SSRF 守卫——开了显式 opt-in 口子,默认仍公网 only 保持安全 | B+(台账) | `8d2bfc8` Merge;无后续修复 |
| 89 | smeinecke | 04-28→04-29 | 登录流 toasts / result 显示 / login history 三处错误码全接 i18n;错误码命名向 ERR_PROXY_PRIVATE_IP 看齐(ERR_PRIVATE_IP → ERR_PROXY_PRIVATE_IP 等);补缺失翻译键;语言切换 tooltip 动态化 | ERR_NO_PASSWORD_SET 这类错误码裸串糊在 toast 和登录历史里,没翻成人话 | B(台账) | `e2c951b` Merge;无后续修复 |
| 90 | smeinecke | 04-28→04-29 | Account Pool「添加账号」表单加可选 proxy 字段,API Key / Auth Token 两种方式可用,复用现有校验链(格式错 ERR_PROXY_FORMAT_INVALID、私有 IP 默认拦、SOCKS5 带 auth 可解析),建号即设代理即 spawn LS | 单账号添加只能先建号再单独配 per-account proxy,与批量导入的 inline proxy 补到 feature parity | B+(台账) | **有后续修正**:`7299d4a`(2.0.28)含「PR #90 proxy 顺序修正」——合并后有一处 proxy 应用顺序问题被修 |
| 92 | smeinecke | CLOSED-采纳(部分) | ① test/account-add-proxy.test.js 覆盖 bad/private/valid/no proxy 四种路径(响应 #90 review 里「建号前必须校验 proxy」的意见);② 前端 App.translateError(code, fallback) 集中化,替换 6 处内联 `error.${err.message}` 查找 | 无直接 bug(补测试 + i18n 重构) | B+(自判) | `946a14f fix: dashboard i18n centralization (PR #92, smeinecke)` 采纳 i18n centralization 部分;测试部分无采纳记录 |
| 139 | chukangkang | CLOSED-未采纳 | 与 #144 同题——上游 PostAuth 要求 `X-Devin-Auth1-Token` 请求头,原代码只放 body 导致批量导入与单个登录都 ERR_POSTAUTH_FAILED;postAuthDualPath 增加 extraHeaders 参数并传入该头 | 同 #144 的同一 bug | 未评级-废案(重复) | 无(采纳落在 #144:`04c1500` 明确引用 PR #144);#139 较 #144 缺 proto body 切换与 raw token 解析 |
| 142 | suhaihui-git | CLOSED-采纳 | 新增 `RESPONSE_CACHE_ENABLED` / `WINDSURFAPI_RESPONSE_CACHE` 开关显式关闭本地精确匹配响应缓存;默认仍启用、行为不变,.env.example 只给注释示例,cacheStats() 加 enabled 字段,补三类测试 | 功能(可配置开关,让响应缓存可被关闭) | B(自判) | `defe660 feat: add RESPONSE_CACHE_ENABLED env to disable response cache`(采纳);随 release 2.0.91 发布 |
| 144 | Await-d | CLOSED-采纳 | 修 v2.0.89 时代邮箱/密码登录失败——上游 PostAuth 接口更新:auth1Token 改经 `X-Devin-Auth1-Token` 请求头传递而不再放 JSON body;同时 PostAuth 请求体切到空 `application/proto` 形态,并解析原始 PostAuth 响应里的 `devin-session-token$...` 会话凭证 | 上游协议变更导致登录链路断(ERR_POSTAUTH_FAILED: missing required header) | B+(自判,不在台账) | `04c1500 fix(#134): PostAuth empty proto body + raw token parsing (PR #144 by @Await-d)`(采纳);随 release 2.0.91 发布 |
| 161 | Kloping | CLOSED-未采纳(存疑) | dashboard 账号管理页宽屏布局自适应,3 行改动(2 文件),解决账号邮箱过长无法点击操作 | UX bug | 未评级(极小改动) | gitrefs 空;但 release 2.0.95 说明提到「dashboard width fix」(与 #162/#163/#173/#175 同批发版),与本题高度吻合——可能未署名并入,存疑 |
| 171 | UntaDotMy | CLOSED-未采纳 | 为 /v1/responses、/v1/ws/responses、/backend-api/codex/responses 加 Codex 兼容的 Responses WebSocket 处理,含持久 sticky 路由与 dashboard/runtime 控制;同时加固 websocket 流生命周期——流在 response.completed 前结束/抛错时发终态 response.failed 事件,避免 Codex 客户端干等 | 功能(WebSocket 协议支持)+ 生命周期加固 | 未评级-废案(1940 行新协议路径,gitrefs 空,无采纳记录) | 无 |
| 173 | datfooldive | 05-11→05-12 | Dashboard 前端大重构——1177 行新增 / 465 行删除全部集中在 index.html:清理冗余样式、统一组件风格、优化卡片布局和响应式适配 | 无 bug,纯 UI 重构 | A(台账) | `d72b093` refactor 落地 + 2.0.95 release;无后续修复 |
| 175 | linqichenggg | 05-12→05-12 | Windows / macOS / Linux 三平台的 LS 二进制路径、数据目录、安装脚本全部对齐——之前 macOS 用 ~/Library 路径但 install-ls.sh 写的是 Linux 路径、Windows 直接找不到 LS;统一到 config.js 集中管理 + platform-ls-paths.test.js 覆盖三平台断言 | 跨平台 LS 路径不一致 bug | A(台账) | `971eaca` fix 落地 + 2.0.95 release;无后续修复 |
| 181 | Fermiz | 05-18→05-22 | 三件事一个 PR——① 单用户场景 Cascade 复用优化(跳过多余的账号轮询直接命中唯一账号);② HTTPS 代理中间层 https-proxy.js 让反代本身也能跑 TLS;③ conversation-pool 大小可配置化 | 无 bug,性能/能力优化(Cascade 复用路径 + TLS 入口 + 池配置) | A+(台账,本批最高) | `a6b7936` feat 落地;无后续修复 |
| 182 | lauvww | 05-19→05-21 | 重写 Dashboard 的账号批量导入解析器 import-parser.js:支持 JSON / CSV / 纯文本多种格式混合粘贴、自动检测分隔符、容错处理格式不一致的行 | 无明确 bug,是导入解析能力的重构升级 | A(台账;588 行新增含完整测试覆盖和中英双语 i18n) | `8090033` feat 落地;无后续修复 |
| 184 | ucloudnb666 | 05-21→05-21 | 支持 Astraflow 作为第三方提供商路由:config.js 新增 ASTRAFLOW_API_KEY 配置项,models.js 注册 astraflow/* 模型前缀 | 无 bug,新 provider 支持 | B+(台账;30 行精准修改) | `acac78a` feat 落地;无后续修复 |
| 188 | The-five-stooges | 05-25→06-02 | 修 sticky session 流式路径失效 + 多用户隔离两件事。① 流式重试路径 `waitForAccountFn` 缺第 5 个参数 callerKey,导致 auth 层 callerKey=null、绑定完全失效;② 新增 body.user 多用户隔离:caller-key 只 hash body.user、messages.js 修 `:user:` 段重复追加、新开关 stickyBindByUserOnly(按 user 绑、模型维度置 *)与 stickyNoFallback(绑定账号故障不回退、直接报错,三道防线),Dashboard 双 toggle + 诊断日志 + 示例脚本 | 流式路径绑定失效 bug + 多用户隔离功能 | S(台账) | `f5425ff` 采纳;`6ce8591` 补 sticky 面板缺失 i18n;`542bb7c` 记入 contributors;另有 `3397f25` 结构守卫 |
| 192 | andya1lan | 06-04→06-05 | 把 update.sh 的 LS binary 更新路径从旧的硬编码 WindsurfAPI release URL 收敛到 install-ls.sh(WindsurfAPI release → Windsurf 桌面 LS release → Exafunction fallback),让一键更新、Dashboard LS 更新、手动 install-ls.sh 三条路径共用同一套下载链和 atomic rename 行为;同时把 update.sh 读 .env 的 LS_BINARY_PATH 从 GNU-only 的 grep -P 改成 awk,兼容 macOS BSD grep | 旧 release asset 漂移 + live binary file-busy 问题,以及 macOS 上脚本不可运行问题 | B+(台账;三条路径收敛到同一套下载链) | `3e133bb` merge;无后续修复 |
| 194 | The-five-stooges | CLOSED-未采纳 | 新增 `WAPI_ACCOUNTS_ONLY` 环境变量做 per-instance 账号过滤。多端口部署(3003/3004 共享一份 accounts.json)时,sticky 绑定可能把请求路由到错误实例的账号 | 思路是部署隔离问题,但 255 个文件 +48851 行——远超一个 env 过滤功能所需的改动面(对照 #188 同为该作者、同主题域,320 行/9 文件) | 未评级-废案(gitrefs 空,与 #80「面太大但被摘取」的先例不同) | 无 |
| 195 | MatrixNeoKozak | 06-13→06-22 | dashboard API 收到 malformed JSON 时此前仍按 200 返回,改为返回 400——前端、脚本和自动化调用方现在能用 HTTP status 正确区分请求体格式错误,而不是从成功响应里猜 `ok:false` | malformed JSON 错误语义缺失 bug;配套跑过 dashboard API 和 cache-control 测试 | B(台账) | 无;gitrefs #195 段为空(9 天周期实为提交后等待合并,非多轮迭代) |
| 198 | kevin5251984 | CLOSED-采纳 | 新增「默认模型」(default model):请求的模型不在白名单时,用配置的默认模型代替直接拒绝;留空则恢复拒绝行为。Dashboard 模型访问控制区加默认模型输入框 | 功能(白名单外请求 fallback,不再一刀切 400) | B+(自判;4 文件 759 行对单功能偏大,可能夹带格式化/重构) | `6adec7b feat(dashboard): fall back to a default model for out-of-list requests`(采纳) |
| 199 | limweb | CLOSED-未采纳 | 标题仅「update」,body 是 PR 模板原样——「改了什么 / 为什么 / 测试」全是占位注释,无任何实质描述;+45230/-2877/249f 整仓库覆盖式改动 | 无法判断(无描述) | 未评级-废案 | 无 |
| 201 | brandonedley | 06-21→06-22 | 把 GLM 5.2 和 Kimi K2.7 补进模型 catalog,同步 README、英文 README、package description、模型文档生成检查和 catalog correctness 测试——代码、文档、校验一起补齐,防止 dashboard/docs/测试互相漂移 | 无 bug,纯 catalog 新增(带完整配套) | A(台账) | 无后续修复(gitrefs 空) |
| 204 | lovivi | 06-27→06-28 | 修 glm-5.2 工具调用哑火——glm 系列默认走 glm47 的 XML tool-call 标记,但 glm-5.2 无视标记用纯文本作答;在 pickToolDialect 里给 glm-5.2 及其 glm-5-2-* SKU 别名单独切到 gpt_native 的 function_call JSON dialect,老 GLM 维持 glm47 不动;顺手加 WINDSURFAPI_FORCE_TOOL_DIALECT 环境变量(限定四值)方便测试强制方言 | 新模型工具协议方言对不上导致的工具调用哑火 | B+(台账;首次贡献,10 行精准改动,老 GLM 没经验证不乱动的克制) | 无合并后修复(`7ee509a` fix + `0dc3e23` test 是落地 commit 本身,非后续修复) |
| 206 | clarezoe | CLOSED-采纳(手动合入 master) | `fetchAndMergeModelCatalog()` 只在 initAuth() 启动时跑一次,启动瞬间无活跃账号则云端 catalog 同步被整个跳过且永不重试——后加账号永远触发不了 re-sync,新上游模型(如 2026-07-01 重部署的 Claude Fable 5)不重启就不出现在 /v1/models。修法:_modelCatalogSynced 标志 + trySyncModelCatalog() 并发合并包装,挂到全部 7 条账号激活路径(加号/登录/手动改状态/错误重置/成功自愈/半开恢复),四道 guard 防重复 | catalog 永不重同步 bug;捎带 opus-4-8 effort 梯队、reasoning_effort 替换、thinking 提取 tool_call | S(台账) | `4fc3c39` credit;`ea32403` release v2.0.147 发布(tool_calls-in-thinking + opus-4.8 tier ladder + catalog self-heal) |
| 211 | moduvoice | CLOSED-未采纳 | 新增韩语 (ko) locale 全量翻译:ko.json 与 en.json 1:1 key-path 匹配(1069/1069、0 缺 0 多),语言切换器从 zh/en 二元改成 zh-CN → en → ko 三向循环,I18n.init() 加 ko 浏览器语言兜底。附全量测试 2536/2536 通过 | 功能(i18n 扩展) | 未评级(质量面自证不差,但 gitrefs 空、无采纳记录) | 无 |
| 215 | warelik | CLOSED-采纳 | 网络层根因修复——关闭 Node Happy Eyeballs(net.setDefaultAutoSelectFamily(false),DNS 按默认顺序 IPv4 优先),同时把所有本地 HTTP/2 连接从 localhost 改 127.0.0.1(否则 macOS 上 ::1 优先 + autoSelectFamily=false 只试第一个结果 → ECONNREFUSED,会引入 9 个测试回归)。排除过 4 种替代方案并写明理由 | DNS 把上游域名解析到不可路由 IPv6 ULA 时 Node 的 Happy Eyeballs 在挂起时不回退,请求 ~270ms 后 ETIMEDOUT(curl 能活 Node 不行) | A(台账) | 无 |
| 216 | warelik | CLOSED-采纳 | 两个耦合根因修复救回 native tool-def 路:① MCP-gate 中和——上游对工具描述做指纹匹配(顶层 description + 参数 schema annotation 组合触发),用 Cursor 21 工具 A/B 定位 8 个中招,修法为顶层描述换工具名 + 递归剥离 schema annotation(精准区分「schema 注解」vs「字面叫 description 的参数」),21/21 全过;② native preamble——描述被剥后给 4 个 tier 加 nativeStructured 选项,preamble 注入 system prompt(gate 不扫 prompt) | 工具定义被上游指纹 gate 拦(permission_denied)+ native 路径模型零工具描述 | S(台账;对 Cline 这类工具巨多的 agentic 场景是「能不能用」的分水岭) | 无 |
| 217 | wangergou777 | 07-17→07-17 | codex 的 responses-lite 变体把客户端工具放在 `input[].type="additional_tools"` 而顶层 `tools` 为空,此前被静默丢弃,模型收到的工具列表为空、工具调用整体哑火。修法把 additional_tools 归并进标准 tools 通道,与既有 tools 去重共存,扁平化命名冲突时返回 400 | 工具定义被丢弃 bug | B+(台账,首次贡献) | 间接触发后续系统性清查——`b864447` 按同一模式修了默认模型/developer 角色/缓存键/schema 数组序 4 处同类缺陷;credit 补记 `db5c902` |
| 218 | forrinzhao | CLOSED-未采纳 | 与 #219 同题(codex apply_patch 工具描述中和),body 自述为早期 draft,「accidentally bundled unrelated local branch changes」——误打包无关本地改动导致 +6848/-6635 巨量 diff(净内容与 #219 相同) | 同 #219,但被 #219 的干净 rebase 版取代 | 未评级-废案(重复) | 无(采纳内容归 #219) |
| 219 | forrinzhao | CLOSED-采纳(部分) | codex 的 apply_patch 工具描述里「FREEFORM tool, so do not wrap the patch in JSON.」触发 Devin content policy,飞书 codex bot 全量请求被拦。新增 sanitizeToolDescriptions() 改写两段触发词(FREEFORM → free-form、do not wrap the patch in JSON → provide the patch as plain text),带 `WINDSURFAPI_NEUTRALIZE_TOOL_DESC=0` 开关。附带第二个修复:Codex 发的裸 `{role, content}` input item(无 `type:"message"`)被静默丢弃导致上游收到空 messages → UPSTREAM_INTERNAL | content policy 误拦(纯工具描述措辞误伤)+ 裸 input item 被丢 | B+(台账) | `175b1dd` 裸 input item 修复原样采纳;`67b8357` 工具描述中和挪到 preamble 注入之后并做成 (a7) 规则复用既有序列(覆盖所有客户端而非仅 codex);`__route` 改动因会回退 gpt_native dialect 未采纳;`5cf0116` 致谢 |
| 224 | warelik | 07-24→07-25 | 流式 DEVIN_CONNECT 路径的 429 reset 窗口透传(两条错误路径补 resetMs)+ 冷却落位修正——从 model-scoped `_modelRateLimits` 改为 account-wide `rateLimitedUntil`(DEVIN_CONNECT 选池用 modelKey=null,model 维度结构上永远关不住这条路径)。只修一条毫无可观测效果,两条都定位到了 | 刚被 429 的账号几秒内被重新选中、反复撞上游 velocity limiter 直到硬封(实测约 3h) | S(台账) | **有**——`ac045ba` fix(chat,auth):CAPACITY 冷却写在 connect selector 维度,标注「#224 同构缺陷第三次」(冷却维度写错这类缺陷在本 PR 之后仍第三次复发);`68da125` fix(chat) 同段(断连吞 429 冷却窗口) |
| 225 | warelik | 07-24→07-25 | 把「客户端断开不惩罚账号、不 failover」扩展到 DEVIN_CONNECT 全部退出路径——isAbortError 同时认 AbortError 名/ABORT_ERR 码/aborted 正则(undici 与 Node AbortController 行为不一致只认一种会漏);首次尝试/transient replay/re-login retry 三条 catch 全覆盖,kind:'abort' 让 failover 循环干净退出,非流式返回 499;replay 启动前与 [DONE] 写入前都重查 aborted | 客户端断连被当账号故障 → 计 error budget、跨账号 failover 烧配额、往死 socket 写 | A+(台账) | **有**——`68da125` fix(chat):客户端断连吞掉上游 429 冷却窗口 + 上游 aborted 被当成断连(本 PR 断连/abort 主题的后续修复,同时出现在 #224 段) |
| 226 | warelik | 07-24→07-25 | src/session-continuity.js(600 行、零 npm 依赖)从对话自身的 request/response pair 链推导稳定 session_id,让多轮对话复用同一 session_id 而非每轮 randomUUID——身份只来自客户端可见输入/输出(显式不用 requestId/IP/时间戳);root anchor 让 id 从第 1 轮稳定,provisional-claim 规则让同开场白的对话第 2 轮分叉;TTL 30min + LRU 500 双重有界。真实上游(glm-5.2)实测两段 3 轮对话各 3 轮同 id | 每轮新 session_id 在上游 velocity limiter 眼里放大账号级限流压力 | S(台账;对「能否降低 429 频率」这种无法短 smoke 证明的问题保持正确保守姿态,先把「重复 session_id 会不会被拒」这个关键风险证伪) | 无 fix 类(`e980bc2` 为 docs 注释指回、`9bbab01`/`a9e8a49` 为 #242 的 commit) |
| 227 | warelik | 07-24→07-25 | neutralizeClientIdentity 加 a6-grok 规则——Grok CLI 的「You are Grok ... released by xAI」自我介绍归一化为通用助手行(任意版本号都命中),整块剥离 `<executing_actions_with_care>` 段;与 a1-a5 同族同开关。边界处理到位(无 xAI 归属的「You are Grok, a helpful assistant.」和无关提及不动) | Grok 自我介绍触发 Devin content policy 拦截(live A/B 确认) | A(台账) | 无 |
| 228 | warelik | 07-24→07-25 | Anthropic thinking 块无真实签名时整个省略 signature 字段,不再发代理合成的 signature:"";上游真给了签名则原样透传(前向兼容)。流式只在收到真实 reasoning_signature 时才发 signature_delta | 严格客户端(Grok Build messages 后端)把 signature:"" 判为非法值拒掉 | B+(台账) | 无 |
| 229 | warelik | 07-24→07-25 | Responses API usage 补齐 input_tokens_details / output_tokens_details(text 由 total 减 cached/reasoning 推导,上游显式 text_tokens 优先);与同期 cache_read_tokens 的 wire tag 校准合成一条链的两半——缓存命中数进得了 usage、以 OpenAI 标准形态出得了 Responses API | 严格消费者(Grok Build、codex 类客户端)需要的 usage 明细结构缺失 | A(台账) | 无 |
| 230 | wangergou777 | 07-25→07-26 | DEVIN_CONNECT 路径补上 sticky session 绑定写入。此前 `setStickyBinding` 只在两条 Cascade 成功路径被调用,connect 部署开 `STICKY_SESSION_ENABLED=1` 完全空转(实测 23 次请求 23 CHECK / 23 MISS / 0 SET),`getApiKey` 以 lastUsed 升序派号,同一会话每轮被主动派到不同账号,而上游 prompt cache 按账号隔离、写入单价约为读取一个量级,换号 = 每轮重付整个累积上下文 | connect 路径只查不写绑定;且连接取号 `modelKey` 恒为 null,绑定必须同域以 null 写入,否则查询静默不命中。修复后 41/44 命中,write 恒为本轮增量、read 线性攒到 17 万 token;绑定账号额度耗尽时回退清绑定重选正常 | S(台账) | 合并时 master 侧同步补了 sticky 快路径 excludeKeys、per-user-scope 门禁与 connect-metrics 观测;另有 docs/sticky 注释修正、connect 维度守卫行为断言等;credit 补记 `db5c902` |
| 232 | andya1lan | 07-28→07-30 | 架构级修复——静态模型表此前被 /v1/models、Dashboard、路由各自独立暴露,受限上游目录的部署会宣传账号用不了的模型。落地为分层方案:池级列表用活跃账号目录的并集、路由校验所选账号自己的目录。在 issue #231 里先穷举方案空间(手工白名单会漂移 / 单账号目录套用他人 / 交集隐藏可用模型),最终取舍正确。超要求还做了两件事:Dashboard 两个界面加 restrictionFailOpen 告警、把「无账号时上游 catalog 变更检测」划为 out-of-scope 另开 #234 | 初版有两个 fail-open 未覆盖——干旱白名单被 catalog 过滤成空后 isModelBlockedByDrought 对每个模型返回 true(干旱一激活全站中断)、上游「部分但非空」响应被当权威目录(实测 pro 账号从 130 个模型塌到 6 个)。两轮修复都精准命中,干旱那条用「catalog ∩ free-tier 交集非空时限制、为空时只让低配额保护 fail-open」的方案比评审建议更好;缩小检查用 last-known-good + 单轮 30s 延迟确认 | A(台账;首贡即跨三条暴露路径的架构级修复。未到 S 因初版漏两个 fail-open 场景,且描述里一条「master 上也复现」的声明经核实不准确、作者已撤回) | **有**(本批唯一)——`b0989ac` fix(models,auth): connect 目录永久 latch + `3cc5aab` fix(models): 空/畸形目录响应零确认擦掉 last-known-good + 缩水目录永久楔住 |
| 233 | andya1lan | 07-29→07-30 | GitHub 已于 2025-12-04 下线 macos-13 runner 镜像,而 release.yml 的 x64 job 一直写 `runs-on: macos-13`,导致 v3.9.1~v3.9.4 四个版本的 macOS x64 二进制从未产出(job 终态全为 cancelled)。改为 macos-26-intel,并附 GitHub changelog 与 actions/runner-images README 两条官方依据;保留 x64 不进 release needs 的非阻塞语义 | CI 发布链 bug——外部贡献者替维护方发现了已影响四个已发布版本的发布缺口(此前维护方一直把 queued 解释成「非阻塞」) | B+(台账;四行改动,价值全在诊断) | 无(`7c39896` merge + release/docs/chore,无 fix 类 commit) |
| 238 | warelik | CLOSED(内容被采纳,记入 v3.9.7 台账) | swe-1-7(Kimi K2 fine-tune)在 agentic 循环里整轮走 reasoning 通道、声明工具意图却不发 tool call——reasoning-only finish 且有 tools 时自动追加纠正 nudge 重试(实测 24/24 恢复)、重试历史剔除空 assistant turn(否则 10/10 复现)、无 tools 或重试耗尽时 reasoning 提升为可见 content 兜底。按失效特征而非模型名按键 | thinking-only 完成 + 空 assistant turn 污染重试历史的三环一链根因(上游花在 reasoning、isEmptyCompletion 把 reasoning 算 content 导致不重试、空轮原样转发上游) | S(台账;评审查出两条自引入缺陷,均比它们修复的缺陷轻一个数量级,由维护方合并后修掉;未到 MR/LR 因这两条本可在提交前自查到) | **有**——`69fec13` fix(devin-connect)(move leading think-tag reroute to the event level, before the #238 rescue)、`61871e1` fix(think-reroute)、`7b99c8e` fix(messages,gemini)(零内容+无终止信号必须报错);台账明说的两条自引入缺陷修复也在此列;`438d1d6` 记台账 |
| 241 | warelik | 08-03→08-04 | #238 的下一层——救援 nudge 引用上一次失败尝试的 reasoning 尾部(默认取尾 2000 字符,可调),模型接着自己想到哪发出动作;每次救援恰一条新 nudge(从原始请求重建,不再累积旧 nudge);判定从 emulateTools 旗标改为请求自带的 params.tools 长度;field-0 护栏(protobuf 保留字段拒绝序列化) | 上游带 tools 请求只返纯 reasoning 流、无 content/tool call 的「光想不做」失败形态(#238 修的是 reasoning 整段丢弃那一半,本 PR 修剩下的一半) | S(台账;digest cap 缺钳属提交前自查能发现,同函数上方十二行就有 RESCUE_MAX 的教训注释) | **有**——`025bb73` fix(devin-connect) 补 digest 上限钳(明确以「#241 DIGEST_MAX_CEILING 的课后作业」为先例);`61871e1` fix(think-reroute) 同段出现;台账证实两条评审意见(nudge 累积、field-0)两轮内改到位且改得比要求远 |
| 242 | warelik | 08-05→08-09 | DEVIN_CONNECT 会话保真,两个 opt-in 特性(DEVIN_CONNECT_SESSION_REUSE 主门控):① 稳定 ModelConfig(#15.1 会话内恒定、#15.2 单调递增,对齐 devin.exe,不再每轮随机 UUID);② reasoning continuity(会话内 reasoning 尾摘要按 TTL+LRU 有界存储,下一轮 system 后缀 checkpoint 回注,减少重推与退化),附带 T4 出口去重 | 多轮 agentic 会话在 devin-connect 上的质量退化(会话身份不稳定 + 推理链跨轮不可用) | S(台账;count/chars 两个小数洞同族复发——0.5 → Math.floor 静默 0,提交前自查可发现) | 无 fix 类——`44eeac5` 修正与 #243 的 baseline 碰撞、与 #248 的 findExistingState 抽取结构性交互(git 三方合并看不见,合并时实测修复) |
| 243 | warelik | 08-05→08-09 | Anthropic 类出口加分类器,把前导 `<think>` 标签块(前面只允许空白)从 content 重路由到 thinking 通道,切断「reasoning 泄漏为可见文本 → 客户端存回 → 模型再被引导 reasoning-as-text」的自强化循环;流式+非流式都覆盖,DEVIN_CONNECT_THINKTEXT_REROUTE=1 默认关 | thinking 模型把前导 think-tagged 内容写进 content 通道的循环退化;刻意不触发 #238 rescue、不教 isEmptyCompletion 读 sawText,保持 rescue 边界干净;Kimi ◁think▷ 方言留作后续 | A+(台账;未到 S:范围描述低报——标题/文档说 Anthropic Messages egress,实际接在 streamChatWithEmptyRetry 上、服务 connect 全 4 条路由,变量名诚实但文档低报) | **有**——`61871e1` fix(think-reroute)(honest rescue-interplay comment;pin history/replay isolation)、`69fec13` fix(devin-connect)(把前导 think-tag 重路由挪到事件层、置于 #238 rescue 之前)、`44eeac5` test(mutations) 修正与 #242 合并产生的 baseline 碰撞 |
| 247 | warelik | 08-06→08-09 | 统一流式出口的 reasoning/content 增量去重——content 与 reasoning 前缀逐字一致时毫秒级短缓冲,出现分歧立即放行并直通;仅 settle 时完全等长且逐字相同才抑制;严格前缀一律放行,客户端永远拿不到空答案;失败/中止路径 release() 无条件放出;held 1 MiB 上限。默认 ON | thinking 模型把整段 reasoning 原样重复写进 content 通道(客户端看到同一段文字两遍)。设计要点:抑制前提是「调用方明确要了 thinking」(沿用 shouldFallbackThinkingToText 先例);多字节安全经维护方攻击性验证 | S(台账;第一轮严格前缀洞与第二轮完全等长洞是同一前提错误的两次发作——提交前自查能发现) | `b87ac6a` WINDSURFAPI_REASONING_DEDUP off-switch(feat 类,默认开可关——本 PR 是五个 PR 里唯一默认生效且无 kill switch 的行为变更,属部署侧要求而非实现缺陷);`d7ac4e6` test 修正 spec baseline |
| 248 | warelik | 08-06→08-09 | 会话复用扛住客户端单对话历史压缩——pair 窗口失效时回退到对话根锚点(首轮输入)匹配,歧义时拒绝误认(宁可开新 session 也不 hijack);overlap 计分改尾锚点加权;reuse 日志补 acct= 字段。仍在 DEVIN_CONNECT_SESSION_REUSE 门控内,默认关 | 压缩后会话认不出、continuity/稳定 ModelConfig 静默失效的问题;边界写清(多对话共享 opener 时压缩路退化为新 session 是有意保守,fork 可判定化留后续) | A(台账;未到 S:描述把「单对话压缩存活」说成通用能力;与 #242 的 findExistingState 抽取有文本层之下的结构交互,合并时实测 15 条测试失败后修复) | `0da8e02` test baseline 修正、`b87ac6a` #247 off-switch,均非本 PR 行为修复 |
| 249 | warelik | 08-06→08-10 | 为 reasoning/content 边界泄漏问题架设结构化观测设施——`WINDSURFAPI_LEAK_TRACE` 门控的结构化日志(devin-connect-openai 事件层、messages 分类层、chat 出口层),记录通道、think 标记状态、有限文本采样、reqId/acct。只做可观测性不做修复,抓到现场后另开 PR 提修复;日志经既有 log-safety 裁剪,不落 key 与完整响应;settle 记录带 outcome 字段防止「空字段误读为没抓到问题」 | 非修复 PR,纯观测(为下一层修复提供现场数据) | A(自判;台账未收录。设计质量高——默认关、关闭时字节不变、热路径零开销、复用 log-safety、outcome 语义防误读) | 无 fix 类——`1fbef0d` test(docs) 守卫第五个盲点(#249 一进来就漏掉了,新开关引入时文档守卫没拦住)、`2e3daeb` docs 开关数 156→157,均为文档/测试侧尾巴 |
| 251 | dwgx | 08-07→08-07 | 判定一份 16 条的审计清单(提交者自陈没有复核任何一条,同一轮里已有 4 条被证否、1 条是编造的文件名)。结果:修 11 条、3 条判定成立但附理由不修、2 条已被上轮覆盖;四条报告弄错了自己的机制,正文逐条更正。三个提交按 spec 的真实依赖切分(terminal-event-guard 绑 responses/messages/gemini、retry-after-route-parity 绑 messages/gemini/server、bash-prefix-repair 绑 chat/messages),每个提交在独立 worktree 里实测门禁。最重一条:item 7 是语义反转——`repairToolCallArguments` 的 startsWith 允许中段扩写,网关推翻模型的决定(rm -i → rm -if /data),五个洞逐个实测封掉;封完后发现第五道门——Anthropic 块级别区分附件与调用方原话而翻译层 join 进同一条,document 文本区间在 repair 里遮蔽。item 9/10 定性为潜伏守卫(不可达);item 10 修根(storedResponseBody 不越过最后一条 user);item 15 上游沉默时省略 `input_tokens: 0` 断言(上游明确报 0 是数据、沉默不是);item 12 归因修正——真正阻塞事件循环的是 fsync 不是 Atomics.wait;item 16 post-tool 文本在工具 item 创建时封段,store 刻意存全量文本(丢文本比丢顺序更糟);W3 四条断言钉住 response id 的 ~90 bit 熵。门禁 3691 pass / 0 fail 跨两次稳定 | 11 条 client-visible 缺陷跨四条 egress 路径(含 bash 扩写、响应存储返回 stale answer、usage 假 0、post-tool 文本错位、terminal-event 守卫等) | 未入台账(维护方自审 PR;evidence 密度极高——每条修复带突变证据含 over-reach 对照) | 无(`b6e7dc9` 为合并 commit 本身;gitrefs #251 段空) |
| 252 | wjurkowlaniec | 08-07→08-10 | pickToolDialect 让所有 swe- 前缀模型路由到 kimi_k2 工具调用方言(SWE 家族与 Kimi K2 同用 `^N...^O` section-token 格式),修外部 harness 的 "invalid tool call" 错误;54/54 单测 + 本地实例全 SWE 模型 live 集成验证;零新依赖 | SWE 家族模型走错误工具方言导致 invalid tool call | B-(自判;台账未收录,首次贡献。方向正确但与同型先例 #204(10 行精准方言修复,B+)相比改动面大;合并后两条 fix 类后续直接否定其完备性) | **有,两条铁证**——`0523910` fix(prompt):**#252 的两处改动互相抵消**(还原全局默认值,让方言隔离真正生效);`6cc2886` fix(tool-emulation):exclude swe lightning from kimi dialect(无脑路由 swe- 前缀误伤了不该走 kimi 方言的模型);另有 `1308ff4` refactor trim unproven kimi compatibility |
| 253 | dwgx | 08-09→08-09 | docs(contributors):warelik 四条(#243 A+ / #242 S / #248 A / #247 S)记入台账,+80/-0 纯文档 | 台账完整性(评审裁决落账) | 文档(未评级) | 无(`ba4d768` 为合并 commit 本身;gitrefs #253 段空) |
---
## 重点加深(10 个 PR 的技术细节段)
### #226 —— pair-chain 稳定 session_id(warelik,S)
- **架构**:`src/session-continuity.js` 600 行、零 npm 依赖。从对话自身的 request/response pair 链推导稳定 session_id——身份只来自客户端可见的输入/输出,显式不用 requestId/IP/时间戳(这三者要么客户端可见可伪造、要么不稳定)。
- **稳定机制**:root anchor 让 id 从第 1 轮就稳定(首轮输入即锚点);provisional-claim 规则让「同开场白」的对话第 2 轮分叉(第二轮输入不同 → 各自 claim);TTL 30min + LRU 500 双重有界,防内存无限增长。
- **实测**:真实上游(glm-5.2)两段各 3 轮对话均 3 轮同 id。
- **修复对象**:每轮新 session_id 在上游 velocity limiter 眼里放大账号级限流压力。
- **评级姿态**:对「能否降低 429 频率」这种无法短 smoke 证明的问题保持正确保守——先证伪「重复 session_id 会不会被上游拒绝」这个关键风险,再谈收益。
- **后续**:无 fix 类;`e980bc2` docs 注释指回,`9bbab01`/`a9e8a49` 为 #242 的 commit。
### #61 —— Opus 4.7 五件事(baily-zhang,S+)
一个 PR 内五件耦合修复,救回 Claude Code + 图片 + tools 的多模态调用(此前上下文指数膨胀触发 Opus 4.7 prompt-injection 拒答):
1. **图片/二进制 block 历史只留占位符**,不嵌 base64——切断「图片 base64 进 fingerprint → 每次 hash 不同 → 永不命中 → 全量重放 → 指数膨胀」链。
2. **长 system prompt 压缩去 billing header**——降输入体积。
3. **禁用 user-message tool fallback**——堵住把 user 消息当工具结果回放的路径。
4. **窄范围 cascade reuse + 严格账号绑定**——复用只在安全范围开,且账号必须匹配。
5. **fingerprint 不再 hash 图片 base64**——与 ① 配套,让含图对话的 fingerprint 稳定可命中。
作者累积 S+ 升 LR(Legendary Rare)。后续 `e078f35 fix: prevent Opus 4.7 context bloat (#61)` 落地,无缺陷修复;与 aict666 的 #54(preamble 瘦身)互补。
### #230 —— 三账号交叉实测钉死 sticky 空转(wangergou777,S)
- **机制**:`setStickyBinding` 只在两条 Cascade 成功路径被调用,DEVIN_CONNECT 路径**只查不写**——connect 部署开 `STICKY_SESSION_ENABLED=1` 完全空转,实测 23 次请求 23 CHECK / 23 MISS / 0 SET。
- **代价量化**:`getApiKey` 以 lastUsed 升序派号,同一会话每轮被主动派到不同账号;上游 prompt cache 按账号隔离、写入单价约为读取一个量级,换号 = 每轮重付整个累积上下文。
- **第二个洞**:连接取号 `modelKey` 恒为 null,绑定必须**同域以 null 写入**,否则查询静默不命中——两洞缺一不可,只补写入不补键域照样失效。
- **修复后实测**:41/44 命中,write 恒为本轮增量、read 线性攒到 17 万 token;绑定账号额度耗尽时回退清绑定重选正常。
- **合并后配套**:master 侧同步补了 sticky 快路径 excludeKeys、per-user-scope 门禁与 connect-metrics 观测。
### #252 —— 两处改动互相抵消(wjurkowlaniec,B-)
- **方案**:pickToolDialect 让所有 swe- 前缀模型路由到 kimi_k2 方言(SWE 家族与 Kimi K2 同用 `^N...^O` section-token 格式),54/54 单测 + 本地全 SWE 模型 live 集成验证,零新依赖。
- **缺陷证据(合并后两条 fix 铁证)**:
- `0523910` fix(prompt):**#252 的两处改动互相抵消**——改动 A 改了全局默认方言、改动 B 在别处还原,方言隔离从未真正生效,需要还原全局默认值才救回来。
- `6cc2886` fix(tool-emulation):**exclude swe lightning from kimi dialect**——无脑按 swe- 前缀路由误伤了不该走 kimi 方言的模型。
- 对照同型先例 #204(lovivi,10 行精准、B+),#252 的 383 行改动面与合并后必须还原的抵消缺陷形成质量差距。
### #251 —— 16 条审计裁决(dwgx,维护方自审)
- **判定口径**:提交者自陈没有复核任何一条审计清单;同一轮里已有 4 条被证否、1 条是编造的文件名。结果修 11 条、3 条判定成立但附理由不修、2 条已被上轮覆盖;四条报告弄错了自己的机制,逐条更正。
- **item 7 最重(语义反转而非扩写)**:`repairToolCallArguments` 的 startsWith 允许**中段扩写**,网关推翻模型的决定——`rm -i` → `rm -if /data`(交互式变强制)、`deploy --dry` → `deploy --dry-run=false --prod`。五个洞逐个实测封掉:token 边界、不引入新命令(`COMMAND_CHAINING_RE`)、否定遮蔽(复用 NLU 层 `maskNonActionableRegions` 而非长第二份拷贝)、来源可信(原本读任意尾部 user 消息,`<tool_result>` 载体让工具输出决定执行什么)、猜测边界(裸模式正则捕获到行尾)。
- **封完后发现的第五道门**:Anthropic 在块级别区分附件与调用方原话,翻译层把两者 join 进同一条消息后下游无法区分——写着一份 `run npm install --force --unsafe-perm` 的 document 让 repair 追加了不该有的 flag。修法:翻译时标记已解码的 document 文本,repair 里**遮蔽区间**而非跳过整条消息(附件和真实指令共处一条,跳整条会把指令一起丢掉)。
- **知情保留**:`ls -la` → `ls -la /etc/shadow` 与合法 `node -p` → `node -p "1+1"` 形状逐字节同构,形状上不可判定——处置是可观测(命中记 warn + 记扩写前后),不猜辩护不了的启发式。
- **机制更正四例**:item 9/10 定性为**潜伏守卫**(`!callerKey` 分支从唯一调用点永不触发,而那行正是唯一 bump `_stats.rejected` 的地方);item 10 修根(`storedResponseBody` 不越过最后一条 user——GET 返回 `"It is 4."` 而最后问题是 `"And 3+3?"`,status 报 completed);item 15 关键区分「上游明确报 0 是数据,上游沉默不是」(缓存命中会合法报 0 completion tokens)——上游沉默时省略字段而非断言假 0;item 12 归因修正——真正阻塞事件循环的是 `writeFileSyncDurable` 的 fsync,Atomics.wait 那条只在 EPERM/EBUSY 的 rename 重试里可达(POSIX 同文件系统 rename 不返回 EBUSY,那是 Windows 形态),照报告去改会改错地方。
- **item 16 post-tool 文本**:`msgId` 构造时铸一次、`messageStarted` 置位后永不复位,工具调用之后的文本被并回第一个 item(实测 done 顺序 [1,0],Anthropic 同帧是 0 text / 1 tool_use / 2 text)。修法:在**工具 item 创建时封段**,`output_item.done` 自然升序,不需要重排 `finish()`;store 刻意存全量文本(content 是单字符串承载不了「文本-工具-文本」顺序,分段存会让下一轮丢掉工具前的文本——丢文本比丢顺序更糟)。
- **W3**:README 已写实但依赖的事实没有守卫——先验证共享 API key 下两个不同 caller(不同 IP、不同 UA)发同一个 user 得到逐字节相同的 callerKey,再用四条断言钉住 response id 的 ~90 bit 熵宽度与随机性。
- **验证口径**:每条修复带突变证据含 over-reach 对照;`COMMAND_CHAINING_RE` 收窄成 `/[&|]/` 起初 SURVIVED——不是护栏有洞而是 fixture 无反引号无句末标点被 `bounded` 门提前拒了,补三条带反引号 fixture 后 CAUGHT;并发跑门禁时计数不可信(`--test-force-exit` 在 stdout flush 前切断),同一棵树三次读数 3685→3687→3687,`EXIT=0` 加跨次稳定才是证据。门禁 3691 pass / 0 fail 跨两次一致,212 anchor 唯一。
### #249 —— WINDSURFAPI_LEAK_TRACE 观测设施(warelik,A 自判)
- **分层**:devin-connect-openai 事件层、messages 分类层、chat 出口层三层日志点,记录通道、think 标记状态、有限文本采样、reqId/acct。
- **原则**:只做可观测性不做修复(抓到现场后另开 PR 提修复);默认关、关闭时字节不变、热路径零开销;日志经既有 log-safety 裁剪,不落 key 与完整响应;settle 记录带 outcome 字段防「空字段误读为没抓到问题」。
- **与主线的衔接**:think 方言与 #243/#250 分类器知识直接衔接(后续 `a6c41ef` fix(leak-trace) 补 `<think>` dialect in THINK_MARKERS、settle outcome on failure exits)。
- **合并后尾巴**:`1fbef0d` test(docs) 守卫第五个盲点——#249 一进来就漏掉,新开关引入时文档守卫没拦住,属项目侧守卫盲点被本 PR 暴露;`2e3daeb` docs 开关数 156→157。
### 会话保真四件套:#242 / #243 / #247 / #248(warelik)
**#242(S)—— 会话保真地基**:DEVIN_CONNECT_SESSION_REUSE 主门控下两个 opt-in 特性:① 稳定 ModelConfig(#15.1 会话内恒定、#15.2 单调递增,对齐 devin.exe,不再每轮随机 UUID);② reasoning continuity(会话内 reasoning 尾摘要按 TTL+LRU 有界存储,下一轮 system 后缀 checkpoint 回注),附带 T4 出口去重。修多轮 agentic 会话质量退化。已知弱点:count/chars 两个小数洞同族复发(0.5 → Math.floor 静默 0)。
**#243(A+)—— 前导 think 标签重路由**:Anthropic 类出口加分类器,把前导 `<think>` 标签块(前面只允许空白)从 content 重路由到 thinking 通道,切断「reasoning 泄漏为可见文本 → 客户端存回 → 模型再被引导 reasoning-as-text」的自强化循环;流式+非流式都覆盖,DEVIN_CONNECT_THINKTEXT_REROUTE=1 默认关。刻意不触发 #238 rescue、不教 isEmptyCompletion 读 sawText,保持 rescue 边界干净;Kimi ◁think▷ 方言留作后续。未到 S:范围描述低报(标题/文档说 Anthropic Messages egress,实际服务 connect 全 4 条路由)。后续 `69fec13` 把重路由挪到事件层、置于 #238 rescue 之前。
**#247(S)—— 增量去重**:统一流式出口 reasoning/content 增量去重——前缀逐字一致时毫秒级短缓冲,出现分歧立即放行直通;仅 settle 时完全等长且逐字相同才抑制;严格前缀一律放行(客户端永远拿不到空答案);失败/中止路径 release() 无条件放出;held 1 MiB 上限。默认 ON(五个 PR 里唯一默认生效的行为变更)。抑制前提是「调用方明确要了 thinking」(沿用 shouldFallbackThinkingToText 先例);多字节安全经维护方攻击性验证。后续 `b87ac6a` 补 WINDSURFAPI_REASONING_DEDUP off-switch(部署侧要求,非实现缺陷)。
**#248(A)—— 压缩存活**:pair 窗口失效时回退到对话根锚点(首轮输入)匹配,歧义时拒绝误认(宁可开新 session 也不 hijack);overlap 计分改尾锚点加权;reuse 日志补 acct=。修压缩后会话认不出、continuity/稳定 ModelConfig 静默失效。与 #242 的 findExistingState 抽取有文本层之下的结构交互,合并时实测 15 条测试失败后修复(`0da8e02` 修正 baseline 289→300)。
**交叉风险**:四条线(#242→#248)在 findExistingState/root-anchor 上真实互撞,git 三方合并看不见,全靠合并时实测救回(#243×#242 baseline 碰撞、#243×#248 同批文件加测试)——这是跨 PR 叠加风险的典型样本。
---
## 总结
**覆盖确认**:69/69 全量入账(表内 69 行,编号 1–253 全数覆盖)。数据来源:A 文件(warelik 16 个含 #252)、B 文件(4 月老 PR 组 25 个)、C 文件(5–7 月中段 15 个)、D 文件(单发作者 11 个)覆盖 67 个;缺的 #251/#253 用 `gh pr view` + PR body 全文补齐(含 16 条审计裁决细节)。合并状态分布:MERGED 38、CLOSED-采纳/部分采纳 16、CLOSED-未采纳 12、CLOSED-内容被采纳(#238 记台账)1、文档 1、废案 1(#218 被 #219 取代归并统计)。评级分布:S 档 10(#36 #45 #64 #72 #188 #206 #216 #224 #226 #230 #238 #241 #242 #247 计 14,含 S+ 的 #44 #51 #53 #54 #61 #62)、A 档 13、B+ 档 15、B/B- 档 5、未评级 12。
**3 个最值得注意的 PR**:
1. **#251(dwgx)**——信息密度最高的一份自审:16 条审计裁决里修 11 条、更正 4 条错报机制,item 7 把「扩写」定性为「语义反转」(rm -i → rm -if /data)并五洞实测封掉,还额外挖出附件/原话 join 的第五道门;证据口径(突变含 over-reach 对照、EXIT=0 加跨次稳定)是这条仓库的验证标杆。
2. **#226(warelik)**——600 行 pair-chain 会话推导 + root anchor/provisional-claim + TTL/LRU 双重有界,0→1 架构性贡献且对「能否降 429」保持正确保守姿态先证伪关键风险;它和 #242/#248 的 findExistingState 结构交互合并时实测救回,是跨 PR 叠加风险的样本。
3. **#252(wjurkowlaniec)**——69 个 PR 里唯一的「合并后必须还原」:两处改动互相抵消(0523910 还原全局默认值)+ 无脑 swe- 前缀路由误伤模型族(6cc2886),与 #204 的 10 行精准先例形成同型修复的质量差距两端,是评审完备性最值得引用的反例。
(次席候选:#230 三账号实测钉死 modelKey=null 键域双洞;#61 五件事一次救回 Opus 4.7 多模态。)