# NousResearch/hermes-agent · delegate_task per-call model and provider overrides

## Open now

- 4 issues, 1 PR open on GitHub.
- #4928: Feature: add named delegation capability profiles for subagents
- #41889: Feature Request: Cross-profile subagent support in delegate_task
- #51540: delegation.model ignored for opencode-go provider
- #67699: feature request: delegate_task should pass env vars or accept a profile parameter
- PR #58337: fix(delegate): keep child credential pools provider-scoped

These issues and PRs concern adding per-call model, provider, and profile override parameters to the delegate_task tool, along with fixes for bugs where delegation ignores configuration and subagents inherit the parent model or incorrect credentials instead of the intended settings.

## TL;DR

- 105 member(s): 4 open issue(s), 56 closed issue(s), 1 open PR(s), 5 merged PR(s), 39 PR(s) closed without merging.
- A fix was merged to main in PR #7360. Verify it addresses your case.
- **Recommended:** read PR #7360 (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-16894,pr-41730`. 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.03`: 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

- #4928: Feature: add named delegation capability profiles for subagents
- #41889: Feature Request: Cross-profile subagent support in delegate_task
- #51540: delegation.model ignored for opencode-go provider
- #67699: feature request: delegate_task should pass env vars or accept a profile parameter

## Closed / duplicate issues

- #609: Allow configurable subagent model in config.yaml
- #3719: Feature: Per-call model/provider override on delegate_task
- #5012: \[Feature\]: Allow delegate_task to accept provider/model parameters
- #6306: Feature: Allow per-task model selection in delegate_task
- #6825: Bug: delegation.base_url correctly resolved but overwritten before first API call
- #7833: delegate_task can overwrite delegated custom endpoint with parent custom credential pool
- #9712: delegate_task(acp_command=...) silently ignored under Anthropic parent
- #10653: \[Bug\]: delegation config doesn\`t work
- #10995: feat: delegate_task should support per-task model selection
- #11999: delegate_task ignores subagent model config — always inherits parent model
- #14974: \[Feature\]: delegation-per-task-endpoint-overrides
- #15789: feat: per-task model/provider overrides in delegate_task
- #16816: \[Bug\]: Add delegation.acp_command: none config option
- #17685: delegate_task: per-task model override is silently ignored — children always use parent's model
- #17732: Feature: per-call model/provider override in delegate_task
- #18591: Feature: Per-task model override for delegate_task subagents
- #19567: TUI delegation subagent incorrectly routed to copilot-acp instead of configured deepseek provider
- #23467: delegate_task model parameter silently discarded — subagents always inherit parent model
- #24263: Feature: Per-sub-agent provider/model selection in delegate_task
- #26482: delegation model override ignored — subagents always inherit parent provider/model
- #27294: \[Bug\] delegate_task ignores delegation.base_url — subagent always inherits parent model
- #27295: \[Bug\] delegate_task ignores delegation.base_url — subagent always inherits parent model
- #27296: \[Bug\] delegate_task ignores delegation.base_url — subagent always inherits parent model
- #31155: delegation.model override ignored — subagents always inherit parent model
- #32711: delegate_task() lacks model and provider parameters — prevents per-call subagent routing
- #34318: delegate_task sends API key to wrong provider endpoint (OpenRouter key → OpenAI)
- #34334: Optimization: Correct API key routing for delegate_task
- #34462: \[TASK\] Implement Per-Call Provider and Model Overrides for delegate_task
- #34489: Per-Task Model Override in delegate_task
- #34727: \[FEATURE\] Task Delegation + Intelligent Model Selection (Phases 7 & 3)
- #34764: feat(delegate): per-task model/provider override in delegate_task tasks array
- #35409: Add profile/model override parameter to delegate_task
- #35437: feat: add model/provider/base_url/api_format parameters to delegate_task (consolidating #5012, #10995, #17685, #23467, #32711)
- #37574: feat: add model/provider override param to delegate_task tool
- #41814: Feature: Per-task model override for delegate_task
- #44900: feat(delegation): add model override to delegate_task
- #46295: Feature Request: Add subagent configuration to Model page with provider selection
- #47014: Feature Request: Per-call model override for delegate_task
- #47295: feat: per-call model routing in delegate_task (multi-model delegation)
- #49332: delegate_task model override ignored — subagents use wrong model, consume unauthorized credits
- #50202: \[Feature\]: per-call model/provider override for delegate_task (multi-model subagent orchestration)
- #51303: Custom providers not recognized by delegate_task delegation system  
- #51401: Feature Request: Per-Task Model Override in delegate_task
- #52331: \[Feature\]: Per-call model routing for delegate_task subagents
- #53064: delegation.provider=custom:* resolves wrong base_url (inherits parent provider base_url)
- #53846: delegate_task model parameter not honored — child agents always inherit parent session model
- #55380: Bug: delegate_task ignores delegation config — hardcoded to xiaomi
- #56891: Feature: Expose model parameter on delegate_task for per-call model selection
- #58298: Subagent delegation ignores config, always uses credential-pool model (glm-4-flash)
- #58731: Feature Request: Per-subagent model override in delegate_task
- #61195: \[Bug\]: delegation.base_url correctly resolved through init_agent() but subagent's actual API call still routes to OpenRouter → 401
- #61622: feat: support per-subagent model selection in delegate_task
- #62443: delegate_task subagent always resolves to Groq/llama-3.3-70b-versatile (401) regardless of delegation config
- #62731: Feature Request: Allow delegate_task to use different models per subtask
- #66536: \[Feature\]: Per-call model/provider override for delegate_task
- #66819: Feature request: Role-based subagents — inherit profile identity (SOUL.md + skills + tools)

