Files
Teknium 1da2eb42dc refactor!: remove GitHub Copilot from core (now the github-copilot labs plugin)
Delete the in-tree model-provider plugins plugins/model-providers/copilot/
(provider `copilot`: Copilot API via the GitHub->Copilot token exchange) and
plugins/model-providers/copilot-acp/ (provider `copilot-acp`: Copilot CLI over
ACP). Both ship as ONE plugin, `github-copilot`, in
NousResearch/hermes-labs-plugins; the LEFT_CORE row added before this commit
installs it for every profile that routes through copilot/copilot-acp, and
every "unknown provider" surface names `hermes plugins install
github-copilot`. Core keeps no Copilot code: the generic seams the plugins
fill stay, and agent/copilot_acp_client.py stays as the deprecated alias the
kimchi-acp catalog plugin subclasses (agent.acp_client.ACPClient is the
public base). This commit is revertible on its own: reverting it restores the
bundled providers (the labs plugin stays inert on a core that bundles them).

Test files that only tested the plugins move to the labs plugin's tests:
  tests/plugins/model_providers/test_copilot_acp_client.py
  tests/plugins/model_providers/test_copilot_acp_deprecation.py
  tests/plugins/model_providers/test_copilot_auth.py
  tests/plugins/model_providers/test_copilot_catalog_oauth_fallback.py
  tests/plugins/model_providers/test_copilot_context.py
  tests/plugins/model_providers/test_copilot_custom_model_ids.py
  tests/plugins/model_providers/test_copilot_hosts.py
  tests/plugins/model_providers/test_copilot_pool_exchange.py
  tests/plugins/model_providers/test_copilot_profile.py
  tests/plugins/model_providers/test_copilot_refresh_notice.py
  tests/plugins/model_providers/test_copilot_setup.py
  tests/plugins/model_providers/test_copilot_token_exchange.py
  tests/agent/test_copilot_initiator.py
  tests/agent/test_copilot_native_vision_headers.py
  tests/hermes_cli/test_copilot_in_model_list.py
  tests/hermes_cli/test_copilot_model_api_mode.py
  tests/hermes_cli/test_copilot_runtime_api_mode.py
  tests/hermes_cli/test_copilot_runtime_api_mode_stale.py
  tests/hermes_cli/test_model_switch_copilot_api_mode.py
  tests/e2e/core/providers/test_native_copilot_acp.py
  tests/e2e/core/providers/test_native_copilot_acp_errors.py
  tests/e2e/core/providers/test_native_copilot_acp_streaming.py
  tests/fakes/providers/copilot_acp.py

Copilot-behaviour cases extracted from generic test files (same move):
  test_credential_pool (Copilot pool seeding / gh / env suppression),
  test_reasoning_carriers (Copilot carriers), test_run_agent (Copilot gpt-5
  reasoning, xhigh), test_run_agent_codex_responses (Copilot 401 refresh),
  test_codex_transport (Copilot reasoning), test_auxiliary_main_first (vision
  headers), test_private_credential_writers (JWT store row),
  test_provider_client_seam (copilot-acp client), test_provider_wire_snapshot
  + its fixture (copilot / github-models cells), test_api_key_providers,
  test_auth_commands, test_credential_pool_off_loop (exchange single-flight),
  test_external_process_auth_status (Copilot CLI token stores),
  test_external_process_provider_seam, test_model_cache_fallback_rows,
  test_model_validation, test_models_relay_base_url (copilot row),
  test_multiplex_cli_cache_scope, test_provider_catalog, test_cold_start_gil_stall.

Tests that used Copilot only as an example provider now use a fake
ProviderProfile (or another in-tree provider) with the same assertions:
  test_auxiliary_client, test_auxiliary_named_custom_providers,
  test_init_fallback_on_exhausted_pool, test_intent_ack_continuation,
  test_models_dev, test_turn_retry_state, test_one_turn_route_publish,
  test_mcp_catalog_env_boundary, test_plugin_provider_picker_residue,
  test_delegate_external_provider, test_delegation_profile_route,
  test_overlay_slug_resolution, test_installed_plugin_discovery, plus the
  generic halves of test_run_agent_codex_responses, test_codex_transport,
  test_api_key_providers, test_external_process_auth_status and
  test_model_cache_fallback_rows.
2026-10-25 20:57:22 -07:00
..
…
…
…

Model Provider Plugins

Each subdirectory is a self-contained provider profile plugin. The directory layout mirrors plugins/platforms/:

plugins/model-providers/
├── openrouter/
│   ├── __init__.py      # registers the ProviderProfile
│   └── plugin.yaml      # manifest: name, kind, version, description
├── anthropic/
│   ├── __init__.py
│   └── plugin.yaml
└── ...

How discovery works

providers/__init__.py._discover_providers() scans this directory (and $HERMES_HOME/plugins/model-providers/) the first time anything calls get_provider_profile() or list_providers(). Each __init__.py is imported and expected to call providers.register_provider(profile).

User plugins at $HERMES_HOME/plugins/model-providers/<name>/ override bundled plugins of the same name — last-writer-wins in register_provider(). Drop a file there to replace a built-in.

Adding a new provider

  1. Create plugins/model-providers/<your_provider>/__init__.py:

    from providers import register_provider
    from providers.base import ProviderProfile
    
    my_provider = ProviderProfile(
        name="your-provider",
        aliases=("alias1", "alias2"),
        display_name="Your Provider",
        description="One-line description shown in the setup picker",
        signup_url="https://your-provider.example.com/keys",
        env_vars=("YOUR_PROVIDER_API_KEY", "YOUR_PROVIDER_BASE_URL"),
        base_url="https://api.your-provider.example.com/v1",
        default_aux_model="your-cheap-model",
    )
    
    register_provider(my_provider)
    
  2. Create plugins/model-providers/<your_provider>/plugin.yaml:

    name: your-provider-profile
    kind: model-provider
    version: 1.0.0
    description: Short sentence about the provider
    author: Your Name
    

Nothing else needs to change. auth.py, config.py, models.py, doctor.py, model_metadata.py, runtime_provider.py, and the chat_completions transport all auto-wire from the registry.

Non-trivial profiles

Override the ProviderProfile hooks in a subclass for per-provider quirks — see plugins/model-providers/openrouter/__init__.py for build_extra_body and build_api_kwargs_extras examples, and plugins/model-providers/gemini/__init__.py for thinking_config translation.