Skip to content

fix(executor): harden Claude OAuth cloaking for proxied tools#2629

Draft
chuck-ma wants to merge 9 commits intorouter-for-me:devfrom
chuck-ma:codex/release-v6.9.18-opencode.1
Draft

fix(executor): harden Claude OAuth cloaking for proxied tools#2629
chuck-ma wants to merge 9 commits intorouter-for-me:devfrom
chuck-ma:codex/release-v6.9.18-opencode.1

Conversation

@chuck-ma
Copy link
Copy Markdown

@chuck-ma chuck-ma commented Apr 9, 2026

Summary

  • carry the Claude OAuth cloaking work from fix(claude): prevent OAuth extra-usage billing via tool name fingerprinting and system prompt cloaking #2621 onto the current v6.9.18 release baseline
  • drop cache_control from the static Claude Code block because some proxied OAuth request shapes still trip 400 / extra-usage responses when the proxied request carries custom cache scope metadata
  • alias custom tool names on OAuth requests and restore the original names on both non-streaming and streaming responses so names like todowrite are not exposed upstream
  • refresh executor tests to match the new Claude Code block layout and cover the alias round-trip

Validation

  • go build ./...
  • go test ./internal/runtime/executor/...
  • replayed the fix against a Thailand deployment serving real OpenCode traffic
  • opencode run -m cliproxy_thailand/claude-opus-4-6-1m --variant high --format json "hello"
  • verified a real todowrite tool call round-trip end to end

Notes

wykk-12138 and others added 8 commits April 9, 2026 16:14
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.
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

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.

@conversun
Copy link
Copy Markdown

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.

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.

3 participants