Summary
Transcript files grow without bound (no rotation, compaction, or truncation ever) and are read whole into memory on resume. The 20 s resume deadline is only checked between sessions, so a single huge transcript blocks daemon startup and can OOM.
Location
src/daemon/transcript.ts:207-224 — await file.text() then per-line JSON.parse of the entire file.
src/daemon/session-manager.ts:133-139 — resume deadline checked between sessions only.
- write amplification: up to three full JSONL lines per tool call —
session.ts:1940 (tool_start), :1609 (approval resolution), :1966 (completion including full tool output).
restoreScrollback re-stringifies every historical message for size accounting even though only the last 5000 / 20 MB survive eviction.
Failure scenario
A months-old autonomous session accumulates a 300 MB–1 GB transcript. On restart, resume calls file.text() — the whole read/parse runs under no deadline (the check is only between sessions), blocking startup and risking OOM. packSession (export) hits the same full read on demand.
Fix
Rotate/segment transcripts (size- or time-based) with an index; stream-parse on resume with the deadline applied within a file; cap per-episode persisted output; drop the full re-stringify in restoreScrollback (track sizes incrementally).
Summary
Transcript files grow without bound (no rotation, compaction, or truncation ever) and are read whole into memory on resume. The 20 s resume deadline is only checked between sessions, so a single huge transcript blocks daemon startup and can OOM.
Location
src/daemon/transcript.ts:207-224—await file.text()then per-lineJSON.parseof the entire file.src/daemon/session-manager.ts:133-139— resume deadline checked between sessions only.session.ts:1940(tool_start),:1609(approval resolution),:1966(completion including full tool output).restoreScrollbackre-stringifies every historical message for size accounting even though only the last 5000 / 20 MB survive eviction.Failure scenario
A months-old autonomous session accumulates a 300 MB–1 GB transcript. On restart, resume calls
file.text()— the whole read/parse runs under no deadline (the check is only between sessions), blocking startup and risking OOM.packSession(export) hits the same full read on demand.Fix
Rotate/segment transcripts (size- or time-based) with an index; stream-parse on resume with the deadline applied within a file; cap per-episode persisted output; drop the full re-stringify in
restoreScrollback(track sizes incrementally).