Skip to content

DEV-1602: re-enable Claude.ai subscription (OAuth) auth for the Claude Agent SDK - #59

Merged
ZmeiGorynych merged 6 commits into
mainfrom
egor/dev-1602-re-enable-claudeai-subscription-oauth-auth-for-the-claude
Jun 25, 2026
Merged

DEV-1602: re-enable Claude.ai subscription (OAuth) auth for the Claude Agent SDK#59
ZmeiGorynych merged 6 commits into
mainfrom
egor/dev-1602-re-enable-claudeai-subscription-oauth-auth-for-the-claude

Conversation

@ZmeiGorynych

@ZmeiGorynych ZmeiGorynych commented Jun 24, 2026

Copy link
Copy Markdown
Member

What

DEV-1579 severed the OAuth path only at the agents/claude_sdk/sdk_env.py choke point; the cloud --subscription-auth plumbing stayed intact. Anthropic confirmed OAuth was never actually disabled upstream, so this re-enables it.

The auth path is chosen by an explicit operator signal (BIRD_INTERACT_SUBSCRIPTION_AUTH), never inferred from which credential survives. The registry/open-weight exemption is checked first, so the Anthropic-only signal is inert for registry models.

Changes

  • sdk_env.py: _subscription_auth_selected() reads the signal var. assert_api_key_auth accepts a valid sk-ant-oat01- CLAUDE_CODE_OAUTH_TOKEN on the subscription path (missing/malformed hard-fails — no fall-back to the API key); else requires ANTHROPIC_API_KEY. build_hermetic_session_env masks ANTHROPIC_API_KEY in the subprocess options.env on the subscription path (precedence trap) and lets the OAuth token inherit; the API-key path keeps masking the OAuth token. Registry layering unchanged (masks both, wins).
  • cloud/driver.py + cloud/prereqs.py: gate the OAuth branch on the agent model not being a registry model (registry-first); the driver ships BIRD_INTERACT_SUBSCRIPTION_AUTH=1 to the actors on the OAuth path.
  • run.py: new --subscription-auth flag (default off) + _apply_subscription_auth_env helper that sets/clears the signal var locally; Anthropic-only, claude_sdk-only.
  • Credential forwarding stays env-var only (no .credentials.json copy), so the DEV-1579 hermetic CLAUDE_CONFIG_DIR isolation + MCP parity are preserved.
  • Supersedes DEV-1582 (retire --subscription-auth machinery). Docstrings + CLAUDE.md updated to drop the "OAuth is dead" wording.

Tests

Rewritten/added across tests/test_sdk_subprocess_hermetic.py, tests/cloud/test_driver.py, tests/cloud/test_prereqs.py, the new tests/test_dev1602_run_subscription_flag.py, and an OAuth-path real-CLI smoke in tests/test_dev1579_hermetic_integration.py (@pytest.mark.integration). Full non-integration suite: 3370 passed, 94 skipped.

Acceptance (manual, follow-up)

Cloud opus claude_sdk smoke (single DB, --mode a-interact, --subscription-auth) authenticating via the subscription with no ANTHROPIC_API_KEY consumed.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added an optional Claude SDK subscription/OAuth auth mode for eligible Anthropic models via --subscription-auth / --no-subscription-auth.
  • Bug Fixes
    • Registry-backed models now consistently use provider credentials without requiring subscription/OAuth.
    • Hermetic runs no longer copy local credential/config files; auth is sourced from environment variables only. Missing/malformed OAuth tokens now fail fast (no fallback).
  • Documentation
    • Updated README and usage examples, plus CLI option requirements, to reflect the new auth flag behavior.
  • Tests
    • Expanded automated coverage for auth selection, env signaling, masking/precedence, and end-to-end hermetic subscription sessions.

…e Agent SDK

DEV-1579 severed the OAuth path ONLY at the agents/claude_sdk/sdk_env.py
choke point (the cloud --subscription-auth plumbing stayed intact). Anthropic
confirmed OAuth was never actually disabled upstream, so re-enable it.

Path is chosen by an EXPLICIT operator signal (BIRD_INTERACT_SUBSCRIPTION_AUTH),
never inferred from which credential survives. Registry/open-weight exemption
is checked FIRST, so the Anthropic-only signal is inert for registry models.

