Skip to content

Fix Claude and Gemini provider authentication compatibility#147

Open
Daltonganger wants to merge 3 commits into
opgginc:mainfrom
Daltonganger:codex/fix-provider-auth-compatibility
Open

Fix Claude and Gemini provider authentication compatibility#147
Daltonganger wants to merge 3 commits into
opgginc:mainfrom
Daltonganger:codex/fix-provider-auth-compatibility

Conversation

@Daltonganger

@Daltonganger Daltonganger commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Detect the installed Claude Code version by executing claude --version and use the validated version in Anthropic OAuth request headers.
  • Share the same strict Claude version format in the bundled shell diagnostic, with CI coverage for malformed output and command failure.
  • Keep Antigravity accounts without explicit project metadata and use the API-supported default project fallback.
  • Centralize the Gemini quota request contract for Gemini CLI and Antigravity.
  • Preserve typed Gemini OAuth errors and retry with the Gemini CLI client only for OpenCode credentials rejected with a client-mismatch error.
  • Preserve partial Gemini success when another discovered account is invalid.

Root cause

Claude usage requests advertised claude-code/2.1.80 while the installed Claude Code client was newer. Anthropic returned HTTP 429 for the stale client identity, while the same credentials succeeded with the installed client version.

Current Gemini and Antigravity credential files can omit projectId. The quota API accepts project: "default", but OpenCode Bar rejected or skipped these accounts before making the request. This caused both providers to show an error even when a valid Antigravity refresh token was available.

Review hardening

  • Swift and shell now treat executable --version output as authoritative and accept only ASCII X.Y.Z or X.Y.Z (Claude Code).
  • OAuth retry is gated by auth-source provenance and typed client-mismatch errors. invalid_client and unauthorized_client retry on HTTP 400 or 401; invalid_grant, transport failures, Antigravity, and standalone Gemini CLI credentials do not retry.
  • A shared quota request sets a 10-second timeout. Its request/response contract test asserts the exact endpoint, headers, {"project":"default"} payload, and successful fixture decoding.
  • Standalone oauth_creds.json credentials use the same project precedence as the other sources: explicit project, quota project, environment project, then default.
  • Antigravity fallback continues past disabled or tokenless preferred accounts until it finds a usable refresh token.
  • The Antigravity regression test is now included in the Xcode test target.

Validation

  • Full macOS XCTest suite: 247 total, 231 passed, 16 credential-dependent tests skipped, 0 failures.
  • swiftlint lint --strict --no-cache --config .swiftlint.yml: 0 violations.
  • bash scripts/tests/query-claude-version-test.sh: all strict version-parser cases passed, including Unicode-digit rejection.
  • make lint-actions: all workflow validations passed.
  • Project file validation and git diff --check passed.
  • Clean Debug build completed and launched through the project Kill + Build + Run flow.

@opgg-ai-devops

opgg-ai-devops Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

✅ AI Code Review Completed

Review finished. Check the PR for inline comments.


📋 View Logs | 🤖 Model: openai/gpt-5.6-luna|low

@opgg-ai-devops opgg-ai-devops 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.

The direction is sensible: Gemini project precedence is centralized, the existing override order is preserved, and the new tests cover the pure helper behavior. CI is green across Lint, Build and Release, CI, and Test.

I’m requesting changes before merge because the new fallback paths are not contract-tested:

  • project: "default" is now sent to the quota API for accounts with missing project metadata, but the PR does not establish that this sentinel is accepted or maps to the correct account scope.
  • Any Gemini token-refresh failure now triggers a second OAuth-client attempt, even for accounts whose refresh token is not tied to the Gemini CLI client.
  • The shell version parser can pass malformed claude --version output straight into the User-Agent, and Swift and shell now carry separate version-discovery policies.

The review runner could not execute macOS XCTest because xcodebuild is unavailable in this Linux environment. The existing GitHub Test check is green, but request-level tests for the changed network and shell behaviors are still needed. Also, the Antigravity “no account with project ID” message is now stale after the new fallback and should be updated while fixing the fallback contract.

Comment thread scripts/query-claude.sh Outdated
Comment thread CopilotMonitor/CopilotMonitor/Providers/ClaudeProvider.swift
Comment thread CopilotMonitor/CopilotMonitor/Providers/GeminiCLIProvider.swift Outdated
Comment thread CopilotMonitor/CopilotMonitor/Providers/GeminiCLIProvider.swift Outdated
@opgg-ai-devops

opgg-ai-devops Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

✅ AI Code Review Completed

Review finished. Check the PR for inline comments.


📋 View Logs | 🤖 Model: openai/gpt-5.6-luna|low

@Daltonganger

Copy link
Copy Markdown
Contributor Author

Addressed all requested review changes in 22c83ce:

  • Added a tested Gemini request/response contract for project: "default" shared by Gemini CLI and Antigravity.
  • Replaced untyped OAuth fallback with typed errors and source-scoped client-mismatch retry.
  • Replaced Claude path inference with authoritative claude --version execution and strict matching in Swift and shell.
  • Added shell contract tests to CI and updated the stale Antigravity error message.

Validation: 236 Xcode tests total (220 passed, 16 credential-dependent skips, 0 failures), SwiftLint clean, shell contract tests passed, workflow validation passed, and live Gemini/Antigravity fetches succeeded. All four inline threads have been replied to and resolved.

