diff --git a/docs/providers/anthropic.md b/docs/providers/anthropic.md
index 10fa965cd695..46cdbf8c841d 100644
--- a/docs/providers/anthropic.md
+++ b/docs/providers/anthropic.md
@@ -185,7 +185,7 @@ OpenClaw release:
-## Thinking defaults (Claude Sonnet 5, Fable 5, 4.8, and 4.6)
+## Thinking defaults (Claude Sonnet 5, Mythos 5, Fable 5, 4.8, and 4.6)
`anthropic/claude-sonnet-5` uses adaptive thinking at `high` effort by default.
Use `/think off` to disable thinking, or `/think xhigh|max` for the model's
@@ -201,6 +201,12 @@ effort. Anthropic does not allow thinking to be disabled for this model, so
omits custom temperature values for Fable 5 requests, since Anthropic rejects
a temperature override on any thinking-enabled request.
+`anthropic/claude-mythos-5` is a limited-access model with the same always-on
+adaptive-thinking contract. OpenClaw defaults to `high`, maps `/think off` and
+`/think minimal` to `low`, and omits caller-selected sampling parameters.
+The catalog publishes its 1,000,000-token context window, 128,000-token output
+limit, image input, and `$10/$50` input/output pricing.
+
Claude Opus 4.8 keeps thinking off by default in OpenClaw. When you explicitly
enable adaptive thinking with `/think high|xhigh|max`, OpenClaw sends
Anthropic's Opus 4.8 effort values; Claude 4.6 models (Opus 4.6 and Sonnet 4.6)
@@ -414,10 +420,11 @@ OpenClaw supports Anthropic's prompt caching feature for API-key auth.
- Claude Sonnet 5 has an exact 1,000,000-token input window and supports up to
- 128,000 output tokens. Anthropic's 1M context window is also GA on Claude 4.x
- models with adaptive thinking: Opus 4.8, Opus 4.7, Opus 4.6, and Sonnet 4.6.
- OpenClaw sizes these models automatically, no `params.context1m` needed:
+ Claude Sonnet 5, Mythos 5, and Fable 5 have an exact 1,000,000-token input
+ window and support up to 128,000 output tokens. Anthropic's 1M context
+ window is also GA on Claude 4.x models with adaptive thinking: Opus 4.8,
+ Opus 4.7, Opus 4.6, and Sonnet 4.6. OpenClaw sizes these models
+ automatically, no `params.context1m` needed:
```json5
{
@@ -425,6 +432,7 @@ OpenClaw supports Anthropic's prompt caching feature for API-key auth.
defaults: {
models: {
"anthropic/claude-sonnet-5": {},
+ "anthropic/claude-mythos-5": {},
"anthropic/claude-opus-4-6": {},
},
},
diff --git a/docs/providers/bedrock-mantle.md b/docs/providers/bedrock-mantle.md
index 80905110b38c..9a4d380777c0 100644
--- a/docs/providers/bedrock-mantle.md
+++ b/docs/providers/bedrock-mantle.md
@@ -3,7 +3,7 @@ summary: "Use Amazon Bedrock Mantle OpenAI-compatible and Claude Messages models
read_when:
- You want to use Bedrock Mantle hosted OSS models with OpenClaw
- You need the Mantle OpenAI-compatible endpoint for GPT-OSS, Qwen, Kimi, or GLM
- - You want to use Claude Sonnet 5 through Amazon Bedrock Mantle
+ - You want to use Claude Sonnet 5 or Mythos 5 through Amazon Bedrock Mantle
title: "Amazon Bedrock Mantle"
---
@@ -162,10 +162,11 @@ want to use.
- When automatic discovery owns the model list, OpenClaw appends three Claude
+ When automatic discovery owns the model list, OpenClaw appends four Claude
models after a successful lookup, regardless of what `/v1/models` returns:
`amazon-bedrock-mantle/anthropic.claude-sonnet-5` (Claude Sonnet 5),
`amazon-bedrock-mantle/anthropic.claude-opus-4-7` (Claude Opus 4.7), and
+ `amazon-bedrock-mantle/anthropic.claude-mythos-5` (Claude Mythos 5), plus
`amazon-bedrock-mantle/anthropic.claude-mythos-preview` (Claude Mythos
Preview). They use the `anthropic-messages` API surface and stream through
the same bearer-authenticated Anthropic-compatible endpoint
@@ -177,6 +178,11 @@ want to use.
route cannot disable thinking. OpenClaw also omits custom temperature for
Sonnet 5 requests.
+ Claude Mythos 5 is limited access. It publishes a 1,000,000-token context
+ window and 128,000-token output limit, always uses adaptive thinking, maps
+ `/think off` and `/think minimal` to `low`, and omits caller-selected
+ sampling parameters.
+
Claude Mythos Preview always requests reasoning, defaulting to `high`
effort when no `/think` level is set (mapped from `xhigh`/`max` down to
`high`, and `minimal` up to `low`). Opus 4.7 on Mantle streams without
diff --git a/docs/providers/bedrock.md b/docs/providers/bedrock.md
index 0de2600361e8..d3da2f9e4bf7 100644
--- a/docs/providers/bedrock.md
+++ b/docs/providers/bedrock.md
@@ -347,6 +347,20 @@ openclaw models list
+
+ Claude Mythos 5 is available through Bedrock only for accounts with the
+ required limited-access approval. OpenClaw recognizes the foundation model
+ `anthropic.claude-mythos-5` and regional or global inference profiles such
+ as `us.anthropic.claude-mythos-5`.
+
+ OpenClaw applies the 1,000,000-token context window, 128,000-token output
+ limit, image input, prompt caching, refusal-safe streaming, and native
+ effort levels. Adaptive thinking is always enabled: `/think off` and
+ `/think minimal` map to `low`, while `xhigh` and `max` remain available.
+ Custom sampling and forced tool choice values are omitted.
+
+
+
AWS documents Sonnet 5 for both the
[`bedrock-runtime` and `bedrock-mantle` endpoints](https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-anthropic-claude-sonnet-5.html).
diff --git a/extensions/amazon-bedrock-mantle/discovery.test.ts b/extensions/amazon-bedrock-mantle/discovery.test.ts
index 3e8175e49dd7..5efe210b838f 100644
--- a/extensions/amazon-bedrock-mantle/discovery.test.ts
+++ b/extensions/amazon-bedrock-mantle/discovery.test.ts
@@ -520,7 +520,7 @@ describe("bedrock mantle discovery", () => {
expect(provider?.api).toBe("openai-completions");
expect(provider?.auth).toBe("api-key");
expect(provider?.apiKey).toBe("env:AWS_BEARER_TOKEN_BEDROCK");
- expect(provider?.models).toHaveLength(4);
+ expect(provider?.models).toHaveLength(5);
const sonnet = provider?.models?.find((model) => model.id === "anthropic.claude-sonnet-5");
expect(sonnet).toMatchObject({
api: "anthropic-messages",
@@ -536,10 +536,20 @@ describe("bedrock mantle discovery", () => {
expect(opus?.api).toBe("anthropic-messages");
expect(opus?.reasoning).toBe(false);
expect(opus).not.toHaveProperty("baseUrl");
- const mythos = provider?.models?.find(
+ const mythos = provider?.models?.find((model) => model.id === "anthropic.claude-mythos-5");
+ expect(mythos).toMatchObject({
+ api: "anthropic-messages",
+ reasoning: true,
+ params: { canonicalModelId: "claude-mythos-5" },
+ cost: { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 },
+ contextWindow: 1_000_000,
+ maxTokens: 128_000,
+ thinkingLevelMap: { off: "low", minimal: "low", xhigh: "xhigh", max: "max" },
+ });
+ const mythosPreview = provider?.models?.find(
(model) => model.id === "anthropic.claude-mythos-preview",
);
- expect(mythos).toMatchObject({
+ expect(mythosPreview).toMatchObject({
api: "anthropic-messages",
reasoning: true,
params: { canonicalModelId: "claude-mythos-preview" },
diff --git a/extensions/amazon-bedrock-mantle/discovery.ts b/extensions/amazon-bedrock-mantle/discovery.ts
index da3841759e4f..4020c5fd66f6 100644
--- a/extensions/amazon-bedrock-mantle/discovery.ts
+++ b/extensions/amazon-bedrock-mantle/discovery.ts
@@ -463,6 +463,18 @@ export async function resolveImplicitMantleProvider(params: {
contextWindow: 1_000_000,
maxTokens: 128_000,
},
+ {
+ id: "anthropic.claude-mythos-5",
+ name: "Claude Mythos 5",
+ api: "anthropic-messages" as const,
+ reasoning: true,
+ params: { canonicalModelId: "claude-mythos-5" },
+ input: ["text", "image"],
+ cost: { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 },
+ contextWindow: 1_000_000,
+ maxTokens: 128_000,
+ thinkingLevelMap: { off: "low", minimal: "low", xhigh: "xhigh", max: "max" },
+ },
{
id: "anthropic.claude-mythos-preview",
name: "Claude Mythos Preview",
diff --git a/extensions/amazon-bedrock-mantle/mantle-anthropic.runtime.test.ts b/extensions/amazon-bedrock-mantle/mantle-anthropic.runtime.test.ts
index 485f78fce42f..3e69c1601cd2 100644
--- a/extensions/amazon-bedrock-mantle/mantle-anthropic.runtime.test.ts
+++ b/extensions/amazon-bedrock-mantle/mantle-anthropic.runtime.test.ts
@@ -213,6 +213,40 @@ describe("createMantleAnthropicStreamFn", () => {
expect(streamOptions.effort).toBe("low");
});
+ it.each([
+ { reasoning: undefined, effort: "high" },
+ { reasoning: "off" as const, effort: "low" },
+ { reasoning: "max" as const, effort: "max" },
+ ])("maps Mythos 5 $reasoning reasoning to adaptive $effort", ({ reasoning, effort }) => {
+ const model = createTestModel({
+ id: "anthropic.claude-mythos-5",
+ name: "Claude Mythos 5",
+ reasoning: true,
+ params: { canonicalModelId: "claude-mythos-5" },
+ thinkingLevelMap: { off: "low", minimal: "low", xhigh: "xhigh", max: "max" },
+ });
+ const deps = createTestDeps();
+ deps.stream.mockReturnValue({ kind: "anthropic-stream" } as never);
+
+ void createMantleAnthropicStreamFn(deps)(
+ model,
+ { messages: [] },
+ {
+ apiKey: "bedrock-bearer-token",
+ maxTokens: 1_000,
+ temperature: 0.2,
+ ...(reasoning ? { reasoning } : {}),
+ },
+ );
+
+ const streamOptions = firstStreamOptions(deps);
+ expect(streamOptions.thinkingEnabled).toBe(true);
+ expect(streamOptions.effort).toBe(effort);
+ expect(streamOptions.maxTokens).toBe(1_000);
+ expect(streamOptions).not.toHaveProperty("thinkingBudgetTokens");
+ expect(streamOptions.temperature).toBeUndefined();
+ });
+
it("normalizes Mantle provider URLs to the Anthropic endpoint", () => {
expect(resolveMantleAnthropicBaseUrl("https://bedrock-mantle.us-east-1.api.aws/v1")).toBe(
"https://bedrock-mantle.us-east-1.api.aws/anthropic",
diff --git a/extensions/amazon-bedrock-mantle/mantle-anthropic.runtime.ts b/extensions/amazon-bedrock-mantle/mantle-anthropic.runtime.ts
index 3486f0451e18..54bbfec6f001 100644
--- a/extensions/amazon-bedrock-mantle/mantle-anthropic.runtime.ts
+++ b/extensions/amazon-bedrock-mantle/mantle-anthropic.runtime.ts
@@ -10,7 +10,11 @@ import {
type SimpleStreamOptions,
type ThinkingLevel,
} from "openclaw/plugin-sdk/llm";
-import { resolveClaudeSonnet5ModelIdentity } from "openclaw/plugin-sdk/provider-model-shared";
+import {
+ requiresClaudeDefaultSampling,
+ resolveClaudeMythos5ModelIdentity,
+ resolveClaudeSonnet5ModelIdentity,
+} from "openclaw/plugin-sdk/provider-model-shared";
const MANTLE_ANTHROPIC_BETA = "fine-grained-tool-streaming-2025-05-14";
type AnthropicOptions = ConstructorParameters[0];
@@ -33,7 +37,7 @@ function isClaudeSonnet5Model(model: Model): boolean {
}
function requiresDefaultSampling(model: Model): boolean {
- return model.id.includes("claude-opus-4-7") || isClaudeSonnet5Model(model);
+ return requiresClaudeDefaultSampling(model);
}
function isClaudeMythosPreviewModel(model: Model): boolean {
@@ -49,6 +53,14 @@ function isClaudeMythosPreviewModel(model: Model): boolean {
);
}
+function isClaudeMythos5Model(model: Model): boolean {
+ return resolveClaudeMythos5ModelIdentity(model) !== undefined;
+}
+
+function requiresClaudeMythosAdaptiveThinking(model: Model): boolean {
+ return isClaudeMythos5Model(model) || isClaudeMythosPreviewModel(model);
+}
+
function resolveMantleReasoning(
model: Model,
options: SimpleStreamOptions | undefined,
@@ -57,21 +69,19 @@ function resolveMantleReasoning(
return undefined;
}
const sonnet5 = isClaudeSonnet5Model(model);
- const reasoning =
- options?.reasoning ?? (isClaudeMythosPreviewModel(model) || sonnet5 ? "high" : undefined);
+ const mythosPreview = isClaudeMythosPreviewModel(model);
+ const mandatoryMythos = isClaudeMythos5Model(model) || mythosPreview;
+ const reasoning = options?.reasoning ?? (mandatoryMythos || sonnet5 ? "high" : undefined);
if (sonnet5) {
return reasoning === "off" || reasoning === "minimal" ? "low" : reasoning;
}
- if (!isClaudeMythosPreviewModel(model)) {
+ if (!mandatoryMythos) {
return reasoning;
}
- if (reasoning === "off") {
- return "high";
- }
- if (reasoning === "minimal") {
+ if (reasoning === "off" || reasoning === "minimal") {
return "low";
}
- return reasoning === "xhigh" || reasoning === "max" ? "high" : reasoning;
+ return mythosPreview && (reasoning === "xhigh" || reasoning === "max") ? "high" : reasoning;
}
function mapSonnet5Effort(
@@ -107,7 +117,9 @@ function buildMantleAnthropicBaseOptions(
...(requiresDefaultSampling(model) ? {} : { temperature: options?.temperature }),
maxTokens:
options?.maxTokens ||
- (isClaudeSonnet5Model(model) ? model.maxTokens : Math.min(model.maxTokens, 32_000)),
+ (isClaudeSonnet5Model(model) || isClaudeMythos5Model(model)
+ ? model.maxTokens
+ : Math.min(model.maxTokens, 32_000)),
signal: options?.signal,
apiKey,
cacheRetention: options?.cacheRetention,
@@ -172,6 +184,7 @@ export function createMantleAnthropicStreamFn(deps?: {
const streamClient = client as unknown as Anthropic;
const reasoning = resolveMantleReasoning(model, options);
const sonnet5 = isClaudeSonnet5Model(model);
+ const mythos5 = isClaudeMythos5Model(model);
if (!reasoning || reasoning === "off") {
return streamFn(model as Model<"anthropic-messages">, context, {
...base,
@@ -180,12 +193,12 @@ export function createMantleAnthropicStreamFn(deps?: {
});
}
- if (sonnet5) {
+ if (sonnet5 || mythos5) {
return streamFn(model as Model<"anthropic-messages">, context, {
...base,
client: streamClient,
thinkingEnabled: true,
- effort: mapSonnet5Effort(reasoning),
+ effort: sonnet5 ? mapSonnet5Effort(reasoning) : reasoning,
});
}
@@ -200,7 +213,7 @@ export function createMantleAnthropicStreamFn(deps?: {
client: streamClient,
maxTokens: adjusted.maxTokens,
thinkingEnabled: true,
- ...(isClaudeMythosPreviewModel(model) ? { effort: reasoning } : {}),
+ ...(requiresClaudeMythosAdaptiveThinking(model) ? { effort: reasoning } : {}),
thinkingBudgetTokens: adjusted.thinkingBudget,
});
};
diff --git a/extensions/amazon-bedrock/bedrock-options.ts b/extensions/amazon-bedrock/bedrock-options.ts
index e0e6b37b8ce6..ab29f48977a7 100644
--- a/extensions/amazon-bedrock/bedrock-options.ts
+++ b/extensions/amazon-bedrock/bedrock-options.ts
@@ -41,7 +41,14 @@ export function supportsBedrockPromptCaching(modelId: string, modelName?: string
if (candidates.some((s) => s.includes("-4-"))) {
return true;
}
- if (candidates.some((s) => s.includes("claude-fable-5") || s.includes("claude-sonnet-5"))) {
+ if (
+ candidates.some(
+ (candidate) =>
+ candidate.includes("claude-fable-5") ||
+ candidate.includes("claude-mythos-5") ||
+ candidate.includes("claude-sonnet-5"),
+ )
+ ) {
return true;
}
if (candidates.some((s) => s.includes("claude-3-7-sonnet"))) {
diff --git a/extensions/amazon-bedrock/discovery.test.ts b/extensions/amazon-bedrock/discovery.test.ts
index 098e75f9ac3f..c4591a55a4c3 100644
--- a/extensions/amazon-bedrock/discovery.test.ts
+++ b/extensions/amazon-bedrock/discovery.test.ts
@@ -166,37 +166,54 @@ describe("bedrock discovery", () => {
});
});
- it("marks known Fable inference profile fallbacks as reasoning capable", async () => {
- sendMock
- .mockResolvedValueOnce({
- modelSummaries: [],
- })
- .mockResolvedValueOnce({
- inferenceProfileSummaries: [
- {
- inferenceProfileId: "us.anthropic.claude-fable-5",
- inferenceProfileName: "US Claude Fable 5",
- status: "ACTIVE",
- type: "SYSTEM_DEFINED",
- models: [
- {
- modelArn: "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-fable-5",
- },
- ],
- },
- ],
+ it.each([
+ {
+ label: "Fable",
+ profileId: "us.anthropic.claude-fable-5",
+ profileName: "US Claude Fable 5",
+ foundationId: "anthropic.claude-fable-5",
+ },
+ {
+ label: "Mythos",
+ profileId: "us.anthropic.claude-mythos-5",
+ profileName: "US Claude Mythos 5",
+ foundationId: "anthropic.claude-mythos-5",
+ },
+ ])(
+ "marks known $label inference profile fallbacks as reasoning capable",
+ async ({ profileId, profileName, foundationId }) => {
+ sendMock
+ .mockResolvedValueOnce({
+ modelSummaries: [],
+ })
+ .mockResolvedValueOnce({
+ inferenceProfileSummaries: [
+ {
+ inferenceProfileId: profileId,
+ inferenceProfileName: profileName,
+ status: "ACTIVE",
+ type: "SYSTEM_DEFINED",
+ models: [
+ {
+ modelArn: `arn:aws:bedrock:us-east-1::foundation-model/${foundationId}`,
+ },
+ ],
+ },
+ ],
+ });
+
+ const models = await discoverBedrockModels({ region: "us-east-1", clientFactory });
+
+ expect(models).toHaveLength(1);
+ expectModelFields(models[0], {
+ id: profileId,
+ reasoning: true,
+ contextWindow: 1_000_000,
+ maxTokens: 128_000,
+ thinkingLevelMap: { off: "low", minimal: "low", xhigh: "xhigh", max: "max" },
});
-
- const models = await discoverBedrockModels({ region: "us-east-1", clientFactory });
-
- expect(models).toHaveLength(1);
- expectModelFields(models[0], {
- id: "us.anthropic.claude-fable-5",
- reasoning: true,
- contextWindow: 1_000_000,
- thinkingLevelMap: { off: "low", minimal: "low", xhigh: "xhigh", max: "max" },
- });
- });
+ },
+ );
it("applies the Sonnet 5 contract to inference-profile-only discovery", async () => {
sendMock.mockResolvedValueOnce({ modelSummaries: [] }).mockResolvedValueOnce({
diff --git a/extensions/amazon-bedrock/discovery.ts b/extensions/amazon-bedrock/discovery.ts
index ff4c9ae80d72..8185fbfc283b 100644
--- a/extensions/amazon-bedrock/discovery.ts
+++ b/extensions/amazon-bedrock/discovery.ts
@@ -21,6 +21,7 @@ import type {
import {
resolveClaudeFable5ModelIdentity,
resolveClaudeModelIdentity,
+ resolveClaudeMythos5ModelIdentity,
resolveClaudeSonnet5ModelIdentity,
supportsClaudeAdaptiveThinking,
} from "openclaw/plugin-sdk/provider-model-shared";
@@ -61,6 +62,7 @@ const DEFAULT_MAX_TOKENS = 4096;
const KNOWN_CONTEXT_WINDOWS: Record = {
// Anthropic Claude
"anthropic.claude-fable-5": 1_000_000,
+ "anthropic.claude-mythos-5": 1_000_000,
// AWS publishes Sonnet 5 on both bedrock-runtime (Invoke/Converse) and Mantle.
"anthropic.claude-sonnet-5": 1_000_000,
"anthropic.claude-3-7-sonnet-20250219-v1:0": 200_000,
@@ -142,6 +144,7 @@ function resolveKnownContextWindow(modelId: string): number | undefined {
for (const candidate of candidates) {
if (
resolveClaudeFable5ModelIdentity({ id: candidate }) ||
+ resolveClaudeMythos5ModelIdentity({ id: candidate }) ||
resolveClaudeSonnet5ModelIdentity({ id: candidate })
) {
return 1_000_000;
@@ -178,6 +181,7 @@ function resolveKnownThinkingLevelMap(
function resolveKnownMaxTokens(modelId: string): number | undefined {
return resolveClaudeFable5ModelIdentity({ id: modelId }) ||
+ resolveClaudeMythos5ModelIdentity({ id: modelId }) ||
resolveClaudeSonnet5ModelIdentity({ id: modelId })
? 128_000
: undefined;
@@ -185,6 +189,7 @@ function resolveKnownMaxTokens(modelId: string): number | undefined {
function resolveKnownInput(modelId: string): ModelDefinitionConfig["input"] | undefined {
return resolveClaudeFable5ModelIdentity({ id: modelId }) ||
+ resolveClaudeMythos5ModelIdentity({ id: modelId }) ||
resolveClaudeSonnet5ModelIdentity({ id: modelId })
? ["text", "image"]
: undefined;
diff --git a/extensions/amazon-bedrock/index.test.ts b/extensions/amazon-bedrock/index.test.ts
index 568d45f5021b..f6e5bca57ff8 100644
--- a/extensions/amazon-bedrock/index.test.ts
+++ b/extensions/amazon-bedrock/index.test.ts
@@ -377,13 +377,16 @@ describe("amazon-bedrock provider plugin", () => {
}
});
- it("keeps Claude Fable 5 always adaptive with high default effort", async () => {
+ it("keeps mandatory-adaptive Claude 5 models at high default effort", async () => {
const provider = await registerSingleProviderPlugin(amazonBedrockPlugin);
for (const modelId of [
"anthropic.claude-fable-5",
"us.anthropic.claude-fable-5",
"global.anthropic.claude-fable-5",
+ "anthropic.claude-mythos-5",
+ "us.anthropic.claude-mythos-5",
+ "global.anthropic.claude-mythos-5",
]) {
expectThinkingProfile(
provider.resolveThinkingProfile?.({
@@ -414,8 +417,9 @@ describe("amazon-bedrock provider plugin", () => {
);
});
- it("recognizes direct Fable model refs as prompt-cache eligible", () => {
+ it("recognizes direct Claude 5 model refs as prompt-cache eligible", () => {
expect(supportsBedrockPromptCaching("us.anthropic.claude-fable-5")).toBe(true);
+ expect(supportsBedrockPromptCaching("us.anthropic.claude-mythos-5")).toBe(true);
expect(supportsBedrockPromptCaching("global.anthropic.claude-sonnet-5")).toBe(true);
});
diff --git a/extensions/amazon-bedrock/provider-policy-api.test.ts b/extensions/amazon-bedrock/provider-policy-api.test.ts
index 11a984f63990..864418f186c2 100644
--- a/extensions/amazon-bedrock/provider-policy-api.test.ts
+++ b/extensions/amazon-bedrock/provider-policy-api.test.ts
@@ -73,6 +73,11 @@ describe("amazon-bedrock provider-policy-api", () => {
defaultLevel: "high",
preservesCatalogOptOut: true,
},
+ {
+ canonicalModelId: "claude-mythos-5",
+ defaultLevel: "high",
+ preservesCatalogOptOut: true,
+ },
{
canonicalModelId: "claude-opus-4-8",
defaultLevel: "off",
diff --git a/extensions/amazon-bedrock/register.sync.runtime.ts b/extensions/amazon-bedrock/register.sync.runtime.ts
index 254eabc4a918..176c42d816ca 100644
--- a/extensions/amazon-bedrock/register.sync.runtime.ts
+++ b/extensions/amazon-bedrock/register.sync.runtime.ts
@@ -15,6 +15,7 @@ import {
normalizeProviderId,
resolveClaudeFable5ModelIdentity,
resolveClaudeModelIdentity,
+ resolveClaudeMythos5ModelIdentity,
resolveClaudeSonnet5ModelIdentity,
} from "openclaw/plugin-sdk/provider-model-shared";
import { streamWithPayloadPatch } from "openclaw/plugin-sdk/provider-stream-shared";
@@ -57,7 +58,8 @@ function normalizeBedrockResolvedModel({ modelId, model }: ProviderNormalizeReso
}
const reasoning =
model.reasoning ||
- resolveClaudeFable5ModelIdentity({ id: modelId, params: model.params }) !== undefined;
+ resolveClaudeFable5ModelIdentity({ id: modelId, params: model.params }) !== undefined ||
+ resolveClaudeMythos5ModelIdentity({ id: modelId, params: model.params }) !== undefined;
const current = model.thinkingLevelMap;
const currentEfforts = current as Record | undefined;
if (
diff --git a/extensions/amazon-bedrock/stream.runtime.test.ts b/extensions/amazon-bedrock/stream.runtime.test.ts
index 9676a5dfda51..10a2a28bfba2 100644
--- a/extensions/amazon-bedrock/stream.runtime.test.ts
+++ b/extensions/amazon-bedrock/stream.runtime.test.ts
@@ -254,23 +254,47 @@ describe("Bedrock thinking effort mapping", () => {
expect(options.maxTokens).toBe(32_000);
});
- it("forces adaptive thinking for Bedrock Mythos Preview when callers omit reasoning", () => {
- const model = bedrockModel({
- id: "us.anthropic.claude-mythos-preview",
- name: "US Claude Mythos Preview",
- reasoning: true,
- contextWindow: 1_000_000,
- maxTokens: 128_000,
- });
- const options = testing.resolveSimpleBedrockOptions(model, {});
+ it.each(["claude-mythos-preview", "claude-mythos-5"])(
+ "forces adaptive thinking for Bedrock %s when callers omit reasoning",
+ (modelId) => {
+ const model = bedrockModel({
+ id: `us.anthropic.${modelId}`,
+ name: modelId,
+ reasoning: true,
+ contextWindow: 1_000_000,
+ maxTokens: 128_000,
+ });
+ const options = testing.resolveSimpleBedrockOptions(model, {});
- expect(options.reasoning).toBe("high");
- expect(options.maxTokens).toBe(128_000);
- expect(testing.buildAdditionalModelRequestFields(model, options)).toEqual({
- thinking: { type: "adaptive", display: "summarized" },
- output_config: { effort: "high" },
- });
- });
+ expect(options.reasoning).toBe("high");
+ expect(options.maxTokens).toBe(128_000);
+ expect(testing.buildAdditionalModelRequestFields(model, options)).toEqual({
+ thinking: { type: "adaptive", display: "summarized" },
+ output_config: { effort: "high" },
+ });
+ },
+ );
+
+ it.each(["claude-mythos-preview", "claude-mythos-5"])(
+ "maps explicit off to low effort for Bedrock %s",
+ (modelId) => {
+ const model = bedrockModel({
+ id: `us.anthropic.${modelId}`,
+ name: modelId,
+ reasoning: true,
+ contextWindow: 1_000_000,
+ maxTokens: 128_000,
+ });
+ const options = testing.resolveSimpleBedrockOptions(model, { reasoning: "off" });
+
+ expect(options.reasoning).toBe("low");
+ expect(options.maxTokens).toBe(128_000);
+ expect(testing.buildAdditionalModelRequestFields(model, options)).toEqual({
+ thinking: { type: "adaptive", display: "summarized" },
+ output_config: { effort: "low" },
+ });
+ },
+ );
it("clamps max effort for Claude models without native max support", () => {
expect(
@@ -308,6 +332,18 @@ describe("Bedrock thinking effort mapping", () => {
).toBe("max");
});
+ it("preserves max effort for Claude Mythos 5", () => {
+ expect(
+ testing.mapThinkingLevelToEffort(
+ bedrockModel({
+ id: "anthropic.claude-mythos-5",
+ name: "Claude Mythos 5",
+ }),
+ "max",
+ ),
+ ).toBe("max");
+ });
+
it("uses canonical Claude policy for deployment aliases", () => {
expect(
testing.mapThinkingLevelToEffort(
@@ -422,7 +458,20 @@ describe("Bedrock Fable contract", () => {
expect(command.input?.toolConfig).toBeUndefined();
});
- it("quarantines partial output when Fable returns a terminal refusal", async () => {
+ it.each([
+ ["Fable", () => fableModel()],
+ [
+ "Mythos 5",
+ () =>
+ bedrockModel({
+ id: "production-mythos",
+ name: "Production deployment",
+ params: { canonicalModelId: "claude-mythos-5" },
+ contextWindow: 1_000_000,
+ maxTokens: 128_000,
+ }),
+ ],
+ ])("quarantines partial output when %s returns a terminal refusal", async (_name, model) => {
vi.spyOn(BedrockRuntimeClient.prototype, "send").mockResolvedValue({
$metadata: { httpStatusCode: 200 },
stream: streamEvents([
@@ -446,7 +495,7 @@ describe("Bedrock Fable contract", () => {
]),
} as never);
- const stream = streamSimpleBedrock(fableModel(), context());
+ const stream = streamSimpleBedrock(model(), context());
const eventTypes: string[] = [];
for await (const event of stream) {
eventTypes.push(event.type);
diff --git a/extensions/amazon-bedrock/stream.runtime.ts b/extensions/amazon-bedrock/stream.runtime.ts
index 4a6f93272bce..52e07d3cf993 100644
--- a/extensions/amazon-bedrock/stream.runtime.ts
+++ b/extensions/amazon-bedrock/stream.runtime.ts
@@ -55,7 +55,9 @@ import {
import {
resolveClaudeFable5ModelIdentity,
resolveClaudeModelIdentity,
+ resolveClaudeMythos5ModelIdentity,
resolveClaudeSonnet5ModelIdentity,
+ requiresClaudeMandatoryAdaptiveThinking,
supportsClaudeAdaptiveThinking,
supportsClaudeNativeXhighEffort,
} from "openclaw/plugin-sdk/provider-model-shared";
@@ -81,7 +83,11 @@ function usesClaudeSonnet5BedrockContract(model: Model<"bedrock-converse-stream"
function usesClaudeStreamingRefusalBedrockContract(
model: Model<"bedrock-converse-stream">,
): boolean {
- return usesClaudeFable5BedrockContract(model) || usesClaudeSonnet5BedrockContract(model);
+ return (
+ usesClaudeFable5BedrockContract(model) ||
+ resolveClaudeMythos5ModelIdentity(model) !== undefined ||
+ usesClaudeSonnet5BedrockContract(model)
+ );
}
function readBedrockStopDetails(fields: DocumentType | undefined): unknown {
@@ -387,7 +393,7 @@ function resolveSimpleBedrockOptions(
options?: SimpleStreamOptions,
): BedrockOptions {
const base = buildBaseOptions(model, options, undefined);
- if (usesClaudeFable5BedrockContract(model) || usesClaudeSonnet5BedrockContract(model)) {
+ if (requiresMandatoryAdaptiveThinking(model)) {
return {
...base,
maxTokens: resolveAdaptiveBedrockMaxTokens(model, base.maxTokens),
@@ -600,7 +606,7 @@ function resolveClaudeProfileNameModelId(modelName?: string): string | undefined
if (!normalized.includes("claude")) {
return undefined;
}
- const family = /(?:fable-5|mythos-preview|opus-4-(?:6|7|8)|sonnet-(?:5|4-6))(?:$|-)/.exec(
+ const family = /(?:fable-5|mythos-(?:5|preview)|opus-4-(?:6|7|8)|sonnet-(?:5|4-6))(?:$|-)/.exec(
normalized,
)?.[0];
return family ? `claude-${family.replace(/-$/, "")}` : undefined;
@@ -631,6 +637,8 @@ function supportsAdaptiveThinking(model: Model<"bedrock-converse-stream">): bool
function requiresMandatoryAdaptiveThinking(model: Model<"bedrock-converse-stream">): boolean {
const profileModelId = resolveClaudeProfileNameModelId(model.name);
return (
+ requiresClaudeMandatoryAdaptiveThinking(model) ||
+ requiresClaudeMandatoryAdaptiveThinking({ id: profileModelId }) ||
isClaudeMythosPreviewModelId(resolveClaudeModelIdentity(model)) ||
isClaudeMythosPreviewModelId(profileModelId) ||
usesClaudeSonnet5BedrockContract(model) ||
@@ -1060,17 +1068,14 @@ function buildAdditionalModelRequestFields(
model: Model<"bedrock-converse-stream">,
options: BedrockOptions,
): DocumentType | undefined {
- // Bedrock keeps Fable/Sonnet 5 adaptive. Preserve the public `off` control by
+ // Mandatory-adaptive Claude routes preserve the public `off` control by
// lowering effort instead of silently falling back to the route's high default.
- const mandatoryAdaptiveThinking =
- usesClaudeFable5BedrockContract(model) || requiresMandatoryAdaptiveThinking(model);
+ const mandatoryAdaptiveThinking = requiresMandatoryAdaptiveThinking(model);
const reasoning =
options.reasoning === "off"
- ? usesClaudeFable5BedrockContract(model) || usesClaudeSonnet5BedrockContract(model)
+ ? mandatoryAdaptiveThinking
? "low"
- : mandatoryAdaptiveThinking
- ? "high"
- : "off"
+ : "off"
: (options.reasoning ?? (mandatoryAdaptiveThinking ? "high" : undefined));
if (reasoning === "off") {
return undefined;
diff --git a/extensions/amazon-bedrock/thinking-policy.ts b/extensions/amazon-bedrock/thinking-policy.ts
index cd8c7ec4bfd8..e8e861b47383 100644
--- a/extensions/amazon-bedrock/thinking-policy.ts
+++ b/extensions/amazon-bedrock/thinking-policy.ts
@@ -9,6 +9,7 @@ import type {
import {
resolveClaudeFable5ModelIdentity,
resolveClaudeModelIdentity,
+ resolveClaudeMythos5ModelIdentity,
resolveClaudeSonnet5ModelIdentity,
} from "openclaw/plugin-sdk/provider-model-shared";
@@ -59,6 +60,7 @@ export function isLatestAdaptiveBedrockModelRef(
const canonicalModelId = resolveClaudeModelIdentity(modelRef);
return (
resolveClaudeFable5ModelIdentity(modelRef) !== undefined ||
+ resolveClaudeMythos5ModelIdentity(modelRef) !== undefined ||
resolveClaudeSonnet5ModelIdentity(modelRef) !== undefined ||
[modelId, canonicalModelId].some(
(candidate) =>
@@ -74,6 +76,7 @@ export function supportsBedrockNativeMaxEffort(
): boolean {
if (
resolveClaudeFable5ModelIdentity({ id: modelId, params }) ||
+ resolveClaudeMythos5ModelIdentity({ id: modelId, params }) ||
resolveClaudeSonnet5ModelIdentity({ id: modelId, params })
) {
return true;
@@ -90,7 +93,7 @@ export function resolveBedrockNativeThinkingLevelMap(
params?: Record,
): ProviderRuntimeModel["thinkingLevelMap"] | undefined {
const modelRef = { id: modelId, params };
- if (resolveClaudeFable5ModelIdentity(modelRef)) {
+ if (resolveClaudeFable5ModelIdentity(modelRef) || resolveClaudeMythos5ModelIdentity(modelRef)) {
return { off: "low", minimal: "low", xhigh: "xhigh", max: "max" };
}
if (resolveClaudeSonnet5ModelIdentity(modelRef)) {
@@ -116,6 +119,7 @@ export function resolveBedrockClaudeThinkingProfile(
const modelRefs = [trimmed, canonicalModelId];
if (
resolveClaudeFable5ModelIdentity({ id: trimmed, params }) ||
+ resolveClaudeMythos5ModelIdentity({ id: trimmed, params }) ||
resolveClaudeSonnet5ModelIdentity({ id: trimmed, params })
) {
return {
diff --git a/extensions/anthropic-vertex/index.test.ts b/extensions/anthropic-vertex/index.test.ts
index bf05f162c28a..cc3f95053d79 100644
--- a/extensions/anthropic-vertex/index.test.ts
+++ b/extensions/anthropic-vertex/index.test.ts
@@ -80,6 +80,7 @@ describe("anthropic-vertex provider plugin", () => {
expect(result.provider.headers).toEqual({ "x-test-header": "1" });
expect(result.provider.models.map((model) => model.id)).toEqual([
"claude-fable-5",
+ "claude-mythos-5",
"claude-opus-4-8",
"claude-opus-4-6",
"claude-sonnet-4-6",
@@ -90,9 +91,15 @@ describe("anthropic-vertex provider plugin", () => {
xhigh: "xhigh",
max: "max",
});
- expect(result.provider.models[1]?.thinkingLevelMap).toEqual({ xhigh: "xhigh", max: "max" });
- expect(result.provider.models[2]?.thinkingLevelMap).toEqual({ xhigh: null, max: "max" });
+ expect(result.provider.models[1]?.thinkingLevelMap).toEqual({
+ off: "low",
+ minimal: "low",
+ xhigh: "xhigh",
+ max: "max",
+ });
+ expect(result.provider.models[2]?.thinkingLevelMap).toEqual({ xhigh: "xhigh", max: "max" });
expect(result.provider.models[3]?.thinkingLevelMap).toEqual({ xhigh: null, max: "max" });
+ expect(result.provider.models[4]?.thinkingLevelMap).toEqual({ xhigh: null, max: "max" });
});
it.each(["global", "us", "eu"])("publishes Sonnet 5 for the %s endpoint", (region) => {
@@ -289,6 +296,39 @@ describe("anthropic-vertex provider plugin", () => {
});
});
+ it("restores Mythos 5 metadata for explicit Vertex catalog rows", async () => {
+ const provider = await registerSingleProviderPlugin(anthropicVertexPlugin);
+ const normalized = provider.normalizeResolvedModel?.({
+ provider: "anthropic-vertex",
+ modelId: "claude-mythos-5",
+ model: {
+ id: "claude-mythos-5",
+ name: "Claude Mythos 5",
+ api: "anthropic-messages",
+ provider: "anthropic-vertex",
+ baseUrl: "https://aiplatform.googleapis.com",
+ reasoning: false,
+ input: ["text"],
+ cost: { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 },
+ contextWindow: 200_000,
+ maxTokens: 8192,
+ },
+ } as never);
+
+ expect(normalized).toMatchObject({
+ reasoning: true,
+ input: ["text", "image"],
+ contextWindow: 1_000_000,
+ contextTokens: 1_000_000,
+ maxTokens: 128_000,
+ thinkingLevelMap: {
+ off: "low",
+ minimal: "low",
+ xhigh: "xhigh",
+ max: "max",
+ },
+ });
+ });
it("resolves synthetic auth when ADC is available", async () => {
hasAnthropicVertexAvailableAuthMock.mockReturnValue(true);
const provider = await registerSingleProviderPlugin(anthropicVertexPlugin);
diff --git a/extensions/anthropic-vertex/provider-catalog.ts b/extensions/anthropic-vertex/provider-catalog.ts
index d80d0a2a982f..84c41c1e80af 100644
--- a/extensions/anthropic-vertex/provider-catalog.ts
+++ b/extensions/anthropic-vertex/provider-catalog.ts
@@ -9,6 +9,7 @@ import type {
} from "openclaw/plugin-sdk/provider-model-shared";
import {
resolveClaudeFable5ModelIdentity,
+ resolveClaudeMythos5ModelIdentity,
resolveClaudeSonnet5ModelIdentity,
} from "openclaw/plugin-sdk/provider-model-shared";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
@@ -16,7 +17,7 @@ import { resolveAnthropicVertexClientRegion, resolveAnthropicVertexRegion } from
/** Default Anthropic Vertex model used for implicit provider catalogs. */
export const ANTHROPIC_VERTEX_DEFAULT_MODEL_ID = "claude-sonnet-4-6";
const ANTHROPIC_VERTEX_DEFAULT_CONTEXT_WINDOW = 1_000_000;
-const ANTHROPIC_VERTEX_FABLE_MAX_TOKENS = 128_000;
+const ANTHROPIC_VERTEX_CLAUDE_5_MAX_TOKENS = 128_000;
// Vertex's introductory rate expires at the documented UTC month boundary.
const SONNET_5_STANDARD_PRICING_START_MS = Date.UTC(2026, 8, 1);
const SONNET_5_SUPPORTED_REGIONS = new Set(["global", "us", "eu"]);
@@ -96,7 +97,16 @@ function buildAnthropicVertexCatalog(region: string, nowMs: number): ModelDefini
reasoning: true,
input: ["text", "image"],
cost: { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 },
- maxTokens: ANTHROPIC_VERTEX_FABLE_MAX_TOKENS,
+ maxTokens: ANTHROPIC_VERTEX_CLAUDE_5_MAX_TOKENS,
+ thinkingLevelMap: { off: "low", minimal: "low", xhigh: "xhigh", max: "max" },
+ }),
+ buildAnthropicVertexModel({
+ id: "claude-mythos-5",
+ name: "Claude Mythos 5",
+ reasoning: true,
+ input: ["text", "image"],
+ cost: { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 },
+ maxTokens: ANTHROPIC_VERTEX_CLAUDE_5_MAX_TOKENS,
thinkingLevelMap: { off: "low", minimal: "low", xhigh: "xhigh", max: "max" },
}),
...sonnet5,
@@ -137,15 +147,16 @@ export function normalizeAnthropicVertexResolvedModel(
): ProviderRuntimeModel | undefined {
const ref = { id: modelId, params: model.params };
const fable5 = resolveClaudeFable5ModelIdentity(ref) !== undefined;
+ const mythos5 = resolveClaudeMythos5ModelIdentity(ref) !== undefined;
const sonnet5 = resolveClaudeSonnet5ModelIdentity(ref) !== undefined;
- if (!fable5 && !sonnet5) {
+ if (!fable5 && !mythos5 && !sonnet5) {
return undefined;
}
const input: ProviderRuntimeModel["input"] = model.input.includes("image")
? model.input
: [...model.input, "image"];
const nativeThinkingLevelMap = {
- ...(fable5 ? { off: "low" as const, minimal: "low" as const } : {}),
+ ...(fable5 || mythos5 ? { off: "low" as const, minimal: "low" as const } : {}),
xhigh: "xhigh",
max: "max",
};
@@ -156,7 +167,8 @@ export function normalizeAnthropicVertexResolvedModel(
const nativeThinkingLevelsMatch =
model.thinkingLevelMap?.xhigh === "xhigh" &&
model.thinkingLevelMap.max === "max" &&
- (!fable5 || (model.thinkingLevelMap.off === "low" && model.thinkingLevelMap.minimal === "low"));
+ (!(fable5 || mythos5) ||
+ (model.thinkingLevelMap.off === "low" && model.thinkingLevelMap.minimal === "low"));
const cost = sonnet5
? resolveSonnet5Cost(resolveAnthropicVertexClientRegion({ baseUrl: model.baseUrl }))
: undefined;
@@ -171,7 +183,7 @@ export function normalizeAnthropicVertexResolvedModel(
input === model.input &&
model.contextWindow === ANTHROPIC_VERTEX_DEFAULT_CONTEXT_WINDOW &&
model.contextTokens === ANTHROPIC_VERTEX_DEFAULT_CONTEXT_WINDOW &&
- (model.maxTokens ?? 0) >= ANTHROPIC_VERTEX_FABLE_MAX_TOKENS &&
+ (model.maxTokens ?? 0) >= ANTHROPIC_VERTEX_CLAUDE_5_MAX_TOKENS &&
nativeThinkingLevelsMatch &&
costMatches
) {
@@ -183,7 +195,7 @@ export function normalizeAnthropicVertexResolvedModel(
input,
contextWindow: ANTHROPIC_VERTEX_DEFAULT_CONTEXT_WINDOW,
contextTokens: ANTHROPIC_VERTEX_DEFAULT_CONTEXT_WINDOW,
- maxTokens: Math.max(model.maxTokens ?? 0, ANTHROPIC_VERTEX_FABLE_MAX_TOKENS),
+ maxTokens: Math.max(model.maxTokens ?? 0, ANTHROPIC_VERTEX_CLAUDE_5_MAX_TOKENS),
thinkingLevelMap,
...(cost ? { cost } : {}),
};
diff --git a/extensions/anthropic-vertex/provider-policy-api.test.ts b/extensions/anthropic-vertex/provider-policy-api.test.ts
index 7d2cdcb75f6e..ea69240cf080 100644
--- a/extensions/anthropic-vertex/provider-policy-api.test.ts
+++ b/extensions/anthropic-vertex/provider-policy-api.test.ts
@@ -32,16 +32,19 @@ describe("anthropic-vertex provider-policy-api", () => {
expect(profile?.levels.map((level) => level.id)).not.toContain("xhigh");
});
- it("inherits Claude Fable 5's provider-agnostic thinking contract", () => {
- const profile = resolveThinkingProfile({
- provider: "anthropic-vertex",
- modelId: "claude-fable-5",
- });
+ it.each(["claude-fable-5", "claude-mythos-5"])(
+ "inherits %s's provider-agnostic thinking contract",
+ (modelId) => {
+ const profile = resolveThinkingProfile({
+ provider: "anthropic-vertex",
+ modelId,
+ });
- expect(profile?.defaultLevel).toBe("high");
- expect(profile?.preserveWhenCatalogReasoningFalse).toBe(true);
- expect(profile?.levels.map((level) => level.id)).toContain("max");
- });
+ expect(profile?.defaultLevel).toBe("high");
+ expect(profile?.preserveWhenCatalogReasoningFalse).toBe(true);
+ expect(profile?.levels.map((level) => level.id)).toContain("max");
+ },
+ );
it("resolves deployment aliases from canonical model metadata", () => {
const profile = resolveThinkingProfile({
diff --git a/extensions/anthropic-vertex/stream-runtime.ts b/extensions/anthropic-vertex/stream-runtime.ts
index a51ab34e7f40..48e19d83968a 100644
--- a/extensions/anthropic-vertex/stream-runtime.ts
+++ b/extensions/anthropic-vertex/stream-runtime.ts
@@ -14,7 +14,9 @@ import {
import {
resolveClaudeFable5ModelIdentity,
resolveClaudeModelIdentity,
+ resolveClaudeMythos5ModelIdentity,
resolveClaudeSonnet5ModelIdentity,
+ requiresClaudeMandatoryAdaptiveThinking,
supportsClaudeAdaptiveThinking,
supportsClaudeNativeMaxEffort,
supportsClaudeNativeXhighEffort,
@@ -60,11 +62,11 @@ function isClaudeSonnet5Model(modelId: string): boolean {
}
function isClaudeMythos5Model(modelId: string): boolean {
- return /(?:^|-)claude-mythos-5(?=$|[^a-z0-9])/.test(resolveClaudeModelIdentity({ id: modelId }));
+ return resolveClaudeMythos5ModelIdentity({ id: modelId }) !== undefined;
}
function supportsAdaptiveThinking(modelId: string): boolean {
- return supportsClaudeAdaptiveThinking({ id: modelId }) || isClaudeMythos5Model(modelId);
+ return supportsClaudeAdaptiveThinking({ id: modelId });
}
function mapAnthropicAdaptiveEffort(
@@ -153,15 +155,14 @@ export function createAnthropicVertexStreamFn(
requestedMaxTokens: options?.maxTokens,
});
const contractModelId = resolveClaudeModelIdentity(model);
- const fable5 = isClaudeFable5Model(contractModelId);
const sonnet5 = isClaudeSonnet5Model(contractModelId);
- const mandatoryAdaptiveThinking = fable5 || isClaudeMythos5Model(contractModelId);
+ const mandatoryAdaptiveThinking = requiresClaudeMandatoryAdaptiveThinking({
+ id: contractModelId,
+ });
const requestedReasoning = options?.reasoning;
const reasoning =
requestedReasoning === "off" && mandatoryAdaptiveThinking
- ? fable5
- ? "low"
- : "high"
+ ? "low"
: (requestedReasoning ?? (mandatoryAdaptiveThinking || sonnet5 ? "high" : undefined));
const adaptiveThinking =
mandatoryAdaptiveThinking ||
@@ -206,7 +207,7 @@ export function createAnthropicVertexStreamFn(
? budgets[reasoning as keyof typeof budgets]
: undefined) ?? 10000;
}
- } else if (fable5) {
+ } else if (mandatoryAdaptiveThinking) {
opts.thinkingEnabled = true;
opts.effort = "high";
} else {
diff --git a/extensions/anthropic/index.test.ts b/extensions/anthropic/index.test.ts
index f550d03567c5..c6075e239979 100644
--- a/extensions/anthropic/index.test.ts
+++ b/extensions/anthropic/index.test.ts
@@ -696,6 +696,58 @@ describe("anthropic provider replay hooks", () => {
});
});
+ it("resolves Claude Mythos 5 with its direct-only mandatory-adaptive contract", async () => {
+ const provider = await registerSingleProviderPlugin(anthropicPlugin);
+ const resolved = provider.resolveDynamicModel?.({
+ provider: "anthropic",
+ modelId: "claude-mythos-5",
+ modelRegistry: createModelRegistry([]),
+ } as ProviderResolveDynamicModelContext);
+
+ expectFields(resolved, {
+ provider: "anthropic",
+ id: "claude-mythos-5",
+ api: "anthropic-messages",
+ reasoning: true,
+ input: ["text", "image"],
+ cost: { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 },
+ contextWindow: 1_000_000,
+ contextTokens: 1_000_000,
+ maxTokens: 128_000,
+ thinkingLevelMap: {
+ off: "low",
+ minimal: "low",
+ xhigh: "xhigh",
+ max: "max",
+ },
+ });
+ expect(requireRecord(resolved, "Mythos model").mediaInput).toEqual({
+ image: { maxSidePx: 2576, preferredSidePx: 2576, tokenMode: "provider" },
+ });
+ expect(
+ provider.resolveThinkingProfile?.({
+ provider: "anthropic",
+ modelId: "claude-mythos-5",
+ } as never)?.defaultLevel,
+ ).toBe("high");
+ expect(
+ provider.resolveDynamicModel?.({
+ provider: "claude-cli",
+ modelId: "claude-mythos-5",
+ modelRegistry: createModelRegistry([]),
+ } as ProviderResolveDynamicModelContext),
+ ).toBeUndefined();
+ expect(
+ provider.resolveThinkingProfile?.({
+ provider: "claude-cli",
+ modelId: "claude-mythos-5",
+ } as never),
+ ).toEqual({
+ levels: [{ id: "off" }],
+ defaultLevel: "off",
+ });
+ });
+
it("rolls Claude Sonnet 5 to standard pricing on September 1, 2026", async () => {
vi.useFakeTimers();
vi.setSystemTime(Date.UTC(2026, 8, 1));
diff --git a/extensions/anthropic/openclaw.plugin.json b/extensions/anthropic/openclaw.plugin.json
index 1a6ceb4a0446..9abc894521ac 100644
--- a/extensions/anthropic/openclaw.plugin.json
+++ b/extensions/anthropic/openclaw.plugin.json
@@ -110,6 +110,24 @@
"max": "max"
}
},
+ {
+ "id": "claude-mythos-5",
+ "name": "Claude Mythos 5",
+ "reasoning": true,
+ "input": ["text", "image"],
+ "mediaInput": {
+ "image": { "maxSidePx": 2576, "preferredSidePx": 2576, "tokenMode": "provider" }
+ },
+ "cost": { "input": 10, "output": 50, "cacheRead": 1, "cacheWrite": 12.5 },
+ "contextWindow": 1000000,
+ "maxTokens": 128000,
+ "thinkingLevelMap": {
+ "off": "low",
+ "minimal": "low",
+ "xhigh": "xhigh",
+ "max": "max"
+ }
+ },
{
"id": "claude-opus-4-8",
"name": "Claude Opus 4.8",
@@ -194,6 +212,7 @@
"opus-4.8": "claude-opus-4-8",
"opus": "claude-opus-4-8",
"opus-4.6": "claude-opus-4-6",
+ "mythos-5": "claude-mythos-5",
"sonnet-5": "claude-sonnet-5",
"sonnet": "claude-sonnet-5",
"sonnet-4.6": "claude-sonnet-4-6"
diff --git a/extensions/anthropic/provider-policy-api.test.ts b/extensions/anthropic/provider-policy-api.test.ts
index d8c09ffc681e..e13cc0f8ad8d 100644
--- a/extensions/anthropic/provider-policy-api.test.ts
+++ b/extensions/anthropic/provider-policy-api.test.ts
@@ -136,36 +136,39 @@ describe("anthropic provider policy public artifact", () => {
expect(profile?.defaultLevel).toBe("off");
});
- it("exposes the always-adaptive Claude Fable 5 thinking profile", () => {
- const profile = resolveThinkingProfile({
- provider: "anthropic",
- modelId: "claude-fable-5",
- });
+ it.each(["claude-fable-5", "claude-mythos-5"])(
+ "exposes the mandatory-adaptive %s thinking profile",
+ (modelId) => {
+ const profile = resolveThinkingProfile({
+ provider: "anthropic",
+ modelId,
+ });
- expect(profile).toEqual({
- levels: [
- { id: "off" },
- { id: "minimal" },
- { id: "low" },
- { id: "medium" },
- { id: "high" },
- { id: "xhigh" },
- { id: "adaptive" },
- { id: "max" },
- ],
- defaultLevel: "high",
- preserveWhenCatalogReasoningFalse: true,
- });
- expect(
- resolveThinkingProfile({
- provider: "claude-cli",
- modelId: "claude-fable-5",
- }),
- ).toEqual({
- levels: [{ id: "off" }],
- defaultLevel: "off",
- });
- });
+ expect(profile).toEqual({
+ levels: [
+ { id: "off" },
+ { id: "minimal" },
+ { id: "low" },
+ { id: "medium" },
+ { id: "high" },
+ { id: "xhigh" },
+ { id: "adaptive" },
+ { id: "max" },
+ ],
+ defaultLevel: "high",
+ preserveWhenCatalogReasoningFalse: true,
+ });
+ expect(
+ resolveThinkingProfile({
+ provider: "claude-cli",
+ modelId,
+ }),
+ ).toEqual({
+ levels: [{ id: "off" }],
+ defaultLevel: "off",
+ });
+ },
+ );
it("does not return fable-5 off-thinking profile for claude-fable-50 (prefix boundary check)", () => {
const profile = resolveThinkingProfile({
@@ -177,6 +180,16 @@ describe("anthropic provider policy public artifact", () => {
expect(profile?.defaultLevel).not.toBe("off");
});
+ it("preserves the existing Claude CLI Mythos Preview thinking profile", () => {
+ const profile = resolveThinkingProfile({
+ provider: "claude-cli",
+ modelId: "claude-mythos-preview",
+ });
+
+ expect(profile?.defaultLevel).toBe("adaptive");
+ expect(profile?.levels.map((level) => level.id)).toContain("max");
+ });
+
it("exposes native max without xhigh for direct Claude 4.6 routes", () => {
for (const provider of ["anthropic", "claude-cli"]) {
const profile = resolveThinkingProfile({
diff --git a/extensions/anthropic/provider-policy-api.ts b/extensions/anthropic/provider-policy-api.ts
index d5292a16e4fc..b2f36d1da179 100644
--- a/extensions/anthropic/provider-policy-api.ts
+++ b/extensions/anthropic/provider-policy-api.ts
@@ -3,8 +3,9 @@
* path for config defaults and thinking profiles.
*/
import {
- resolveClaudeFable5ModelIdentity,
resolveClaudeModelIdentity,
+ resolveClaudeFable5ModelIdentity,
+ resolveClaudeMythos5ModelIdentity,
resolveClaudeThinkingProfile,
} from "openclaw/plugin-sdk/provider-model-shared";
import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-types";
@@ -40,7 +41,10 @@ export function resolveThinkingProfile(params: {
includeNativeMax: true,
});
case "claude-cli":
- if (resolveClaudeFable5ModelIdentity({ id: contractModelId })) {
+ if (
+ resolveClaudeFable5ModelIdentity({ id: contractModelId }) ||
+ resolveClaudeMythos5ModelIdentity({ id: contractModelId })
+ ) {
return CLAUDE_CLI_OFF_THINKING_PROFILE;
}
return resolveClaudeThinkingProfile(contractModelId, undefined, {
diff --git a/extensions/anthropic/register.runtime.ts b/extensions/anthropic/register.runtime.ts
index 6ff4087791d3..dc828695d069 100644
--- a/extensions/anthropic/register.runtime.ts
+++ b/extensions/anthropic/register.runtime.ts
@@ -30,6 +30,7 @@ import {
type ProviderPlugin,
resolveClaudeFable5ModelIdentity,
resolveClaudeModelIdentity,
+ resolveClaudeMythos5ModelIdentity,
resolveClaudeSonnet5ModelIdentity,
resolveClaudeThinkingProfile,
supportsClaudeAdaptiveThinking,
@@ -292,7 +293,7 @@ function buildAnthropicForwardCompatModel(
if (trimmedModelId !== lower || !matchesAnthropicModernModel(lower)) {
return undefined;
}
- if (isAnthropicFable5Model(lower) && normalizedProvider !== PROVIDER_ID) {
+ if (isAnthropicMandatoryClaude5Model(lower) && normalizedProvider !== PROVIDER_ID) {
return undefined;
}
const provider =
@@ -305,7 +306,7 @@ function buildAnthropicForwardCompatModel(
baseUrl: "https://api.anthropic.com",
reasoning: true,
input: ["text", "image"],
- cost: isAnthropicFable5Model(trimmedModelId)
+ cost: isAnthropicMandatoryClaude5Model(trimmedModelId)
? { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 }
: isAnthropicSonnet5Model(trimmedModelId) && provider === PROVIDER_ID
? resolveAnthropicSonnet5Cost()
@@ -315,7 +316,15 @@ function buildAnthropicForwardCompatModel(
? ANTHROPIC_MODERN_MAX_OUTPUT_TOKENS
: 64_000,
...(supportsClaudeNativeXhighEffort({ id: trimmedModelId })
- ? { thinkingLevelMap: { xhigh: "xhigh", max: "max" } }
+ ? {
+ thinkingLevelMap: {
+ ...(isAnthropicMandatoryClaude5Model(trimmedModelId)
+ ? { off: "low" as const, minimal: "low" as const }
+ : {}),
+ xhigh: "xhigh",
+ max: "max",
+ },
+ }
: supportsAnthropicNativeMaxEffort(trimmedModelId)
? { thinkingLevelMap: { max: "max" } }
: {}),
@@ -370,19 +379,27 @@ function isAnthropicFable5Model(modelId: string): boolean {
return resolveClaudeFable5ModelIdentity({ id: modelId }) !== undefined;
}
+function isAnthropicMythos5Model(modelId: string): boolean {
+ return resolveClaudeMythos5ModelIdentity({ id: modelId }) !== undefined;
+}
+
+function isAnthropicMandatoryClaude5Model(modelId: string): boolean {
+ return isAnthropicFable5Model(modelId) || isAnthropicMythos5Model(modelId);
+}
+
function isAnthropicSonnet5Model(modelId: string): boolean {
return resolveClaudeSonnet5ModelIdentity({ id: modelId }) !== undefined;
}
function resolveAnthropicFixedContextWindow(modelId: string): number | undefined {
- if (isAnthropicFable5Model(modelId) || isAnthropicSonnet5Model(modelId)) {
+ if (isAnthropicMandatoryClaude5Model(modelId) || isAnthropicSonnet5Model(modelId)) {
return ANTHROPIC_EXACT_1M_CONTEXT_TOKENS;
}
return isAnthropicGa1MModel(modelId) ? ANTHROPIC_GA_1M_CONTEXT_TOKENS : undefined;
}
function isAnthropic128kOutputModel(modelId: string): boolean {
- if (isAnthropicFable5Model(modelId) || isAnthropicSonnet5Model(modelId)) {
+ if (isAnthropicMandatoryClaude5Model(modelId) || isAnthropicSonnet5Model(modelId)) {
return true;
}
return /^claude-opus-4-8(?=$|[^a-z0-9])/.test(resolveClaudeModelIdentity({ id: modelId }));
@@ -453,7 +470,7 @@ function applyAnthropicFixedContextWindow(params: {
return undefined;
}
const exactContextWindow =
- isAnthropicFable5Model(params.contractModelId) ||
+ isAnthropicMandatoryClaude5Model(params.contractModelId) ||
isAnthropicSonnet5Model(params.contractModelId);
const nextContextWindow = exactContextWindow
? fixedContextWindow
@@ -496,8 +513,8 @@ function applyAnthropicThinkingLevelMap(params: {
modelId: string;
model: ProviderRuntimeModel;
}): ProviderRuntimeModel | undefined {
- const fable5 = isAnthropicFable5Model(params.modelId);
- const nativeXhigh = fable5 || supportsClaudeNativeXhighEffort({ id: params.modelId });
+ const mandatoryClaude5 = isAnthropicMandatoryClaude5Model(params.modelId);
+ const nativeXhigh = mandatoryClaude5 || supportsClaudeNativeXhighEffort({ id: params.modelId });
if (!supportsAnthropicNativeMaxEffort(params.modelId)) {
return undefined;
}
@@ -505,7 +522,7 @@ function applyAnthropicThinkingLevelMap(params: {
const nativeDefaults = isAnthropicMythosPreviewModel(params.modelId)
? { max: "max" as const }
: {
- ...(fable5 ? { off: "low" as const, minimal: "low" as const } : {}),
+ ...(mandatoryClaude5 ? { off: "low" as const, minimal: "low" as const } : {}),
xhigh: nativeXhigh ? ("xhigh" as const) : null,
max: "max" as const,
};
@@ -594,13 +611,14 @@ function normalizeAnthropicResolvedModel(
params: ctx.model.params,
});
if (
- isAnthropicFable5Model(contractModelId) &&
+ isAnthropicMandatoryClaude5Model(contractModelId) &&
normalizeLowercaseStringOrEmpty(ctx.provider) !== PROVIDER_ID
) {
return undefined;
}
const contractModel =
- (isAnthropicFable5Model(contractModelId) || isAnthropicSonnet5Model(contractModelId)) &&
+ (isAnthropicMandatoryClaude5Model(contractModelId) ||
+ isAnthropicSonnet5Model(contractModelId)) &&
!ctx.model.reasoning
? { ...ctx.model, reasoning: true }
: ctx.model;
@@ -877,12 +895,12 @@ export function buildAnthropicProvider(): ProviderPlugin {
...NATIVE_ANTHROPIC_REPLAY_HOOKS,
isModernModelRef: ({ provider, modelId }) =>
matchesAnthropicModernModel(modelId) &&
- (!isAnthropicFable5Model(modelId) ||
+ (!isAnthropicMandatoryClaude5Model(modelId) ||
normalizeLowercaseStringOrEmpty(provider) === PROVIDER_ID),
resolveReasoningOutputMode: () => "native",
resolveThinkingProfile: ({ provider, modelId, params }) => {
const contractModelId = resolveClaudeModelIdentity({ id: modelId, params });
- return isAnthropicFable5Model(contractModelId) &&
+ return isAnthropicMandatoryClaude5Model(contractModelId) &&
normalizeLowercaseStringOrEmpty(provider) !== PROVIDER_ID
? CLAUDE_CLI_OFF_THINKING_PROFILE
: resolveClaudeThinkingProfile(contractModelId, undefined, {
diff --git a/packages/ai/src/model-utils.ts b/packages/ai/src/model-utils.ts
index c8a4598f051a..2413877f911c 100644
--- a/packages/ai/src/model-utils.ts
+++ b/packages/ai/src/model-utils.ts
@@ -1,7 +1,7 @@
// Provides model selection, usage, and thinking-level utility helpers.
import {
- resolveClaudeFable5ModelIdentity,
resolveClaudeNativeThinkingLevelMap,
+ requiresClaudeMandatoryAdaptiveThinking,
} from "@openclaw/llm-core";
import type { Api, Model, ModelThinkingLevel, Usage } from "./types.js";
@@ -36,9 +36,9 @@ function resolveThinkingLevelMap(model: Model) {
export function getSupportedThinkingLevels(
model: Model,
): ModelThinkingLevel[] {
- const fableContract =
- model.api === "anthropic-messages" && resolveClaudeFable5ModelIdentity(model) !== undefined;
- if (!model.reasoning && !fableContract) {
+ const mandatoryAdaptiveContract =
+ model.api === "anthropic-messages" && requiresClaudeMandatoryAdaptiveThinking(model);
+ if (!model.reasoning && !mandatoryAdaptiveContract) {
return ["off"];
}
const thinkingLevelMap = resolveThinkingLevelMap(model);
diff --git a/packages/ai/src/providers/anthropic-model-contract.ts b/packages/ai/src/providers/anthropic-model-contract.ts
index 4860e1f00150..bb754f8ce67c 100644
--- a/packages/ai/src/providers/anthropic-model-contract.ts
+++ b/packages/ai/src/providers/anthropic-model-contract.ts
@@ -1,14 +1,19 @@
// Model-bound thinking cannot be exposed or replayed after a model switch.
import {
+ requiresClaudeDefaultSampling,
+ requiresClaudeMandatoryAdaptiveThinking,
resolveClaudeFable5ModelIdentity,
- resolveClaudeModelIdentity,
+ resolveClaudeMythos5ModelIdentity,
resolveClaudeSonnet5ModelIdentity,
} from "@openclaw/llm-core";
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
import type { Context, Model } from "../types.js";
export {
+ requiresClaudeDefaultSampling,
+ requiresClaudeMandatoryAdaptiveThinking,
resolveClaudeFable5ModelIdentity,
resolveClaudeModelIdentity,
+ resolveClaudeMythos5ModelIdentity,
resolveClaudeNativeThinkingLevelMap,
resolveClaudeSonnet5ModelIdentity,
supportsClaudeAdaptiveThinking,
@@ -66,6 +71,7 @@ export function usesClaudeStreamingRefusalContract(model: {
}
return (
resolveClaudeFable5ModelIdentity(model) !== undefined ||
+ resolveClaudeMythos5ModelIdentity(model) !== undefined ||
resolveClaudeSonnet5ModelIdentity(model) !== undefined
);
}
@@ -78,11 +84,7 @@ export function requiresClaudeAdaptiveThinking(model: {
if (normalizeApi(model.api) !== "anthropic-messages") {
return false;
}
- const modelId = resolveClaudeModelIdentity(model);
- return (
- resolveClaudeFable5ModelIdentity(model) !== undefined ||
- /(?:^|-)claude-mythos-preview(?=$|[^a-z0-9])/.test(modelId)
- );
+ return requiresClaudeMandatoryAdaptiveThinking(model);
}
/** Return whether omitted thinking should default to adaptive/high. */
@@ -120,14 +122,46 @@ export function prepareClaudeSonnet5RequestContext(model: Model, context: Contex
: { ...context, messages: context.messages.slice(0, end) };
}
-function resolveReplayFableIdentity(ref: ReplayModelRef): string | undefined {
+export function applyClaudeRequestContract(
+ params: Record,
+ model: {
+ id?: string;
+ params?: Record;
+ api?: string;
+ },
+): void {
+ if (normalizeApi(model.api) !== "anthropic-messages") {
+ return;
+ }
+ const sonnet5 = resolveClaudeSonnet5ModelIdentity(model) !== undefined;
+ if (!requiresClaudeDefaultSampling(model) && !sonnet5) {
+ return;
+ }
+ delete params.temperature;
+ delete params.top_p;
+ delete params.top_k;
+ if (sonnet5) {
+ delete params.service_tier;
+ }
+}
+
+function resolveReplayModelBoundIdentity(ref: ReplayModelRef): string | undefined {
if (normalizeApi(ref.api) !== "anthropic-messages") {
return undefined;
}
- if (hasConcreteResponseModel(ref)) {
- return resolveClaudeFable5ModelIdentity({ id: ref.responseModelId });
+ const modelRef = hasConcreteResponseModel(ref)
+ ? { id: ref.responseModelId }
+ : { id: ref.modelId, params: ref.modelParams };
+ const fableIdentity = resolveClaudeFable5ModelIdentity(modelRef);
+ if (fableIdentity) {
+ return `fable:${fableIdentity}`;
}
- return resolveClaudeFable5ModelIdentity({ id: ref.modelId, params: ref.modelParams });
+ const mythosIdentity = resolveClaudeMythos5ModelIdentity(modelRef);
+ if (mythosIdentity) {
+ return `mythos:${mythosIdentity}`;
+ }
+ const sonnetIdentity = resolveClaudeSonnet5ModelIdentity(modelRef);
+ return sonnetIdentity ? `sonnet:${sonnetIdentity}` : undefined;
}
export function resolveModelBoundThinkingReplayMode(params: {
@@ -136,8 +170,8 @@ export function resolveModelBoundThinkingReplayMode(params: {
}): "default" | "preserve" | "drop" {
const sourceApi = normalizeApi(params.source.api);
const targetApi = normalizeApi(params.target.api);
- const sourceIdentity = resolveReplayFableIdentity(params.source);
- const targetIdentity = resolveReplayFableIdentity(params.target);
+ const sourceIdentity = resolveReplayModelBoundIdentity(params.source);
+ const targetIdentity = resolveReplayModelBoundIdentity(params.target);
const sameRoute =
normalizeLowercaseStringOrEmpty(params.source.provider) ===
normalizeLowercaseStringOrEmpty(params.target.provider) &&
diff --git a/packages/ai/src/providers/anthropic.test.ts b/packages/ai/src/providers/anthropic.test.ts
index 59e2c8a260ea..d8dfec19ad99 100644
--- a/packages/ai/src/providers/anthropic.test.ts
+++ b/packages/ai/src/providers/anthropic.test.ts
@@ -862,6 +862,8 @@ describe("Anthropic provider", () => {
it.each([
["claude-fable-5", "Claude Fable 5", "anthropic", "sk-ant-provider"],
+ ["claude-mythos-5", "Claude Mythos 5", "anthropic", "sk-ant-provider"],
+ ["claude-mythos-5", "Claude Mythos 5", "anthropic-vertex", "vertex-token"],
["claude-sonnet-5", "Claude Sonnet 5", "anthropic", "sk-ant-provider"],
["claude-sonnet-5", "Claude Sonnet 5", "anthropic-vertex", "vertex-token"],
])("surfaces structured %s streaming refusals for %s", async (id, name, provider, apiKey) => {
@@ -1424,6 +1426,47 @@ describe("Anthropic provider", () => {
expect(capturedPayload).not.toHaveProperty("temperature");
});
+ it("uses mandatory adaptive thinking and default sampling for Claude Mythos 5", async () => {
+ let capturedPayload: unknown;
+ const stream = streamSimpleAnthropic(
+ makeAnthropicModel({
+ id: "prod-mythos",
+ name: "Production Claude",
+ provider: "microsoft-foundry",
+ params: { canonicalModelId: "claude-mythos-5" },
+ reasoning: false,
+ baseUrl: "https://example.services.ai.azure.com/anthropic",
+ maxTokens: 128_000,
+ }),
+ {
+ messages: [{ role: "user", content: "hello", timestamp: 0 }],
+ },
+ {
+ apiKey: "sk-ant-provider",
+ reasoning: "off",
+ temperature: 0.2,
+ onPayload: (payload) => {
+ capturedPayload = {
+ ...(payload as Record),
+ top_p: 0.9,
+ top_k: 40,
+ };
+ return capturedPayload;
+ },
+ },
+ );
+
+ await stream.result();
+
+ expect(capturedPayload).toMatchObject({
+ thinking: { type: "adaptive", display: "summarized" },
+ output_config: { effort: "low" },
+ });
+ expect(capturedPayload).not.toHaveProperty("temperature");
+ expect(capturedPayload).not.toHaveProperty("top_p");
+ expect(capturedPayload).not.toHaveProperty("top_k");
+ });
+
it("preserves native max effort for Claude Mythos Preview", async () => {
let capturedPayload: unknown;
const stream = streamSimpleAnthropic(
@@ -1546,6 +1589,41 @@ describe("Anthropic provider", () => {
expect((capturedPayload as { output_config?: unknown }).output_config).toBeUndefined();
});
+ it.each(["claude-opus-4-8", "claude-mythos-preview"])(
+ "restores default sampling for %s after payload hooks",
+ async (modelId) => {
+ let capturedPayload: unknown;
+ const stream = streamSimpleAnthropic(
+ makeAnthropicModel({
+ id: modelId,
+ name: modelId,
+ maxTokens: 128_000,
+ }),
+ { messages: [{ role: "user", content: "hello", timestamp: 0 }] },
+ {
+ apiKey: "sk-ant-provider",
+ reasoning: "high",
+ temperature: 0.2,
+ onPayload: (payload) => {
+ capturedPayload = {
+ ...(payload as Record),
+ temperature: 0.2,
+ top_p: 0.9,
+ top_k: 40,
+ };
+ return capturedPayload;
+ },
+ },
+ );
+
+ await stream.result();
+
+ expect(capturedPayload).not.toHaveProperty("temperature");
+ expect(capturedPayload).not.toHaveProperty("top_p");
+ expect(capturedPayload).not.toHaveProperty("top_k");
+ },
+ );
+
it.each([
{
id: "prod-primary",
diff --git a/packages/ai/src/providers/anthropic.ts b/packages/ai/src/providers/anthropic.ts
index 2e183271d313..a1614478ae76 100644
--- a/packages/ai/src/providers/anthropic.ts
+++ b/packages/ai/src/providers/anthropic.ts
@@ -46,6 +46,7 @@ import {
usesFoundryBearerAuth,
} from "./anthropic-auth-headers.js";
import {
+ applyClaudeRequestContract,
prepareClaudeSonnet5RequestContext,
resolveClaudeNativeThinkingLevelMap,
resolveClaudeSonnet5ModelIdentity,
@@ -575,6 +576,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages", AnthropicOpti
if (nextParams !== undefined) {
params = nextParams as MessageCreateParamsStreaming;
}
+ applyClaudeRequestContract(params as unknown as Record, model);
const requestOptions = {
...(options?.signal ? { signal: options.signal } : {}),
...(options?.timeoutMs !== undefined ? { timeout: options.timeoutMs } : {}),
@@ -1014,14 +1016,18 @@ export const streamSimpleAnthropic: StreamFunction<
toolChoice: options?.toolChoice,
};
const mandatoryAdaptiveThinking = requiresClaudeAdaptiveThinking(model);
- const fable5 = usesClaudeFable5MessagesContract(model);
if (options?.reasoning === "off" && !mandatoryAdaptiveThinking) {
return streamAnthropic(model, context, {
...base,
thinkingEnabled: false,
} satisfies AnthropicOptions);
}
- const reasoning = options?.reasoning === "off" ? (fable5 ? "low" : "high") : options?.reasoning;
+ const reasoning =
+ options?.reasoning === "off"
+ ? mandatoryAdaptiveThinking
+ ? "low"
+ : "high"
+ : options?.reasoning;
if (resolveClaudeSonnet5ModelIdentity(model)) {
return streamAnthropic(model, context, {
...base,
@@ -1239,8 +1245,8 @@ function buildParams(
params: MessageCreateParamsStreaming;
toolProjection?: AnthropicToolProjection;
} {
- const fable5 = usesClaudeFable5MessagesContract(model);
- const replayThinkingEnabled = fable5 || options?.thinkingEnabled === true;
+ const mandatoryAdaptiveThinking = requiresClaudeAdaptiveThinking(model);
+ const replayThinkingEnabled = mandatoryAdaptiveThinking || options?.thinkingEnabled === true;
const { cacheControl } = getCacheControl(model, options?.cacheRetention);
const system = buildAnthropicSystemBlocks(context.systemPrompt, isOAuthTokenResult, cacheControl);
const compat = context.tools ? getAnthropicCompat(model) : undefined;
@@ -1304,18 +1310,18 @@ function buildParams(
params.tools = tools;
}
- // Configure thinking mode: always-on adaptive (Fable 5), adaptive (Opus
- // 4.6+ and Sonnet 4.6),
+ // Configure thinking mode: always-on adaptive (Fable 5 and Mythos 5),
+ // adaptive (Opus 4.6+ and Sonnet 4.6),
// budget-based (older models), or explicitly disabled.
- if (fable5 || model.reasoning || supportsAdaptiveThinking(model)) {
- if (fable5 || options?.thinkingEnabled) {
+ if (mandatoryAdaptiveThinking || model.reasoning || supportsAdaptiveThinking(model)) {
+ if (mandatoryAdaptiveThinking || options?.thinkingEnabled) {
// Default to "summarized" so Opus 4.7+ and Mythos Preview behave like
// older Claude 4 models (whose API default is also "summarized").
const display: AnthropicThinkingDisplay = options?.thinkingDisplay ?? "summarized";
if (supportsAdaptiveThinking(model)) {
// Adaptive thinking: Claude decides when and how much to think.
params.thinking = { type: "adaptive", display };
- const effort = options?.effort ?? (fable5 ? "high" : undefined);
+ const effort = options?.effort ?? (mandatoryAdaptiveThinking ? "high" : undefined);
if (effort) {
// The Anthropic SDK types can lag newly supported effort values such as "xhigh".
params.output_config =
diff --git a/packages/llm-core/src/model-contracts/anthropic.ts b/packages/llm-core/src/model-contracts/anthropic.ts
index bd9fa7b0c653..f66337034ad0 100644
--- a/packages/llm-core/src/model-contracts/anthropic.ts
+++ b/packages/llm-core/src/model-contracts/anthropic.ts
@@ -65,6 +65,26 @@ export function resolveClaudeFable5ModelIdentity(ref: ClaudeModelRef): string |
return normalized.slice((match.index ?? 0) + (match[0].startsWith("-") ? 1 : 0));
}
+/** Resolve Claude Mythos 5 through direct ids, cloud ids, or deployment metadata. */
+export function resolveClaudeMythos5ModelIdentity(ref: ClaudeModelRef): string | undefined {
+ const normalized = resolveClaudeModelIdentity(ref);
+ const match = /(?:^|-)claude-mythos-5(?=$|[^a-z0-9])/.exec(normalized);
+ if (!match) {
+ return undefined;
+ }
+ return normalized.slice((match.index ?? 0) + (match[0].startsWith("-") ? 1 : 0));
+}
+
+/** Return whether a Claude model requires adaptive thinking instead of manual budgets. */
+export function requiresClaudeMandatoryAdaptiveThinking(ref: ClaudeModelRef): boolean {
+ const modelId = resolveClaudeModelIdentity(ref);
+ return (
+ resolveClaudeFable5ModelIdentity(ref) !== undefined ||
+ resolveClaudeMythos5ModelIdentity(ref) !== undefined ||
+ /(?:^|-)claude-mythos-preview(?=$|[^a-z0-9])/.test(modelId)
+ );
+}
+
/** Resolve Claude Sonnet 5 through direct ids, cloud ids, or deployment metadata. */
export function resolveClaudeSonnet5ModelIdentity(ref: ClaudeModelRef): string | undefined {
const normalized = resolveClaudeModelIdentity(ref);
@@ -78,7 +98,7 @@ export function resolveClaudeSonnet5ModelIdentity(ref: ClaudeModelRef): string |
/** Return whether a Claude model supports adaptive thinking. */
export function supportsClaudeAdaptiveThinking(ref: ClaudeModelRef): boolean {
const modelId = resolveClaudeModelIdentity(ref);
- return /(?:^|-)claude-(?:fable-5|mythos-preview|opus-4-(?:6|7|8)|sonnet-(?:5|4-6))(?=$|[^a-z0-9])/.test(
+ return /(?:^|-)claude-(?:fable-5|mythos-(?:5|preview)|opus-4-(?:6|7|8)|sonnet-(?:5|4-6))(?=$|[^a-z0-9])/.test(
modelId,
);
}
@@ -86,13 +106,24 @@ export function supportsClaudeAdaptiveThinking(ref: ClaudeModelRef): boolean {
/** Return whether a Claude model supports native max effort. */
export function supportsClaudeNativeMaxEffort(ref: ClaudeModelRef): boolean {
const modelId = resolveClaudeModelIdentity(ref);
- return /(?:^|-)claude-(?:fable-5|opus-4-(?:6|7|8)|sonnet-(?:5|4-6))(?=$|[^a-z0-9])/.test(modelId);
+ return /(?:^|-)claude-(?:fable-5|mythos-5|opus-4-(?:6|7|8)|sonnet-(?:5|4-6))(?=$|[^a-z0-9])/.test(
+ modelId,
+ );
}
/** Return whether a Claude model supports native xhigh effort. */
export function supportsClaudeNativeXhighEffort(ref: ClaudeModelRef): boolean {
const modelId = resolveClaudeModelIdentity(ref);
- return /(?:^|-)claude-(?:fable-5|opus-4-(?:7|8)|sonnet-5)(?=$|[^a-z0-9])/.test(modelId);
+ return /(?:^|-)claude-(?:fable-5|mythos-5|opus-4-(?:7|8)|sonnet-5)(?=$|[^a-z0-9])/.test(modelId);
+}
+
+/** Return whether a Claude model rejects caller-selected sampling parameters. */
+export function requiresClaudeDefaultSampling(ref: ClaudeModelRef): boolean {
+ const modelId = resolveClaudeModelIdentity(ref);
+ return (
+ supportsClaudeNativeXhighEffort(ref) ||
+ /(?:^|-)claude-mythos-preview(?=$|[^a-z0-9])/.test(modelId)
+ );
}
/**
diff --git a/src/agents/anthropic-transport-stream.test.ts b/src/agents/anthropic-transport-stream.test.ts
index 3e61b3c688ad..db5df8a76a49 100644
--- a/src/agents/anthropic-transport-stream.test.ts
+++ b/src/agents/anthropic-transport-stream.test.ts
@@ -3519,6 +3519,42 @@ describe("anthropic transport stream", () => {
expect(result.responseModel).toBe("claude-fable-5");
});
+ it("uses mandatory adaptive thinking and default sampling for Claude Mythos 5 transport runs", async () => {
+ const model = makeAnthropicTransportModel({
+ id: "prod-mythos",
+ name: "Production Claude",
+ provider: "microsoft-foundry",
+ params: { canonicalModelId: "claude-mythos-5" },
+ reasoning: false,
+ baseUrl: "https://example.services.ai.azure.com/anthropic",
+ maxTokens: 128_000,
+ });
+
+ await runTransportStream(
+ model,
+ {
+ messages: [{ role: "user", content: "Think." }],
+ } as AnthropicStreamContext,
+ {
+ apiKey: "sk-ant-api",
+ reasoning: "off",
+ temperature: 0.2,
+ onPayload: (payload) => ({
+ ...(payload as Record),
+ top_p: 0.9,
+ top_k: 40,
+ }),
+ } as AnthropicStreamOptions,
+ );
+
+ const payload = latestAnthropicRequest().payload;
+ expect(payload.thinking).toEqual({ type: "adaptive", display: "summarized" });
+ expect(payload.output_config).toEqual({ effort: "low" });
+ expect(payload).not.toHaveProperty("temperature");
+ expect(payload).not.toHaveProperty("top_p");
+ expect(payload).not.toHaveProperty("top_k");
+ });
+
it("uses adaptive thinking for canonical Claude Mythos Preview transport aliases", async () => {
const model = makeAnthropicTransportModel({
id: "prod-mythos-preview",
@@ -3574,6 +3610,36 @@ describe("anthropic transport stream", () => {
expect(payload.output_config).toEqual({ effort: "high" });
});
+ it.each(["claude-opus-4-8", "claude-mythos-preview"])(
+ "restores default sampling for %s transport requests after payload hooks",
+ async (modelId) => {
+ await runTransportStream(
+ makeAnthropicTransportModel({
+ id: modelId,
+ name: modelId,
+ maxTokens: 128_000,
+ }),
+ { messages: [{ role: "user", content: "hello" }] } as AnthropicStreamContext,
+ {
+ apiKey: "sk-ant-api",
+ reasoning: "high",
+ temperature: 0.2,
+ onPayload: (payload) => ({
+ ...(payload as Record),
+ temperature: 0.2,
+ top_p: 0.9,
+ top_k: 40,
+ }),
+ } as AnthropicStreamOptions,
+ );
+
+ const payload = latestAnthropicRequest().payload;
+ expect(payload).not.toHaveProperty("temperature");
+ expect(payload).not.toHaveProperty("top_p");
+ expect(payload).not.toHaveProperty("top_k");
+ },
+ );
+
it("maps Claude Fable 5 transport thinking levels to adaptive effort", async () => {
const model = makeAnthropicTransportModel({
id: "claude-fable-5",
diff --git a/src/agents/anthropic-transport-stream.ts b/src/agents/anthropic-transport-stream.ts
index a79723b0ebe4..d37da6c723be 100644
--- a/src/agents/anthropic-transport-stream.ts
+++ b/src/agents/anthropic-transport-stream.ts
@@ -2,6 +2,7 @@ import {
ANTHROPIC_OMITTED_REASONING_TEXT,
ANTHROPIC_SERVER_SIDE_FALLBACK_BETA,
CLAUDE_FABLE_5_FALLBACK_MODEL_COST,
+ applyClaudeRequestContract,
applyAnthropicFallbackBoundary,
buildAnthropicServerSideFallbacks,
defaultsClaudeAdaptiveThinking,
@@ -979,8 +980,8 @@ function buildAnthropicParams(
params: Record;
toolProjection?: AnthropicToolProjection;
} {
- const fable5 = usesClaudeFable5MessagesContract(model);
- const replayThinkingEnabled = fable5 || options?.thinkingEnabled === true;
+ const mandatoryAdaptiveThinking = requiresClaudeAdaptiveThinking(model);
+ const replayThinkingEnabled = mandatoryAdaptiveThinking || options?.thinkingEnabled === true;
const maxTokens = resolveAnthropicMessagesMaxTokens({
modelMaxTokens: model.maxTokens,
requestedMaxTokens: options?.maxTokens,
@@ -1060,8 +1061,8 @@ function buildAnthropicParams(
params.tools = convertedTools.tools;
}
}
- if (fable5 || model.reasoning || supportsAdaptiveThinking(model)) {
- if (fable5 || options?.thinkingEnabled) {
+ if (mandatoryAdaptiveThinking || model.reasoning || supportsAdaptiveThinking(model)) {
+ if (mandatoryAdaptiveThinking || options?.thinkingEnabled) {
if (supportsAdaptiveThinking(model)) {
// Default display to "summarized" so Opus 4.7+/Fable 5 return a thinking
// summary like older Claude 4 models — mirrors the provider path
@@ -1070,7 +1071,7 @@ function buildAnthropicParams(
// blank (the live agent transport previously sent this for opus-4-8).
const display: AnthropicThinkingDisplay = options?.thinkingDisplay ?? "summarized";
params.thinking = { type: "adaptive", display };
- const effort = options?.effort ?? (fable5 ? "high" : undefined);
+ const effort = options?.effort ?? (mandatoryAdaptiveThinking ? "high" : undefined);
if (effort) {
params.output_config = { effort };
}
@@ -1121,13 +1122,8 @@ function resolveAnthropicTransportOptions(
const reasoningModelMaxTokens =
resolvePositiveAnthropicMaxTokens(model.maxTokens) ?? baseMaxTokens;
const mandatoryAdaptiveThinking = requiresClaudeAdaptiveThinking(model);
- const fable5 = usesClaudeFable5MessagesContract(model);
const reasoning =
- options?.reasoning === "off" && mandatoryAdaptiveThinking
- ? fable5
- ? "low"
- : "high"
- : options?.reasoning;
+ options?.reasoning === "off" && mandatoryAdaptiveThinking ? "low" : options?.reasoning;
const resolved: AnthropicTransportOptions = {
temperature: options?.temperature,
stop: options?.stop,
@@ -1229,6 +1225,7 @@ export function createAnthropicMessagesTransportStreamFn(): StreamFn {
if (nextParams !== undefined) {
params = nextParams as Record;
}
+ applyClaudeRequestContract(params, model);
const anthropicStream = client.messages.stream(
{ ...params, stream: true },
transportOptions.signal ? { signal: transportOptions.signal } : undefined,
diff --git a/src/agents/context-resolution.ts b/src/agents/context-resolution.ts
index 87aab9ba747e..28180dfefbd1 100644
--- a/src/agents/context-resolution.ts
+++ b/src/agents/context-resolution.ts
@@ -44,6 +44,7 @@ const ANTHROPIC_GA_1M_MODEL_PREFIXES = [
export const ANTHROPIC_CONTEXT_1M_TOKENS = 1_048_576;
export const ANTHROPIC_VERTEX_CONTEXT_1M_TOKENS = 1_000_000;
export const ANTHROPIC_FABLE_CONTEXT_TOKENS = 1_000_000;
+export const ANTHROPIC_MYTHOS_5_CONTEXT_TOKENS = 1_000_000;
export const ANTHROPIC_SONNET_5_CONTEXT_TOKENS = 1_000_000;
type ConfiguredContextTokens = {
@@ -160,6 +161,12 @@ export function resolveAnthropicFixedContextWindow(
) {
return ANTHROPIC_FABLE_CONTEXT_TOKENS;
}
+ if (
+ (provider === "anthropic" || provider === "anthropic-vertex") &&
+ /^claude-mythos-5(?=$|[^a-z0-9])/.test(modelId)
+ ) {
+ return ANTHROPIC_MYTHOS_5_CONTEXT_TOKENS;
+ }
if (resolveClaudeSonnet5ModelIdentity({ id: modelId })) {
return ANTHROPIC_SONNET_5_CONTEXT_TOKENS;
}
diff --git a/src/agents/context.test.ts b/src/agents/context.test.ts
index 1face6c8b754..74a14b029414 100644
--- a/src/agents/context.test.ts
+++ b/src/agents/context.test.ts
@@ -11,6 +11,7 @@ import {
import {
ANTHROPIC_CONTEXT_1M_TOKENS,
ANTHROPIC_FABLE_CONTEXT_TOKENS,
+ ANTHROPIC_MYTHOS_5_CONTEXT_TOKENS,
ANTHROPIC_SONNET_5_CONTEXT_TOKENS,
ANTHROPIC_VERTEX_CONTEXT_1M_TOKENS,
applyConfiguredContextWindows,
@@ -419,6 +420,8 @@ describe("resolveContextTokensForModel", () => {
it.each([
["anthropic", "claude-fable-5", ANTHROPIC_FABLE_CONTEXT_TOKENS],
["anthropic-vertex", "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],
["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],
@@ -449,6 +452,17 @@ describe("resolveContextTokensForModel", () => {
expect(result).toBe(200_000);
});
+ it("does not give mythos-5 context window to claude-mythos-50", () => {
+ const result = resolveContextTokensForModel({
+ provider: "anthropic",
+ model: "claude-mythos-50",
+ fallbackContextTokens: 200_000,
+ allowAsyncLoad: false,
+ });
+
+ expect(result).toBe(200_000);
+ });
+
it.each([
["anthropic", "claude-fable-5"],
["anthropic-vertex", "claude-fable-5"],
@@ -545,6 +559,11 @@ describe("resolveContextTokensForModel", () => {
it.each([
["anthropic", "claude-fable-5", ANTHROPIC_FABLE_CONTEXT_TOKENS],
["anthropic-vertex", "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],
+ ["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],
["anthropic", "claude-sonnet-4-6", ANTHROPIC_CONTEXT_1M_TOKENS],
["anthropic-vertex", "claude-sonnet-4-6", ANTHROPIC_VERTEX_CONTEXT_1M_TOKENS],
])(
diff --git a/src/agents/context.ts b/src/agents/context.ts
index 7ee8fb49d6e3..4784eb29d7b4 100644
--- a/src/agents/context.ts
+++ b/src/agents/context.ts
@@ -30,6 +30,7 @@ import { normalizeProviderId } from "./model-selection.js";
export {
ANTHROPIC_CONTEXT_1M_TOKENS,
ANTHROPIC_FABLE_CONTEXT_TOKENS,
+ ANTHROPIC_MYTHOS_5_CONTEXT_TOKENS,
ANTHROPIC_SONNET_5_CONTEXT_TOKENS,
ANTHROPIC_VERTEX_CONTEXT_1M_TOKENS,
} from "./context-resolution.js";
diff --git a/src/agents/transport-message-transform.test.ts b/src/agents/transport-message-transform.test.ts
index e9f69ae24392..8233c02e1134 100644
--- a/src/agents/transport-message-transform.test.ts
+++ b/src/agents/transport-message-transform.test.ts
@@ -60,6 +60,18 @@ describe("transformTransportMessages synthetic tool-result policy", () => {
source: { provider: "anthropic", model: "claude-fable-5" },
target: { provider: "anthropic-vertex", model: "claude-opus-4-8" },
},
+ {
+ source: { provider: "anthropic", model: "claude-mythos-5" },
+ target: { provider: "anthropic", model: "claude-opus-4-8" },
+ },
+ {
+ source: { provider: "anthropic", model: "claude-fable-5" },
+ target: { provider: "anthropic-vertex", model: "claude-mythos-5" },
+ },
+ {
+ source: { provider: "anthropic", model: "claude-mythos-5" },
+ target: { provider: "anthropic", model: "claude-fable-5" },
+ },
{
source: { provider: "anthropic", model: "claude-sonnet-4-6" },
target: { provider: "anthropic", model: "claude-fable-5" },
@@ -100,7 +112,7 @@ describe("transformTransportMessages synthetic tool-result policy", () => {
canonicalModelId: "claude-fable-5",
},
},
- ])("drops model-bound thinking for Fable switches", ({ source, target }) => {
+ ])("drops model-bound thinking for Fable/Mythos switches", ({ source, target }) => {
const result = transformTransportMessages(
[
{
@@ -185,8 +197,17 @@ describe("transformTransportMessages synthetic tool-result policy", () => {
targetModel: "prod-primary",
targetCanonicalModelId: "claude-fable-5",
},
+ {
+ sourceProvider: "anthropic",
+ sourceModel: "claude-mythos-5",
+ sourceResponseModel: undefined,
+ targetProvider: "anthropic-vertex",
+ targetApi: "anthropic-messages" as const,
+ targetModel: "claude-mythos-5",
+ targetCanonicalModelId: undefined,
+ },
])(
- "preserves Fable thinking across compatible Anthropic transports",
+ "preserves Fable/Mythos thinking across compatible Anthropic transports",
({
sourceProvider,
sourceModel,
diff --git a/src/plugin-sdk/provider-model-shared.test.ts b/src/plugin-sdk/provider-model-shared.test.ts
index 101c5fc6a787..bcaf5986b7b4 100644
--- a/src/plugin-sdk/provider-model-shared.test.ts
+++ b/src/plugin-sdk/provider-model-shared.test.ts
@@ -9,8 +9,10 @@ import {
OPENAI_COMPATIBLE_REPLAY_HOOKS,
PASSTHROUGH_GEMINI_REPLAY_HOOKS,
resolveClaudeFable5ModelIdentity,
+ resolveClaudeMythos5ModelIdentity,
resolveClaudeSonnet5ModelIdentity,
resolveClaudeThinkingProfile,
+ requiresClaudeDefaultSampling,
supportsClaudeAdaptiveThinking,
supportsClaudeNativeMaxEffort,
supportsClaudeNativeXhighEffort,
@@ -51,9 +53,24 @@ describe("Claude model contracts", () => {
);
});
+ it("recognizes Claude Mythos 5 as mandatory adaptive with native max effort", () => {
+ expect(resolveClaudeMythos5ModelIdentity({ id: "us.anthropic.claude-mythos-5-v1:0" })).toBe(
+ "claude-mythos-5-v1:0",
+ );
+ expect(supportsClaudeAdaptiveThinking({ id: "claude-mythos-5" })).toBe(true);
+ expect(supportsClaudeNativeMaxEffort({ id: "claude-mythos-5" })).toBe(true);
+ expect(supportsClaudeNativeXhighEffort({ id: "anthropic.claude-mythos-5" })).toBe(true);
+ expect(requiresClaudeDefaultSampling({ id: "claude-mythos-5" })).toBe(true);
+ });
+
it("does not classify later numeric model versions as supported aliases", () => {
expect(supportsClaudeAdaptiveThinking({ id: "claude-sonnet-4-60" })).toBe(false);
+ expect(supportsClaudeAdaptiveThinking({ id: "claude-sonnet-50" })).toBe(false);
+ expect(supportsClaudeAdaptiveThinking({ id: "claude-mythos-50" })).toBe(false);
expect(supportsClaudeNativeXhighEffort({ id: "claude-opus-4-80" })).toBe(false);
+ expect(requiresClaudeDefaultSampling({ id: "claude-opus-4-8" })).toBe(true);
+ expect(requiresClaudeDefaultSampling({ id: "claude-mythos-preview" })).toBe(true);
+ expect(requiresClaudeDefaultSampling({ id: "claude-opus-4-6" })).toBe(false);
expect(readLevelIds(resolveClaudeThinkingProfile("claude-opus-4-80"))).toEqual([
"off",
"minimal",
@@ -318,14 +335,17 @@ describe("resolveClaudeThinkingProfile", () => {
expectLevelIdsInclude(profile, ["off", "xhigh", "adaptive", "max"]);
});
- it("exposes Fable 5's always-adaptive profile to Claude providers", () => {
- const profile = resolveClaudeThinkingProfile("claude-fable-5");
- expectFields(profile, {
- defaultLevel: "high",
- preserveWhenCatalogReasoningFalse: true,
- });
- expectLevelIdsInclude(profile, ["xhigh", "adaptive", "max"]);
- });
+ it.each(["claude-fable-5", "claude-mythos-5"])(
+ "exposes %s's mandatory-adaptive profile to Claude providers",
+ (modelId) => {
+ const profile = resolveClaudeThinkingProfile(modelId);
+ expectFields(profile, {
+ defaultLevel: "high",
+ preserveWhenCatalogReasoningFalse: true,
+ });
+ expectLevelIdsInclude(profile, ["xhigh", "adaptive", "max"]);
+ },
+ );
it("does not match longer unrelated Claude ids by prefix only", () => {
expect(resolveClaudeThinkingProfile("vendor/claude-fable-500").defaultLevel).toBeUndefined();
diff --git a/src/plugin-sdk/provider-model-shared.ts b/src/plugin-sdk/provider-model-shared.ts
index 167e249dbc7f..eb65ea7e4cf1 100644
--- a/src/plugin-sdk/provider-model-shared.ts
+++ b/src/plugin-sdk/provider-model-shared.ts
@@ -29,8 +29,11 @@ export type {
export {
resolveClaudeFable5ModelIdentity,
resolveClaudeModelIdentity,
+ resolveClaudeMythos5ModelIdentity,
resolveClaudeNativeThinkingLevelMap,
resolveClaudeSonnet5ModelIdentity,
+ requiresClaudeDefaultSampling,
+ requiresClaudeMandatoryAdaptiveThinking,
supportsClaudeAdaptiveThinking,
supportsClaudeNativeMaxEffort,
supportsClaudeNativeXhighEffort,
diff --git a/src/plugins/provider-claude-thinking.ts b/src/plugins/provider-claude-thinking.ts
index c6d36c7c286e..ca3aa74fe2b0 100644
--- a/src/plugins/provider-claude-thinking.ts
+++ b/src/plugins/provider-claude-thinking.ts
@@ -6,6 +6,7 @@ import {
CLAUDE_SONNET_5_THINKING_PROFILE,
resolveClaudeFable5ModelIdentity,
resolveClaudeModelIdentity,
+ resolveClaudeMythos5ModelIdentity,
resolveClaudeSonnet5ModelIdentity,
supportsClaudeAdaptiveThinking,
supportsClaudeNativeXhighEffort,
@@ -38,7 +39,7 @@ export function resolveClaudeThinkingProfile(
): ProviderThinkingProfile {
const ref = { id: modelId, params };
const canonicalModelId = resolveClaudeModelIdentity(ref);
- if (resolveClaudeFable5ModelIdentity(ref)) {
+ if (resolveClaudeFable5ModelIdentity(ref) || resolveClaudeMythos5ModelIdentity(ref)) {
return CLAUDE_FABLE_5_THINKING_PROFILE;
}
if (resolveClaudeSonnet5ModelIdentity(ref)) {