Skip to content

fix: 对齐 Claude/Codex UA 透传策略并完善 Claude cloaking#205

Merged
Bowl42 merged 3 commits intomainfrom
fix/ua-passthrough-cli-only
Feb 9, 2026
Merged

fix: 对齐 Claude/Codex UA 透传策略并完善 Claude cloaking#205
Bowl42 merged 3 commits intomainfrom
fix/ua-passthrough-cli-only

Conversation

@Bowl42
Copy link
Collaborator

@Bowl42 Bowl42 commented Feb 9, 2026

背景

本 PR 继续对齐 CLIProxyAPI 与 sub2api 的关键行为,重点修正 Claude/Codex 转发时的 User-Agent 与 Claude cloaking 细节。

主要改动

  • UA 透传策略收敛为:仅 CLI 客户端允许透传,非 CLI 一律回退默认 UA。
  • Codex 头部透传中新增对 user-agent 的显式过滤,避免先透传后覆盖导致行为不一致。
  • Claude 客户端识别改为官方版本号模式匹配(claude-cli/x.y.z,大小写不敏感)。
  • Claude cloaking 改为先检测 system 是否已包含 Claude Code prompt,已存在则不重复注入。
  • 保留并强化敏感头过滤(如 host 等)。

测试

  • go test ./internal/adapter/provider/custom ./internal/adapter/provider/codex
  • go test ./...

影响范围

  • Claude custom provider
  • Codex custom provider
  • Codex provider

该改动不涉及凭证获取流程,仅调整请求头与请求体的兼容性/伪装策略。

Summary by CodeRabbit

发布说明

  • Bug 修复

    • 改进了 Claude 系统提示注入与去重逻辑,避免重复注入并在适用场景确保正确注入顺序与内容。
  • 功能改进

    • 优化了 User-Agent 头处理:对命令行客户端的 UA 进行识别并可透传,非 CLI 场景使用默认 UA。
    • 统一并强化了 Cloaking(遮蔽)流程,确保在更多模式下一致应用系统指令处理。
  • 测试

    • 增加并扩展多项针对 UA 透传、默认 UA 行为及系统提示注入的测试用例。

@coderabbitai
Copy link

coderabbitai bot commented Feb 9, 2026

📝 Walkthrough

Walkthrough

将 Codex 和 Claude 提供程序的 User-Agent 处理改为 CLI 感知的动态解析:保留符合 CLI 模式的客户端 UA,否则回退到默认 UA;同时调整头部透传逻辑以显式将 user-agent 标记为系统控制,并增强 Claude 的系统提示注入与检测逻辑以避免重复注入并统一 cloaking 行为。

Changes

Cohort / File(s) Summary
Codex 适配器与头部
internal/adapter/provider/codex/adapter.go, internal/adapter/provider/custom/codex_headers.go, internal/adapter/provider/codex/adapter_test.go, internal/adapter/provider/custom/codex_headers_test.go
新增 resolveCodexUserAgent()isCodexCLIUserAgent(),在上游请求中有条件地保留客户端 CLI 风格的 User-Agent,否则使用默认 codexUserAgent;将 user-agent 加入过滤列表以避免透传覆盖;更新/新增单元测试覆盖这些场景。
Claude 系统提示与 cloaking
internal/adapter/provider/custom/claude_body.go, internal/adapter/provider/custom/claude_body_test.go
添加 claudeCLIUserAgentPattern,重构 checkSystemInstructionsWithMode()、添加 hasClaudeCodeSystemPrompt() 并确保在 cloaking 路径中始终正确注入或保留 Claude Code 系统提示,调整多处逻辑以避免重复注入并改进非严格模式处理;更新与新增测试以反映行为变化。
Claude 头部处理与测试
internal/adapter/provider/custom/claude_headers.go, internal/adapter/provider/custom/claude_headers_test.go
修改 applyClaudeHeaders() 以基于 isClaudeCodeClient() 条件性地透传客户端 CLI User-Agent,否则使用默认 defaultClaudeUserAgent;新增测试验证 CLI/非 CLI UA 的传递规则及 cloaking 相关 header/body 形状。

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Adapter as Provider Adapter
    participant Upstream

    Client->>Adapter: 发起请求(含 User-Agent)
    Adapter->>Adapter: isCodexCLIUserAgent / isClaudeCodeClient 检查 UA
    alt UA 匹配 CLI 模式
        Adapter->>Upstream: 透传客户端 User-Agent(保留 CLI UA)及其 headers/body
    else 非 CLI
        Adapter->>Adapter: 使用默认 codex/claude User-Agent
        Adapter->>Upstream: 发送默认 User-Agent 与其它处理后的 headers/body
    end
