From d3602e630874b5633841d0ad21b95496ca35a317 Mon Sep 17 00:00:00 2001 From: davidgut1982 Date: Tue, 23 Jun 2026 13:18:42 +0000 Subject: [PATCH] fix(gateway): read multiplex_profiles from nested gateway section load_gateway_config() only surfaced the top-level `multiplex_profiles` key into gw_data before calling GatewayConfig.from_dict(). A config.yaml that pinned the flag under the nested `gateway:` section -- the form written by `hermes config set gateway.multiplex_profiles true` -- was silently ignored, so the gateway loaded with multiplex_profiles=False. from_dict() already honors the nested fallback, but load_gateway_config() builds gw_data from top-level keys first, so the nested value never reached it. Read gateway.multiplex_profiles into gw_data when the top-level key is absent, mirroring the existing nested fallback for max_concurrent_sessions. Adds a load_gateway_config() regression test that writes a config.yaml with `gateway.multiplex_profiles: true` and asserts the loaded config has multiplex_profiles=True (fails without the fix). Co-Authored-By: Claude Opus 4.8 --- gateway/config.py | 13 ++++++++----- tests/gateway/test_config.py | 26 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/gateway/config.py b/gateway/config.py index a793436f126..d6f84b2405b 100644 --- a/gateway/config.py +++ b/gateway/config.py @@ -980,15 +980,18 @@ def load_gateway_config() -> GatewayConfig: gw_data["thread_sessions_per_user"] = yaml_cfg["thread_sessions_per_user"] # Multiplexing flag: accept both the top-level key and the nested - # gateway.multiplex_profiles form (from_dict resolves the nested - # fallback, but surface the top-level key here for parity with the - # other session-scope flags above). + # gateway.multiplex_profiles form (written by + # ``hermes config set gateway.multiplex_profiles true``). if "multiplex_profiles" in yaml_cfg: gw_data["multiplex_profiles"] = yaml_cfg["multiplex_profiles"] gateway_section = yaml_cfg.get("gateway") - if isinstance(gateway_section, dict) and "max_concurrent_sessions" in gateway_section: - gw_data["max_concurrent_sessions"] = gateway_section["max_concurrent_sessions"] + if isinstance(gateway_section, dict): + if "multiplex_profiles" in gateway_section and "multiplex_profiles" not in gw_data: + # gateway.multiplex_profiles written by `hermes config set gateway.multiplex_profiles true` + gw_data["multiplex_profiles"] = gateway_section["multiplex_profiles"] + if "max_concurrent_sessions" in gateway_section: + gw_data["max_concurrent_sessions"] = gateway_section["max_concurrent_sessions"] if "max_concurrent_sessions" in yaml_cfg: gw_data["max_concurrent_sessions"] = yaml_cfg["max_concurrent_sessions"] diff --git a/tests/gateway/test_config.py b/tests/gateway/test_config.py index 4bc450fdfce..d32c63ee8be 100644 --- a/tests/gateway/test_config.py +++ b/tests/gateway/test_config.py @@ -405,6 +405,32 @@ class TestLoadGatewayConfig: assert config.quick_commands == {"limits": {"type": "exec", "command": "echo ok"}} + def test_multiplex_profiles_from_nested_gateway_section(self, tmp_path, monkeypatch): + """``gateway.multiplex_profiles: true`` (the nested form written by + ``hermes config set gateway.multiplex_profiles true``) must enable + multiplexing when loaded via load_gateway_config(). + + Regression: load_gateway_config() only surfaced the *top-level* + ``multiplex_profiles`` key into gw_data, so a config.yaml that pinned + the flag under the nested ``gateway:`` section silently loaded with + multiplex_profiles=False. (from_dict honors the nested fallback, but + load_gateway_config builds gw_data from the top-level keys before + calling from_dict, so the nested value never reached it.) + """ + hermes_home = tmp_path / ".hermes" + hermes_home.mkdir() + config_path = hermes_home / "config.yaml" + config_path.write_text( + "gateway:\n multiplex_profiles: true\n", + encoding="utf-8", + ) + + monkeypatch.setenv("HERMES_HOME", str(hermes_home)) + + config = load_gateway_config() + + assert config.multiplex_profiles is True + def test_relay_platform_enabled_from_env_url(self, tmp_path, monkeypatch): """GATEWAY_RELAY_URL must enable Platform.RELAY in config.platforms so start_gateway()'s connect loop actually dials the connector. Registering