## Prior & competing PRs

- **#7360: fix(delegate): salvage delegate PRs #6933 #6881**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `run_agent.py`, `tests/run_agent/test_agent_guardrails.py`, `tests/tools/test_delegate.py`, `tools/delegate_tool.py`
- **#10996: feat(tools): add model parameter to delegate_task for per-task model selection**. closed without merge · fix tier `suggested`
  - maintainer direction: category `uncategorized`; implication `needs_llm`; refs `#9737`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#12105: fix(delegation): _load_config falls through to hermes_cli config when CLI_CONFIG has no delegation overrides (#11999)**. closed without merge · fix tier `suggested`
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `#11999`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#16894: fix(delegate): clear acp_command when override_provider is set**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `tools/delegate_tool.py`
- **#17581: Fix delegate_task model/provider precedence and expose per-task overrides**. closed without merge · fix tier `suggested`
  - maintainer direction: category `uncategorized`; implication `needs_llm`; refs `a6f08ff0c`, `#10995`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#17718: fix: honor per-task model override in delegate_task**. closed without merge · fix tier `suggested`
  - maintainer direction: category `uncategorized`; implication `needs_llm`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#19744: fix(delegation): clear parent OpenRouter filters when delegation.provider override is set**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `tests/tools/test_delegate.py`, `tools/delegate_tool.py`
  - fix hunks:
  - `tests/tools/test_delegate.py`

    ```diff
    @@ -980,6 +980,48 @@ def test_cross_provider_delegation(self, mock_creds, mock_cfg):
                 self.assertNotEqual(kwargs["base_url"], parent.base_url)
                 self.assertNotEqual(kwargs["api_key"], parent.api_key)
     
    +    @patch("tools.delegate_tool._load_config")
    +    @patch("tools.delegate_tool._resolve_delegation_credentials")
    +    def test_provider_override_clears_parent_openrouter_filters(
    +        self, mock_creds, mock_cfg
    +    ):
    +        """Delegated provider should not inherit parent provider-preference filters."""
    +        mock_cfg.return_value = {
    +            "max_iterations": 45,
    +            "model": "google/gemini-3-flash-preview",
    +            "provider": "openrouter",
    +        }
    +        mock_creds.return_value = {
    +            "model": "google/gemini-3-flash-preview",
    +            "provider": "openrouter",
    +            "base_url": "https://openrouter.ai/api/v1",
    +            "api_key": "sk-or-key",
    +            "api_mode": "chat_completions",
    +        }
    +        parent = _make_mock_parent(depth=0)
    +        parent.providers_allowed = ["anthropic/claude-3.5-sonnet"]
    +        parent.providers_ignored = ["openai/gpt-4o-mini"]
    +        parent.providers_order = ["google/gemini-2.5-pro"]
    +        parent.provider_sort = "price"
    +
    +        with patch("run_agent.AIAgent") as MockAgent:
    +            mock_child = MagicMock()
    +            mock_child.run_conversation.return_value = {
    +                "final_response": "done",
    +                "completed": True,
    +                "api_calls": 1,
    +            }
    +            MockAgent.return_value = mock_child
    +
    +            delegate_task(goal="Cross-provider test", parent_agent=parent)
    +
    +            _, kwargs = MockAgent.call_args
    +            self.assertEqual(kwargs["provider"], "openrouter")
    +            self.assertIsNone(kwargs["providers_allowed"])
    +            self.assertIsNone(kwargs["providers_ignored"])
    +            self.assertIsNone(kwargs["providers_order"])
    +            self.assertIsNone(kwargs["provider_sort"])
    +
         @patch("tools.delegate_tool._load_config")
         @patch("tools.delegate_tool._resolve_delegation_credentials")
         def test_direct_endpoint_credentials_reach_child_agent(self, mock_creds, mock_cfg):
    ```
  - `tools/delegate_tool.py`

    ```diff
    @@ -1032,6 +1032,23 @@ def _child_thinking(text: str) -> None:
         # fallback_model parameter (which handles both list and dict forms).
         parent_fallback = getattr(parent_agent, "_fallback_chain", None) or None
     
    +    # Inherit the parent's OpenRouter provider-preference filters by default
    +    # (so subagents routed to the same provider honour the same routing
    +    # constraints).  BUT: when `delegation.provider` is set the user is
    +    # explicitly asking the child to run on a different provider, and
    +    # parent-level OpenRouter filters (e.g. `only=["Anthropic"]`) would
    +    # silently force the child back onto the parent's provider. Clear the
    +    # filters in that case so the delegated provider is honoured.
    +    child_providers_allowed = getattr(parent_agent, "providers_allowed", None)
    +    child_providers_ignored = getattr(parent_agent, "providers_ignored", None)
    +    child_providers_order = getattr(parent_agent, "providers_order", None)
    +    child_provider_sort = getattr(parent_agent, "provider_sort", None)
    +    if override_provider:
    +        child_providers_allowed = None
    +        child_providers_ignored = None
    +        child_providers_order = None
    +        child_provider_sort = None
    +
         child = AIAgent(
             base_url=effective_base_url,
             api_key=[redacted:secret-assignment]
    ```

    ```diff
    @@ -1056,10 +1073,10 @@ def _child_thinking(text: str) -> None:
             thinking_callback=child_thinking_cb,
             session_db=getattr(parent_agent, "_session_db", None),
             parent_session_id=getattr(parent_agent, "session_id", None),
    -        providers_allowed=parent_agent.providers_allowed,
    -        providers_ignored=parent_agent.providers_ignored,
    -        providers_order=parent_agent.providers_order,
    -        provider_sort=parent_agent.provider_sort,
    +        providers_allowed=child_providers_allowed,
    +        providers_ignored=child_providers_ignored,
    +        providers_order=child_providers_order,
    +        provider_sort=child_provider_sort,
             tool_progress_callback=child_progress_cb,
             iteration_budget=None,  # fresh budget per subagent
         )
    ```
