# NousResearch/hermes-agent · Dash vs dot notation in Anthropic model IDs across providers

## Open now

- 2 issues, 1 PR open on GitHub.
- #45362: nous provider: dash-style model ids (anthropic/claude-sonnet-4-6) 404 where Nous needs dot-style (…4.6); catalog also lists chat-uncallable models (claude-fable-5) — opaque 'Couldn't find that, sorry.'
- #65152: _find_openrouter_slug fails on Anthropic-native dash IDs (e.g. claude-opus-4-8), causing HTTP 400 not a valid model ID
- PR #45532: fix: nous provider dash-vs-dot model ID format and uncallable models

Multiple providers (Copilot, OpenRouter, Nous) reject or misroute Anthropic model IDs when submitted in dash-notation (e.g. claude-opus-4-8) instead of dot-notation (e.g. claude-opus-4.8). The issues and accompanying fixes address normalization and aliasing of these ID formats across different provider integrations.

## TL;DR

- 8 member(s): 2 open issue(s), 1 closed issue(s), 1 open PR(s), 1 merged PR(s), 3 PR(s) closed without merging.
- A fix was merged to main in PR #11561. Verify it addresses your case.
- **Recommended:** read PR #11561 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `partially_resolved` · deciding PR(s): `pr-11561`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_PARTIAL`): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
- Members outside that structural core may belong to a different underlying problem.
- Pairwise link density `0.20`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Open related issues

- #45362: nous provider: dash-style model ids (anthropic/claude-sonnet-4-6) 404 where Nous needs dot-style (…4.6); catalog also lists chat-uncallable models (claude-fable-5) — opaque 'Couldn't find that, sorry.'
- #65152: _find_openrouter_slug fails on Anthropic-native dash IDs (e.g. claude-opus-4-8), causing HTTP 400 not a valid model ID

## Closed / duplicate issues

- #6879: \[Bug\]: Copilot provider rejects Claude model names in dash-notation (HTTP 400)

## Prior & competing PRs

- **#6743: fix(copilot): add dash-notation aliases for Claude model IDs**. closed without merge · fix tier `suggested`
  - maintainer direction: category `merged_via`; implication `superseded`