- sdk_env.py: `_subscription_auth_selected()` reads the signal var.
  `assert_api_key_auth` accepts a valid sk-ant-oat01- CLAUDE_CODE_OAUTH_TOKEN
  on the subscription path (missing/malformed hard-fails, no fall-back to the
  API key); else requires ANTHROPIC_API_KEY. `build_hermetic_session_env` masks
  ANTHROPIC_API_KEY in the subprocess options.env on the subscription path
  (precedence trap) and lets the OAuth token inherit; API-key path keeps masking
  the OAuth token. Registry layering unchanged (masks both, wins).
- cloud/driver.py + cloud/prereqs.py: gate the OAuth branch on the agent model
  NOT being a registry model (registry-first); the driver ships
  BIRD_INTERACT_SUBSCRIPTION_AUTH=1 to the actors on the OAuth path.
- run.py: new --subscription-auth flag (default off) + `_apply_subscription_auth_env`
  helper that sets/clears the signal var locally; Anthropic-only, claude_sdk-only.
- Credential forwarding stays env-var only (no .credentials.json copy), so the
  DEV-1579 hermetic CLAUDE_CONFIG_DIR isolation + MCP parity are preserved.
- Supersedes DEV-1582 (retire --subscription-auth machinery). Docstrings +
  CLAUDE.md updated to drop the "OAuth is dead" wording.

Tests: rewritten/added across tests/test_sdk_subprocess_hermetic.py,
tests/cloud/test_driver.py, tests/cloud/test_prereqs.py, the new
tests/test_dev1602_run_subscription_flag.py, and an OAuth-path real-CLI smoke
in tests/test_dev1579_hermetic_integration.py (@pytest.mark.integration).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@linear

linear Bot commented Jun 24, 2026

Copy link
Copy Markdown
DEV-1602 Re-enable Claude.ai subscription (OAuth) auth for the Claude Agent SDK

Context

DEV-1579 (2026-06-15) disabled Claude.ai subscription / OAuth auth (CLAUDE_CODE_OAUTH_TOKEN) for the Claude Agent SDK. agents/claude_sdk/sdk_env.py::assert_api_key_auth — the choke point every claude_sdk* agent (the 8 OTF agents, the annotator, the DEV-1589 build-time encoder) routes through — now hard-fails a lone CLAUDE_CODE_OAUTH_TOKEN and demands ANTHROPIC_API_KEY. hermetic_claude_config_dir also deliberately stopped copying .credentials.json into the per-task hermetic config dir.

Effect: a cloud submit with --subscription-auth (the flag still exists in cloud/cli.py; its help text predates DEV-1579) submits + spins up the GCE/Ray cluster, then every actor immediately errors with ApiKeyAuthError. So opus cloud runs currently MUST burn ANTHROPIC_API_KEY credits — there is no working subscription path.

Goal

Re-enable the subscription/OAuth path so cloud opus claude_sdk* runs can authenticate against the Claude.ai subscription (CLAUDE_CODE_OAUTH_TOKEN) instead of API-key credits when the operator passes --subscription-auth.

Scope / investigation

  1. Determine why it was disabled — was the upstream Agent SDK change that broke OAuth reverted/fixed, or was it a deliberate Anthropic policy on 2026-06-15? Re-enabling only makes sense if the bundled claude CLI again accepts sk-ant-oat01- OAuth tokens.
  2. If OAuth is viable again:
    • assert_api_key_auth: accept a valid CLAUDE_CODE_OAUTH_TOKEN (sk-ant-oat01- prefix) as satisfying auth for Anthropic models, instead of demanding ANTHROPIC_API_KEY.
    • hermetic_claude_config_dir / hermetic_session_option_kwargs: forward the OAuth credential (env and/or .credentials.json) into the hermetic per-task config dir so the subprocess uses it, WITHOUT re-introducing the ~/.claude.json connector leak DEV-1579 closed.
    • cloud/cli.py + cloud runner: thread CLAUDE_CODE_OAUTH_TOKEN from the submitter env into the cluster actors when --subscription-auth is set; keep the API-key path for --no-subscription-auth.
    • Reconcile with DEV-1582 (slated to RETIRE the --subscription-auth machinery) and DEV-1583 (registry Bearer var rename) — decide whether DEV-1582 is superseded by this.
  3. Update the now-stale --subscription-auth help text in cloud/cli.py.

Acceptance

A cloud opus claude_sdk smoke (single DB, --mode a-interact, --subscription-auth) runs to completion with non-error actors, authenticating via the Claude.ai subscription (no ANTHROPIC_API_KEY consumed). Hermetic CLAUDE_CONFIG_DIR isolation + MCP parity (DEV-1579) preserved.

