diff --git a/apps/server/src/provider/providerCatalog.ts b/apps/server/src/provider/providerCatalog.ts index 3b76bc5c..57a19e81 100644 --- a/apps/server/src/provider/providerCatalog.ts +++ b/apps/server/src/provider/providerCatalog.ts @@ -53,6 +53,23 @@ export const BUILT_IN_PROVIDER_MODELS: Record { it("uses provider-specific aliases", () => { expect(normalizeModelSlug("sonnet", "claudeAgent")).toBe("claude-sonnet-4-6"); + expect(normalizeModelSlug("opus", "claudeAgent")).toBe("claude-opus-4-7"); + expect(normalizeModelSlug("opus-4.7", "claudeAgent")).toBe("claude-opus-4-7"); expect(normalizeModelSlug("opus-4.6", "claudeAgent")).toBe("claude-opus-4-6"); expect(normalizeModelSlug("claude-haiku-4-5-20251001", "claudeAgent")).toBe("claude-haiku-4-5"); }); @@ -171,6 +173,16 @@ describe("getReasoningEffortOptions", () => { expect(getReasoningEffortOptions("codex")).toEqual(REASONING_EFFORT_OPTIONS_BY_PROVIDER.codex); }); + it("returns claude effort options for Opus 4.7", () => { + expect(getReasoningEffortOptions("claudeAgent", "claude-opus-4-7")).toEqual([ + "low", + "medium", + "high", + "max", + "ultrathink", + ]); + }); + it("returns claude effort options for Opus 4.6", () => { expect(getReasoningEffortOptions("claudeAgent", "claude-opus-4-6")).toEqual([ "low", @@ -198,6 +210,7 @@ describe("getReasoningEffortOptions", () => { describe("inferProviderForModel", () => { it("detects known provider model slugs", () => { expect(inferProviderForModel("gpt-5.3-codex")).toBe("codex"); + expect(inferProviderForModel("claude-opus-4-7")).toBe("claudeAgent"); expect(inferProviderForModel("claude-sonnet-4-6")).toBe("claudeAgent"); expect(inferProviderForModel("anthropic/claude-sonnet-4-6")).toBe("claudeAgent"); expect(inferProviderForModel("sonnet")).toBe("claudeAgent"); @@ -299,7 +312,8 @@ describe("normalizeClaudeModelOptions", () => { }); describe("supportsClaudeAdaptiveReasoning", () => { - it("only enables adaptive reasoning for Opus 4.6 and Sonnet 4.6", () => { + it("only enables adaptive reasoning for Opus 4.7, Opus 4.6, and Sonnet 4.6", () => { + expect(supportsClaudeAdaptiveReasoning("claude-opus-4-7")).toBe(true); expect(supportsClaudeAdaptiveReasoning("claude-opus-4-6")).toBe(true); expect(supportsClaudeAdaptiveReasoning("claude-sonnet-4-6")).toBe(true); expect(supportsClaudeAdaptiveReasoning("claude-haiku-4-5")).toBe(false); @@ -308,7 +322,8 @@ describe("supportsClaudeAdaptiveReasoning", () => { }); describe("supportsClaudeMaxEffort", () => { - it("only enables max effort for Opus 4.6", () => { + it("only enables max effort for Opus 4.7 and Opus 4.6", () => { + expect(supportsClaudeMaxEffort("claude-opus-4-7")).toBe(true); expect(supportsClaudeMaxEffort("claude-opus-4-6")).toBe(true); expect(supportsClaudeMaxEffort("claude-sonnet-4-6")).toBe(false); expect(supportsClaudeMaxEffort("claude-haiku-4-5")).toBe(false); @@ -317,9 +332,11 @@ describe("supportsClaudeMaxEffort", () => { }); describe("supportsClaudeFastMode", () => { - it("only enables Claude fast mode for Opus 4.6", () => { - expect(supportsClaudeFastMode("claude-opus-4-6")).toBe(true); + it("only enables Claude fast mode for Opus 4.7 and Opus 4.6", () => { + expect(supportsClaudeFastMode("claude-opus-4-7")).toBe(true); expect(supportsClaudeFastMode("opus")).toBe(true); + expect(supportsClaudeFastMode("claude-opus-4-6")).toBe(true); + expect(supportsClaudeFastMode("opus-4.6")).toBe(true); expect(supportsClaudeFastMode("claude-sonnet-4-6")).toBe(false); expect(supportsClaudeFastMode("claude-haiku-4-5")).toBe(false); expect(supportsClaudeFastMode(undefined)).toBe(false); @@ -327,7 +344,8 @@ describe("supportsClaudeFastMode", () => { }); describe("supportsClaudeUltrathinkKeyword", () => { - it("only enables ultrathink keyword handling for Opus 4.6 and Sonnet 4.6", () => { + it("only enables ultrathink keyword handling for Opus 4.7, Opus 4.6, and Sonnet 4.6", () => { + expect(supportsClaudeUltrathinkKeyword("claude-opus-4-7")).toBe(true); expect(supportsClaudeUltrathinkKeyword("claude-opus-4-6")).toBe(true); expect(supportsClaudeUltrathinkKeyword("claude-sonnet-4-6")).toBe(true); expect(supportsClaudeUltrathinkKeyword("claude-haiku-4-5")).toBe(false); @@ -336,6 +354,7 @@ describe("supportsClaudeUltrathinkKeyword", () => { describe("supportsClaudeThinkingToggle", () => { it("only enables the Claude thinking toggle for Haiku 4.5", () => { + expect(supportsClaudeThinkingToggle("claude-opus-4-7")).toBe(false); expect(supportsClaudeThinkingToggle("claude-opus-4-6")).toBe(false); expect(supportsClaudeThinkingToggle("claude-sonnet-4-6")).toBe(false); expect(supportsClaudeThinkingToggle("claude-haiku-4-5")).toBe(true); diff --git a/packages/shared/src/model.ts b/packages/shared/src/model.ts index 727e29cb..bcefb27b 100644 --- a/packages/shared/src/model.ts +++ b/packages/shared/src/model.ts @@ -28,6 +28,7 @@ const MODEL_SLUG_SET_BY_PROVIDER: Record> = gemini: new Set(MODEL_OPTIONS_BY_PROVIDER.gemini.map((option) => option.slug)), }; +const CLAUDE_OPUS_4_7_MODEL = "claude-opus-4-7"; const CLAUDE_OPUS_4_6_MODEL = "claude-opus-4-6"; const CLAUDE_SONNET_4_6_MODEL = "claude-sonnet-4-6"; const CLAUDE_HAIKU_4_5_MODEL = "claude-haiku-4-5"; @@ -46,16 +47,22 @@ export function getDefaultModel(provider: ProviderKind = "codex"): ModelSlug { } export function supportsClaudeFastMode(model: string | null | undefined): boolean { - return normalizeModelSlug(model, "claudeAgent") === CLAUDE_OPUS_4_6_MODEL; + const normalized = normalizeModelSlug(model, "claudeAgent"); + return normalized === CLAUDE_OPUS_4_7_MODEL || normalized === CLAUDE_OPUS_4_6_MODEL; } export function supportsClaudeAdaptiveReasoning(model: string | null | undefined): boolean { const normalized = normalizeModelSlug(model, "claudeAgent"); - return normalized === CLAUDE_OPUS_4_6_MODEL || normalized === CLAUDE_SONNET_4_6_MODEL; + return ( + normalized === CLAUDE_OPUS_4_7_MODEL || + normalized === CLAUDE_OPUS_4_6_MODEL || + normalized === CLAUDE_SONNET_4_6_MODEL + ); } export function supportsClaudeMaxEffort(model: string | null | undefined): boolean { - return normalizeModelSlug(model, "claudeAgent") === CLAUDE_OPUS_4_6_MODEL; + const normalized = normalizeModelSlug(model, "claudeAgent"); + return normalized === CLAUDE_OPUS_4_7_MODEL || normalized === CLAUDE_OPUS_4_6_MODEL; } export function supportsClaudeUltrathinkKeyword(model: string | null | undefined): boolean {