Repository navigation
fix(desktop): preserve remote session routing - #93451
lukeroberts wants to merge 1 commit into
Conversation
0a8598b to
4bcd9c0
Compare
|
I reproduced the same local+SSH I opened a dependent PR that completes the rewind/resubmit path, keeps one captured owner through history lookup, interrupt, session-not-found recovery, busy retry, and stale-target resume, and aborts edit recovery if foreground selection changes while resume is pending: Verification on the dependent diff:
The existing affected remote session remained read-only; resume succeeded without |
|
Confirmed as an independent, high-impact real-world case of this SSH/remote routing failure. Environment: macOS Hermes Desktop connected primarily through a registered SSH gateway to a Linux host. Both the Mac client and gateway host were updated together and are on the same current Hermes generation (the gateway checkout is current Observed behavior:
This makes the Desktop effectively unusable for an SSH-gateway-first workflow: the normal way to return to most prior conversations is broken even though their histories are intact. The local + SSH/remote Happy to provide redacted gateway logs or help validate this branch against the affected setup. |
…nsport-error recovery Salvage of #94192's unique work (owner-hardening portions that overlap the class-1 branch — #94824/#93451 seams — and out-of-cluster #94864 are intentionally excluded): - use-gateway-request: recognize the full transport-error family (ECONNRESET & friends, including error.code and error.cause.code) so a reset SSH/remote socket triggers the connection-owned reconnect instead of surfacing as a request failure; background profiles keep the registry reconnect path for composite remote/SSH sources. - session-tile-actions: tile attachment uploads and session RPCs follow the tile's composite owner (connectionId+profile) even when the active gateway moved to a same-named profile on another source. - knownSessionOwner: sessions expose their complete owner (registry connection + profile) instead of a bare profile name that silently collapsed the route back to the local path; delegate/wiring resolve owners through it. Fixes the SSH-reconnect share of #91365-adjacent routing gaps. Salvaged (partial) from #94192.
…nsport-error recovery Salvage of #94192's unique work (owner-hardening portions that overlap the class-1 branch — #94824/#93451 seams — and out-of-cluster #94864 are intentionally excluded): - use-gateway-request: recognize the full transport-error family (ECONNRESET & friends, including error.code and error.cause.code) so a reset SSH/remote socket triggers the connection-owned reconnect instead of surfacing as a request failure; background profiles keep the registry reconnect path for composite remote/SSH sources. - session-tile-actions: tile attachment uploads and session RPCs follow the tile's composite owner (connectionId+profile) even when the active gateway moved to a same-named profile on another source. - knownSessionOwner: sessions expose their complete owner (registry connection + profile) instead of a bare profile name that silently collapsed the route back to the local path; delegate/wiring resolve owners through it. Fixes the SSH-reconnect share of #91365-adjacent routing gaps. Salvaged (partial) from #94192.
…nsport-error recovery Salvage of #94192's unique work (owner-hardening portions that overlap the class-1 branch — #94824/#93451 seams — and out-of-cluster #94864 are intentionally excluded): - use-gateway-request: recognize the full transport-error family (ECONNRESET & friends, including error.code and error.cause.code) so a reset SSH/remote socket triggers the connection-owned reconnect instead of surfacing as a request failure; background profiles keep the registry reconnect path for composite remote/SSH sources. - session-tile-actions: tile attachment uploads and session RPCs follow the tile's composite owner (connectionId+profile) even when the active gateway moved to a same-named profile on another source. - knownSessionOwner: sessions expose their complete owner (registry connection + profile) instead of a bare profile name that silently collapsed the route back to the local path; delegate/wiring resolve owners through it. Fixes the SSH-reconnect share of #91365-adjacent routing gaps. Salvaged (partial) from #94192.
|
Merged via #95080 at 682a34a — your commit was cherry-picked into the consolidated salvage PR with authorship preserved, so this work carries your name in git history. Thanks for preserving remote session routing — your fix kept sessions pinned to the connection that owns them instead of drifting to the ambient source. Closing this original PR now that the consolidated branch has landed on main. Thanks for contributing to the desktop multi-gateway campaign (tracker: #94724). |
… mock traffic Follow-up for salvaged PR NousResearch#93451: main grew branchStoredSession tests (NousResearch#93444) that drive the same hoisted requestGatewayForProfile/ForAgent mocks, so the not-called assertions in the warm-cache describe saw stale recorded calls.
…nsport-error recovery Salvage of NousResearch#94192's unique work (owner-hardening portions that overlap the class-1 branch — NousResearch#94824/NousResearch#93451 seams — and out-of-cluster NousResearch#94864 are intentionally excluded): - use-gateway-request: recognize the full transport-error family (ECONNRESET & friends, including error.code and error.cause.code) so a reset SSH/remote socket triggers the connection-owned reconnect instead of surfacing as a request failure; background profiles keep the registry reconnect path for composite remote/SSH sources. - session-tile-actions: tile attachment uploads and session RPCs follow the tile's composite owner (connectionId+profile) even when the active gateway moved to a same-named profile on another source. - knownSessionOwner: sessions expose their complete owner (registry connection + profile) instead of a bare profile name that silently collapsed the route back to the local path; delegate/wiring resolve owners through it. Fixes the SSH-reconnect share of NousResearch#91365-adjacent routing gaps. Salvaged (partial) from NousResearch#94192.
… mock traffic Follow-up for salvaged PR NousResearch#93451: main grew branchStoredSession tests (NousResearch#93444) that drive the same hoisted requestGatewayForProfile/ForAgent mocks, so the not-called assertions in the warm-cache describe saw stale recorded calls.
…nsport-error recovery Salvage of NousResearch#94192's unique work (owner-hardening portions that overlap the class-1 branch — NousResearch#94824/NousResearch#93451 seams — and out-of-cluster NousResearch#94864 are intentionally excluded): - use-gateway-request: recognize the full transport-error family (ECONNRESET & friends, including error.code and error.cause.code) so a reset SSH/remote socket triggers the connection-owned reconnect instead of surfacing as a request failure; background profiles keep the registry reconnect path for composite remote/SSH sources. - session-tile-actions: tile attachment uploads and session RPCs follow the tile's composite owner (connectionId+profile) even when the active gateway moved to a same-named profile on another source. - knownSessionOwner: sessions expose their complete owner (registry connection + profile) instead of a bare profile name that silently collapsed the route back to the local path; delegate/wiring resolve owners through it. Fixes the SSH-reconnect share of NousResearch#91365-adjacent routing gaps. Salvaged (partial) from NousResearch#94192.
Summary
connection_idsession.resume, and follow-upprompt.submiton that same(connection, profile)routeProblem
The unified Sessions list normally tags registry-owned rows with
connection_id, but a direct connection switch can reload rows without that tag. In a multi-gateway setup, an untagged remote row whose profile isdefaultcan then be resolved as onlydefault.That profile name is not globally unique: both the local gateway and an SSH/remote gateway can own a
defaultprofile. The renderer consequently falls back to the ambient/local dispatcher. The remote session still exists, butsession.resumereaches the local backend and fails withresume failed, session not found. Even after a successful resume, the prompt pipeline had its own ambient dispatcher and could fail the next message withsession not foundfor the same reason.This can affect other users with multiple registered gateways and legacy/untagged session rows, particularly local + SSH/remote configurations where both sides use the default profile name.
The behavior became reachable when the unified cross-gateway Sessions list was introduced in d354af5 (
feat(desktop): unified Sessions list shows every connected gateway's chats (#88880), Aug 18, 2026). Later profile-routing fixes covered explicit owner metadata, but not this connection-less fallback or the separate prompt submission path.Fix
For an untagged row, use the active registry connection as an ambient owner only when the current connection is remote. Explicit owner hints and persisted
connection_idvalues continue to take precedence, and local mode is unchanged.The resolved connection is then used consistently for:
session.resume/session.activate/session.usageprompt.submitTest plan
npx vitest run src/app/session/hooks/use-prompt-actions/index.test.tsx src/app/session/hooks/use-session-actions.test.tsx(183 passed)npm run typecheckgit diff --checknpm run pack