Tests

  • tests/test_sdk_subprocess_hermetic.py (the assert_api_key_auth units) updated so a valid OAuth token satisfies auth under the subscription path.
  • The real-CLI isolation smoke (tests/test_dev1579_hermetic_integration.py, @pytest.mark.integration) extended to cover the OAuth path.

Why now

Blocks running opus cloud smokes on the subscription — e.g. validating the DEV-1589 claude_sdk OTF encoder branch end-to-end — without burning API credits. Once this lands, the DEV-1589 branch will be merged with it and the deferred single-DB opus smoke launched.

Review in Linear

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@ZmeiGorynych, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 48 minutes and 50 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8fe38f43-498a-4405-a111-31dde6c59b7b

📥 Commits

Reviewing files that changed from the base of the PR and between e1beda8 and c6f9eb1.

📒 Files selected for processing (7)
  • README.md
  • src/bird_interact_agents/cloud/driver.py
  • src/bird_interact_agents/cloud/prereqs.py
  • src/bird_interact_agents/run.py
  • tests/cloud/test_driver.py
  • tests/cloud/test_prereqs.py
  • tests/test_dev1602_run_subscription_flag.py
📝 Walkthrough

Walkthrough

Adds a BIRD_INTERACT_SUBSCRIPTION_AUTH signal that switches claude_sdk* agents between API-key and subscription/OAuth authentication paths. The core auth logic, CLI wiring, cloud credential routing, docs, and tests are updated to match.

Changes

Subscription/OAuth Auth Path for claude_sdk Agents

Layer / File(s) Summary
Auth contract and hermetic env masking
src/bird_interact_agents/agents/claude_sdk/sdk_env.py, CLAUDE.md
assert_api_key_auth selects API-key or subscription/OAuth auth from BIRD_INTERACT_SUBSCRIPTION_AUTH, requires a valid CLAUDE_CODE_OAUTH_TOKEN on the subscription path, exempts registry models, and updates hermetic env masking. CLAUDE.md and related docstrings describe the same contract.
CLI subscription flag and required argument wiring
src/bird_interact_agents/run.py
Adds _apply_subscription_auth_env, registers --subscription-auth, makes --mode, --query-mode, and --agent-model required, updates defaults, and calls the helper after parsing.
Cloud credential routing and prereq checks
src/bird_interact_agents/cloud/driver.py, src/bird_interact_agents/cloud/prereqs.py
Cloud env loading and prereq checks add an early annotator model guard, skip Claude SDK OAuth handling for registry-backed models, and emit BIRD_INTERACT_SUBSCRIPTION_AUTH=1 with the OAuth token.
Subscription flag and cloud prereq tests
tests/test_dev1602_run_subscription_flag.py, tests/cloud/test_driver.py, tests/cloud/test_prereqs.py
Tests cover subscription signal setting/clearing, OAuth validation, registry-model handling, annotator rejection, and CLI wiring for the new required flags and defaults.
Hermetic integration and subprocess tests
tests/test_dev1579_hermetic_integration.py, tests/test_sdk_subprocess_hermetic.py, tests/test_claude_sdk_otf_ainteract_v1_run_wiring.py, tests/test_claude_sdk_otf_v1_run_wiring.py, tests/test_one_shot_mode.py, tests/test_pydantic_ai_otf_encode_run_wiring.py, tests/test_slayer_setup_flag.py, README.md
Hermetic and subprocess coverage expands for subscription-path auth, env masking, and registry exemptions. CLI helpers and README examples are updated to include required agent-model and subscription-auth flags.

Sequence Diagram(s)

