refactor: Socket Unification — a fixed 2-WebSocket budget per tab (state socket + relay mux)#378
Merged
Conversation
…e WebSocket (#375) * refactor: Mux session-state + host-metrics SSE into one WebSocket Replace the per-server EventSource pool and host-metrics EventSource with a single /ws/state connection (hello/subscribe/ack protocol), eliminating steady-state h1 pool usage that was blocking WebSocket handshakes on Firefox/WebKit once 6 SSE connections saturated the pool. Retires GET /api/sessions/stream; SSE event names/payloads move verbatim into the new envelope, and attachServer/subscribe* consumer seams are unchanged. * Update ship status and record PR URL * fix: address review feedback from @Copilot * Update review-pr status --------- Co-authored-by: Sahil Ahuja <sahilahuja@gmail.com>
# Conflicts: # app/frontend/tests/e2e/chat-view.spec.ts # docs/memory/run-kit/index.md # docs/memory/run-kit/ui-patterns.md
Co-authored-by: Sahil Ahuja <sahilahuja@gmail.com>
sahil-noon
marked this pull request as ready for review
July 17, 2026 18:57
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.
Summary
Session-state SSE streams saturate the browser's 6-per-origin HTTP/1.1 pool on plaintext origins — and on Firefox/WebKit a full pool blocks new WebSocket handshakes entirely, so a multi-server host could never connect a terminal. This branch collapses the per-tab connection budget from
panes + servers + 1long-lived streams to exactly two WebSockets:/ws/state(all session-state + host-metrics) and/ws/terminals(all pane relays, muxed). Evidence:docs/findings/socket-pool-accounting.md,docs/findings/relay-mux-hol.md; design:fab/plans/sahil/socket-unification.md.Changes
260716-qf3j-state-socket): new/ws/statehandler with an in-band hello/subscribe/ack protocol;sseHubclient edge generalized to per-connection subscription sets (producers frozen, payloads byte-identical);GET /api/sessions/stream+handleSSEretired;session-context.tsxswapped onto aStateSocketclient preserving everyattachServer/subscribe*seam; connection dots keyed on subscription acks; 12 e2e specs migrated to a shared/ws/statemock; structurally fixes the long-standingattachServer→SSE race (multi-server-sidebarflake).260717-803u-relay-mux): new/ws/terminalshandler — binary[u32 streamId][payload]frames + JSON control ops, per-stream bounded queues + fair scheduler (spike-proven v1 requirement: 1.66s → 32ms echo p50 under co-stream flood);handleRelay+/relay/{windowId}retired; frontendRelayMuxsingleton +TerminalClientport preserving the four delicate seams (confirmation-gate receipt, connect-select epoch, pane suspension as stream close/open, per-stream deferred reset); connection-budget e2e guard tightened to 2 WS + 0 SSE.ecd5eeeb): deadBoardEntriesFetcherchain and deadsetPreviewScopeclient fn removed; dead/relayVite dev-proxy block dropped;relay.gofolded into the socket files (sharedupgrader→state_ws.go,forceTERM→terminals_ws.go); test-onlyoutFrame.payload()relocated to the test file; unused e2e mock control surface collapsed; stale SSE/EventSource transport comments reworded across backend + frontend.Chat still rides its own SSE by design — it joins the state socket in change 3 (
chat-on-state-socket), not in this branch.