Repo: https://github.com/RooVetGit/Roo-Code
Commit: 3e237e6 (v0.1.17)
Language: TypeScript (VS Code extension)
Roo Code calls its compression system "condensation". It's a full extract mechanism using a "fresh start model": all prior messages are non-destructively tagged and hidden; the new effective history is a single summary user message. A separate LLM API call performs the summarisation. Sliding window truncation exists as a fallback when condensation fails or is disabled.
src/core/condense/index.ts—summarizeConversation(),getEffectiveApiHistory()
Not a piggyback. This is a full history replacement, but non-destructive:
- A separate API call is made with the conversation history + condensation prompt
- The model responds with a
<analysis>...<summary>...structured text - All existing messages get tagged with
condenseParent: <uuid>(stored but hidden) - A new user-role summary message is appended with
isSummary: trueandcondenseId: <uuid> getEffectiveApiHistory()slices history from the summary forward — old messages never sent to API again
// tag all prior messages
const newMessages = messages.map(msg => {
if (!msg.condenseParent) return { ...msg, condenseParent: condenseId }
return msg
})
// append summary
newMessages.push(summaryMessage) // role: "user", isSummary: trueWhy user-role? So the next assistant turn starts cleanly — the model "reads" its own briefing as a user instruction.
Non-destructive: Messages are never deleted from storage. If the user rewinds past the condensation point, cleanupAfterTruncation() clears orphaned condenseParent references and the full history is restored.
src/core/context-management/index.ts—manageContext(),willManageContext()
Condensation fires when either condition is true:
contextPercent >= effectiveThreshold
OR
prevContextTokens > allowedTokens
Where:
contextPercent = (100 * prevContextTokens) / contextWindowallowedTokens = contextWindow * (1 - TOKEN_BUFFER_PERCENTAGE) - reservedTokensTOKEN_BUFFER_PERCENTAGE = 0.1(10% buffer)reservedTokens = maxTokens || ANTHROPIC_DEFAULT_MAX_TOKENS
autoCondenseContext = true(enabled by default)autoCondenseContextPercent = 100(fires at 100% fill — in practice, theallowedTokenscheck fires first, around ~85-90%)
The allowedTokens formula effectively means condensation fires at approximately 90% of the context window minus the max output tokens. With Claude Sonnet 3.5 (200k context, 8k max output): fires at ~200k * 0.9 - 8k = 172k tokens.
Three levels of control:
- Global toggle:
autoCondenseContext(on/off) - Global threshold:
autoCondenseContextPercent— user-configurable, clamped to[5, 100]% - Per-profile threshold:
profileThresholds[currentProfileId]— overrides global per-mode profile.-1means inherit global.
Custom condensing prompt: customCondensingPrompt setting — replaces entire supportPrompt.default.CONDENSE template if set.
Three scenarios trigger condensation:
src/core/task/Task.ts—recursivelyMakeClineRequests()
After each assistant turn, token count is checked. If over threshold, manageContext() runs.
src/core/task/Task.ts~line 3850, constantFORCED_CONTEXT_REDUCTION_PERCENT = 75
If the API returns a context-window-exceeded error (checkContextWindowExceededError()), condensation is forced immediately with threshold overridden to 75% (i.e., "compress enough to keep 75% of the window").
User can click a button in the UI to manually trigger condensation at any time. Does not include environment details in the summary (they'll be freshly injected on the next turn).
src/core/context-management/index.ts—truncateConversation()
When condensation fails (API error) or is disabled and tokens exceed allowedTokens, sliding window truncation runs:
- Tags 50% of visible messages (excluding the first) with
truncationParent: <uuid> - Inserts a
isTruncationMarkeruser message at the boundary:"[Sliding window truncation: N messages hidden to reduce context]" - Also non-destructive: messages can be restored on rewind
Also non-destructive: uses truncationParent UUID linking, same pattern as condenseParent.
After condensation, the stored summary message looks like:
role: "user"
isSummary: true
condenseId: <uuid>
content: [
{ type: "text", text: "<analysis>...\n<summary>...</summary>" },
// optionally:
{ type: "text", text: "<system-reminder>\n<command>...</command>\n</system-reminder>" },
{ type: "text", text: "<system-reminder>\n[Folded file context]\n</system-reminder>" },
{ type: "text", text: "<environment_details>...</environment_details>" }, // auto only
]
Three notable extras appended to the summary content:
extractCommandBlocks()— extracts<command>...</command>XML from the first message
Active shell commands / workflows from the original task are extracted and re-injected wrapped in <system-reminder> tags. Survives across multiple consecutive condensations.
src/core/condense/foldedFileContext.ts—generateFoldedFileContext()
Files read during the session (filesReadByRoo) are processed through tree-sitter to extract only function signatures and class declarations (not bodies). These are injected as additional <system-reminder> blocks alongside the summary. Capped at 50,000 characters total.
This means the model retains structural awareness of previously-read source files even after condensation.
When condensation fires automatically mid-turn (isAutomaticTrigger=true), the current environment details (open files, terminal output, etc.) are appended to the summary content. When triggered manually, they're omitted (fresh details will arrive on the next user turn).
Two-layer design:
| Layer | Role | Content |
|---|---|---|
| System | SUMMARY_PROMPT |
Short anti-hallucination guard: "SYSTEM OPERATION, not user message, no tool calls" |
| Final user message | supportPrompt.default.CONDENSE |
9-section structured template with full example |
The user-turn prompt is fully replaceable via customCondensingPrompt. The system prompt is hardcoded.
The summarisation call uses the same apiHandler as the main conversation (same model). Tools are passed in the metadata but tool calls are blocked via the system prompt.
| Scenario | Handling |
|---|---|
Condensed recently (summary at end of getMessagesSinceLastSummary) |
Returns error if only 1 message since last summary |
Orphaned tool_use without matching tool_result |
injectSyntheticToolResults() adds fake result: "Context condensation triggered. Tool execution deferred." |
Providers requiring tools param for tool blocks |
transformMessagesForCondensing() converts all tool blocks to plain text |
| Image blocks in history | maybeRemoveImageBlocks() strips if provider doesn't support |
| Multiple condensations | Nested condenseParent handled: only new messages (without existing condenseParent) get tagged; prior tagged messages left as-is |
| Rewind past condensation | cleanupAfterTruncation() clears orphaned condenseParent/truncationParent refs → full history restored |
| Condensation API call fails | Falls through to sliding window truncation if tokens still exceed allowedTokens |
| Context window error from API | Forces condensation at 75% threshold; on repeated failure, truncation |
| Setting | Default | Range | Description |
|---|---|---|---|
autoCondenseContext |
true |
bool | Master toggle |
autoCondenseContextPercent |
100 |
5–100 | % of context window that triggers condensation |
profileThresholds[profileId] |
undefined | 5–100 or -1 | Per-profile override; -1 = inherit global |
customCondensingPrompt |
"" |
string | Replaces entire user-turn condensation prompt |
| File | Purpose |
|---|---|
src/core/condense/index.ts |
Core: summarizeConversation(), getEffectiveApiHistory(), truncateConversation(), SUMMARY_PROMPT |
src/shared/support-prompt.ts |
supportPrompt.default.CONDENSE — the 9-section user-turn prompt |
src/core/context-management/index.ts |
manageContext(), willManageContext(), TOKEN_BUFFER_PERCENTAGE, threshold logic |
src/core/condense/foldedFileContext.ts |
Tree-sitter file signature extraction for post-summary injection |
src/core/task/Task.ts |
Trigger points: auto (~line 4000), forced (~line 3850), UI flow |
src/core/context/context-management/context-error-handling.ts |
API error detection for forced condensation |
src/core/message-manager/index.ts |
MessageManager — rewind/undo handling, cleanup of orphaned parent refs |