diff --git a/tests/tools/test_delegate.py b/tests/tools/test_delegate.py index 5830706bf95..5deb6c24946 100644 --- a/tests/tools/test_delegate.py +++ b/tests/tools/test_delegate.py @@ -13,6 +13,7 @@ import json import os import threading import time +import types import unittest from unittest.mock import MagicMock, patch @@ -21,6 +22,7 @@ from tools.delegate_tool import ( DELEGATE_TASK_SCHEMA, DelegateEvent, _get_max_concurrent_children, + _load_config, _LEGACY_EVENT_MAP, MAX_DEPTH, check_delegate_requirements, @@ -2390,6 +2392,43 @@ class TestDelegateEventEnum(unittest.TestCase): 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", 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", side_effect=RuntimeError("boom")): + self.assertEqual(_load_config()["max_concurrent_children"], 8) + @patch("tools.delegate_tool._load_config", return_value={}) def test_default_is_three(self, mock_cfg): # Clear env var if set diff --git a/tools/delegate_tool.py b/tools/delegate_tool.py index 3e6777498b8..58829528a69 100644 --- a/tools/delegate_tool.py +++ b/tools/delegate_tool.py @@ -3098,26 +3098,28 @@ def _resolve_delegation_credentials(cfg: dict, parent_agent) -> dict: def _load_config() -> dict: - """Load delegation config from CLI_CONFIG or persistent config. + """Load delegation config from the active Hermes 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). + 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``. """ - 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 {} + 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 {} + return cfg if isinstance(cfg, dict) else {} except Exception: return {}