A Codeman restart (every deploy) or a network blip used to leave Pane B
dead, with a marker asking the user to close and reopen the split.
TerminalTile now reconnects:
- A transient close reconnects on the primary pane's backoff ladder
(CodemanWsReconnect) plus jitter; the attempt count resets only on a
successful open. The redelivery sweep's forced close (1005) counts as
transient.
- On reopen the closed state is cleared before the buffer refresh that
closes the output gap, so no stale marker lands under a healthy pane.
- 4003/4004/4009/4010 stop the pane for good and report once through a
new onExit(code) callback; the marker says why.
- Sockets are replaced race-free: the old one is detached before a new
one opens, and every handler ignores events from a socket that is no
longer current. destroy() cancels a pending reconnect.
- reconnectNow() lets an owner skip the backoff.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
TerminalTile used to send every xterm onData chunk as a bare {t:'i'}
frame: no seq, no ACK, silently dropped while its socket was down, and
typing never acknowledged the session's idle alert. Keystrokes and
pastes now go through app._sendInputAsync over the tile's own socket,
registered in the input-socket map while open (HTTP fallback while
not), so they are ACKed, persisted until delivered, redelivered after a
drop, and the ACK clears the idle alert.
What xterm generates on its own stays out of that persisted queue: a
query reply (DA/CPR/OSC) is dropped, as the primary pane drops it, and a
focus or mouse report goes out once via _sendInputEphemeral. The tile's
socket carries the tab identity with a :tile suffix so it can never
evict the primary pane's socket.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>