From 893196483d22957ec00f6e6f3e9fe56f50630d7f Mon Sep 17 00:00:00 2001
From: Peter Lee
Date: Sat, 18 Jul 2026 00:36:45 -0500
Subject: [PATCH] fix(anthropic): add Fable 5 to Claude CLI catalog (#101453)
* fix(anthropic): add claude-fable-5 to CLI allowlist, aliases, labels, and context window
CLI path was missing claude-fable-5 across all four metadata layers while
the direct Anthropic provider path in register.runtime.ts already had full
support via isAnthropicFable5Model() / resolveAnthropicFixedContextWindow().
- CLAUDE_CLI_DEFAULT_ALLOWLIST_REFS: add claude-cli/claude-fable-5
- CLAUDE_CLI_MODEL_ALIASES: add fable/fable-5/claude-fable-5 mappings
- CLAUDE_CLI_CONTEXT_WINDOWS: add claude-fable-5 -> 1_000_000 (was falling back to 200K)
- CLAUDE_CLI_MODEL_LABELS: add Claude Fable 5 (Claude CLI)
- resolveClaudeCliImageMediaInput: add fable-5 to 2576 max-side tier
Co-Authored-By: Claude Opus 4.8 (1M context)
* fix(anthropic): route fable alias through context/model-ref canonicalization paths
* fix(agent): include claude-cli in fable-5/mythos-5 fixed context window resolution
* fix(anthropic): add claude-fable-5 to static CLI manifest catalog
* test(anthropic): cover fable-5 alias canonicalization in bare and provider-qualified forms
* style(anthropic): fix indentation in plugin manifest
* fix(anthropic): publish Fable CLI output limit
---------
Co-authored-by: Claude Opus 4.8 (1M context)
Co-authored-by: Peter Steinberger
---
extensions/anthropic/claude-model-refs.ts | 1 +
extensions/anthropic/cli-catalog.ts | 10 +++++++-
extensions/anthropic/cli-constants.ts | 4 ++++
extensions/anthropic/cli-migration.test.ts | 27 ++++++++++++++++++----
extensions/anthropic/index.test.ts | 14 +++++++++++
extensions/anthropic/openclaw.plugin.json | 13 ++++++++++-
src/agents/cli-runner/prepare-claude.ts | 2 ++
src/agents/context-resolution.ts | 4 ++--
src/agents/context.test.ts | 4 ++++
9 files changed, 70 insertions(+), 9 deletions(-)
diff --git a/extensions/anthropic/claude-model-refs.ts b/extensions/anthropic/claude-model-refs.ts
index 44d21b80e592..f13d58c212d7 100644
--- a/extensions/anthropic/claude-model-refs.ts
+++ b/extensions/anthropic/claude-model-refs.ts
@@ -8,6 +8,7 @@ import { CLAUDE_CLI_BACKEND_ID, CLAUDE_CLI_MODEL_ALIASES } from "./cli-constants
const DEFAULT_CLAUDE_MODEL_BY_FAMILY: Record = {
opus: "claude-opus-4-8",
sonnet: "claude-sonnet-5",
+ fable: "claude-fable-5",
haiku: "claude-haiku-4-5",
};
diff --git a/extensions/anthropic/cli-catalog.ts b/extensions/anthropic/cli-catalog.ts
index 0a9a42934c06..d9ecb8aa87cf 100644
--- a/extensions/anthropic/cli-catalog.ts
+++ b/extensions/anthropic/cli-catalog.ts
@@ -9,6 +9,7 @@ import { CLAUDE_CLI_BACKEND_ID, CLAUDE_CLI_DEFAULT_ALLOWLIST_REFS } from "./cli-
const CLAUDE_CLI_DEFAULT_CONTEXT_WINDOW = 200_000;
const CLAUDE_CLI_CONTEXT_WINDOWS: Record = {
"claude-sonnet-5": 1_000_000,
+ "claude-fable-5": 1_000_000,
};
const CLAUDE_CLI_DEFAULT_MAX_OUTPUT_TOKENS = 64_000;
const CLAUDE_CLI_MAX_OUTPUT_TOKENS: Record = {
@@ -16,6 +17,7 @@ const CLAUDE_CLI_MAX_OUTPUT_TOKENS: Record = {
"claude-opus-4-7": 128_000,
"claude-opus-4-6": 128_000,
"claude-sonnet-5": 128_000,
+ "claude-fable-5": 128_000,
"claude-sonnet-4-6": 128_000,
};
@@ -24,12 +26,18 @@ const CLAUDE_CLI_MODEL_LABELS: Record = {
"claude-opus-4-7": "Claude Opus 4.7 (Claude CLI)",
"claude-opus-4-6": "Claude Opus 4.6 (Claude CLI)",
"claude-sonnet-5": "Claude Sonnet 5 (Claude CLI)",
+ "claude-fable-5": "Claude Fable 5 (Claude CLI)",
"claude-sonnet-4-6": "Claude Sonnet 4.6 (Claude CLI)",
};
function resolveClaudeCliImageMediaInput(id: string): ModelCatalogEntry["mediaInput"] {
const maxSidePx =
- id === "claude-opus-4-8" || id === "claude-opus-4-7" || id === "claude-sonnet-5" ? 2576 : 1568;
+ id === "claude-opus-4-8" ||
+ id === "claude-opus-4-7" ||
+ id === "claude-sonnet-5" ||
+ id === "claude-fable-5"
+ ? 2576
+ : 1568;
return {
image: {
maxSidePx,
diff --git a/extensions/anthropic/cli-constants.ts b/extensions/anthropic/cli-constants.ts
index 164a2f5515f6..55cdfd73d945 100644
--- a/extensions/anthropic/cli-constants.ts
+++ b/extensions/anthropic/cli-constants.ts
@@ -20,6 +20,7 @@ export const CLAUDE_CLI_CANONICAL_DEFAULT_MODEL_REF = `anthropic/${CLAUDE_CLI_CA
export const CLAUDE_CLI_DEFAULT_ALLOWLIST_REFS = [
CLAUDE_CLI_DEFAULT_MODEL_REF,
`${CLAUDE_CLI_BACKEND_ID}/claude-sonnet-5`,
+ `${CLAUDE_CLI_BACKEND_ID}/claude-fable-5`,
`${CLAUDE_CLI_BACKEND_ID}/claude-opus-4-7`,
`${CLAUDE_CLI_BACKEND_ID}/claude-sonnet-4-6`,
`${CLAUDE_CLI_BACKEND_ID}/claude-opus-4-6`,
@@ -39,6 +40,9 @@ export const CLAUDE_CLI_MODEL_ALIASES: Record = {
"claude-sonnet-5": "claude-sonnet-5",
"sonnet-4.6": "claude-sonnet-4-6",
"claude-sonnet-4-6": "claude-sonnet-4-6",
+ fable: "fable",
+ "fable-5": "claude-fable-5",
+ "claude-fable-5": "claude-fable-5",
haiku: "haiku",
};
diff --git a/extensions/anthropic/cli-migration.test.ts b/extensions/anthropic/cli-migration.test.ts
index b7ef548aaae9..12bcbf1fc5ea 100644
--- a/extensions/anthropic/cli-migration.test.ts
+++ b/extensions/anthropic/cli-migration.test.ts
@@ -61,6 +61,20 @@ describe("anthropic Claude model refs", () => {
);
});
+ it("canonicalizes fable family aliases in bare and provider-qualified forms", () => {
+ // "fable-5" must map to the full model id, not the family name: the
+ // canonicalizer only accepts alias values that already start with
+ // "claude-", so a family-name value would leave the shorthand unresolved.
+ expect(resolveKnownAnthropicModelRef("fable")).toBe("anthropic/claude-fable-5");
+ expect(resolveKnownAnthropicModelRef("fable-5")).toBe("anthropic/claude-fable-5");
+ expect(resolveKnownAnthropicModelRef("claude-fable-5")).toBe("anthropic/claude-fable-5");
+ expect(resolveKnownAnthropicModelRef("claude-cli/fable")).toBe("anthropic/claude-fable-5");
+ expect(resolveKnownAnthropicModelRef("claude-cli/fable-5")).toBe("anthropic/claude-fable-5");
+ expect(resolveKnownAnthropicModelRef("claude-cli/claude-fable-5")).toBe(
+ "anthropic/claude-fable-5",
+ );
+ });
+
it("preserves the current claude-haiku-4-5 model and its bare alias", () => {
// claude-haiku-4-5 is a current production model (not retired), so neither
// its full ref, its dotted variant, nor the bare "haiku" family alias must
@@ -166,14 +180,15 @@ describe("anthropic cli migration", () => {
alias: "Opus",
agentRuntime: { id: "claude-cli" },
},
- "anthropic/claude-opus-4-8": { agentRuntime: { id: "claude-cli" } },
- "anthropic/claude-sonnet-5": { agentRuntime: { id: "claude-cli" } },
- "anthropic/claude-sonnet-4-6": { agentRuntime: { id: "claude-cli" } },
"anthropic/claude-opus-4-6": {
alias: "Opus",
agentRuntime: { id: "claude-cli" },
},
"openai/gpt-5.2": {},
+ "anthropic/claude-opus-4-8": { agentRuntime: { id: "claude-cli" } },
+ "anthropic/claude-sonnet-5": { agentRuntime: { id: "claude-cli" } },
+ "anthropic/claude-fable-5": { agentRuntime: { id: "claude-cli" } },
+ "anthropic/claude-sonnet-4-6": { agentRuntime: { id: "claude-cli" } },
},
},
},
@@ -259,9 +274,10 @@ describe("anthropic cli migration", () => {
defaults: {
models: {
"openai/gpt-5.2": {},
+ "anthropic/claude-opus-4-7": { agentRuntime: { id: "claude-cli" } },
"anthropic/claude-opus-4-8": { agentRuntime: { id: "claude-cli" } },
"anthropic/claude-sonnet-5": { agentRuntime: { id: "claude-cli" } },
- "anthropic/claude-opus-4-7": { agentRuntime: { id: "claude-cli" } },
+ "anthropic/claude-fable-5": { agentRuntime: { id: "claude-cli" } },
"anthropic/claude-sonnet-4-6": { agentRuntime: { id: "claude-cli" } },
"anthropic/claude-opus-4-6": { agentRuntime: { id: "claude-cli" } },
},
@@ -304,9 +320,10 @@ describe("anthropic cli migration", () => {
defaults: {
model: { primary: "anthropic/claude-opus-4-7" },
models: {
+ "anthropic/claude-opus-4-7": { agentRuntime: { id: "claude-cli" } },
"anthropic/claude-opus-4-8": { agentRuntime: { id: "claude-cli" } },
"anthropic/claude-sonnet-5": { agentRuntime: { id: "claude-cli" } },
- "anthropic/claude-opus-4-7": { agentRuntime: { id: "claude-cli" } },
+ "anthropic/claude-fable-5": { agentRuntime: { id: "claude-cli" } },
"anthropic/claude-sonnet-4-6": { agentRuntime: { id: "claude-cli" } },
"anthropic/claude-opus-4-6": { agentRuntime: { id: "claude-cli" } },
},
diff --git a/extensions/anthropic/index.test.ts b/extensions/anthropic/index.test.ts
index 053fb98df87e..e07a70e918e6 100644
--- a/extensions/anthropic/index.test.ts
+++ b/extensions/anthropic/index.test.ts
@@ -134,6 +134,20 @@ describe("anthropic provider replay hooks", () => {
});
});
+ it("publishes Claude Fable 5 CLI metadata without downgrading its API contract", () => {
+ expect(
+ buildClaudeCliCatalogEntries().find((model) => model.id === "claude-fable-5"),
+ ).toMatchObject({
+ id: "claude-fable-5",
+ name: "Claude Fable 5 (Claude CLI)",
+ contextWindow: 1_000_000,
+ maxTokens: 128_000,
+ mediaInput: {
+ image: { maxSidePx: 2576, preferredSidePx: 2576, tokenMode: "provider" },
+ },
+ });
+ });
+
it("keeps bare Claude CLI context plan-safe while publishing output limits", () => {
const models = buildClaudeCliCatalogEntries();
for (const id of [
diff --git a/extensions/anthropic/openclaw.plugin.json b/extensions/anthropic/openclaw.plugin.json
index cfab8b9478cf..e674107f536f 100644
--- a/extensions/anthropic/openclaw.plugin.json
+++ b/extensions/anthropic/openclaw.plugin.json
@@ -14,7 +14,7 @@
"providerCatalogEntry": "./provider-discovery.ts",
"modelCatalog": {
"runtimeAugment": true,
- "providers": {
+ "providers": {
"claude-cli": {
"models": [
{
@@ -28,6 +28,17 @@
"contextWindow": 1000000,
"maxTokens": 128000
},
+ {
+ "id": "claude-fable-5",
+ "name": "Claude Fable 5 (Claude CLI)",
+ "reasoning": true,
+ "input": ["text", "image"],
+ "mediaInput": {
+ "image": { "maxSidePx": 2576, "preferredSidePx": 2576, "tokenMode": "provider" }
+ },
+ "contextWindow": 1000000,
+ "maxTokens": 128000
+ },
{
"id": "claude-opus-4-8",
"name": "Claude Opus 4.8 (Claude CLI)",
diff --git a/src/agents/cli-runner/prepare-claude.ts b/src/agents/cli-runner/prepare-claude.ts
index 2350e7e515b0..fa242583cb71 100644
--- a/src/agents/cli-runner/prepare-claude.ts
+++ b/src/agents/cli-runner/prepare-claude.ts
@@ -10,6 +10,8 @@ export const CLAUDE_CLI_CONTEXT_MODEL_ALIASES: Record = {
"sonnet-5": "claude-sonnet-5",
"sonnet-4.6": "claude-sonnet-4-6",
"sonnet-4-6": "claude-sonnet-4-6",
+ fable: "claude-fable-5",
+ "fable-5": "claude-fable-5",
};
export function resolveNodeClaudePlacement(params: {
diff --git a/src/agents/context-resolution.ts b/src/agents/context-resolution.ts
index 809fdf2907a2..0f7ca5b1ca49 100644
--- a/src/agents/context-resolution.ts
+++ b/src/agents/context-resolution.ts
@@ -197,13 +197,13 @@ export function resolveAnthropicFixedContextWindow(
return undefined;
}
if (
- (provider === "anthropic" || provider === "anthropic-vertex") &&
+ (provider === "anthropic" || provider === "anthropic-vertex" || provider === "claude-cli") &&
/^claude-fable-5(?=$|[^a-z0-9])/.test(modelId)
) {
return ANTHROPIC_FABLE_CONTEXT_TOKENS;
}
if (
- (provider === "anthropic" || provider === "anthropic-vertex") &&
+ (provider === "anthropic" || provider === "anthropic-vertex" || provider === "claude-cli") &&
/^claude-mythos-5(?=$|[^a-z0-9])/.test(modelId)
) {
return ANTHROPIC_MYTHOS_5_CONTEXT_TOKENS;
diff --git a/src/agents/context.test.ts b/src/agents/context.test.ts
index d645a9263c7b..e50c17dc25f5 100644
--- a/src/agents/context.test.ts
+++ b/src/agents/context.test.ts
@@ -558,8 +558,10 @@ describe("resolveContextTokensForModel", () => {
it.each([
["anthropic", "claude-fable-5", ANTHROPIC_FABLE_CONTEXT_TOKENS],
["anthropic-vertex", "claude-fable-5", ANTHROPIC_FABLE_CONTEXT_TOKENS],
+ ["claude-cli", "claude-fable-5", ANTHROPIC_FABLE_CONTEXT_TOKENS],
["anthropic", "claude-mythos-5", ANTHROPIC_MYTHOS_5_CONTEXT_TOKENS],
["anthropic-vertex", "claude-mythos-5", ANTHROPIC_MYTHOS_5_CONTEXT_TOKENS],
+ ["claude-cli", "claude-mythos-5", ANTHROPIC_MYTHOS_5_CONTEXT_TOKENS],
["anthropic", "claude-sonnet-5", ANTHROPIC_SONNET_5_CONTEXT_TOKENS],
["anthropic-vertex", "claude-sonnet-5", ANTHROPIC_SONNET_5_CONTEXT_TOKENS],
["claude-cli", "claude-sonnet-5", ANTHROPIC_SONNET_5_CONTEXT_TOKENS],
@@ -697,8 +699,10 @@ describe("resolveContextTokensForModel", () => {
it.each([
["anthropic", "claude-fable-5", ANTHROPIC_FABLE_CONTEXT_TOKENS],
["anthropic-vertex", "claude-fable-5", ANTHROPIC_FABLE_CONTEXT_TOKENS],
+ ["claude-cli", "claude-fable-5", ANTHROPIC_FABLE_CONTEXT_TOKENS],
["anthropic", "claude-mythos-5", ANTHROPIC_MYTHOS_5_CONTEXT_TOKENS],
["anthropic-vertex", "claude-mythos-5", ANTHROPIC_MYTHOS_5_CONTEXT_TOKENS],
+ ["claude-cli", "claude-mythos-5", ANTHROPIC_MYTHOS_5_CONTEXT_TOKENS],
["anthropic", "claude-sonnet-5", ANTHROPIC_SONNET_5_CONTEXT_TOKENS],
["anthropic-vertex", "claude-sonnet-5", ANTHROPIC_SONNET_5_CONTEXT_TOKENS],
["claude-cli", "claude-sonnet-5", ANTHROPIC_SONNET_5_CONTEXT_TOKENS],