fix(tui): add WebSocket heartbeat + auto-reconnect for silent drops (#32997) - #60727
Open
indigokarasu wants to merge 2 commits into
Open
fix(tui): add WebSocket heartbeat + auto-reconnect for silent drops (#32997)#60727indigokarasu wants to merge 2 commits into
indigokarasu wants to merge 2 commits into
Conversation
Fixes NousResearch#32997 The Ink TUI WebSocket client had no heartbeat and no auto-reconnect, so a silent connection drop (macOS sleep, proxy idle timeout, VPN reconnect) left the UI stranded at a dead socket with only Ctrl+C as an escape. Add a periodic ping keepalive plus dead-connection detection that forces a reconnect with exponential backoff; never reconnect after an intentional kill(). Supersedes NousResearch#30114 (which only restarted on the exit event and could not heal silent drops) by moving reconnect into the transport client and adding the heartbeat.
indigokarasu
force-pushed
the
fix/tui-ws-heartbeat-autoreconnect
branch
from
July 8, 2026 06:18
ee728bd to
2b5c7ff
Compare
indigokarasu
marked this pull request as ready for review
July 8, 2026 06:49
teknium1
reviewed
Jul 10, 2026
teknium1
left a comment
Contributor
There was a problem hiding this comment.
Thanks for targeting a real silent-drop gap in the TUI transport. Two correctness issues need resolution before this can be safely salvaged.
Problems
ui-tui/src/gatewayClient.ts:242treats absence of inbound application frames as a dead socket.tui_gateway/ws.py:319-337sendsgateway.readythen waits for client input, so a healthy idle client has no messages to refreshlastActivityAt. The local undici WebSocket declaration exposes noping/pongAPI (node_modules/undici/types/websocket.d.ts:16-66), so the optional call at PR line 237 is not an acknowledged heartbeat. This will deliberately close healthy idle sockets; the new test atui-tui/src/__tests__/gatewayClient.test.ts:497currently asserts that behavior.ui-tui/src/gatewayClient.ts:350schedules retry after the synchronousexitemission. The existing subscriber already invokesgw.start()for session recovery (ui-tui/src/app/useMainApp.ts:795-821), so the subsequently-created timer later replaces that fresh transport.
Suggested changes
- Add a server-supported, acknowledged heartbeat and test that healthy idle sockets stay connected.
- Use one recovery owner, and test an attached client with the current exit subscriber to prove a single reconnect occurs.
Automated hermes-sweeper review.
indigokarasu
marked this pull request as draft
July 10, 2026 15:42
Contributor
Author
|
Addressed the review in
Validation:
|
indigokarasu
marked this pull request as ready for review
July 10, 2026 16:08
Contributor
Author
|
@teknium1 this is ready for another look. I addressed the review feedback and moved the PR out of draft. |
This was referenced Jul 12, 2026
indigokarasu
marked this pull request as draft
July 15, 2026 23:38
indigokarasu
marked this pull request as ready for review
July 17, 2026 00:26
This was referenced Aug 10, 2026
|
The still-relevant TUI heartbeat and bounded reconnect behavior from this PR has been salvaged onto current main in #83166. Indigo Karasu's original commits are preserved as authored, with explicit credit in the combined PR. This cross-link does not close or rewrite this PR. |
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 / Why
Issue #32997: the Ink TUI (
hermes --tui) WebSocket client had no heartbeat andno auto-reconnect. A silent drop (macOS sleep, proxy idle timeout, VPN reconnect)
kills the TCP socket without a
closeevent, so the client hangs forever —input is blocked and only Ctrl+C recovers.
These four issues (#46563, #40164, #53142, #32997) share one root cause: the
WebSocket transport has no unified resilience contract. Three facets are already
covered by in-flight PRs and are intentionally NOT touched here:
This PR closes the remaining facet: the TUI client now sends a periodic ping
keepalive, detects dead/silent connections, and auto-reconnects with exponential
backoff. Intentional
kill()is honored (no reconnect loop).Supersedes #30114 (exit-event-only restart, could not heal silent drops). Credit
to @sbiesaar for the original attempt.
How to test
npm run typecheck --prefix ui-tuinpm test --prefix ui-tui(gatewayClient connect/reconnect tests)npx eslint src/gatewayClient.ts src/__tests__/gatewayClient.test.tsPlatforms tested
Linux (Node undici WebSocket). Logic is transport-only; ping is guarded for
environments without
WebSocket.ping.