fix(tui): bound retained state against idle OOM

Guards four unbounded growth paths reachable at idle — the shape matches
reports of the TUI hitting V8's 2GB heap limit after ~1m of idle with 0
tokens used (Mark-Compact freed ~6MB of 2045MB → pure retention).

- `GatewayClient.logs` + `gateway.stderr` events: 200-line cap is bytes-
  uncapped; a chatty Python child emitting multi-MB lines (traceback,
  dumped config, unsplit JSON) retains everything. Truncate at 4KB/line.
- `GatewayClient.bufferedEvents`: unbounded until `drain()` fires. Cap
  at 2000 so a pre-mount event storm can't pin memory indefinitely.
- `useMainApp` gateway `exit` handler: didn't reset `turnController`, so
  a mid-stream crash left `bufRef`/`reasoningText` alive forever.
- `pasteSnips` count-capped (32) but byte-uncapped. Add a 4MB total cap
  and clear snips in `clearIn` so submitted pastes don't linger.
- `StylePool.transitionCache`: uncapped `Map<number,string>`. Full-clear
  at 32k entries (mirrors `charCache` pattern).
This commit is contained in:
Brooklyn Nicholson 2026-04-19 19:41:25 -05:00 committed by Teknium
parent 424e9f36b0
commit 0d353ca6a8
4 changed files with 44 additions and 5 deletions

View file

@ -448,6 +448,7 @@ export function useMainApp(gw: GatewayClient) {
const handler = (ev: GatewayEvent) => onEventRef.current(ev)
const exitHandler = () => {
turnController.reset()
patchUiState({ busy: false, sid: null, status: 'gateway exited' })
turnController.pushActivity('gateway exited · /logs to inspect', 'error')
sys('error: gateway exited')