Conversation
- Introduced Gemini plan as a new provider type with OAuth integration. - Implemented authentication flow including token exchange and callback server. - Updated settings schema to include Gemini provider and associated OAuth fields. - Enhanced chat model settings to support Gemini-specific configurations. - Added migration logic to transition existing settings to support the new provider. - Comprehensive tests added for migration and Gemini functionality.
…Auth access for Claude subscriptions - Added a warning in the README about the risks associated with connecting a Claude subscription, including potential account bans. - Introduced a warning message in the PlanConnectionsSection to inform users about the restrictions on third-party OAuth access and advised caution in usage. - Enhanced styles for warning messages to improve visibility and user awareness.
📝 WalkthroughWalkthroughAdds Gemini Plan support: OAuth-based desktop PKCE flow, provider implementation, project onboarding/caching, code-assist adapter with streaming, UI to connect/disconnect Gemini Plan, schema updates, and a migration to include gemini-plan in defaults. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Modal as ConnectGeminiPlanModal
participant Server as Local Callback Server
participant Google as Google OAuth Endpoint
participant TokenExch as Token Exchange
participant ProjAPI as Gemini Project API
participant Adapter as GeminiCodeAssistAdapter
rect rgba(100,150,255,0.5)
User->>Modal: Click "Login to Google"
Modal->>Modal: Generate PKCE & state
Modal->>Google: Open authorize URL (code_challenge, scopes)
Google-->>User: Consent screen
User->>Google: Grant permission
Google->>Server: Redirect with code & state
Server->>Server: Validate state & respond UI
Server-->>Modal: Resolve code
end
rect rgba(150,200,150,0.5)
Modal->>TokenExch: Exchange code (pkce verifier)
TokenExch-->>Modal: accessToken, refreshToken, expiresAt, email
Modal->>ProjAPI: Load or onboard project (accessToken)
ProjAPI-->>Modal: projectId / managedProjectId
Modal->>Modal: Persist OAuth to provider settings
end
rect rgba(200,180,100,0.5)
User->>Adapter: Request code-assist (headers with accessToken, projectId)
alt access token expired
Adapter->>TokenExch: Refresh token
TokenExch-->>Adapter: New accessToken
end
Adapter-->>User: Stream or return generation
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Fix all issues with AI agents
In `@src/components/settings/modals/ConnectGeminiPlanModal.tsx`:
- Around line 186-199: The current manual-connect flow fails because
ensureAuthContext may generate new PKCE/state that won't match a user-pasted
redirect URL (redirectState), causing the state check in the
ConnectGeminiPlanModal to always fail; update the logic in the block that calls
ensureAuthContext (using ensureAuthContext, effectivePkceVerifier,
effectiveState, redirectState, setManualError) so that when a redirectState is
present you parse/extract the original state from that redirect URL and use it
as the authoritative effectiveState (or skip calling ensureAuthContext when
manual paste is detected), ensure the pkceVerifier is obtained consistently for
that manual flow, and only run the mismatch error branch when neither a parsed
redirectState nor a valid ensured state exist.
In `@src/core/llm/geminiAuth.ts`:
- Around line 35-38: The startGeminiCallbackServer promise can hang if the
server is closed externally because its internal finalize callback is never
invoked; modify startGeminiCallbackServer to capture and store the finalize
resolver (e.g., a variable like currentGeminiFinalize) when creating the
auth-wait promise, and update stopGeminiCallbackServer to call that stored
finalize with an appropriate error when
isGeminiCallbackStopping/geminiCallbackServer is triggered so the awaiting
promise is rejected/cancelled immediately (also clear the stored finalize after
calling it and honor CALLBACK_TIMEOUT_MS as before).
- Around line 76-111: The code currently sends GEMINI_OAUTH_CLIENT_SECRET from
the client in exchangeGeminiCodeForTokens and refreshGeminiAccessToken; remove
the secret from these calls or move the token exchange to a secure backend:
either (A) verify that Google's Gemini/OAuth token endpoint accepts PKCE-only
flows and then delete GEMINI_OAUTH_CLIENT_SECRET usage from
exchangeGeminiCodeForTokens and refreshGeminiAccessToken (remove client_secret
parameter and ensure redirect_uri and code_verifier are retained), or (B)
implement a backend endpoint to perform the POST to
https://oauth2.googleapis.com/token (keeping GEMINI_OAUTH_CLIENT_SECRET
server-side) and change the client-side
exchangeGeminiCodeForTokens/refreshGeminiAccessToken to call that backend
instead; reference functions exchangeGeminiCodeForTokens,
refreshGeminiAccessToken and the constant GEMINI_OAUTH_CLIENT_SECRET when making
the change.
In `@src/core/llm/geminiPlanProvider.ts`:
- Around line 88-155: getAuthContext currently treats a missing expiresAt as
still valid and only invalidates the new refresh token after rotation; change
the expiry check in getAuthContext to trigger a refresh when expiresAt is
missing or expired (i.e. treat !this.provider.oauth.expiresAt the same as
expired) and when refreshing call invalidateProjectContextCache with the
previous refresh token before replacing provider.oauth (and also with the new
token if rotation occurred) so cache entries for the old token are cleared;
update the refresh flow around refreshGeminiAccessToken, updatedOauth
assignment, invalidateProjectContextCache and await
this.onProviderUpdate?.(this.provider.id, { oauth: updatedOauth }) accordingly
to use the old token for invalidation and handle token rotation.
In `@src/core/llm/geminiProject.ts`:
- Around line 116-147: The current loadManagedProject function swallows all
non-2xx responses and exceptions by returning null, which later causes
ProjectIdRequiredError to be thrown incorrectly; instead, on non-2xx responses
(in loadManagedProject) parse/collect the response status and body and throw a
distinct error (e.g., LoadProjectError) that includes status and diagnostic
info, and in the catch block rethrow or wrap the caught error in that same
LoadProjectError so callers can distinguish auth/network/load failures from a
genuine missing project id; update callers that expect null (where
ProjectIdRequiredError is raised) to treat this new error separately.
- Around line 149-203: The loop in onboardManagedProject always calls await
wait(delayMs) even after the final retry, causing an unnecessary delay; modify
the for loop so the wait is only invoked between attempts (e.g., call
wait(delayMs) only when attempt < attempts - 1) ensuring no delay after the last
iteration and preserving current retry/return logic.
🧹 Nitpick comments (5)
src/components/settings/sections/PlanConnectionsSection.tsx (1)
87-98: Consider expanding the warning scope to include Gemini.The warning banner specifically mentions Anthropic OAuth restrictions, but since Gemini Plan also uses third-party OAuth (connecting to Google via a non-official client), similar risks may apply. Consider generalizing the warning or explicitly noting the risk status for Gemini.
src/core/llm/geminiCodeAssistAdapter.ts (2)
65-66: Prototype mutation for SDK compatibility is fragile but documented.Using
Object.setPrototypeOfto enable SDK helper methods (likeresponse.text) on plain JSON is a clever workaround, but this pattern is fragile—if the SDK'sGenerateContentResponseclass changes its internal structure, this could silently break. Consider adding a comment noting this coupling.
140-141: Consider documenting the in-place mutation.
addThoughtSignaturesmutates thecontentEntriesarray's nestedcontent.partsin place. While this works, adding a brief comment clarifying the mutation intent would improve readability.src/components/settings/modals/ConnectGeminiPlanModal.tsx (2)
149-152: Consider logging errors for debugging.Empty catch blocks with generic error messages make troubleshooting OAuth issues difficult. Consider logging the actual error (e.g., to console in development) while showing the user-friendly message.
💡 Suggested improvement
- } catch { + } catch (err) { + console.error('Gemini OAuth auto-connect failed:', err) setAutoError( 'Automatic connect failed. Paste the full redirect URL below and click "Connect with URL".', )
135-136: Popup blockers may prevent automatic OAuth flow.
window.opencan be blocked by popup blockers, leaving the user waiting indefinitely. Consider detecting if the window opened successfully and guiding the user to allow popups if needed.
Description
Summary
Adds support for connecting a Google AI subscription (Gemini Plan) to use Gemini Code Assist quota, similar to existing Anthropic and OpenAI plan connections.
Changes
New Provider Implementation
GeminiPlanProvider- Main provider that handles authentication and delegates to the adapterGeminiCodeAssistAdapter- Translates requests to Gemini Code Assist API format (cloudcode-pa.googleapis.com)geminiAuth.ts- OAuth flow with PKCE for Google authentication, including local callback servergeminiProject.ts- Manages project context discovery and onboarding for Code AssistType & Schema Updates
gemini-planprovider type toLLMProvider,ChatModel, andEmbeddingModelschemasgemini-planmodels (same as regular Gemini)UI
PlanConnectionsSectionConnectGeminiPlanModalfor OAuth login flow with automatic callback handling and manual URL fallbackDefault Models
gemini-3-pro-preview (plan)gemini-3-flash-preview (plan)Technical Notes
thoughtSignaturefield to assistant tool calls to satisfy Code Assist API validationTesting
Checklist before requesting a review
npm run lint:checkandnpm run type:check)npm run test)Summary by CodeRabbit
New Features
Settings
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.