From 3b361cb0b918b9e37b43c5136af68b7d91921a60 Mon Sep 17 00:00:00 2001 From: Kacper Date: Thu, 5 Feb 2026 22:17:55 +0100 Subject: [PATCH 1/4] chore: update Codex SDK to version 0.98.0 and add GPT-5.3-Codex model - Upgraded @openai/codex-sdk from version 0.77.0 to 0.98.0 in package-lock.json and package.json. - Introduced new model 'GPT-5.3-Codex' with enhanced capabilities in codex-models.ts and related files. - Updated descriptions for existing models to reflect their latest features and improvements. - Adjusted Codex model configuration and display to include the new model and its attributes. These changes enhance the Codex model offerings and ensure compatibility with the latest SDK version. --- apps/server/package.json | 2 +- apps/server/src/providers/codex-models.ts | 24 ++++++++++++++----- .../providers/codex-model-configuration.tsx | 14 +++++++---- libs/platform/src/system-paths.ts | 24 ++++++++++++++++--- libs/types/src/codex-models.ts | 16 +++++++++---- libs/types/src/model-display.ts | 17 +++++++++---- libs/types/src/model.ts | 11 +++++---- package-lock.json | 8 +++---- 8 files changed, 86 insertions(+), 30 deletions(-) diff --git a/apps/server/package.json b/apps/server/package.json index c9015aeaa..0b4deeac0 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -34,7 +34,7 @@ "@automaker/utils": "1.0.0", "@github/copilot-sdk": "^0.1.16", "@modelcontextprotocol/sdk": "1.25.2", - "@openai/codex-sdk": "^0.77.0", + "@openai/codex-sdk": "^0.98.0", "cookie-parser": "1.4.7", "cors": "2.8.5", "dotenv": "17.2.3", diff --git a/apps/server/src/providers/codex-models.ts b/apps/server/src/providers/codex-models.ts index 141d53554..7840888b0 100644 --- a/apps/server/src/providers/codex-models.ts +++ b/apps/server/src/providers/codex-models.ts @@ -18,19 +18,31 @@ const MAX_OUTPUT_16K = 16000; */ export const CODEX_MODELS: ModelDefinition[] = [ // ========== Recommended Codex Models ========== + { + id: CODEX_MODEL_MAP.gpt53Codex, + name: 'GPT-5.3-Codex', + modelString: CODEX_MODEL_MAP.gpt53Codex, + provider: 'openai', + description: 'Latest frontier agentic coding model.', + contextWindow: CONTEXT_WINDOW_256K, + maxOutputTokens: MAX_OUTPUT_32K, + supportsVision: true, + supportsTools: true, + tier: 'premium' as const, + default: true, + hasReasoning: true, + }, { id: CODEX_MODEL_MAP.gpt52Codex, name: 'GPT-5.2-Codex', modelString: CODEX_MODEL_MAP.gpt52Codex, provider: 'openai', - description: - 'Most advanced agentic coding model for complex software engineering (default for ChatGPT users).', + description: 'Frontier agentic coding model.', contextWindow: CONTEXT_WINDOW_256K, maxOutputTokens: MAX_OUTPUT_32K, supportsVision: true, supportsTools: true, tier: 'premium' as const, - default: true, hasReasoning: true, }, { @@ -38,7 +50,7 @@ export const CODEX_MODELS: ModelDefinition[] = [ name: 'GPT-5.1-Codex-Max', modelString: CODEX_MODEL_MAP.gpt51CodexMax, provider: 'openai', - description: 'Optimized for long-horizon, agentic coding tasks in Codex.', + description: 'Codex-optimized flagship for deep and fast reasoning.', contextWindow: CONTEXT_WINDOW_256K, maxOutputTokens: MAX_OUTPUT_32K, supportsVision: true, @@ -51,7 +63,7 @@ export const CODEX_MODELS: ModelDefinition[] = [ name: 'GPT-5.1-Codex-Mini', modelString: CODEX_MODEL_MAP.gpt51CodexMini, provider: 'openai', - description: 'Smaller, more cost-effective version for faster workflows.', + description: 'Optimized for codex. Cheaper, faster, but less capable.', contextWindow: CONTEXT_WINDOW_128K, maxOutputTokens: MAX_OUTPUT_16K, supportsVision: true, @@ -66,7 +78,7 @@ export const CODEX_MODELS: ModelDefinition[] = [ name: 'GPT-5.2', modelString: CODEX_MODEL_MAP.gpt52, provider: 'openai', - description: 'Best general agentic model for tasks across industries and domains.', + description: 'Latest frontier model with improvements across knowledge, reasoning and coding.', contextWindow: CONTEXT_WINDOW_256K, maxOutputTokens: MAX_OUTPUT_32K, supportsVision: true, diff --git a/apps/ui/src/components/views/settings-view/providers/codex-model-configuration.tsx b/apps/ui/src/components/views/settings-view/providers/codex-model-configuration.tsx index a9d8c06ee..de1d9555d 100644 --- a/apps/ui/src/components/views/settings-view/providers/codex-model-configuration.tsx +++ b/apps/ui/src/components/views/settings-view/providers/codex-model-configuration.tsx @@ -27,25 +27,30 @@ interface CodexModelInfo { } const CODEX_MODEL_INFO: Record = { + 'codex-gpt-5.3-codex': { + id: 'codex-gpt-5.3-codex', + label: 'GPT-5.3-Codex', + description: 'Latest frontier agentic coding model', + }, 'codex-gpt-5.2-codex': { id: 'codex-gpt-5.2-codex', label: 'GPT-5.2-Codex', - description: 'Most advanced agentic coding model for complex software engineering', + description: 'Frontier agentic coding model', }, 'codex-gpt-5.1-codex-max': { id: 'codex-gpt-5.1-codex-max', label: 'GPT-5.1-Codex-Max', - description: 'Optimized for long-horizon, agentic coding tasks in Codex', + description: 'Codex-optimized flagship for deep and fast reasoning', }, 'codex-gpt-5.1-codex-mini': { id: 'codex-gpt-5.1-codex-mini', label: 'GPT-5.1-Codex-Mini', - description: 'Smaller, more cost-effective version for faster workflows', + description: 'Optimized for codex. Cheaper, faster, but less capable', }, 'codex-gpt-5.2': { id: 'codex-gpt-5.2', label: 'GPT-5.2', - description: 'Best general agentic model for tasks across industries and domains', + description: 'Latest frontier model with improvements across knowledge, reasoning and coding', }, 'codex-gpt-5.1': { id: 'codex-gpt-5.1', @@ -160,6 +165,7 @@ export function CodexModelConfiguration({ function supportsReasoningEffort(modelId: string): boolean { const reasoningModels = [ + 'codex-gpt-5.3-codex', 'codex-gpt-5.2-codex', 'codex-gpt-5.1-codex-max', 'codex-gpt-5.2', diff --git a/libs/platform/src/system-paths.ts b/libs/platform/src/system-paths.ts index 0d900dfa4..ce1246eb5 100644 --- a/libs/platform/src/system-paths.ts +++ b/libs/platform/src/system-paths.ts @@ -54,13 +54,19 @@ export function getClaudeCliPaths(): string[] { if (isWindows) { const appData = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'); - return [ + const nvmSymlink = process.env.NVM_SYMLINK; + const paths = [ path.join(os.homedir(), '.local', 'bin', 'claude.exe'), path.join(appData, 'npm', 'claude.cmd'), path.join(appData, 'npm', 'claude'), path.join(appData, '.npm-global', 'bin', 'claude.cmd'), path.join(appData, '.npm-global', 'bin', 'claude'), ]; + // nvm4w (NVM for Windows) symlink path + if (nvmSymlink) { + paths.push(path.join(nvmSymlink, 'claude.cmd'), path.join(nvmSymlink, 'claude')); + } + return paths; } return [ @@ -130,7 +136,8 @@ export function getCodexCliPaths(): string[] { if (isWindows) { const appData = process.env.APPDATA || path.join(homeDir, 'AppData', 'Roaming'); const localAppData = process.env.LOCALAPPDATA || path.join(homeDir, 'AppData', 'Local'); - return [ + const nvmSymlink = process.env.NVM_SYMLINK; + const paths = [ path.join(homeDir, '.local', 'bin', 'codex.exe'), path.join(appData, 'npm', 'codex.cmd'), path.join(appData, 'npm', 'codex'), @@ -142,6 +149,11 @@ export function getCodexCliPaths(): string[] { path.join(localAppData, 'pnpm', 'codex.cmd'), path.join(localAppData, 'pnpm', 'codex'), ]; + // nvm4w (NVM for Windows) symlink path + if (nvmSymlink) { + paths.push(path.join(nvmSymlink, 'codex.cmd'), path.join(nvmSymlink, 'codex')); + } + return paths; } // Include NVM bin paths for codex installed via npm global under NVM @@ -1126,7 +1138,8 @@ export function getOpenCodeCliPaths(): string[] { if (isWindows) { const appData = process.env.APPDATA || path.join(homeDir, 'AppData', 'Roaming'); const localAppData = process.env.LOCALAPPDATA || path.join(homeDir, 'AppData', 'Local'); - return [ + const nvmSymlink = process.env.NVM_SYMLINK; + const paths = [ // OpenCode's default installation directory path.join(homeDir, '.opencode', 'bin', 'opencode.exe'), path.join(homeDir, '.local', 'bin', 'opencode.exe'), @@ -1143,6 +1156,11 @@ export function getOpenCodeCliPaths(): string[] { path.join(homeDir, 'go', 'bin', 'opencode.exe'), path.join(process.env.GOPATH || path.join(homeDir, 'go'), 'bin', 'opencode.exe'), ]; + // nvm4w (NVM for Windows) symlink path + if (nvmSymlink) { + paths.push(path.join(nvmSymlink, 'opencode.cmd'), path.join(nvmSymlink, 'opencode')); + } + return paths; } // Include NVM bin paths for opencode installed via npm global under NVM diff --git a/libs/types/src/codex-models.ts b/libs/types/src/codex-models.ts index cf4db0ea9..934218ea4 100644 --- a/libs/types/src/codex-models.ts +++ b/libs/types/src/codex-models.ts @@ -6,6 +6,7 @@ * IMPORTANT: All Codex models use 'codex-' prefix to distinguish from Cursor CLI models */ export type CodexModelId = + | 'codex-gpt-5.3-codex' | 'codex-gpt-5.2-codex' | 'codex-gpt-5.1-codex-max' | 'codex-gpt-5.1-codex-mini' @@ -29,31 +30,38 @@ export interface CodexModelConfig { * All keys use 'codex-' prefix to distinguish from Cursor CLI models */ export const CODEX_MODEL_CONFIG_MAP: Record = { + 'codex-gpt-5.3-codex': { + id: 'codex-gpt-5.3-codex', + label: 'GPT-5.3-Codex', + description: 'Latest frontier agentic coding model', + hasThinking: true, + supportsVision: true, + }, 'codex-gpt-5.2-codex': { id: 'codex-gpt-5.2-codex', label: 'GPT-5.2-Codex', - description: 'Most advanced agentic coding model for complex software engineering', + description: 'Frontier agentic coding model', hasThinking: true, supportsVision: true, }, 'codex-gpt-5.1-codex-max': { id: 'codex-gpt-5.1-codex-max', label: 'GPT-5.1-Codex-Max', - description: 'Optimized for long-horizon, agentic coding tasks in Codex', + description: 'Codex-optimized flagship for deep and fast reasoning', hasThinking: true, supportsVision: true, }, 'codex-gpt-5.1-codex-mini': { id: 'codex-gpt-5.1-codex-mini', label: 'GPT-5.1-Codex-Mini', - description: 'Smaller, more cost-effective version for faster workflows', + description: 'Optimized for codex. Cheaper, faster, but less capable', hasThinking: false, supportsVision: true, }, 'codex-gpt-5.2': { id: 'codex-gpt-5.2', label: 'GPT-5.2 (Codex)', - description: 'Best general agentic model for tasks across industries and domains via Codex', + description: 'Latest frontier model with improvements across knowledge, reasoning and coding', hasThinking: true, supportsVision: true, }, diff --git a/libs/types/src/model-display.ts b/libs/types/src/model-display.ts index 286703281..08eaf208c 100644 --- a/libs/types/src/model-display.ts +++ b/libs/types/src/model-display.ts @@ -72,10 +72,18 @@ export const CLAUDE_MODELS: ModelOption[] = [ * Official models from https://developers.openai.com/codex/models/ */ export const CODEX_MODELS: (ModelOption & { hasReasoning?: boolean })[] = [ + { + id: CODEX_MODEL_MAP.gpt53Codex, + label: 'GPT-5.3-Codex', + description: 'Latest frontier agentic coding model.', + badge: 'Premium', + provider: 'codex', + hasReasoning: true, + }, { id: CODEX_MODEL_MAP.gpt52Codex, label: 'GPT-5.2-Codex', - description: 'Most advanced agentic coding model for complex software engineering.', + description: 'Frontier agentic coding model.', badge: 'Premium', provider: 'codex', hasReasoning: true, @@ -83,7 +91,7 @@ export const CODEX_MODELS: (ModelOption & { hasReasoning?: boolean })[] = [ { id: CODEX_MODEL_MAP.gpt51CodexMax, label: 'GPT-5.1-Codex-Max', - description: 'Optimized for long-horizon, agentic coding tasks in Codex.', + description: 'Codex-optimized flagship for deep and fast reasoning.', badge: 'Premium', provider: 'codex', hasReasoning: true, @@ -91,7 +99,7 @@ export const CODEX_MODELS: (ModelOption & { hasReasoning?: boolean })[] = [ { id: CODEX_MODEL_MAP.gpt51CodexMini, label: 'GPT-5.1-Codex-Mini', - description: 'Smaller, more cost-effective version for faster workflows.', + description: 'Optimized for codex. Cheaper, faster, but less capable.', badge: 'Speed', provider: 'codex', hasReasoning: false, @@ -99,7 +107,7 @@ export const CODEX_MODELS: (ModelOption & { hasReasoning?: boolean })[] = [ { id: CODEX_MODEL_MAP.gpt52, label: 'GPT-5.2', - description: 'Best general agentic model for tasks across industries and domains.', + description: 'Latest frontier model with improvements across knowledge, reasoning and coding.', badge: 'Balanced', provider: 'codex', hasReasoning: true, @@ -211,6 +219,7 @@ export function getModelDisplayName(model: ModelAlias | string): string { haiku: 'Claude Haiku', sonnet: 'Claude Sonnet', opus: 'Claude Opus', + [CODEX_MODEL_MAP.gpt53Codex]: 'GPT-5.3-Codex', [CODEX_MODEL_MAP.gpt52Codex]: 'GPT-5.2-Codex', [CODEX_MODEL_MAP.gpt51CodexMax]: 'GPT-5.1-Codex-Max', [CODEX_MODEL_MAP.gpt51CodexMini]: 'GPT-5.1-Codex-Mini', diff --git a/libs/types/src/model.ts b/libs/types/src/model.ts index 5538989e5..b6b90da9c 100644 --- a/libs/types/src/model.ts +++ b/libs/types/src/model.ts @@ -50,15 +50,17 @@ export const LEGACY_CLAUDE_ALIAS_MAP: Record = { */ export const CODEX_MODEL_MAP = { // Recommended Codex-specific models - /** Most advanced agentic coding model for complex software engineering (default for ChatGPT users) */ + /** Latest frontier agentic coding model */ + gpt53Codex: 'codex-gpt-5.3-codex', + /** Frontier agentic coding model */ gpt52Codex: 'codex-gpt-5.2-codex', - /** Optimized for long-horizon, agentic coding tasks in Codex */ + /** Codex-optimized flagship for deep and fast reasoning */ gpt51CodexMax: 'codex-gpt-5.1-codex-max', - /** Smaller, more cost-effective version for faster workflows */ + /** Optimized for codex. Cheaper, faster, but less capable */ gpt51CodexMini: 'codex-gpt-5.1-codex-mini', // General-purpose GPT models (also available in Codex) - /** Best general agentic model for tasks across industries and domains */ + /** Latest frontier model with improvements across knowledge, reasoning and coding */ gpt52: 'codex-gpt-5.2', /** Great for coding and agentic tasks across domains */ gpt51: 'codex-gpt-5.1', @@ -71,6 +73,7 @@ export const CODEX_MODEL_IDS = Object.values(CODEX_MODEL_MAP); * These models can use reasoning.effort parameter */ export const REASONING_CAPABLE_MODELS = new Set([ + CODEX_MODEL_MAP.gpt53Codex, CODEX_MODEL_MAP.gpt52Codex, CODEX_MODEL_MAP.gpt51CodexMax, CODEX_MODEL_MAP.gpt52, diff --git a/package-lock.json b/package-lock.json index 9f4f4d28a..0649982d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45,7 +45,7 @@ "@automaker/utils": "1.0.0", "@github/copilot-sdk": "^0.1.16", "@modelcontextprotocol/sdk": "1.25.2", - "@openai/codex-sdk": "^0.77.0", + "@openai/codex-sdk": "^0.98.0", "cookie-parser": "1.4.7", "cors": "2.8.5", "dotenv": "17.2.3", @@ -3949,9 +3949,9 @@ } }, "node_modules/@openai/codex-sdk": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@openai/codex-sdk/-/codex-sdk-0.77.0.tgz", - "integrity": "sha512-bvJQ4dASnZ7jgfxmseViQwdRupHxs0TwHSZFeYB0gpdOAXnWwDWdGJRCMyphLSHwExRp27JNOk7EBFVmZRBanQ==", + "version": "0.98.0", + "resolved": "https://registry.npmjs.org/@openai/codex-sdk/-/codex-sdk-0.98.0.tgz", + "integrity": "sha512-TbPgrBpuSNMJyOXys0HNsh6UoP5VIHu1fVh2KDdACi5XyB0vuPtzBZC+qOsxHz7WXEQPFlomPLyxS6JnE5Okmg==", "license": "Apache-2.0", "engines": { "node": ">=18" From 835ffe31853dde25d3676ca6dd4a100268f39570 Mon Sep 17 00:00:00 2001 From: Kacper Date: Thu, 5 Feb 2026 22:43:22 +0100 Subject: [PATCH 2/4] feat: update Claude model to Opus 4.6 and enhance adaptive thinking support - Changed model identifier from `claude-opus-4-5-20251101` to `claude-opus-4-6` across various files, including documentation and code references. - Updated the SDK to support adaptive thinking for Opus 4.6, allowing the model to determine its own reasoning depth. - Enhanced the thinking level options to include 'adaptive' and adjusted related components to reflect this change. - Updated tests to ensure compatibility with the new model and its features. These changes improve the model's capabilities and user experience by leveraging adaptive reasoning. --- CLAUDE.md | 2 +- apps/server/package.json | 2 +- apps/server/src/lib/sdk-options.ts | 18 ++++++++++- apps/server/src/providers/claude-provider.ts | 17 ++++++---- apps/server/src/providers/provider-factory.ts | 2 +- .../tests/unit/lib/model-resolver.test.ts | 4 +-- .../server/tests/unit/lib/sdk-options.test.ts | 24 ++++++++++++++ .../unit/providers/claude-provider.test.ts | 32 +++++++++---------- .../unit/providers/provider-factory.test.ts | 6 ++-- apps/ui/docs/AGENT_ARCHITECTURE.md | 2 +- .../board-view/dialogs/add-feature-dialog.tsx | 16 +++++++++- .../board-view/shared/model-constants.ts | 10 +++++- .../shared/thinking-level-selector.tsx | 14 ++++++-- .../model-defaults/phase-model-selector.tsx | 18 ++++++++--- apps/ui/src/lib/agent-context-parser.ts | 3 +- docs/llm-shared-packages.md | 2 +- docs/server/providers.md | 6 ++-- docs/server/utilities.md | 12 +++---- libs/model-resolver/README.md | 12 +++---- libs/model-resolver/tests/resolver.test.ts | 4 +-- libs/types/src/index.ts | 2 ++ libs/types/src/model-display.ts | 2 ++ libs/types/src/model.ts | 6 ++-- libs/types/src/settings.ts | 23 ++++++++++++- package-lock.json | 10 +++--- 25 files changed, 178 insertions(+), 71 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 128cd8d71..84dd1fbb1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -161,7 +161,7 @@ Use `resolveModelString()` from `@automaker/model-resolver` to convert model ali - `haiku` → `claude-haiku-4-5` - `sonnet` → `claude-sonnet-4-20250514` -- `opus` → `claude-opus-4-5-20251101` +- `opus` → `claude-opus-4-6` ## Environment Variables diff --git a/apps/server/package.json b/apps/server/package.json index 0b4deeac0..ed005c54b 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -24,7 +24,7 @@ "test:unit": "vitest run tests/unit" }, "dependencies": { - "@anthropic-ai/claude-agent-sdk": "0.1.76", + "@anthropic-ai/claude-agent-sdk": "0.2.32", "@automaker/dependency-resolver": "1.0.0", "@automaker/git-utils": "1.0.0", "@automaker/model-resolver": "1.0.0", diff --git a/apps/server/src/lib/sdk-options.ts b/apps/server/src/lib/sdk-options.ts index cc1df2f57..674350a55 100644 --- a/apps/server/src/lib/sdk-options.ts +++ b/apps/server/src/lib/sdk-options.ts @@ -253,11 +253,27 @@ function buildMcpOptions(config: CreateSdkOptionsConfig): McpOptions { /** * Build thinking options for SDK configuration. * Converts ThinkingLevel to maxThinkingTokens for the Claude SDK. + * For adaptive thinking (Opus 4.6), omits maxThinkingTokens to let the model + * decide its own reasoning depth. * * @param thinkingLevel - The thinking level to convert - * @returns Object with maxThinkingTokens if thinking is enabled + * @returns Object with maxThinkingTokens if thinking is enabled with a budget */ function buildThinkingOptions(thinkingLevel?: ThinkingLevel): Partial { + if (!thinkingLevel || thinkingLevel === 'none') { + return {}; + } + + // Adaptive thinking (Opus 4.6): don't set maxThinkingTokens + // The model will use adaptive thinking by default + if (thinkingLevel === 'adaptive') { + logger.debug( + `buildThinkingOptions: thinkingLevel="adaptive" -> no maxThinkingTokens (model decides)` + ); + return {}; + } + + // Manual budget-based thinking for Haiku/Sonnet const maxThinkingTokens = getThinkingTokenBudget(thinkingLevel); logger.debug( `buildThinkingOptions: thinkingLevel="${thinkingLevel}" -> maxThinkingTokens=${maxThinkingTokens}` diff --git a/apps/server/src/providers/claude-provider.ts b/apps/server/src/providers/claude-provider.ts index cfb590932..78a0a0c74 100644 --- a/apps/server/src/providers/claude-provider.ts +++ b/apps/server/src/providers/claude-provider.ts @@ -219,8 +219,11 @@ export class ClaudeProvider extends BaseProvider { // claudeCompatibleProvider takes precedence over claudeApiProfile const providerConfig = claudeCompatibleProvider || claudeApiProfile; - // Convert thinking level to token budget - const maxThinkingTokens = getThinkingTokenBudget(thinkingLevel); + // Build thinking configuration + // Adaptive thinking (Opus 4.6): don't set maxThinkingTokens, model uses adaptive by default + // Manual thinking (Haiku/Sonnet): use budget_tokens + const maxThinkingTokens = + thinkingLevel === 'adaptive' ? undefined : getThinkingTokenBudget(thinkingLevel); // Build Claude SDK options const sdkOptions: Options = { @@ -349,13 +352,13 @@ export class ClaudeProvider extends BaseProvider { getAvailableModels(): ModelDefinition[] { const models = [ { - id: 'claude-opus-4-5-20251101', - name: 'Claude Opus 4.5', - modelString: 'claude-opus-4-5-20251101', + id: 'claude-opus-4-6', + name: 'Claude Opus 4.6', + modelString: 'claude-opus-4-6', provider: 'anthropic', - description: 'Most capable Claude model', + description: 'Most capable Claude model with adaptive thinking', contextWindow: 200000, - maxOutputTokens: 16000, + maxOutputTokens: 128000, supportsVision: true, supportsTools: true, tier: 'premium' as const, diff --git a/apps/server/src/providers/provider-factory.ts b/apps/server/src/providers/provider-factory.ts index 1e91760f2..a6dff69ee 100644 --- a/apps/server/src/providers/provider-factory.ts +++ b/apps/server/src/providers/provider-factory.ts @@ -103,7 +103,7 @@ export class ProviderFactory { /** * Get the appropriate provider for a given model ID * - * @param modelId Model identifier (e.g., "claude-opus-4-5-20251101", "cursor-gpt-4o", "cursor-auto") + * @param modelId Model identifier (e.g., "claude-opus-4-6", "cursor-gpt-4o", "cursor-auto") * @param options Optional settings * @param options.throwOnDisconnected Throw error if provider is disconnected (default: true) * @returns Provider instance for the model diff --git a/apps/server/tests/unit/lib/model-resolver.test.ts b/apps/server/tests/unit/lib/model-resolver.test.ts index c1bff78da..65e3115df 100644 --- a/apps/server/tests/unit/lib/model-resolver.test.ts +++ b/apps/server/tests/unit/lib/model-resolver.test.ts @@ -35,7 +35,7 @@ describe('model-resolver.ts', () => { it("should resolve 'opus' alias to full model string", () => { const result = resolveModelString('opus'); - expect(result).toBe('claude-opus-4-5-20251101'); + expect(result).toBe('claude-opus-4-6'); expect(consoleSpy.log).toHaveBeenCalledWith( expect.stringContaining('Migrated legacy ID: "opus" -> "claude-opus"') ); @@ -117,7 +117,7 @@ describe('model-resolver.ts', () => { describe('getEffectiveModel', () => { it('should prioritize explicit model over session and default', () => { const result = getEffectiveModel('opus', 'haiku', 'gpt-5.2'); - expect(result).toBe('claude-opus-4-5-20251101'); + expect(result).toBe('claude-opus-4-6'); }); it('should use session model when explicit is not provided', () => { diff --git a/apps/server/tests/unit/lib/sdk-options.test.ts b/apps/server/tests/unit/lib/sdk-options.test.ts index 029cd8fa5..69d697946 100644 --- a/apps/server/tests/unit/lib/sdk-options.test.ts +++ b/apps/server/tests/unit/lib/sdk-options.test.ts @@ -491,5 +491,29 @@ describe('sdk-options.ts', () => { expect(options.maxThinkingTokens).toBeUndefined(); }); }); + + describe('adaptive thinking for Opus 4.6', () => { + it('should not set maxThinkingTokens for adaptive thinking (model decides)', async () => { + const { createAutoModeOptions } = await import('@/lib/sdk-options.js'); + + const options = createAutoModeOptions({ + cwd: '/test/path', + thinkingLevel: 'adaptive', + }); + + expect(options.maxThinkingTokens).toBeUndefined(); + }); + + it('should not include maxThinkingTokens when thinkingLevel is "none"', async () => { + const { createAutoModeOptions } = await import('@/lib/sdk-options.js'); + + const options = createAutoModeOptions({ + cwd: '/test/path', + thinkingLevel: 'none', + }); + + expect(options.maxThinkingTokens).toBeUndefined(); + }); + }); }); }); diff --git a/apps/server/tests/unit/providers/claude-provider.test.ts b/apps/server/tests/unit/providers/claude-provider.test.ts index c3f83f8fd..7df211ef3 100644 --- a/apps/server/tests/unit/providers/claude-provider.test.ts +++ b/apps/server/tests/unit/providers/claude-provider.test.ts @@ -39,7 +39,7 @@ describe('claude-provider.ts', () => { const generator = provider.executeQuery({ prompt: 'Hello', - model: 'claude-opus-4-5-20251101', + model: 'claude-opus-4-6', cwd: '/test', }); @@ -59,7 +59,7 @@ describe('claude-provider.ts', () => { const generator = provider.executeQuery({ prompt: 'Test prompt', - model: 'claude-opus-4-5-20251101', + model: 'claude-opus-4-6', cwd: '/test/dir', systemPrompt: 'You are helpful', maxTurns: 10, @@ -71,7 +71,7 @@ describe('claude-provider.ts', () => { expect(sdk.query).toHaveBeenCalledWith({ prompt: 'Test prompt', options: expect.objectContaining({ - model: 'claude-opus-4-5-20251101', + model: 'claude-opus-4-6', systemPrompt: 'You are helpful', maxTurns: 10, cwd: '/test/dir', @@ -91,7 +91,7 @@ describe('claude-provider.ts', () => { const generator = provider.executeQuery({ prompt: 'Test', - model: 'claude-opus-4-5-20251101', + model: 'claude-opus-4-6', cwd: '/test', }); @@ -116,7 +116,7 @@ describe('claude-provider.ts', () => { const generator = provider.executeQuery({ prompt: 'Test', - model: 'claude-opus-4-5-20251101', + model: 'claude-opus-4-6', cwd: '/test', abortController, }); @@ -145,7 +145,7 @@ describe('claude-provider.ts', () => { const generator = provider.executeQuery({ prompt: 'Current message', - model: 'claude-opus-4-5-20251101', + model: 'claude-opus-4-6', cwd: '/test', conversationHistory, sdkSessionId: 'test-session-id', @@ -176,7 +176,7 @@ describe('claude-provider.ts', () => { const generator = provider.executeQuery({ prompt: arrayPrompt as any, - model: 'claude-opus-4-5-20251101', + model: 'claude-opus-4-6', cwd: '/test', }); @@ -196,7 +196,7 @@ describe('claude-provider.ts', () => { const generator = provider.executeQuery({ prompt: 'Test', - model: 'claude-opus-4-5-20251101', + model: 'claude-opus-4-6', cwd: '/test', }); @@ -222,7 +222,7 @@ describe('claude-provider.ts', () => { const generator = provider.executeQuery({ prompt: 'Test', - model: 'claude-opus-4-5-20251101', + model: 'claude-opus-4-6', cwd: '/test', }); @@ -286,7 +286,7 @@ describe('claude-provider.ts', () => { const generator = provider.executeQuery({ prompt: 'Test', - model: 'claude-opus-4-5-20251101', + model: 'claude-opus-4-6', cwd: '/test', }); @@ -313,7 +313,7 @@ describe('claude-provider.ts', () => { const generator = provider.executeQuery({ prompt: 'Test', - model: 'claude-opus-4-5-20251101', + model: 'claude-opus-4-6', cwd: '/test', }); @@ -341,7 +341,7 @@ describe('claude-provider.ts', () => { const generator = provider.executeQuery({ prompt: 'Test', - model: 'claude-opus-4-5-20251101', + model: 'claude-opus-4-6', cwd: '/test', }); @@ -366,12 +366,12 @@ describe('claude-provider.ts', () => { expect(models).toHaveLength(4); }); - it('should include Claude Opus 4.5', () => { + it('should include Claude Opus 4.6', () => { const models = provider.getAvailableModels(); - const opus = models.find((m) => m.id === 'claude-opus-4-5-20251101'); + const opus = models.find((m) => m.id === 'claude-opus-4-6'); expect(opus).toBeDefined(); - expect(opus?.name).toBe('Claude Opus 4.5'); + expect(opus?.name).toBe('Claude Opus 4.6'); expect(opus?.provider).toBe('anthropic'); }); @@ -400,7 +400,7 @@ describe('claude-provider.ts', () => { it('should mark Opus as default', () => { const models = provider.getAvailableModels(); - const opus = models.find((m) => m.id === 'claude-opus-4-5-20251101'); + const opus = models.find((m) => m.id === 'claude-opus-4-6'); expect(opus?.default).toBe(true); }); diff --git a/apps/server/tests/unit/providers/provider-factory.test.ts b/apps/server/tests/unit/providers/provider-factory.test.ts index fbf01e908..b9aef9281 100644 --- a/apps/server/tests/unit/providers/provider-factory.test.ts +++ b/apps/server/tests/unit/providers/provider-factory.test.ts @@ -54,8 +54,8 @@ describe('provider-factory.ts', () => { describe('getProviderForModel', () => { describe('Claude models (claude-* prefix)', () => { - it('should return ClaudeProvider for claude-opus-4-5-20251101', () => { - const provider = ProviderFactory.getProviderForModel('claude-opus-4-5-20251101'); + it('should return ClaudeProvider for claude-opus-4-6', () => { + const provider = ProviderFactory.getProviderForModel('claude-opus-4-6'); expect(provider).toBeInstanceOf(ClaudeProvider); }); @@ -70,7 +70,7 @@ describe('provider-factory.ts', () => { }); it('should be case-insensitive for claude models', () => { - const provider = ProviderFactory.getProviderForModel('CLAUDE-OPUS-4-5-20251101'); + const provider = ProviderFactory.getProviderForModel('CLAUDE-OPUS-4-6'); expect(provider).toBeInstanceOf(ClaudeProvider); }); }); diff --git a/apps/ui/docs/AGENT_ARCHITECTURE.md b/apps/ui/docs/AGENT_ARCHITECTURE.md index 4c9f0d111..f5c374c47 100644 --- a/apps/ui/docs/AGENT_ARCHITECTURE.md +++ b/apps/ui/docs/AGENT_ARCHITECTURE.md @@ -199,7 +199,7 @@ The agent is configured with: ```javascript { - model: "claude-opus-4-5-20251101", + model: "claude-opus-4-6", maxTurns: 20, cwd: workingDirectory, allowedTools: [ diff --git a/apps/ui/src/components/views/board-view/dialogs/add-feature-dialog.tsx b/apps/ui/src/components/views/board-view/dialogs/add-feature-dialog.tsx index b8dd8776c..2dbf0808c 100644 --- a/apps/ui/src/components/views/board-view/dialogs/add-feature-dialog.tsx +++ b/apps/ui/src/components/views/board-view/dialogs/add-feature-dialog.tsx @@ -264,7 +264,21 @@ export function AddFeatureDialog({ }, [planningMode]); const handleModelChange = (entry: PhaseModelEntry) => { - setModelEntry(entry); + // Normalize thinking level when switching between adaptive and non-adaptive models + const isNewModelAdaptive = + entry.model === 'claude-opus' || + (typeof entry.model === 'string' && entry.model.includes('opus-4-6')); + const currentLevel = entry.thinkingLevel || 'none'; + + if (isNewModelAdaptive && currentLevel !== 'none' && currentLevel !== 'adaptive') { + // Switching TO Opus 4.6 with a manual level -> auto-switch to 'adaptive' + setModelEntry({ ...entry, thinkingLevel: 'adaptive' }); + } else if (!isNewModelAdaptive && currentLevel === 'adaptive') { + // Switching FROM Opus 4.6 with adaptive -> auto-switch to 'high' + setModelEntry({ ...entry, thinkingLevel: 'high' }); + } else { + setModelEntry(entry); + } }; const buildFeatureData = (): FeatureData | null => { diff --git a/apps/ui/src/components/views/board-view/shared/model-constants.ts b/apps/ui/src/components/views/board-view/shared/model-constants.ts index c56ad46a2..2816e5564 100644 --- a/apps/ui/src/components/views/board-view/shared/model-constants.ts +++ b/apps/ui/src/components/views/board-view/shared/model-constants.ts @@ -167,7 +167,14 @@ export const ALL_MODELS: ModelOption[] = [ ...COPILOT_MODELS, ]; -export const THINKING_LEVELS: ThinkingLevel[] = ['none', 'low', 'medium', 'high', 'ultrathink']; +export const THINKING_LEVELS: ThinkingLevel[] = [ + 'none', + 'low', + 'medium', + 'high', + 'ultrathink', + 'adaptive', +]; export const THINKING_LEVEL_LABELS: Record = { none: 'None', @@ -175,6 +182,7 @@ export const THINKING_LEVEL_LABELS: Record = { medium: 'Med', high: 'High', ultrathink: 'Ultra', + adaptive: 'Adaptive', }; /** diff --git a/apps/ui/src/components/views/board-view/shared/thinking-level-selector.tsx b/apps/ui/src/components/views/board-view/shared/thinking-level-selector.tsx index 74b791a3c..3e111a316 100644 --- a/apps/ui/src/components/views/board-view/shared/thinking-level-selector.tsx +++ b/apps/ui/src/components/views/board-view/shared/thinking-level-selector.tsx @@ -2,19 +2,25 @@ import { Label } from '@/components/ui/label'; import { Brain } from 'lucide-react'; import { cn } from '@/lib/utils'; import { ThinkingLevel } from '@/store/app-store'; -import { THINKING_LEVELS, THINKING_LEVEL_LABELS } from './model-constants'; +import { THINKING_LEVEL_LABELS } from './model-constants'; +import { getThinkingLevelsForModel } from '@automaker/types'; interface ThinkingLevelSelectorProps { selectedLevel: ThinkingLevel; onLevelSelect: (level: ThinkingLevel) => void; testIdPrefix?: string; + /** Optional model ID to filter available thinking levels (e.g., Opus 4.6 only shows None/Adaptive) */ + model?: string; } export function ThinkingLevelSelector({ selectedLevel, onLevelSelect, testIdPrefix = 'thinking-level', + model, }: ThinkingLevelSelectorProps) { + const levels = model ? getThinkingLevelsForModel(model) : getThinkingLevelsForModel(''); + return (
- {THINKING_LEVELS.map((level) => ( + {levels.map((level) => (
); diff --git a/apps/ui/src/components/views/settings-view/model-defaults/phase-model-selector.tsx b/apps/ui/src/components/views/settings-view/model-defaults/phase-model-selector.tsx index 204203881..25424fa6f 100644 --- a/apps/ui/src/components/views/settings-view/model-defaults/phase-model-selector.tsx +++ b/apps/ui/src/components/views/settings-view/model-defaults/phase-model-selector.tsx @@ -21,6 +21,7 @@ import { isGroupSelected, getSelectedVariant, codexModelHasThinking, + getThinkingLevelsForModel, } from '@automaker/types'; import { CLAUDE_MODELS, @@ -28,7 +29,6 @@ import { OPENCODE_MODELS, GEMINI_MODELS, COPILOT_MODELS, - THINKING_LEVELS, THINKING_LEVEL_LABELS, REASONING_EFFORT_LEVELS, REASONING_EFFORT_LABELS, @@ -1296,7 +1296,9 @@ export function PhaseModelSelector({
Thinking Level
- {THINKING_LEVELS.map((level) => ( + {getThinkingLevelsForModel( + model.mapsToClaudeModel === 'opus' ? 'claude-opus' : '' + ).map((level) => (
{isSelected && currentThinking === level && ( @@ -1402,7 +1405,9 @@ export function PhaseModelSelector({
Thinking Level
- {THINKING_LEVELS.map((level) => ( + {getThinkingLevelsForModel( + model.mapsToClaudeModel === 'opus' ? 'claude-opus' : '' + ).map((level) => (