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 f57f4ab284..3c35988a8f 100644 --- a/packages/server/src/server/agent/providers/claude/agent.test.ts +++ b/packages/server/src/server/agent/providers/claude/agent.test.ts @@ -1892,6 +1892,45 @@ describe("ClaudeAgentSession context window usage", () => { } }); + test("selected native-1M Claude models seed active context window usage with 1M max tokens", async () => { + const session = await createSessionForTurns( + [ + [ + createInitMessage(), + createMessageStartEvent(), + createSuccessResult({ modelUsage: undefined }), + ], + ], + { model: "claude-sonnet-5" }, + ); + + try { + const events = await collectStreamEvents(session); + + expect(events).toContainEqual( + expect.objectContaining({ + type: "usage_updated", + provider: "claude", + usage: { + contextWindowMaxTokens: 1_000_000, + contextWindowUsedTokens: 150, + }, + }), + ); + expect(events).toContainEqual( + expect.objectContaining({ + type: "turn_completed", + provider: "claude", + usage: expect.objectContaining({ + contextWindowMaxTokens: 1_000_000, + }), + }), + ); + } finally { + await session.close(); + } + }); + test("message_delta stream events update per-request usage", async () => { const session = await createSessionForTurns([ [ diff --git a/packages/server/src/server/agent/providers/claude/agent.ts b/packages/server/src/server/agent/providers/claude/agent.ts index 62af2b0fa8..9d4b6c4ba0 100644 --- a/packages/server/src/server/agent/providers/claude/agent.ts +++ b/packages/server/src/server/agent/providers/claude/agent.ts @@ -1634,7 +1634,8 @@ function resolveInitialContextWindowSize(modelId: string | null | undefined): nu if (normalized.includes("[1m]") || normalized.includes("context-1m")) { return 1_000_000; } - if (normalized.includes("claude-fable-5")) { + const catalogModelId = normalizeClaudeRuntimeModelId(normalized); + if (catalogModelId === "claude-fable-5" || catalogModelId === "claude-sonnet-5") { return 1_000_000; } if (/(?:^|[~/_-])(?:claude[-_ ]*)?(opus|sonnet|haiku)(?:$|[-_ ./])/.test(normalized)) { 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 7264052d23..500ca942c9 100644 --- a/packages/server/src/server/agent/providers/claude/models.test.ts +++ b/packages/server/src/server/agent/providers/claude/models.test.ts @@ -219,6 +219,9 @@ describe("normalizeClaudeRuntimeModelId", () => { it("preserves [1m] suffix from runtime model strings", () => { expect(normalizeClaudeRuntimeModelId("claude-opus-4-6[1m]")).toBe("claude-opus-4-6[1m]"); + }); + + it("strips [1m] suffix for natively-1M models", () => { expect(normalizeClaudeRuntimeModelId("claude-sonnet-5[1m]")).toBe("claude-sonnet-5"); }); diff --git a/packages/server/src/server/agent/providers/claude/models.ts b/packages/server/src/server/agent/providers/claude/models.ts index df9d59a9b4..b978b4641b 100644 --- a/packages/server/src/server/agent/providers/claude/models.ts +++ b/packages/server/src/server/agent/providers/claude/models.ts @@ -225,15 +225,16 @@ export function normalizeClaudeRuntimeModelId(value: string | null | undefined): return trimmed; } - // Fable and Sonnet 5 use single-segment versions, not the {major}-{minor} - // scheme of older opus/sonnet/haiku models. They are natively 1M, so map - // dated or suffixed runtime strings back to the one catalog ID. + // Some new Claude model families use single-segment versions, not the + // {major}-{minor} scheme of older opus/sonnet/haiku models. Map dated or + // suffixed runtime strings back to the matching catalog ID. const singleSegmentMatch = trimmed.match(/(?:claude-)?(fable|sonnet)[-_ ]+(\d+)/i); if (singleSegmentMatch) { const family = singleSegmentMatch[1].toLowerCase(); const major = singleSegmentMatch[2]; - if (family === "fable" || (family === "sonnet" && major === "5")) { - return `claude-${family}-${major}`; + const modelId = `claude-${family}-${major}`; + if (CLAUDE_MODELS.some((model) => model.id === modelId)) { + return modelId; } }