code-yeongyu / code-yeongyu/senpi

bug(codemode): eval cells, tool-call queue, and display buffers are retained for the session lifetime (unbounded memory growth)

Closed
#1,695 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
429
Forks
98
Avg merge
5h 3m
Merged PRs (30d)
526

Description

## Summary
Three unbounded-retention defects in packages/senpi-codemode make every long-lived session's JS heap grow without limit:

1. src/tool/detached-cell-manager.ts — #cells entries are never removed once a cell settles. The only #cells.delete is at line 84 (same-id reuse), and dispose() (lines 186-194) stops detached cells but never clears the map. Every eval cell's input, live result, terminal result, and onKill closure (capturing the whole CellExecution) is retained for the session lifetime.
2. src/kernels/js/context-manager.ts:216 — every tool-call kernel message is pushed to #pendingToolCalls when no waiter is registered. The only consumer is nextToolCall() (line 83), which has no caller on the normal production path (git grep: only tests and the subprocess-kernel wrapper). Full tool arguments (write/edit bodies, task prompts) accumulate forever.
3. src/tool/image.ts — #displayImages (line 95) and the JSON display list grow without any byte or count cap; compaction accounting (compaction.ts:408-411) counts message content but not these details, so compaction never relieves the heap.

## Context
Root-caused during a profiling investigation of a long-lived macOS host (M4 Pro, 64 GB) running ~28 concurrent senpi/omo sessions for several days. Symptoms: load average ~150 with only ~50% CPU utilization (threads blocked on swap I/O), 20 GB swap in use, idle sessions each burning 2-8% CPU sustained, and idle session heaps growing to multiple GB (one 4-day-idle session: 648 MB resident JSC heap + 1.4 GB swapped, 3.3 GB peak footprint). All file:line references are against main (b19491745). Raw profiling evidence held locally.

## Expected (ideal state)
Settled cells are removed from the registry (terminal snapshots remain available via the notification/artifact path, or a small bounded LRU); the tool-call fallback queue is bounded or absent on paths that cannot consume it; display buffers have byte/count caps with oldest-first eviction and are included in compaction accounting.

## Acceptance criteria
- Unit tests prove: after N settled eval cells the registry size stays bounded; the tool-call queue does not grow on the normal path; display buffers evict beyond the cap.
- A soak running repeated eval cells shows heap delta plateau instead of linear growth.

## Related
- #1461 (compaction/session memory growth — same theme, different surface)

Contributor guide

Open the contributing guide

Research direction

Read packages/senpi-codemode/src/tool/detached-cell-manager.ts, src/kernels/js/context-manager.ts, src/tool/image.ts, and the compaction accounting at compaction.ts:408-411. Add unit tests for bounded settled-cell registries, the normal tool-call path, and display-buffer eviction, then run a repeated-evaluation soak to confirm heap growth plateaus.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
performance, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.