Bug Description
Hermes can miss a running hermes dashboard process when the command has --profile before the dashboard subcommand. In that shape, hermes dashboard --status, hermes dashboard --stop, and the dashboard cleanup in hermes update can all behave as though no dashboard is running.
In my case, the dashboard process was supervised by a custom local macOS LaunchAgent (my own setup, not a standard Hermes-managed dashboard service). The important part is the command shape:
$HOME/.hermes/hermes-agent/venv/bin/python -m hermes_cli.main \
--profile $PROFILE \
dashboard \
--host 0.0.0.0 \
--insecure \
--port 9119 \
--no-open \
--skip-build
The failure showed up after hermes update: Hermes rebuilt the Web UI and restarted the gateway under the updated checkout, but left this dashboard backend running on its old Python process until I restarted my custom supervisor.
The likely cause is the fixed substring matching in hermes_cli/main.py::_find_stale_dashboard_pids():
patterns = [
"hermes dashboard",
"hermes_cli.main dashboard",
"hermes_cli/main.py dashboard",
]
Those patterns don’t match python -m hermes_cli.main --profile $PROFILE dashboard ..., because --profile $PROFILE sits between the module name and the dashboard subcommand.
(To be clear: this report is only about detecting an already-running dashboard—whether Hermes should offer a first-class macOS dashboard LaunchAgent is a separate issue.)
Steps to Reproduce
-
On macOS, start hermes dashboard via the module entrypoint with a global profile argument before the dashboard subcommand. This can be done manually or through any external supervisor; it doesn’t depend on launchd.
$HOME/.hermes/hermes-agent/venv/bin/python -m hermes_cli.main \
--profile $PROFILE \
dashboard \
--host 0.0.0.0 \
--insecure \
--port 9119 \
--no-open \
--skip-build
-
Confirm the real dashboard listener exists:
lsof -nP -iTCP:9119 -sTCP:LISTEN
Observed:
python3.11 <pid> ... TCP *:9119 (LISTEN)
-
Run the direct module status command:
$HOME/.hermes/hermes-agent/venv/bin/python -m hermes_cli.main \
--profile $PROFILE \
dashboard \
--status
-
Run hermes update while that dashboard is running.
Expected Behavior
hermes dashboard --status should report the running dashboard PID. hermes dashboard --stop and the stale-dashboard cleanup path in hermes update should detect the same process. Profile-qualified dashboard commands should be detected in all of these forms:
hermes --profile $PROFILE dashboard ...
python -m hermes_cli.main --profile $PROFILE dashboard ...
python hermes_cli/main.py --profile $PROFILE dashboard ...
Actual Behavior
The dashboard stays bound to port 9119, but direct module status doesn’t see it:
$HOME/.hermes/hermes-agent/venv/bin/python -m hermes_cli.main \
--profile $PROFILE \
dashboard \
--status
No hermes dashboard processes running.
exit=0
At the same time, lsof shows the dashboard running:
python3.11 <pid> ... TCP *:9119 (LISTEN)
During the update, Hermes rebuilt the Web UI and restarted the gateway, but the dashboard process stayed on the old backend. Restarting my custom supervisor fixed it.
There is also a related false-positive shape with the console-script wrapper: hermes dashboard --status can match the short-lived shell/wrapper command line containing the words hermes dashboard --status, rather than the long-running dashboard PID.
Affected Component
Setup / Installation, Configuration (config.yaml, .env, hermes setup)
Messaging Platform (if gateway-related)
No response
Debug Report
Not attaching a public debug bundle because this is a process-matching bug and the reproduction above avoids publishing local configuration. I can provide a redacted debug bundle if needed.
Operating System
macOS 26.5.1 build 25F80
Python Version
No response
Hermes Version
Hermes Agent v0.16.0 (2026.6.5) - upstream 3e74f75
Additional Logs / Traceback (optional)
Root Cause Analysis (optional)
hermes_cli/main.py::_find_stale_dashboard_pids() scans process command lines using fixed substrings:
patterns = [
"hermes dashboard",
"hermes_cli.main dashboard",
"hermes_cli/main.py dashboard",
]
That misses valid CLI shapes where global options appear before the subcommand:
python -m hermes_cli.main --profile $PROFILE dashboard ...
The existing tests cover python3 -m hermes_cli.main dashboard --port 9119, but not global arguments between hermes_cli.main and dashboard.
Proposed Fix (optional)
Parse the command arguments rather than relying on the current fixed substrings.
Suggested properties:
- Recognise global Hermes options before the
dashboard subcommand, including
--profile $PROFILE.
- Restrict matches to the current UID and, where possible, the current Hermes
install/venv path.
- Avoid matching the current
dashboard --status/--stop invocation, its
transient shell wrapper, or chat/test commands that merely contain the words
hermes and dashboard.
- Add regression coverage for:
python -m hermes_cli.main --profile $PROFILE dashboard --port 9119
hermes --profile $PROFILE dashboard --port 9119
python hermes_cli/main.py --profile $PROFILE dashboard --port 9119
hermes dashboard --status not reporting its own wrapper as a dashboard
Are you willing to submit a PR for this?
Bug Description
Hermes can miss a running
hermes dashboardprocess when the command has--profilebefore thedashboardsubcommand. In that shape,hermes dashboard --status,hermes dashboard --stop, and the dashboard cleanup inhermes updatecan all behave as though no dashboard is running.In my case, the dashboard process was supervised by a custom local macOS LaunchAgent (my own setup, not a standard Hermes-managed dashboard service). The important part is the command shape:
The failure showed up after
hermes update: Hermes rebuilt the Web UI and restarted the gateway under the updated checkout, but left this dashboard backend running on its old Python process until I restarted my custom supervisor.The likely cause is the fixed substring matching in
hermes_cli/main.py::_find_stale_dashboard_pids():Those patterns don’t match
python -m hermes_cli.main --profile $PROFILE dashboard ..., because--profile $PROFILEsits between the module name and thedashboardsubcommand.(To be clear: this report is only about detecting an already-running dashboard—whether Hermes should offer a first-class macOS dashboard LaunchAgent is a separate issue.)
Steps to Reproduce
On macOS, start
hermes dashboardvia the module entrypoint with a global profile argument before the dashboard subcommand. This can be done manually or through any external supervisor; it doesn’t depend on launchd.Confirm the real dashboard listener exists:
Observed:
Run the direct module status command:
Run
hermes updatewhile that dashboard is running.Expected Behavior
hermes dashboard --statusshould report the running dashboard PID.hermes dashboard --stopand the stale-dashboard cleanup path inhermes updateshould detect the same process. Profile-qualified dashboard commands should be detected in all of these forms:hermes --profile $PROFILE dashboard ...python -m hermes_cli.main --profile $PROFILE dashboard ...python hermes_cli/main.py --profile $PROFILE dashboard ...Actual Behavior
The dashboard stays bound to port
9119, but direct module status doesn’t see it:At the same time,
lsofshows the dashboard running:During the update, Hermes rebuilt the Web UI and restarted the gateway, but the dashboard process stayed on the old backend. Restarting my custom supervisor fixed it.
There is also a related false-positive shape with the console-script wrapper:
hermes dashboard --statuscan match the short-lived shell/wrapper command line containing the wordshermes dashboard --status, rather than the long-running dashboard PID.Affected Component
Setup / Installation, 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
No response
Hermes Version
Hermes Agent v0.16.0 (2026.6.5) - upstream 3e74f75
Additional Logs / Traceback (optional)
Root Cause Analysis (optional)
hermes_cli/main.py::_find_stale_dashboard_pids()scans process command lines using fixed substrings:That misses valid CLI shapes where global options appear before the subcommand:
The existing tests cover
python3 -m hermes_cli.main dashboard --port 9119, but not global arguments betweenhermes_cli.mainanddashboard.Proposed Fix (optional)
Parse the command arguments rather than relying on the current fixed substrings.
Suggested properties:
dashboardsubcommand, including--profile $PROFILE.install/venv path.
dashboard --status/--stopinvocation, itstransient shell wrapper, or chat/test commands that merely contain the words
hermesanddashboard.python -m hermes_cli.main --profile $PROFILE dashboard --port 9119hermes --profile $PROFILE dashboard --port 9119python hermes_cli/main.py --profile $PROFILE dashboard --port 9119hermes dashboard --statusnot reporting its own wrapper as a dashboardAre you willing to submit a PR for this?