Commit Graph

3 Commits

Author SHA1 Message Date
joaomarcos 7cbffdd125 refactor(anthropic): split the adapter godfile into four modules
`agent/anthropic_adapter.py` was 3,423 lines and this PR adds another auth
boundary to it. Split along the seams that were already there, so the
credential surface this PR changes has a single owner instead of being
interleaved with request building:

- `agent/anthropic_endpoints.py` (258) — base-URL/endpoint-family predicates.
  Pure functions over a URL string, which is what lets both of the modules
  below depend on it without a cycle.
- `agent/anthropic_message_convert.py` (1,225) — OpenAI-style to Anthropic
  Messages payload conversion: model ids, tool schemas, content/thinking
  blocks, tool_use pairing, cache_control, screenshot eviction, blank-block
  scrubbing.
- `agent/anthropic_credentials.py` (910) — credential sources, the OAuth
  flows, and the refresh commit (`CredentialPersistError` and both singleton
  writers).
- `agent/anthropic_adapter.py` (1,215) — client construction and the Messages
  API call, re-exporting every name from the three modules above so existing
  `from agent.anthropic_adapter import ...` imports keep resolving. The
  re-export surface was diffed against the pre-split module: nothing dropped.

Call sites that read a moved name through the adapter's namespace at runtime
(`credential_pool._refresh_entry_impl`, `auxiliary_client`) now import it from
the defining module, so there is one patchable seam rather than two bindings
that can disagree. The tests that monkeypatched those seams were retargeted to
match; no assertion was changed.

No behavior change.
2026-08-29 18:34:35 -07:00
joaomarcos 0099f250c2 fix(auth): close Anthropic OAuth review gaps 2026-08-29 18:34:35 -07:00
joaomarcos 739dc6d198 fix(auth): close Anthropic OAuth CSRF gap, cross-process refresh race, and API-key shadowing
Dashboard PKCE login reused the code_verifier as the OAuth state (leaking
it and disabling CSRF validation) and never checked state on callback --
the same class of bug already fixed for the CLI flow. Credential-pool
refresh excluded "anthropic" from the cross-process lock Codex/xAI already
get, so concurrent Hermes processes racing a single-use refresh token could
leave the loser stuck exhausted with no recovery for hermes_pkce/dashboard
sources. The dashboard OAuth save also never cleared a stale
ANTHROPIC_API_KEY, which resolve_anthropic_token() prioritizes over the
OAuth pool entry by design -- so a leftover key silently kept billing
pay-per-token after a Claude Pro/Max login.

A concurrency stress test written to validate the refresh-race fix under
load surfaced a fifth, unrelated bug: _auth_store_lock()'s Windows
lock-file "ensure content" write was unguarded and could raise an uncaught
PermissionError under real contention -- affecting every single-use-token
provider sharing that lock, not just Anthropic.

Fixes #87887, #87888, #87889.
2026-08-29 18:34:35 -07:00