Skip to content

Conversation

@hendem
Copy link

@hendem hendem commented Jan 6, 2026

Fixes #3013

Summary

Clear tool output and attachments when compaction prunes old tool results, actually freeing memory instead of just flagging.

Evidence

In a real session working on this repo:

Metric Value
OpenCode process RAM 16.4 GB
Tool parts on disk 34,887 files
Session duration ~3 hours

Sample tool output sizes from this session:

  • webfetch of docs page: 10 KB
  • read of source file: 5-50 KB
  • git diff / gh pr diff: 10-100 KB
  • bash command outputs: 1-50 KB

All of these outputs stay in memory even after compaction marks them "old".

The Problem

In SessionCompaction.prune(), when tool outputs are pruned:

// BEFORE: Only sets a flag, output data stays in memory
part.state.time.compacted = Date.now()
await Session.updatePart(part)

The compacted timestamp is used by toModelMessage() to replace output with placeholder text like "(Old tool result content cleared)" - but the actual data never gets freed.

Over a long session:

  • 34,887 tool calls × average output size = hundreds of MB to GBs retained
  • Memory never decreases, even after compaction runs
  • Eventually Bun runs out of memory and crashes

The Fix

// AFTER: Actually clear the data
part.state.time.compacted = Date.now()
part.state.output = ""              // Free the output string
part.state.attachments = undefined  // Free any attachments
await Session.updatePart(part)

Now when compaction runs, the memory is actually freed. The placeholder text is already shown to the LLM (that logic exists), we just were not clearing the source data.

Testing

Existing compaction tests pass. Added test verifying output/attachments are cleared after prune.

Copilot AI review requested due to automatic review settings January 6, 2026 05:49
@github-actions
Copy link
Contributor

github-actions bot commented Jan 6, 2026

The following comment was made by an LLM, it may be inaccurate:

No duplicate PRs found

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a memory leak where compacted tool outputs and their attachments were retained indefinitely in storage. When the pruning mechanism marks old tool outputs as compacted, the fix now clears both the output string and attachments array to free memory.

Key changes:

  • Clears tool output and attachments during compaction to prevent unbounded memory growth
  • Adds comprehensive test coverage for the pruning behavior

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
packages/opencode/src/session/compaction.ts Sets output to empty string and attachments to undefined when marking tool parts as compacted, with explanatory comment
packages/opencode/test/session/compaction.test.ts Adds two new test cases: one verifying output/attachments are cleared during pruning, and one testing that pruning respects the disabled configuration

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@hendem
Copy link
Author

hendem commented Jan 6, 2026

Addressed review comment about test not verifying behavior when pruning is disabled (commit 4881734).

The test now:

  1. Creates a tool part with large output (200,000 chars) and an attachment (similar to the first test)
  2. Creates additional user messages to get past turn protection
  3. Calls prune() with pruning disabled via config
  4. Verifies that:
    • output.length remains 200,000 (unchanged)
    • attachments.length remains 1 (unchanged)
    • time.compacted is undefined (not set)

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@kevoconnell
Copy link

this would be really helpful

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 9, 2026

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

Mark Henderson and others added 7 commits January 10, 2026 14:14
… leak

When compaction prunes old tool outputs, only the compacted timestamp
was being set - the actual output string and attachments array remained
in storage indefinitely. This caused storage files to grow unbounded
with large tool outputs (file contents, base64 images/PDFs, etc.).

Now prune() clears both output and attachments when marking parts as
compacted. The toModelMessage function already replaces compacted
outputs with placeholder text, so this is safe.

Fixes part of anomalyco#4315
This reverts commit b302472.
@fwang fwang force-pushed the fix/memory-leak-compaction-cleanup branch from 9d089b6 to 141c413 Compare January 10, 2026 19:15
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.

Uses a huge amount of memory

4 participants