sequenceDiagram
  participant Operator
  participant run.py
  participant provider_registry
  participant cloud_driver
  participant cloud_prereqs
  participant sdk_env
  participant ClaudeSDK

  Operator->>run.py: parse --subscription-auth
  run.py->>provider_registry: get_provider(agent_model)
  provider_registry-->>run.py: registry or none
  run.py->>run.py: set or clear BIRD_INTERACT_SUBSCRIPTION_AUTH

  Operator->>cloud_driver: read_api_keys_from_local_env
  cloud_driver->>provider_registry: get_provider(agent_model)
  provider_registry-->>cloud_driver: registry or none
  cloud_driver->>cloud_driver: return env with token and signal when enabled

  Operator->>cloud_prereqs: check_api_keys
  cloud_prereqs->>provider_registry: get_provider(agent_model)
  provider_registry-->>cloud_prereqs: registry or none
  cloud_prereqs->>cloud_prereqs: require provider key or OAuth token

  Operator->>sdk_env: hermetic_claude_sdk_session
  sdk_env->>provider_registry: get_provider(agent_model)
  provider_registry-->>sdk_env: registry or none
  sdk_env->>ClaudeSDK: spawn with masked subprocess env
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • MotleyAI/bird-agents#17: Shares the Claude SDK OAuth auth flow in cloud/driver.py and cloud/prereqs.py, which this PR extends with a subscription signal and registry-first handling.
  • MotleyAI/bird-agents#28: Touches the same cloud-side OAuth selection logic for Claude SDK runs that this PR further gates and signals.
  • MotleyAI/bird-agents#54: Updates the hermetic Claude SDK session machinery in sdk_env.py, which this PR extends with operator-gated subscription auth.

Poem