- **#11561: fix(copilot): normalize vendor-prefixed and dash-notation model IDs (#6879)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `hermes_cli/model_normalize.py`, `hermes_cli/models.py`, `tests/hermes_cli/test_model_normalize.py`
  - fix hunks:
  - `hermes_cli/model_normalize.py`

    ```diff
    @@ -374,7 +374,26 @@ def normalize_model_for_provider(model_input: str, target_provider: str) -> str:
                 return bare
             return _dots_to_hyphens(bare)
     
    -    # --- Copilot: strip matching provider prefix, keep dots ---
    +    # --- Copilot / Copilot ACP: delegate to the Copilot-specific
    +    #     normalizer.  It knows about the alias table (vendor-prefix
    +    #     stripping for Anthropic/OpenAI, dash-to-dot repair for Claude)
    +    #     and live-catalog lookups.  Without this, vendor-prefixed or
    +    #     dash-notation Claude IDs survive to the Copilot API and hit
    +    #     HTTP 400 "model_not_supported".  See issue #6879.
    +    if provider in {"copilot", "copilot-acp"}:
    +        try:
    +            from hermes_cli.models import normalize_copilot_model_id
    +
    +            normalized = normalize_copilot_model_id(name)
    +            if normalized:
    +                return normalized
    +        except Exception:
    +            # Fall through to the generic strip-vendor behaviour below
    +            # if the Copilot-specific path is unavailable for any reason.
    +            pass
    +
    +    # --- Copilot / Copilot ACP / openai-codex fallback:
    +    #     strip matching provider prefix, keep dots ---
         if provider in _STRIP_VENDOR_ONLY_PROVIDERS:
             stripped = _strip_matching_provider_prefix(name, provider)
             if stripped == name and name.startswith("openai/"):
    ```
  - `hermes_cli/models.py`

    ```diff
    @@ -1488,6 +1488,19 @@ def _fetch_github_models(api_key: [redacted:secret-assignment] = None, timeout: float = 5.0) ->
         "anthropic/claude-sonnet-4.6": "claude-sonnet-4.6",
         "anthropic/claude-sonnet-4.5": "claude-sonnet-4.5",
         "anthropic/claude-haiku-4.5": "claude-haiku-4.5",
    +    # Dash-notation fallbacks: Hermes' default Claude IDs elsewhere use
    +    # hyphens (anthropic native format), but Copilot's API only accepts
    +    # 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-opus-4-6": "claude-opus-4.6",
    +    "claude-sonnet-4-6": "claude-sonnet-4.6",
    +    "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-4-6": "claude-sonnet-4.6",
    +    "anthropic/claude-sonnet-4-5": "claude-sonnet-4.5",
    +    "anthropic/claude-haiku-4-5": "claude-haiku-4.5",
     }
     
     
    ```
  - `tests/hermes_cli/test_model_normalize.py`

    ```diff
    @@ -93,6 +93,59 @@ def test_copilot_preserves_dots(self, model, expected):
             assert result == expected
     
     
    +# ── Copilot model-name normalization (issue #6879 regression) ──────────
    +
    +class TestCopilotModelNormalization:
    +    """Copilot requires bare dot-notation model IDs.
    +
    +    Regression coverage for issue #6879 and the broken Copilot branch
    +    that previously left vendor-prefixed Anthropic IDs (e.g.
    +    ``anthropic/claude-sonnet-4.6``) and dash-notation Claude IDs (e.g.
    +    ``claude-sonnet-4-6``) unchanged, causing the Copilot API to reject
    +    the request with HTTP 400 "model_not_supported".
    +    """
    +
    +    @pytest.mark.parametrize("model,expected", [
    +        # Vendor-prefixed Anthropic IDs — prefix must be stripped.
    +        ("anthropic/claude-opus-4.6",   "claude-opus-4.6"),
    +        ("anthropic/claude-sonnet-4.6", "claude-sonnet-4.6"),
    +        ("anthropic/claude-sonnet-4.5", "claude-sonnet-4.5"),
    +        ("anthropic/claude-haiku-4.5",  "claude-haiku-4.5"),
    +        # Vendor-prefixed OpenAI IDs — prefix must be stripped.
    +        ("openai/gpt-5.4",              "gpt-5.4"),
    +        ("openai/gpt-4o",               "gpt-4o"),
    +        ("openai/gpt-4o-mini",          "gpt-4o-mini"),
    +        # Dash-notation Claude IDs — must be converted to dot-notation.
    +        ("claude-opus-4-6",             "claude-opus-4.6"),
    +        ("claude-sonnet-4-6",           "claude-sonnet-4.6"),
    +        ("claude-sonnet-4-5",           "claude-sonnet-4.5"),
    +        ("claude-haiku-4-5",            "claude-haiku-4.5"),
    +        # Combined: vendor-prefixed + dash-notation.
    +        ("anthropic/claude-opus-4-6",   "claude-opus-4.6"),
    +        ("anthropic/claude-sonnet-4-6", "claude-sonnet-4.6"),
    +        # Already-canonical inputs pass through unchanged.
    +        ("claude-sonnet-4.6",           "claude-sonnet-4.6"),
    +        ("gpt-5.4",                     "gpt-5.4"),
    +        ("gpt-5-mini",                  "gpt-5-mini"),
    +    ])
    +    def test_copilot_normalization(self, model, expected):
    +        assert normalize_model_for_provider(model, "copilot") == expected
    +
    +    @pytest.mark.parametrize("model,expected", [
    +        ("anthropic/claude-sonnet-4.6", "claude-sonnet-4.6"),
    +        ("claude-sonnet-4-6",           "claude-sonnet-4.6"),
    +        ("claude-opus-4-6",             "claude-opus-4.6"),
    +        ("openai/gpt-5.4",              "gpt-5.4"),
    +    ])
    +    def test_copilot_acp_normalization(self, model, expected):
    +        """Copilot ACP shares the same API expectations as HTTP Copilot."""
    +        assert normalize_model_for_provider(model, "copilot-acp") == expected
    +
    +    def test_openai_codex_still_strips_openai_prefix(self):
    +        """Regression: openai-codex must still strip the openai/ prefix."""
    +        assert normalize_model_for_provider("openai/gpt-5.4", "openai-codex") == "gpt-5.4"
    +
    +
     # ── Aggregator providers (regression) ──────────────────────────────────
     
     class TestAggregatorProviders:
    ```
- **#65167: fix(models): resolve dash-form model IDs against dotted OpenRouter catalog (#65152)**. closed without merge · fix tier `suggested`
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `148a625ec`, `#65152`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#45405: fix(models): normalize Anthropic dash-style IDs to dot-style for aggregator providers**. closed without merge
  - maintainer direction: category `duplicate_of`; implication `duplicate`; refs `#45367`
- **#45532: fix: nous provider dash-vs-dot model ID format and uncallable models**. open
  - maintainer direction: category `uncategorized`; implication `needs_llm`
  - files: `agent/agent_init.py`, `hermes_cli/auth.py`, `hermes_cli/model_normalize.py`

## Fix status

- Merged to main in PR #11561 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-04-17`. This confirmation is old and may be out of date (a revert/reopen would not have invalidated it here); re-verify.
- Subsystem: `agent`
