c754984df8
Complete rewrite of the terminal pipeline for VS Code Server-level responsiveness. Key improvements: Backend: - Replace blocking select.select(0.1) with asyncio.add_reader() for event-driven PTY reading (eliminates ~110ms polling latency) - Add output batching (2ms window) to reduce WebSocket frame overhead - Add flow control: client acks processed bytes, server pauses PTY reads at 64KB threshold, resumes at 32KB - Add 5s ack timeout fallback to prevent stuck sessions Frontend: - Switch WebSocket to binary mode (binaryType = 'arraybuffer') - Eliminate Blob -> arrayBuffer async conversion overhead - Add flow control ack messages (every 4096 bytes or 100ms) - Add xterm-addon-webgl with graceful DOM fallback - Add performance tuning (scrollback=10000, fastScrollSensitivity) SDD artifacts: - openspec/explorations/terminal-responsiveness.md - openspec/proposals/terminal-responsiveness.md - openspec/specs/terminal-responsiveness.md - openspec/designs/terminal-responsiveness.md - openspec/tasks/terminal-responsiveness.md Quality gates: pytest (19 passed, 1 skipped), tsc --noEmit clean
72 lines
3.3 KiB
Markdown
72 lines
3.3 KiB
Markdown
# Proposal: High-Performance Web Terminal
|
|
|
|
## Status
|
|
**Phase:** proposal → spec → design → tasks → apply
|
|
**Date:** 2026-06-02
|
|
**Owner:** el Gentleman
|
|
**Scope:** Terminal I/O latency, rendering performance, connection stability
|
|
|
|
## Problem
|
|
|
|
The current web terminal has noticeable latency on keystrokes, choppy scrolling, and poor performance during output bursts. Users report it feels "slow" compared to VS Code Server's terminal, which feels almost local.
|
|
|
|
## Goals
|
|
|
|
| Metric | Current | Target | How Measured |
|
|
|--------|---------|--------|--------------|
|
|
| Input latency (keypress → char visible) | ~110ms | < 16ms (1 frame) | `term.write()` timestamp diff |
|
|
| Output throughput (`cat /dev/zero`) | ~200KB/s | > 1 MB/s | Bytes/sec over 5s |
|
|
| Resize latency | ~200ms | < 50ms | Time from resize msg to shell SIGWINCH |
|
|
| Reconnection + replay | ~2s | < 300ms | Time from WS open to first rendered char |
|
|
| Frame drops during `yes` | Many | 0 | `requestAnimationFrame` counter |
|
|
|
|
## Non-Goals
|
|
|
|
- Changing the terminal UI/UX (chrome, controls, tabs)
|
|
- Adding new terminal features (search, multi-cursor, etc.)
|
|
- Changing authentication or session persistence model
|
|
- Supporting non-Docker container runtimes
|
|
|
|
## Constraints
|
|
|
|
- Must work with existing tool instance lifecycle (docker containers)
|
|
- Must preserve WebSocket-based architecture
|
|
- Must preserve session persistence across reconnections
|
|
- Must work in both development and production compose setups
|
|
|
|
## Solution Overview
|
|
|
|
Replace the blocking `select.select()` PTY read loop with asyncio-native event-driven I/O. Replace `docker exec` subprocess with Docker Engine API attach. Switch WebSocket to binary mode. Add output batching. Add WebGL renderer.
|
|
|
|
## Key Decisions
|
|
|
|
1. **Keep `docker exec` for now** — Docker SDK attach doesn't support PTY mode as cleanly. We can optimize the subprocess approach with proper fd handling.
|
|
2. **Use `asyncio.add_reader()`** — Native asyncio event-driven fd reading eliminates polling latency.
|
|
3. **Binary WebSocket frames** — `ws.binaryType = "arraybuffer"` eliminates Blob conversion overhead.
|
|
4. **WebGL renderer with DOM fallback** — GPU acceleration where available, graceful fallback.
|
|
5. **Output batching with 2ms window** — Collect small writes before sending to reduce frame overhead.
|
|
6. **Flow control v2** — Client acknowledges processed bytes; server pauses reads when buffer is full.
|
|
|
|
## Risks
|
|
|
|
- **Event loop blocking**: `asyncio.add_reader()` on a PTY fd may not work on all platforms (should work on Linux)
|
|
- **WebGL compatibility**: Some GPUs/drivers may fail WebGL context creation
|
|
- **Docker exec subprocess**: Still adds overhead; may revisit Docker API attach in future
|
|
- **Full rewrite**: Large change surface; thorough testing required
|
|
|
|
## Acceptance Criteria
|
|
|
|
- [ ] Input latency < 16ms measured with synthetic benchmark
|
|
- [ ] Output throughput > 1 MB/s measured with `cat /dev/zero`
|
|
- [ ] Resize latency < 50ms
|
|
- [ ] Reconnection + replay < 300ms
|
|
- [ ] No frame drops during `yes` command
|
|
- [ ] All existing terminal tests pass
|
|
- [ ] WebGL renderer loads successfully on modern browsers
|
|
- [ ] Graceful fallback to DOM renderer if WebGL fails
|
|
- [ ] Flow control prevents memory bloat on `cat /dev/urandom | base64`
|
|
|
|
## Related
|
|
|
|
- `openspec/explorations/terminal-responsiveness.md` — Detailed bottleneck analysis
|