Bug Description
On macOS with multiple Hermes profiles installed as launchd services, hermes update appears to restart only the current/default launchd service, not all profile LaunchAgents, despite the update code explicitly saying it should restart all gateways.
The affected machine has several profile LaunchAgents:
ai.hermes.gateway
ai.hermes.gateway-<profile-a>
ai.hermes.gateway-<profile-b>
ai.hermes.gateway-<profile-c>
ai.hermes.gateway-<profile-d>
All of these profile gateways use the same updated Hermes codebase. After hermes update, each running gateway should be restarted so it picks up the new code.
Steps to Reproduce
- On macOS, install multiple Hermes profile gateways as launchd services:
default
<profile-a>
<profile-b>
<profile-c>
<profile-d>
-
Confirm the services exist:
~/Library/LaunchAgents/ai.hermes.gateway.plist
-
Confirm multiple gateways are running:
ps -axo pid,ppid,command | grep -i '[h]ermes' | grep -i gateway
-
Run:
hermes update
-
Check which launchd services / gateway PIDs were restarted.
-
Observe that the update flow restarts the current/default launchd label but does not enumerate and restart every profile LaunchAgent.
Expected Behavior
hermes update should restart every running gateway that uses the updated shared codebase, including macOS profile LaunchAgents:
ai.hermes.gateway
ai.hermes.gateway-<profile>
This matches the intent documented in hermes_cli/main.py:
"Auto-restart ALL gateways after update.
The code update (git pull) is shared across all profiles, so every running gateway needs restarting to pick up the new code."
Actual Behavior
On macOS launchd, the update flow appears to restart only the launchd label for the active/current profile via get_launchd_label().
Profile LaunchAgents such as ai.hermes.gateway-sport, ai.hermes.gateway-gaming, ai.hermes.gateway-tiny, and ai.hermes.gateway-wikilibriste are not explicitly enumerated and restarted.
The later manual/non-service gateway cleanup path can exclude launchd-managed PIDs via _get_service_pids(), so profile gateways managed by launchd may be neither restarted through launchd enumeration nor handled as manual gateways.
Result: profile gateways can remain running on pre-update code until manually restarted.
Affected Component
CLI (interactive chat), Gateway (Telegram/Discord/Slack/WhatsApp), Configuration (config.yaml, .env, hermes setup)
Messaging Platform (if gateway-related)
No response
Debug Report
Debug report intentionally not uploaded because it contains local profile names, local filesystem paths, and potentially private conversation/log fragments.
Sanitized environment summary:
Hermes Agent: v0.15.1 (2026.5.29) [e114b31e]
OS: macOS 26.5.1 / Darwin 25.x arm64
Python: 3.11.15
Gateway manager: launchd
Gateway platform: Telegram
Profile layout: default profile plus multiple named profiles under `~/.hermes/profiles/<profile>`
Gateway services: default launchd service plus multiple profile launchd services matching `ai.hermes.gateway-<profile>`
Operating System
macOS 26.5.1 (Build 25F80)
Python Version
3.11.15
Hermes Version
0.15.1
Additional Logs / Traceback (optional)
Observed launchd services:
~/Library/LaunchAgents/ai.hermes.gateway.plist
~/Library/LaunchAgents/ai.hermes.gateway-<profile-a>.plist
~/Library/LaunchAgents/ai.hermes.gateway-<profile-b>.plist
~/Library/LaunchAgents/ai.hermes.gateway-<profile-c>.plist
~/Library/LaunchAgents/ai.hermes.gateway-<profile-d>.plist
Observed running gateway processes:
~/.hermes/hermes-agent/venv/bin/python -m hermes_cli.main gateway run --replace
~/.hermes/hermes-agent/venv/bin/python -m hermes_cli.main --profile <profile-a> gateway run --replace
~/.hermes/hermes-agent/venv/bin/python -m hermes_cli.main --profile <profile-b> gateway run --replace
~/.hermes/hermes-agent/venv/bin/python -m hermes_cli.main --profile <profile-c> gateway run --replace
~/.hermes/hermes-agent/venv/bin/python -m hermes_cli.main --profile <profile-d> gateway run --replace
hermes gateway status reports:
Other profiles:
✓ <profile-a>
✓ <profile-b>
✓ <profile-c>
✓ <profile-d>
Root Cause Analysis (optional)
The issue appears to be in hermes_cli/main.py inside the cmd_update gateway restart flow.
The code comment says:
"Auto-restart ALL gateways after update.
The code update (git pull) is shared across all profiles, so every running gateway needs restarting to pick up the new code."
The flow imports:
- find_gateway_pids(..., all_profiles=True)
- find_profile_gateway_processes
- _get_service_pids
- _graceful_restart_via_sigusr1
So the intended behavior is clearly all-profile restart.
However, the macOS launchd service restart path appears to use get_launchd_label() for the current active profile only, then calls the launchd restart logic for that single label. It does not enumerate all installed/running ai.hermes.gateway- LaunchAgents.
After that, the manual/non-service gateway section excludes PIDs returned by _get_service_pids(). If profile gateways are launchd-managed, they can be excluded from the manual restart path while also not being restarted through launchd enumeration.
This creates a gap specifically for macOS multi-profile launchd setups.
Proposed Fix (optional)
For macOS launchd inside hermes update, enumerate all running/installed Hermes gateway LaunchAgents, not only the current profile label returned by get_launchd_label().
Possible sources:
- ~/Library/LaunchAgents/ai.hermes.gateway*.plist
- launchctl list entries matching ai.hermes.gateway
- Hermes profile registry, resolving each profile's launchd label under that profile context
Then restart each service-managed gateway exactly once.
The restart flow should preserve the existing _get_service_pids() exclusion so manually discovered process cleanup does not kill freshly restarted launchd-managed gateways.
Related issues:
Are you willing to submit a PR for this?
Bug Description
On macOS with multiple Hermes profiles installed as launchd services, hermes update appears to restart only the current/default launchd service, not all profile LaunchAgents, despite the update code explicitly saying it should restart all gateways.
The affected machine has several profile LaunchAgents:
ai.hermes.gatewayai.hermes.gateway-<profile-a>ai.hermes.gateway-<profile-b>ai.hermes.gateway-<profile-c>ai.hermes.gateway-<profile-d>All of these profile gateways use the same updated Hermes codebase. After hermes update, each running gateway should be restarted so it picks up the new code.
Steps to Reproduce
default<profile-a><profile-b><profile-c><profile-d>Confirm the services exist:
~/Library/LaunchAgents/ai.hermes.gateway.plist
Confirm multiple gateways are running:
ps -axo pid,ppid,command | grep -i '[h]ermes' | grep -i gateway
Run:
hermes update
Check which launchd services / gateway PIDs were restarted.
Observe that the update flow restarts the current/default launchd label but does not enumerate and restart every profile LaunchAgent.
Expected Behavior
hermes updateshould restart every running gateway that uses the updated shared codebase, including macOS profile LaunchAgents:ai.hermes.gatewayai.hermes.gateway-<profile>This matches the intent documented in hermes_cli/main.py:
"Auto-restart ALL gateways after update.
The code update (git pull) is shared across all profiles, so every running gateway needs restarting to pick up the new code."
Actual Behavior
On macOS launchd, the update flow appears to restart only the launchd label for the active/current profile via
get_launchd_label().Profile LaunchAgents such as
ai.hermes.gateway-sport,ai.hermes.gateway-gaming,ai.hermes.gateway-tiny, andai.hermes.gateway-wikilibristeare not explicitly enumerated and restarted.The later manual/non-service gateway cleanup path can exclude launchd-managed PIDs via
_get_service_pids(), so profile gateways managed by launchd may be neither restarted through launchd enumeration nor handled as manual gateways.Result: profile gateways can remain running on pre-update code until manually restarted.
Affected Component
CLI (interactive chat), Gateway (Telegram/Discord/Slack/WhatsApp), Configuration (config.yaml, .env, hermes setup)
Messaging Platform (if gateway-related)
No response
Debug Report
Operating System
macOS 26.5.1 (Build 25F80)
Python Version
3.11.15
Hermes Version
0.15.1
Additional Logs / Traceback (optional)
Root Cause Analysis (optional)
The issue appears to be in
hermes_cli/main.pyinside the cmd_update gateway restart flow.The code comment says:
"Auto-restart ALL gateways after update.
The code update (git pull) is shared across all profiles, so every running gateway needs restarting to pick up the new code."
The flow imports:
So the intended behavior is clearly all-profile restart.
However, the macOS launchd service restart path appears to use
get_launchd_label()for the current active profile only, then calls the launchd restart logic for that single label. It does not enumerate all installed/runningai.hermes.gateway- LaunchAgents.After that, the manual/non-service gateway section excludes PIDs returned by
_get_service_pids(). If profile gateways are launchd-managed, they can be excluded from the manual restart path while also not being restarted through launchd enumeration.This creates a gap specifically for macOS multi-profile launchd setups.
Proposed Fix (optional)
For macOS launchd inside hermes update, enumerate all running/installed Hermes gateway LaunchAgents, not only the current profile label returned by get_launchd_label().
Possible sources:
Then restart each service-managed gateway exactly once.
The restart flow should preserve the existing
_get_service_pids()exclusion so manually discovered process cleanup does not kill freshly restarted launchd-managed gateways.Related issues:
Are you willing to submit a PR for this?