delegate_task not honoring delegation configuration settings
Cluster 55380 · agent. Data as of
Nothing open on GitHub. Fix merged in #60577 on 8 Jul 2026. Check it covers your case.
The short answer
- Known?
- Yes. 3 related items grouped here, 1 issue already closed.
- Fix event
- A fix was merged to main in PR #60577. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
unresolved. That is separate from the fix event. - Still open
- Nothing open on GitHub.
The members concern delegate_task apparently not respecting delegation config: one issue reports it as hardcoded to xiaomi, and two pull requests propose making the active config authoritative for limits and merging persistent and runtime config sources. Details beyond the titles are not shown, so the exact relationship between them is unconfirmed.
Open issues0
Open PRs0
Fixes on record
-
#60577 fix(delegation): make active config authoritative for delegation limits
files:
tests/tools/test_delegate.py,tools/delegate_tool.pyShow the diff
-
tests/tools/test_delegate.py@@ -13,6 +13,7 @@ import os import threading import time +import types import unittest from unittest.mock import MagicMock, patch @@ -21,6 +22,7 @@ DELEGATE_TASK_SCHEMA, DelegateEvent, _get_max_concurrent_children, + _load_config, _LEGACY_EVENT_MAP, MAX_DEPTH, check_delegate_requirements, @@ -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@@ -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 {}
-
1 PR closed without merging
From the close comments: done elsewhere 1
-
#55478 fix(delegate): merge persistent + runtime delegation config sources · done elsewhere →
#60577,#59982
Closed issues · 1
- Closed#55380Bug: delegate_task ignores delegation config — hardcoded to xiaomi
How this cluster was grouped
-
Link evidence (
structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference. -
Pairwise link density
0.67: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36.1.00means every pair is linked.
Pick up a Lab task
Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.
Open the LabAt a glance
- Subsystem
- agent
- Merged PRs
- #60577
- Closed issues
- 1
- Attempts
- 1 PR closed without merging
- Fix link checked
- 8 Jul 2026