Skip to content

[verified] fix(desktop): list named remote profiles from the profile store - #87203

Open
d31tcjg wants to merge 1 commit into
NousResearch:mainfrom
d31tcjg:fix/desktop-remote-named-profile-sessions
Open

[verified] fix(desktop): list named remote profiles from the profile store#87203
d31tcjg wants to merge 1 commit into
NousResearch:mainfrom
d31tcjg:fix/desktop-remote-named-profile-sessions

Conversation

@d31tcjg

@d31tcjg d31tcjg commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Named Desktop remotes that share a gateway were listed through /api/sessions. That endpoint returns the host default store, and Desktop then relabeled those rows as the named profile. Opening/resuming the same remotes also dropped ?profile=, so the host opened the default store again.

This routes named remotes through /api/profiles/sessions?profile=<name> first:

  • Any 200 from the aggregator is authoritative, including an empty named store (total: 0 or omitted profile_totals).
  • Fall back only when that endpoint is missing (404 / HTML). Try /api/sessions?profile= before the unscoped dedicated-remote store.
  • 5xx / timeouts and 404 Profile does not exist do not fall back to the default store.
  • On a shared gateway, open/resume keeps ?profile= so the host opens the right store.

Related Issue

No existing issue. Related but not a duplicate of #83708 (messaging scoped to the active remote), #81457 (global sessions across local overrides), or closed #85932 (dedicated-remote resume). This PR is the shared-gateway list/open leak: named remotes were reading and opening the host default store.

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✅ Tests (adding or improving test coverage)

Changes Made

  • apps/desktop/electron/profile-session-routing.ts — named-profile list first; trust any 200; missing-endpoint fallback via scoped then unscoped /api/sessions
  • apps/desktop/electron/main.ts — keep ?profile= on shared-gateway session open/resume
  • apps/desktop/electron/profile-session-routing.test.ts — 15 cases covering shared-gateway empty/unknown profiles, 5xx, scoped fallback, and dedicated remotes

How to Test

  1. cd apps/desktop && npx vitest run --project electron electron/profile-session-routing.test.ts
  2. Point two Desktop profiles at the same gateway, with sessions only in the named profile store.
  3. Open the named profile in the sidebar: rows come from /api/profiles/sessions?profile=<name>, not the default store.
  4. Resume a named-profile session on that shared gateway and confirm the request keeps ?profile=.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass — N/A (JS-only Desktop change; targeted Vitest: 15 passed)
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS 26.0 (Darwin 27.0), arm64

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

…store

Named remotes that share a gateway were listed via /api/sessions, which
returns the host default store. Desktop then relabeled those rows as the
named profile.

Ask /api/profiles/sessions?profile=<name> first and trust any 200,
including an empty named store. Fall back only when that endpoint is
missing: try /api/sessions?profile= before the unscoped dedicated store.
5xx and unknown-profile 404s must not leak the default store. Keep
?profile= on shared-gateway open/resume.
@Enough1122

Copy link
Copy Markdown
Contributor

AI code review — automated review for reference, author can ignore or act on any point.

fix(desktop): list named remote profiles from the profile store

  1. remoteProfileSharesGateway() (apps/desktop/electron/profile-session-routing.ts) treats two remotes as sharing a gateway by exact URL string equality after trailing-slash trimming only. URLs that differ in scheme case, hostname case, or path prefix (http://host:9119/ vs http://host:9119/hermes/) are not recognized as the same host, while the reverse — same origin, different path — would be. Consider normalizing with new URL(url) and comparing on origin (host + port) so the detection is identity-based rather than string-based.

  2. fetchRemoteProfileSessions() now probes /api/profiles/sessions first for every remote, including dedicated per-profile remotes that only expose /api/sessions. A dedicated remote pays one extra 404 round trip on every session list/paging call. Since remoteProfileSharesGateway() already exists in the caller (interceptSessionRequestForRemote), threading that boolean (or the profile override shape) into fetchRemoteProfileSessions would let dedicated remotes skip the probe entirely.

  3. Fallback chain: the final unscoped /api/sessions fallback is only gated by isMissingListEndpointError(). If a shared host returns a plain 404 for an unknown ?profile= query rather than one of the three recognized "missing endpoint" markers, the code falls through to the host's default store and can leak the default profile's sessions. The "do not leak" tests cover the recognized 404 shapes well; consider also treating any 404 on a request that carried an explicit profile= param as non-fallthrough unless the host explicitly reports the aggregator endpoint itself is missing.

  4. interceptSessionRequestForRemote builds passthroughQuery and the shared-gateway branch sets profile back on passthroughParams — the ordering (compute passthroughQuery, then mutate passthroughParams again) is correct, but the two code paths for GET vs mutations could share the body/profile injection once instead of duplicating it, to reduce the chance of drift when a third remote shape arrives.

@alt-glitch alt-glitch added type/bug Something isn't working comp/desktop Electron desktop app (apps/desktop/*) area/sessions Session lifecycle, resume, persistence, history area/profiles Multi-profile isolation, HERMES_HOME scoping P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Aug 16, 2026
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 area/sessions Session lifecycle, resume, persistence, history 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