Files
headquarter/openspec/proposals/terminal-responsiveness.md
T
Alex Blank c754984df8 feat: high-performance web terminal with asyncio-native I/O
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
2026-06-02 14:40:32 +02:00

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