Skip to content

feat(compact): add forge_compact crate with lossless sliding-window conversation compaction#3081

Open
tusharmath wants to merge 4 commits intomainfrom
compaction-lossless-10-apr
Open

feat(compact): add forge_compact crate with lossless sliding-window conversation compaction#3081
tusharmath wants to merge 4 commits intomainfrom
compaction-lossless-10-apr

Conversation

@tusharmath
Copy link
Copy Markdown
Collaborator

@tusharmath tusharmath commented Apr 19, 2026

Summary

Add a new forge_compact crate implementing a generic, lossless conversation compaction algorithm using a sliding window approach, and clean up the existing compaction code by removing the token_threshold_percentage feature and over-defensive error handling.

Context

Conversation compaction is critical for long-running agent sessions: as context grows, it must be summarised before exceeding model limits. The existing compaction logic in forge_app was tightly coupled to domain types and contained defensive fallbacks that masked bugs. This PR extracts the core algorithm into a standalone, fully-tested crate (forge_compact) and trims the feature surface of Compact config to what is actually needed.

Changes

  • New forge_compact crate: generic Compaction<Item> struct with a compact_conversation sliding-window algorithm that works with any type implementing ContextMessage
  • Summary type: wraps original messages alongside their synthesised summary, enabling lossless reconstruction of compacted history
  • compact_conversation algorithm: grows a window from size 1 up to the full length, triggers compaction when the caller-supplied threshold is exceeded, and restarts after each successful compaction; respects retain to keep the most-recent messages untouched
  • find_compact_range: correctly handles tool-call / tool-result pairs so they are never split across a compaction boundary
  • Removed token_threshold_percentage from Compact config — the percentage-based capping logic was removed in favour of simpler absolute thresholds
  • Simplified Compactor in forge_app: replaced .get(start..=end) defensive fallbacks with direct slice indexing (panics are now loud, not silent bugs)
  • Removed dead tests that were testing the removed token_threshold_percentage feature

Key Implementation Details

Compaction<Item> is fully generic over any Item: ContextMessage + Clone. The caller supplies two closures: summarize (produces a single summary item from a slice) and threshold (decides whether the current window needs compacting). A retain count pins the most-recent messages outside the compaction window, preventing recent context from being summarised away.

The sliding window algorithm is designed to be lossless — each Message::Summary carries a source field with the original messages, so callers can reconstruct the full history if needed.

Testing

The new crate ships with a comprehensive unit test suite covering:

  • Basic single-pass compaction
  • Compaction restart after a new summary is introduced
  • retain window pinning the tail of the conversation
  • Correct tool-call / tool-result boundary handling
  • Cache-key stability (deterministic output for the same input)
cargo insta test --accept -p forge_compact
cargo insta test --accept -p forge_app

Links

  • Related to ongoing context-management work in the forge_app compaction pipeline

@tusharmath tusharmath changed the title compaction lossless 10 apr feat(compact): add forge_compact crate with lossless sliding-window conversation compaction Apr 19, 2026
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.

1 participant