diff --git a/agent/anthropic_adapter.py b/agent/anthropic_adapter.py index 4e20ae412eef..5768ae3ea904 100644 --- a/agent/anthropic_adapter.py +++ b/agent/anthropic_adapter.py @@ -127,6 +127,8 @@ _FAST_MODE_SUPPORTED_SUBSTRINGS = ("opus-4-6", "opus-4.6") _ANTHROPIC_OUTPUT_LIMITS = { # Mythos-class named models (claude-fable-5, …) — 1M context, reasoning "claude-fable": 128_000, + # Claude Sonnet 5 + "claude-sonnet-5": 128_000, # Claude 4.8 "claude-opus-4-8": 128_000, # Claude 4.7 diff --git a/agent/bedrock_adapter.py b/agent/bedrock_adapter.py index c0a039aa0323..ab75be917caa 100644 --- a/agent/bedrock_adapter.py +++ b/agent/bedrock_adapter.py @@ -1322,6 +1322,7 @@ def classify_bedrock_error(error_message: str) -> str: BEDROCK_CONTEXT_LENGTHS: Dict[str, int] = { # Anthropic Claude models on Bedrock + "anthropic.claude-sonnet-5": 1_000_000, "anthropic.claude-opus-4-6": 200_000, "anthropic.claude-sonnet-4-6": 200_000, "anthropic.claude-sonnet-4-5": 200_000, diff --git a/agent/model_metadata.py b/agent/model_metadata.py index d2d243116aba..19e58e3d738f 100644 --- a/agent/model_metadata.py +++ b/agent/model_metadata.py @@ -213,6 +213,7 @@ DEFAULT_CONTEXT_LENGTHS = { # OpenRouter-prefixed models resolve via OpenRouter live API or models.dev. "claude-fable-5": 1000000, "claude-fable": 1000000, + "claude-sonnet-5": 1000000, "claude-opus-4-8": 1000000, "claude-opus-4.8": 1000000, "claude-opus-4-7": 1000000, diff --git a/agent/reasoning_timeouts.py b/agent/reasoning_timeouts.py index bebf7825fde8..13df836b1100 100644 --- a/agent/reasoning_timeouts.py +++ b/agent/reasoning_timeouts.py @@ -102,6 +102,7 @@ _REASONING_STALE_TIMEOUT_FLOORS: tuple[tuple[str, int], ...] = ( # ``claude-opus-4`` so non-thinking Claude 3.x or future # non-reasoning Claude variants don't match. ("claude-opus-4", 240), + ("claude-sonnet-5", 180), ("claude-sonnet-4.5", 180), ("claude-sonnet-4.6", 180), # xAI Grok reasoning variants. Explicit reasoning-only keys diff --git a/agent/usage_pricing.py b/agent/usage_pricing.py index 85aa1d180aec..18b3e204308d 100644 --- a/agent/usage_pricing.py +++ b/agent/usage_pricing.py @@ -232,6 +232,18 @@ _OFFICIAL_DOCS_PRICING: Dict[tuple[str, str], PricingEntry] = { source_url="https://platform.claude.com/docs/en/about-claude/pricing", pricing_version="anthropic-pricing-2026-05", ), + ( + "anthropic", + "claude-sonnet-5", + ): PricingEntry( + input_cost_per_million=Decimal("3.00"), + output_cost_per_million=Decimal("15.00"), + cache_read_cost_per_million=Decimal("0.30"), + cache_write_cost_per_million=Decimal("3.75"), + source="official_docs_snapshot", + source_url="https://platform.claude.com/docs/en/about-claude/pricing", + pricing_version="anthropic-pricing-2026-06", + ), ( "anthropic", "claude-sonnet-4-6", @@ -540,6 +552,18 @@ _OFFICIAL_DOCS_PRICING: Dict[tuple[str, str], PricingEntry] = { source_url="https://aws.amazon.com/bedrock/pricing/", pricing_version="bedrock-pricing-2026-04", ), + ( + "bedrock", + "anthropic.claude-sonnet-5", + ): PricingEntry( + input_cost_per_million=Decimal("3.00"), + output_cost_per_million=Decimal("15.00"), + cache_read_cost_per_million=Decimal("0.30"), + cache_write_cost_per_million=Decimal("3.75"), + source="official_docs_snapshot", + source_url="https://aws.amazon.com/bedrock/pricing/", + pricing_version="bedrock-pricing-2026-06", + ), ( "bedrock", "anthropic.claude-sonnet-4-6", diff --git a/hermes_cli/models.py b/hermes_cli/models.py index 3b44fa81b376..75db14f66421 100644 --- a/hermes_cli/models.py +++ b/hermes_cli/models.py @@ -367,6 +367,7 @@ _PROVIDER_MODELS: dict[str, list[str]] = { ], "anthropic": [ "claude-fable-5", + "claude-sonnet-5", "claude-opus-4-8", "claude-opus-4-7", "claude-opus-4-6", @@ -403,6 +404,7 @@ _PROVIDER_MODELS: dict[str, list[str]] = { "deepseek-ai/DeepSeek-V3.2", "moonshotai/Kimi-K2.5", "google/gemini-3.1-flash-lite-preview", + "anthropic/claude-sonnet-5", "anthropic/claude-sonnet-4.6", "openai/gpt-5.4", ], @@ -427,6 +429,7 @@ _PROVIDER_MODELS: dict[str, list[str]] = { "gpt-5-codex", "gpt-5-nano", "claude-fable-5", + "claude-sonnet-5", "claude-opus-4-8", "claude-opus-4-7", "claude-opus-4-6", @@ -535,6 +538,7 @@ _PROVIDER_MODELS: dict[str, list[str]] = { # prefers live discovery via ListFoundationModels + ListInferenceProfiles. # Use inference profile IDs (us.*) since most models require them. "bedrock": [ + "us.anthropic.claude-sonnet-5", "us.anthropic.claude-sonnet-4-6", "us.anthropic.claude-opus-4-6-v1", "us.anthropic.claude-haiku-4-5-20251001-v1:0", @@ -3380,6 +3384,7 @@ _COPILOT_MODEL_ALIASES = { "openai/o3-mini": "gpt-5-mini", "openai/o4-mini": "gpt-5-mini", "anthropic/claude-opus-4.6": "claude-opus-4.6", + "anthropic/claude-sonnet-5": "claude-sonnet-5", "anthropic/claude-sonnet-4.6": "claude-sonnet-4.6", "anthropic/claude-sonnet-4": "claude-sonnet-4", "anthropic/claude-sonnet-4.5": "claude-sonnet-4.5", @@ -3389,12 +3394,14 @@ _COPILOT_MODEL_ALIASES = { # dot-notation. Accept both so users who configure copilot + a # default hyphenated Claude model don't hit HTTP 400 # "model_not_supported". See issue #6879. + "claude-sonnet-5": "claude-sonnet-5", "claude-opus-4-6": "claude-opus-4.6", "claude-sonnet-4-6": "claude-sonnet-4.6", "claude-sonnet-4-0": "claude-sonnet-4", "claude-sonnet-4-5": "claude-sonnet-4.5", "claude-haiku-4-5": "claude-haiku-4.5", "anthropic/claude-opus-4-6": "claude-opus-4.6", + "anthropic/claude-sonnet-5": "claude-sonnet-5", "anthropic/claude-sonnet-4-6": "claude-sonnet-4.6", "anthropic/claude-sonnet-4-0": "claude-sonnet-4", "anthropic/claude-sonnet-4-5": "claude-sonnet-4.5", diff --git a/hermes_cli/setup.py b/hermes_cli/setup.py index 54f4e5f676d5..8754f067d787 100644 --- a/hermes_cli/setup.py +++ b/hermes_cli/setup.py @@ -84,6 +84,7 @@ _DEFAULT_PROVIDER_MODELS = { "gpt-4o", "gpt-4o-mini", "claude-opus-4.6", + "claude-sonnet-5", "claude-sonnet-4.6", "claude-sonnet-4.5", "claude-haiku-4.5", @@ -105,8 +106,8 @@ _DEFAULT_PROVIDER_MODELS = { "arcee": ["trinity-large-thinking", "trinity-large-preview", "trinity-mini"], "minimax": ["MiniMax-M2.7", "MiniMax-M2.5", "MiniMax-M2.1", "MiniMax-M2"], "minimax-cn": ["MiniMax-M2.7", "MiniMax-M2.5", "MiniMax-M2.1", "MiniMax-M2"], - "kilocode": ["anthropic/claude-opus-4.6", "anthropic/claude-sonnet-4.6", "openai/gpt-5.4", "google/gemini-3-pro-preview", "google/gemini-3-flash-preview"], - "opencode-zen": ["gpt-5.4", "gpt-5.3-codex", "claude-sonnet-4-6", "gemini-3-flash", "glm-5", "kimi-k2.5", "minimax-m2.7"], + "kilocode": ["anthropic/claude-sonnet-5", "anthropic/claude-opus-4.6", "anthropic/claude-sonnet-4.6", "openai/gpt-5.4", "google/gemini-3-pro-preview", "google/gemini-3-flash-preview"], + "opencode-zen": ["gpt-5.4", "gpt-5.3-codex", "claude-sonnet-5", "claude-sonnet-4-6", "gemini-3-flash", "glm-5", "kimi-k2.5", "minimax-m2.7"], "opencode-go": ["kimi-k2.6", "kimi-k2.5", "glm-5.1", "glm-5", "mimo-v2.5-pro", "mimo-v2.5", "mimo-v2-pro", "mimo-v2-omni", "minimax-m2.7", "minimax-m2.5", "qwen3.7-max", "qwen3.6-plus", "qwen3.5-plus"], "huggingface": [ "Qwen/Qwen3.5-397B-A17B", "Qwen/Qwen3-235B-A22B-Thinking-2507", diff --git a/tests/agent/test_minimax_provider.py b/tests/agent/test_minimax_provider.py index 2dd1510786e7..1152514c1e52 100644 --- a/tests/agent/test_minimax_provider.py +++ b/tests/agent/test_minimax_provider.py @@ -330,6 +330,7 @@ class TestMinimaxMaxOutput: from agent.anthropic_adapter import _get_anthropic_max_output # Sanity: Claude limits are not broken by the MiniMax entry assert _get_anthropic_max_output("claude-sonnet-4-6") == 64_000 + assert _get_anthropic_max_output("claude-sonnet-5") == 128_000 class TestMinimaxPreserveDots: diff --git a/tests/hermes_cli/test_anthropic_picker_curated.py b/tests/hermes_cli/test_anthropic_picker_curated.py index 407bb362b1f2..6465a87396a0 100644 --- a/tests/hermes_cli/test_anthropic_picker_curated.py +++ b/tests/hermes_cli/test_anthropic_picker_curated.py @@ -20,6 +20,7 @@ def test_anthropic_curated_alias_survives_when_live_omits_it(): """A curated alias missing from /v1/models still surfaces (first).""" curated = M._PROVIDER_MODELS["anthropic"] assert "claude-fable-5" in curated # sanity: the alias is curated + assert "claude-sonnet-5" in curated # newest Sonnet alias is curated # Live catalog the API would actually return — no fable-5. live = ["claude-opus-4-8", "claude-sonnet-4-6", "claude-haiku-4-5-20251001"] @@ -27,6 +28,7 @@ def test_anthropic_curated_alias_survives_when_live_omits_it(): result = M.provider_model_ids("anthropic") assert "claude-fable-5" in result + assert "claude-sonnet-5" in result # Curated order is preserved at the front. assert result[:len(curated)] == list(curated)