Repository navigation
v3.141.592654
v3.141.592654
自 v3.10.1 以来的 51 个提交 / 76 个文件(git rev-list --count v3.10.1..e3cc399;git diff --stat v3.10.1..e3cc399:5,859 增 / 447 删)把 wave-3 的修复、三轮真实客户端抓包和一次历史查漏补缺合入。上游 proto 形状未变;有两处有意的客户端可见变化:/v1/chat/completions 对 server-executed 工具类型从静默丢弃改为 400,Codex 0.164 每轮都发的 parallel_tool_calls:false 从 400 改为正常服务。
版本号:π 的十进制是 3.141592654,语义化版本要求三段,所以发布号写作 3.141.592654(tag
v3.141.592654,本文件RELEASE_NOTES_3.141.592654.md)。分支名沿用release/3.141592654。
1. Cascade 重放历史的图片:不再静默丢弃
修复前只有最新一轮走 extractImages(src/client.js:897/910/956,修复前),更早轮次的图片到达模型时是字面占位符 [Image omitted from text history](src/client.js:166)——没有计数器,也没有一行日志。
wire 能力来自 builder,不是猜的:buildSendCascadeMessageRequest(src/windsurf.js:348)的 field 6 是 repeated ImageData{base64_data=1, mime_type=2}(src/windsurf.js:382-391),是消息级平铺列表,没有轮次索引——所以按轮归属不可能,但每张图都能搭同一条消息。
- 现在每个重放轮次都用与当前轮相同的护栏抽取(
extractImages→ SSRF / 大小 / MIME / 解码)。当前轮保持位置1..k,历史图按时间序接在k+1..;每轮文本给每张附上的图一个编号 marker,模型能区分。 - 有界且偏好最近的轮次:
CASCADE_MAX_HISTORY_IMAGES(默认 4)、CASCADE_MAX_HISTORY_IMAGE_BYTES(默认 1,600,000,即 4 ×image.js的 400 KB 重编码目标;src/client.js:308-316)。装不下的按cascade_image_history_dropped计数并记日志,装上的按cascade_image_history_attached计数,进程生命周期;/connect-metrics暴露cascadeImages(DELETE时重置)。 - 恢复后的重建路径用同一个 builder(
src/client.js:1133),重建的历史与全新历史携带的内容完全一致。 - 测试:
test/cascade-image-history.test.js(新,9/9,假 LS 解真实 proto);新 spectest/mutations/cascade-image-history.json8 条突变;client-history-coverage.json4 个 anchor 重钉到新 builder。 - 文档:
docs/ENV-SWITCHES.md收录两个开关。
2. Codex parallel_tool_calls:false:400 → 正常服务 + 计数
Codex CLI 0.164 在 29/29 次抓到的请求里都发 parallel_tool_calls:false(evidence/ClientSandboxes.md §3.1、§3.3 C1),且总与 11 个工具、tool_choice:"auto" 同行。wave 2 让 responsesToChat() 转发这个字段本身是对的,但 chat 路由的门在 DEVIN_CONNECT_TOOL_CHOICE 不是 1(出厂默认)时回
400 invalid_request_error / code=unsupported_parameter / param=parallel_tool_calls
Codex 收到 turn.failed 后退出 1:默认配置下客户端不可用。决策是永不 400(这是偏好,不是协议):开关打开时转发;关闭时照常服务,每进程 warn 一次并计数 parallel_tool_calls_hint_ignored。
探针(evidence/CodexParallel-probe.mjs,真实 handler、只桩掉 connect 传输)前后:
{"label":"codex-shape with parallel_tool_calls:false","status":400,"modelCalls":0,"error":{"code":"unsupported_parameter","param":"parallel_tool_calls"}}
{"label":"codex-shape with parallel_tool_calls:false","status":200,"modelCalls":1,"forwardedParallelToolCalls":false,"hintIgnoredDelta":1}- 测试:验收 1,220/1,220;聚焦套件
parallel-tool-calls-connect12/12、drought-gate-reachability10/10、retry-after-route-parity5/5。 - 突变:
parallel-tool-calls.json10/10 被捕获(基线 12);retry-after-route-parity.json6 条(5 捕获 + 1 条有记录的幸存者,基线 5/5)。第一次跑 spec 时tool_choice的线程化幸免——替换旧拒绝测试时丢了唯一断言,测试补回(b8a7d24)后该突变才被捕获。 - 真实 Codex CLI 修复后没有重跑(见「尚未验证」)。
3. /v1/chat/completions:server-executed / hosted 工具类型显式 400
chat 面此前接受 {type:'web_search'} / {type:'code_interpreter'} 等并回 200、工具凭空消失,而 /v1/responses 对同样的载荷回 400(evidence/DataAuditTools/findings.md F3)。
- 谓词抽到
src/server-side-tools.js(新):UNBRIDGED_SERVER_SIDE_TYPES、isServerSideToolType(带日期后缀的快照同样拒)、isHostedSearchType(版本化家族)、forEachTool(走tools/children/functions/items四个 namespace 键)、两面的错误工厂,以及firstUnsupportedServerToolType。responses.js改为 import,错误码与消息逐字不变。 - chat 的门在
_handleChatCompletionsInner顶部、提示词组装、缓存/账号工作和任何上游调用之前:400 invalid_request_error、code=unsupported_server_side_tool、param=tools;消息区分 hosted 与 server-executed,并点名function逃生口。 - 只看
tool.type:{type:'function', function:{name:'apply_patch'}}与{type:'custom', name:'apply_patch'}照常 200,函数形式仍进上游工具前导(有测试)。 - 未知垃圾类型保留旧的 200,但不再静默:记
dropping unknown tool type <type>。wire builder 未动(devin-connect.js:1740仍过滤非 function 的 ToolDef,纵深防御)。 - 测试:
test/chat-hosted-tool-gate.test.js(新,9 条:8 类 × 流/非流、8 个日期快照、4 个 hosted/版本化类型、四个 namespace 键、混合 function+server、两个apply_patch反例、未知类型日志);验收 1,131/1,131。
4. Claude Code 2.1.306:CL1 / CL2 / CL3
- CL2
context_management(2.1.306 新增,betacontext-management-2025-06-27)此前被静默丢弃。观测到的clear_thinking_20251015 keep:"all"每轮都接受(400 会让真实客户端不可用),且历史翻译器本来就不转发传入的 thinking 块;真正的裁剪请求记日志。clear_tool_uses_20250919与未知 edit 类型回400 unsupported_parameter并点名要删什么;形状错误回400 invalid_request_error。该字段从不转发上游。 - CL3
defer_loading此前被忽略,延迟占位符被当成普通可调用工具转发。无参数声明的延迟工具(Claude Code 的DeferredToolPlaceholder,"never call this tool")现在被丢弃并计为 honored;带 schema 的延迟工具仍整份转发(上游没有工具加载协议,丢掉 schema 会让工具不可调用),但计toolDeferLoadingIgnored并每请求记tool_defer_loading_ignored——偏差不再静默。defer_loading本身从不转发。 - CL1
signature:""是 #278 /b60ec3d的既定行为(严格客户端要求键存在,""能解析),不是缺陷:抓包里 2.1.306 在 11/11 次交换上都接受它(7/7 个 thinking 起点形状逐字为{"type":"thinking","thinking":"","signature":""},0 个signature_delta,后续请求回放同样 200)。本上游产生不了真签名,reasoning_signature的向前兼容路径保留;精确 wire 形状由测试钉住。 - 测试:CL 套件 141/141;全量
6,725 tests / 6,628 pass / 0 fail / 97 skip(evidence/ClaudeCompatGaps.md)。
5. usage 口径:cache-write 计入输入,session-id 来源可见
- O2:omp 在 2/7 个请求上看到约 11.5k 提示词只报
prompt_tokens=3。那些是前缀缓存写轮(fresh=3, cache_write=11329),不是重试。normalizeConnectUsage现在prompt_tokens = fresh + cache_read + cache_write,total = prompt + completion,总是输出prompt_tokens_details,并新增显式fresh_input_tokens桶,使 Anthropic 形状的消费方(messages.js)、成本核算(auth.js)和 dashboard 统计不重复计写;Cascade 保留 #118 的拆分。 - O3:连续性本就是 opt-in,抓包里是关的。
DEVIN_CONNECT_SESSION_REUSE关闭时记录"每请求 fresh"的来源,并在 connect runbook 里写明;开关没有翻转。 - O1:保留刻意的点号别名
claude-sonnet-4.6→claude-sonnet-4-6-thinking,验证基础拼写claude-sonnet-4-6仍可达,两处都写进 README 与 runbook。 - 测试:验收 1,023/1,023;全量
6,624 pass / 0 fail / 97 skip(GOAL 矩阵记录,见「门禁」)。
6. 别名 / 方言:整类修复,不是逐个字符串
来源 evidence/AliasResolverFix.md(别名矩阵审计的 S2–S7 结论 + 复审 P2-1/P2-2,逐条前后探针):
| 项 | 缺陷 | 修法 | 判定 |
|---|---|---|---|
| S3 | 提供方前缀剥离 /^[a-z]+\// 匹配不了 x-ai/、z-ai/,所有带连字符前缀的拼写每次查找都失败 |
放宽到 /^[a-z0-9][a-z0-9-]*\//,仍只剥一段 |
修好,突变捕获 |
| S4 | 紧凑拼写 gpt5.x / gemini3.x 没有映射行 |
查表前在分隔符处拆开紧凑版本号,不新增映射行 | 修好,突变捕获(含过度覆盖) |
| S5 | enum 判定跑在原始名字上,小写/带前缀的 MODEL_* 两条路都不匹配 |
在规范化键上判 enum,查大写形式,保留目录存在性门 | 修好,突变捕获(含门移除) |
| S2 | 家族正则测字面拼写,点号与紧凑名漏掉家族 | pickToolDialect 顶部把点号折叠、紧凑版本拆一次,所有家族规则测这个键 |
修好,突变捕获 |
| S7 | swe-1-5 → MODEL_SWE_1_5_SLOW,但别名落到 kimi_k2 与自己的 openai_json_xml |
两个 SWE-1.5 enum 读成家族拼写 | 修好,突变捕获——方向是推断,见原证据 |
| P2-2 | 方言的前缀剥离比解析器宽(接受 _),会为解析器已降级的名字选方言 |
用与解析器完全相同的规则 | 修好,突变捕获 |
| P2-1 | 注释声称前缀永不改变上游模型 | 改正注释;不对称性由测试钉住 | 只改文档 |
| 残留 | 裸 grok-4-5 / inkling 400 而同族可用 |
补两个裸别名(快照里 -medium 目标存在) |
修好,突变捕获 |
同一轮还修了:swe-2-0-{medium,high,max} 的破折号 effort 拼写(冷启动曾降级免费档、live 目录下回 model_not_found)、紧凑 gpt5-6-luna、extractImages 接受大写 scheme(HTTPS:// 此前静默跳过)、以及方言判定先剥提供方前缀。全部有红先行测试;test/mutations/alias-resolver-systemic.json 与重钉的 connect-catalog-hotfix.json 钉住方向。
7. /v1/responses 与 Gemini 的参数保真;metadata 回声与持久化
- F1
/v1/responses的parallel_tool_calls现在真正转发(配合 §2 的 chat 策略);F2 GeminigenerationConfig.candidateCount、F5generationConfig.topK不再被丢。验收 313/313(test/responses.test.js+test/gemini*.test.js+test/response-store*.test.js),红先行 9 条失败。 - F4
metadata:normalizeResponseMetadata()(导出)按 OpenAI 契约校验——扁平 map、≤16 对、键 ≤64 字符、值 ≤512 字符——违反时400 invalid_request_error且param:'metadata';null与{}都表示"没有"(POST 与 GET 一致);值从不进日志。非流式响应体、每个流式response.*帧、GET /v1/responses/{id}都回声;存储侧新增有界槽(同样的上限、跳过__proto__),metadataBytes()计入条目的bytes,与其它保留字节一样算进租户份额和全局预算。 - 测试:
responses.test.js+response-store*.test.js207/207(新增 8 条 metadata 测试);四个 responses 基线随测试数 +8 重测:68 / 83 / 70 / 73,0 drift。
8. 门禁:mutation 基线默认按变更域测量,release 全量
scripts/spec-baseline-check.mjs 默认只测这次 diff 可能移动过的 spec(spec 文件本身或其钉住的测试文件变了);--all 是 release 门(gate:release 与发布 workflow 用)。旧的"任何 src//scripts//测试辅助文件变了就全测"既让增量门几乎每个分支都付全量成本(约 100 秒),又没覆盖它想覆盖的失效——过期钉子是 spec 自己测试清单的属性。
真实仓库证明(不是只测 fixture):把 responses-post-tool-text-item.json 的钉从 82 改成 83 → DRIFT … exit=1;还原 → 4 spec(s) measured, 0 drift, 0 untrustworthy;release 域能测到 diff 从未碰过的 spec。wiring 由 test/local-gate.test.js 与 test/mutate-verify-harness.test.js 钉住(该文件在本机声明为 inert,见「门禁」)。
9. 社区 PR #281:Claude Code 子代理的内容策略
合并 e399c15 / 提交 3e3f21a(作者 PsychMing)。症状:每个 Claude Code 子代理(Agent 工具:general-purpose、Explore…)都收到 permission_denied / 400 "blocked by our content policy",而同代理的主会话正常。规则 a8-cc-subagent-emoji 把 "…MUST avoid using emojis." 重写成 "should",不挂在默认关闭的 (cc) 块后面——被门控的规则会让所有默认部署的子代理继续失败。线上二分:整句作为全部系统提示即被拒,近似变体通过,整份被拒请求只把 MUST 换成 should 后 3/3 通过。测试钉住重写、幂等、近似变体不动、关开关。
10. 查漏补缺与真实客户端(证据,不改产品代码)
evidence/HistoryGapAudit/gaps.md:语料 183 issues / 89 PR(快照 2026-10-17;54 merged / 31 closed-unmerged / 4 open),今天线上仍开 7 个(#208 #236 #239 #245 #250 #262 #273);15 个非完成关闭(13 NOT_PLANNED + 2 DUPLICATE);[UNVERIFIED]marker 78 处 / 24 文件(是下限,不是清单)。留下的代码级开放项:#149 免费档基表主张、#159reason:'success'被忽略、supports_images宣称但未读。evidence/ClientSandboxes.md:47 次真实客户端交换(29 Codex + 11 Claude + 7 omp),其中 46 次过 wire 校验、0 违规;三家各 3/3 任务通过;1 个客户端级破坏缺陷(C1,§2 已修)+ O1/O2/O3(§4、§5)。evidence/PlannerVisionProbe.md:30 次线上 A/B 推翻了"planner_mode=NO_TOOL不启用视觉"的说法——planner_mode=3在线上确认,图片字节仍在 field 6,且模型确实读图(claude-haiku-5-5-medium3/3、gemini-3-6-flash-medium3/3、gpt-5.5-xhigh3/3,均带声明工具);绿色 PNG 在两种模式下都答 Green,无图控制 4/4 答"看不到图"。无代码改动;src/windsurf.js:361的注释读起来像 NO_TOOL 会丢视觉,实际不会。- kimi 断供:上游 WorkBuddy abort → hub 502,只能缓解(短轮次、重试、新会话),不是本版修复。
门禁
(本表每一行都写出来源;[UNVERIFIED] 表示至今没有任何测量。)
| 项 | 命令 | 结果 | 来源 |
|---|---|---|---|
| spec 静态一致性 | node scripts/spec-static-check.mjs |
PASS — 92 份 spec / 822 条突变,anchor 各命中一次 | 本版运行(ls test/mutations/*.json | wc -l = 92,逐 spec 求和 = 822) |
| Windows 本地门禁(incremental) | GATE_INERT_SKIP_PATHS="test/wire-byte-identity.test.js;test/devin-connect-relogin-live.test.js;test/mutate-verify-harness.test.js;test/self-update-untracked-safety.test.js;test/detach-soak.test.js;test/detach-ls-pool-pressure.test.js" npm run gate |
PASS — 6,674 pass / 0 fail / 97 skip(551/551 文件实测;6 个 inert 文件,6 条声明;摘要逐字见下) | 本版运行 |
| mutation baseline(变更域) | node scripts/spec-baseline-check.mjs |
exit 0 — no affected specs; no measurement claimed(本版没有移动任何 spec 或其钉住的测试文件) |
本版运行 |
| docs 一致性 | node --import ./test/setup-env.mjs --test test/docs-consistency-guard.test.js |
PASS — 44/44;§0 的 tag/package 行、214 份说明计数、版本链接全部核对 | 本版运行 |
| Linux(HMC)Node 24.21.0 | npm run test:release |
6,772 tests / 6,767 pass / 0 fail / 5 skip | GOAL 矩阵 @ e3cc399 |
| Linux(HMC)Node 22.23.4 | npm run test:release |
6,772 tests / 6,767 pass / 0 fail / 5 skip | GOAL 矩阵 @ e3cc399 |
| Windows 合并树(前序全量) | npm run gate |
PASS — 6,674 pass / 0 fail / 97 skip(551 文件) | GOAL 矩阵 @ e3cc399 |
| wire vs v3.10.1 | WIRE_BASE_TREE=/tmp/wave3-wire-base WIRE_BASE_REQUIRED=1 WIRE_BASE_TAG=v3.10.1 WIRE_BASE_SHA=b00b3b9a… node --import ./test/setup-env.mjs --test test/wire-byte-identity.test.js |
PASS — 270/270,0 mismatch;WIRE_PROVENANCE_COMPARED {"compared":270,"expected":270,"base_head":"b00b3b9a…"},tag_ref_verified true,tested/base 两侧 clean |
HMC @ e3cc399(node v24.21.0);TAP 1/1 pass,log sha256 84496fb1…(10,386 B) |
| 全量 mutation sweep(92 份 spec) | node scripts/mutation-sweep.mjs --report-dir /tmp/wa-sweep-e3cc399 |
PASS — 92/92 spec;822 executed = 808 caught + 14 条声明的 expectCaught:false,0 意外幸存、0 drift、0 untrustworthy;wall 2,953 s(49 m 13 s) |
HMC @ e3cc399(node v24.21.0);sweep.out sha256 c20e7bac…;逐 spec 执行数 = 其 JSON mutations.length,幸存数 = 其声明数 |
本表数字产生于本版提交前的同一棵树(e3cc399;wire 与 mutation 两行在 HMC 上对该 commit 的 clone 实测,两侧 git status --porcelain 均为空);发布提交只回填这张表。npm run gate 的摘要逐字如下:
PASS spec-static-check exit=0
PASS spec-baseline-check exit=0
PASS secret-scan exit=0
PASS test:release exit=0 — 6674 pass / 0 fail / 97 skip (551 files) — 6 inert file(s), 6 declared
PASS git diff --check exit=0
PASS git diff --cached --check exit=0
SKIP mutation EXECUTION — this gate checks spec shape, anchors and baseline values, but does not run the mutants themselves; the Linux full-gate recipe runs the sweep.
INCREMENTAL GATE: PASS exit=0
TEST_SHARD_SUMMARY {"files":551,"measuredFiles":551,"tests":6771,"pass":6674,"fail":0,"skipped":97,"cancelled":0,"todo":0}
已知 / 尚未验证
- wire 已实测,但只覆盖请求体:
e3cc399对v3.10.1(b00b3b9,annotated tag 已验证)270/270 逐字节一致,0 mismatch(HMC,见门禁表)。比较范围是 9 组历史 × 5 条 prepare 路由 × 2 native × 3 环境 = 270 个 protobuf 请求体;HTTP/Connect 帧与响应流按设计不在内。本机(Windows)无WIRE_BASE_TREE时该测试按设计跳过,已声明为 inert。 - 全量 mutation sweep 已实测:92/92 spec、822 条全部执行,808 被捕获 + 14 条 spec 自带的
expectCaught:false,0 drift、0 untrustworthy(HMC,2,953 s,见门禁表)。14 条幸存者是各 spec 文档化的不可达/等价分支,不是漂移。 - 真实 Codex CLI 修复后未重跑:客户端级 PASS 属于修复前基线(只有打开
DEVIN_CONNECT_TOOL_CHOICE=1才 3/3);修复后的 200 由探针与单元测试证明,未用真实 CLI 复验。 - S7 的修法方向是推断:
swe-1-5该落MODEL_SWE_1_5_SLOW还是kimi_k2只有快照证据,原证据里已标注。 - 托管 CI / 发布产物未验证:本版在本地分支准备,tag、CI、HMC 部署与 cosign 资产都要在合并后另行执行与核对。
- 本机门禁的 6 条 inert 声明:Windows 没有
/usr/bin/git,test/wire-byte-identity.test.js无 wire 基线,test/devin-connect-relogin-live.test.js未武装,detach-soak/detach-ls-pool-pressure在本机整文件跳过——这些都按GATE_INERT_SKIP_PATHS声明,不当作绿。 - 明文
http:图片、server-executed 工具类型的 400 仍是有意行为:依赖"静默忽略"的客户端会开始收到错误。