@opgg-ai-devops opgg-ai-devops 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.

The direction is solid: typed Gemini OAuth failures, centralized quota requests, and the Claude shell contract test are all useful improvements. CI is green, and bash scripts/tests/query-claude-version-test.sh passes.

The concrete benefit is that provider auth fallbacks now preserve error reasons and can use the installed Claude version instead of always sending the stale default.

I’m requesting changes because a few new fallback paths still fail for valid configurations:

  • A 400 OAuth client-mismatch response skips the intended Gemini CLI-client retry.
  • Antigravity stops at an unusable preferred account instead of trying the next usable one.
  • The new default project policy is not applied consistently to standalone OAuth credentials.

The macOS XCTest/build path could not run in this Linux review environment. GitHub’s Test, Lint, Build and Release, and CI checks are green. The existing docs still describe the old Claude version behavior, so they should be updated with the implementation.

Comment thread CopilotMonitor/CopilotMonitor/Providers/AntigravityProvider.swift
Comment thread CopilotMonitor/CopilotMonitor/Services/TokenManager.swift
Comment thread CopilotMonitor/CopilotMonitor/Providers/ClaudeProvider.swift
Comment thread CopilotMonitor/CopilotMonitor/Services/TokenManager.swift
Comment thread CopilotMonitor/CopilotMonitor/Providers/GeminiCLIProvider.swift
@opgg-ai-devops

opgg-ai-devops Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

✅ AI Code Review Completed

Review finished. Check the PR for inline comments.


📋 View Logs | 🤖 Model: openai/gpt-5.6-luna|low

@Daltonganger

Copy link
Copy Markdown
Contributor Author

Second review round completed in bec84c4. This covers stale Antigravity fallback accounts, HTTP 400 OAuth client mismatches, ASCII-only Claude version parsing, standalone oauth_creds default-project handling, and the shared quota timeout. The Antigravity regression test is now active in the Xcode test target. Local validation: 247 tests total, 231 passed, 16 credential-dependent skips, 0 failures; SwiftLint, shell contracts, action validation, project validation, and a clean Debug build all passed.

@opgg-ai-devops opgg-ai-devops 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.

The direction is solid: typed Gemini OAuth failures, the centralized quota request, and the Claude version contract tests are all useful. CI is green, and the shell contract test passes locally too.

The concrete benefit is real: Claude requests can identify the installed client version, and Gemini/Antigravity accounts without project metadata can reach the quota endpoint instead of being rejected up front.

I’m requesting changes because the new process/network fallback paths still add avoidable production risk:

  • Claude launches claude --version on every provider refresh, with a three-second timeout and no cache. A missing or hung executable can therefore add that delay to every periodic refresh.
  • Gemini quota calls have a 10-second timeout, but the OAuth token request does not have an explicit timeout, so the refresh can still hang before the quota timeout is reached.
  • The Swift and shell implementations now duplicate the Claude version/discovery policy, but their executable search paths differ (CLAUDE_CODE_PATH/fallback paths in Swift versus only command -v in the script). They can report different versions for the same machine.
  • The Swift process runner accepts valid-looking stdout without checking the exit status, so a failing claude --version command can still become the User-Agent.

GitHub’s macOS Build, Test, SwiftLint, and Actions Lint checks are green. Local macOS XCTest/SwiftLint could not run here because this review environment is Linux.


do {
let (data, response) = try await URLSession.shared.data(for: request)
let (data, response) = try await session.data(for: request)

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.

Major: Perf missing token timeout: refresh can hang
GeminiQuotaAPI has a 10-second timeout, but this token request inherits the session/default timeout. A stalled OAuth endpoint can therefore block the provider before the quota request starts. Set an explicit token-request timeout and cover it in the request contract test.

@@ -191,11 +321,12 @@ final class ClaudeProvider: ProviderProtocol {
throw ProviderError.authenticationFailed("Anthropic access token not available")

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.

Minor: Bug ignored exit status: failed binary can look valid
The process termination handler returns stdout without checking the exit status. A broken claude executable can print a valid-looking version and exit nonzero, and that output will still become the User-Agent. Require a zero termination status before accepting the output, and add a regression test.

throw ProviderError.authenticationFailed("Anthropic access token not available")
}

let installedVersion = await ClaudeOAuthRequestPolicy.installedClaudeCodeVersion()

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.

Major: Perf repeated process lookup: slows every refresh
This runs before every account fetch, so a missing or hung claude executable can add up to three seconds to every periodic refresh even though the version is unchanged. Cache the discovered version for the provider/process lifetime, or resolve it only when needed and invalidate it explicitly.

Comment thread scripts/claude-version.sh
fi

printf '%s\n' "$CLAUDE_DEFAULT_CODE_VERSION"
}

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.

Minor: Breaking split discovery policy: app and script disagree
The shell diagnostic only calls command -v claude, while the Swift path also honors CLAUDE_CODE_PATH, every supplied PATH entry, and fallback locations. An installation found by the app can therefore fall back to 2.1.80 in query-claude.sh, contradicting the documented “same version policy.” Make discovery order identical across both consumers or drive both from shared contract fixtures.

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