Skip to content

fix(anthropic): let Claude Code auto mode's server review through the native OAuth lane - #8507

Merged
lidge-jun merged 3 commits into
lidge-jun:devfrom
david-wang-0:fix/claude-auto-mode-native-review
Nov 11, 2026
Merged

lidge-jun merged 3 commits into
lidge-jun:devfrom
david-wang-0:fix/claude-auto-mode-native-review

Conversation

@david-wang-0

@david-wang-0 david-wang-0 commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Claude Code's auto mode fails through the managed native Messages lane on an Anthropic OAuth pool account: every action that auto mode sends for review is denied, and the Claude Code debug log says [server-classifier] Bash: no server verdict for this call (server_no_result). Claude Code (2.1.322) asks the Anthropic API to review risky tool calls as part of each model request. A session that fetches no feature flags (telemetry or nonessential traffic turned off, or behind a gateway) has no local classifier to fall back on, so an action without a server verdict is denied rather than reviewed locally. On a direct connection the same request works.

Three things on the native lane kept the review from happening. Each is fixed here, with a regression test.

  1. The review beta and its body field were dropped. In auto mode Claude Code sends anthropic-beta: …,dangerous-tool-use-2026-09-03,… and a top-level safeguards field (the classifier context), and reads the verdicts from message_delta.delta.safeguard_results. The beta was not on the native-client allowance, and safeguards is not a passthrough field, so both were removed and the API ran no review. Fix: one row in src/adapters/anthropic/beta-body-pairs.ts, dangerous-tool-use-2026-09-03 ↔ ["safeguards"], nativeClientOnly: true, the same policy as context_management. The field is forwarded only while the beta survives into the final header, and only for an observed Claude Code client on first-party Anthropic.
  2. OAuth renamed Claude Code's own tools. anthropicOAuthWireBody prefixes every declared client tool with custom_ (Bash → custom_Bash). The server review matches calls by tool name, so with the beta and field forwarded it answered "safeguard_results":[{"type":"dangerous_tool_use","status":{"type":"available","tool_uses":{}}}]: it reviewed nothing, and Claude Code again found no verdict. An observed Claude Code client already sends the names its own OAuth login uses (Claude Code sends Bash over OAuth directly), so src/adapters/anthropic/passthrough.ts now keeps an observed client's tool names (hasObservedAnthropicClientIdentity). The collision checks are unchanged. The reverse map is empty, so restoreOAuthToolNamesInSse / …InMessage relay the answer untouched, including safeguard_results. Other OAuth callers (generated Responses traffic, unobserved clients) keep the prefix. With both changes the same request answered "tool_uses":{"toolu_…":{"type":"evaluated","outcome":"not_flagged"}} and Claude Code ran the command.
  3. Background sessions were not observed clients. claude --bg sessions send X-App: cli-bg, but src/adapters/anthropic/client-identity.ts accepted only X-App: cli, a required header. So every background session fell back to the generic shape: its native betas were dropped, its tools prefixed, and the first-party OAuth API refused the request with 400 ("The provider rejected the request."). It now accepts cli and cli-bg. Any other value still makes the client unobserved. No other place in src/ compares X-App; the proxy's own fingerprint keeps sending cli.

Evidence comes from a live Claude Code 2.1.322 session in auto mode through a local proxy on an Anthropic OAuth pool account, with a logging relay between client and proxy. The captured request carried the beta and safeguards. The verdict frames are quoted above, before and after the tool-name fix. A curl through the patched lane without the observed identity headers still drops both, as intended.

With the three changes applied as a local patch on OpenCodex 2.100.1 (these files are identical on dev), auto-mode claude -p runs asking for curl -sI https://example.com | head -1 completed with no [server-classifier] warning for Opus, Sonnet and Fable models on Anthropic OAuth pool accounts, both on direct model routes and through a combo targeting the pool account. A background (claude --bg) session and a resumed background session (--bg --resume) each ran Bash and SendMessage calls in auto mode.

Precondition and follow-up: this fixes the native lane (protocols.rollout.managedMessagesNative and managedMessagesNativeOAuth on). The legacy bridge (messages → responses-internal → ir → messages, the default for OAuth providers) drops both the beta and safeguards as well, so auto mode through the bridge on an OAuth account still has no server review. That is left as a follow-up rather than mixed into this change. Combo routes on /v1/messages already take the native lane once managedMessagesNative is on (nativeComboSource in src/server/claude-messages.ts), so they get the fix too; the combo case above was verified live.

