NousResearch/hermes-agent

Delegation limits not reflecting live config changes

Cluster 51435 · agent. Data as of

Nothing open on GitHub. Fix merged in #60577 on 8 Jul 2026. Check it covers your case.

The short answer

0open issues
0open PRs
3items grouped
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.

These items address a bug where changes to maximum subagent or delegation limits made mid-session are ignored, and the fixes make the active configuration authoritative by reloading it before reading those limits.

Open issues0

No open issues.

Open PRs0

No open pull requests.

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #60577 fix(delegation): make active config authoritative for delegation limits

    merged to main

    files: tests/tools/test_delegate.py, tools/delegate_tool.py

    Show 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

  • #51516 fix(delegation): reload live config before reading limits · done elsewhere → #60577, #59982
Closed issues · 1
  • Closed#51435[Bug]: Maximum subagents limit is ignored when changed mid-session
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.00 means 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 Lab

At a glance

Subsystem
agent
Merged PRs
#60577
Closed issues
1
Attempts
1 PR closed without merging
Fix link checked
8 Jul 2026

For your agent

The same facts, typed.