Repository navigation
Coach: sign in with ChatGPT to use your ChatGPT plan instead of an API key - #602
Merged
Merged
Conversation
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The AI coach only works with an API key today, so anyone who already pays for ChatGPT pays twice to use it. On Sept 29 OpenAI opened Sign in with ChatGPT to open-source apps: an app can register itself, with no client secret and no approval step, and run eligible Responses API requests on the user's own ChatGPT plan (docs). This PR adds that as a coach provider. It's the official route, not the unofficial one that reuses Codex CLI tokens. OpenAI's docs specifically say not to call ChatGPT's
backend-api.What users see
Before: AI settings list Ollama, LM Studio, OpenAI, Anthropic and OpenRouter. Every cloud option needs a base URL and an API key.
After: a ChatGPT option sits between the local and cloud presets. Picking it hides the base URL, API and key fields and shows Continue with ChatGPT. After sign-in:
<model>· Using ChatGPT plan".The coach, the briefings and the journal all use it, because they share
CoachEngine.postChat.How it works
lib/coach/coach_chatgpt.dart: OAuth with PKCE, state and nonce, plus dynamic client registration (dynamic_agent_clientwith a persistedurn:uuidhost id). The issued client id is saved and reused on later sign-ins, as OpenAI asks.127.0.0.1, as the docs require. The page opens withLaunchMode.inAppBrowserView(SFSafariViewController / Custom Tab) so the app stays in the foreground and the listener stays alive; on iOS the full Safari app would suspend us. There's a Cancel sign-in button, and leaving the screen cancels too, because the app gets no signal when the user just closes the browser.first_unlockaccessibility as the BYOK key, so a locked-phone background relaunch can still read them. Refresh is serialized so concurrent callers share one rotation. A dead refresh token signs the user out; a network or server error keeps the credentials.postChatsendsstream: trueandstore: false, buffers the SSE stream and passes theresponse.completedpayload to the existingCoachResponses.replyparser.systeminput items becomedeveloper(the route rejectssystem). Sampling params were already never sent on Responses. The fail-closed request size ceiling still applies.subscription_sharing_*codes become plain messages. Billing errors stop the request and never fall back to an API key. Signing out leaves the coach in ChatGPT mode, unconfigured, instead of quietly switching back to a saved key.CoachConfiggainsusesChatGpt, andconfigured,apiBase,apiandkeyUnreadablefollow it, so the existing consent prompts (ECG, briefing) seeapi.openai.comas a cloud endpoint. The BYOK settings stay untouched for switching back.…#chatgpt), so a plan turn never replays encrypted reasoning from an API-key turn on the same base.CustomTabsService<queries>entry, so Android 11+ finds a Custom Tab instead of falling back to a WebView.guides/AI_COACH.md, andPRIVACY.mdupdated.The ID token's issuer, audience, expiry and nonce are checked. The signature isn't: the token arrives straight from the token endpoint over TLS in a PKCE code exchange, which OIDC Core §3.1.3.7(6) allows. I can add JWKS verification with pointycastle if you'd rather have it.
Testing
flutter analyzeis clean (Flutter 3.41.6, the CI pin).test/coach_chatgpt_test.dart, 11 tests:stream/store, notemperature,system→developerrussian_review_findings_test: the new card made the setup list taller, which left "List models" a few px off the 800×600 test surface afterscrollUntilVisible. I addedensureVisiblebefore the tap.mainon macOS: three timezone-dependent tests, which pass withTZ=UTC, andhealth_workout_export_delete_gate_test, becauseHealthExport.isAppleis true on a macOS host.Tested on a device: iPhone 16 Pro Max on iOS 27.0.1, with a real ChatGPT account. Sign-in works end to end, and coach replies come back on the plan. The first device run found that the route's
response.completedevent can carry an emptyoutput, which made every reply fail with "Empty response from provider". ec6035b builds the output fromresponse.output_item.doneand adds a test for it. ec6035b also addresses all 13 CodeRabbit/Sourcery findings.Summary by Sourcery
Enable users to connect their ChatGPT plan to the AI coach through OpenAI’s official sign-in flow instead of configuring an API key.
New Features:
Bug Fixes:
Enhancements:
Build:
Documentation:
Tests:
Chores:
Summary by CodeRabbit