diff --git a/src/backend/drivers/ai-chat/providers/claude/ClaudeProvider.test.ts b/src/backend/drivers/ai-chat/providers/claude/ClaudeProvider.test.ts index 3e928fcbc..89dfd8aa7 100644 --- a/src/backend/drivers/ai-chat/providers/claude/ClaudeProvider.test.ts +++ b/src/backend/drivers/ai-chat/providers/claude/ClaudeProvider.test.ts @@ -790,7 +790,12 @@ describe('ClaudeProvider.complete request shape', () => { }); }); - it.each(['claude-fable-5-1', 'claude-opus-5-5', 'claude-sonnet-5'])( + it.each([ + 'claude-fable-5-1', + 'claude-opus-5-5', + 'claude-sonnet-5-5', + 'claude-sonnet-5', + ])( 'omits temperature for %s', async (model) => { const { provider } = makeProvider(); @@ -809,7 +814,12 @@ describe('ClaudeProvider.complete request shape', () => { }, ); - it.each(['claude-fable-5-1', 'claude-opus-5-5', 'claude-sonnet-5'])( + it.each([ + 'claude-fable-5-1', + 'claude-opus-5-5', + 'claude-sonnet-5-5', + 'claude-sonnet-5', + ])( 'uses adaptive thinking and output effort on %s', async (model) => { const { provider } = makeProvider(); @@ -941,7 +951,23 @@ describe('ClaudeProvider model resolution', () => { ); }); - it('falls back to the default model when given an unknown id', async () => { + it('routes the bare claude-sonnet alias to sonnet 5.5 rather than sonnet 5', async () => { + const { provider } = makeProvider(); + messagesCreateMock.mockResolvedValueOnce(baseResponse); + + await withTestActor(() => + provider.complete({ + model: 'claude-sonnet', + messages: [{ role: 'user', content: 'hi' }], + }), + ); + + expect(messagesCreateMock.mock.calls[0]![0].model).toBe( + 'claude-sonnet-5-5', + ); + }); + + it('falls back to the default model when given an unknown id', async () => { const { provider } = makeProvider(); messagesCreateMock.mockResolvedValueOnce(baseResponse); @@ -975,6 +1001,9 @@ describe('ClaudeProvider.complete non-stream output', () => { ['claude-opus', 'claude-opus-5-5', 400, 500, 800, 20, 2000], ['claude-opus-latest', 'claude-opus-5-5', 400, 500, 800, 20, 2000], ['claude-opus-5-latest', 'claude-opus-5', 500, 625, 1000, 50, 2500], + ['claude-sonnet-5-5', 'claude-sonnet-5-5', 200, 250, 400, 20, 1000], + ['claude-sonnet', 'claude-sonnet-5-5', 200, 250, 400, 20, 1000], + ['claude-sonnet-latest', 'claude-sonnet-5-5', 200, 250, 400, 20, 1000], ['claude-sonnet-5', 'claude-sonnet-5', 200, 250, 400, 20, 1000], ])( 'resolves and meters %s at its current rates', diff --git a/src/backend/drivers/ai-chat/providers/claude/ClaudeProvider.ts b/src/backend/drivers/ai-chat/providers/claude/ClaudeProvider.ts index e6e9adcec..ff88f533c 100644 --- a/src/backend/drivers/ai-chat/providers/claude/ClaudeProvider.ts +++ b/src/backend/drivers/ai-chat/providers/claude/ClaudeProvider.ts @@ -358,11 +358,12 @@ export class ClaudeProvider implements IChatProvider { reasoningEffort: requestedReasoningEffort, maxTokens: max_tokens, }); - // Fable 5/5.1, Sonnet 5, and Opus 4.7+ reject non-default sampling; omit temperature entirely. + // Fable 5/5.1, Sonnet 5/5.5, and Opus 4.7+ reject non-default sampling; omit temperature entirely. // Other models require temperature=1 when thinking is enabled. const omitsTemperature = [ 'claude-fable-5-1', 'claude-fable-5', + 'claude-sonnet-5-5', 'claude-sonnet-5', 'claude-opus-4-7', 'claude-opus-4-8', @@ -377,6 +378,7 @@ export class ClaudeProvider implements IChatProvider { const supportsEffort = [ 'claude-fable-5-1', 'claude-fable-5', + 'claude-sonnet-5-5', 'claude-sonnet-5', 'claude-opus-5-5', 'claude-opus-5', @@ -811,6 +813,7 @@ export class ClaudeProvider implements IChatProvider { if ( modelId === 'claude-fable-5-1' || modelId === 'claude-fable-5' || + modelId === 'claude-sonnet-5-5' || modelId === 'claude-sonnet-5' || modelId === 'claude-opus-5-5' || modelId === 'claude-opus-5' || diff --git a/src/backend/drivers/ai-chat/providers/claude/models.ts b/src/backend/drivers/ai-chat/providers/claude/models.ts index 79a146c67..ff6bceef0 100644 --- a/src/backend/drivers/ai-chat/providers/claude/models.ts +++ b/src/backend/drivers/ai-chat/providers/claude/models.ts @@ -74,6 +74,36 @@ export const CLAUDE_MODELS: IChatModel[] = [ context: 1000000, max_tokens: 128000, }, + { + puterId: 'anthropic:anthropic/claude-sonnet-5-5', + id: 'claude-sonnet-5-5', + modalities: { input: ['text', 'image', 'pdf'], output: ['text'] }, + open_weights: false, + tool_call: true, + knowledge: '2026-06', + release_date: '2026-09-28', + aliases: [ + 'claude-sonnet', + 'claude-sonnet-latest', + 'claude-sonnet-5-5-latest', + 'claude-sonnet-5.5', + 'anthropic/claude-sonnet-5-5', + ], + name: 'Claude Sonnet 5.5', + costs_currency: 'usd-cents', + input_cost_key: 'input_tokens', + output_cost_key: 'output_tokens', + costs: { + tokens: 1_000_000, + input_tokens: 200, + ephemeral_5m_input_tokens: 200 * 1.25, + ephemeral_1h_input_tokens: 200 * 2, + cache_read_input_tokens: 200 * 0.1, + output_tokens: 1000, + }, + context: 1000000, + max_tokens: 128000, + }, { puterId: 'anthropic:anthropic/claude-sonnet-5', id: 'claude-sonnet-5', @@ -82,12 +112,7 @@ export const CLAUDE_MODELS: IChatModel[] = [ tool_call: true, knowledge: '2026-01', release_date: '2026-06-30', - aliases: [ - 'claude-sonnet', - 'claude-sonnet-latest', - 'claude-sonnet-5-latest', - 'anthropic/claude-sonnet-5', - ], + aliases: ['claude-sonnet-5-latest', 'anthropic/claude-sonnet-5'], name: 'Claude Sonnet 5', costs_currency: 'usd-cents', input_cost_key: 'input_tokens',