feat(desktop): session hygiene, archive, media streaming + connecting overlay#37099
Conversation
… overlay Address a batch of desktop feedback: - Stop leaking empty "Untitled" sessions: the TUI gateway pre-created a DB row on every session.create (i.e. every launch/draft). Persist the row lazily on first prompt instead, and hide message-less rows in the sidebar. - Archive/hide sessions: new `archived` column + set_session_archived, web API (`?archived=` + PATCH archived), Ctrl/⌘-click and a context-menu item in the sidebar, and an "Archived Chats" settings panel to restore/delete. - Videos load via a streaming `hermes-media://` protocol instead of capped, in-memory data URLs (16 MB limit) — bypasses the cap and supports seeking. - Background-process completions route to the session that launched them: the completion event now carries session_key and each poller only consumes its own. - Sidebar: "Group by workspace" toggle is always visible; each workspace group gets a "+" to start a session in that directory; "New agent"/"Agents" relabeled to "New session"/"Sessions". - New gateway connecting overlay (ascii decode → fade out) replacing the bare skeleton/"starting gateway" state.
|
bugbot run |
|
Skipping Bugbot: Unable to authenticate your request. Please make sure Bugbot is properly installed and configured for this repository. |
There was a problem hiding this comment.
Pull request overview
This PR improves Hermes Desktop/TUI session lifecycle and UX by preventing empty sessions from being persisted, adding soft-archive support end-to-end, correctly routing background-process completion notifications to the originating session, and enabling large audio/video playback via a streaming Electron protocol (plus a new connecting overlay).
Changes:
- Make TUI gateway sessions persist lazily on first prompt submission (avoids empty “Untitled” rows) and add notification routing by
session_key. - Add session soft-archive support in the SQLite state store + web API (list filtering + PATCH
{ archived }) with corresponding desktop UI affordances. - Stream audio/video via a custom
hermes-media://Electron protocol (Range/seek support; no data-URL size cap) and introduce a new connecting overlay component.
Reviewed changes
Copilot reviewed 26 out of 26 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tui_gateway/server.py | Lazy session row persistence and session-key-based completion event routing in the TUI gateway. |
| tools/process_registry.py | Include session_key on process completion events. |
| tests/test_tui_gateway_server.py | Tests for lazy session persistence and notification event routing. |
| tests/test_hermes_state.py | Tests for session archive behavior in the state DB. |
| tests/hermes_cli/test_web_server.py | Web API tests for session archive PATCH and session listing behavior. |
| hermes_state.py | Add archived session column and filtering + setter APIs. |
| hermes_cli/web_server.py | Add archived query param to sessions listing and support { archived } in PATCH. |
| apps/desktop/src/types/hermes.ts | Add archived to the desktop session type. |
| apps/desktop/src/lib/media.ts | Add mediaStreamUrl() for the streaming protocol. |
| apps/desktop/src/lib/icons.ts | Export archive icons for UI usage. |
| apps/desktop/src/hermes.ts | Add archived filtering to listSessions() and introduce setSessionArchived(). |
| apps/desktop/src/components/gateway-connecting-overlay.tsx | New animated connecting overlay component for gateway connect/reconnect. |
| apps/desktop/src/components/assistant-ui/markdown-text.tsx | Route audio/video sources through streaming protocol; keep images on data URLs. |
| apps/desktop/src/app/settings/types.ts | Add “sessions” to settings view/query types. |
| apps/desktop/src/app/settings/sessions-settings.tsx | New settings panel to restore/delete archived sessions. |
| apps/desktop/src/app/settings/index.tsx | Wire “Archived Chats” into the settings navigation and view routing. |
| apps/desktop/src/app/settings/constants.ts | Add search placeholder for archived sessions settings view. |
| apps/desktop/src/app/session/hooks/use-session-actions.ts | Add sidebar “Archive” action wiring via setSessionArchived. |
| apps/desktop/src/app/desktop-controller.tsx | Hide empty sessions in sidebar; add per-workspace “+” start; mount connecting overlay. |
| apps/desktop/src/app/command-center/index.tsx | Rename “New agent” → “New session” in command center. |
| apps/desktop/src/app/chat/sidebar/virtual-session-list.tsx | Thread archive callback through virtualized session list rows. |
| apps/desktop/src/app/chat/sidebar/session-row.tsx | Add ctrl/meta-click archive behavior + menu wiring. |
| apps/desktop/src/app/chat/sidebar/session-actions-menu.tsx | Add “Archive” item to per-session actions menu. |
| apps/desktop/src/app/chat/sidebar/index.tsx | Rename “Agents” → “Sessions”, make grouping toggle always visible, add per-workspace “+”. |
| apps/desktop/src/app/chat/index.tsx | Rename header fallback “New agent” → “New session”. |
| apps/desktop/electron/main.cjs | Register hermes-media protocol handler for streaming local media via Range-aware fetch. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The shownRef latch kept the connecting overlay mounted behind BootFailureOverlay after a hard boot failure. Return null on boot.error so the failure recovery surface fully owns the screen.
🔎 Lint report:
|
- /api/sessions: validate `archived` (400 on unknown) and return `archived`
as a JSON boolean instead of SQLite's 0/1.
- PATCH /api/sessions/{id}: 400 (not a misleading 404) when the body has no
updatable fields; stop conflating a no-op with "not found".
- hermes-media protocol: drop `bypassCSP` — streaming only needs
secure/standard/stream/supportFetchAPI.
- Sidebar workspace header: split the toggle and the "+" into sibling buttons
so we no longer nest interactive elements inside a <button>.
- hermes-media protocol: restrict streaming to an audio/video extension allowlist (415 otherwise) so it can't be used to read arbitrary local files. - Connecting overlay: use z-[1200] instead of the non-standard z-1200 utility.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
… overlay (NousResearch#37099) * feat(desktop): session hygiene, archive, media streaming + connecting overlay Address a batch of desktop feedback: - Stop leaking empty "Untitled" sessions: the TUI gateway pre-created a DB row on every session.create (i.e. every launch/draft). Persist the row lazily on first prompt instead, and hide message-less rows in the sidebar. - Archive/hide sessions: new `archived` column + set_session_archived, web API (`?archived=` + PATCH archived), Ctrl/⌘-click and a context-menu item in the sidebar, and an "Archived Chats" settings panel to restore/delete. - Videos load via a streaming `hermes-media://` protocol instead of capped, in-memory data URLs (16 MB limit) — bypasses the cap and supports seeking. - Background-process completions route to the session that launched them: the completion event now carries session_key and each poller only consumes its own. - Sidebar: "Group by workspace" toggle is always visible; each workspace group gets a "+" to start a session in that directory; "New agent"/"Agents" relabeled to "New session"/"Sessions". - New gateway connecting overlay (ascii decode → fade out) replacing the bare skeleton/"starting gateway" state. * fix(desktop): bail connecting overlay on boot error The shownRef latch kept the connecting overlay mounted behind BootFailureOverlay after a hard boot failure. Return null on boot.error so the failure recovery surface fully owns the screen. * fix(desktop): address Copilot review - /api/sessions: validate `archived` (400 on unknown) and return `archived` as a JSON boolean instead of SQLite's 0/1. - PATCH /api/sessions/{id}: 400 (not a misleading 404) when the body has no updatable fields; stop conflating a no-op with "not found". - hermes-media protocol: drop `bypassCSP` — streaming only needs secure/standard/stream/supportFetchAPI. - Sidebar workspace header: split the toggle and the "+" into sibling buttons so we no longer nest interactive elements inside a <button>. * fix(desktop): address Copilot re-review - hermes-media protocol: restrict streaming to an audio/video extension allowlist (415 otherwise) so it can't be used to read arbitrary local files. - Connecting overlay: use z-[1200] instead of the non-standard z-1200 utility. * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Summary
A batch of desktop feedback fixes (Ben's nitpick list + the connecting-state polish).
Sessions
session.createin the TUI gateway pre-created a DB row on every launch/draft, so eachhermes --tui/ "New session" you never typed into left a message-less row behind. The row is now created lazily on the first prompt (_ensure_session_db_row, INSERT-OR-IGNORE, captures cwd), and the desktop sidebar lists withmin_messages=1so the existing backlog disappears too.archivedcolumn +set_session_archived, web API (/api/sessions?archived=exclude|only|includeandPATCH {archived}), Ctrl/⌘-click a chat (or its context/⋯ menu) to archive, and an Archived Chats settings panel to restore or permanently delete.+per workspace to start a session in a directory you already have sessions in; "Group by workspace" toggle is now always visible (not hover-only).Background processes
session_key, and each session's notification poller only consumes its own (foreign events are left/redelivered).Media
hermes-media://Electron protocol — no size cap and proper Range/seeking. Audio/video go through it; images keep the data-URL path.Connecting overlay
GatewayConnectingOverlayreplaces the bare skeleton + "starting gateway" placeholder while the gateway connects: an ascii decode ofCONNECTING(nousnet-web download-button style) with a blinking dither cursor, that on connect freezes, fades down + desaturates, holds, then fades the overlay out to reveal the app. Shows on cold start / reconnect;?connecting=1forces a looping preview in dev.Test plan
apps/desktop:npm run type-check,npm run lint,npm test(markdown-text + chat-runtime) — green.scripts/run_tests.sh tests/test_hermes_state.py tests/hermes_cli/test_web_server.py tests/test_tui_gateway_server.py tests/tools/test_process_registry.py— green (the unrelatedtest_browser_manage_connect_default_local_reports_launch_hintfails identically onmain).Not included