Skip to content

feat(desktop): session-list overhaul + cancellable install#37379

Merged
OutThisLife merged 7 commits into
mainfrom
bb/desktop-session-list
Jun 2, 2026
Merged

feat(desktop): session-list overhaul + cancellable install#37379
OutThisLife merged 7 commits into
mainfrom
bb/desktop-session-list

Conversation

@OutThisLife

@OutThisLife OutThisLife commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

A batch of desktop session-list fixes (plus a cancellable installer), most rooted in auto-compression rotating a conversation's session id — the gateway ends the original session (end_reason='compression') and list_sessions_rich projects the root forward to its live continuation tip.

Pinned session "vanished" after compression

A pinned long-running session disappeared from the Pinned list (no data loss — the conversation is live under the new continuation id). The pin is stored as the pre-compression root id, but the sidebar matched only on the live id, so the projected tip never matched and the pin was filtered out.

Fix: resolve pins on the durable lineage-root id (_lineage_root_id, already surfaced by the projection, stable across every compression). The sidebar indexes sessions by both ids; pin/unpin/reorder and the Cmd+P toggle use a shared sessionPinId(). Existing pins keep working with no migration.

Freshly-continued session missing from the list

After a compression the continuation was invisible until you ungrouped and hit "load 50 more", because the list paginated by original start time. The desktop now requests order=recent; GET /api/sessions gains an order (created | recent) param backed by the existing recency CTE, so live continuations land on the first page.

Stable in-directory ordering

Rows within a workspace group reshuffled on every new message (sorted by last activity). They now sort by creation time within a group, so muscle memory holds; groups still float up by recency.

"No workspace" as the default

Sessions only persist a workspace cwd when one is explicitly chosen; an auto-detected launch directory is no longer stamped on the row, so untargeted sessions group under "No workspace" instead of "desktop". The agent still runs in the detected directory.

Sidebar session search

A search box over the session list: loaded sessions match instantly client-side; a debounced full-text search (existing /api/sessions/search FTS) covers the rest so all sessions stay findable at 699+.

Cancellable first-launch install

The install overlay had no way to stop a running install. Wired end to end: main.cjs holds an AbortController for the active runBootstrap and aborts on a new hermes:bootstrap:cancel IPC and on app quit (so quitting/cancelling actually kills install.sh/ps1); the runner bails before spawning if already aborted; the overlay gains a Cancel install button.

Tests

  • Python: tests/hermes_cli/test_web_server.pyorder validation + a recency/compression-tip surfacing test asserting _lineage_root_id is exposed.
  • Gateway: tests/test_tui_gateway_server.py — explicit-cwd persists, no-cwd defaults to "No workspace".
  • Desktop: apps/desktop/src/store/session.test.ts (durable pin id) + apps/desktop/electron/bootstrap-runner.test.cjs (abort short-circuits before any spawn).
  • npm run type-check + lint clean for new code; full suites green in CI.

Not included (need a repro/screenshot or external spec)

  • Pane jumps on long output; research disappearing at the final response; "Ran" blocks rendering identical content; flickering lines under the prompt box; multi-day session-list glitch — all need a repro/screenshot before touching the (heavily-engineered) scroll/render paths.
  • Remote-gateway OAuth / "Log into dashboard" — needs the Hosted Hermes authorize/redirect/token-exchange spec.

Long-running sessions auto-compress: the gateway ends the original session
and surfaces the live continuation under a new id (list_sessions_rich projects
the root forward to its tip). Two symptoms fell out of the id rotation:

- A pinned session "vanished" — the pin is stored as the pre-compression root
  id, but the sidebar only matched on the live id, so it was filtered out.
  Pins now resolve on the durable lineage-root id (`_lineage_root_id`, already
  surfaced by the projection): the sidebar indexes sessions by both ids, pin/
  unpin and reorder operate on the durable id, and `sessionPinId()` is shared
  with the Cmd+P toggle. Existing pins keep working with no migration.

- A freshly-continued session was missing from the list until you ungrouped +
  "load 50 more" — the list paginated by original start time, so an old-but-
  active conversation sat past the first page. The desktop now requests
  `order=recent` (GET /api/sessions gains an `order` param backed by the
  existing recency CTE), surfacing live continuations on the first page.
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

🔎 Lint report: bb/desktop-session-list vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 9675 on HEAD, 9671 on base (🆕 +4)

🆕 New issues (2):

Rule Count
unresolved-attribute 2
First entries
tests/hermes_cli/test_web_server.py:327: [unresolved-attribute] unresolved-attribute: Attribute `commit` is not defined on `None` in union `Connection | None`
tests/hermes_cli/test_web_server.py:325: [unresolved-attribute] unresolved-attribute: Attribute `execute` is not defined on `None` in union `Connection | None`

✅ Fixed issues: none

Unchanged: 5012 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@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 2, 2026
- Sidebar: rows within a workspace group now sort by creation time instead of
  last activity, so they stop reshuffling every time a message lands (muscle
  memory). Groups still float up by recency.
- Sessions only persist a workspace cwd when one was explicitly chosen; an
  auto-detected launch directory is no longer stamped on the row, so untargeted
  sessions group under "No workspace" instead of "desktop". The agent still
  runs in the detected directory.
Adds a search box above the session list. Loaded sessions match instantly
client-side; a debounced full-text search (existing /api/sessions/search FTS)
covers the rest so all sessions stay findable at 699+. Results replace the
pinned/agents sections while a query is active and resume on click.
The install overlay had no way to stop a running install — the runner already
supported an abortSignal, but nothing drove it. Wire it end to end:

- main.cjs holds an AbortController for the active runBootstrap and aborts it
  on a new hermes:bootstrap:cancel IPC and on app quit, so quitting/cancelling
  mid-install actually kills install.sh/ps1 instead of orphaning it.
- runBootstrap bails before spawning anything if the signal is already aborted.
- Install overlay gains a "Cancel install" button while a bootstrap is active;
  a cancel surfaces the recovery overlay (retry/repair).

Test: electron/bootstrap-runner.test.cjs asserts the already-aborted early
return (no spawn) via `node --test`.
@OutThisLife OutThisLife requested a review from a team June 2, 2026 13:50
@OutThisLife OutThisLife changed the title fix(desktop): keep pinned + recent sessions visible across compression feat(desktop): session-list overhaul + cancellable install Jun 2, 2026
@austinpickett austinpickett requested a review from Copilot June 2, 2026 14:23

Copilot AI 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.

Pull request overview

This PR overhauls the desktop session list behavior to stay stable and searchable across session auto-compression (continuation tips), and adds end-to-end cancellation for the first-launch installer flow.

Changes:

  • Add durable session lineage support for pinning and “recent” ordering so compressed conversations remain visible and correctly pinned.
  • Update workspace grouping semantics so only explicitly-chosen workspaces persist (defaulting others to “No workspace”), and stabilize intra-group ordering.
  • Add a sidebar session search UI (local filter + debounced server FTS) and add cancellable bootstrap install (IPC + AbortController + runner short-circuit).

Reviewed changes

Copilot reviewed 17 out of 18 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tui_gateway/server.py Persist cwd only when explicitly chosen; track explicit_cwd on session creation / cwd updates.
tests/test_tui_gateway_server.py Adds tests for explicit-cwd persistence and default “No workspace” behavior.
hermes_cli/web_server.py Adds order query param to /api/sessions and passes through to SessionDB.list_sessions_rich.
tests/hermes_cli/test_web_server.py Tests order validation and “recent” ordering surfacing compression tips with _lineage_root_id.
apps/desktop/src/types/hermes.ts Extends SessionInfo with optional _lineage_root_id for compression lineage.
apps/desktop/src/store/session.ts Adds sessionPinId() helper to pin by durable lineage root id.
apps/desktop/src/store/session.test.ts Unit tests for sessionPinId() behavior (live id vs lineage root).
apps/desktop/src/hermes.ts Updates listSessions() to request order=recent by default.
apps/desktop/src/global.d.ts Adds cancelBootstrap API typing; minor type formatting cleanup.
apps/desktop/src/components/desktop-install-overlay.tsx Adds “Cancel install” button + cancelling UI state and layout tweaks.
apps/desktop/src/app/desktop-controller.tsx Toggles pins using durable lineage-root id via sessionPinId().
apps/desktop/src/app/chat/sidebar/virtual-session-list.tsx Pins from virtual list now use sessionPinId(session).
apps/desktop/src/app/chat/sidebar/index.tsx Implements durable pin resolution, stable per-workspace ordering, and session search UI.
apps/desktop/package.json Adds electron/bootstrap-runner.test.cjs to the desktop platform test script.
apps/desktop/electron/preload.cjs Exposes cancelBootstrap IPC to the renderer.
apps/desktop/electron/main.cjs Tracks an active bootstrap AbortController; handles cancel IPC and abort-on-quit.
apps/desktop/electron/bootstrap-runner.cjs Adds early abort short-circuit before any spawn when signal is already aborted.
apps/desktop/electron/bootstrap-runner.test.cjs Tests that early-aborted signals bail out before spawning anything.
Comments suppressed due to low confidence (1)

apps/desktop/src/components/desktop-install-overlay.tsx:219

  • cancelling is set to true when the user clicks Cancel, but it is never reset. If the IPC handler is missing/returns no-op (older Electron build, transient IPC error, etc.) while state.active remains true, the Cancel button will stay disabled for the rest of the install attempt. Reset cancelling when the bootstrap becomes inactive (and before starting a new bootstrap).
export function DesktopInstallOverlay({ enabled = true }: DesktopInstallOverlayProps) {
  const [state, setState] = useState<DesktopBootstrapState>(EMPTY_STATE)
  const [logOpen, setLogOpen] = useState(false)
  const [copied, setCopied] = useState(false)
  const [cancelling, setCancelling] = useState(false)
  const [now, setNow] = useState(() => Date.now())
  const logEndRef = useRef<HTMLDivElement | null>(null)

  // Tick once a second while a bootstrap is in flight so running steps show a
  // live elapsed timer. Stops when nothing is active to avoid idle renders.
  useEffect(() => {
    if (!state.active) return
    const id = window.setInterval(() => setNow(Date.now()), 1000)
    return () => window.clearInterval(id)
  }, [state.active])

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tui_gateway/server.py Outdated
Comment on lines 1750 to +1754
activeRoot: backend.activeRoot,
sourceRepoRoot: SOURCE_REPO_ROOT,
hermesHome: HERMES_HOME,
logRoot: path.join(HERMES_HOME, 'logs'),
abortSignal: bootstrapAbortController.signal,
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@OutThisLife OutThisLife enabled auto-merge June 2, 2026 14:52
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 type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants