diff --git a/CHANGELOG.md b/CHANGELOG.md index bc51d2a9a..dc541a620 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Fixed -- Claude Opus 5 now defaults to its 1M context window. +- Claude 5 models now use the correct context windows. ## 0.2.1 - 2026-07-24 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 1bc026c8f..16cd1e913 100644 --- a/packages/server/src/server/agent/providers/claude/agent.test.ts +++ b/packages/server/src/server/agent/providers/claude/agent.test.ts @@ -418,10 +418,12 @@ describe("ClaudeAgentClient.fetchCatalog", () => { expect(models.map((m) => m.id)).toEqual([ "claude-opus-5[1m]", "claude-opus-5", + "claude-fable-5[1m]", "claude-fable-5", "claude-opus-4-8[1m]", "claude-opus-4-8", "claude-sonnet-5", + "claude-sonnet-5[1m]", "claude-opus-4-7[1m]", "claude-opus-4-7", "claude-opus-4-6[1m]", @@ -2091,10 +2093,10 @@ describe("ClaudeAgentSession context window usage", () => { } }); - test("native 1M Claude models seed active context window usage from the catalog", async () => { + test("selected 1M Claude models seed active context window usage from the catalog", async () => { const session = await createSessionForTurns( [[createInitMessage(), createMessageStartEvent(), createSuccessResult()]], - { model: "claude-sonnet-5" }, + { model: "claude-sonnet-5[1m]" }, ); try { 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 1aa360e1f..1b14469dc 100644 --- a/packages/server/src/server/agent/providers/claude/model-manifest.ts +++ b/packages/server/src/server/agent/providers/claude/model-manifest.ts @@ -50,11 +50,20 @@ export const CLAUDE_MODEL_MANIFEST = [ effortLevels: CLAUDE_EFFORT_LEVELS.xhigh, supportsThinkingDisabled: true, }, + { + id: "claude-fable-5[1m]", + label: "Fable 5 1M", + description: "Fable 5 with 1M context window", + minimumClaudeCodeVersion: "2.1.169", + contextWindowMaxTokens: 1_000_000, + effortLevels: CLAUDE_EFFORT_LEVELS.xhigh, + }, { id: "claude-fable-5", label: "Fable 5", description: "Fable 5 · Most powerful model", - contextWindowMaxTokens: 1_000_000, + minimumClaudeCodeVersion: "2.1.169", + contextWindowMaxTokens: 200_000, effortLevels: CLAUDE_EFFORT_LEVELS.xhigh, }, { @@ -80,6 +89,14 @@ export const CLAUDE_MODEL_MANIFEST = [ id: "claude-sonnet-5", label: "Sonnet 5", description: "Sonnet 5 · Best for everyday tasks", + contextWindowMaxTokens: 200_000, + effortLevels: CLAUDE_EFFORT_LEVELS.xhigh, + supportsThinkingDisabled: true, + }, + { + id: "claude-sonnet-5[1m]", + label: "Sonnet 5 1M", + description: "Sonnet 5 with 1M context window", contextWindowMaxTokens: 1_000_000, effortLevels: CLAUDE_EFFORT_LEVELS.xhigh, supportsThinkingDisabled: true, 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 56e49ff77..f960c8e2d 100644 --- a/packages/server/src/server/agent/providers/claude/models.test.ts +++ b/packages/server/src/server/agent/providers/claude/models.test.ts @@ -51,10 +51,12 @@ describe("getClaudeModels", () => { expect(models.map((m) => m.id)).toEqual([ "claude-opus-5[1m]", "claude-opus-5", + "claude-fable-5[1m]", "claude-fable-5", "claude-opus-4-8[1m]", "claude-opus-4-8", "claude-sonnet-5", + "claude-sonnet-5[1m]", "claude-opus-4-7[1m]", "claude-opus-4-7", "claude-opus-4-6[1m]", @@ -81,10 +83,12 @@ describe("getClaudeModels", () => { new Map([ ["claude-opus-5[1m]", 1_000_000], ["claude-opus-5", 200_000], - ["claude-fable-5", 1_000_000], + ["claude-fable-5[1m]", 1_000_000], + ["claude-fable-5", 200_000], ["claude-opus-4-8[1m]", 1_000_000], ["claude-opus-4-8", 200_000], - ["claude-sonnet-5", 1_000_000], + ["claude-sonnet-5", 200_000], + ["claude-sonnet-5[1m]", 1_000_000], ["claude-opus-4-7[1m]", 1_000_000], ["claude-opus-4-7", 200_000], ["claude-opus-4-6[1m]", 1_000_000], @@ -103,6 +107,11 @@ describe("getClaudeModels", () => { 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]"); + expect(getClaudeModels("2.1.168").map((model) => model.id)).not.toContain("claude-fable-5"); + expect(getClaudeModels("2.1.169").map((model) => model.id)).toContain("claude-fable-5[1m]"); + expect(getClaudeModels("2.1.169").map((model) => model.id)).toContain("claude-fable-5"); }); it("derives thinking options from model effort capabilities", () => { @@ -133,6 +142,9 @@ describe("getClaudeModels", () => { ?.label, ).toBe("Ultra Code"); expect(models.get("claude-sonnet-5")?.defaultThinkingOptionId).toBe("low"); + expect(models.get("claude-sonnet-5[1m]")?.thinkingOptions).toEqual( + models.get("claude-sonnet-5")?.thinkingOptions, + ); expect(models.get("claude-opus-4-7")?.thinkingOptions?.map((option) => option.id)).toEqual([ CLAUDE_DISABLED_THINKING_OPTION_ID, @@ -153,6 +165,9 @@ describe("getClaudeModels", () => { expect(models.get("claude-fable-5")?.thinkingOptions?.map((option) => option.id)).not.toContain( CLAUDE_DISABLED_THINKING_OPTION_ID, ); + expect(models.get("claude-fable-5[1m]")?.thinkingOptions).toEqual( + models.get("claude-fable-5")?.thinkingOptions, + ); expect(models.get("claude-haiku-4-5")?.thinkingOptions).toBeUndefined(); }); @@ -160,6 +175,7 @@ describe("getClaudeModels", () => { ["claude-opus-5", true, "low"], ["claude-opus-5-20260724", true, "low"], ["claude-sonnet-5", true, "low"], + ["claude-sonnet-5[1m]", true, "low"], ["claude-sonnet-5-20260101", true, "low"], ["claude-fable-5", false, "low"], ["claude-haiku-4-5", false, undefined], @@ -333,7 +349,9 @@ describe("normalizeClaudeRuntimeModelId", () => { 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]"); expect(normalizeClaudeRuntimeModelId("claude-sonnet-5")).toBe("claude-sonnet-5"); + expect(normalizeClaudeRuntimeModelId("claude-sonnet-5[1m]")).toBe("claude-sonnet-5[1m]"); expect(normalizeClaudeRuntimeModelId("claude-opus-4-6")).toBe("claude-opus-4-6"); expect(normalizeClaudeRuntimeModelId("claude-opus-4-6[1m]")).toBe("claude-opus-4-6[1m]"); expect(normalizeClaudeRuntimeModelId("claude-sonnet-4-6")).toBe("claude-sonnet-4-6"); @@ -351,6 +369,8 @@ 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]"); });