- **#37578: feat: add model/provider override param to delegate_task tool**. closed without merge · fix tier `suggested`
  - maintainer direction: category `uncategorized`; implication `needs_llm`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#39282: feat(delegate_task): add per-task model/provider override**. closed without merge · fix tier `suggested`
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#41730: fix(delegate): resolve custom-endpoint subagent pools by endpoint identity**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `agent/credential_pool.py`, `tests/tools/test_delegate.py`, `tools/delegate_tool.py`
  - fix hunks:
  - `agent/credential_pool.py`

    ```diff
    @@ -374,7 +374,7 @@ def _iter_custom_providers(config: Optional[dict] = None):
             yield _normalize_custom_pool_name(name), entry
     
     
    -def get_custom_provider_pool_key(base_url: str, provider_name: Optional[str] = None) -> Optional[str]:
    +def get_custom_provider_pool_key(base_url: Optional[str], provider_name: Optional[str] = None) -> Optional[str]:
         """Look up the custom_providers list in config.yaml and return 'custom:<name>' for a matching base_url.
     
         When provider_name is given, prefer matching by name first (solving the case where
    ```
  - `tests/tools/test_delegate.py`

    ```diff
    @@ -1518,6 +1518,73 @@ def test_different_provider_load_failure_returns_none(self):
     
             self.assertIsNone(result)
     
    +    # --- Custom-endpoint identity resolution (issue #7833) ---
    +
    +    def test_custom_different_endpoint_does_not_inherit_parent_pool(self):
    +        """A child on custom endpoint B must not inherit the parent's custom
    +        endpoint A pool just because both normalize to provider='custom'."""
    +        parent = _make_mock_parent()
    +        parent.provider = "custom"
    +        parent.base_url = "https://endpoint-a.example.com/v1"
    +        parent._credential_pool = [redacted:secret-assignment]"parent_custom_a_pool")
    +
    +        child_pool = MagicMock(name="endpoint_b_pool")
    +        child_pool.has_credentials.return_value = True
    +
    +        def fake_key(base_url, provider_name=None):
    +            return {
    +                "https://endpoint-a.example.com/v1": "custom:endpoint-a",
    +                "https://endpoint-b.example.com/v1": "custom:endpoint-b",
    +            }.get(base_url)
    +
    +        with patch("agent.credential_pool.get_custom_provider_pool_key", side_effect=fake_key), \
    +             patch("agent.credential_pool.load_pool", return_value=child_pool) as load_mock:
    +            result = _resolve_child_credential_pool(
    +                "custom", parent, "https://endpoint-b.example.com/v1"
    +            )
    +
    +        # Loaded the child's OWN endpoint pool, not the parent's.
    +        load_mock.assert_called_once_with("custom:endpoint-b")
    +        self.assertIs(result, child_pool)
    +        self.assertIsNot(result, parent._credential_pool)
    +
    +    def test_custom_same_endpoint_shares_parent_pool(self):
    +        """A child on the SAME custom endpoint as the parent reuses the parent's
    +        pool so rotation/cooldown state stays synchronized."""
    +        parent = _make_mock_parent()
    +        parent.provider = "custom"
    +        parent.base_url = "https://endpoint-a.example.com/v1"
    +        parent._credential_pool = [redacted:secret-assignment]"parent_custom_a_pool")
    +
    +        with patch(
    +            "agent.credential_pool.get_custom_provider_pool_key",
    +            return_value="custom:endpoint-a",
    +        ):
    +            result = _resolve_child_credential_pool(
    +                "custom", parent, "https://endpoint-a.example.com/v1"
    +            )
    +
    +        self.assertIs(result, parent._credential_pool)
    +
    +    def test_custom_unregistered_endpoint_returns_none(self):
    +        """A raw delegation.base_url with no matching custom_providers entry
    +        must NOT inherit the parent's pool — return None so the child keeps its
    +        fixed delegated credential."""
    +        parent = _make_mock_parent()
    +        parent.provider = "custom"
    +        parent.base_url = "https://endpoint-a.example.com/v1"
    +        parent._credential_pool = [redacted:secret-assignment]"parent_custom_a_pool")
    +
    +        with patch(
    +            "agent.credential_pool.get_custom_provider_pool_key",
    +            return_value=None,
    +        ):
    +            result = _resolve_child_credential_pool(
    +                "custom", parent, "https://raw-unregistered.example.com/v1"
    +            )
    +
    +        self.assertIsNone(result)
    +
         def test_build_child_agent_assigns_parent_pool_when_shared(self):
             parent = _make_mock_parent()
             mock_pool = MagicMock()
    ```
  - `tools/delegate_tool.py`

    ```diff
    @@ -1184,7 +1184,9 @@ def _child_thinking(text: str) -> None:
     
         # Share a credential pool with the child when possible so subagents can
         # rotate credentials on rate limits instead of getting pinned to one key.
    -    child_pool = _resolve_child_credential_pool(effective_provider, parent_agent)
    +    child_pool = _resolve_child_credential_pool(
    +        effective_provider, parent_agent, effective_base_url
    +    )
         if child_pool is not None:
             child._credential_pool = [redacted:secret-assignment]
     
    ```

    ```diff
    @@ -2368,7 +2370,11 @@ def delegate_task(
         )
     
     
    -def _resolve_child_credential_pool(effective_provider: Optional[str], parent_agent):
    +def _resolve_child_credential_pool(
    +    effective_provider: Optional[str],
    +    parent_agent,
    +    effective_base_url: Optional[str] = None,
    +):
         """Resolve a credential pool for the child agent.
     
         Rules:
    ```

    ```diff
    @@ -2377,12 +2383,60 @@ def _resolve_child_credential_pool(effective_provider: Optional[str], parent_age
         2. Different provider -> try to load that provider's own pool.
         3. No pool available -> return None and let the child keep the inherited
            fixed credential behavior.
    +
    +    Custom endpoints are a special case: every direct ``delegation.base_url``
    +    runtime collapses to ``provider="custom"``, so bare provider equality would
    +    treat two *different* custom endpoints as interchangeable and let the child
    +    inherit the parent's pool. Leasing from that pool then overwrites the
    +    child's delegated ``base_url`` with the parent's endpoint (issue #7833).
    +    We therefore resolve custom runtimes by endpoint identity (the
    +    ``custom:<name>`` pool key derived from the base_url) and only share the
    +    parent's pool when both resolve to the *same* custom endpoint.
         """
         if not effective_provider:
             return getattr(parent_agent, "_credential_pool", None)
     
         parent_provider = getattr(parent_agent, "provider", None) or ""
         parent_pool = getattr(parent_agent, "_credential_pool", None)
    +
    +    # Custom endpoints: distinguish by endpoint identity, not the bare "custom"
    +    # provider string. Two custom runtimes are only interchangeable when they
    +    # resolve to the same custom:<name> pool key.
    +    if effective_provider == "custom":
    +        try:
    +            from agent.credential_pool import get_custom_provider_pool_key, load_pool
    +
    +            child_key = get_custom_provider_pool_key(effective_base_url)
    +            if child_key is None:
    +                # Unregistered endpoint (raw delegation.base_url with no
    +                # matching custom_providers entry) -> no shared pool exists.
    +                # Keep the child's fixed delegated credential rather than
    +                # risk inheriting the parent's custom endpoint.
    +                return None
    +
    +            # Reuse the parent's pool only when it is the same custom endpoint.
    +            parent_key = get_custom_provider_pool_key(
    +                getattr(parent_agent, "base_url", None)
    +            )
    +            if (
    +                parent_pool is not None
    +                and parent_provider == "custom"
    +                and parent_key is not None
    +                and parent_key == child_key
    +            ):
    +                return parent_pool
    +
    +            pool = load_pool(child_key)
    +            if pool is not None and pool.has_credentials():
    +                return pool
    +        except Exception as exc:
    +            logger.debug(
    +                "Could not resolve custom credential pool for child endpoint '%s': %s",
    +                effective_base_url,
    +                exc,
    +            )
    +        return None
    +
         if parent_pool is not None and effective_provider == parent_provider:
             return parent_pool
     
    ```