Loading

估计代码审查工作量

🎯 3 (中等) | ⏱️ ~22 分钟

可能相关的 PR

推荐审查者

  • awsl233777
  • whhjdi
  • dreamhunter2333

🐰 CLI 轻声来与去,
UA 细辨旧与新,
Codex、Claude 各守位,
系统提示不重生,
请求穿梭笑盈盈 ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.59% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题清晰地总结了变更的主要内容:对齐 Claude/Codex 的 User-Agent 透传策略并完善 Claude cloaking 逻辑。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/ua-passthrough-cli-only

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
internal/adapter/provider/custom/codex_headers_test.go (2)

8-26: 建议使用 t.Run 子测试拆分两个场景

第一个断言使用 t.Fatalf(第 15 行),如果失败会直接终止整个测试函数,导致第二个场景(非 CLI UA 回退)被跳过,降低了调试效率。将两个场景拆分为独立子测试可以保证互不影响。

♻️ 建议重构
 func TestApplyCodexHeadersUserAgentPassthroughOnlyForCLI(t *testing.T) {
-	upstreamReq, _ := http.NewRequest("POST", "https://chatgpt.com/backend-api/codex/responses", nil)
-	clientReq, _ := http.NewRequest("POST", "http://localhost/responses", nil)
-	clientReq.Header.Set("User-Agent", "codex-cli/1.2.3")
-
-	applyCodexHeaders(upstreamReq, clientReq, "token-1")
-	if got := upstreamReq.Header.Get("User-Agent"); got != "codex-cli/1.2.3" {
-		t.Fatalf("expected CLI User-Agent passthrough, got %q", got)
-	}
-
-	upstreamReq2, _ := http.NewRequest("POST", "https://chatgpt.com/backend-api/codex/responses", nil)
-	clientReq2, _ := http.NewRequest("POST", "http://localhost/responses", nil)
-	clientReq2.Header.Set("User-Agent", "Mozilla/5.0")
-
-	applyCodexHeaders(upstreamReq2, clientReq2, "token-1")
-	if got := upstreamReq2.Header.Get("User-Agent"); got != codexUserAgent {
-		t.Fatalf("expected default User-Agent for non-CLI client, got %q", got)
-	}
+	t.Run("CLI UA passthrough", func(t *testing.T) {
+		upstreamReq, _ := http.NewRequest("POST", "https://chatgpt.com/backend-api/codex/responses", nil)
+		clientReq, _ := http.NewRequest("POST", "http://localhost/responses", nil)
+		clientReq.Header.Set("User-Agent", "codex-cli/1.2.3")
+
+		applyCodexHeaders(upstreamReq, clientReq, "token-1")
+		if got := upstreamReq.Header.Get("User-Agent"); got != "codex-cli/1.2.3" {
+			t.Fatalf("expected CLI User-Agent passthrough, got %q", got)
+		}
+	})
+
+	t.Run("non-CLI UA fallback", func(t *testing.T) {
+		upstreamReq, _ := http.NewRequest("POST", "https://chatgpt.com/backend-api/codex/responses", nil)
+		clientReq, _ := http.NewRequest("POST", "http://localhost/responses", nil)
+		clientReq.Header.Set("User-Agent", "Mozilla/5.0")
+
+		applyCodexHeaders(upstreamReq, clientReq, "token-1")
+		if got := upstreamReq.Header.Get("User-Agent"); got != codexUserAgent {
+			t.Fatalf("expected default User-Agent for non-CLI client, got %q", got)
+		}
+	})
 }

