fix(executor): harden Claude OAuth cloaking for proxied tools#2629
fix(executor): harden Claude OAuth cloaking for proxied tools#2629chuck-ma wants to merge 9 commits intorouter-for-me:devfrom
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: router-for-me#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.
There was a problem hiding this comment.
Code Review
This pull request implements tool aliasing and refactors system prompt handling to align with Claude Code's behavior. It introduces logic to map custom tool names to aliases and back, while also updating the system prompt to include static instructions and move user system messages into a reminder block within the first user message. Review feedback suggests refactoring duplicated aliasing logic, removing redundant tool registry iterations, adopting safer JSON construction methods for cache control, and addressing potential edge cases in message content array manipulation.
|
This approach is correct. I have been testing it on opencode for several days following this idea, and the issue of additional usage has not recurred. |
Summary
v6.9.18release baselinecache_controlfrom the static Claude Code block because some proxied OAuth request shapes still trip400/ extra-usage responses when the proxied request carries custom cache scope metadatatodowriteare not exposed upstreamValidation
go build ./...go test ./internal/runtime/executor/...opencode run -m cliproxy_thailand/claude-opus-4-6-1m --variant high --format json "hello"todowritetool call round-trip end to endNotes
Third-party apps now draw from your extra usage...response.