Replace the basic printable-only predictor with a clean-room speculative
local-echo engine modeled on the Mosh paper's prediction overlay design
(no GPL code copied or translated; original Rust written from the
algorithm).
Model:
- Per-line cell model with a local cursor; printable keystrokes echo
immediately, dimmed (and underlined on a very slow link) via
save/restore cursor so full-screen TUIs are never corrupted.
- Backspace and in-line Left/Right arrow prediction (CSI and SS3).
- Epoch grouping per keystroke burst; anything unmodelable (CR/LF,
escapes, control bytes, wide chars, right margin, paste-sized bursts)
ends the epoch safely rather than mispredicting.
- Frame-sequenced confirmation: an authoritative server frame supersedes
and erases the overlay (dosh has no client-side emulator, so it cannot
do Mosh's per-cell content match; frame arrival is the confirmation).
- Glitch detection: contradicting output (e.g. alternate-screen entry)
discards the epoch.
- SRTT-based display policy with hysteresis and a latency-spike override;
modes off / experimental(adaptive, default) / always via new
client config `predict_mode` and `DOSH_PREDICT_MODE` env override.
Improvements over the prior predictor: in-line arrow motion, dim+flag
styling, exact-width erase, paste guard, and faster (frame-arrival)
confirmation. Prediction is display-only and never swallows input.
Adds unit tests: printable run, confirmation-clears, glitch-discards,
backspace, line-wrap budget, paste-skip, newline, inline arrows, and the
experimental SRTT threshold gate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The native-v1 work changed the UDP wire format (HEADER_LEN 42->58) without a
version bump, so a current client silently timed out against the older
still-deployed server. The server just needed redeploying; alongside that,
fix the robustness bugs surfaced while restoring the connection:
- Server: clamp terminal size to >=1 at every PTY/parser resize site. A client
reporting a 0x0 terminal made vt100::Parser::set_size panic ("attempt to
subtract with overflow") and took down the whole single-threaded daemon.
- Client: treat size() == Ok((0,0)) like a missing size and fall back to 80x24
instead of sending 0x0 to the server.
- Reap abandoned sessions and their shells. PtyHandle now retains the child and
kills it on drop, and the cleanup task removes clientless, non-prewarmed
sessions after the grace period. Previously the shell leaked forever (one
zsh per abandoned session); prewarmed sessions still stay hot.
- Evict half-finished native handshakes (pending_native) after 30s so an
unauthenticated ClientHello flood can't grow the map without bound.
- Drop the per-client, per-packet vt100 Screen clone on the output hot path and
remove the write-only last_screen field.
- Guard load_or_create_server_secret against a <32-byte secret file (was an
index-out-of-range panic at startup).
- Reconcile stream flow-control credit (client and server) when StreamData hits
a missing writer, so a closed/unknown stream can't wedge the peer's window.
Adds a unit test for session reaping. cargo fmt + cargo test green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>