feat(dashboard): add configurable Chat UI sub-features — chat system monitor, agent profile picker, and per-profile gating - #30815
Conversation
…monitor, agent profile picker, and per-profile gating
- Wrap WebglAddon construction + loadAddon() separately so constructor failures (WebGL2 unavailable in headless/VM environments) don't crash the synchronous React render path - Fix TS6133 'arcPath' unused by exporting the function - Add missing MEM_MAX/DISK_MAX constants to AgentMetricsBar - Remove unused ChevronDown import from ProfilePickerDialog - Re-add missing cn import to ProfilePickerDialog - Remove unused profiles state from ChatSidebar (was only set, never read) - Remove unused getProfiles() call from ChatSidebar (profile list comes from ProfilePickerDialog) - Fix unused html parameter in vite.config.ts transformIndexHtml hook - Add missing 'api' import to ChatSidebar
…mp on profile switch
…and Task 8 (rapid switches, no WS leaks); add vitest + jsdom to web devDeps
…ve sidebar label The Agent Profile card was showing 'default' while the PTY was running a different profile, because getAgentMetrics reads active_profile from the web_server process's own HERMES_HOME rather than the profile the PTY child is actually running under. Fix: the PTY-side TUI gateway emits session.info with profile_name in the payload every time it boots. ChatSidebar now reads ev.payload.profile_name in the session.info handler and uses it to set activeProfile — this is the ground-truth label of what the agent loop is actually doing. getAgentMetrics is retained for the initial mount (in case the PTY hasn't emitted session.info yet) and as a fallback when session.info has no profile_name (e.g. older TUI builds). 3 new tests for the session.info precedence logic.
…em; agent profile card now shows 'switch profile' action
Live deployment report — working, with 3 issues + fixesDeployed the profile switching parts of this PR on Debian 12, Hermes v0.14.0, What we used as-is from this PR ✅
What we skipped (unrelated to profile switching)
Issue 1 — PTY ignores switched profile (fixed in #33056)
Fix: #33056 adds 14 lines to Issue 2 —
|
…oint + public active-profile endpoint
1. _resolve_chat_argv() reads ~/.hermes/active_profile sticky file and sets
HERMES_HOME before spawning the PTY. Handles named profiles and default
(missing file = default profile). Includes path traversal guard via
.resolve() + boundary check.
2. POST /api/profiles/{name}/activate - activates a profile persistently
(sticky across restarts). Delegates validation to set_active_profile()
from hermes_cli.profiles (no redundant pre-validation).
3. GET /api/active-profile - public endpoint (no auth) for frontend to
read the current active profile. Added to _PUBLIC_API_PATHS.
Fixes: PTY ignoring switched profile after page reload.
Related: NousResearch#30815
teknium1
left a comment
There was a problem hiding this comment.
Thanks for the dashboard work. Current main has since adopted a different, machine-level profile-selection architecture, so this needs selective rework before its remaining monitor/gating work can be considered.
Problems
web/src/components/ProfilePickerDialog.tsx:92activates a profile, then its callback reachesweb/src/components/ChatSidebar.tsx:339, which activates it a second time.hermes_cli/web_server.py:2986and:2993hardcode~/.hermesand aprofiles/active_profilepath. Current main resolves sticky selection throughhermes_cli.profiles(web_server.py:12922) and scopes Chat explicitly through?profile=(web_server.py:14572-14645).- Main commit
875aa8f162aa40f07b19b2ca229720da70193d41already supplies a globalProfileSwitcher; Chat derives its PTY scope from it atweb/src/pages/ChatPage.tsx:299-302and forwards it at:911-915. A second sidebar picker would duplicate and conflict with that source of truth.
Suggested changes
- Salvage only the monitor/gating idea onto the shared profile-scope flow, with profile-safe metrics and one activation owner.
- Add resolver-level profile-scope coverage rather than mock-only switch-flow tests.
Automated hermes-sweeper review.
| if (activating) return; | ||
| setActivating(name); | ||
| try { | ||
| await api.activateProfile(name); |
There was a problem hiding this comment.
This request is duplicated by the callback path: onProfileActivated(name) reaches ChatSidebar.handleProfileActivated, which calls api.activateProfile(name) again. Make either the dialog or the sidebar the sole mutation owner, then have the other layer only report success/reconnect.
| # Disk: walk ~/.hermes for total, and active profile dir for active | ||
| import shutil | ||
|
|
||
| hermes_home = os.path.expanduser("~/.hermes") |
There was a problem hiding this comment.
Do not hardcode ~/.hermes here. This bypasses profile-aware path resolution; the adjacent active-profile read also targets ~/.hermes/profiles/active_profile, while the current profile helper owns the sticky-file location. Use the profile helpers / get_hermes_home() and test with a temporary HERMES_HOME.
Summary
Adds three new configurable sub-features to the Hermes dashboard Chat tab:
dashboard.chat_uitrue--tuiserver flag)dashboard.chat_system_monitortruedashboard.chat_by_agent_profiletrueBackend additions
POST /api/profiles/{name}/activate— activates a named profile persistently (sticky across restarts)GET /api/agents/metrics— live telemetry: memory usage, disk usage, token totals+by-model, active profile name, server timeFrontend additions
AgentMetricsBar.tsx— mini SVG arc gauges for memory/disk, token bar, live clock. Polls/api/agents/metricsevery 30s. Stops polling entirely whenvisible={false}.ProfilePickerDialog.tsx— profile switcher with active indicator, soul badge, emoji icon, inline soul editor.Files changed
Testing
hermes dashboard --tuiContributed by Emmanuel A Otchere ·
feat/chat-ui-feature-gating