diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a489871a..207fce943 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.1.103 - 2026-07-01 + +### Added + +- Claude Sonnet 5 is available in the Claude model picker ([#1850](https://github.com/getpaseo/paseo/pull/1850)) + ## 0.1.102 - 2026-06-30 ### Added diff --git a/package-lock.json b/package-lock.json index 1c2e712ad..cf8e32b22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "paseo", - "version": "0.1.102", + "version": "0.1.103", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "paseo", - "version": "0.1.102", + "version": "0.1.103", "hasInstallScript": true, "license": "AGPL-3.0-or-later", "workspaces": [ @@ -35132,7 +35132,7 @@ }, "packages/app": { "name": "@getpaseo/app", - "version": "0.1.102", + "version": "0.1.103", "dependencies": { "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", @@ -36150,12 +36150,12 @@ }, "packages/cli": { "name": "@getpaseo/cli", - "version": "0.1.102", + "version": "0.1.103", "dependencies": { "@clack/prompts": "^1.0.0", - "@getpaseo/client": "0.1.102", - "@getpaseo/protocol": "0.1.102", - "@getpaseo/server": "0.1.102", + "@getpaseo/client": "0.1.103", + "@getpaseo/protocol": "0.1.103", + "@getpaseo/server": "0.1.103", "chalk": "^5.3.0", "commander": "^12.0.0", "mime-types": "^2.1.35", @@ -36401,10 +36401,10 @@ }, "packages/client": { "name": "@getpaseo/client", - "version": "0.1.102", + "version": "0.1.103", "dependencies": { - "@getpaseo/protocol": "0.1.102", - "@getpaseo/relay": "0.1.102", + "@getpaseo/protocol": "0.1.103", + "@getpaseo/relay": "0.1.103", "zod": "^4.4.3" }, "devDependencies": { @@ -36415,7 +36415,7 @@ }, "packages/desktop": { "name": "@getpaseo/desktop", - "version": "0.1.102", + "version": "0.1.103", "license": "AGPL-3.0-or-later", "dependencies": { "@getpaseo/cli": "*", @@ -36658,7 +36658,7 @@ }, "packages/expo-two-way-audio": { "name": "@getpaseo/expo-two-way-audio", - "version": "0.1.102", + "version": "0.1.103", "license": "MIT", "devDependencies": { "@types/jest": "^29.5.14", @@ -37554,7 +37554,7 @@ }, "packages/highlight": { "name": "@getpaseo/highlight", - "version": "0.1.102", + "version": "0.1.103", "dependencies": { "@codemirror/language": "^6.12.3", "@codemirror/legacy-modes": "^6.5.3", @@ -37786,7 +37786,7 @@ }, "packages/protocol": { "name": "@getpaseo/protocol", - "version": "0.1.102", + "version": "0.1.103", "dependencies": { "zod": "^4.4.3" }, @@ -37798,7 +37798,7 @@ }, "packages/relay": { "name": "@getpaseo/relay", - "version": "0.1.102", + "version": "0.1.103", "dependencies": { "base64-js": "^1.5.1", "tweetnacl": "^1.0.3", @@ -38016,15 +38016,15 @@ }, "packages/server": { "name": "@getpaseo/server", - "version": "0.1.102", + "version": "0.1.103", "dependencies": { "@agentclientprotocol/sdk": "^0.17.1", "@anthropic-ai/claude-agent-sdk": "^0.3.195", "@anthropic-ai/sdk": "^0.104.2", - "@getpaseo/client": "0.1.102", - "@getpaseo/highlight": "0.1.102", - "@getpaseo/protocol": "0.1.102", - "@getpaseo/relay": "0.1.102", + "@getpaseo/client": "0.1.103", + "@getpaseo/highlight": "0.1.103", + "@getpaseo/protocol": "0.1.103", + "@getpaseo/relay": "0.1.103", "@isaacs/ttlcache": "^2.1.4", "@modelcontextprotocol/sdk": "^1.20.1", "@opencode-ai/sdk": "1.14.46", @@ -38561,7 +38561,7 @@ }, "packages/website": { "name": "@getpaseo/website", - "version": "0.1.102", + "version": "0.1.103", "dependencies": { "@cloudflare/vite-plugin": "^1.29.1", "@cloudflare/workers-types": "^4.20260317.1", diff --git a/package.json b/package.json index ee7a9dbee..ab9555428 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "paseo", - "version": "0.1.102", + "version": "0.1.103", "private": true, "description": "Paseo: voice-controlled development environment for local AI coding agents", "keywords": [ diff --git a/packages/app/package.json b/packages/app/package.json index 44f596166..65efb683e 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/app", - "version": "0.1.102", + "version": "0.1.103", "private": true, "main": "index.ts", "scripts": { diff --git a/packages/cli/package.json b/packages/cli/package.json index 0df17a644..4dc2ac46d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/cli", - "version": "0.1.102", + "version": "0.1.103", "description": "Paseo CLI - control your AI coding agents from the command line", "bin": { "paseo": "bin/paseo" @@ -27,9 +27,9 @@ }, "dependencies": { "@clack/prompts": "^1.0.0", - "@getpaseo/client": "0.1.102", - "@getpaseo/protocol": "0.1.102", - "@getpaseo/server": "0.1.102", + "@getpaseo/client": "0.1.103", + "@getpaseo/protocol": "0.1.103", + "@getpaseo/server": "0.1.103", "chalk": "^5.3.0", "commander": "^12.0.0", "mime-types": "^2.1.35", diff --git a/packages/cli/tests/15-provider.test.ts b/packages/cli/tests/15-provider.test.ts index aaf0bf0f1..f194ff1d4 100644 --- a/packages/cli/tests/15-provider.test.ts +++ b/packages/cli/tests/15-provider.test.ts @@ -60,6 +60,11 @@ const EXPECTED_CLAUDE_MODELS = [ model: "Opus 4.8", descriptionFragment: "Latest release", }, + { + id: "claude-sonnet-5", + model: "Sonnet 5", + descriptionFragment: "Best for everyday tasks", + }, { id: "claude-opus-4-7[1m]", model: "Opus 4.7 1M", @@ -392,7 +397,7 @@ try { "--quiet should print the current Claude catalog IDs", ); assert( - claudeModelsFromJson.some((m) => m.id === "claude-sonnet-4-6"), + claudeModelsFromJson.some((m) => m.id === "claude-sonnet-5"), "captured --json output should include the current Claude everyday model id", ); console.log("✓ provider models --quiet outputs model IDs only\n"); diff --git a/packages/client/package.json b/packages/client/package.json index 2002c2169..fcb8ed419 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/client", - "version": "0.1.102", + "version": "0.1.103", "description": "Paseo client SDK package", "files": [ "dist", @@ -35,8 +35,8 @@ "test": "vitest run" }, "dependencies": { - "@getpaseo/protocol": "0.1.102", - "@getpaseo/relay": "0.1.102", + "@getpaseo/protocol": "0.1.103", + "@getpaseo/relay": "0.1.103", "zod": "^4.4.3" }, "devDependencies": { diff --git a/packages/desktop/package.json b/packages/desktop/package.json index d5b6d9f75..f1c0182b8 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/desktop", - "version": "0.1.102", + "version": "0.1.103", "private": true, "description": "Paseo desktop app (Electron wrapper)", "homepage": "https://paseo.sh", diff --git a/packages/expo-two-way-audio/package.json b/packages/expo-two-way-audio/package.json index 905a5ee7a..f1f5a0561 100644 --- a/packages/expo-two-way-audio/package.json +++ b/packages/expo-two-way-audio/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/expo-two-way-audio", - "version": "0.1.102", + "version": "0.1.103", "description": "Native module for two way audio streaming", "keywords": [ "ExpoTwoWayAudio", diff --git a/packages/highlight/package.json b/packages/highlight/package.json index d0da8d7d7..bf552e588 100644 --- a/packages/highlight/package.json +++ b/packages/highlight/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/highlight", - "version": "0.1.102", + "version": "0.1.103", "files": [ "dist", "!dist/**/*.map" diff --git a/packages/protocol/package.json b/packages/protocol/package.json index ae18debef..692bf57d5 100644 --- a/packages/protocol/package.json +++ b/packages/protocol/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/protocol", - "version": "0.1.102", + "version": "0.1.103", "description": "Paseo shared protocol schemas and wire types", "files": [ "dist", diff --git a/packages/protocol/src/agent-types.ts b/packages/protocol/src/agent-types.ts index 9f90cc65e..b589ac3d0 100644 --- a/packages/protocol/src/agent-types.ts +++ b/packages/protocol/src/agent-types.ts @@ -78,6 +78,7 @@ export interface AgentModelDefinition { description?: string; isDefault?: boolean; metadata?: AgentMetadata; + contextWindowMaxTokens?: number; thinkingOptions?: AgentSelectOption[]; defaultThinkingOptionId?: string; } diff --git a/packages/protocol/src/messages.providers-snapshot.test.ts b/packages/protocol/src/messages.providers-snapshot.test.ts index 1b066062d..ca8a2c94c 100644 --- a/packages/protocol/src/messages.providers-snapshot.test.ts +++ b/packages/protocol/src/messages.providers-snapshot.test.ts @@ -48,6 +48,7 @@ describe("provider snapshot message schemas", () => { id: "MiniMax-M2.7", label: "MiniMax-M2.7", isDefault: true, + contextWindowMaxTokens: 1_000_000, thinkingOptions: [ { id: "off", label: "Off" }, { id: "max", label: "Max", isDefault: true }, @@ -62,6 +63,7 @@ describe("provider snapshot message schemas", () => { id: "MiniMax-M2.7", label: "MiniMax-M2.7", isDefault: true, + contextWindowMaxTokens: 1_000_000, thinkingOptions: [ { id: "off", label: "Off" }, { id: "max", label: "Max", isDefault: true }, diff --git a/packages/protocol/src/messages.ts b/packages/protocol/src/messages.ts index 498c2caf7..8e726078e 100644 --- a/packages/protocol/src/messages.ts +++ b/packages/protocol/src/messages.ts @@ -254,6 +254,7 @@ const AgentModelDefinitionSchema: z.ZodType = z description: z.string().optional(), isDefault: z.boolean().optional(), metadata: z.record(z.string(), z.unknown()).optional(), + contextWindowMaxTokens: z.number().optional(), thinkingOptions: z.array(AgentSelectOptionSchema).optional(), defaultThinkingOptionId: z.string().optional(), }) diff --git a/packages/relay/package.json b/packages/relay/package.json index 372662799..f5d16f1cf 100644 --- a/packages/relay/package.json +++ b/packages/relay/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/relay", - "version": "0.1.102", + "version": "0.1.103", "description": "Paseo relay for bridging daemon and client connections", "files": [ "dist", diff --git a/packages/server/package.json b/packages/server/package.json index c879ec8ef..43bc2021b 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/server", - "version": "0.1.102", + "version": "0.1.103", "description": "Paseo backend server", "files": [ "dist/server", @@ -65,10 +65,10 @@ "@agentclientprotocol/sdk": "^0.17.1", "@anthropic-ai/claude-agent-sdk": "^0.3.195", "@anthropic-ai/sdk": "^0.104.2", - "@getpaseo/client": "0.1.102", - "@getpaseo/highlight": "0.1.102", - "@getpaseo/protocol": "0.1.102", - "@getpaseo/relay": "0.1.102", + "@getpaseo/client": "0.1.103", + "@getpaseo/highlight": "0.1.103", + "@getpaseo/protocol": "0.1.103", + "@getpaseo/relay": "0.1.103", "@isaacs/ttlcache": "^2.1.4", "@modelcontextprotocol/sdk": "^1.20.1", "@opencode-ai/sdk": "1.14.46", diff --git a/packages/server/src/server/agent/agent-sdk-types.ts b/packages/server/src/server/agent/agent-sdk-types.ts index 56c437ef0..86dddab26 100644 --- a/packages/server/src/server/agent/agent-sdk-types.ts +++ b/packages/server/src/server/agent/agent-sdk-types.ts @@ -79,6 +79,7 @@ export interface AgentModelDefinition { description?: string; isDefault?: boolean; metadata?: AgentMetadata; + contextWindowMaxTokens?: number; thinkingOptions?: AgentSelectOption[]; defaultThinkingOptionId?: string; } 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 9000fbc93..69222e1e6 100644 --- a/packages/server/src/server/agent/providers/claude/agent.test.ts +++ b/packages/server/src/server/agent/providers/claude/agent.test.ts @@ -416,6 +416,7 @@ describe("ClaudeAgentClient.fetchCatalog", () => { "claude-fable-5", "claude-opus-4-8[1m]", "claude-opus-4-8", + "claude-sonnet-5", "claude-opus-4-7[1m]", "claude-opus-4-7", "claude-opus-4-6[1m]", @@ -457,6 +458,8 @@ describe("ClaudeAgentClient.fetchCatalog", () => { expect(getThinkingIds("claude-fable-5")).toContain("ultracode"); expect(getThinkingIds("claude-opus-4-8[1m]")).toContain("ultracode"); expect(getThinkingIds("claude-opus-4-8")).toContain("ultracode"); + expect(getThinkingIds("claude-sonnet-5")).toContain("xhigh"); + expect(getThinkingIds("claude-sonnet-5")).not.toContain("ultracode"); expect(getThinkingIds("claude-opus-4-7")).not.toContain("ultracode"); expect(getThinkingIds("claude-sonnet-4-6")).not.toContain("ultracode"); } finally { @@ -1889,6 +1892,30 @@ describe("ClaudeAgentSession context window usage", () => { } }); + test("native 1M Claude models seed active context window usage from the catalog", async () => { + const session = await createSessionForTurns( + [[createInitMessage(), createMessageStartEvent(), createSuccessResult()]], + { 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, + }, + }), + ); + } 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 62af2b0fa..427a24ce5 100644 --- a/packages/server/src/server/agent/providers/claude/agent.ts +++ b/packages/server/src/server/agent/providers/claude/agent.ts @@ -29,7 +29,11 @@ import { mapTaskNotificationSystemRecordToToolCall, mapTaskNotificationUserContentToToolCall, } from "./task-notification-tool-call.js"; -import { getClaudeModelsWithSettings, normalizeClaudeRuntimeModelId } from "./models.js"; +import { + findClaudeModel, + getClaudeModelsWithSettings, + normalizeClaudeRuntimeModelId, +} from "./models.js"; import { parsePartialJsonObject } from "./partial-json.js"; import { ClaudeSidechainTracker } from "./sidechain-tracker.js"; import { buildClaudeFeatures, claudeModelSupportsFastMode } from "./feature-definitions.js"; @@ -1626,23 +1630,6 @@ function extractContextWindowSize(modelUsage: unknown): number | undefined { return maxContextWindow; } -function resolveInitialContextWindowSize(modelId: string | null | undefined): number | undefined { - const normalized = typeof modelId === "string" ? modelId.trim().toLowerCase() : ""; - if (!normalized) { - return undefined; - } - if (normalized.includes("[1m]") || normalized.includes("context-1m")) { - return 1_000_000; - } - if (normalized.includes("claude-fable-5")) { - return 1_000_000; - } - if (/(?:^|[~/_-])(?:claude[-_ ]*)?(opus|sonnet|haiku)(?:$|[-_ ./])/.test(normalized)) { - return 200_000; - } - return undefined; -} - function readStreamRequestInputTokens(event: Record): number | undefined { const messageUsage = toObjectRecord(toObjectRecord(event.message)?.usage); if (!messageUsage) { @@ -1941,7 +1928,7 @@ class ClaudeAgentSession implements AgentSession { this.queryFactory = options.queryFactory; this.resolveBinary = options.resolveBinary; this.contextUsage = new ClaudeContextUsageState( - resolveInitialContextWindowSize(this.config.model), + findClaudeModel(this.config.model)?.contextWindowMaxTokens, ); const handle = options.handle; @@ -2187,7 +2174,7 @@ class ClaudeAgentSession implements AgentSession { await this.applyFastModeFeature(false, activeQuery); } this.contextUsage.setInitialContextWindowMaxTokens( - resolveInitialContextWindowSize(this.config.model), + findClaudeModel(this.config.model)?.contextWindowMaxTokens, ); this.lastOptionsModel = normalizedModelId ?? this.lastOptionsModel; this.lastRuntimeModel = null; 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 3ed41aa04..ec66447b3 100644 --- a/packages/server/src/server/agent/providers/claude/models.test.ts +++ b/packages/server/src/server/agent/providers/claude/models.test.ts @@ -5,7 +5,7 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import { createTestLogger } from "../../../../test-utils/test-logger.js"; import { ClaudeAgentClient } from "./agent.js"; -import { getClaudeModels, normalizeClaudeRuntimeModelId } from "./models.js"; +import { findClaudeModel, getClaudeModels, normalizeClaudeRuntimeModelId } from "./models.js"; const createdClaudeConfigDirs: string[] = []; @@ -38,6 +38,7 @@ describe("getClaudeModels", () => { "claude-fable-5", "claude-opus-4-8[1m]", "claude-opus-4-8", + "claude-sonnet-5", "claude-opus-4-7[1m]", "claude-opus-4-7", "claude-opus-4-6[1m]", @@ -55,6 +56,28 @@ describe("getClaudeModels", () => { expect(defaults[0].id).toBe("claude-opus-4-8"); }); + it("defines context window sizes in the catalog", () => { + const contextWindows = new Map( + getClaudeModels().map((model) => [model.id, model.contextWindowMaxTokens]), + ); + + expect(contextWindows).toEqual( + new Map([ + ["claude-fable-5", 1_000_000], + ["claude-opus-4-8[1m]", 1_000_000], + ["claude-opus-4-8", 200_000], + ["claude-sonnet-5", 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], + ["claude-opus-4-6", 200_000], + ["claude-sonnet-4-6[1m]", 1_000_000], + ["claude-sonnet-4-6", 200_000], + ["claude-haiku-4-5", 200_000], + ]), + ); + }); + it("returns fresh copies each call", () => { const a = getClaudeModels(); const b = getClaudeModels(); @@ -201,6 +224,7 @@ describe("ClaudeAgentClient.fetchCatalog", () => { describe("normalizeClaudeRuntimeModelId", () => { it("returns exact match for known model IDs", () => { expect(normalizeClaudeRuntimeModelId("claude-fable-5")).toBe("claude-fable-5"); + expect(normalizeClaudeRuntimeModelId("claude-sonnet-5")).toBe("claude-sonnet-5"); 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"); @@ -209,6 +233,7 @@ describe("normalizeClaudeRuntimeModelId", () => { it("normalizes dated model IDs to base model", () => { expect(normalizeClaudeRuntimeModelId("claude-fable-5-20260301")).toBe("claude-fable-5"); + expect(normalizeClaudeRuntimeModelId("claude-sonnet-5-20260101")).toBe("claude-sonnet-5"); 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"); @@ -230,3 +255,10 @@ describe("normalizeClaudeRuntimeModelId", () => { expect(normalizeClaudeRuntimeModelId("random")).toBeNull(); }); }); + +describe("findClaudeModel", () => { + it("resolves runtime model IDs to catalog entries", () => { + expect(findClaudeModel("claude-sonnet-5-20260101")?.id).toBe("claude-sonnet-5"); + expect(findClaudeModel("claude-sonnet-5[1m]")?.contextWindowMaxTokens).toBe(1_000_000); + }); +}); diff --git a/packages/server/src/server/agent/providers/claude/models.ts b/packages/server/src/server/agent/providers/claude/models.ts index 7c8ea10e8..f64120775 100644 --- a/packages/server/src/server/agent/providers/claude/models.ts +++ b/packages/server/src/server/agent/providers/claude/models.ts @@ -12,7 +12,7 @@ const CLAUDE_THINKING_OPTIONS = [ { id: "max", label: "Max" }, ] as const; -const CLAUDE_OPUS_EXTENDED_THINKING_OPTIONS = [ +const CLAUDE_EXTENDED_THINKING_OPTIONS = [ { id: "low", label: "Low" }, { id: "medium", label: "Medium" }, { id: "high", label: "High" }, @@ -21,7 +21,7 @@ const CLAUDE_OPUS_EXTENDED_THINKING_OPTIONS = [ ] as const; const CLAUDE_ULTRACODE_THINKING_OPTIONS = [ - ...CLAUDE_OPUS_EXTENDED_THINKING_OPTIONS, + ...CLAUDE_EXTENDED_THINKING_OPTIONS, { id: "ultracode", label: "Ultracode" }, ] as const; @@ -31,6 +31,7 @@ const CLAUDE_MODELS: AgentModelDefinition[] = [ id: "claude-fable-5", label: "Fable 5", description: "Fable 5 · Most powerful model", + contextWindowMaxTokens: 1_000_000, thinkingOptions: [...CLAUDE_ULTRACODE_THINKING_OPTIONS], }, { @@ -38,6 +39,7 @@ const CLAUDE_MODELS: AgentModelDefinition[] = [ id: "claude-opus-4-8[1m]", label: "Opus 4.8 1M", description: "Opus 4.8 with 1M context window", + contextWindowMaxTokens: 1_000_000, thinkingOptions: [...CLAUDE_ULTRACODE_THINKING_OPTIONS], }, { @@ -46,27 +48,39 @@ const CLAUDE_MODELS: AgentModelDefinition[] = [ label: "Opus 4.8", description: "Opus 4.8 · Latest release", isDefault: true, + contextWindowMaxTokens: 200_000, thinkingOptions: [...CLAUDE_ULTRACODE_THINKING_OPTIONS], }, + { + provider: "claude", + id: "claude-sonnet-5", + label: "Sonnet 5", + description: "Sonnet 5 · Best for everyday tasks", + contextWindowMaxTokens: 1_000_000, + thinkingOptions: [...CLAUDE_EXTENDED_THINKING_OPTIONS], + }, { provider: "claude", id: "claude-opus-4-7[1m]", label: "Opus 4.7 1M", description: "Opus 4.7 with 1M context window", - thinkingOptions: [...CLAUDE_OPUS_EXTENDED_THINKING_OPTIONS], + contextWindowMaxTokens: 1_000_000, + thinkingOptions: [...CLAUDE_EXTENDED_THINKING_OPTIONS], }, { provider: "claude", id: "claude-opus-4-7", label: "Opus 4.7", description: "Opus 4.7 · Previous release", - thinkingOptions: [...CLAUDE_OPUS_EXTENDED_THINKING_OPTIONS], + contextWindowMaxTokens: 200_000, + thinkingOptions: [...CLAUDE_EXTENDED_THINKING_OPTIONS], }, { provider: "claude", id: "claude-opus-4-6[1m]", label: "Opus 4.6 1M", description: "Opus 4.6 with 1M context window", + contextWindowMaxTokens: 1_000_000, thinkingOptions: [...CLAUDE_THINKING_OPTIONS], }, { @@ -74,6 +88,7 @@ const CLAUDE_MODELS: AgentModelDefinition[] = [ id: "claude-opus-4-6", label: "Opus 4.6", description: "Opus 4.6 · Most capable for complex work", + contextWindowMaxTokens: 200_000, thinkingOptions: [...CLAUDE_THINKING_OPTIONS], }, { @@ -81,6 +96,7 @@ const CLAUDE_MODELS: AgentModelDefinition[] = [ id: "claude-sonnet-4-6[1m]", label: "Sonnet 4.6 1M", description: "Sonnet 4.6 with 1M context window", + contextWindowMaxTokens: 1_000_000, thinkingOptions: [...CLAUDE_THINKING_OPTIONS], }, { @@ -88,6 +104,7 @@ const CLAUDE_MODELS: AgentModelDefinition[] = [ id: "claude-sonnet-4-6", label: "Sonnet 4.6", description: "Sonnet 4.6 · Best for everyday tasks", + contextWindowMaxTokens: 200_000, thinkingOptions: [...CLAUDE_THINKING_OPTIONS], }, { @@ -95,6 +112,7 @@ const CLAUDE_MODELS: AgentModelDefinition[] = [ id: "claude-haiku-4-5", label: "Haiku 4.5", description: "Haiku 4.5 · Fastest for quick answers", + contextWindowMaxTokens: 200_000, }, ]; @@ -110,6 +128,16 @@ export function getClaudeModels(): AgentModelDefinition[] { return CLAUDE_MODELS.map((model) => ({ ...model })); } +export function findClaudeModel( + modelId: string | null | undefined, +): AgentModelDefinition | undefined { + const normalizedModelId = normalizeClaudeRuntimeModelId(modelId); + if (!normalizedModelId) { + return undefined; + } + return CLAUDE_MODELS.find((model) => model.id === normalizedModelId); +} + export async function getClaudeModelsWithSettings( logger: Logger, configDir?: string, @@ -218,13 +246,19 @@ export function normalizeClaudeRuntimeModelId(value: string | null | undefined): return trimmed; } - // Fable uses a single-segment version (claude-fable-5), not the {major}-{minor} - // scheme of opus/sonnet/haiku, so match it separately. This maps dated runtime - // strings (e.g. claude-fable-5-20260301) back to the catalog ID. No [1m] variant: - // Fable 5 is natively 1M, so there is no 200K-default model to opt into 1M. - const fableMatch = trimmed.match(/(?:claude-)?fable[-_ ]+(\d+)/i); - if (fableMatch) { - return `claude-fable-${fableMatch[1]}`; + // Match single-segment major-version model names such as claude-fable-5 + // and claude-sonnet-5, including dated runtime strings from the SDK. + const singleSegmentMatch = trimmed.match(/(?:claude-)?(fable|opus|sonnet|haiku)[-_ ]+(\d+)/i); + if (singleSegmentMatch) { + const family = singleSegmentMatch[1].toLowerCase(); + const major = singleSegmentMatch[2]; + const suffix = trimmed.toLowerCase().includes("[1m]") ? "[1m]" : ""; + const candidates = [`claude-${family}-${major}${suffix}`, `claude-${family}-${major}`]; + for (const candidate of candidates) { + if (CLAUDE_MODELS.some((model) => model.id === candidate)) { + return candidate; + } + } } // Match: claude-{family}-{major}-{minor}[1m]? possibly followed by a date suffix diff --git a/packages/website/package.json b/packages/website/package.json index aa414cd35..1e9a678ea 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -1,6 +1,6 @@ { "name": "@getpaseo/website", - "version": "0.1.102", + "version": "0.1.103", "private": true, "type": "module", "scripts": {