Skip to content

Bug: SessionSummary runs on every processor finish-step and repeatedly loads full session history #21761

@sjawhar

Description

@sjawhar

Bug Description

Tool-using sessions can freeze the TUI and drive multi-GB RSS growth because SessionSummary.summarize(...) is invoked on every assistant finish-step, and summarize() loads the full session history (Session.messages({ sessionID })) every time.

This creates a hot loop during multi-step agent execution:

  1. prompt loop re-enters for normal tool-assisted execution
  2. processor finish-step fires for each assistant model turn
  3. processor calls SessionSummary.summarize(...)
  4. summarize loads the full session history and hydrates all parts
  5. memory spikes and repeats on every turn

Root Cause

Primary trigger

packages/opencode/src/session/processor.ts

Inside the finish-step event handler, the processor unconditionally calls:

SessionSummary.summarize({
  sessionID: ctx.sessionID,
  messageID: ctx.assistantMessage.parentID,
})

That means summary runs once per assistant model turn, not once per overall user prompt.

Expensive operation

packages/opencode/src/session/summary.ts

summarize() calls:

const all = yield* sessions.messages({ sessionID: input.sessionID })

With no limit, this hydrates the full message history every time.

Evidence

Live diagnostic repro with labeled binary:

  • prompt loop entry: 4
  • summary start: 4
  • summary loaded messages: 4
  • prompt loop summary trigger: 1
  • prune start: 0
  • session messages full: 10
  • message hydrate: 45

This proves:

  • prompt loop re-entry is happening during normal multi-step execution
  • repeated full-history loads are coming from summary, not prune
  • processor-side summary calls explain the count gap beyond the one prompt-loop trigger

User Impact

  • standalone TUI freezes / becomes unresponsive
  • process RSS grows into multi-GB territory
  • likely contributes to shared serve memory drift under long-running tool sessions

Proposed Fix

  1. Remove the processor-side SessionSummary.summarize(...) call from finish-step
  2. Keep summary responsibility at the prompt-loop level instead of per-step
  3. Add a cheap guard in summary.ts to skip work if the target user message already has summary.diffs

Metadata

Metadata

Assignees

Labels

coreAnything pertaining to core functionality of the application (opencode server stuff)perfIndicates a performance issue or need for optimization

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions