Fix TUI freezing caused by memory leak and excessive CPU usage.
- Process consumes 99.4% CPU and 5.5GB memory after ~19 iterations
- Unbounded event array grows indefinitely, never trimmed
- OpenTUI creates new TextBuffers on every render cycle
- 30fps render + 80ms spinner + growing event list = allocation thrashing
- Add
MAX_EVENTSconstant tosrc/state.ts(e.g., 200) - Update
onEventcallback insrc/index.tsto slice events array to keep only lastMAX_EVENTS - Add unit test in
tests/unit/state.test.tsto verify event trimming behavior - Verify memory usage stays bounded after 20+ iterations
- Lower
targetFpsfrom 30 to 15 insrc/app.tsxrender options - Increase spinner interval from 80ms to 120ms in
src/components/log.tsx - Increase elapsed time update interval from 1000ms to 2000ms in
src/app.tsx - Verify spinner still appears smooth at 120ms interval
- Extract event item key function in
src/components/log.tsxto use stable keys (e.g.,${iteration}-${timestamp}) - Wrap
ToolEventItemcomponent with memoization to prevent re-renders of unchanged items - Wrap
SeparatorEventcomponent with memoization to prevent re-renders of unchanged items - Add
indexprop to<For>loop for stable keying
- Create
batchStateUpdatehelper function insrc/index.tsto coalesce rapid state changes - Debounce
onEventcallback to batch events arriving within 50ms window - Debounce
onDiffUpdatedandonCommitsUpdatedcallbacks (these can lag slightly) - Ensure
onIterationStartandonIterationCompleteremain unbatched (user needs immediate feedback)
- Add
isIdlestate flag toLoopStateinsrc/state.ts - Set
isIdle: truewhen waiting for LLM response (after prompt sent, before events arrive) - Set
isIdle: falsewhen tool events start arriving - Skip elapsed timer updates when
isIdle: true(reduce unnecessary re-renders) - Only animate spinner when
isIdle: false(static spinner during idle waits)
- Investigate OpenTUI scrollbox
stickyScrollperformance with large content - Consider adding
overflow="hidden"and manual scroll position management - Test if disabling scrollbar reduces TextBuffer allocations
- Profile before/after to measure improvement
- Add memory usage logging to
src/util/log.ts(logprocess.memoryUsage()periodically) - Create integration test that runs 50 mock iterations and asserts memory stays under 500MB
- Document expected memory footprint in README.md
- Add
--profileCLI flag to enable verbose performance logging
- Remove any unused imports introduced during optimization
- Add code comments explaining performance-critical sections
- Update README.md with performance characteristics and known limitations
- Run full test suite to ensure no regressions