Security boundary: change 2 alters the body sent with an OAuth token (tool names are no longer rewritten for an observed Claude Code client), and change 3 widens which caller headers count as an observed Claude Code client. Both touch the authentication surface, so per MAINTAINERS.md this needs a maintainer to sponsor it (maintainer-sponsored). Neither grants credential authority: the identity bundle stays a compatibility observation, and no caller credential, beta outside the allowlist, or field outside the pairing table reaches the provider. The new pairing row follows the existing context_management rule exactly.

Docs: structure/data-planes/protocol-paths.md (managed native Messages contract: pairing table, OAuth tool names, X-App values; still 600 lines) and docs-site/src/content/docs/guides/protocol-paths.md (English source). The Turkish translation does not cover these sentences and is not contradicted.

Verification

Bun 1.4.4 (the version scripts/test.ts expects), on Linux. Branch based on dev at ddee2c200.

  • bun test tests/adapters/anthropic/anthropic-messages-passthrough-oauth.test.ts tests/adapters/anthropic/anthropic-beta-body-pairs.test.ts tests/adapters/anthropic/anthropic-beta-allowlist.test.ts tests/adapters/anthropic/anthropic-messages-passthrough.test.ts tests/adapters/anthropic/anthropic-client-identity.test.ts tests/claude-integration/messages-native-oauth.test.ts tests/claude-integration/messages-native.test.ts tests/claude-integration/messages-native-scope.test.ts: 303 pass, 0 fail (2407 expect() calls, 8 files).
  • Red check: with the src/ changes stashed, the new and updated cases fail (6 in the beta, OAuth-body and OAuth-lane files; 1 in anthropic-client-identity.test.ts). With the changes they pass.
  • bun run test:changed: 6850 pass, 34 skip, 0 fail (6884 tests across 280 files), on the final source and tests before two one-line doc edits.
  • OCX_TEST_MAIN_TIMEOUT_MS=3600000 bun run test --parallel=2: parallel lane 63131 pass, 690 skip, 6 fail (63827 tests across 3019 files, 1627 s); serial lane 21 files, 748 pass, 24 skip, 0 fail. None of the 6 failures is in a file this change touches or imports. Run alone, tests/providers/deepseek-image-files.test.ts (1) and tests/usage/request-decompress.test.ts (3) pass; in the suite they hit RangeError: Out of memory under the per-process address-space cap the run used. tests/service/service-definition-escaping.test.ts (launchd plist parse through a local plutil) and tests/codex-integration/codex-composed-acceptance.test.ts ("A-reduced", OFF Codex home) fail the same way on the base commit ddee2c200 without this change. The run used two workers instead of four and a longer main-lane timeout because of a per-process memory limit on the test machine; with two workers the default 900 s budget expired.
  • bun run typecheck: no type errors in 2,384 files.
  • bun run structure:check: structure/ SSOT checks passed.
  • bun run privacy:scan: passed.

New regression cases:

  • anthropic-beta-body-pairs.test.ts, "auto mode server review": the beta and safeguards are forwarded for an observed client (key and OAuth); a generic caller gets neither, and the drop is named; safeguards without the beta in the final header is dropped; a compatible destination receives neither.
  • anthropic-messages-passthrough-oauth.test.ts and anthropic-beta-allowlist.test.ts: an observed client's tool names, history and context_management.exclude_tools are forwarded as sent, with an empty reverse map. A generic OAuth caller (including one whose operator header admits context_management) keeps the custom_ prefix and the exclude_tools rewrite.
  • messages-native-oauth.test.ts, end to end through handleClaudeMessages: an observed client in auto mode sends the beta, safeguards and unprefixed names, and the client stream carries safeguard_results unchanged. The same request without the observed identity is prefixed and loses the review.
  • anthropic-client-identity.test.ts: an X-App: cli-bg bundle is observed and forwarded with its own X-App; cli-fg, bg, CLI-BG and cli,cli-bg are not.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.

Agent attribution: Claude Opus 5.5 (Claude Code).

🤖 Generated with Claude Code

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • Required local validation passed; commands, results, and any full-suite exception are documented.

  • I pushed my PR to a recent dev commit (at most 10 behind; a maintainer may still ask for the exact tip before merge).

  • I resolved all correct Codex and CodeRabbit findings.

  • My PR is ready for review.

Maintainer follow-up (bundle lane, head 4c90221)

Commits added on this branch by a maintainer (no force push): a merge of current dev and a docs note from the security review.

  • Why the prefix existed. The custom_ prefix arrived with Anthropic OAuth (14fc3fc, "required or Claude Pro/Max OAuth 401s" for third-party tool names). f34f742 then renamed it from proxy_ while hardening client fingerprints. A recognized Claude Code client already sends the names its own OAuth login sends, so keeping them reproduces the first-party request. Generated Responses traffic and unrecognized callers keep the prefix.
  • Security review: PASS. It was a separate review.
    • No credential, account-selection or destination change.
    • safeguards and identity headers reach only api.anthropic.com for a recognized client.
    • The collision checks are intact, and the empty reverse map relays the answer untouched.
    • One Low finding remains: recognition is header-based, so a client that copies Claude Code's headers now also sends unprefixed names on the OAuth account. docs-site/.../guides/protocol-paths.md now states that, and that an operator-added dangerous-tool-use beta forwards safeguards for unrecognized callers whose names stay prefixed.

Verification at 4c90221:

  • The PR's five test files fail 7 cases on origin/dev with only the tests applied. The branch passes 164/164.
  • bun run typecheck, structure:check and privacy:scan pass, as do the file-size ratchet, test layout and Lab boundary.
  • bun test --changed=<merge-base>: 285 files, 8 fail / 8 errors. The failures are in catalog-omit-disabled-native (fails identically on unmodified dev on this host) and chat-native-spend (22/22 alone, a load flake).
  • NOT RUN: the full suite, Windows/macOS lanes, and a live Claude Code auto-mode session.

Summary by CodeRabbit

  • New Features
    • Recognized Claude Code background sessions now retain their client identity.
    • For eligible first-party Anthropic requests, recognized Claude Code clients can retain tool names and use the safeguards feature with its matching beta header.
    • OAuth context-management settings continue to use the appropriate tool names when requests require name prefixing.
  • Documentation
    • Updated protocol guides to explain client identity, beta-header and safeguards handling, and OAuth tool-name behavior.

… native OAuth lane

Claude Code in auto mode asks the Anthropic API to review risky tool calls
as part of each model request. A session without feature flags (telemetry
off, or behind a gateway) has no local classifier to fall back on, so on
the managed native Messages lane over Anthropic OAuth every reviewed call
was denied ("[server-classifier] ... server_no_result"). Three causes:

- The dangerous-tool-use-2026-09-03 beta and its top-level `safeguards`
  body field were not admitted. They are now one nativeClientOnly
  beta/body pair in beta-body-pairs.ts, the same policy as
  context_management.
- OAuth renamed every declared client tool to `custom_<name>`, so the
  server review matched none of the calls (`tool_uses: {}`). An observed
  Claude Code client already sends the tool names its own OAuth login
  uses, so it now keeps them; the reverse map is empty and the verdicts in
  message_delta.delta.safeguard_results reach the client unchanged. Other
  OAuth callers keep the prefix; the collision checks are unchanged.
- Background sessions (`claude --bg`) send `X-App: cli-bg`, but client
  identity accepted only `cli`, so they fell back to the generic shape and
  the OAuth API refused them with 400. `cli` and `cli-bg` are accepted.

Regression tests cover the pairing, the unprefixed names for an observed
client (and the prefix for a generic one), the end-to-end native OAuth
lane including relayed verdicts, and the cli-bg identity. The managed
native Messages contract in structure/data-planes/protocol-paths.md and
the protocol-paths guide are updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

Caution

Review failed

The pull request is closed.

Walkthrough

The change accepts cli-bg as a Claude Code identity value, preserves tool names for observed Claude Code clients on Anthropic OAuth, and pairs safeguards with its beta for eligible native-client requests. Tests and protocol guides cover these behaviors.

Changes: Anthropic OAuth behavior

Layer / File(s) Summary
Client recognition and OAuth tool-name handling
src/adapters/anthropic/client-identity.ts, src/adapters/anthropic/passthrough.ts, tests/adapters/anthropic/anthropic-client-identity.test.ts, tests/adapters/anthropic/anthropic-messages-passthrough-oauth.test.ts, tests/adapters/anthropic/anthropic-beta-allowlist.test.ts, structure/data-planes/protocol-paths.md, docs-site/src/content/docs/guides/protocol-paths.md
Identity capture accepts X-App: cli-bg for recognized Claude Code clients. Observed clients retain tool names on OAuth requests; other callers continue to receive prefixed names. Tests cover identity capture and exclude_tools rewriting.
Safeguards beta and request handling
src/adapters/anthropic/beta-body-pairs.ts, tests/adapters/anthropic/anthropic-beta-body-pairs.test.ts, tests/claude-integration/messages-native-oauth.test.ts, structure/data-planes/protocol-paths.md, docs-site/src/content/docs/guides/protocol-paths.md
The adapter registers the dangerous-tool-use beta and pairs it with the safeguards body field for eligible native-client requests. Tests cover authentication modes, destination behavior, and streamed safeguard verdicts.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant ClaudeCode
  participant AnthropicAdapter
  participant AnthropicAPI
  ClaudeCode->>AnthropicAdapter: Send recognized identity and native Messages request
  AnthropicAdapter->>AnthropicAPI: Forward tool names, safeguards, and review beta
  AnthropicAPI->>AnthropicAdapter: Return streamed safeguard verdicts
  AnthropicAdapter->>ClaudeCode: Forward verdicts
Loading

Possibly related PRs

  • lidge-jun/opencodex#7838: Both changes use Claude Code identity on Anthropic OAuth requests. This PR changes tool-name preservation and native-client beta/body-field handling.
Pre-merge checks: Passed 1 · Inconclusive 1

Failed Failed checks: 1 inconclusive

Check name Status Explanation Resolution
Title check Pending This assessment has not completed. The check will be retried on the next pre-merge check run.
Passed Passed checks: 1 passed

Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@github-actions

Copy link
Copy Markdown
Contributor

✅ Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the bug Something isn't working label Nov 10, 2026
@github-actions

github-actions Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

⏳ DRAFT

  • review readiness checklist open (3/4 boxes ticked).

What to do

  • Tick all four boxes in the PR description once you're done (currently 3/4).
  • The PR is more than 10 commits behind dev; the latest dev box has been unticked.
  • The checklist has been reset: re-test against the latest code and tick the boxes again.

Review readiness checklist

  • ✅ Required local validation passed; commands, results, and any full-suite exception are documented.
  • ⬜ I pushed my PR to a recent dev commit (at most 10 behind; a maintainer may still ask for the exact tip before merge).
  • ✅ I resolved all correct Codex and CodeRabbit findings.
  • ✅ My PR is ready for review.

3/4 boxes ticked.

The PR is more than 10 commits behind dev; the latest dev box has been unticked.
The checklist has been reset: re-test against the latest code and tick the boxes again.
Automatic draft conversion failed. Please convert this pull request to a draft manually until every box above is ticked.

Hygiene

✅ Deterministic PR hygiene checks passed.

lidge-jun and others added 2 commits November 11, 2026 18:53
… turns on

Security review follow-up for lidge-jun#8507: recognition is header-based, so a client
that copies Claude Code's identity also sends unprefixed tool names on the
OAuth account, and an operator-added dangerous-tool-use beta forwards
safeguards for unrecognized callers whose names stay prefixed.

Co-authored-by: david-wang-0 <72378768+david-wang-0@users.noreply.github.com>
@lidge-jun

Copy link
Copy Markdown
Owner

Maintainer judgment (bundle lane: Anthropic / Chat protocol fixes, head 4c902210fb)

  • Value: Claude Code auto mode on the managed native OAuth lane denied every action it sent for review (server_no_result). Two things caused it: the review beta and its safeguards field were dropped, and the server could not match the client's custom_-prefixed tool names. Background (claude --bg) sessions were also not recognized.
  • Scope of the UX change: only a recognized Claude Code client on first-party Anthropic changes. It keeps the review beta, safeguards, and (over OAuth) its own tool names. Generated Responses traffic, unrecognized callers, compatible hosts and cloud destinations are unchanged.
  • Security: a separate security review passed (no credential, account or destination change). Its Low finding, header-based recognition now also turning off the prefix for a client that copies Claude Code's headers, is documented in protocol-paths.md. Unverified: whether Anthropic flags unprefixed third-party tool names on OAuth today.
  • Regression evidence for existing users: tests pin generic callers (neither beta nor field), compatible hosts, prefix retention for unrecognized OAuth callers, and exact X-App matching (cli/cli-bg only). --changed failures are pre-existing or flaky as stated in the description.
  • Windows: no platform-specific code.
  • NOT RUN: the full suite, cross-platform lanes, and a live auto-mode session.

@github-actions
github-actions Bot marked this pull request as ready for review November 11, 2026 10:07
@lidge-jun
lidge-jun merged commit 3856b27 into lidge-jun:dev Nov 11, 2026
39 of 40 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants