diff --git a/gateway/platforms/api_server.py b/gateway/platforms/api_server.py index 03862a771..608a6efc1 100644 --- a/gateway/platforms/api_server.py +++ b/gateway/platforms/api_server.py @@ -423,8 +423,8 @@ class APIServerAdapter(BasePlatformAdapter): # Load fallback provider chain so the API server platform has the # same fallback behaviour as Telegram/Discord/Slack (fixes #4954). - from gateway.run import GatewayApp - fallback_model = GatewayApp._load_fallback_model() + from gateway.run import GatewayRunner + fallback_model = GatewayRunner._load_fallback_model() agent = AIAgent( model=model, diff --git a/gateway/run.py b/gateway/run.py index abc8e1433..32e9d40b8 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -6233,6 +6233,14 @@ class GatewayRunner: logger.debug("status_callback error (%s): %s", event_type, _e) def run_sync(): + # The conditional re-assignment of `message` further below + # (prepending model-switch notes) makes Python treat it as a + # local variable in the entire function. `nonlocal` lets us + # read *and* reassign the outer `_run_agent` parameter without + # triggering an UnboundLocalError on the earlier read at + # `_resolve_turn_agent_config(message, …)`. + nonlocal message + # Pass session_key to process registry via env var so background # processes can be mapped back to this gateway session os.environ["HERMES_SESSION_KEY"] = session_key or "" diff --git a/tests/hermes_cli/test_update_gateway_restart.py b/tests/hermes_cli/test_update_gateway_restart.py index ff91e134d..ca25c05a7 100644 --- a/tests/hermes_cli/test_update_gateway_restart.py +++ b/tests/hermes_cli/test_update_gateway_restart.py @@ -218,9 +218,9 @@ class TestLaunchdPlistRefresh: assert result is True # Plist should now contain the generated content (which includes --replace) assert "--replace" in plist_path.read_text() - # Should have unloaded then reloaded - assert any("unload" in str(c) for c in calls) - assert any("load" in str(c) for c in calls) + # Should have booted out then bootstrapped + assert any("bootout" in str(c) for c in calls) + assert any("bootstrap" in str(c) for c in calls) def test_refresh_skips_when_current(self, tmp_path, monkeypatch): plist_path = tmp_path / "ai.hermes.gateway.plist" @@ -262,10 +262,10 @@ class TestLaunchdPlistRefresh: gateway_cli.launchd_start() - # First calls should be refresh (unload/load), then start + # First calls should be refresh (bootout/bootstrap), then kickstart cmd_strs = [" ".join(c) for c in calls] - assert any("unload" in s for s in cmd_strs) - assert any("start" in s for s in cmd_strs) + assert any("bootout" in s for s in cmd_strs) + assert any("kickstart" in s for s in cmd_strs) def test_launchd_start_recreates_missing_plist_and_loads_service(self, tmp_path, monkeypatch): """launchd_start self-heals when the plist file is missing entirely.""" @@ -288,11 +288,11 @@ class TestLaunchdPlistRefresh: assert "--replace" in plist_path.read_text() cmd_strs = [" ".join(c) for c in calls] - # Should load the new plist, then start - assert any("load" in s for s in cmd_strs) - assert any("start" in s for s in cmd_strs) - # Should NOT call unload (nothing to unload) - assert not any("unload" in s for s in cmd_strs) + # Should bootstrap the new plist, then kickstart + assert any("bootstrap" in s for s in cmd_strs) + assert any("kickstart" in s for s in cmd_strs) + # Should NOT call bootout (nothing to bootout) + assert not any("bootout" in s for s in cmd_strs) class TestCmdUpdateLaunchdRestart: