Skip to content

fix(desktop): scope dedicated remote resumes correctly - #85932

Closed
plcunha wants to merge 1 commit into
NousResearch:mainfrom
plcunha:fix/desktop-dedicated-remote-profile-scope
Closed

fix(desktop): scope dedicated remote resumes correctly#85932
plcunha wants to merge 1 commit into
NousResearch:mainfrom
plcunha:fix/desktop-dedicated-remote-profile-scope

Conversation

@plcunha

@plcunha plcunha commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes Desktop sessions that load their transcript from a per-profile remote but fail WebSocket resume with session not found.

A Desktop profile alias and a backend-internal profile are not always the same thing:

  • shared global remote: one backend serves several profiles, so gateway RPCs must carry the selected profile;
  • per-profile remote override or local pooled backend: the descriptor is already dedicated, so forwarding the Desktop alias addresses a nonexistent profile inside that backend.

This change adds an explicit sharedPrimary route discriminator, uses it for socket selection, and translates Desktop ownership into the correct session.resume RPC scope across every resume entry point.

Fixes #85834.

Root cause

HermesConnection.profile was overloaded with two meanings:

  1. logical Desktop owner / pool descriptor identity;
  2. profile scope inside a shared backend.

Per-profile remote descriptors carry their Desktop alias, but the remote backend may expose only its own default profile. The renderer forwarded the alias in session.resume; the correctly selected remote backend then rejected it as an unknown profile. The same overloaded field also made dedicated pooled descriptors look like shared-primary routes.

RED proof

Before the implementation, the new routing tests failed:

FAIL src/store/gateway-shared-remote.test.ts > gatewayRpcProfile
TypeError: gatewayRpcProfile is not a function

Test Files  1 failed (1)
Tests       2 failed | 2 passed (4)

The failing cases pin both required semantics: keep scope for an explicitly shared-primary descriptor and omit a Desktop alias for a dedicated descriptor.

Changes

  • mark only truly shared-primary descriptors with sharedPrimary: true;
  • stop inferring shared routing from the presence of profile;
  • add gatewayRpcProfile() to translate Desktop owner aliases into backend RPC scope;
  • apply the translation to cold open, tile open, slash-target recovery, queued-submit recovery, and stale-runtime recovery;
  • preserve the logical Desktop profile for REST transcript prefetching;
  • retain caller scope if descriptor lookup fails, avoiding an unsafe cross-profile fallback.

Widening audit

Searched all Desktop session.resume callers. The profile-bearing paths are covered:

  • normal cold session open;
  • session tile delegation;
  • routed slash/target recovery;
  • direct submit rebind;
  • generic stale-runtime recovery.

Profile-free test paths and new-session creation are unchanged.

Related work: #85750 and #85778 identify the same descriptor-field ambiguity for socket selection. This PR carries the explicit discriminator through that routing decision and the inner session.resume payload, which is the remaining failure observed when the remote socket itself is correct.

Verification

npm run typecheck
PASS

npm run lint
0 errors (88 pre-existing warnings)

npm run test:ui
428 files passed; 3844 tests passed

npx vitest run --project electron electron/connection-config.test.ts
1 file passed; 75 tests passed

npx vitest run --project ui \
  src/store/gateway-shared-remote.test.ts \
  src/app/contrib/hooks/use-session-tile-delegate.test.ts \
  src/app/session/hooks/use-prompt-actions/resolve-target-session.test.ts \
  src/app/session/hooks/use-prompt-actions/index.test.tsx
4 files passed; 118 tests passed

npm run build
PASS

Safety

No session data, credentials, hostnames, or connection URLs are included. The change is renderer/Electron routing only; backend storage and API contracts are unchanged.

@alt-glitch alt-glitch added type/bug Something isn't working comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Aug 14, 2026
@OutThisLife

Copy link
Copy Markdown
Collaborator

Superseded by #86325, which consolidates the sharedPrimary fix from this PR and its siblings (#85750, #85778, #85932) into one change — credit to all three authors via Co-authored-by. Thanks @plcunha!

@OutThisLife

Copy link
Copy Markdown
Collaborator

Note: this PR also touched use-prompt-actions/* and use-session-tile-delegate.ts — session-targeting changes that are separate from the sharedPrimary routing fix (they look closer to #85237). #86325 deliberately keeps to the routing fix. If that session-targeting change is a real standalone bug, please open a focused PR with just those files and a repro — happy to review it on its own.

OutThisLife added a commit that referenced this pull request Aug 14, 2026
…0.20.1

sharedPrimaryRoute() inferred "served by the shared primary backend" from
the mere presence of connection.profile. But pooled backends (a local named
profile, or a per-profile remote override) also carry `profile` so their
WebSocket URL mints against the right backend. Both descriptors looked the
same, so ensureGatewayForProfile() took the shared-primary branch for a
pooled profile and never dialed its socket — Desktop stayed on the default
profile's socket even though the sidebar (REST) listed the right sessions.

Regression from #85665 (d16e236). Tag only the true shared-primary
descriptor with an explicit `sharedPrimary: true` marker in ensureBackend()
and check that marker instead of `profile`. Covers both the local-pool and
remote-override routes — the whole bug class, not one path.

Test asserts both sides of the invariant: a { profile, sharedPrimary: true }
descriptor activates the primary socket without dialing, and a pooled
descriptor carrying { profile } dials its own exact WebSocket URL.

Supersedes #85750, #85778, #85932
Fixes #85777

Co-authored-by: Tigrannnnnnn <122704900+Tigrannnnnnn@users.noreply.github.com>
Co-authored-by: Don Tuttle <11698271+wdon@users.noreply.github.com>
Co-authored-by: plcunha <145560011+plcunha@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have 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.

[Bug]: Desktop per-profile SSH remote — resume fails 'Session not found' while transcript loads (resume/detail path lacks profile routing)

3 participants