fix(desktop): profiles unusable over a global SSH remote — don't dial doomed per-profile sockets - #85665
Merged
Merged
Conversation
…he shared global-remote primary Under a global SSH/remote gateway, resolveProfileBackendRoute routes every profile to the shared primary backend (case 3) and getConnection() returns the primary descriptor tagged with the profile. ensureGatewayForProfile still dialed a per-profile secondary socket at that descriptor; over SSH the duplicate dial fails (per-backend tunnel/ticket) and the closed socket became the ACTIVE gateway — every profile except the primary showed 'Hermes gateway is not connected' even though the primary socket was open. Detect the shared-primary route and activate the primary socket instead; $activeGatewayProfile still tracks the selected profile so per-request ?profile= scoping is unchanged. Hover pre-warm no-ops on this route. Local pooled profiles and per-profile remote overrides are untouched (pinned by test).
Contributor
૮ >ﻌ< ა ci reviewran on 4d4500d — fix(desktop): don't dial per-profile sockets for profiles se
|
8 tasks
3 tasks
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Fixes "Hermes gateway is not connected" when clicking into any non-primary profile while connected to a global SSH/remote gateway.
Reported by a Bot Mode user (@signalbydel): the roster (primary socket) shows all agents, but opening any of them lands on the gateway-disconnected screen.
Root cause
ensureGatewayForProfile()/openGatewayForProfile()unconditionally dial a per-profile secondary WebSocket for any non-primary profile. Under a global remote, the electron routing table (resolveProfileBackendRoute, case 3) routes every profile to the SHARED primary backend — one host, scoped per request; the descriptorgetConnection()returns is the primary connection tagged withprofile. There is no per-profile backend to dial: over SSH the tunnel/ticket belong to the primary connection, so the duplicate dial fails and the CLOSED socket becomes the active gateway. Every RPC then throws "Hermes gateway is not connected" even though the healthy primary socket is right there. The renderer's socket registry was contradicting the routing table it sits on top of.Fix
Detect the shared-primary route (descriptor tagged with
profile— only the global-remote share does this; own-remote-override and local pooled descriptors are never tagged) and activate the primary socket instead of dialing a duplicate.$activeGatewayProfilestill moves to the clicked profile, so?profile=request scoping and profile-aware surfaces behave identically. The hover pre-warm becomes a no-op on this route for the same reason.Local multi-profile (pooled backends) and per-profile remote overrides are untouched — pinned by the second test.
Verification
gateway-shared-remote.test.ts: shared descriptor → primary socket activated, no dial; untagged descriptor → pooled path still dials. 2/2 pass.