Skip to content

feat(providers): route built-in agentrouter through dynamic Claude-Code wire-image pipeline #6056

Description

@diegosouzapw

Summary

The built-in agentrouter provider talks the correct protocol (Anthropic Messages API), but it sends a static, hardcoded Claude Code CLI fingerprint via getClaudeCliHeaders(). AgentRouter runs a WAF that only accepts traffic matching the current Claude Code "wire image", so as that fingerprint drifts, users increasingly hit unauthorized client detected. We should route the built-in agentrouter provider through the same dynamic Claude-Code-compatible wire-image pipeline used by anthropic-compatible-cc-* providers, instead of the static default-executor headers.

Current implementation (correct protocol, fragile fingerprint)

open-sse/config/providers/registry/agentrouter/index.ts:

format: "claude",                                // ✅ Anthropic Messages API (not OpenAI)
baseUrl: "https://agentrouter.org/v1/messages",  // ✅ /v1/messages, not /chat/completions
authType: "apikey", authHeader: "x-api-key",     // ✅ Anthropic-style auth
headers: getClaudeCliHeaders(),                  // ⚠️ STATIC fingerprint

The static fingerprint lives in open-sse/config/anthropicHeaders.ts:124-127:

export const CLAUDE_CLI_VERSION = "2.1.195";
export const CLAUDE_CLI_USER_AGENT = `claude-cli/${CLAUDE_CLI_VERSION} (external, cli)`;
export const CLAUDE_CLI_STAINLESS_PACKAGE_VERSION = "0.94.0";
export const CLAUDE_CLI_STAINLESS_RUNTIME_VERSION = "v24.3.0";

The agentrouter registry entry has not been touched since v3.8.27; the fingerprint constants were last bumped at v3.8.40. Only claude (official OAuth — safe, it hits the real Anthropic endpoint) and agentrouter use getClaudeCliHeaders(). For a third-party relay with a strict WAF, a static fingerprint is a slow-moving footgun.

Why users see unauthorized client detected

AgentRouter markets itself as a drop-in ANTHROPIC_BASE_URL replacement for Claude Code and its WAF rejects anything that does not look like current Claude Code. The two failure modes (documented in our own Agentrouter wiki):

  1. Provider added as a generic openai-compatible-chat custom provider → wrong wire image → rejected.
  2. Static/aged Claude Code fingerprint no longer matches what the WAF expects → rejected.

The robust path today is the Claude Code Compatible provider (anthropic-compatible-cc-*) with ENABLE_CC_COMPATIBLE_PROVIDER=true (Base URL https://agentrouter.org, chat path /v1/messages?beta=true), which runs the full dynamic wire-image pipeline (open-sse/services/ccBridgeTransforms.ts, open-sse/services/systemTransforms.ts T4-200, isClaudeCodeCompatible in open-sse/services/provider.ts:31). But that requires manual advanced setup + a feature flag, while the "Quick Start" built-in provider silently uses the fragile static path.

Proposed improvement

Make the built-in agentrouter provider consume the same dynamic Claude-Code wire-image pipeline as anthropic-compatible-cc-* (dynamic identity/anchors + /v1/messages?beta=true), so the "Quick Start" path stays WAF-valid without users needing the advanced flow or the ENABLE_CC_COMPATIBLE_PROVIDER flag. Options to evaluate:

  • Have the agentrouter registry entry opt into the CC-bridge pipeline (a registry flag that makes the default executor route through ccBridgeTransforms/systemTransforms instead of static getClaudeCliHeaders()), or
  • Internally alias the built-in agentrouter to an anthropic-compatible-cc-agentrouter configuration so the dashboard "Quick Start" wires the CC pipeline transparently.

Acceptance criteria

  • Built-in agentrouter provider passes AgentRouter's WAF without manual anthropic-compatible-cc setup.
  • The fix does not require operators to set ENABLE_CC_COMPATIBLE_PROVIDER=true for the built-in provider.
  • Regression test asserting the outbound request for agentrouter carries the dynamic CC wire image (not the static claude-cli/2.1.195 headers).
  • Wiki updated to reflect the simplified path.

Not in scope

Bumping the static CLAUDE_CLI_VERSION/stainless constants is only a temporary patch — the point is to stop depending on a static fingerprint for a WAF-gated relay.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions