diff --git a/internal/runtime/executor/claude_executor.go b/internal/runtime/executor/claude_executor.go index e6cabde3..d67d61d3 100644 --- a/internal/runtime/executor/claude_executor.go +++ b/internal/runtime/executor/claude_executor.go @@ -134,7 +134,7 @@ func (e *ClaudeExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, r extraBetas, body = extractAndRemoveBetas(body) bodyForTranslation := body bodyForUpstream := body - if isClaudeOAuthToken(apiKey) { + if isClaudeOAuthToken(apiKey) && !auth.ToolPrefixDisabled() { bodyForUpstream = applyClaudeToolPrefix(body, claudeToolPrefix) } @@ -208,7 +208,7 @@ func (e *ClaudeExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, r } else { reporter.publish(ctx, parseClaudeUsage(data)) } - if isClaudeOAuthToken(apiKey) { + if isClaudeOAuthToken(apiKey) && !auth.ToolPrefixDisabled() { data = stripClaudeToolPrefixFromResponse(data, claudeToolPrefix) } var param any @@ -275,7 +275,7 @@ func (e *ClaudeExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A extraBetas, body = extractAndRemoveBetas(body) bodyForTranslation := body bodyForUpstream := body - if isClaudeOAuthToken(apiKey) { + if isClaudeOAuthToken(apiKey) && !auth.ToolPrefixDisabled() { bodyForUpstream = applyClaudeToolPrefix(body, claudeToolPrefix) } @@ -348,7 +348,7 @@ func (e *ClaudeExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A if detail, ok := parseClaudeStreamUsage(line); ok { reporter.publish(ctx, detail) } - if isClaudeOAuthToken(apiKey) { + if isClaudeOAuthToken(apiKey) && !auth.ToolPrefixDisabled() { line = stripClaudeToolPrefixFromStreamLine(line, claudeToolPrefix) } // Forward the line as-is to preserve SSE format @@ -375,7 +375,7 @@ func (e *ClaudeExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A if detail, ok := parseClaudeStreamUsage(line); ok { reporter.publish(ctx, detail) } - if isClaudeOAuthToken(apiKey) { + if isClaudeOAuthToken(apiKey) && !auth.ToolPrefixDisabled() { line = stripClaudeToolPrefixFromStreamLine(line, claudeToolPrefix) } chunks := sdktranslator.TranslateStream( @@ -423,7 +423,7 @@ func (e *ClaudeExecutor) CountTokens(ctx context.Context, auth *cliproxyauth.Aut // Extract betas from body and convert to header (for count_tokens too) var extraBetas []string extraBetas, body = extractAndRemoveBetas(body) - if isClaudeOAuthToken(apiKey) { + if isClaudeOAuthToken(apiKey) && !auth.ToolPrefixDisabled() { body = applyClaudeToolPrefix(body, claudeToolPrefix) } diff --git a/sdk/cliproxy/auth/types.go b/sdk/cliproxy/auth/types.go index b2bbe0a2..96534bbe 100644 --- a/sdk/cliproxy/auth/types.go +++ b/sdk/cliproxy/auth/types.go @@ -213,6 +213,23 @@ func (a *Auth) DisableCoolingOverride() (bool, bool) { return false, false } +// ToolPrefixDisabled returns whether the proxy_ tool name prefix should be +// skipped for this auth. When true, tool names are sent to Anthropic unchanged. +// The value is read from metadata key "tool_prefix_disabled" (or "tool-prefix-disabled"). +func (a *Auth) ToolPrefixDisabled() bool { + if a == nil || a.Metadata == nil { + return false + } + for _, key := range []string{"tool_prefix_disabled", "tool-prefix-disabled"} { + if val, ok := a.Metadata[key]; ok { + if parsed, okParse := parseBoolAny(val); okParse { + return parsed + } + } + } + return false +} + // RequestRetryOverride returns the auth-file scoped request_retry override when present. // The value is read from metadata key "request_retry" (or legacy "request-retry"). func (a *Auth) RequestRetryOverride() (int, bool) { diff --git a/sdk/cliproxy/auth/types_test.go b/sdk/cliproxy/auth/types_test.go new file mode 100644 index 00000000..8249b063 --- /dev/null +++ b/sdk/cliproxy/auth/types_test.go @@ -0,0 +1,35 @@ +package auth + +import "testing" + +func TestToolPrefixDisabled(t *testing.T) { + var a *Auth + if a.ToolPrefixDisabled() { + t.Error("nil auth should return false") + } + + a = &Auth{} + if a.ToolPrefixDisabled() { + t.Error("empty auth should return false") + } + + a = &Auth{Metadata: map[string]any{"tool_prefix_disabled": true}} + if !a.ToolPrefixDisabled() { + t.Error("should return true when set to true") + } + + a = &Auth{Metadata: map[string]any{"tool_prefix_disabled": "true"}} + if !a.ToolPrefixDisabled() { + t.Error("should return true when set to string 'true'") + } + + a = &Auth{Metadata: map[string]any{"tool-prefix-disabled": true}} + if !a.ToolPrefixDisabled() { + t.Error("should return true with kebab-case key") + } + + a = &Auth{Metadata: map[string]any{"tool_prefix_disabled": false}} + if a.ToolPrefixDisabled() { + t.Error("should return false when set to false") + } +}