parseLargeJsonl dropped promptSource for exactly the lines SDK-generated
prompts live on, so the recurring-context detector regex-scanned the ends of
the raw line for it. Add the field to LARGE_ROOT_FIELDS (tiny scalar,
add-only, isSidechain already there) and delete the workaround: it read only
2 KB from each end, so a flag further in was missed.
No cache change: optimize scans the raw JSONL each run, so promptSource never
has to persist on CachedFile.
Fixes#1030. With #994 this closes#1023.
Three-layer fix for V8 heap exhaustion when parsing heavy session data:
1. Buffer-based readSessionLines (fs-utils.ts): Replace readline with raw
Buffer streaming using Buffer.indexOf(0x0a). Eliminates ConsString trees
that caused OOM when regex-flattening 100MB+ lines. Two-state machine
(ACCUMULATING/SCANNING) skips old lines at ~2KB cost instead of 200MB.
2. Large-line streaming parser (parser.ts): Hand-written JSON scanner for
lines >32KB extracts only cost/token/tool fields without JSON.parse,
avoiding full object graph allocation. Dual string/Buffer paths.
3. Dashboard memory management (dashboard.tsx): Disable auto-refresh for
heavy periods (30d/month/all), clear old dataset before reload via
nextTick to allow GC, prevent overlapping reloads with mutex, lazy
optimize scanning on keypress instead of useEffect.
Also fixes three race conditions in dashboard reload deduplication:
- Early return after nextTick bypassing finally block (permanent mutex lock)
- A->B->A period switching dropping final reload (stale pending)
- Stale pendingReloadRef not cleared when in-flight matches request