Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions apps/server/src/provider/providerCatalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,23 @@ export const BUILT_IN_PROVIDER_MODELS: Record<ProviderKind, ReadonlyArray<Provid
{ slug: "gpt-5.2", name: "GPT-5.2", capabilities: noCapabilities },
],
claudeAgent: [
{
slug: "claude-opus-4-7",
name: "Claude Opus 4.7",
capabilities: {
reasoningEffortLevels: [
{ value: "low", label: "Low" },
{ value: "medium", label: "Medium" },
{ value: "high", label: "High", isDefault: true },
{ value: "max", label: "Max" },
{ value: "ultrathink", label: "Ultrathink" },
],
supportsFastMode: true,
supportsThinkingToggle: false,
contextWindowOptions: [],
promptInjectedEffortLevels: ["ultrathink"],
},
},
{
slug: "claude-opus-4-6",
name: "Claude Opus 4.6",
Expand Down Expand Up @@ -110,6 +127,7 @@ export const BUILT_IN_PROVIDER_MODELS: Record<ProviderKind, ReadonlyArray<Provid
{ slug: "claude-sonnet-4-6", name: "Claude Sonnet 4.6", capabilities: noCapabilities },
{ slug: "claude-sonnet-4-5", name: "Claude Sonnet 4.5", capabilities: noCapabilities },
{ slug: "claude-haiku-4-5", name: "Claude Haiku 4.5", capabilities: noCapabilities },
{ slug: "claude-opus-4-7", name: "Claude Opus 4.7", capabilities: noCapabilities },
{ slug: "claude-opus-4-6", name: "Claude Opus 4.6", capabilities: noCapabilities },
{ slug: "claude-opus-4-5", name: "Claude Opus 4.5", capabilities: noCapabilities },
{ slug: "gemini-3.1-pro", name: "Gemini 3.1 Pro", capabilities: noCapabilities },
Expand Down
9 changes: 8 additions & 1 deletion packages/contracts/src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export const MODEL_OPTIONS_BY_PROVIDER = {
{ slug: "gpt-5.2", name: "GPT-5.2" },
],
claudeAgent: [
{ slug: "claude-opus-4-7", name: "Claude Opus 4.7" },
{ slug: "claude-opus-4-6", name: "Claude Opus 4.6" },
{ slug: "claude-sonnet-4-6", name: "Claude Sonnet 4.6" },
{ slug: "claude-haiku-4-5", name: "Claude Haiku 4.5" },
Expand All @@ -84,6 +85,7 @@ export const MODEL_OPTIONS_BY_PROVIDER = {
{ slug: "claude-sonnet-4-6", name: "Claude Sonnet 4.6" },
{ slug: "claude-sonnet-4-5", name: "Claude Sonnet 4.5" },
{ slug: "claude-haiku-4-5", name: "Claude Haiku 4.5" },
{ slug: "claude-opus-4-7", name: "Claude Opus 4.7" },
{ slug: "claude-opus-4-6", name: "Claude Opus 4.6" },
{ slug: "claude-opus-4-5", name: "Claude Opus 4.5" },
{ slug: "gemini-3.1-pro", name: "Gemini 3.1 Pro" },
Expand Down Expand Up @@ -126,7 +128,9 @@ export const MODEL_SLUG_ALIASES_BY_PROVIDER: Record<ProviderKind, Record<string,
"gpt-5.3-spark": "gpt-5.3-codex-spark",
},
claudeAgent: {
opus: "claude-opus-4-6",
opus: "claude-opus-4-7",
"opus-4.7": "claude-opus-4-7",
"claude-opus-4.7": "claude-opus-4-7",
"opus-4.6": "claude-opus-4-6",
"claude-opus-4.6": "claude-opus-4-6",
"claude-opus-4-6-20251117": "claude-opus-4-6",
Expand Down Expand Up @@ -167,6 +171,9 @@ export const MODEL_SLUG_ALIASES_BY_PROVIDER: Record<ProviderKind, Record<string,
"claude opus 4.5": "claude-opus-4-5",
"claude-opus-4.5": "claude-opus-4-5",
"claude-opus-4-5": "claude-opus-4-5",
"claude opus 4.7": "claude-opus-4-7",
"claude-opus-4.7": "claude-opus-4-7",
"claude-opus-4-7": "claude-opus-4-7",
"claude opus 4.6": "claude-opus-4-6",
"claude-opus-4.6": "claude-opus-4-6",
"claude-opus-4-6": "claude-opus-4-6",
Expand Down
29 changes: 24 additions & 5 deletions packages/shared/src/model.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ describe("normalizeModelSlug", () => {

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");
});
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -317,17 +332,20 @@ 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);
});
});

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);
Expand All @@ -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);
Expand Down
13 changes: 10 additions & 3 deletions packages/shared/src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const MODEL_SLUG_SET_BY_PROVIDER: Record<ProviderKind, ReadonlySet<ModelSlug>> =
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";
Expand All @@ -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 {
Expand Down
Loading