fix(maestro): force-replace pane sockets on mobile resume

Mobile browsers report readyState === OPEN after background suspension
even though the OS killed the TCP connection. The per-pane visibility
handlers skipped reconnection in that case, leaving the pane frozen
until TCP keepalive fired (30–120 s on cellular).

Remove the readyState guard and unconditionally replace the socket on
visibility return — same pattern the events socket already uses. Also
let pageshow/online fire recovery without the visibilityState gate,
since some mobile browsers fire pageshow before updating that property.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Miguel Palhas
2026-09-12 20:50:48 +01:00
co-authored by Claude Opus 4.6
parent 2512b8be42
commit 35a6ff9d0f
3 changed files with 25 additions and 11 deletions
@@ -197,11 +197,12 @@ export function useStructuredSession(
};
// Mobile browsers suspend timers and silently kill WebSockets in the
// background. Kick a fresh connect when the page becomes visible again.
// background. The browser often still reports readyState === OPEN because
// TCP keepalive hasn't noticed the dead connection yet. Force-replace
// unconditionally — connect() closes the old socket first, and the server
// replays the full transcript on every reconnect so nothing is lost.
const onVisibility = () => {
if (document.visibilityState !== "visible" || disposed) return;
if (socket && (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING)) return;
cancelTimer();
setStatus("reconnecting");
connect();
};