fix(ssh): reliable legacy fallback when dashboard /api/ws is unavaila…#714
Conversation
…ble (#667) In SSH Tunnel mode the desktop starts `hermes gateway` and tunnels its port, but the dashboard chat transport speaks WebSocket at `/api/ws`, which is served only by `hermes dashboard` (web_server) — never by the gateway. So the dashboard client can never connect over SSH, and `ensureClient` re-ran the multi-second status+probe on every message before falling back, feeling stuck. Latch a sticky "dashboard unavailable" flag on the first failed connect for a remote/SSH connection so subsequent messages fall back to the working legacy HTTP transport (`/v1/chat/completions` through the tunnel) immediately, and fire a one-time toast explaining the limitation. The flag resets on any connection change. Adds renderHook tests for fast-fallback, reset-on-change, and local-stays-retryable. Also documents the full fix (run a remote `hermes dashboard` and tunnel it, with the HERMES_DASHBOARD_SESSION_TOKEN the WS actually requires) in docs/ssh-dashboard-transport.md — deferred until it can be tested on a real SSH host.
Greptile SummaryAdds a sticky "dashboard unavailable" latch to
Confidence Score: 5/5Safe to merge; the change is a targeted fallback path that only activates on failed remote dashboard probes and leaves local and explicit-dashboard-preference flows untouched. The latch is correctly ordered after the already-connected check, correctly cleared on every connection/profile change, and gated by both connectionMode and fallbackOnUnavailable before either writing the ref or calling onDashboardUnavailable. Three new tests verify all the behaviorally important cases. No files require special attention. Important Files Changed
Reviews (2): Last reviewed commit: "Merge main into fix/ssh-tunnel-chat-fall..." | Re-trigger Greptile |
Resolve the useDashboardChatTransport.ts conflict in ensureClient by keeping the SSH fast-fallback/latch logic on top of main's slash/background transport work. Also addresses the two greptile review findings: - P1: latch "dashboard unavailable" and fire onDashboardUnavailable only when fallbackOnUnavailable is true, so the "using basic chat" toast can't appear in the explicit-dashboard transport mode (where the turn errors instead of falling back). - P2: drop the internal "#667" reference from the user-facing toast string. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(#667)
In SSH Tunnel mode the desktop starts
hermes gatewayand tunnels its port, but the dashboard chat transport speaks WebSocket at/api/ws, which is served only byhermes dashboard(web_server) — never by the gateway. So the dashboard client can never connect over SSH, andensureClientre-ran the multi-second status+probe on every message before falling back, feeling stuck.Latch a sticky "dashboard unavailable" flag on the first failed connect for a remote/SSH connection so subsequent messages fall back to the working legacy HTTP transport (
/v1/chat/completionsthrough the tunnel) immediately, and fire a one-time toast explaining the limitation. The flag resets on any connection change. Adds renderHook tests for fast-fallback, reset-on-change, and local-stays-retryable.Also documents the full fix (run a remote
hermes dashboardand tunnel it, with the HERMES_DASHBOARD_SESSION_TOKEN the WS actually requires) in docs/ssh-dashboard-transport.md — deferred until it can be tested on a real SSH host.