🐇 Hop, hop, the signal is set,
API key or OAuth, no secrets to fret.
Registry models pass with a careful glide,
Hermetic sessions keep the door locked tight inside.
sk-ant-oat01- twinkles, then off we go,
With tidy env paths in a neat little row.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 78.26% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: re-enabling Claude.ai subscription OAuth auth for the Claude Agent SDK.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/cloud/test_driver.py`:
- Around line 1283-1294: The
test_read_api_keys_api_key_path_no_subscription_signal regression case is too
weak because it deletes CLAUDE_CODE_OAUTH_TOKEN before calling
driver.read_api_keys_from_local_env, so it does not verify that
no_subscription_auth=True suppresses BIRD_INTERACT_SUBSCRIPTION_AUTH even when
an ambient OAuth token exists. Keep the OAuth token set in this test, still set
ANTHROPIC_API_KEY, and assert the returned keys do not include
BIRD_INTERACT_SUBSCRIPTION_AUTH so the explicit opt-out behavior is covered.

In `@tests/test_dev1602_run_subscription_flag.py`:
- Around line 143-147: Strengthen the wiring test for run.main by asserting
actual execution of _apply_subscription_auth_env rather than just searching
run.main source text. Update test_main_invokes_subscription_auth_helper to
invoke main with a controlled setup and verify the helper is called via a
mock/spy or observable side effect, so comments, string literals, or dead
references cannot satisfy the test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bc49a6fb-14af-4679-96b0-3557ed1f2939

📥 Commits

Reviewing files that changed from the base of the PR and between 9ad1e4b and 5eb75a1.

📒 Files selected for processing (10)
  • CLAUDE.md
  • src/bird_interact_agents/agents/claude_sdk/sdk_env.py
  • src/bird_interact_agents/cloud/driver.py
  • src/bird_interact_agents/cloud/prereqs.py
  • src/bird_interact_agents/run.py
  • tests/cloud/test_driver.py
  • tests/cloud/test_prereqs.py
  • tests/test_dev1579_hermetic_integration.py
  • tests/test_dev1602_run_subscription_flag.py
  • tests/test_sdk_subprocess_hermetic.py

Comment thread tests/cloud/test_driver.py
Comment thread tests/test_dev1602_run_subscription_flag.py Outdated
ZmeiGorynych and others added 4 commits June 24, 2026 17:48
- sdk_env.build_hermetic_session_env: on the subscription path mask
  ANTHROPIC_AUTH_TOKEN too (not just ANTHROPIC_API_KEY) — it is an SDK Bearer
  credential the auth-precedence rule would otherwise pick over the OAuth token
  (Codex). OAuth stays authoritative; no silent fallback.
- driver.read_api_keys_from_local_env + prereqs.check_api_keys: scope the
  registry-first exemption to PROVIDER-AWARE claude_sdk* frameworks so the
  Anthropic-only `annotator` (provider_aware=False) never diverts to the
  provider-key branch for a registry agent model — it stays on the OAuth path
  (Codex).
- tests: keep an ambient OAuth token in the API-key-path driver test so the
  explicit opt-out is proven to beat ambient creds (CodeRabbit); AST-assert the
  helper is actually CALLED in run.main, not merely present in source
  (CodeRabbit); add driver+prereqs annotator-registry-stays-on-OAuth tests and
  the ANTHROPIC_AUTH_TOKEN mask assertion.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…arly guard

A registry agent model on the `annotator` framework (which runs
provider_aware=False, Anthropic-only, at runtime) previously fell through to
the generic provider-key branch when --no-subscription-auth was set (annotate
has no model-provider validation, unlike submit), shipping a provider key the
runtime would reject. Replace the OAuth-branch-only scoping with a single
explicit fail-early guard in both read_api_keys_from_local_env and
check_api_keys: the annotator rejects any non-Anthropic agent model up front,
consistently for every no_subscription_auth value. The OAuth registry-first
gate reverts to its simple `get_provider(agent_model) is None` form.

Tests updated: annotator + registry now raises "Anthropic-only" early
(parametrized over both subscription-auth values).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-Anthropic models

The annotator Anthropic-only guard previously rejected only registry models
(get_provider() is not None), so openai/*, gemini/*, etc. — which are not in
the open-weight registry — still slipped through to the OAuth/provider-key
branches and failed late. Generalise the guard in both
read_api_keys_from_local_env and check_api_keys to reject any agent model that
isn't anthropic/*. Tests parametrized over both a registry (moonshot/*) and a
non-registry (openai/*) non-Anthropic model.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The local run.py CLI diverged from the cloud submit CLI on several flags. Align
local → cloud (cloud was already the canonical shape):

- Make --agent-model, --mode, --query-mode REQUIRED (no defaults) — matches
  cloud's explicit-choice philosophy (no silent wrong/expensive run).
- --subscription-auth: switch local default False → None and mirror cloud's
  explicit-choice rule — a claude_sdk* run on an Anthropic agent model must pass
  --subscription-auth or --no-subscription-auth (registry models exempt;
  non-claude_sdk defaults off).
- --patience default 3 → 250 (3 was too low and skewed local eval vs cloud).
- --user-sim-model default haiku-4-5 → claude-sonnet-4-6 (cloud default).
- --use-audited-gold-sql: store_true/default False → BooleanOptionalAction/
  default True (cloud default; pass --no-use-audited-gold-sql to opt out).
- (--strict left as-is per request — near-cosmetic.)

--dataset was already required on both; verified.

Tests: the run-wiring / one-shot / slayer-setup tests that drove run.main with
synthesized argv now pass the newly-required --agent-model / --query-mode /
--no-subscription-auth; the DEV-1602 flag tests pin the new default=None
explicit-choice behavior and the required-flag + value-default contract. README
local examples updated to the required-flag surface. Full non-integration suite:
3383 passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/bird_interact_agents/run.py (1)

1663-1705: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reject non-Anthropic non-registry models before enabling OAuth.

Line 1664 only distinguishes registry models, so --subscription-auth --framework claude_sdk --agent-model openai/... passes the OAuth token checks and sets BIRD_INTERACT_SUBSCRIPTION_AUTH=1. That contradicts the Anthropic-only flag contract and can send the downstream SDK down the wrong auth path.

Proposed fix
     is_claude_sdk = framework.startswith("claude_sdk")
     is_registry = get_provider(agent_model) is not None
+    is_anthropic_model = agent_model.startswith("anthropic/")
     # Explicit-choice requirement (cloud parity): claude_sdk* + Anthropic model
     # must pass --subscription-auth or --no-subscription-auth.
-    if is_claude_sdk and not is_registry and subscription_auth is None:
+    if (
+        is_claude_sdk
+        and is_anthropic_model
+        and not is_registry
+        and subscription_auth is None
+    ):
         error(
             "an explicit --subscription-auth / --no-subscription-auth choice is "
             "required for claude_sdk* runs on an Anthropic agent model (no "
             "default, to prevent a silent fall-back to the API-key path)."
         )
@@
     if is_registry:
         error(
             f"--subscription-auth is Anthropic-only; {agent_model!r} is a "
             "registry open-weight model that authenticates via its provider "
             "key. Omit the flag for registry models."
         )
         return
+    if not is_anthropic_model:
+        error(
+            f"--subscription-auth is Anthropic-only; got non-Anthropic "
+            f"agent_model={agent_model!r}."
+        )
+        return
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/bird_interact_agents/run.py` around lines 1663 - 1705, The
subscription-auth gate in the run.py auth setup is only excluding registry
models, so claude_sdk runs with non-Anthropic non-registry agent models can
still pass and enable BIRD_INTERACT_SUBSCRIPTION_AUTH. Update the validation in
the same block around is_claude_sdk, is_registry, and subscription_auth so that
only Anthropic models are allowed to proceed; reject any other agent_model
before checking CLAUDE_CODE_OAUTH_TOKEN or setting the env var. Keep the
existing error path and make the check explicit in the subscription-auth
handling logic.
src/bird_interact_agents/cloud/prereqs.py (1)

