Files
orca/src/main
Brennan Benson 150cc6f50c fix(daemon): retain a flat per-session scrollback window (#13061)
* fix(daemon): bound retained scrollback across sessions

The daemon retains ~5000 rows of xterm grid per live session with no aggregate
bound, so retention scales with an unbounded session count. A host owning 100+
terminals held ~1 GB of grid, was killed under system memory exhaustion, and took
every session it owned with it.

Split a fixed row budget across live sessions instead: full depth until the budget
binds, then an even share down to a floor that still restores the command which
produced the visible screen. Re-applied on create and reap, so depth returns to
survivors as terminals exit.

The emulator itself stays load-bearing (on-disk history is a projection of it), so
this bounds the aggregate rather than lowering the per-session default.

* fix(daemon): preserve scrollback budget invariants

* fix(daemon): budget scrollback by retained cells

* fix(daemon): retain full scrollback for viewed sessions, trim only parked LRU overflow

Replace the even-split budget: dividing a fixed row budget across all sessions
shallowed every terminal as the count grew, so a user with 60 terminals silently
lost half their reachable scrollback — depth degradation with no signal.

Retention now trims by attention, not arithmetic. Attached sessions always keep
full depth and never consume the cap. Parked sessions keep full depth up to a
cap of 24, least-recently-viewed evicted first down to 1000 rows — enough to
reattach with the recent command context on screen. A reattached session
returns to full depth for everything it emits afterward, and a freed slot
returns the newest trimmed session to deep retention.

Worst case is bounded at cap x full depth plus trimmed remainder, the same
memory class as the old budget, without ever shallowing a terminal the user is
looking at.

* fix(daemon): release attachments on transport drop so dead clients cannot pin full depth

The attached-client exemption made an attachment that outlives its transport a
permanent full-depth pin: protocol detach was logging-only, and neither control-
nor stream-socket loss removed the Session.attachedClients entry. Enough dead
attachments would silently rebuild the unbounded retention the LRU cap prevents.

Track the attach token per session, release exactly the dropped client's
attachments in one batched retention pass, implement protocol detach for real,
and cancel an attach whose client vanished mid-flight. Move session-exit
bookkeeping to the host reap hook — it previously lived in the per-attachment
exit callback, which only fired for unattached sessions BECAUSE of the leak, so
fixing the leak made an unattached session's exit invisible to idle shutdown.

Also drop the create-time double recency increment.

Co-developed with a review pass; transport-drop release is regression-tested
against the unfixed daemon (fails without, passes with).

* refactor(daemon): move retention application into the policy module

terminal-host.ts crossed the 300-line cap; the entry-building and depth
application belong with the selection policy anyway, leaving the host with
only the recency bookkeeping it uniquely owns.

* fix(daemon): remove stray QA artifacts and restore the emulator line budget

The previous commit accidentally swept QA screenshots and a report from the
repo root into the tree (root-directory-guard failure), and headless-emulator
had crept to 301 counted lines. Screenshots live on the PR via the attachments
CDN, never in the tree. The retained-scrollback trim moves to
headless-emulator-modes with the OSC-link shift as a pure helper.

* fix(daemon): retain a flat per-session scrollback window

Replace the LRU parked-session retention with what every peer terminal ships:
a flat, small per-session window in the durable host. Sessions retain 1000
rows — the generous end of what terminal products restore on a rebuild — and
deep scrolling on an open terminal remains the renderer's live buffer.

This deletes the dynamic-retention machinery entirely: no recency tracking, no
attached-exemption, no runtime trimming, no OSC-link shift on trim. The window
is set once at session creation. Worst case at the incident's 100+ sessions is
~100k rows of grid, versus ~500k before.

The bounded env override can tune the window within [100, 5000]; anything
outside falls back to the default, since an unbounded daemon is the failure
this window exists to prevent.

The v29->v30 history-handoff fixture now pins the old-daemon depth explicitly:
it plays an old binary whose sessions retained ~5000 rows, and the new flat
window would otherwise shrink its history below the chunked-seed threshold and
silently skip the transfer path the test exists to cover.

* docs(daemon): retire retention-era wording the flat window obsoleted
2026-08-12 19:16:34 -07:00
..
2026-08-12 18:53:17 -07:00
…
2026-08-12 18:53:17 -07:00
2026-08-12 18:53:17 -07:00
2026-08-12 18:53:17 -07:00
2026-08-12 18:53:17 -07:00
2026-08-02 00:01:23 -07:00
2026-08-12 18:53:17 -07:00
2026-08-12 18:53:17 -07:00
2026-08-09 19:01:35 -07:00