diff --git a/apps/x/apps/main/src/ipc.ts b/apps/x/apps/main/src/ipc.ts
index 74388f65..fee36663 100644
--- a/apps/x/apps/main/src/ipc.ts
+++ b/apps/x/apps/main/src/ipc.ts
@@ -29,6 +29,7 @@ import { isSignedIn } from '@x/core/dist/account/account.js';
import { listGatewayModels } from '@x/core/dist/models/gateway.js';
import type { IModelConfigRepo } from '@x/core/dist/models/repo.js';
import type { IOAuthRepo } from '@x/core/dist/auth/repo.js';
+import { startGitHubCopilotAuthentication, getGitHubCopilotAccessToken, isGitHubCopilotAuthenticated, disconnectGitHubCopilot } from '@x/core/dist/auth/github-copilot-auth.js';
import { IGranolaConfigRepo } from '@x/core/dist/knowledge/granola/repo.js';
import { triggerSync as triggerGranolaSync } from '@x/core/dist/knowledge/granola/sync.js';
import { ISlackConfigRepo } from '@x/core/dist/slack/repo.js';
@@ -488,6 +489,46 @@ export function setupIpcHandlers() {
const config = await repo.getClientFacingConfig();
return { config };
},
+ 'github-copilot:authenticate': async () => {
+ try {
+ const authInfo = await startGitHubCopilotAuthentication();
+ // Don't await the token promise - it will complete in the background
+ // Just return the device code info immediately
+ return {
+ success: true,
+ userCode: authInfo.userCode,
+ verificationUri: authInfo.verificationUri,
+ expiresIn: authInfo.expiresIn,
+ };
+ } catch (error) {
+ console.error('[GitHub Copilot] Authentication error:', error);
+ return {
+ success: false,
+ error: error instanceof Error ? error.message : 'Authentication failed',
+ };
+ }
+ },
+ 'github-copilot:isAuthenticated': async () => {
+ try {
+ const authenticated = await isGitHubCopilotAuthenticated();
+ return { authenticated };
+ } catch (error) {
+ console.error('[GitHub Copilot] Error checking authentication:', error);
+ return { authenticated: false };
+ }
+ },
+ 'github-copilot:disconnect': async () => {
+ try {
+ await disconnectGitHubCopilot();
+ return { success: true };
+ } catch (error) {
+ console.error('[GitHub Copilot] Disconnect error:', error);
+ return {
+ success: false,
+ error: error instanceof Error ? error.message : 'Disconnect failed',
+ };
+ }
+ },
'account:getRowboat': async () => {
const signedIn = await isSignedIn();
if (!signedIn) {
diff --git a/apps/x/apps/renderer/src/components/settings-dialog.tsx b/apps/x/apps/renderer/src/components/settings-dialog.tsx
index e0e4ba6c..6ff990b7 100644
--- a/apps/x/apps/renderer/src/components/settings-dialog.tsx
+++ b/apps/x/apps/renderer/src/components/settings-dialog.tsx
@@ -691,17 +691,64 @@ function ModelSettings({ dialogOpen }: { dialogOpen: boolean }) {
{isGitHubCopilot && (
Authentication
-
GitHub Copilot uses Device Flow OAuth for authentication. Click the button below to authenticate with your GitHub account.
+
GitHub Copilot uses Device Flow OAuth for authentication. Click the button to authenticate with your GitHub account.
)}
diff --git a/apps/x/packages/core/src/auth/github-copilot-models.ts b/apps/x/packages/core/src/auth/github-copilot-models.ts
index 0da39ed0..756584bb 100644
--- a/apps/x/packages/core/src/auth/github-copilot-models.ts
+++ b/apps/x/packages/core/src/auth/github-copilot-models.ts
@@ -14,14 +14,13 @@ import { LlmProvider } from '@x/shared/dist/models.js';
const GITHUB_COPILOT_API_BASE = 'https://models.github.com/api/openai/';
// List of models available through GitHub Copilot
-// Based on GitHub Copilot documentation
+// Based on GitHub Copilot API documentation
+// https://docs.github.com/en/copilot/using-github-copilot/asking-github-copilot-questions
export const GITHUB_COPILOT_MODELS = [
- 'gpt-4o',
- 'gpt-4-turbo',
- 'gpt-4',
- 'gpt-3.5-turbo',
- 'claude-3.5-sonnet', // If available in student plan
- 'claude-3-opus', // If available in student plan
+ 'gpt-4o', // GPT-4 Optimized (recommended)
+ 'gpt-4-turbo', // GPT-4 Turbo
+ 'gpt-3.5-turbo', // GPT-3.5 Turbo (fastest)
+ 'claude-3.5-sonnet', // Claude 3.5 Sonnet (if available in plan)
] as const;
export type GitHubCopilotModel = typeof GITHUB_COPILOT_MODELS[number];
diff --git a/apps/x/packages/core/src/models/models-dev.ts b/apps/x/packages/core/src/models/models-dev.ts
index ca341edf..5036aeb0 100644
--- a/apps/x/packages/core/src/models/models-dev.ts
+++ b/apps/x/packages/core/src/models/models-dev.ts
@@ -227,12 +227,10 @@ export async function listOnboardingModels(): Promise<{ providers: ProviderSumma
id: "github-copilot",
name: "GitHub Copilot Student",
models: [
- { id: "gpt-4o", name: "GPT-4o" },
+ { id: "gpt-4o", name: "GPT-4o (Recommended)" },
{ id: "gpt-4-turbo", name: "GPT-4 Turbo" },
- { id: "gpt-4", name: "GPT-4" },
- { id: "gpt-3.5-turbo", name: "GPT-3.5 Turbo" },
+ { id: "gpt-3.5-turbo", name: "GPT-3.5 Turbo (Fastest)" },
{ id: "claude-3.5-sonnet", name: "Claude 3.5 Sonnet" },
- { id: "claude-3-opus", name: "Claude 3 Opus" },
],
});
diff --git a/apps/x/packages/shared/src/ipc.ts b/apps/x/packages/shared/src/ipc.ts
index 645e41ff..c509023d 100644
--- a/apps/x/packages/shared/src/ipc.ts
+++ b/apps/x/packages/shared/src/ipc.ts
@@ -257,6 +257,39 @@ const ipcSchemas = {
})),
}),
},
+ 'github-copilot:authenticate': {
+ req: z.null(),
+ res: z.union([
+ z.object({
+ success: z.literal(true),
+ userCode: z.string(),
+ verificationUri: z.string(),
+ expiresIn: z.number(),
+ }),
+ z.object({
+ success: z.literal(false),
+ error: z.string(),
+ }),
+ ]),
+ },
+ 'github-copilot:isAuthenticated': {
+ req: z.null(),
+ res: z.object({
+ authenticated: z.boolean(),
+ }),
+ },
+ 'github-copilot:disconnect': {
+ req: z.null(),
+ res: z.union([
+ z.object({
+ success: z.literal(true),
+ }),
+ z.object({
+ success: z.literal(false),
+ error: z.string(),
+ }),
+ ]),
+ },
'account:getRowboat': {
req: z.null(),
res: z.object({