diff --git a/.changeset/open-tips-notice.md b/.changeset/open-tips-notice.md new file mode 100644 index 00000000..97fa664a --- /dev/null +++ b/.changeset/open-tips-notice.md @@ -0,0 +1,5 @@ +--- +"task-master-ai": minor +--- + +Add 4.5 haiku and sonnet to supported models for claude-code and anthropic ai providers diff --git a/scripts/modules/config-manager.js b/scripts/modules/config-manager.js index 00f3ebf8..6980c9b8 100644 --- a/scripts/modules/config-manager.js +++ b/scripts/modules/config-manager.js @@ -307,6 +307,20 @@ function validateProviderModelCombination(providerName, modelId) { ); } +/** + * Gets the list of supported model IDs for a given provider from supported-models.json + * @param {string} providerName - The name of the provider (e.g., 'claude-code', 'anthropic') + * @returns {string[]} Array of supported model IDs, or empty array if provider not found + */ +export function getSupportedModelsForProvider(providerName) { + if (!MODEL_MAP[providerName]) { + return []; + } + return MODEL_MAP[providerName] + .filter((model) => model.supported !== false) + .map((model) => model.id); +} + /** * Validates Claude Code AI provider custom settings * @param {object} settings The settings to validate diff --git a/scripts/modules/supported-models.json b/scripts/modules/supported-models.json index 2015d277..60d56a87 100644 --- a/scripts/modules/supported-models.json +++ b/scripts/modules/supported-models.json @@ -43,6 +43,28 @@ "allowed_roles": ["main", "fallback"], "max_tokens": 8192, "supported": true + }, + { + "id": "claude-sonnet-4-5-20250929", + "swe_score": 0.73, + "cost_per_1m_tokens": { + "input": 3.0, + "output": 15.0 + }, + "allowed_roles": ["main", "fallback"], + "max_tokens": 64000, + "supported": true + }, + { + "id": "claude-haiku-4-5-20251001", + "swe_score": 0.45, + "cost_per_1m_tokens": { + "input": 1.0, + "output": 5.0 + }, + "allowed_roles": ["main", "fallback"], + "max_tokens": 200000, + "supported": true } ], "claude-code": [ @@ -67,6 +89,17 @@ "allowed_roles": ["main", "fallback", "research"], "max_tokens": 64000, "supported": true + }, + { + "id": "haiku", + "swe_score": 0.45, + "cost_per_1m_tokens": { + "input": 0, + "output": 0 + }, + "allowed_roles": ["main", "fallback", "research"], + "max_tokens": 200000, + "supported": true } ], "codex-cli": [ diff --git a/src/ai-providers/claude-code.js b/src/ai-providers/claude-code.js index 067fb749..19146cc6 100644 --- a/src/ai-providers/claude-code.js +++ b/src/ai-providers/claude-code.js @@ -12,7 +12,10 @@ import { createClaudeCode } from 'ai-sdk-provider-claude-code'; import { BaseAIProvider } from './base-provider.js'; -import { getClaudeCodeSettingsForCommand } from '../../scripts/modules/config-manager.js'; +import { + getClaudeCodeSettingsForCommand, + getSupportedModelsForProvider +} from '../../scripts/modules/config-manager.js'; import { execSync } from 'child_process'; import { log } from '../../scripts/modules/utils.js'; @@ -24,14 +27,24 @@ let _claudeCliAvailable = null; * * Features: * - No API key required (uses local Claude Code CLI) - * - Supports 'sonnet' and 'opus' models + * - Supported models loaded from supported-models.json * - Command-specific configuration support */ export class ClaudeCodeProvider extends BaseAIProvider { constructor() { super(); this.name = 'Claude Code'; - this.supportedModels = ['sonnet', 'opus']; + // Load supported models from supported-models.json + this.supportedModels = getSupportedModelsForProvider('claude-code'); + + // Validate that models were loaded successfully + if (this.supportedModels.length === 0) { + log( + 'warn', + 'No supported models found for claude-code provider. Check supported-models.json configuration.' + ); + } + // Claude Code requires explicit JSON schema mode this.needsExplicitJsonSchema = true; // Claude Code does not support temperature parameter diff --git a/src/ai-providers/codex-cli.js b/src/ai-providers/codex-cli.js index d3566edb..c9e082e6 100644 --- a/src/ai-providers/codex-cli.js +++ b/src/ai-providers/codex-cli.js @@ -10,7 +10,10 @@ import { createCodexCli } from 'ai-sdk-provider-codex-cli'; import { BaseAIProvider } from './base-provider.js'; import { execSync } from 'child_process'; import { log } from '../../scripts/modules/utils.js'; -import { getCodexCliSettingsForCommand } from '../../scripts/modules/config-manager.js'; +import { + getCodexCliSettingsForCommand, + getSupportedModelsForProvider +} from '../../scripts/modules/config-manager.js'; export class CodexCliProvider extends BaseAIProvider { constructor() { @@ -20,8 +23,17 @@ export class CodexCliProvider extends BaseAIProvider { this.needsExplicitJsonSchema = false; // Codex CLI does not support temperature parameter this.supportsTemperature = false; - // Restrict to supported models for OAuth subscription usage - this.supportedModels = ['gpt-5', 'gpt-5-codex']; + // Load supported models from supported-models.json + this.supportedModels = getSupportedModelsForProvider('codex-cli'); + + // Validate that models were loaded successfully + if (this.supportedModels.length === 0) { + log( + 'warn', + 'No supported models found for codex-cli provider. Check supported-models.json configuration.' + ); + } + // CLI availability check cache this._codexCliChecked = false; this._codexCliAvailable = null; diff --git a/tests/integration/claude-code-error-handling.test.js b/tests/integration/claude-code-error-handling.test.js index 0cd07dde..fe0c1167 100644 --- a/tests/integration/claude-code-error-handling.test.js +++ b/tests/integration/claude-code-error-handling.test.js @@ -43,9 +43,9 @@ describe('Claude Code Error Handling', () => { // These should work even if CLI is not available expect(provider.name).toBe('Claude Code'); - expect(provider.getSupportedModels()).toEqual(['sonnet', 'opus']); + expect(provider.getSupportedModels()).toEqual(['opus', 'sonnet', 'haiku']); expect(provider.isModelSupported('sonnet')).toBe(true); - expect(provider.isModelSupported('haiku')).toBe(false); + expect(provider.isModelSupported('haiku')).toBe(true); expect(provider.isRequiredApiKey()).toBe(false); expect(() => provider.validateAuth()).not.toThrow(); }); diff --git a/tests/integration/claude-code-optional.test.js b/tests/integration/claude-code-optional.test.js index 28e9268e..7c268fbb 100644 --- a/tests/integration/claude-code-optional.test.js +++ b/tests/integration/claude-code-optional.test.js @@ -40,14 +40,14 @@ describe('Claude Code Integration (Optional)', () => { it('should create a working provider instance', () => { const provider = new ClaudeCodeProvider(); expect(provider.name).toBe('Claude Code'); - expect(provider.getSupportedModels()).toEqual(['sonnet', 'opus']); + expect(provider.getSupportedModels()).toEqual(['opus', 'sonnet', 'haiku']); }); it('should support model validation', () => { const provider = new ClaudeCodeProvider(); expect(provider.isModelSupported('sonnet')).toBe(true); expect(provider.isModelSupported('opus')).toBe(true); - expect(provider.isModelSupported('haiku')).toBe(false); + expect(provider.isModelSupported('haiku')).toBe(true); expect(provider.isModelSupported('unknown')).toBe(false); }); diff --git a/tests/unit/ai-providers/claude-code.test.js b/tests/unit/ai-providers/claude-code.test.js index ac0fa625..d914c532 100644 --- a/tests/unit/ai-providers/claude-code.test.js +++ b/tests/unit/ai-providers/claude-code.test.js @@ -28,6 +28,14 @@ jest.unstable_mockModule('../../../src/ai-providers/base-provider.js', () => ({ } })); +// Mock config getters +jest.unstable_mockModule('../../../scripts/modules/config-manager.js', () => ({ + getClaudeCodeSettingsForCommand: jest.fn(() => ({})), + getSupportedModelsForProvider: jest.fn(() => ['opus', 'sonnet', 'haiku']), + getDebugFlag: jest.fn(() => false), + getLogLevel: jest.fn(() => 'info') +})); + // Import after mocking const { ClaudeCodeProvider } = await import( '../../../src/ai-providers/claude-code.js' @@ -96,13 +104,13 @@ describe('ClaudeCodeProvider', () => { describe('model support', () => { it('should return supported models', () => { const models = provider.getSupportedModels(); - expect(models).toEqual(['sonnet', 'opus']); + expect(models).toEqual(['opus', 'sonnet', 'haiku']); }); it('should check if model is supported', () => { expect(provider.isModelSupported('sonnet')).toBe(true); expect(provider.isModelSupported('opus')).toBe(true); - expect(provider.isModelSupported('haiku')).toBe(false); + expect(provider.isModelSupported('haiku')).toBe(true); expect(provider.isModelSupported('unknown')).toBe(false); }); }); diff --git a/tests/unit/ai-providers/codex-cli.test.js b/tests/unit/ai-providers/codex-cli.test.js index de7c90f9..adb5d065 100644 --- a/tests/unit/ai-providers/codex-cli.test.js +++ b/tests/unit/ai-providers/codex-cli.test.js @@ -20,6 +20,7 @@ jest.unstable_mockModule('ai-sdk-provider-codex-cli', () => ({ // Mock config getters jest.unstable_mockModule('../../../scripts/modules/config-manager.js', () => ({ getCodexCliSettingsForCommand: jest.fn(() => ({ allowNpx: true })), + getSupportedModelsForProvider: jest.fn(() => ['gpt-5', 'gpt-5-codex']), // Provide commonly imported getters to satisfy other module imports if any getDebugFlag: jest.fn(() => false), getLogLevel: jest.fn(() => 'info')