28-37: 补充大小写变体的测试用例以验证大小写不敏感匹配

isCodexCLIUserAgent 函数(第56-59行)确实使用 strings.ToLower() 实现了大小写不敏感的匹配。当前测试套件应补充对大小写变体的覆盖,例如 "Codex-CLI/1.2.3""CODEX-CLI/1.0.0",以显式验证大小写不敏感行为工作正常。同时建议也添加 "codex_cli_rs/" 前缀的测试用例,以完整覆盖所有 CLI UA 变体。

internal/adapter/provider/custom/claude_body.go (2)

341-371: 非 strict 模式下仅保留 type=text 的系统元素,其他类型会被静默丢弃。

Lines 355-360 中的 ForEach 只拷贝 type == "text" 的元素到新数组。如果原始 system 数组中包含带有 cache_control 的 text block,part.Raw 会保留完整 JSON 所以没问题。但如果存在非 text 类型元素(虽然目前 Claude API 的 system 字段主要是 text blocks),它们会被丢弃。

如果当前使用场景确实只有 text blocks,可以暂不处理,但建议添加注释说明这一行为。


346-346: 系统提示 JSON 通过字符串拼接构造,存在 JSON 注入隐患。

claudeCodeInstructions 通过字符串拼接 claudeCodeSystemPrompt 常量构建 JSON。当前常量不含需要转义的字符("\、换行等),所以是安全的。但如果未来有人修改这个常量加入此类字符,会产生无效 JSON 且无运行时错误提示。

建议使用 sjsonjson.Marshal 构建,以防止未来修改引入问题:

♻️ 建议修改
-	claudeCodeInstructions := `[{"type":"text","text":"` + claudeCodeSystemPrompt + `"}]`
+	promptBlock, _ := sjson.Set("{}", "type", "text")
+	promptBlock, _ = sjson.Set(promptBlock, "text", claudeCodeSystemPrompt)
+	claudeCodeInstructions := "[" + promptBlock + "]"
📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ab56120 and 39c947f.

📒 Files selected for processing (7)
  • internal/adapter/provider/codex/adapter.go
  • internal/adapter/provider/codex/adapter_test.go
  • internal/adapter/provider/custom/claude_body.go
  • internal/adapter/provider/custom/claude_body_test.go
  • internal/adapter/provider/custom/claude_headers_test.go
  • internal/adapter/provider/custom/codex_headers.go
  • internal/adapter/provider/custom/codex_headers_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • internal/adapter/provider/custom/codex_headers.go
  • internal/adapter/provider/codex/adapter.go
  • internal/adapter/provider/codex/adapter_test.go
🧰 Additional context used
🧬 Code graph analysis (1)
internal/adapter/provider/custom/claude_body_test.go (1)
internal/domain/model.go (1)
  • ProviderConfigCustomCloak (38-47)
🔇 Additional comments (9)
internal/adapter/provider/custom/claude_headers_test.go (1)

102-129: 测试覆盖全面,LGTM!

三个子场景(CLI 透传、非 CLI 回退、非官方 CLI 回退)清晰地覆盖了 UA 透传策略的关键分支。

internal/adapter/provider/custom/claude_body.go (4)

28-30: CLI UA 正则匹配逻辑清晰,LGTM。

(?i)^claude-cli/\d+\.\d+\.\d+ 锚定了起始位置,要求 semver 格式版本号,case-insensitive 匹配。这正确地拒绝了 claude-cli/dev 等非官方版本,同时允许 Claude-CLI/2.1.17 等大小写变体。


373-404: hasClaudeCodeSystemPrompt 实现全面,覆盖了多种 system 字段形状。

函数正确处理了 array(含 object 元素和 string 元素)、string、object 三种 system 字段类型,使用 strings.TrimSpace 后精确匹配避免了空白字符干扰。early guard 有效防止了重复注入。


81-83: 移除 Haiku 模型的跳过逻辑,统一 cloaking 行为,LGTM。

所有 cloaked 请求现在都会注入 Claude Code 系统提示,这与 sub2api 的行为对齐,消除了之前特定模型的特殊路径。


97-100: isClaudeCodeClient 使用编译好的正则 + TrimSpace,逻辑正确。

internal/adapter/provider/custom/claude_body_test.go (4)

124-129: 新增的 shouldCloak 边界用例覆盖了关键分支,LGTM。

claude-cli/dev 验证了非 semver 格式被正确拒绝,Claude-CLI/2.1.17 验证了大小写不敏感匹配,与 claudeCLIUserAgentPattern(?i) 标志一致。


132-143: 测试期望已正确更新,与 Haiku 模型不再跳过 cloaking 的逻辑变更一致。


371-386: TestNoDuplicateSystemPromptInjection 验证了 hasClaudeCodeSystemPrompt 的去重逻辑。

测试在 system 数组中将 Claude Code prompt 放在第二个位置(而非第一个),有效验证了无论 prompt 在数组中的位置如何,都能被正确检测到并跳过重复注入。

需要注意的是,此测试仅验证了不重复注入,但没有验证已有 prompt 的位置是否保持不变。当前行为(保留原始顺序)是合理的——如果 prompt 已经存在就不做任何修改。


415-430: 非 strict 模式下保留原始 system 字符串的测试,验证了之前 review 中发现的 bug 修复。

测试正确验证了两个关键行为:

  1. Claude Code 提示词被前置为 system[0]
  2. 原始字符串 "Keep this instruction" 被转换为 text block 并保留在 system[1]

这与 claude_body.go 中 Lines 365-368 的新增字符串处理逻辑完全对应。

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@internal/adapter/provider/custom/claude_body.go`:
- Around line 341-367: In checkSystemInstructionsWithMode, non-strict handling
currently only merges when system.IsArray(), so when "system" is a string its
original content is overwritten; update the function to detect when
system.IsString() (or otherwise non-array/non-empty) and build a new array that
prepends the claudeCodeInstructions while preserving the original string as a
text element, then write that array back with sjson.SetRawBytes (just like the
array branch does); keep the existing array branch logic for IsArray() and the
strictMode branch unchanged, and reference function
checkSystemInstructionsWithMode, the gjson.GetBytes result "system", and
sjson.SetRawBytes when implementing this fix.

In `@internal/adapter/provider/custom/claude_headers_test.go`:
- Around line 152-156: The test's userIDPattern in claude_headers_test.go is too
permissive (uses [\w-]+) and should match the production UUID v4 session format;
update the regex used by userIDPattern to enforce the UUID v4 pattern used in
claude_body.go (use [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}
for the session portion after the fixed "user_[a-fA-F0-9]{64}_account__session_"
prefix), so the test validates the same strict format as the production
validation.

In `@internal/adapter/provider/custom/codex_headers.go`:
- Around line 56-59: The isCodexCLIUserAgent function uses strings.HasPrefix(ua,
"codex-cli") which is too broad; change the prefix check to require the
separator by using "codex-cli/" instead (i.e., replace "codex-cli" with
"codex-cli/") so it matches user agents like "codex-cli-custom/1.2.3" without
accidentally matching longer words like "codex-climax"; update this in the
isCodexCLIUserAgent implementation shown and make the same change in the
duplicate implementation in the other codex adapter file (the function named
isCodexCLIUserAgent in both locations).
🧹 Nitpick comments (3)
internal/adapter/provider/custom/codex_headers.go (1)

47-59: resolveCodexUserAgentisCodexCLIUserAgentcustomcodex 两个包中完全重复。

internal/adapter/provider/codex/adapter.go 的 Lines 722-734 中定义了逻辑完全相同的 resolveCodexUserAgentisCodexCLIUserAgent(只是引用的默认 UA 常量不同)。建议将 isCodexCLIUserAgent 提取到共享的 utility 包中,避免 DRY 违反。resolveCodexUserAgent 可接受默认 UA 作为参数来消除剩余的重复。

♻️ 建议的重构方案

例如,在共享包中定义:

// pkg/uautil/codex.go
package uautil

import "strings"

func IsCodexCLIUserAgent(userAgent string) bool {
	ua := strings.ToLower(strings.TrimSpace(userAgent))
	return strings.HasPrefix(ua, "codex_cli_rs/") || strings.HasPrefix(ua, "codex-cli/")
}

然后两个包均引用此共享函数。

internal/adapter/provider/custom/claude_headers.go (1)

106-127: ensureHeader 的三层优先级(source > target existing > default)与 codex_headers.go 中的 ensureCodexHeader 语义不同。

Claude 的 ensureHeader 支持三层优先级,而 Codex 的 ensureCodexHeader(同一个 custom 包内)只有两层。这在当前场景下各自适用,但命名相近易产生混淆。如果未来有人误用,可能导致 header 覆盖行为不符预期。可考虑在函数名或文档中体现这种差异。

internal/adapter/provider/custom/claude_headers_test.go (1)

131-162: 整体 cloaking 测试结构良好,端到端验证了 sub2api 兼容性。

测试完整覆盖了以下关键路径:

  • 非 CLI 客户端经 cloaking 后上游 UA 变为 claude-cli/x.y.z 格式
  • 必要头部(X-AppAnthropic-BetaAnthropic-Version)均被设置
  • metadata.user_id 符合预期格式
  • 系统提示词包含 Claude Code 标识

一个小建议:Lines 146-150 循环中使用 t.Fatalf 会在首个缺失头部时终止,改用 t.Errorf 可以一次暴露所有缺失项,提升调试效率。

♻️ 建议使用 t.Errorf 替代 t.Fatalf 以显示所有缺失头部
 	for _, key := range []string{"X-App", "Anthropic-Beta", "Anthropic-Version"} {
 		if strings.TrimSpace(upstreamReq.Header.Get(key)) == "" {
-			t.Fatalf("expected %s to be set", key)
+			t.Errorf("expected %s to be set", key)
 		}
 	}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3bcd0c6 and ab56120.

📒 Files selected for processing (8)
  • internal/adapter/provider/codex/adapter.go
  • internal/adapter/provider/codex/adapter_test.go
  • internal/adapter/provider/custom/claude_body.go
  • internal/adapter/provider/custom/claude_body_test.go
  • internal/adapter/provider/custom/claude_headers.go
  • internal/adapter/provider/custom/claude_headers_test.go
  • internal/adapter/provider/custom/codex_headers.go
  • internal/adapter/provider/custom/codex_headers_test.go
🧰 Additional context used
🧬 Code graph analysis (2)
internal/adapter/provider/codex/adapter_test.go (2)
internal/adapter/provider/codex/adapter.go (1)
  • CodexAdapter (55-61)
internal/adapter/provider/codex/settings.go (1)
  • CodexUserAgent (29-29)
internal/adapter/provider/codex/adapter.go (1)
internal/adapter/provider/codex/settings.go (1)
  • CodexUserAgent (29-29)
🔇 Additional comments (14)
internal/adapter/provider/custom/codex_headers.go (1)

22-44: Header 设置逻辑清晰,passthrough + 显式控制 UA 的策略正确。

applyCodexHeaders 先通过 copyCodexPassthroughHeaders 复制客户端 header(已排除 user-agent),再通过 resolveCodexUserAgent 解析并设置 UA,避免了先复制再覆盖的不一致问题。整体逻辑合理。

internal/adapter/provider/codex/adapter.go (1)

660-711: applyCodexHeaders 整体逻辑清晰,header 过滤与 UA 解析策略与 custom 包保持一致。

passthrough 逻辑正确跳过了 codexFilteredHeaders 中的 header,UA 通过 resolveCodexUserAgent 动态解析,auth 在有 accessToken 时正确覆盖。ensureHeader 的优先级(客户端 > 默认)合理。

internal/adapter/provider/codex/adapter_test.go (2)

81-96: 新测试覆盖了非 CLI 客户端的默认 UA 回退行为,LGTM。

建议额外添加一个 clientReqnil 的测试用例,确认当没有客户端请求时也能正确回退到默认 UA。


40-67: 测试中 "codex-cli-custom/1.2.3" 作为 CLI UA 的代表值得注意。

"codex-cli-custom/1.2.3" 能通过 isCodexCLIUserAgent 检测是因为 "codex-cli" 前缀匹配过于宽泛(已在实现文件中标记)。如果收紧前缀为 "codex-cli/",此测试用例需要同步更新为合法的 Codex CLI UA,例如 "codex-cli/1.2.3"

internal/adapter/provider/custom/codex_headers_test.go (1)

8-26: 测试覆盖 CLI 和非 CLI 两种场景,逻辑正确。

同样需要注意:如果 isCodexCLIUserAgent 的前缀匹配从 "codex-cli" 收紧为 "codex-cli/",Line 11 的测试用例 "codex-cli-custom/1.2.3" 将不再匹配,需同步更新为 "codex-cli/1.2.3" 之类的合法 UA。

internal/adapter/provider/custom/claude_body.go (3)

28-31: Claude CLI UA 检测使用正则 (?i)^claude-cli/\d+\.\d+\.\d+,精确合理。

与 Codex 端使用宽泛的 strings.HasPrefix 不同,Claude 端要求严格的语义化版本号匹配(x.y.z),能有效区分官方 CLI 与其他以 claude-cli 开头的非官方客户端。


369-400: hasClaudeCodeSystemPrompt 覆盖了多种 system 字段格式,逻辑完备。

处理了数组(含 text block 和纯字符串元素)、字符串和对象三种格式,使用精确字符串比较(带 TrimSpace),能有效防止重复注入。


96-100: isClaudeCodeClient 改用正则匹配,与 PR 目标的"匹配官方版本模式"一致。

claudeCLIUserAgentPattern.MatchString 配合 strings.TrimSpace 确保了空白符不会干扰匹配。

internal/adapter/provider/custom/claude_body_test.go (3)

124-129: 新增的 shouldCloak 边界测试用例有效验证了正则匹配的严格性。

"claude-cli/dev" 不匹配 \d+\.\d+\.\d+,正确触发 cloaking;"Claude-CLI/2.1.17" 通过大小写不敏感匹配,正确跳过 cloaking。这些用例与 claudeCLIUserAgentPattern 的设计意图完全对齐。


132-143: Haiku 测试更新正确反映了实现变更:cloaked 请求一律注入系统提示。

移除了 Haiku 模型的特殊跳过逻辑后,此测试确认 Haiku 模型在 cloaking 模式下同样会注入系统提示和 user_id


371-386: TestNoDuplicateSystemPromptInjection 验证了 hasClaudeCodeSystemPrompt 的位置无关检测。

Claude Code prompt 置于数组索引 1 而非 0,确认检测逻辑能在数组任意位置找到已有 prompt 并阻止重复注入。

internal/adapter/provider/custom/claude_headers.go (1)

83-92: Claude 的 UA 处理逻辑正确,与 Codex 端策略一致。

CLI 客户端 UA 透传、非 CLI 回退到 defaultClaudeUserAgent,逻辑清晰。由于 Claude header 是从零构建(非 passthrough 复制),无需像 Codex 那样在 skip list 中显式排除 user-agent

internal/adapter/provider/custom/claude_headers_test.go (2)

5-9: 新增导入合理,均在新测试中使用。

regexp 用于 UA 和 metadata 格式校验,gjson 用于 JSON 路径提取,引入合理。


102-129: 测试覆盖全面,逻辑清晰。

三个子场景(官方 CLI UA 透传、浏览器 UA 回退、非官方 CLI UA 回退)准确覆盖了 PR 描述中的 UA 透传策略。claude-cli/dev 作为非官方格式被正确拦截,验证了 semver 模式匹配的严格性。

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@Bowl42 Bowl42 merged commit 18a3bd3 into main Feb 9, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants