Background
Follow-up to the integrated terminal panel work (PR replacing #88). The original review on #88 raised this concern:
All tabs from all sessions stay in �llTabs() permanently — DOM nodes and xterm instances never get cleaned up. If someone uses 20+ sessions over a workday, that's 20+ xterm renderers alive in memory.
Consider some kind of LRU eviction for inactive sessions — e.g., dispose xterm instances after a session has been inactive for a while, and re-attach from the still-running PTY when the user switches back.
The current implementation only enforces server-side limits (maxPerClient=20, maxPerSession=5) which bounds PTY count but not the front-end xterm renderer footprint.
Scope
LRU eviction operates per-session on front-end xterm instances, not on PTY processes:
| Layer |
Behaviour |
| PTY process count |
Unchanged — still gated by maxPerClient / maxPerSession |
| xterm renderer instances |
New: keep at most N (default 3) most-recently-active sessions mounted |
| User-facing terminal limit |
Unchanged — users can still create up to maxPerSession per session |
When the active-session cache exceeds N, the least-recently-used session's xterm instances are dispose()-d but their server-side PTYs continue running. When the user switches back, the panel re-creates xterm instances and re-attaches to the live PTYs.
Required protocol additions
- Server-side scrollback ring buffer —
terminal-service.ts keeps the last ~100 KB of output per PTY.
- New RPC:
terminal.attach(terminalId) — returns the current scrollback as one chunk and resumes terminal.data notifications. Distinct from terminal.create (no spawn) and terminal.list (no scrollback).
- Client LRU eviction —
TerminalPanel tracks a Set of mounted sessions; when props.sessionId changes and the cache is full, evict the LRU and dispose() its xterm instances. Re-attach on next access.
Trade-offs
- Switching back to an evicted session shows a brief loading state before the scrollback re-renders.
- Output produced between
dispose() and attach() is preserved in the ring buffer up to its size limit; older output is lost.
- Pseudo-shell prompt state may look inconsistent until the PTY emits a fresh prompt (e.g. after pressing Enter).
Acceptance criteria
Estimated work
~200–250 lines across server (ring buffer + attach handler), gateway client/API, TerminalPanel (LRU bookkeeping + re-attach flow), and tests.
Related
Background
Follow-up to the integrated terminal panel work (PR replacing #88). The original review on #88 raised this concern:
The current implementation only enforces server-side limits (
maxPerClient=20,maxPerSession=5) which bounds PTY count but not the front-end xterm renderer footprint.Scope
LRU eviction operates per-session on front-end xterm instances, not on PTY processes:
maxPerClient/maxPerSessionmaxPerSessionper sessionWhen the active-session cache exceeds N, the least-recently-used session's xterm instances are
dispose()-d but their server-side PTYs continue running. When the user switches back, the panel re-creates xterm instances and re-attaches to the live PTYs.Required protocol additions
terminal-service.tskeeps the last ~100 KB of output per PTY.terminal.attach(terminalId)— returns the current scrollback as one chunk and resumesterminal.datanotifications. Distinct fromterminal.create(no spawn) andterminal.list(no scrollback).TerminalPaneltracks a Set of mounted sessions; whenprops.sessionIdchanges and the cache is full, evict the LRU anddispose()its xterm instances. Re-attach on next access.Trade-offs
dispose()andattach()is preserved in the ring buffer up to its size limit; older output is lost.Acceptance criteria
terminal.attachrequest type added tounified.ts,ws-server.ts,gateway-client.ts,gateway-api.ts.TerminalPanelevicts xterm instances per LRU policy with default N=3 active sessions.Estimated work
~200–250 lines across server (ring buffer + attach handler), gateway client/API,
TerminalPanel(LRU bookkeeping + re-attach flow), and tests.Related