Conversation
Three changes to avoid Anthropic's content-based system prompt validation: 1. Fix identity prefix: Use 'You are Claude Code, Anthropic's official CLI for Claude.' instead of the SDK agent prefix, matching real Claude Code. 2. Move user system instructions to user message: Only keep billing header + identity prefix in system[] array. User system instructions are prepended to the first user message as <system-reminder> blocks. 3. Enable cch signing for OAuth tokens by default: The xxHash64 cch integrity check was previously gated behind experimentalCCHSigning config flag. Now automatically enabled when using OAuth tokens. Related: #2599
…er cache scopes Previous fix only injected billing header + agent identifier (2 blocks). Anthropic's updated detection now validates system prompt content depth: - Block count (needs 4-6 blocks, not 2) - Cache control scopes (org for agent, global for core prompt) - Presence of known Claude Code instruction sections Changes: - Add claude_system_prompt.go with extracted Claude Code v2.1.63 system prompt sections (intro, system instructions, doing tasks, tone & style, output efficiency) - Rewrite checkSystemInstructionsWithSigningMode to build 5 system blocks: [0] billing header (no cache_control) [1] agent identifier (cache_control: ephemeral, scope=org) [2] core intro prompt (cache_control: ephemeral, scope=global) [3] system instructions (no cache_control) [4] doing tasks (no cache_control) - Third-party client system instructions still moved to first user message Follow-up to 69b950d
The previous commit used fmt.Sprintf with %s to insert multi-line string constants into JSON strings. Go raw string literals contain actual newline bytes, which produce invalid JSON (control characters in string values). Replace with buildTextBlock() helper that uses sjson.SetBytes to properly escape text content for JSON serialization.
sjson treats 'cache_control.type' as nested path, creating
{ephemeral: {scope: org}} instead of {type: ephemeral, scope: org}.
Pass the whole map to sjson.SetBytes as a single value.
Only for Claude OAuth requests, sanitize forwarded system-prompt context before it is prepended into the first user message. This preserves neutral task/tool instructions while removing OpenCode branding, docs links, environment banners, and product-specific workflow sections that still triggered Anthropic extra-usage classification after top-level system[] cloaking.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…rd-party fingerprint detection A/B testing confirmed that Anthropic uses tool name fingerprinting to detect third-party clients on OAuth traffic. OpenCode-style lowercase names like 'bash', 'read', 'todowrite' trigger extra-usage billing, while Claude Code TitleCase names like 'Bash', 'Read', 'TodoWrite' pass through normally. Changes: - Add oauthToolRenameMap: maps lowercase tool names to Claude Code equivalents - Add oauthToolsToRemove: removes 'question' and 'skill' (no Claude Code counterpart) - remapOAuthToolNames: renames tools, removes blacklisted ones, updates tool_choice and messages - reverseRemapOAuthToolNames/reverseRemapOAuthToolNamesFromStreamLine: reverse map for responses - Apply in Execute(), ExecuteStream(), and CountTokens() for OAuth token requests
…dation Antigravity 的 Claude thinking signature 处理新增 cache/bypass 双模式, 并为 bypass 模式实现按 SIGNATURE-CHANNEL-SPEC.md 的签名校验。 新增 antigravity-signature-cache-enabled 配置项(默认 true): - cache mode(true):使用服务端缓存的签名,行为与原有逻辑完全一致 - bypass mode(false):直接使用客户端提供的签名,经过校验和归一化 支持配置热重载,运行时可切换模式。 校验流程: 1. 剥离历史 cache-mode 的 'modelGroup#' 前缀(如 claude#Exxxx → Exxxx) 2. 首字符必须为 'E'(单层编码)或 'R'(双层编码),否则拒绝 3. R 开头:base64 解码 → 内层必须以 'E' 开头 → 继续单层校验 4. E 开头:base64 解码 → 首字节必须为 0x12(Claude protobuf 标识) 5. 所有合法签名归一化为 R 形式(双层 base64)发往 Antigravity 后端 非法签名处理策略: - 非严格模式(默认):translator 静默丢弃无签名的 thinking block - 严格模式(antigravity-signature-bypass-strict: true): executor 层在请求发往上游前直接返回 HTTP 400 按 SIGNATURE-CHANNEL-SPEC.md 解析 Claude 签名的完整 protobuf 结构: - Top-level Field 2(容器)→ Field 1(渠道块) - 渠道块提取:channel_id (Field 1)、infrastructure (Field 2)、 model_text (Field 6)、field7 (Field 7) - 计算 routing_class、infrastructure_class、schema_features - 使用 google.golang.org/protobuf/encoding/protowire 解析 - resolveThinkingSignature 拆分为 resolveCacheModeSignature / resolveBypassModeSignature - hasResolvedThinkingSignature:mode-aware 签名有效性判断 (cache: len>=50 via HasValidSignature,bypass: non-empty) - validateAntigravityRequestSignatures:executor 预检, 仅在 bypass + strict 模式下拦截非法签名返回 400 - 响应侧签名缓存逻辑与 cache mode 集成 - Cache mode 行为完全保留:无 '#' 前缀的原生签名静默丢弃
Add package-level comment documenting the protobuf tree structure, base64 encoding equivalence proof, output dimensions, and spec section references. Remove unreachable legacy_vertex_group dead code.
Includes: restore SDK docs under docs/; update antigravity executor credits tests; gofmt.
feat: configurable signature cache toggle for Antigravity/Claude thinking blocks
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
- Use buildTextBlock for billing header to avoid raw JSON string interpolation - Fix empty array edge case in prependToFirstUserMessage - Allow remapOAuthToolNames to process messages even without tools array - Move claude_system_prompt.go to helps/ per repo convention - Export prompt constants (ClaudeCode* prefix) for cross-package access Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
fix(claude): prevent OAuth extra-usage billing via tool name fingerprinting and system prompt cloaking
Promote cloudcode-pa.googleapis.com to the first position in the fallback order, with daily and sandbox URLs as fallbacks.
feat(antigravity): prefer prod URL as first priority
…r Qwen and add associated tests Closes: #2661
… and add tests Closes: #2656
There was a problem hiding this comment.
Code Review
This pull request introduces a signature cache and bypass mode for Antigravity/Claude thinking blocks, refactors 429 error handling and credit fallback logic in the Antigravity executor, and implements tool name remapping and system prompt cloaking for Claude OAuth requests to prevent fingerprinting. It also adds an immediate token refresh and retry mechanism for the Qwen executor. Feedback identifies a security risk in binding a local forwarder to all interfaces and notes that the usage reporter in the Qwen executor may use stale authentication metadata after a token refresh.
I am having trouble creating individual review comments. Click here to see my feedback.
internal/api/handlers/management/auth_files.go (145)
Changing the listening address from 127.0.0.1 to 0.0.0.0 exposes this local callback forwarder to the network. This is a security risk as it allows any device on the network to connect to the forwarder, which is typically used for sensitive OAuth callback handling. Unless there is a specific requirement for network-wide access (e.g., running in a containerized environment without host networking), it is safer to bind to 127.0.0.1.
addr := fmt.Sprintf("127.0.0.1:%d", port)
internal/runtime/executor/qwen_executor.go (317)
The UsageReporter is initialized with the initial auth object. If a 429 error occurs and the token is refreshed (resulting in a new auth object via refreshFn), the reporter remains associated with the old auth metadata. While the auth.ID likely stays the same, any updated metadata or attributes in the refreshed auth object will not be reflected in the usage reporting. Consider re-initializing the reporter or updating its reference if the auth object changes during the retry loop.
internal/runtime/executor/qwen_executor.go (458)
Similar to the Execute method, the UsageReporter here is initialized with the initial auth object. If the token is refreshed during a 429 retry, the reporter will continue to use the stale auth metadata for reporting. Consider re-initializing the reporter if the auth object is updated.
No description provided.