Skip to content

fix(desktop): keep session tabs inside the selected profile context - #88095

Open
cspiritsong wants to merge 1 commit into
NousResearch:mainfrom
cspiritsong:fix/desktop-profile-context
Open

cspiritsong wants to merge 1 commit into
NousResearch:mainfrom
cspiritsong:fix/desktop-profile-context

Conversation

@cspiritsong

@cspiritsong cspiritsong commented Aug 17, 2026

Copy link
Copy Markdown

Summary

Keep Desktop session tabs and panes scoped to the selected profile.

Session IDs can collide across profiles, so profile ownership now travels through tab titles, drag payloads, @session:<profile>/<id> opens, pane filtering, and resume paths. Profile switches re-home tiles synchronously, and late resume responses are discarded if the foreground profile changed.

The branch was rebuilt on current main while preserving the upstream review-state changes.

Verification:

  • Focused UI suites: 142 passed across 11 files
  • Full UI suite before the upstream-only rebase: 559 files / 5,354 tests passed
  • Typecheck passed; lint reported 0 errors
  • git diff --check passed

The profile rail's selection is the foreground identity, but the renderer
kept painting the previous profile's context until the pooled backend
finished booting, and the top session strip resolved rows from a shared
all-profile cache by stored id alone — a same-id row from another profile
could title, tint, or resume the wrong bot's tab.

- $profileScope now includes the pending swap target, and the session-tile
  set re-homes synchronously on selection: old tabs leave the strip the
  same tick the rail is clicked.
- findSessionForProfile() (id + owner) is the single resolver for the
  workspace tab title, chat header, drag payload, tile row/status lookup,
  and @session:<profile>/<id> openers.
- SessionTile carries its owning profile; pane contributions carry it; the
  layout renderer hides a pane whose owner is not the foreground profile;
  a stale keep-alive pane cannot resume against the new profile's socket
  (component guard + delegate-side late-response rejection).
- Active rail square/pill gets a tokenized ring and data-active-profile;
  workspace tab and session tiles show the owner glyph beside the status
  dot. Session underlines stay session-selection-only.
- Cross-profile opens (drag from All Profiles, @session refs) route the
  foreground to the owner via ensureGatewayProfile before creating a tile.

Tests: red first, then green; focused suites pass (145 targeted),
tsc + eslint clean. Full UI suite result recorded on the PR body.
@cspiritsong
cspiritsong force-pushed the fix/desktop-profile-context branch from 07c2f31 to f9a7f07 Compare August 21, 2026 12:07
@Enough1122

Copy link
Copy Markdown
Contributor

AI code review — automated review for reference; please use your judgment.

Reviewed by reviewer-e (AI automated review).

Important correctness layer for multi-profile desktops: stored-session ids can collide across profiles, so giving every pane/tile an owner (profile on PaneMirror, payload.profile through drag-open) and resolving visibility through findSessionForProfile(sessions, id, foregroundProfile) fixes tabs being titled/resumed against another profile's same-id row. The readiness gate that also honors the pending swap target (scope flips the moment the rail is clicked, before the gateway settles) is the subtle part, and both the same-id collision and swap-timing cases are directly tested. Findings below are minor:

  1. apps/desktop/src/app/chat/session-tile.tsx:257 — profileReady requires tileProfile === normalizeProfileKey(activeGatewayProfile); on a cold boot before any gateway profile is resolved (empty string), normalize presumably yields `` and no unlisted tab will ever promote to listed until the first profile lands. If that's intended (no resume before connect), fine — but a comment or a test for the cold-boot shape would keep it from reading as a bug later.

  2. Scope note — at 24 files this touches drag, mirror, header, composer directives, and wiring beyond what fits a single review pass; I focused on the ownership-resolution core. The pieces I sampled are consistent, but the breadth itself argues for landing behind the feature flag/scope you have for the profile-scope rollout.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/profiles Multi-profile isolation, HERMES_HOME scoping comp/desktop Electron desktop app (apps/desktop/*) P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants