Repository navigation
fix(model-gateway): allow the betas the pinned Claude and OpenCode CLIs send for Claude models - #115
Conversation
… CLI sends Claude Code 2.1.303, driven through the pinned ACP adapter with claude-haiku-5-5, adds per-turn-control-2026-07-01 to its first Messages request. The gateway refused it as an unknown Anthropic extension, so every such run ended with agent_turn_failed before any upstream call.
The pinned OpenCode 1.19.3 declares thinking-binding-controls-2026-08-01 and structured-outputs-2025-11-13 when it requests a claude-* model over Anthropic Messages through @ai-sdk/anthropic. Both were refused as unknown extensions, failing the turn before any upstream attempt. The test now replays each pinned CLI's exact first-request header.
HappyAuratus
left a comment
There was a problem hiding this comment.
已审查 head b4c5e23097f4215490ee6427ffeb28f0c247825a(base ff72afa0d8dbebe77ec10231936221e17945c048),无阻塞发现,Approve。
生产改动仅向既有 Anthropic beta 白名单添加三个固定 token。已沿 runtimeModelRoute → ModelHandler → anthropicProtocolHeaders → 上游请求构造核对:未知/重复值拒绝、固定协议版本、冻结模型、认证替换、限定目标及转发生命周期均未被放宽。四个改动文件均为交付物,中英文模型网关文档与新增值一致。
本地验证:新增 Claude/OpenCode 首请求 beta 组合、既有 query/header/tool SSE、未知协议面拒绝与冻结模型目录测试均通过;go run ./cmd/checkformat 和 git diff --check 通过。全包 go test -count=1 ./internal/modelgateway/ 在本 Windows 环境有三个既有 egress 测试因 staticAddresses 地址策略失败;已在 base 对相同三个测试复现。PR 的 Backend CI 为 SUCCESS,包含 format、lint、race、PostgreSQL/S3 测试及 build。未重跑真实 CLI/供应商、完整 PostgreSQL 验收,本结论不替代 #370 的正式验收。
非阻塞建议:
internal/modelgateway/anthropic_test.go:19:新增两个 CLI 用例时替换掉了原TestPinnedClaudeAgentSDKCapabilities,使原先structured-outputs-2025-12-15与thinking-display-updates-2026-08-18的 SDK 组合不再有直接转发回归覆盖。建议在表里保留第三个 SDK 用例。internal/modelgateway/README.md:26与README.en.md:28仍说白名单只有九项;当前已有十二项。建议同步中英文说明,直接引用策略表而不固定数量可避免再次漂移。
wanglongan587
left a comment
There was a problem hiding this comment.
审查版本:b4c5e23097f4215490ee6427ffeb28f0c247825a(base ff72afa0d8dbebe77ec10231936221e17945c048,对照 upstream/main 66533b4f592aa24ef654b18fdd2ff68907504302)。结论:Approve,无阻塞发现。与 @HappyAuratus 在同一 head 上的 Approve 一致,本次是在同一 head 上的独立复核。
已核对
- 生产改动只有
internal/modelgateway/anthropic.go的三个新 token。anthropicProtocolHeaders仍是逐 token 的受限白名单:未知值、重复值、空值、超过 1024 字节都拒绝;runtimeModelRoute的路径与 query 约束、冻结模型目录、认证替换、出网检查和转发生命周期都没有放宽。 - 与当前
upstream/main(66533b4)对比:internal/modelgateway/和docs/models/自 base 起没有其它提交;本地git merge-tree模拟与 main 合并无冲突(MERGEABLE/CLEAN)。 - 文档:中英文 model-gateway 文档的改动与代码一致,四个改动文件都是交付物。
- ADR:PR 描述写明不涉及 ADR 状态变化。我在 specs
upstream/main(0a100a6)上检索anthropic-beta、beta token、受限白名单,没有任何 ADR 列举这份白名单,因此本 PR 不需要规格落地 PR,描述的说法成立。 - CI:
check通过(run 39443434741)。 - 本地验证(worktree 检出
b4c5e230):gofmt -l internal/modelgateway无输出;go build ./internal/modelgateway/通过;TestPinnedCLIClaudeModelBetas、TestClaudeProtocolRejectsUnknownSurface、TestClaudeMessagesQueryHeadersAndToolStream通过。 - 反向核对:临时删掉这三个 token 后,
TestPinnedCLIClaudeModelBetas的claude与opencode两个子用例都以status=400 calls=0失败;恢复后通过。这条测试确实保护新增值,与 PR 描述一致。 - 全包
go test -count=1 ./internal/modelgateway/在本 Windows 环境有三个egress_test.go用例因 staticAddresses 地址策略失败;在 baseff72afa0上用相同命令复现了同样三个失败,与本次改动无关。
非阻塞(建议在 #370 的下一个 PR 一并处理)
internal/modelgateway/README.md:26、README.en.md:28仍写 beta token“九项”(英文 “the nine in anthropic.go”),本 PR 之后是十二项。建议改成直接引用anthropic.go的策略表、不再固定数量,避免下次再漂移。internal/modelgateway/anthropic_test.go:19-25的表格替换掉TestPinnedClaudeAgentSDKCapabilities后,structured-outputs-2025-12-15与thinking-display-updates-2026-08-18的 SDK 组合不再有直接转发回归覆盖(两个值仍在白名单里,但没有任何测试固定它们)。建议保留第三个 SDK 用例。anthropic_test.go:22的注释写 “through the pinned ACP adapter”,但按 PR 描述,这份头是“pointing the bundled aarch64 CLI at a local recorder”直连录的,不含 SDK 追加的两项。建议把注释改成 CLI 直连头(或保留 SDK 用例后分别标注),免得后来的人把它当成 adapter 的完整生产头。
以上都不是本次改动的功能问题。本结论只针对 b4c5e230,不替代 #370 第 3 项的真实供应商验收;第 2–4 项仍在等前置与 owner 的开关决定。
|
补充一条同源的非阻塞项(不改变 Approve 结论):上面第 1 条的模块 README 里,除了“九项”之外,同一段还写 “Anthropic 兼容面固定到 Claude Code 2.1.303”( 另外核对过但不需要改动的:替换后的表测试仍覆盖 |
Summary
Claude Code 2.1.303, driven through the pinned
claude-agent-acp0.94.0 withclaude-haiku-5-5, sendsper-turn-control-2026-07-01inanthropic-betaon its first Messages request. The gateway's bounded allowlist did not have it, so the request was refused with400 model_protocol_header_forbiddenbefore any upstream attempt, and the run endedagent_turn_failed.The same rehearsal then showed the pinned OpenCode 1.19.3, requesting
claude-haiku-5-5through@ai-sdk/anthropic, sendingthinking-binding-controls-2026-08-01andstructured-outputs-2025-11-13; both were refused the same way. The allowlist was only ever drawn from the Claude CLI, so this affects OpenCode with any Claude model regardless of route.This adds those three tokens, a test that replays each pinned CLI's exact first-request header, and the matching sentences in both model-gateway docs.
Evidence
API Error: 400 {"code":"model_protocol_header_forbidden",...}and there were nomodel_request_attemptsrows.interleaved-thinking-2025-05-14,thinking-token-count-2026-05-13,context-management-2025-06-27,prompt-caching-scope-2026-01-05,claude-code-20250219,per-turn-control-2026-07-01,mid-conversation-tool-changes-2026-07-01,effort-2025-11-24.completedwith a changed Revision, and released the run Workspace.thinking-binding-controls-2026-08-01,structured-outputs-2025-11-13.TestPinnedCLIClaudeModelBetas(Claude and OpenCode cases) fails withstatus=400 calls=0without the entries and passes with them;go test -count=1 ./internal/modelgateway/passes.Not related to any ADR status change: the policy is the existing bounded allowlist.
Refs ora-space/circus#370
Merge Danger
Door: two-way
Blast Radius: model-gateway
Adds three accepted beta values on the Anthropic Messages route; unknown and duplicate values are still refused.