- **#41865: fix(delegate): honor model parameter end-to-end (fixes #41821, closes #41814)**. closed without merge · fix tier `suggested`
  - maintainer direction: category `uncategorized`; implication `needs_llm`; refs `#41814`, `#53846`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#43134: feat(delegate_tool): per-task model/provider override**. closed without merge · fix tier `suggested`
  - maintainer direction: category `superseded_by_pr`; implication `superseded`; refs `#43185`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#51368: fix(delegate): preserve named custom provider overrides**. closed without merge · fix tier `suggested`
  - maintainer direction: category `uncategorized`; implication `needs_llm`
- **#51650: fix(delegation): fix opencode-go subagents ignoring delegation.model config**. closed without merge · fix tier `suggested`
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `9ae1fa9e39057517ef4cb70511e3e294f39e1a2f`, `#51540`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#53240: fix(delegation): resolve custom:* provider base_url from config instead of falling through to parent (Fixes #53064)**. closed without merge · fix tier `suggested`
  - maintainer direction: category `uncategorized`; implication `needs_llm`; refs `#53064`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#59670: fix(tools): respect config model in delegate_task subagent spawn (#58298)**. closed without merge · fix tier `suggested`
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `0263f1d12`, `#58298`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#60577: fix(delegation): make active config authoritative for delegation limits**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `tests/tools/test_delegate.py`, `tools/delegate_tool.py`
  - fix hunks:
  - `tests/tools/test_delegate.py`

    ```diff
    @@ -13,6 +13,7 @@
     import os
     import threading
     import time
    +import types
     import unittest
     from unittest.mock import MagicMock, patch
     
    ```

    ```diff
    @@ -21,6 +22,7 @@
         DELEGATE_TASK_SCHEMA,
         DelegateEvent,
         _get_max_concurrent_children,
    +    _load_config,
         _LEGACY_EVENT_MAP,
         MAX_DEPTH,
         check_delegate_requirements,
    ```

    ```diff
    @@ -2390,6 +2392,71 @@ def test_progress_callback_task_progress_not_misrendered(self):
     class TestConcurrencyDefaults(unittest.TestCase):
         """Tests for the concurrency default and no hard ceiling."""
     
    +    def test_load_config_prefers_active_persistent_config_over_cli_defaults(self):
    +        stale_cli = types.ModuleType("cli")
    +        stale_cli.CLI_CONFIG = {
    +            "delegation": {
    +                "max_iterations": 45,
    +                "model": "",
    +                "provider": "",
    +                "base_url": "",
    +                "api_key": "",
    +            }
    +        }
    +        active_config = {
    +            "delegation": {
    +                "max_iterations": 50,
    +                "max_concurrent_children": 50,
    +                "max_spawn_depth": 10,
    +            }
    +        }
    +
    +        with patch.dict("sys.modules", {"cli": stale_cli}):
    +            with patch(
    +                "hermes_cli.config.load_config_readonly", return_value=active_config
    +            ):
    +                self.assertEqual(_load_config()["max_concurrent_children"], 50)
    +                self.assertEqual(_get_max_concurrent_children(), 50)
    +
    +    def test_load_config_falls_back_to_cli_config_when_persistent_load_fails(self):
    +        fallback_cli = types.ModuleType("cli")
    +        fallback_cli.CLI_CONFIG = {
    +            "delegation": {
    +                "max_iterations": 45,
    +                "max_concurrent_children": 8,
    +            }
    +        }
    +
    +        with patch.dict("sys.modules", {"cli": fallback_cli}):
    +            with patch(
    +                "hermes_cli.config.load_config_readonly",
    +                side_effect=RuntimeError("boom"),
    +            ):
    +                self.assertEqual(_load_config()["max_concurrent_children"], 8)
    +
    +    def test_load_config_prefers_cli_config_when_user_config_ignored(self):
    +        # `hermes chat --ignore-user-config` sets HERMES_IGNORE_USER_CONFIG=1,
    +        # which only load_cli_config() honors. The delegation loader must keep
    +        # CLI_CONFIG authoritative under the flag so user config.yaml
    +        # delegation keys stay suppressed.
    +        ignoring_cli = types.ModuleType("cli")
    +        ignoring_cli.CLI_CONFIG = {
    +            "delegation": {
    +                "max_iterations": 45,
    +                "max_concurrent_children": 4,
    +            }
    +        }
    +        user_config = {"delegation": {"max_concurrent_children": 50}}
    +
    +        with patch.dict("sys.modules", {"cli": ignoring_cli}):
    +            with patch.dict(os.environ, {"HERMES_IGNORE_USER_CONFIG": "1"}):
    +                with patch(
    +                    "hermes_cli.config.load_config_readonly",
    +                    return_value=user_config,
    +                ) as mock_loader:
    +                    self.assertEqual(_load_config()["max_concurrent_children"], 4)
    +                    mock_loader.assert_not_called()
    +
         @patch("tools.delegate_tool._load_config", return_value={})
         def test_default_is_three(self, mock_cfg):
             # Clear env var if set
    ```
  - `tools/delegate_tool.py`

    ```diff
    @@ -3098,26 +3098,40 @@ def _resolve_delegation_credentials(cfg: dict, parent_agent) -> dict:
     
     
     def _load_config() -> dict:
    -    """Load delegation config from CLI_CONFIG or persistent config.
    -
    -    Checks the runtime config (cli.py CLI_CONFIG) first, then falls back
    -    to the persistent config (hermes_cli/config.py load_config()) so that
    -    ``delegation.model`` / ``delegation.provider`` are picked up regardless
    -    of the entry point (CLI, gateway, cron).
    +    """Load delegation config from the active Hermes config.
    +
    +    Prefer the shared persistent loader because it follows the active
    +    HERMES_HOME/profile. ``cli.CLI_CONFIG`` is a legacy fallback for entry
    +    points that cannot import the shared loader; importing it first can return
    +    an old default ``delegation`` block and hide user-set keys such as
    +    ``max_concurrent_children``.
    +
    +    Uses ``load_config_readonly()``: every consumer of this dict is read-only
    +    (``.get()`` lookups), and this runs on each ``get_definitions()`` schema
    +    rebuild via ``_get_max_concurrent_children``, so skipping the defensive
    +    deepcopy matters. Do NOT mutate the returned dict.
    +
    +    ``HERMES_IGNORE_USER_CONFIG=1`` (``hermes chat --ignore-user-config``) is
    +    only honored by the legacy ``cli`` loader, not the shared one, so when the
    +    flag is set we keep ``cli.CLI_CONFIG`` authoritative to preserve the
    +    flag's contract of suppressing user config.yaml settings.
         """
    +    prefer_legacy = os.environ.get("HERMES_IGNORE_USER_CONFIG") == "1"
    +    if not prefer_legacy:
    +        try:
    +            from hermes_cli.config import load_config_readonly
    +
    +            full = load_config_readonly()
    +            cfg = full.get("delegation") or {}
    +            if isinstance(cfg, dict):
    +                return cfg
    +        except Exception:
    +            pass
         try:
             from cli import CLI_CONFIG
     
             cfg = CLI_CONFIG.get("delegation") or {}
    -        if cfg:
    -            return cfg
    -    except Exception:
    -        pass
    -    try:
    -        from hermes_cli.config import load_config
    -
    -        full = load_config()
    -        return full.get("delegation") or {}
    +        return cfg if isinstance(cfg, dict) else {}
         except Exception:
             return {}
     
    ```
