From a02830d100f1aa5ffdd0cc2e6119f4218bd24804 Mon Sep 17 00:00:00 2001 From: davidebaraldo Date: Tue, 1 Sep 2026 23:41:58 +0200 Subject: [PATCH 1/5] feat(models): add Claude Fable 5.1 Anthropic released Claude Fable 5.1 (claude-fable-5-1) on 2026-09-01: same request surface as Claude Fable 5 (adaptive-only thinking that cannot be disabled, non-default temperature/top_p/top_k rejected), 1M context, 128K max output, $10/$50 per MTok with cache reads at $0.25/MTok - a quarter of Fable 5's cache-read price. Add the model everywhere the platform actually serves it today, mirroring the existing claude-fable-5 entries: - provider registries: anthropic (API key), claude (OAuth), claude/web, vertex - modelSpecs: capabilities + Bedrock aliases (anthropic.claude-fable-5-1) - pricing: frontier-labs (new CLAUDE_FABLE_5_1_PRICING shared tier) and the cc OAuth subscription table, both with the lower cache-read price - providerCostData, staticModels, CONTEXT_1M_SUPPORTED_MODELS - modelFamilyFallback: falls to claude-fable-5, then claude-opus-5/sonnet-5 Deliberately not added: third-party gateway registries (cursor, kiro, copilot, etc.) whose upstream catalogs have not published the model, and the fast-mode default list, since fast-mode availability for 5.1 is not documented. --- open-sse/config/context1m.ts | 3 ++- .../config/providers/registry/anthropic/index.ts | 9 +++++++++ .../config/providers/registry/claude/index.ts | 7 +++++++ .../providers/registry/claude/web/index.ts | 1 + .../config/providers/registry/vertex/index.ts | 1 + open-sse/services/modelFamilyFallback.ts | 3 ++- open-sse/services/providerCostData.ts | 1 + src/lib/providers/staticModels.ts | 1 + src/shared/constants/modelSpecs.ts | 16 ++++++++++++++++ src/shared/constants/pricing/frontier-labs.ts | 2 ++ .../constants/pricing/oauth-subscriptions.ts | 7 +++++++ src/shared/constants/pricing/shared-tiers.ts | 10 ++++++++++ 12 files changed, 59 insertions(+), 2 deletions(-) diff --git a/open-sse/config/context1m.ts b/open-sse/config/context1m.ts index dab5c405b1b0..6173cf8411b7 100644 --- a/open-sse/config/context1m.ts +++ b/open-sse/config/context1m.ts @@ -13,6 +13,7 @@ * without importing each other. */ export const CONTEXT_1M_SUPPORTED_MODELS = [ + "claude-fable-5-1", "claude-fable-5", "claude-sonnet-5", "claude-sonnet-4-6", @@ -36,4 +37,4 @@ export function modelSupportsContext1mBeta(model: string | null | undefined): bo return CONTEXT_1M_SUPPORTED_MODELS.some( (supported) => normalizedModel === supported || normalizedModel.startsWith(`${supported}-`) ); -} \ No newline at end of file +} diff --git a/open-sse/config/providers/registry/anthropic/index.ts b/open-sse/config/providers/registry/anthropic/index.ts index 2a3726276d2f..9e72a4690c74 100644 --- a/open-sse/config/providers/registry/anthropic/index.ts +++ b/open-sse/config/providers/registry/anthropic/index.ts @@ -16,6 +16,15 @@ export const anthropicProvider: RegistryEntry = { "Anthropic-Beta": ANTHROPIC_BETA_API_KEY, }, models: [ + { + id: "claude-fable-5-1", + name: "Claude Fable 5.1", + contextLength: 1048576, + maxOutputTokens: 128000, + // Fable 5.1 (2026-09-01) keeps Fable 5's request surface: adaptive-only thinking, + // non-default temperature/top_p/top_k rejected with a 400. + unsupportedParams: ["temperature", "top_p", "top_k"], + }, { id: "claude-fable-5", name: "Claude Fable 5", diff --git a/open-sse/config/providers/registry/claude/index.ts b/open-sse/config/providers/registry/claude/index.ts index 481c831c2f12..e1fe4bf3ab9b 100644 --- a/open-sse/config/providers/registry/claude/index.ts +++ b/open-sse/config/providers/registry/claude/index.ts @@ -28,6 +28,13 @@ export const claudeProvider: RegistryEntry = { tokenUrl: "https://api.anthropic.com/v1/oauth/token", }, models: [ + { + id: "claude-fable-5-1", + name: "Claude Fable 5.1", + contextLength: 1000000, + maxOutputTokens: 128000, + unsupportedParams: ["temperature", "top_p", "top_k"], + }, { id: "claude-fable-5", name: "Claude Fable 5", diff --git a/open-sse/config/providers/registry/claude/web/index.ts b/open-sse/config/providers/registry/claude/web/index.ts index c701ff9aa3ff..5e1f18e4c939 100644 --- a/open-sse/config/providers/registry/claude/web/index.ts +++ b/open-sse/config/providers/registry/claude/web/index.ts @@ -9,6 +9,7 @@ export const claude_webProvider: RegistryEntry = { authType: "apikey", authHeader: "cookie", models: [ + { id: "claude-fable-5-1", name: "Claude Fable 5.1 (web)", toolCalling: false }, { id: "claude-fable-5", name: "Claude Fable 5 (web)", toolCalling: false }, { id: "claude-opus-5", diff --git a/open-sse/config/providers/registry/vertex/index.ts b/open-sse/config/providers/registry/vertex/index.ts index 1eac20fc15ef..4b72b9f7c451 100644 --- a/open-sse/config/providers/registry/vertex/index.ts +++ b/open-sse/config/providers/registry/vertex/index.ts @@ -27,6 +27,7 @@ export const vertexProvider: RegistryEntry = { { id: "DeepSeek-V4-Pro", name: "DeepSeek V4 Pro (Vertex Partner)" }, { id: "Qwen3.6-35B-A3B", name: "Qwen3.6 35B A3B (Vertex Partner)" }, { id: "GLM-5.1-FP8", name: "GLM-5.1 (Vertex Partner)" }, + { id: "claude-fable-5-1", name: "Claude Fable 5.1 (Vertex)", targetFormat: "claude" }, { id: "claude-fable-5", name: "Claude Fable 5 (Vertex)", targetFormat: "claude" }, { id: "claude-opus-5", name: "Claude Opus 5 (Vertex)", targetFormat: "claude" }, { id: "claude-sonnet-5", name: "Claude Sonnet 5 (Vertex)", targetFormat: "claude" }, diff --git a/open-sse/services/modelFamilyFallback.ts b/open-sse/services/modelFamilyFallback.ts index 5e80fe0d4050..b3aa45ec87e3 100644 --- a/open-sse/services/modelFamilyFallback.ts +++ b/open-sse/services/modelFamilyFallback.ts @@ -78,8 +78,9 @@ const FAMILY_FALLBACK_TEMPLATES: Record = { "gemini-2.5-pro": ["gemini-2.5-pro-preview-06-05", "gemini-2.5-pro-exp-03-25"], "gemini-2.5-pro-preview-06-05": ["gemini-2.5-pro", "gemini-2.5-pro-exp-03-25"], - // Claude Mythos family (Fable 5) — flagship falls to the next-best Opus + // Claude Mythos family (Fable 5.1/5) — flagship falls to the next-best Opus // tiers before the cheaper Sonnet, matching the Opus family ordering. + "claude-fable-5-1": ["claude-fable-5", "claude-opus-5", "claude-sonnet-5"], "claude-fable-5": ["claude-opus-4-8", "claude-opus-4-7", "claude-sonnet-5"], // Claude Opus family diff --git a/open-sse/services/providerCostData.ts b/open-sse/services/providerCostData.ts index d9a4e0a8abe4..c655da778107 100644 --- a/open-sse/services/providerCostData.ts +++ b/open-sse/services/providerCostData.ts @@ -11,6 +11,7 @@ export interface ModelPricing { export const KNOWN_MODEL_PRICING: Record = { "gpt-4o": { inputCostPer1M: 2.5, outputCostPer1M: 10.0, isFree: false }, "gpt-4o-mini": { inputCostPer1M: 0.15, outputCostPer1M: 0.6, isFree: false }, + "claude-fable-5-1": { inputCostPer1M: 10.0, outputCostPer1M: 50.0, isFree: false }, "claude-fable-5": { inputCostPer1M: 15.0, outputCostPer1M: 75.0, isFree: false }, "claude-opus-5": { inputCostPer1M: 5.0, outputCostPer1M: 25.0, isFree: false }, "claude-opus-4-8": { inputCostPer1M: 15.0, outputCostPer1M: 75.0, isFree: false }, diff --git a/src/lib/providers/staticModels.ts b/src/lib/providers/staticModels.ts index 63eebdce28c1..5bb6598db5df 100644 --- a/src/lib/providers/staticModels.ts +++ b/src/lib/providers/staticModels.ts @@ -35,6 +35,7 @@ const STATIC_MODEL_PROVIDERS: Record Array<{ id: string; name: str ], antigravity: () => ANTIGRAVITY_PUBLIC_MODELS.map((model) => ({ ...model })), claude: () => [ + { id: "claude-fable-5-1", name: "Claude Fable 5.1" }, { id: "claude-fable-5", name: "Claude Fable 5" }, { id: "claude-opus-5", name: "Claude Opus 5" }, { id: "claude-opus-4-8", name: "Claude Opus 4.8" }, diff --git a/src/shared/constants/modelSpecs.ts b/src/shared/constants/modelSpecs.ts index fcc96c8e9bef..7c8c5b71395c 100644 --- a/src/shared/constants/modelSpecs.ts +++ b/src/shared/constants/modelSpecs.ts @@ -371,6 +371,22 @@ export const MODEL_SPECS: Record = { aliases: BEDROCK_CLAUDE_ALIASES("claude-opus-4-7", "claude-opus-4.7"), }, + // ── Claude Fable 5.1 ──────────────────────────────────────────── + // Same request surface as Claude Fable 5 (adaptive-only, thinking cannot be + // disabled); released 2026-09-01 with cache reads at a quarter of Fable 5's price. + "claude-fable-5-1": { + maxOutputTokens: 128000, + contextWindow: 1000000, + defaultThinkingBudget: 32000, + thinkingBudgetCap: 120000, + supportsThinking: true, + supportsTools: true, + supportsVision: true, + rejectsThinkingDisabled: true, + adaptiveThinkingOnly: true, + aliases: BEDROCK_CLAUDE_ALIASES("claude-fable-5-1"), + }, + // ── Claude Fable 5 ────────────────────────────────────────────── "claude-fable-5": { maxOutputTokens: 128000, diff --git a/src/shared/constants/pricing/frontier-labs.ts b/src/shared/constants/pricing/frontier-labs.ts index d20a18772042..b8a80d3bf195 100644 --- a/src/shared/constants/pricing/frontier-labs.ts +++ b/src/shared/constants/pricing/frontier-labs.ts @@ -8,6 +8,7 @@ import { GPT_5_6_LUNA_PRICING, GPT_5_6_SOL_PRICING, GPT_5_6_TERRA_PRICING, + CLAUDE_FABLE_5_1_PRICING, CLAUDE_FABLE_5_PRICING, CLAUDE_OPUS_5_PRICING, CLAUDE_OPUS_4_PRICING, @@ -213,6 +214,7 @@ export const DEFAULT_PRICING_FRONTIER = { // Common model IDs (without dates) used across providers // Intentional duplicates of dot-notation variants (e.g. claude-opus-4.6) // to cover hyphen-notation IDs (claude-opus-4-6) used by some clients + "claude-fable-5-1": CLAUDE_FABLE_5_1_PRICING, "claude-fable-5": CLAUDE_FABLE_5_PRICING, "claude-opus-5": CLAUDE_OPUS_5_PRICING, "claude-sonnet-5": CLAUDE_SONNET_5_PRICING, diff --git a/src/shared/constants/pricing/oauth-subscriptions.ts b/src/shared/constants/pricing/oauth-subscriptions.ts index 87f5aa320f3c..387ca036806e 100644 --- a/src/shared/constants/pricing/oauth-subscriptions.ts +++ b/src/shared/constants/pricing/oauth-subscriptions.ts @@ -20,6 +20,13 @@ const ANTIGRAVITY_GEMINI_3_7_PRICING = { export const DEFAULT_PRICING_OAUTH = { cc: { + "claude-fable-5-1": { + input: 10.0, + output: 50.0, + cached: 0.25, + reasoning: 50.0, + cache_creation: 12.5, + }, "claude-fable-5": { input: 10.0, output: 50.0, diff --git a/src/shared/constants/pricing/shared-tiers.ts b/src/shared/constants/pricing/shared-tiers.ts index 542e03521abf..5d3eb460e81a 100644 --- a/src/shared/constants/pricing/shared-tiers.ts +++ b/src/shared/constants/pricing/shared-tiers.ts @@ -52,6 +52,16 @@ export const GEMINI_3_7_FLASH_PROMO_PRICING = { cache_creation: 0.75, }; +// Claude Fable 5.1 (2026-09-01): same list price as Fable 5's official $10/$50 tier, +// with cache reads dropped to $0.25/MTok (a quarter of Fable 5's read price). +export const CLAUDE_FABLE_5_1_PRICING = { + input: 10.0, + output: 50.0, + cached: 0.25, + reasoning: 50.0, + cache_creation: 12.5, +}; + export const CLAUDE_FABLE_5_PRICING = { input: 15.0, output: 75.0, From 878e3eef810961b2ef308c88dd119571bed08742 Mon Sep 17 00:00:00 2001 From: davidebaraldo Date: Tue, 1 Sep 2026 23:43:13 +0200 Subject: [PATCH 2/5] docs(models): changelog fragment for #12366 --- changelog.d/features/12366-claude-fable-5-1.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/features/12366-claude-fable-5-1.md diff --git a/changelog.d/features/12366-claude-fable-5-1.md b/changelog.d/features/12366-claude-fable-5-1.md new file mode 100644 index 000000000000..b154db2d5d12 --- /dev/null +++ b/changelog.d/features/12366-claude-fable-5-1.md @@ -0,0 +1 @@ +Added Claude Fable 5.1 (`claude-fable-5-1`, released 2026-09-01) to the anthropic, claude, claude-web, and vertex registries with capability specs, Bedrock aliases, 1M-context beta eligibility, family fallback, and pricing — including its lower $0.25/MTok cache-read price. From 3e89781a9e461b014e7cc1b9e379167c86728fb4 Mon Sep 17 00:00:00 2001 From: davidebaraldo Date: Wed, 2 Sep 2026 21:14:23 +0200 Subject: [PATCH 3/5] fix(claude): bump spoofed Claude Code client version to 2.1.258 Anthropic gates newer models on the Claude Code client version it sees on the wire. With CLAUDE_CODE_CLIENT_VERSION pinned at 2.1.220, every OAuth request for claude-fable-5-1 is rejected with HTTP 200 + an error message: Claude Code 2.1.220 does not support this model; version 2.1.251 or newer is required. Run 'claude update' ... The rejection is independent of the caller's own User-Agent: this constant is the single source of truth every Claude executor, compatibility bridge, and identity preset derives its wire identity from, so a caller already running a recent CLI still gets refused. Bump to 2.1.258 (a released CLI version above Anthropic's 2.1.251 floor) so Fable 5.1 is reachable on Claude OAuth connections. Verified end to end against a live Claude Max subscription: the same request that returned the version error now returns model=claude-fable-5-1. Note for future model launches: this constant needs a bump whenever Anthropic raises the minimum client version for a new model. --- src/shared/constants/claudeCodeClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/constants/claudeCodeClient.ts b/src/shared/constants/claudeCodeClient.ts index 415068bc2d02..3f6806e6ac80 100644 --- a/src/shared/constants/claudeCodeClient.ts +++ b/src/shared/constants/claudeCodeClient.ts @@ -4,7 +4,7 @@ * Keep this leaf dependency-free so server executors, compatibility bridges, * and client-facing identity presets can share one source of truth. */ -export const CLAUDE_CODE_CLIENT_VERSION = "2.1.220"; +export const CLAUDE_CODE_CLIENT_VERSION = "2.1.258"; export const CLAUDE_CODE_CLIENT_BUILD_REVISION = "1f2"; export const CLAUDE_CODE_CLIENT_BILLING_VERSION = `${CLAUDE_CODE_CLIENT_VERSION}.${CLAUDE_CODE_CLIENT_BUILD_REVISION}`; export const CLAUDE_CODE_SDK_PACKAGE_VERSION = "0.94.0"; From 1a95483731325f09570fe9408ad0634b4b545747 Mon Sep 17 00:00:00 2001 From: davidebaraldo Date: Thu, 3 Sep 2026 07:43:42 -0300 Subject: [PATCH 4/5] test(models): align Fable 5.1 pins, registry, and changelog fragment Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --- changelog.d/features/12366-claude-fable-5-1.md | 3 ++- tests/snapshots/provider/translate-path.json | 12 ++++++------ tests/unit/anthropic-cache-fingerprint.test.ts | 2 +- tests/unit/cc-bridge-transforms.test.ts | 6 +++++- .../unit/claude-codex-identity-version-sync.test.ts | 6 +++--- tests/unit/claude-web-sonnet5-registry-6209.test.ts | 1 + tests/unit/client-identity-profiles.test.ts | 7 ++++--- tests/unit/executor-default-base.test.ts | 5 ++++- tests/unit/glm-executor.test.ts | 12 ++++++++---- 9 files changed, 34 insertions(+), 20 deletions(-) diff --git a/changelog.d/features/12366-claude-fable-5-1.md b/changelog.d/features/12366-claude-fable-5-1.md index b154db2d5d12..dea0888b430b 100644 --- a/changelog.d/features/12366-claude-fable-5-1.md +++ b/changelog.d/features/12366-claude-fable-5-1.md @@ -1 +1,2 @@ -Added Claude Fable 5.1 (`claude-fable-5-1`, released 2026-09-01) to the anthropic, claude, claude-web, and vertex registries with capability specs, Bedrock aliases, 1M-context beta eligibility, family fallback, and pricing — including its lower $0.25/MTok cache-read price. +- **feat(models):** add Claude Fable 5.1 (`claude-fable-5-1`) to the anthropic, claude, claude-web, and vertex registries with capability specs, Bedrock aliases, 1M-context beta eligibility, family fallback, and pricing — including its lower $0.25/MTok cache-read price ([#12366](https://github.com/diegosouzapw/OmniRoute/pull/12366)) — thanks @davidebaraldo +- **fix(claude):** bump spoofed Claude Code client version to 2.1.258 so Anthropic accepts Fable 5.1 on OAuth connections ([#12366](https://github.com/diegosouzapw/OmniRoute/pull/12366)) — thanks @davidebaraldo diff --git a/tests/snapshots/provider/translate-path.json b/tests/snapshots/provider/translate-path.json index d00eb4488a1b..01992de02d39 100644 --- a/tests/snapshots/provider/translate-path.json +++ b/tests/snapshots/provider/translate-path.json @@ -28,7 +28,7 @@ "apiKey": { "Accept": "text/event-stream", "Content-Type": "application/json", - "User-Agent": "claude-cli/2.1.220 (external, sdk-cli)", + "User-Agent": "claude-cli/2.1.258 (external, sdk-cli)", "X-Stainless-Arch": "", "X-Stainless-Lang": "js", "X-Stainless-OS": "MacOS", @@ -47,7 +47,7 @@ "nonStream": { "Accept": "application/json", "Content-Type": "application/json", - "User-Agent": "claude-cli/2.1.220 (external, sdk-cli)", + "User-Agent": "claude-cli/2.1.258 (external, sdk-cli)", "X-Stainless-Arch": "", "X-Stainless-Lang": "js", "X-Stainless-OS": "MacOS", @@ -66,7 +66,7 @@ "oauth": { "Accept": "text/event-stream", "Content-Type": "application/json", - "User-Agent": "claude-cli/2.1.220 (external, sdk-cli)", + "User-Agent": "claude-cli/2.1.258 (external, sdk-cli)", "X-Stainless-Arch": "", "X-Stainless-Lang": "js", "X-Stainless-OS": "MacOS", @@ -966,7 +966,7 @@ "Anthropic-Dangerous-Direct-Browser-Access": "true", "Anthropic-Version": "2023-06-01", "Content-Type": "application/json", - "User-Agent": "claude-cli/2.1.220 (external, cli)", + "User-Agent": "claude-cli/2.1.258 (external, cli)", "X-App": "cli", "X-Stainless-Arch": "", "X-Stainless-Helper-Method": "stream", @@ -984,7 +984,7 @@ "Anthropic-Dangerous-Direct-Browser-Access": "true", "Anthropic-Version": "2023-06-01", "Content-Type": "application/json", - "User-Agent": "claude-cli/2.1.220 (external, cli)", + "User-Agent": "claude-cli/2.1.258 (external, cli)", "X-App": "cli", "X-Stainless-Arch": "", "X-Stainless-Helper-Method": "stream", @@ -1003,7 +1003,7 @@ "Anthropic-Dangerous-Direct-Browser-Access": "true", "Anthropic-Version": "2023-06-01", "Content-Type": "application/json", - "User-Agent": "claude-cli/2.1.220 (external, cli)", + "User-Agent": "claude-cli/2.1.258 (external, cli)", "X-App": "cli", "X-Stainless-Arch": "", "X-Stainless-Helper-Method": "stream", diff --git a/tests/unit/anthropic-cache-fingerprint.test.ts b/tests/unit/anthropic-cache-fingerprint.test.ts index ebdb9690afac..770f26dd6541 100644 --- a/tests/unit/anthropic-cache-fingerprint.test.ts +++ b/tests/unit/anthropic-cache-fingerprint.test.ts @@ -5,6 +5,6 @@ import { CLAUDE_CODE_CLIENT_BILLING_VERSION } from "../../src/shared/constants/c describe("Anthropic billing header fingerprint (#1638)", () => { it("uses the immutable build revision captured from the signed CLI", () => { - assert.equal(CLAUDE_CODE_CLIENT_BILLING_VERSION, "2.1.220.1f2"); + assert.equal(CLAUDE_CODE_CLIENT_BILLING_VERSION, "2.1.258.1f2"); }); }); diff --git a/tests/unit/cc-bridge-transforms.test.ts b/tests/unit/cc-bridge-transforms.test.ts index ba915dcaca88..cb664edb970a 100644 --- a/tests/unit/cc-bridge-transforms.test.ts +++ b/tests/unit/cc-bridge-transforms.test.ts @@ -52,7 +52,11 @@ test("DEFAULT_CC_BRIDGE_PIPELINE places billing header at [0] and identity at [1 DEFAULT_CC_BRIDGE_PIPELINE ); const blocks = result.body.system as any[]; - assert.ok(blocks[0].text.startsWith("x-anthropic-billing-header: cc_version=2.1.220.1f2;")); + assert.ok( + blocks[0].text.startsWith( + `x-anthropic-billing-header: cc_version=${DEFAULT_CLAUDE_CODE_VERSION}.1f2;` + ) + ); assert.equal(blocks[1].text, CLAUDE_AGENT_SDK_IDENTITY); }); diff --git a/tests/unit/claude-codex-identity-version-sync.test.ts b/tests/unit/claude-codex-identity-version-sync.test.ts index d274bb9f34b0..8824fae6d041 100644 --- a/tests/unit/claude-codex-identity-version-sync.test.ts +++ b/tests/unit/claude-codex-identity-version-sync.test.ts @@ -38,10 +38,10 @@ test("Claude CLI version constants are in lockstep across all 4 sources", () => ); }); -test("Claude CLI wire versions match the captured 2.1.220 binary", () => { - assert.equal(canonical.CLAUDE_CODE_CLIENT_VERSION, "2.1.220"); +test("Claude CLI wire versions match the captured 2.1.258 binary", () => { + assert.equal(canonical.CLAUDE_CODE_CLIENT_VERSION, "2.1.258"); assert.equal(canonical.CLAUDE_CODE_CLIENT_BUILD_REVISION, "1f2"); - assert.equal(canonical.CLAUDE_CODE_CLIENT_BILLING_VERSION, "2.1.220.1f2"); + assert.equal(canonical.CLAUDE_CODE_CLIENT_BILLING_VERSION, "2.1.258.1f2"); assert.equal(canonical.CLAUDE_CODE_SDK_PACKAGE_VERSION, "0.94.0"); assert.equal(canonical.CLAUDE_CODE_RUNTIME_VERSION, "v26.3.0"); assert.equal( diff --git a/tests/unit/claude-web-sonnet5-registry-6209.test.ts b/tests/unit/claude-web-sonnet5-registry-6209.test.ts index 9930a04ae2df..a2f568fc9c7e 100644 --- a/tests/unit/claude-web-sonnet5-registry-6209.test.ts +++ b/tests/unit/claude-web-sonnet5-registry-6209.test.ts @@ -9,6 +9,7 @@ test("claude-web registry matches the current selectable model set", () => { assert.deepEqual( ids, [ + "claude-fable-5-1", "claude-fable-5", "claude-haiku-4-5-20251001", "claude-opus-5", diff --git a/tests/unit/client-identity-profiles.test.ts b/tests/unit/client-identity-profiles.test.ts index 54e00736a518..8bc852b76a52 100644 --- a/tests/unit/client-identity-profiles.test.ts +++ b/tests/unit/client-identity-profiles.test.ts @@ -17,6 +17,7 @@ const { getClientIdentityProfileHeaders, isClientIdentityProfileId, } = await import("../../src/shared/constants/clientIdentityProfiles.ts"); +const { getClaudeCodeUserAgent } = await import("../../src/shared/constants/claudeCodeClient.ts"); const { isForbiddenCustomHeaderName } = await import("../../src/shared/constants/upstreamHeaders.ts"); const { DefaultExecutor } = await import("../../open-sse/executors/default.ts"); @@ -39,7 +40,7 @@ test("getClientIdentityProfileHeaders: unknown profile id falls back to no heade test("getClientIdentityProfileHeaders: known CLI profiles expose their preset headers", () => { const claudeCli = getClientIdentityProfileHeaders("claude-cli"); - assert.equal(claudeCli["User-Agent"], "claude-cli/2.1.220 (external, cli)"); + assert.equal(claudeCli["User-Agent"], getClaudeCodeUserAgent("cli")); assert.equal(claudeCli["X-App"], "cli"); const codexCli = getClientIdentityProfileHeaders("codex-cli"); @@ -55,7 +56,7 @@ test("getClientIdentityProfileHeaders: returns a fresh mutable copy (catalog sta headers["User-Agent"] = "tampered"; assert.equal( CLIENT_IDENTITY_PROFILES["claude-cli"].headers["User-Agent"], - "claude-cli/2.1.220 (external, cli)" + getClaudeCodeUserAgent("cli") ); }); @@ -100,7 +101,7 @@ test("profile headers merged into customHeaders survive applyCustomHeaders sanit true ) as Record; - assert.equal(headers["User-Agent"], "claude-cli/2.1.220 (external, cli)"); + assert.equal(headers["User-Agent"], getClaudeCodeUserAgent("cli")); assert.equal(headers["X-App"], "cli"); assert.equal(headers["Authorization"], "Bearer test-key"); }); diff --git a/tests/unit/executor-default-base.test.ts b/tests/unit/executor-default-base.test.ts index 0e216780541c..a41d1c160297 100644 --- a/tests/unit/executor-default-base.test.ts +++ b/tests/unit/executor-default-base.test.ts @@ -19,6 +19,7 @@ import { CONTEXT_1M_BETA_HEADER, } from "../../open-sse/services/claudeCodeCompatible.ts"; import { runWithCapture } from "../../open-sse/utils/providerRequestLogging.ts"; +import { CLAUDE_CODE_CLIENT_BILLING_VERSION } from "../../src/shared/constants/claudeCodeClient.ts"; class TestExecutor extends BaseExecutor { constructor(config = {}) { @@ -1576,7 +1577,9 @@ test("DefaultExecutor.execute does not produce duplicate anthropic-version heade const sentBody = JSON.parse(capturedBody) as { system?: Array<{ text?: string }> }; assert.match( sentBody.system?.[0]?.text ?? "", - /^x-anthropic-billing-header: cc_version=2\.1\.220\.1f2; cc_entrypoint=cli; cch=[0-9a-f]{5};$/ + new RegExp( + `^x-anthropic-billing-header: cc_version=${CLAUDE_CODE_CLIENT_BILLING_VERSION.replaceAll(".", "\\.")}; cc_entrypoint=cli; cch=[0-9a-f]{5};$` + ) ); }); diff --git a/tests/unit/glm-executor.test.ts b/tests/unit/glm-executor.test.ts index 4cb31e90f69d..933564abd746 100644 --- a/tests/unit/glm-executor.test.ts +++ b/tests/unit/glm-executor.test.ts @@ -3,6 +3,7 @@ import assert from "node:assert/strict"; import { getExecutor } from "../../open-sse/executors/index.ts"; import { GlmExecutor } from "../../open-sse/executors/glm.ts"; +import { CLAUDE_CODE_CLIENT_VERSION } from "../../src/shared/constants/claudeCodeClient.ts"; function makeSseResponse(lines: string[]): Response { return new Response(lines.join("\n\n") + "\n\n", { @@ -130,9 +131,9 @@ test("GlmExecutor normalizes GLM coding and Anthropic URLs without duplicating e }); test("GlmExecutor separates OpenAI-compatible coding headers from Anthropic headers", async () => { - assert.equal(await getExecutor("glm") instanceof GlmExecutor, true); - assert.equal(await getExecutor("glm-cn") instanceof GlmExecutor, true); - assert.equal(await getExecutor("glmt") instanceof GlmExecutor, true); + assert.equal((await getExecutor("glm")) instanceof GlmExecutor, true); + assert.equal((await getExecutor("glm-cn")) instanceof GlmExecutor, true); + assert.equal((await getExecutor("glmt")) instanceof GlmExecutor, true); const executor = new GlmExecutor("glm"); const codingHeaders = executor.buildHeaders( @@ -183,7 +184,10 @@ test("GlmExecutor separates OpenAI-compatible coding headers from Anthropic head assert.equal(anthropicHeaders["anthropic-version"], "2023-06-01"); assert.match(anthropicHeaders["anthropic-beta"], /claude-code-20250219/); assert.equal(anthropicHeaders["anthropic-dangerous-direct-browser-access"], "true"); - assert.match(anthropicHeaders["User-Agent"], /^claude-cli\/2\.1\.220 \(external, sdk-cli\)$/); + assert.equal( + anthropicHeaders["User-Agent"], + `claude-cli/${CLAUDE_CODE_CLIENT_VERSION} (external, sdk-cli)` + ); assert.equal(anthropicHeaders["X-Stainless-Lang"], "js"); assert.equal(anthropicHeaders["X-Stainless-Runtime"], "node"); }); From 4a53a0b5d5aa6e1d0a8897da06b4b997c2494d24 Mon Sep 17 00:00:00 2001 From: davidebaraldo Date: Thu, 3 Sep 2026 09:37:51 -0300 Subject: [PATCH 5/5] test(models): expect Fable 5.1 first without growing frozen tests Claude flagship catalogs on anthropic/cc/cw now list claude-fable-5-1 first. Keep the executor billing-header pin on one line so executor-default-base.test.ts stays at the frozen 1632 LOC cap. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --- tests/unit/executor-default-base.test.ts | 5 +---- tests/unit/provider-models-config.test.ts | 13 +++++++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/unit/executor-default-base.test.ts b/tests/unit/executor-default-base.test.ts index a41d1c160297..ed889428e008 100644 --- a/tests/unit/executor-default-base.test.ts +++ b/tests/unit/executor-default-base.test.ts @@ -19,7 +19,6 @@ import { CONTEXT_1M_BETA_HEADER, } from "../../open-sse/services/claudeCodeCompatible.ts"; import { runWithCapture } from "../../open-sse/utils/providerRequestLogging.ts"; -import { CLAUDE_CODE_CLIENT_BILLING_VERSION } from "../../src/shared/constants/claudeCodeClient.ts"; class TestExecutor extends BaseExecutor { constructor(config = {}) { @@ -1577,9 +1576,7 @@ test("DefaultExecutor.execute does not produce duplicate anthropic-version heade const sentBody = JSON.parse(capturedBody) as { system?: Array<{ text?: string }> }; assert.match( sentBody.system?.[0]?.text ?? "", - new RegExp( - `^x-anthropic-billing-header: cc_version=${CLAUDE_CODE_CLIENT_BILLING_VERSION.replaceAll(".", "\\.")}; cc_entrypoint=cli; cch=[0-9a-f]{5};$` - ) + /^x-anthropic-billing-header: cc_version=2\.1\.258\.1f2; cc_entrypoint=cli; cch=[0-9a-f]{5};$/ ); }); diff --git a/tests/unit/provider-models-config.test.ts b/tests/unit/provider-models-config.test.ts index 793605cc4f4e..ae7142674844 100644 --- a/tests/unit/provider-models-config.test.ts +++ b/tests/unit/provider-models-config.test.ts @@ -142,10 +142,19 @@ test("GitHub Copilot registry reflects the current supported model lineup", () = }); test("Claude flagship catalogs keep Fable 5 first", () => { - for (const provider of ["anthropic", "cc", "cw", "gh", "ghe-copilot"]) { + // Anthropic-native catalogs list Fable 5.1 first (2026-09-01). Copilot's + // public model list has not shipped 5.1 yet, so gh/ghe still flagship Fable 5. + const expectedFirst = { + anthropic: "claude-fable-5-1", + cc: "claude-fable-5-1", + cw: "claude-fable-5-1", + gh: "claude-fable-5", + "ghe-copilot": "claude-fable-5", + }; + for (const [provider, expected] of Object.entries(expectedFirst)) { assert.equal( getProviderModels(provider)[0]?.id, - "claude-fable-5", + expected, `${provider} must list the strongest Claude model first` ); }