diff --git a/packages/cli/tests/15-provider.test.ts b/packages/cli/tests/15-provider.test.ts index ec07fe862..421f68aa5 100644 --- a/packages/cli/tests/15-provider.test.ts +++ b/packages/cli/tests/15-provider.test.ts @@ -108,16 +108,6 @@ const EXPECTED_CLAUDE_MODELS = [ ] as const; const EXPECTED_CLAUDE_CONTEXT_MODELS = [ - { - id: "claude-opus-5[1m]", - model: "Opus 5 1M", - descriptionFragment: "1M context window", - }, - { - id: "claude-opus-5", - model: "Opus 5", - descriptionFragment: "200K context window", - }, { id: "claude-fable-5[1m]", model: "Fable 5 1M", diff --git a/packages/server/src/server/agent/providers/claude/agent.test.ts b/packages/server/src/server/agent/providers/claude/agent.test.ts index e773cd735..e4e880592 100644 --- a/packages/server/src/server/agent/providers/claude/agent.test.ts +++ b/packages/server/src/server/agent/providers/claude/agent.test.ts @@ -416,7 +416,6 @@ describe("ClaudeAgentClient.fetchCatalog", () => { }); expect(models.map((m) => m.id)).toEqual([ - "claude-opus-5[1m]", "claude-opus-5", "claude-fable-5[1m]", "claude-fable-5", @@ -439,7 +438,7 @@ describe("ClaudeAgentClient.fetchCatalog", () => { } const defaultModel = models.find((m) => m.isDefault); - expect(defaultModel?.id).toBe("claude-opus-5[1m]"); + expect(defaultModel?.id).toBe("claude-opus-5"); } finally { await fs.rm(emptyConfigDir, { recursive: true, force: true }); } @@ -461,7 +460,7 @@ describe("ClaudeAgentClient.fetchCatalog", () => { force: false, }); - expect(models.find((model) => model.isDefault)?.id).toBe("claude-opus-5[1m]"); + expect(models.find((model) => model.isDefault)?.id).toBe("claude-opus-5"); expect(models.map((model) => model.id)).toContain("claude-fable-5[1m]"); } finally { await fs.rm(emptyConfigDir, { recursive: true, force: true }); diff --git a/packages/server/src/server/agent/providers/claude/model-manifest.ts b/packages/server/src/server/agent/providers/claude/model-manifest.ts index ec9efdb1f..d091e07c7 100644 --- a/packages/server/src/server/agent/providers/claude/model-manifest.ts +++ b/packages/server/src/server/agent/providers/claude/model-manifest.ts @@ -32,24 +32,15 @@ export const CLAUDE_ULTRACODE_THINKING_OPTION_ID = "ultracode"; export const CLAUDE_MODEL_MANIFEST = [ { - id: "claude-opus-5[1m]", - label: "Opus 5 1M", - description: "Opus 5 with 1M context window", + id: "claude-opus-5", + label: "Opus 5", + description: "Opus 5 · Latest release", defaultPriority: 2, minimumClaudeCodeVersion: "2.1.219", contextWindowMaxTokens: 1_000_000, effortLevels: CLAUDE_EFFORT_LEVELS.xhigh, supportsThinkingDisabled: true, }, - { - id: "claude-opus-5", - label: "Opus 5", - description: "Opus 5 · 200K context window", - minimumClaudeCodeVersion: "2.1.219", - contextWindowMaxTokens: 200_000, - effortLevels: CLAUDE_EFFORT_LEVELS.xhigh, - supportsThinkingDisabled: true, - }, { id: "claude-fable-5[1m]", label: "Fable 5 1M", diff --git a/packages/server/src/server/agent/providers/claude/models.test.ts b/packages/server/src/server/agent/providers/claude/models.test.ts index c3e62b1dc..24a49baa7 100644 --- a/packages/server/src/server/agent/providers/claude/models.test.ts +++ b/packages/server/src/server/agent/providers/claude/models.test.ts @@ -50,7 +50,6 @@ describe("getClaudeModels", () => { it("returns all claude models", () => { const models = getClaudeModels(); expect(models.map((m) => m.id)).toEqual([ - "claude-opus-5[1m]", "claude-opus-5", "claude-fable-5[1m]", "claude-fable-5", @@ -72,7 +71,7 @@ describe("getClaudeModels", () => { const models = getClaudeModels(); const defaults = models.filter((m) => m.isDefault); expect(defaults).toHaveLength(1); - expect(defaults[0].id).toBe("claude-opus-5[1m]"); + expect(defaults[0].id).toBe("claude-opus-5"); }); it("defines context window sizes in the catalog", () => { @@ -82,8 +81,7 @@ describe("getClaudeModels", () => { expect(contextWindows).toEqual( new Map([ - ["claude-opus-5[1m]", 1_000_000], - ["claude-opus-5", 200_000], + ["claude-opus-5", 1_000_000], ["claude-fable-5[1m]", 1_000_000], ["claude-fable-5", 200_000], ["claude-opus-4-8[1m]", 1_000_000], @@ -103,10 +101,8 @@ describe("getClaudeModels", () => { it("filters models by their minimum Claude Code version", () => { const oldVersionModels = getClaudeModels("2.1.218"); - expect(oldVersionModels.map((model) => model.id)).not.toContain("claude-opus-5[1m]"); expect(oldVersionModels.map((model) => model.id)).not.toContain("claude-opus-5"); expect(oldVersionModels.find((model) => model.isDefault)?.id).toBe("claude-opus-4-8"); - expect(getClaudeModels("2.1.219").map((model) => model.id)).toContain("claude-opus-5[1m]"); expect(getClaudeModels("2.1.219").map((model) => model.id)).toContain("claude-opus-5"); expect(getClaudeModels("2.1.168").map((model) => model.id)).not.toContain("claude-fable-5[1m]"); @@ -347,7 +343,6 @@ describe("ClaudeAgentClient.fetchCatalog", () => { describe("normalizeClaudeRuntimeModelId", () => { it("returns exact match for known model IDs", () => { - expect(normalizeClaudeRuntimeModelId("claude-opus-5[1m]")).toBe("claude-opus-5[1m]"); expect(normalizeClaudeRuntimeModelId("claude-opus-5")).toBe("claude-opus-5"); expect(normalizeClaudeRuntimeModelId("claude-fable-5")).toBe("claude-fable-5"); expect(normalizeClaudeRuntimeModelId("claude-fable-5[1m]")).toBe("claude-fable-5[1m]"); @@ -366,7 +361,6 @@ describe("normalizeClaudeRuntimeModelId", () => { expect(normalizeClaudeRuntimeModelId("claude-opus-4-6-20260101")).toBe("claude-opus-4-6"); expect(normalizeClaudeRuntimeModelId("claude-sonnet-4-6-20260101")).toBe("claude-sonnet-4-6"); expect(normalizeClaudeRuntimeModelId("claude-haiku-4-5-20251001")).toBe("claude-haiku-4-5"); - expect(normalizeClaudeRuntimeModelId("claude-opus-5-20260724[1m]")).toBe("claude-opus-5[1m]"); expect(normalizeClaudeRuntimeModelId("claude-fable-5-20260301[1m]")).toBe("claude-fable-5[1m]"); expect(normalizeClaudeRuntimeModelId("claude-sonnet-5-20260101[1m]")).toBe( "claude-sonnet-5[1m]", @@ -374,7 +368,6 @@ describe("normalizeClaudeRuntimeModelId", () => { }); it("preserves [1m] suffix from runtime model strings", () => { - expect(normalizeClaudeRuntimeModelId("claude-opus-5[1m]")).toBe("claude-opus-5[1m]"); expect(normalizeClaudeRuntimeModelId("claude-fable-5[1m]")).toBe("claude-fable-5[1m]"); expect(normalizeClaudeRuntimeModelId("claude-sonnet-5[1m]")).toBe("claude-sonnet-5[1m]"); expect(normalizeClaudeRuntimeModelId("claude-opus-4-6[1m]")).toBe("claude-opus-4-6[1m]"); @@ -421,6 +414,32 @@ describe("findClaudeModel", () => { }); }); +describe("Claude Opus 5 catalog", () => { + it("offers a single Opus 5 entry with a 1M context window", () => { + const opus5Models = getClaudeModels() + .filter((model) => model.id.startsWith("claude-opus-5")) + .map(({ id, label, contextWindowMaxTokens }) => ({ id, label, contextWindowMaxTokens })); + + expect(opus5Models).toEqual([ + { id: "claude-opus-5", label: "Opus 5", contextWindowMaxTokens: 1_000_000 }, + ]); + }); + + it("resolves retired and dated Opus 5 IDs to the single catalog entry", () => { + expect(findClaudeModel("claude-opus-5[1m]")?.id).toBe("claude-opus-5"); + expect(findClaudeModel("claude-opus-5-20260724")?.id).toBe("claude-opus-5"); + expect(findClaudeModel("claude-opus-5-20260724[1m]")?.id).toBe("claude-opus-5"); + expect(findClaudeModel("claude-opus-5[1m]")?.contextWindowMaxTokens).toBe(1_000_000); + }); + + it("keeps disabled thinking available for agents persisted on the retired 1M ID", () => { + expect(resolveClaudeDisabledThinkingForModel("claude-opus-5[1m]")).toEqual({ + supported: true, + fallbackThinkingOptionId: "low", + }); + }); +}); + describe("claudeManifestModelSupportsFastMode", () => { it("keeps fast mode strict to first-party manifest model IDs", () => { expect(normalizeClaudeManifestModelId("openrouter/anthropic/claude-opus-4-8")).toBeNull();