Skip to content

fix(cli): tokenized dashboard cmdline matcher — detect global flags before the subcommand - #44165

Open
AIalliAI wants to merge 2 commits into
NousResearch:mainfrom
AIalliAI:fix/44035-dashboard-pid-profile-flag
Open

fix(cli): tokenized dashboard cmdline matcher — detect global flags before the subcommand#44165
AIalliAI wants to merge 2 commits into
NousResearch:mainfrom
AIalliAI:fix/44035-dashboard-pid-profile-flag

Conversation

@AIalliAI

@AIalliAI AIalliAI commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Problem

_find_stale_dashboard_pids() matches fixed substrings ("hermes dashboard", "hermes_cli.main dashboard", "hermes_cli/main.py dashboard"), so any invocation with global options between the entrypoint and the subcommand —

python -m hermes_cli.main --profile work dashboard --host 0.0.0.0 --port 9119 --no-open --skip-build

— is invisible to hermes dashboard --status, hermes dashboard --stop, and the post-update stale-backend cleanup. After hermes update, a profile-scoped dashboard keeps serving the old Python backend against the freshly-built JS bundle.

Fix

Replace the substring patterns with a tokenized matcher (_is_dashboard_cmdline):

  1. Find the hermes entrypoint token — hermes/hermes.exe basename, hermes_cli.main (after -m), or a hermes_cli/main.py script path.
  2. Walk forward over known top-level flags until the first positional; match only if it is exactly dashboard.

Flag arity (does --profile consume a value? is --tui boolean?) is introspected from the real top-level parser plus PRE_ARGPARSE_INHERITED_FLAGS — the same approach hermes_cli.relaunch uses for its inherited-flag table — so the matcher can't drift out of sync as global options are added.

Unknown flags and free-text arguments bail out. This matters because the scan feeds a SIGTERM/SIGKILL pass: ps output does not preserve shell quoting, so a looser "both words appear" match would kill hermes -z "summarize my dashboard" mid-session during hermes update. The matcher is strictly tighter than the old patterns on this front — the old "hermes dashboard" substring already false-matched cmdlines like hermes -z fix hermes dashboard, which now stays alive (covered by a regression test).

Relation to #44048: alternative implementation. That PR's helper accepts any cmdline where an entrypoint substring appears anywhere (including e.g. paths containing .hermes/) and dashboard appears as a whitespace-delimited word anywhere — which false-positive-kills oneshot/chat sessions that mention "dashboard", since real ps output strips the quotes its guard relies on. Happy to converge the two PRs either way.

Both detection consumers are covered (--status/--stop via _report_dashboard_status/_kill_stale_dashboard_processes, update cleanup via the same kill helper), Windows wmic and POSIX ps branches share the matcher.

Tests

  • 9 new regression tests: the exact [Bug]: dashboard status/update miss running dashboards when --profile appears before the subcommand #44035 LaunchAgent shape, -p, --profile=, boolean flags, script-path form, plus negative cases (other subcommands, prompts mentioning "dashboard", profile literally named dashboard, unknown flags).
  • tests/hermes_cli/test_update_stale_dashboard.py + test_dashboard_lifecycle_flags.py: 41/41 pass.
  • Full tests/hermes_cli/ suite: failure set identical to origin/main baseline on the same machine (161 pre-existing environment failures, zero new), 9 more tests passing.

Fixes #44035

@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/cli CLI entry point, hermes_cli/, setup wizard labels Jun 11, 2026
@liuhao1024

Copy link
Copy Markdown
Contributor

Code review: clean

The tokenized cmdline matcher is a solid improvement over substring matching. Verified:

  1. _dashboard_global_flag_arity() introspects the real top-level parser — flags won'''t drift out of sync with the CLI.
  2. The =-form flag handling (--profile=work) correctly skips the value token by only incrementing i += 1.
  3. Unknown flags bail out rather than guessing arity — prevents false positives on future flags.
  4. The test suite covers all edge cases: profile flag before subcommand, short flag, equals form, boolean flag, script path form, oneshot prompt mentioning dashboard (no false positive), profile value named dashboard (no false positive), and unknown flag (no false positive).

No issues found.

@AIalliAI

Copy link
Copy Markdown
Contributor Author

Requesting maintainer review — this is ready to land from my side. Standalone fork CI is pending first-run approval here; the rollup branch in #44061 carrying this session's batch is fully green on upstream CI (all test shards, typecheck, e2e).

…the subcommand

The stale-dashboard scan in _find_stale_dashboard_pids() matched fixed
substrings ("hermes dashboard", "hermes_cli.main dashboard", ...), so any
invocation with global options between the entrypoint and the subcommand —
e.g. `python -m hermes_cli.main --profile work dashboard --port 9119` —
was invisible to `hermes dashboard --status`, `--stop`, and the
post-update stale-backend cleanup. After `hermes update`, a
profile-scoped dashboard kept serving the old Python backend against the
new JS bundle.

Replace the substring patterns with a tokenized matcher that finds the
hermes entrypoint (binary, -m module, or script path) and then walks
known top-level flags — introspected from the real parser, the same way
hermes_cli.relaunch builds its inherited-flag table — until it hits the
subcommand. Unknown flags and free-text arguments bail out, so cmdlines
that merely mention "dashboard" (`hermes -z "fix my dashboard"`, which
the old substring match would have killed) are never matched.

Fixes NousResearch#44035
@AIalliAI
AIalliAI force-pushed the fix/44035-dashboard-pid-profile-flag branch from f3e6e40 to fe149e5 Compare June 20, 2026 04:49
@pasevin

pasevin commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Independently reproduced this bug and verified the fix approach is correct.

Reproduction: My dashboard runs as a systemd user service with the command shape python -m hermes_cli.main -p default dashboard --port 9119 --host 0.0.0.0 --insecure. After hermes update from v0.16.0 to v0.17.0, the dashboard API kept reporting v0.16.0 — the stale process was never detected by _find_stale_dashboard_pids because the -p default flag breaks the contiguous "hermes_cli.main dashboard" substring match.

Verification of this PR's approach: The tokenized matcher correctly strips --profile/-p (both space-separated and = forms) before checking for the dashboard subcommand. I implemented the same fix locally using shlex tokenization + flag stripping — the approach is sound. Using argparse introspection for flag arity (as this PR does) is more future-proof than hardcoding which flags consume values.

Complementary work: Issue #40449 and PR #39166 address the other half — when the dashboard IS detected but runs as a systemd service, raw-killing the PID leaves it dead (systemd records it as a clean stop, no restart under Restart=on-failure). Both fixes are needed: detection (this PR) + service-aware restart (#39166). Without detection, the service restart never fires because the PID is never found.

The false-positive guard (bail on unknown flags, strict subcommand match) is important — ps output strips shell quoting, so a looser match would kill chat sessions mentioning "dashboard".

@pasevin pasevin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing a real current-main detection gap: hermes_cli/main.py:5877-5880 still uses contiguous substring patterns, so --profile before dashboard is missed.

Problems

  • The PR's equals-form branch (hermes_cli/main.py:5373 on the PR head) skips validation before the unknown-flag bailout. hermes --future-flag=x dashboard would match; validate the flag and value arity first.
  • Current main added hermes serve cleanup at hermes_cli/main.py:5881-5886 in dff491a. Replacing the whole pattern list with a dashboard-only matcher would regress headless desktop backend cleanup.
  • The entrypoint scan accepts hermes anywhere in the command line (hermes_cli/main.py:5352-5361 on the PR head), so shell wrappers such as sh -c hermes dashboard --status remain false positives described in #44035.

Suggested changes

  • Preserve both current stale-server targets (dashboard and serve), validate equals-form flags, and add regressions for unknown --flag=value, profile-prefixed serve, and a shell wrapper.

Automated hermes-sweeper review.

Comment thread hermes_cli/main.py

flag_arity = _dashboard_global_flag_arity()
i = entry_idx + 1
while i < len(tokens):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This accepts every equals-form option before checking flag_arity, so hermes --future-flag=x dashboard returns true despite the documented unknown-flag bailout. Split on =, require the flag to be known and value-taking, then add a negative regression.

Comment thread hermes_cli/main.py
parser, _subparsers, _chat_parser = build_top_level_parser()
for action in parser._actions:
takes_value = action.nargs != 0 # store_true/false set nargs=0
for opt in action.option_strings:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current main also reaps hermes serve as the desktop headless backend (hermes_cli/main.py:5881-5886, dff491a). A dashboard-only matcher would drop that behavior; generalize this matcher to the supported stale-server subcommands and cover serve.

Comment thread hermes_cli/main.py
entry_idx = i
break
if entry_idx is None:
return False

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the entrypoint search accepts hermes anywhere in the command line, sh -c hermes dashboard --status still reaches this return path. #44035 calls out wrapper false positives; please add a regression and constrain the matcher accordingly.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 14, 2026
Three fixes per @teknium1 review:

1. Validate equals-form flags before accepting them — require the flag
   name to be known and value-taking (rejects ).

2. Generalize  to  and accept
   both  and  as the subcommand, preserving current
   main's headless desktop backend cleanup support.

3. Reject shell-wrapped cmdlines (

╭─ Hermes Agent v0.18.2 (2026.7.7.2) · upstream eb52760 · local 935e424 (+2 ─╮
│                                       Available Tools                        │
│    ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⡀⠀⣀⣀⠀⢀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀     browser: browser_back, browser_click,  │
│    ⠀⠀⠀⠀⠀⠀⢀⣠⣴⣾⣿⣿⣇⠸⣿⣿⠇⣸⣿⣿⣷⣦⣄⡀⠀⠀⠀⠀⠀⠀     ...                                    │
│    ⠀⢀⣠⣴⣶⠿⠋⣩⡿⣿⡿⠻⣿⡇⢠⡄⢸⣿⠟⢿⣿⢿⣍⠙⠿⣶⣦⣄⡀⠀     clarify: clarify                       │
│    ⠀⠀⠉⠉⠁⠶⠟⠋⠀⠉⠀⢀⣈⣁⡈⢁⣈⣁⡀⠀⠉⠀⠙⠻⠶⠈⠉⠉⠀⠀     code_execution: execute_code           │
│    ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣴⣿⡿⠛⢁⡈⠛⢿⣿⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀     computer_use: computer_use             │
│    ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠿⣿⣦⣤⣈⠁⢠⣴⣿⠿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀     cronjob: cronjob                       │
│    ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠻⢿⣿⣦⡉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀     delegation: delegate_task              │
│    ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢷⣦⣈⠛⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀     file: patch, read_file, search_files,  │
│    ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣴⠦⠈⠙⠿⣦⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀     write_file                             │
│    ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⣿⣤⡈⠁⢤⣿⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀     image_gen: image_generate              │
│    ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠷⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀     (and 9 more toolsets...)               │
│    ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⠑⢶⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀                                            │
│    ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠁⢰⡆⠈⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀     Available Skills                       │
│    ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⠈⣡⠞⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀     apple: apple-notes, +3 more            │
│    ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀     autonomous-ai-agents: claude-code, +7  │
│                                       more                                   │
│      MiniMax-M3 · Nous Research       creative: architecture-diagram, +16    │
│  /Users/adalsteinnhelgason/hermes-4…  more                                   │
│    Session: 20260726_130302_26b8d2    data-science: jupyter-live-kernel      │
│                                       devops: ci-cd-pipeline-audit, +3 more  │
│                                       email: himalaya                        │
│                                       general: code-quality-analysis, +7     │
│                                       more                                   │
│                                       github: bulk-pr-review-fix, +20 more   │
│                                       media: gif-search, heartmula, +2 more  │
│                                       mlops: audiocraft-audio-generation,    │
│                                       +8 more                                │
│                                       mlops-inference: api-fleet-routing     │
│                                       note-taking: local-knowledge-base, +2  │
│                                       more                                   │
│                                       productivity: airtable, docx, +10      │
│                                       more                                   │
│                                       research:                              │
│                                       acquisition-target-analysis, +45 more  │
│                                       smart-home: openhue                    │
│                                       social-media:                          │
│                                       organization-social-audit, +2 more     │
│                                       software-development:                  │
│                                       android-foreground-service, +57 more   │
│                                                                              │
│                                       33 tools · 201 skills · /help for      │
│                                       commands                               │
│                                       ⚠ 1 commit behind — run hermes update  │
│                                       to update                              │
╰──────────────────────────────────────────────────────────────────────────────╯

Welcome to Hermes Agent! Type your message or /help for commands.
✦ Tip: The status bar turns yellow, then orange, then red as context fills up.

 ⚕ MiniMax-M3 │ ctx -- │ [░░░░░░░░░░] -- │ 2s │ ⏲ 0s
───────────────────────────────────────────────────────────────────────────────
                                                                               ─
❯
───────────────────────────────────────────────────────────────────────────────
                                                                               ─

   �[2;3mShutting down… (finalizing session)�[0m

Goodbye! ⚕)
   whose parent shell is the real process — fixes false positive noted
   in NousResearch#44035.

Add regression tests for all three fixes:
-
-
-

All 34 tests in test_update_stale_dashboard.py pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: dashboard status/update miss running dashboards when --profile appears before the subcommand

5 participants