Skip to content

fix(session): prune turns after each runTurn to prevent heap OOM - #214

Open
yablokolabs wants to merge 1 commit into
AtomicBot-ai:mainfrom
yablokolabs:fix/tui-oom-session-pruning
Open

fix(session): prune turns after each runTurn to prevent heap OOM#214
yablokolabs wants to merge 1 commit into
AtomicBot-ai:mainfrom
yablokolabs:fix/tui-oom-session-pruning

Conversation

@yablokolabs

Copy link
Copy Markdown
Contributor

Summary

Fixes #121. SessionState.turns[] grows unbounded — every assistant_tool_call, tool_result, and assistant_reply is appended forever. Large tool outputs (file reads, web fetches) in a 25+ step session push the V8 heap past 4 GB.

The TUI side (feed, messages, reasoning) was already bounded via pushRing with ringBufferSize. The real culprit was turns[].

Fix

packConversation already computes the token-budgeted visible window at prompt-build time. This PR applies the same windowing at the persistence boundary: after runTurn returns, pruneSessionTurns() drops old turns beyond conversationMaxTokens and replaces them with a compact summary line. The next prompt build sees the pruned state; packConversation computes a fresh summary from what remains.

The pruning is zero-cost when turns already fit within budget (returns a copy of the input array). It only activates when the accumulated turns exceed the token cap.

Changes

  • src/session/conversation-turn.ts — new pruneSessionTurns(turns, maxTokens) pure function
  • src/runtime/bootstrap.ts — call pruneSessionTurns before sessionStore.save in executeTurn
  • src/session/index.ts — export the new function

Test coverage

5 new tests:

  • Within-budget passthrough (no pruning)
  • Over-budget pruning with summary prepended
  • Empty input
  • Immutability guarantee (input array not mutated)
  • Large-turn-list pruning

Verification

  • npm run lint clean
  • 5043/5049 tests pass (2 pre-existing flaky: send-message-concurrency, llm-health-poller)

…mory

Issue AtomicBot-ai#121: TUI crashes with heap OOM after 25+ tool-call steps because
SessionState.turns grows unbounded — every assistant_tool_call,
tool_result, and assistant_reply is appended forever. Large tool outputs
(file reads, web fetches) push long sessions past the V8 heap limit.

The TUI side (feed, messages, reasoning) was already bounded via
pushRing with ringBufferSize. The real culprit was turns[].

Add pruneSessionTurns() which applies the same packConversation
token-based windowing at the persistence boundary: after runTurn
returns, old turns beyond the conversationMaxTokens budget are dropped
and replaced with a compact summary line. The next prompt build sees
the pruned state and packConversation computes a fresh summary from
what remains.

5 new tests covering: within-budget passthrough, over-budget pruning,
empty input, immutability guarantee, large-turn-list pruning.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TUI crashes with JavaScript heap OOM during long agentic sessions (25+ tool-call steps)

1 participant