From 9787f120f1f7158d61d129ee89e8ae5d3c323c0e Mon Sep 17 00:00:00 2001 From: kyssta-exe Date: Tue, 2 Jun 2026 17:41:16 +0000 Subject: [PATCH] fix(launchd): use unconditional KeepAlive so gateway restarts after clean exit The launchd plist shipped with KeepAlive.SuccessfulExit=false, which meant launchd only restarted the gateway on non-zero exit. When 'gateway run --replace' triggered a clean exit (code 0), launchd did not relaunch it, leaving the gateway down indefinitely until manually restarted. Changed KeepAlive to unconditional true so the gateway always restarts regardless of exit code. Also updated docstrings to reflect the new KeepAlive policy. Fixes #37388 --- hermes_cli/gateway.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/hermes_cli/gateway.py b/hermes_cli/gateway.py index 8673195748061..8698e44d0aa5b 100644 --- a/hermes_cli/gateway.py +++ b/hermes_cli/gateway.py @@ -207,9 +207,9 @@ def _graceful_restart_via_sigusr1(pid: int, drain_timeout: float) -> bool: SIGUSR1 is wired in gateway/run.py to ``request_restart(via_service=True)`` which drains in-flight agent runs (up to ``agent.restart_drain_timeout`` - seconds), then exits with code 75. Both systemd (``Restart=always`` - + ``RestartForceExitStatus=75``) and launchd (``KeepAlive.SuccessfulExit - = false``) relaunch the process after the graceful exit. + seconds), then exits with code 75. Both systemd (``Restart=always``) + and launchd (``KeepAlive: true``) relaunch the process regardless of + exit code. This is the drain-aware alternative to ``systemctl restart`` / ``SIGTERM``, which SIGKILL in-flight agents after a short timeout. @@ -2874,10 +2874,7 @@ def generate_launchd_plist() -> str: KeepAlive - - SuccessfulExit - - + StandardOutPath {log_dir}/gateway.log @@ -2994,8 +2991,8 @@ def launchd_stop(): pass # bootout unloads the service definition so KeepAlive doesn't respawn # the process. A plain `kill SIGTERM` only signals the process — launchd - # immediately restarts it because KeepAlive.SuccessfulExit = false. - # `hermes gateway start` re-bootstraps when it detects the job is unloaded. + # immediately restarts it because KeepAlive: true. `hermes gateway start` + # re-bootstraps when it detects the job is unloaded. try: subprocess.run(["launchctl", "bootout", target], check=True, timeout=90) except subprocess.CalledProcessError as e: