From 55603cb5c7fd57ef7d424714e9da55efac13e328 Mon Sep 17 00:00:00 2001 From: Kacper Date: Sat, 13 Dec 2025 01:36:15 +0100 Subject: [PATCH] feat: add GPT-5.2 model support and refresh profiles functionality - Introduced the GPT-5.2 model with advanced coding capabilities across various components. - Added a new button in ProfilesView to refresh default profiles, enhancing user experience. - Updated CodexSetupStep to clarify authentication requirements and added commands for verifying login status. - Enhanced utility functions to recognize the new GPT-5.2 model in the application. --- apps/app/src/components/views/board-view.tsx | 7 ++ .../src/components/views/profiles-view.tsx | 39 ++++++++--- .../setup-view/steps/codex-setup-step.tsx | 29 ++++++-- apps/app/src/lib/utils.ts | 2 + apps/app/src/store/app-store.ts | 20 ++++++ apps/server/src/routes/models.ts | 9 +++ apps/server/src/routes/setup.ts | 45 ++++-------- apps/server/src/services/auto-mode-service.ts | 70 +++++++++++++++++-- 8 files changed, 170 insertions(+), 51 deletions(-) diff --git a/apps/app/src/components/views/board-view.tsx b/apps/app/src/components/views/board-view.tsx index 23d332a9..8aa70362 100644 --- a/apps/app/src/components/views/board-view.tsx +++ b/apps/app/src/components/views/board-view.tsx @@ -146,6 +146,13 @@ const CLAUDE_MODELS: ModelOption[] = [ ]; const CODEX_MODELS: ModelOption[] = [ + { + id: "gpt-5.2", + label: "GPT-5.2", + description: "Latest OpenAI model with advanced coding capabilities.", + badge: "Latest", + provider: "codex", + }, { id: "gpt-5.1-codex-max", label: "GPT-5.1 Codex Max", diff --git a/apps/app/src/components/views/profiles-view.tsx b/apps/app/src/components/views/profiles-view.tsx index 30316518..43530d55 100644 --- a/apps/app/src/components/views/profiles-view.tsx +++ b/apps/app/src/components/views/profiles-view.tsx @@ -41,6 +41,7 @@ import { GripVertical, Lock, Check, + RefreshCw, } from "lucide-react"; import { toast } from "sonner"; import { @@ -89,6 +90,7 @@ const CLAUDE_MODELS: { id: AgentModel; label: string }[] = [ ]; const CODEX_MODELS: { id: AgentModel; label: string }[] = [ + { id: "gpt-5.2", label: "GPT-5.2" }, { id: "gpt-5.1-codex-max", label: "GPT-5.1 Codex Max" }, { id: "gpt-5.1-codex", label: "GPT-5.1 Codex" }, { id: "gpt-5.1-codex-mini", label: "GPT-5.1 Codex Mini" }, @@ -461,6 +463,7 @@ export function ProfilesView() { updateAIProfile, removeAIProfile, reorderAIProfiles, + resetAIProfiles, } = useAppStore(); const shortcuts = useKeyboardShortcutsConfig(); @@ -529,6 +532,13 @@ export function ProfilesView() { }); }; + const handleResetProfiles = () => { + resetAIProfiles(); + toast.success("Profiles refreshed", { + description: "Default profiles have been updated to the latest version", + }); + }; + // Build keyboard shortcuts for profiles view const profilesShortcuts: KeyboardShortcut[] = useMemo(() => { const shortcutsList: KeyboardShortcut[] = []; @@ -568,15 +578,26 @@ export function ProfilesView() {

- setShowAddDialog(true)} - hotkey={shortcuts.addProfile} - hotkeyActive={false} - data-testid="add-profile-button" - > - - New Profile - +
+ + setShowAddDialog(true)} + hotkey={shortcuts.addProfile} + hotkeyActive={false} + data-testid="add-profile-button" + > + + New Profile + +
diff --git a/apps/app/src/components/views/setup-view/steps/codex-setup-step.tsx b/apps/app/src/components/views/setup-view/steps/codex-setup-step.tsx index 13335e02..a5f18ce4 100644 --- a/apps/app/src/components/views/setup-view/steps/codex-setup-step.tsx +++ b/apps/app/src/components/views/setup-view/steps/codex-setup-step.tsx @@ -282,21 +282,21 @@ export function CodexSetupStep({ Authentication - Codex requires an OpenAI API key + Codex requires authentication via ChatGPT account or API key {codexCliStatus?.installed && (
-
-

- Authenticate via CLI +

+

+ Authenticate via CLI (Recommended)

-

- Run this command in your terminal: +

+ Run the following command in your terminal to login with your ChatGPT account:

-
+
codex auth login @@ -308,6 +308,21 @@ export function CodexSetupStep({
+

+ After logging in, you can verify your authentication status: +

+
+ + codex login status + + +
diff --git a/apps/app/src/lib/utils.ts b/apps/app/src/lib/utils.ts index 9fa2f503..8d7b6386 100644 --- a/apps/app/src/lib/utils.ts +++ b/apps/app/src/lib/utils.ts @@ -12,6 +12,7 @@ export function cn(...inputs: ClassValue[]) { export function isCodexModel(model?: AgentModel | string): boolean { if (!model) return false; const codexModels: string[] = [ + "gpt-5.2", "gpt-5.1-codex-max", "gpt-5.1-codex", "gpt-5.1-codex-mini", @@ -36,6 +37,7 @@ export function getModelDisplayName(model: AgentModel | string): string { haiku: "Claude Haiku", sonnet: "Claude Sonnet", opus: "Claude Opus", + "gpt-5.2": "GPT-5.2", "gpt-5.1-codex-max": "GPT-5.1 Codex Max", "gpt-5.1-codex": "GPT-5.1 Codex", "gpt-5.1-codex-mini": "GPT-5.1 Codex Mini", diff --git a/apps/app/src/store/app-store.ts b/apps/app/src/store/app-store.ts index 81d50617..2136e4a6 100644 --- a/apps/app/src/store/app-store.ts +++ b/apps/app/src/store/app-store.ts @@ -203,6 +203,7 @@ export interface FeatureImagePath { export type ClaudeModel = "opus" | "sonnet" | "haiku"; // OpenAI/Codex models export type OpenAIModel = + | "gpt-5.2" | "gpt-5.1-codex-max" | "gpt-5.1-codex" | "gpt-5.1-codex-mini" @@ -445,6 +446,7 @@ export interface AppActions { updateAIProfile: (id: string, updates: Partial) => void; removeAIProfile: (id: string) => void; reorderAIProfiles: (oldIndex: number, newIndex: number) => void; + resetAIProfiles: () => void; // Project Analysis actions setProjectAnalysis: (analysis: ProjectAnalysis | null) => void; @@ -491,6 +493,16 @@ const DEFAULT_AI_PROFILES: AIProfile[] = [ isBuiltIn: true, icon: "Zap", }, + { + id: "profile-gpt52", + name: "GPT-5.2", + description: "GPT-5.2 - Latest OpenAI model for advanced coding tasks.", + model: "gpt-5.2", + thinkingLevel: "none", + provider: "codex", + isBuiltIn: true, + icon: "Sparkles", + }, { id: "profile-codex-power", name: "Codex Power", @@ -1106,6 +1118,14 @@ export const useAppStore = create()( set({ aiProfiles: profiles }); }, + resetAIProfiles: () => { + // Merge: keep user-created profiles, but refresh all built-in profiles to latest defaults + const currentProfiles = get().aiProfiles; + const userProfiles = currentProfiles.filter((p) => !p.isBuiltIn); + const mergedProfiles = [...DEFAULT_AI_PROFILES, ...userProfiles]; + set({ aiProfiles: mergedProfiles }); + }, + // Project Analysis actions setProjectAnalysis: (analysis) => set({ projectAnalysis: analysis }), setIsAnalyzing: (analyzing) => set({ isAnalyzing: analyzing }), diff --git a/apps/server/src/routes/models.ts b/apps/server/src/routes/models.ts index 529c50a6..5856fac5 100644 --- a/apps/server/src/routes/models.ts +++ b/apps/server/src/routes/models.ts @@ -90,6 +90,15 @@ export function createModelsRoutes(): Router { supportsVision: true, supportsTools: false, }, + { + id: "gpt-5.2", + name: "GPT-5.2 (Codex)", + provider: "openai", + contextWindow: 256000, + maxOutputTokens: 32768, + supportsVision: true, + supportsTools: true, + }, ]; res.json({ success: true, models }); diff --git a/apps/server/src/routes/setup.ts b/apps/server/src/routes/setup.ts index a1b5b38b..6403fbfa 100644 --- a/apps/server/src/routes/setup.ts +++ b/apps/server/src/routes/setup.ts @@ -249,56 +249,41 @@ export function createSetupRoutes(): Router { const { stdout: versionOut } = await execAsync("codex --version"); version = versionOut.trim(); } catch { - // Version command might not be available + version = "unknown"; } } catch { // Not found } // Check for OpenAI/Codex authentication + // Simplified: only check via CLI command, no file parsing let auth = { authenticated: false, method: "none" as string, - hasAuthFile: false, hasEnvKey: !!process.env.OPENAI_API_KEY, hasStoredApiKey: !!apiKeys.openai, - hasEnvApiKey: !!process.env.OPENAI_API_KEY, - // Additional fields for subscription/account detection - hasSubscription: false, - cliLoggedIn: false, }; - // Check for OpenAI CLI auth file (~/.codex/auth.json or similar) - const codexAuthPaths = [ - path.join(os.homedir(), ".codex", "auth.json"), - path.join(os.homedir(), ".openai", "credentials"), - path.join(os.homedir(), ".config", "openai", "credentials.json"), - ]; - - for (const authPath of codexAuthPaths) { + // Try to verify authentication using codex CLI command if CLI is installed + if (installed && cliPath) { try { - const authContent = await fs.readFile(authPath, "utf-8"); - const authData = JSON.parse(authContent); - auth.hasAuthFile = true; + const { stdout: statusOutput } = await execAsync(`"${cliPath}" login status 2>&1`, { + timeout: 5000, + }); - // Check for subscription/tokens - if (authData.subscription || authData.plan || authData.account_type) { - auth.hasSubscription = true; + // Check if the output indicates logged in status + if (statusOutput && (statusOutput.includes('Logged in') || statusOutput.includes('Authenticated'))) { auth.authenticated = true; - auth.method = "subscription"; // Codex subscription (Plus/Team) - } else if (authData.access_token || authData.api_key) { - auth.cliLoggedIn = true; - auth.authenticated = true; - auth.method = "cli_verified"; // CLI logged in with account + auth.method = "cli_verified"; // CLI verified via login status command } - break; - } catch { - // Auth file not found at this path + } catch (error) { + // CLI check failed - user needs to login manually + console.log("[Setup] Codex login status check failed:", error); } } - // Environment variable has highest priority - if (auth.hasEnvApiKey) { + // Environment variable override + if (process.env.OPENAI_API_KEY) { auth.authenticated = true; auth.method = "env"; // OPENAI_API_KEY environment variable } diff --git a/apps/server/src/services/auto-mode-service.ts b/apps/server/src/services/auto-mode-service.ts index 8015de91..4c44cd6e 100644 --- a/apps/server/src/services/auto-mode-service.ts +++ b/apps/server/src/services/auto-mode-service.ts @@ -18,6 +18,13 @@ import type { EventEmitter, EventType } from "../lib/events.js"; const execAsync = promisify(exec); +// Model name mappings for Claude (matching electron version) +const MODEL_MAP: Record = { + haiku: "claude-haiku-4-5", + sonnet: "claude-sonnet-4-20250514", + opus: "claude-opus-4-5-20251101", +}; + interface Feature { id: string; title: string; @@ -25,6 +32,37 @@ interface Feature { status: string; priority?: number; spec?: string; + model?: string; // Model to use for this feature +} + +/** + * Get model string from feature's model property + * Supports model keys like "opus", "sonnet", "haiku" or full model strings + * Also supports OpenAI/Codex models like "gpt-5.2", "gpt-5.1-codex", etc. + */ +function getModelString(feature: Feature): string { + const modelKey = feature.model || "opus"; // Default to opus + + // Check if it's an OpenAI/Codex model (starts with "gpt-" or "o" for O-series) + if (modelKey.startsWith("gpt-") || modelKey.startsWith("o")) { + console.log(`[AutoMode] Using OpenAI/Codex model from feature ${feature.id}: ${modelKey} (passing through)`); + return modelKey; + } + + // If it's already a full Claude model string (contains "claude-"), use it directly + if (modelKey.includes("claude-")) { + console.log(`[AutoMode] Using Claude model from feature ${feature.id}: ${modelKey} (full model string)`); + return modelKey; + } + + // Otherwise, look it up in the Claude model map + const modelString = MODEL_MAP[modelKey] || MODEL_MAP.opus; + if (modelString !== MODEL_MAP.opus || modelKey === "opus") { + console.log(`[AutoMode] Resolved Claude model for feature ${feature.id}: "${modelKey}" -> "${modelString}"`); + } else { + console.warn(`[AutoMode] Unknown model key "${modelKey}" for feature ${feature.id}, defaulting to "${modelString}"`); + } + return modelString; } interface RunningFeature { @@ -199,8 +237,12 @@ export class AutoModeService { // Build the prompt const prompt = this.buildFeaturePrompt(feature); - // Run the agent - await this.runAgent(workDir, featureId, prompt, abortController); + // Get model from feature + const model = getModelString(feature); + console.log(`[AutoMode] Executing feature ${featureId} with model: ${model}`); + + // Run the agent with the feature's model + await this.runAgent(workDir, featureId, prompt, abortController, undefined, model); // Mark as waiting_approval for user review await this.updateFeatureStatus(projectPath, featureId, "waiting_approval"); @@ -330,7 +372,12 @@ export class AutoModeService { }); try { - await this.runAgent(workDir, featureId, prompt, abortController, imagePaths); + // Load feature to get its model + const feature = await this.loadFeature(projectPath, featureId); + const model = feature ? getModelString(feature) : MODEL_MAP.opus; + console.log(`[AutoMode] Follow-up for feature ${featureId} using model: ${model}`); + + await this.runAgent(workDir, featureId, prompt, abortController, imagePaths, model); this.emitAutoModeEvent("auto_mode_feature_complete", { featureId, @@ -709,10 +756,23 @@ When done, summarize what you implemented and any notes for the developer.`; featureId: string, prompt: string, abortController: AbortController, - imagePaths?: string[] + imagePaths?: string[], + model?: string ): Promise { + const finalModel = model || MODEL_MAP.opus; + console.log(`[AutoMode] runAgent called for feature ${featureId} with model: ${finalModel}`); + + // Check if this is an OpenAI/Codex model - Claude Agent SDK doesn't support these + if (finalModel.startsWith("gpt-") || finalModel.startsWith("o")) { + const errorMessage = `OpenAI/Codex models (like "${finalModel}") are not yet supported in server mode. ` + + `Please use a Claude model (opus, sonnet, or haiku) instead. ` + + `OpenAI/Codex models are only supported in the Electron app.`; + console.error(`[AutoMode] ${errorMessage}`); + throw new Error(errorMessage); + } + const options: Options = { - model: "claude-opus-4-5-20251101", + model: finalModel, maxTurns: 50, cwd: workDir, allowedTools: [