diff --git a/.changeset/fn-7390-claude-cli-latest-models.md b/.changeset/fn-7390-claude-cli-latest-models.md new file mode 100644 index 0000000000..29cf276231 --- /dev/null +++ b/.changeset/fn-7390-claude-cli-latest-models.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Restore Claude Sonnet 5 and latest Anthropic models in the Claude CLI model picker. +category: fix +dev: pi-claude-cli supplemental extraModels now advertises claude-sonnet-5 for the subscription-authenticated CLI surface; direct-Anthropic supplemental registration and static pricing remain withheld per FN-7374's 404 not_found_error handling. Local evidence used claude 2.1.197 with --model accepting aliases/full names; checksum remains upstream-pending-verification. diff --git a/packages/pi-claude-cli/index.ts b/packages/pi-claude-cli/index.ts index a792509737..886bd06293 100644 --- a/packages/pi-claude-cli/index.ts +++ b/packages/pi-claude-cli/index.ts @@ -193,7 +193,39 @@ export default function (pi: ExtensionAPI) { /* * FNXC:ModelCatalog 2026-07-01-18:18: * Keep Claude CLI supplemental metadata limited to models that Fusion can advertise without triggering the direct-Anthropic Sonnet 5 404 loop. `claude-sonnet-5` must come from the upstream/live registry before this provider shows it, because static metadata cannot prove the current account and CLI surface can call that model. + * + * FNXC:ModelCatalog 2026-07-01-20:37: + * Supersede the prior Claude CLI withholding only for `pi-claude-cli`: the local `claude` subprocess routes through the user's Claude subscription/login and its 2.1.197 `--model` help accepts latest aliases/full names, so the CLI picker should advertise Sonnet 5 and newer CLI-callable rows. Keep direct Anthropic supplemental metadata and static pricing withheld because FN-7374's per-account `404 not_found_error` applies to that API surface, not this subscription-authenticated CLI surface. */ + { + id: "claude-sonnet-5", + name: "Claude Sonnet 5", + reasoning: true, + input: ["text", "image"], + cost: { input: 2, output: 10, cacheRead: 0.2, cacheWrite: 2.5 }, + contextWindow: 1_000_000, + maxTokens: 128_000, + }, + { + id: "claude-fable-5", + name: "Claude Fable 5", + reasoning: true, + input: ["text", "image"], + // FNXC:ModelCatalog 2026-07-01-21:04: Fable 5 must use Anthropic's current $10/$50 per MTok row so Fusion's CLI picker does not understate supplemental model cost estimates. + cost: { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 }, + contextWindow: 1_000_000, + maxTokens: 128_000, + }, + { + id: "claude-opus-4-8", + name: "Claude Opus 4.8", + reasoning: true, + input: ["text", "image"], + // FNXC:ModelCatalog 2026-07-01-20:37: The overview confirms Opus 4.8 context/output limits; mirror the closest current Opus CLI supplemental pricing until a precise per-model price row is pinned for this subscription surface. + cost: { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 }, + contextWindow: 1_000_000, + maxTokens: 128_000, + }, { id: "claude-opus-4-7", name: "Claude Opus 4.7", @@ -209,8 +241,8 @@ export default function (pi: ExtensionAPI) { reasoning: true, input: ["text", "image"], cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 }, - contextWindow: 200_000, - maxTokens: 16_384, + contextWindow: 1_000_000, + maxTokens: 64_000, }, { id: "claude-sonnet-4-5", @@ -219,7 +251,7 @@ export default function (pi: ExtensionAPI) { input: ["text", "image"], cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 }, contextWindow: 200_000, - maxTokens: 8_192, + maxTokens: 64_000, }, { id: "claude-haiku-4-5", @@ -228,7 +260,7 @@ export default function (pi: ExtensionAPI) { input: ["text", "image"], cost: { input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1 }, contextWindow: 200_000, - maxTokens: 8_192, + maxTokens: 64_000, }, ]; diff --git a/packages/pi-claude-cli/src/__tests__/provider.test.ts b/packages/pi-claude-cli/src/__tests__/provider.test.ts index 49d4a54ce4..42d314c9d8 100644 --- a/packages/pi-claude-cli/src/__tests__/provider.test.ts +++ b/packages/pi-claude-cli/src/__tests__/provider.test.ts @@ -116,7 +116,7 @@ describe("provider registration (default export)", () => { expect(firstModel.cost).toBeDefined(); }); - it("includes supported extra Claude model entries without force-adding Sonnet 5", async () => { + it("includes latest Claude models including Sonnet 5 for the CLI surface", async () => { const registerProvider = vi.fn(); const mockPi = { registerProvider, on: vi.fn() } as any; @@ -127,6 +127,9 @@ describe("provider registration (default export)", () => { const modelIds = new Set(config.models.map((m: { id: string }) => m.id)); for (const id of [ + "claude-sonnet-5", + "claude-fable-5", + "claude-opus-4-8", "claude-opus-4-7", "claude-sonnet-4-6", "claude-sonnet-4-5", @@ -135,30 +138,35 @@ describe("provider registration (default export)", () => { expect(modelIds.has(id)).toBe(true); } - expect(modelIds.has("claude-sonnet-5")).toBe(false); + expect( + config.models.find((m: { id: string }) => m.id === "claude-fable-5"), + ).toMatchObject({ + cost: { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 }, + }); }); it("deduplicates extra models when catalog already includes them", async () => { const registerProvider = vi.fn(); const mockPi = { registerProvider, on: vi.fn() } as any; const getModelsMock = vi.mocked(getModels); + const upstreamSonnet5 = { + id: "claude-sonnet-5", + name: "Claude Sonnet 5 Upstream", + api: "anthropic", + provider: "anthropic", + reasoning: false, + input: ["text"], + cost: { input: 99, output: 199, cacheRead: 9.9, cacheWrite: 24.75 }, + contextWindow: 123_456, + maxTokens: 7_654, + } as any; getModelsMock.mockReturnValueOnce([ ...mockModels, - { - id: "claude-sonnet-5", - name: "Claude Sonnet 5 Upstream", - api: "anthropic", - provider: "anthropic", - reasoning: true, - input: ["text", "image"], - cost: { input: 2, output: 10, cacheRead: 0.2, cacheWrite: 2.5 }, - contextWindow: 1_000_000, - maxTokens: 128_000, - } as any, + upstreamSonnet5, { id: "claude-sonnet-4-6", - name: "Claude Sonnet 4.6", + name: "Claude Sonnet 4.6 Upstream", api: "anthropic", provider: "anthropic", reasoning: true, @@ -179,6 +187,17 @@ describe("provider registration (default export)", () => { ); expect(matches).toHaveLength(1); } + + expect( + config.models.find((m: { id: string }) => m.id === "claude-sonnet-5"), + ).toMatchObject({ + name: upstreamSonnet5.name, + reasoning: upstreamSonnet5.reasoning, + input: upstreamSonnet5.input, + cost: upstreamSonnet5.cost, + contextWindow: upstreamSonnet5.contextWindow, + maxTokens: upstreamSonnet5.maxTokens, + }); }); });