From 39db4619de605f8a2828d22bf9d28a1670659e0d Mon Sep 17 00:00:00 2001 From: Norvel Roosje Date: Fri, 6 Feb 2026 11:11:47 -0400 Subject: [PATCH] feat(auth): force subscription-only mode for Claude provider Prevent accidental API credit consumption by explicitly disabling API authentication when using the default Claude provider. This ensures users with Claude Code subscriptions don't inadvertently incur API charges. Changes: - Add safety check in client.py to reject API credentials for Claude provider - Modify registry.py to explicitly clear API auth env vars in Claude mode - Add user-facing confirmation messages for subscription mode --- client.py | 26 ++++++++++++++++++++++++++ registry.py | 17 ++++++++++------- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/client.py b/client.py index a81a66db..12121c2a 100644 --- a/client.py +++ b/client.py @@ -472,6 +472,12 @@ def create_client( elif "ANTHROPIC_BASE_URL" in sdk_env: print(f" - GLM Mode: Using {sdk_env['ANTHROPIC_BASE_URL']}") + # ADDED: Confirm subscription-only mode for default Claude provider + # This prevents accidental API credit consumption when user has a Claude Code subscription + if not is_alternative_api and not base_url: + print(" ✓ Claude Code Subscription Mode: API credit usage DISABLED") + print(" ✓ Using OAuth token in subscription mode only") + # Create a wrapper for bash_security_hook that passes project_dir via context async def bash_hook_with_context(input_data, tool_use_id=None, context=None): """Wrapper that injects project_dir into context for security hook.""" @@ -552,6 +558,26 @@ async def pre_compact_hook( } ) + # CRITICAL SAFETY CHECK: Ensure we're not in API mode for default Claude provider + # This prevents accidental API charges when user has a Claude Code subscription. + # The check must happen BEFORE creating the SDK client to catch configuration errors early. + from registry import get_all_settings + all_settings = get_all_settings() + provider_id = all_settings.get("api_provider", "claude") + if provider_id == "claude" and not is_alternative_api: + # Verify no API credentials are being passed that would trigger API billing + if sdk_env.get("ANTHROPIC_API_KEY") and sdk_env.get("ANTHROPIC_API_KEY") != "": + raise RuntimeError( + "SAFETY CHECK FAILED: ANTHROPIC_API_KEY detected for Claude provider! " + "This would consume API credits. Check registry settings and environment variables." + ) + if sdk_env.get("ANTHROPIC_AUTH_TOKEN") and sdk_env.get("ANTHROPIC_AUTH_TOKEN") != "": + raise RuntimeError( + "SAFETY CHECK FAILED: ANTHROPIC_AUTH_TOKEN detected for Claude provider! " + "This would consume API credits. Check registry settings and environment variables." + ) + print(" ✓ Safety check passed: No API credentials detected") + # PROMPT CACHING: The Claude Code CLI applies cache_control breakpoints internally. # Our system_prompt benefits from automatic caching without explicit configuration. # If explicit cache_control is needed, the SDK would need to accept content blocks diff --git a/registry.py b/registry.py index 30765198..af821e94 100644 --- a/registry.py +++ b/registry.py @@ -710,13 +710,16 @@ def get_effective_sdk_env() -> dict[str, str]: provider_id = all_settings.get("api_provider", "claude") if provider_id == "claude": - # Default behavior: forward existing env vars - from env_constants import API_ENV_VARS - sdk_env: dict[str, str] = {} - for var in API_ENV_VARS: - value = os.getenv(var) - if value: - sdk_env[var] = value + # MODIFIED: Force subscription-only mode by explicitly disabling API + # This prevents the SDK from using OAuth tokens in API mode, which would + # charge the user's Anthropic API account instead of using their Claude Code subscription. + sdk_env: dict[str, str] = { + # Explicitly clear API authentication to prevent unexpected charges + "ANTHROPIC_API_KEY": "", + "ANTHROPIC_AUTH_TOKEN": "", + } + # Log that we're in subscription-only mode + logger.info("Claude Code subscription mode: API authentication explicitly disabled") return sdk_env # Alternative provider: build env from settings