94d86fa4de
A relay-side close the transport never surfaces (observed as a CLOSE_WAIT socket behind Cloudflare, #98097) parks the read loop forever while the gateway keeps reporting connected: inbound stops, gateway_state.json stays healthy, and only a restart recovers. The library keepalive should catch this first, but as a last resort the read side now waits at most _WS_READ_IDLE_TIMEOUT (300s) for a frame before raising into the existing reconnect path, which re-authenticates and re-subscribes with per-channel since filters intact. Fixes #98097