fix: recover ssh sessions after reconnects#2666
Conversation
Greptile SummaryThis PR adds SSH session recovery to both
Confidence Score: 5/5Safe to merge. The reconnect/rehydrate flow is well-guarded: PTY leaks on races between detachAll and in-flight openSsh2Pty calls are blocked by the detached flag in terminal providers and by supervisor.acceptSpawn token checking in the conversation provider. All three concurrency hazards in this class of change are handled: (1) a fresh client destroyed by a stale timeout — blocked by the clientSnapshot capture; (2) PTYs spawned after detachAll — blocked by detached flag + acceptSpawn token mismatch; (3) rehydrate running after detachAll — blocked by both this.detached early-returns and isDesired returning false once supervisor.stop has been called. The new tests cover each scenario. The liveModelResult refactor in ssh-git.ts is a straightforward error-wrapping change with no new behaviour. No files require special attention.
|
| Filename | Overview |
|---|---|
| apps/emdash-desktop/src/main/core/pty/ssh2-pty.ts | Adds a 15 s channel-open timeout with a pre-captured client snapshot so the timer destroys only the stalled connection, never a reconnected one. settled flag prevents double-resolution. Synchronous throw from execPty is caught and returned as an err value. |
| apps/emdash-desktop/src/main/core/conversations/impl/ssh-conversation.ts | Adds disconnect/reconnect handling via sshConnectionManager; detachStaleSessionsForReconnect clears sessions and saves sizes, rehydrate re-opens them. The detached flag + supervisor.acceptSpawn guard correctly prevent leaked PTYs when detachAll races a pending openSsh2Pty. |
| apps/emdash-desktop/src/main/core/terminals/impl/ssh-terminal-provider.ts | Mirrors the conversation-provider pattern: detachStaleSessionsForReconnect on disconnect, rehydrate on reconnect. Explicit if (this.detached) guard after openSsh2Pty ensures PTYs are killed if detachAll races in-flight opens. |
| apps/emdash-desktop/src/main/core/conversations/conversation-session-supervisor.ts | New detachActive method clears active and spawnInFlight without touching desired, letting rehydrate pass the isDesired guard while still preventing duplicate spawns via the acceptSpawn token check. |
| apps/emdash-desktop/src/main/core/runtime/legacy/ssh-git.ts | Wraps LiveModel.compute lambdas in liveModelResult so SSH errors are returned as err(...) values rather than unhandled promise rejections, routing them through the existing onError callbacks. |
| apps/emdash-desktop/src/main/core/workspaces/workspace-factory.ts | Single-line addition passing connectionId to SshConversationProvider constructor, which was the missing plumbing needed for the reconnect handler to filter events by connection. |
Reviews (3): Last reviewed commit: "fix(ssh): cancel rehydrate after detach" | Re-trigger Greptile
Description
Screenshot/Recording (if applicable)
https://streamable.com/8ryd6b
Checklist
messages and, when possible, the PR title