- **#61258: fix(delegation): prevent credential pool from overriding explicit delegation.base_url**. closed without merge · fix tier `suggested`
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `f2c11ff30cd5601a4017cae64cbbeac0a481f5c9`, `#61195`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#61275: fix(delegate): keep delegation.base_url authoritative against credential-pool rotation**. closed without merge · fix tier `suggested`
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `f2c11ff30cd5601a4017cae64cbbeac0a481f5c9`
- **#64299: feat(delegate): per-call and per-task model/provider overrides for delegate_task**. closed without merge · fix tier `suggested`
  - maintainer direction: category `superseded_by_pr`; implication `superseded`; refs `#63461`, `#63461`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#791: feat: allow configurable subagent model in config.yaml (#609)**. closed without merge
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`
- **#3734: feat: per-call model/provider override on delegate_task**. closed without merge
  - maintainer direction: category `silent_close`; implication `active`
- **#4929: feat(delegation): add named subagent capability profiles**. closed without merge
  - maintainer direction: category `uncategorized`; implication `needs_llm`; refs `ba0bc01d1f`, `#5583`, `#44412`, `#64213`, `#4928`
- **#6933: fix(delegate): sync self.base_url with client_kwargs after credential resolution**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#7360`
- **#9255: feat(delegate): add task-tier profiles with per-task reasoning_effort and pool validation**. closed without merge
  - maintainer direction: category `stale_abandoned`; implication `active`
- **#9737: feat(delegate): automatic tier routing + task-tier profiles with per-task reasoning_effort**. closed without merge
  - maintainer direction: category `stale_abandoned`; implication `active`
- **#12053: fix(delegate): read fresh config.yaml so delegation.model edits take effect (#11999)**. closed without merge
  - maintainer direction: category `stale_abandoned`; implication `active`
- **#16329: fix(delegation): honor delegated provider routing for subagents**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#19744`
- **#16503: fix(delegate): detect /anthropic URL suffix for proxy endpoints (#7833)**. closed without merge
  - maintainer direction: category `superseded_by_pr`; implication `superseded`; refs `#13567`
- **#16839: fix(delegate): clear acp_command when override_provider is set**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#16894`
- **#23769: fix(delegation): accept per-call model override on delegate_task**. closed without merge
  - maintainer direction: category `duplicate_of`; implication `duplicate`; refs `#23649`
- **#25026: feat(delegate): per-task model and provider override for delegate_task subagents**. closed without merge
  - maintainer direction: category `duplicate_of`; implication `duplicate`; refs `#3172`, `#16163`, `#17756`, `#15789`, `#14974`
- **#26486: fix(delegation): honor provider overrides for subagents**. closed without merge
  - maintainer direction: category `silent_close`; implication `active`