264-275: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Mirror the driver fix: don’t treat every non-registry model as Anthropic.

This OAuth prereq branch also matches openai/..., gemini/..., etc. because those are non-registry. That makes the prereq check require CLAUDE_CODE_OAUTH_TOKEN for non-Anthropic models instead of rejecting the invalid subscription-auth combination.

Proposed fix
+    _agent_spec = provider_registry.get_provider(agent_model)
+    if (
+        _is_claude_sdk_framework(framework)
+        and not no_subscription_auth
+        and _agent_spec is None
+        and not agent_model.startswith("anthropic/")
+    ):
+        raise PrereqError(
+            "--subscription-auth is Anthropic-only; got non-Anthropic "
+            f"agent model {agent_model!r}.",
+            remediation=(
+                "pass an anthropic/* --agent-model for subscription auth, "
+                "use a registry model provider path, or pass --no-subscription-auth."
+            ),
+        )
+
     if (
         _is_claude_sdk_framework(framework)
         and not no_subscription_auth
-        and provider_registry.get_provider(agent_model) is None
+        and _agent_spec is None
+        and agent_model.startswith("anthropic/")
     ):
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/bird_interact_agents/cloud/prereqs.py` around lines 264 - 275, The OAuth
prereq gate in prereqs.py is too broad because it only excludes registry models,
so non-Anthropic models like openai/... and gemini/... can incorrectly enter the
Claude subscription-auth path. Tighten the condition around the existing
`_is_claude_sdk_framework`, `no_subscription_auth`, and
`provider_registry.get_provider(agent_model)` checks so the OAuth branch is only
reachable for Anthropic-Claude models, mirroring the driver-side guard and
rejecting invalid subscription-auth combinations for other providers.
src/bird_interact_agents/cloud/driver.py (1)

189-224: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Gate the OAuth branch on an Anthropic agent model, not just “not registry”.

For framework="claude_sdk" and agent_model="openai/...", provider_registry.get_provider(agent_model) is None is true, so this branch requires and forwards CLAUDE_CODE_OAUTH_TOKEN. That is neither the registry path nor a valid Anthropic subscription-auth target.

Proposed fix
-    if (
+    _agent_spec = provider_registry.get_provider(agent_model)
+    if (
+        _is_claude_sdk_framework(framework)
+        and not no_subscription_auth
+        and _agent_spec is None
+        and not agent_model.startswith("anthropic/")
+    ):
+        raise PrereqError(
+            "--subscription-auth is Anthropic-only; got non-Anthropic "
+            f"agent model {agent_model!r}.",
+            remediation=(
+                "pass an anthropic/* --agent-model for subscription auth, "
+                "use a registry model provider path, or pass --no-subscription-auth."
+            ),
+        )
+
+    if (
         _is_claude_sdk_framework(framework)
         and not no_subscription_auth
-        and provider_registry.get_provider(agent_model) is None
+        and _agent_spec is None
+        and agent_model.startswith("anthropic/")
     ):

Then reuse _agent_spec for the registry branch below instead of recomputing it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/bird_interact_agents/cloud/driver.py` around lines 189 - 224, The OAuth
path in driver.py is currently gated only by
provider_registry.get_provider(agent_model) being None, which incorrectly sends
non-Anthropic models like openai/... into the CLAUDE_CODE_OAUTH_TOKEN branch.
Update the condition around the claude_sdk subscription-auth logic to require an
Anthropic agent model using _agent_spec (and its provider/model metadata) rather
than “not registry” alone, so only valid Anthropic subscription-auth targets
reach the OAuth checks and token forwarding. Also reuse the existing _agent_spec
for the registry/provider branch below instead of recomputing the lookup.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@README.md`:
- Around line 47-49: The updated README examples still invoke bird-interact
directly instead of using the repo’s required uv run prefix. Update the newly
changed command examples in the README so they consistently use uv run
bird-interact (or the equivalent project-tool invocation pattern already used
elsewhere), and apply the same fix to the other bird-interact examples
referenced in this section.

---

Outside diff comments:
In `@src/bird_interact_agents/cloud/driver.py`:
- Around line 189-224: The OAuth path in driver.py is currently gated only by
provider_registry.get_provider(agent_model) being None, which incorrectly sends
non-Anthropic models like openai/... into the CLAUDE_CODE_OAUTH_TOKEN branch.
Update the condition around the claude_sdk subscription-auth logic to require an
Anthropic agent model using _agent_spec (and its provider/model metadata) rather
than “not registry” alone, so only valid Anthropic subscription-auth targets
reach the OAuth checks and token forwarding. Also reuse the existing _agent_spec
for the registry/provider branch below instead of recomputing the lookup.

In `@src/bird_interact_agents/cloud/prereqs.py`:
- Around line 264-275: The OAuth prereq gate in prereqs.py is too broad because
it only excludes registry models, so non-Anthropic models like openai/... and
gemini/... can incorrectly enter the Claude subscription-auth path. Tighten the
condition around the existing `_is_claude_sdk_framework`,
`no_subscription_auth`, and `provider_registry.get_provider(agent_model)` checks
so the OAuth branch is only reachable for Anthropic-Claude models, mirroring the
driver-side guard and rejecting invalid subscription-auth combinations for other
providers.

In `@src/bird_interact_agents/run.py`:
- Around line 1663-1705: The subscription-auth gate in the run.py auth setup is
only excluding registry models, so claude_sdk runs with non-Anthropic
non-registry agent models can still pass and enable
BIRD_INTERACT_SUBSCRIPTION_AUTH. Update the validation in the same block around
is_claude_sdk, is_registry, and subscription_auth so that only Anthropic models
are allowed to proceed; reject any other agent_model before checking
CLAUDE_CODE_OAUTH_TOKEN or setting the env var. Keep the existing error path and
make the check explicit in the subscription-auth handling logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9c2ec475-09ff-4d70-b299-2c5e9f343a1e

📥 Commits

Reviewing files that changed from the base of the PR and between 5eb75a1 and e1beda8.

📒 Files selected for processing (14)
  • README.md
  • src/bird_interact_agents/agents/claude_sdk/sdk_env.py
  • src/bird_interact_agents/cloud/driver.py
  • src/bird_interact_agents/cloud/prereqs.py
  • src/bird_interact_agents/run.py
  • tests/cloud/test_driver.py
  • tests/cloud/test_prereqs.py
  • tests/test_claude_sdk_otf_ainteract_v1_run_wiring.py
  • tests/test_claude_sdk_otf_v1_run_wiring.py
  • tests/test_dev1602_run_subscription_flag.py
  • tests/test_one_shot_mode.py
  • tests/test_pydantic_ai_otf_encode_run_wiring.py
  • tests/test_sdk_subprocess_hermetic.py
  • tests/test_slayer_setup_flag.py
✅ Files skipped from review due to trivial changes (2)
  • tests/test_claude_sdk_otf_ainteract_v1_run_wiring.py
  • tests/test_slayer_setup_flag.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/bird_interact_agents/agents/claude_sdk/sdk_env.py
  • tests/test_sdk_subprocess_hermetic.py

Comment thread README.md Outdated
…c-ONLY + README uv run

CodeRabbit (3 majors, same root cause): the --subscription-auth gate was keyed
on "not a registry model" rather than "is an Anthropic model", so a claude_sdk*
run on a non-Anthropic, non-registry model (openai/*, gemini/*) wrongly took the
OAuth path. Fix in all three places:

- run.py _apply_subscription_auth_env: gate on agent_model.startswith("anthropic/")
  (is_anthropic) instead of "not is_registry"; the explicit-choice requirement and
  the True-path reject now both key off is_anthropic, so non-Anthropic models
  (registry or openai/gemini) are rejected with an Anthropic-only message.
- cloud/driver.py + cloud/prereqs.py: add an Anthropic-only reject for a
  claude_sdk* subscription run on a non-Anthropic non-registry model, before the
  OAuth branch (registry still routes to the provider-key branch).

CodeRabbit (minor): README local examples now use `uv run bird-interact`
consistently (project rule).

Tests added for the non-Anthropic non-registry rejection in run.py / driver /
prereqs. run_evaluation()'s signature defaults are intentionally left unchanged
(no production path uses them; CLI + cloud pass explicit values) per maintainer
decision. Full non-integration suite: 3387 passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ZmeiGorynych
ZmeiGorynych merged commit 2b8ad84 into main Jun 25, 2026
1 check passed
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.

1 participant