fix(gateway): skip client header passthrough on OAuth mimicry path
Root cause of persistent third-party detection: sub2api's buildUpstreamRequest transparently forwards client headers via allowedHeaders whitelist (addHeaderRaw) before applying mimicry overrides. When third-party clients (opencode, etc.) send their own anthropic-beta / user-agent / x-stainless-* / x-claude-code-session-id values, these get appended to the request alongside our injected headers, creating an inconsistent header set that Anthropic detects. Parrot's build_upstream_headers constructs exactly 9 headers from scratch and never forwards anything from the client. This is why 'same opencode version, some users work some don't' — different opencode configs/versions send different header combinations. Fix: when tokenType=oauth and mimicClaudeCode=true, skip the client header passthrough loop entirely. The subsequent applyClaudeCodeMimicHeaders + ApplyFingerprint + beta merge pipeline constructs all necessary headers from our controlled values. Also: remove systemIncludesClaudeCodePrompt gate — OAuth accounts now unconditionally rewrite system (even if client already sent a Claude Code-style prompt), ensuring billing attribution block is always present.
This commit is contained in:
@@ -1197,8 +1197,7 @@ func (s *GatewayService) applyClaudeCodeOAuthMimicryToBody(
|
|||||||
}
|
}
|
||||||
|
|
||||||
systemRewritten := false
|
systemRewritten := false
|
||||||
if !strings.Contains(strings.ToLower(model), "haiku") &&
|
if !strings.Contains(strings.ToLower(model), "haiku") {
|
||||||
!systemIncludesClaudeCodePrompt(systemRaw) {
|
|
||||||
body = rewriteSystemForNonClaudeCode(body, systemRaw)
|
body = rewriteSystemForNonClaudeCode(body, systemRaw)
|
||||||
systemRewritten = true
|
systemRewritten = true
|
||||||
}
|
}
|
||||||
@@ -4163,9 +4162,13 @@ func (s *GatewayService) Forward(ctx context.Context, c *gin.Context, account *A
|
|||||||
shouldMimicClaudeCode := account.IsOAuth()
|
shouldMimicClaudeCode := account.IsOAuth()
|
||||||
|
|
||||||
if shouldMimicClaudeCode {
|
if shouldMimicClaudeCode {
|
||||||
|
// 与 Parrot 对齐:OAuth 账号无条件重写 system(即使客户端已发了 Claude Code
|
||||||
|
// 风格的 system prompt)。原因:第三方工具(opencode 等)会发 "You are Claude
|
||||||
|
// Code..." system prompt 但缺少 billing attribution block,导致 Anthropic
|
||||||
|
// 检测到"有 CC prompt 但无 billing block"的不一致而判为 third-party。
|
||||||
|
// Parrot 的 transform_request 从不检查客户端 system 内容,直接覆盖。
|
||||||
systemRewritten := false
|
systemRewritten := false
|
||||||
if !strings.Contains(strings.ToLower(reqModel), "haiku") &&
|
if !strings.Contains(strings.ToLower(reqModel), "haiku") {
|
||||||
!systemIncludesClaudeCodePrompt(parsed.System) {
|
|
||||||
body = rewriteSystemForNonClaudeCode(body, parsed.System)
|
body = rewriteSystemForNonClaudeCode(body, parsed.System)
|
||||||
systemRewritten = true
|
systemRewritten = true
|
||||||
}
|
}
|
||||||
@@ -5766,7 +5769,12 @@ func (s *GatewayService) buildUpstreamRequest(ctx context.Context, c *gin.Contex
|
|||||||
setHeaderRaw(req.Header, "x-api-key", token)
|
setHeaderRaw(req.Header, "x-api-key", token)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 白名单透传headers(恢复真实 wire casing)
|
// 白名单透传 headers
|
||||||
|
// OAuth mimicry 路径:跳过客户端 header 透传,与 Parrot 对齐。
|
||||||
|
// Parrot 的 build_upstream_headers 只发 9 个精确 header,不透传任何客户端 header。
|
||||||
|
// 透传客户端 header 会引入不一致的 x-stainless-* / anthropic-beta / user-agent /
|
||||||
|
// x-claude-code-session-id 等值,和我们注入的伪装 header 冲突,被 Anthropic 判 third-party。
|
||||||
|
if !(tokenType == "oauth" && mimicClaudeCode) {
|
||||||
for key, values := range clientHeaders {
|
for key, values := range clientHeaders {
|
||||||
lowerKey := strings.ToLower(key)
|
lowerKey := strings.ToLower(key)
|
||||||
if allowedHeaders[lowerKey] {
|
if allowedHeaders[lowerKey] {
|
||||||
@@ -5776,6 +5784,7 @@ func (s *GatewayService) buildUpstreamRequest(ctx context.Context, c *gin.Contex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// OAuth账号:应用缓存的指纹到请求头(覆盖白名单透传的头)
|
// OAuth账号:应用缓存的指纹到请求头(覆盖白名单透传的头)
|
||||||
if fingerprint != nil {
|
if fingerprint != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user