- **#34369: fix(delegate): detect credential/endpoint mismatch before sending request (#34318, #34334)**. closed without merge
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `#34318`, `#34334`
- **#34773: feat(delegate): per-task model/provider override in delegate_task tasks array**. closed without merge
  - maintainer direction: category `duplicate_of`; implication `duplicate`; refs `#14974`, `#17756`, `#20000`, `#23266`, `#32555`, `#34752`, `#34764`, `#15789`
- **#34776: feat(phase34754): Centralized Model Complexity Configuration**. closed without merge
  - maintainer direction: category `duplicate_of`; implication `duplicate`; refs `#34752`, `#34727`, `#10995`, `#13663`
- **#35033: feat(delegate): per-task model/provider overrides (+ fix two latent delegation bugs)**. closed without merge
  - maintainer direction: category `duplicate_of`; implication `duplicate`; refs `#14974`, `#25530`, `#23649`, `#17756`
- **#36790: feat(delegate): add per-task model/provider routing**. closed without merge
  - maintainer direction: category `wontfix_bydesign`; implication `wontfix`
- **#41826: feat(delegation): add per-task model override for delegate_task**. closed without merge
  - maintainer direction: category `duplicate_of`; implication `duplicate`; refs `#6771`, `#14974`, `#15789`, `#6771`, `#12794`, `#16163`
- **#43185: fix(delegate_tool): address PR #43134 multi-provider review feedback**. closed without merge
  - maintainer direction: category `uncategorized`; implication `needs_llm`
- **#50015: feat(delegate_task): per-call model/provider overrides**. closed without merge
  - maintainer direction: category `duplicate_of`; implication `duplicate`; refs `#6771`, `#6771`, `#15789`, `#47014`, `#47295`, `#46880`, `#32711`, `#6306`, `#23769`, `#6771`, `#12715`, `#12794`, `#12503`, `#7586`
- **#55175: fix(delegation): resolve api_key from provider env when base_url + provider set**. closed without merge
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `efc1c2d33`
- **#55478: fix(delegate): merge persistent + runtime delegation config sources**. closed without merge
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `#60577`, `#59982`
- **#58337: fix(delegate): keep child credential pools provider-scoped**. open
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `bdbb94dc4`
  - files: `tests/tools/test_delegate.py`, `tools/delegate_tool.py`
- **#72834: feat: add per-call model/provider override to delegate_task**. closed without merge
  - maintainer direction: category `uncategorized`; implication `needs_llm`

## Fix status

- Merged to main in PR #7360 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-07-08`.
- Subsystem: `agent`
