feat: add GitHub Copilot as LLM provider with device flow OAuth#323
feat: add GitHub Copilot as LLM provider with device flow OAuth#323maros7 wants to merge 13 commits intocampfirein:mainfrom
Conversation
85d9c10 to
1788839
Compare
Add Copilot OAuth/API constants (client ID, endpoints, scopes) and extend shared transport types to support device flow authentication mode with userCode and verificationUri fields. Co-authored-by: Claude <noreply@anthropic.com>
Implement GitHub OAuth device flow (RFC 8628) for Copilot authentication including device code request, access token polling, and Copilot session token exchange. Includes 12 unit tests covering success paths, error handling, and polling states. Co-authored-by: Claude <noreply@anthropic.com>
Add github-copilot entry to PROVIDER_REGISTRY with device callback mode, priority 8, and default model claude-sonnet-4. Update providerRequiresApiKey to return false for Copilot. Includes registry test coverage. Co-authored-by: Claude <noreply@anthropic.com>
Implement CopilotModelFetcher that fetches available models from the Copilot API using session tokens, register it in the fetcher registry, and add Copilot case to provider config resolver. Includes model fetcher and config resolver test coverage. Co-authored-by: Claude <noreply@anthropic.com>
Extend provider handler to support device flow OAuth: add startDeviceFlow and awaitDeviceFlow methods, refactor callback flow into awaitCallbackFlow, and fix return-await in try/catch for proper error propagation. Includes handler test coverage for all device flow paths. Co-authored-by: Claude <noreply@anthropic.com>
Extend TokenRefreshManager with doCopilotRefresh method that detects device callback mode and exchanges the stored GitHub token for a fresh Copilot session token. Refactor shared error handling into handleRefreshError. Includes 8 new tests for Copilot refresh paths. Co-authored-by: Claude <noreply@anthropic.com>
Implement multi-API routing provider that detects model family from ID and routes to Anthropic SDK for Claude models or OpenAI-compatible SDK for GPT/Gemini/o-series. Injects Copilot session token as bearer auth. Includes 18 tests covering all model families and error paths. Co-authored-by: Claude <noreply@anthropic.com>
Bare AxiosError from exchangeForCopilotToken bypassed isPermanentOAuthError detection, causing revoked tokens (401/403) to be treated as transient errors. Wrap axios errors in ProviderTokenExchangeError with status code to match the pattern used by refresh-token-exchange.ts. Co-authored-by: Claude <noreply@anthropic.com>
Extract duplicated 'Editor-Version' and 'Copilot-Integration-Id' headers into COPILOT_REQUEST_HEADERS constant. Replace hardcoded URL strings with COPILOT_API_BASE_URL in model fetcher. Single source of truth for all Copilot API configuration values. Co-authored-by: Claude <noreply@anthropic.com>
…to token exchange
…ired headers Switch from @ai-sdk/anthropic to @ai-sdk/openai-compatible for all models since the Copilot API only supports OpenAI chat completions format. Add required Copilot headers (Editor-Plugin-Version, User-Agent, openai-intent, x-github-api-version) and update default model to claude-sonnet-4.6.
1788839 to
1fdf475
Compare
|
Hey @maros7 - first off, thank you for the effort here. The code quality is genuinely strong: the device flow implementation is RFC 8628 compliant, the token refresh integration is clean, the refactoring of That said, I can't merge this as-is due to a fundamental issue with how it authenticates against GitHub's Copilot API. Why this is blockedThe implementation uses GitHub's internal VS Code Copilot OAuth client ID ( This creates two problems we can't ship:
This isn't something that can be fixed with code changes - the core issue is using an unofficial API with a client ID that belongs to VS Code. I'll be closing this PR, but thanks again for the contribution - the quality of the work is clear and we appreciate the time you put into it. |
Summary
github-copilotprovider: OAuth device flow authentication, automatic Copilot token refresh, dynamic model listing from the Copilot API, and OpenAI-compatible chat completions for all models (including Claude, Gemini). CLI and TUI connect flows display the device code for the user to enter at github.com/login/device.devicecallback mode in shared types/events.Type of change
Scope (select all touched areas)
Linked issues
Root cause (bug fixes only, otherwise write
N/A)N/A
Test plan
test/unit/infra/provider-oauth/device-flow.test.ts— 14 tests (device code request, polling, token exchange, error handling)test/unit/infra/llm/providers/github-copilot.test.ts— 18 tests (provider creation, model routing, header injection, error handling)test/unit/infra/http/copilot-model-fetcher.test.ts— 12 tests (model listing, token auth, error handling, empty responses)test/unit/infra/provider-oauth/token-refresh-manager.test.ts— 8 new tests (Copilot token refresh, expiry detection, error wrapping)test/unit/infra/transport/handlers/provider-handler.test.ts— new device flow handler teststest/unit/infra/provider/provider-config-resolver.test.ts— Copilot config resolution teststest/unit/core/domain/entities/provider-registry.test.ts— registry entry teststest/commands/providers/connect.test.ts— CLI connect command device flow testsauthorization_pending,slow_down(interval backoff),expired_token,access_deniedProviderTokenExchangeErrorCopilot-Integration-Id,Editor-Version, etc.)User-visible changes
github-copilotavailable in/providers connectclaude-sonnet-4.6Evidence
npm run lint: 0 errors (69 pre-existing warnings, none from new code)npm run typecheck: cleannpm run build: cleannpm test: 4766 passing, 6 pending, 0 failinggithub-copilot.ts100% lines,device-flow.ts95% lines,token-refresh-manager.ts100% lines,copilot.ts100% lines,CopilotModelFetcher100% lines — all well above 50% minimum/curatewithclaude-sonnet-4.6through Copilot API successfullyChecklist
npm test)npm run lint)npm run typecheck)npm run build)mainRisks and mitigations
src/shared/constants/copilot.ts— a single file to update. Token exchange errors are wrapped inProviderTokenExchangeErrorwith descriptive messages.Iv1.b507a08c87ecfe98is a well-known public ID (used by VS Code, Goose, gpt4free, etc.) but not officially documented for third-party use.