How the Context Pruner Decides What to Keep or Discard in CodebuffAI

The context pruner uses a deterministic, budget-driven pipeline with separate token caps for user messages (50,000 tokens) versus assistant/tool messages (20,000 tokens), always forcing retention of the latest entry even if it exceeds budget.

The context pruner is a hidden agent in the CodebuffAI/freebuff repository that runs before every step of a parent agent. Its job is to compress conversation history into a token budget while preserving critical information for coherent multi-turn interactions. Understanding how the context pruner makes keep-or-discard decisions helps developers debug truncated conversations and optimize their agent configurations.

When the Context Pruner Activates

The pruner does not run on every turn. Two conditions in agents/context-pruner.ts trigger its execution:

  1. Token overflow — when agentState.contextTokenCount + TOKEN_COUNT_FUDGE_FACTOR exceeds maxContextLength (lines 4005–4007)
  2. Prompt cache expiry — when the time gap between the last assistant message and current user message exceeds CACHE_EXPIRY_MS (default 5 minutes, lines 3984–4002)

Both checks appear in the contextLimitExceeded and cacheWillMiss functions. The fudge factor provides a safety buffer against token estimation errors.

// Trigger logic from agents/context-pruner.ts (~lines 4005-4012)
if (agentState.contextTokenCount + 1_000 > maxContextLength) {
  // Context exceeded → pruner will run
}
if (gapMs > CACHE_EXPIRY_MS) {
  // Prompt cache miss → pruner will run
}

The Seven-Step Pruning Pipeline

Once triggered, the context pruner executes a deterministic pipeline:

Step 1: Strip Control Messages

The pruner removes hidden control messages that have no semantic value for the model. The shouldExcludeMessage function (lines 444–447) filters out:

  • INSTRUCTIONS_PROMPT
  • STEP_PROMPT
  • SUBAGENT_SPAWN

Live-prompt parameters not needed for the next turn are also removed.

Step 2: Summarize Remaining Messages

Every retained message is compressed into a concise representation with role-specific length limits:

Message Type Transformation Character Limit
User Truncated text + optional image note 39,000 chars (~13,000 tokens × 3)
Assistant Progress notes + tool-call summaries 3,900 chars (~1,300 tokens × 3)
Tool Concise error/command/result summaries Per TOOL_ENTRY_LIMIT

The truncateLongText helper (lines 107–118) and role-specific summarizers (lines 665–1083) enforce these caps.

Step 3: Merge with Prior Summary

If a previous <conversation_summary> exists, its entries are parsed via parseSummaryIntoEntries (lines 779–798) and merged with newly generated summaries (lines 868–974). This preserves continuity across multiple pruning cycles.

Step 4: Apply Independent Token Budgets

The pruner enforces two strictly separate budgets:

  • User budget: 50,000 tokens (USER_BUDGET, line 82)
  • Assistant-tool budget: 20,000 tokens (ASSISTANT_TOOL_BUDGET, line 79)

Entries are walked backwards (newest first) and added until a role's budget exhausts. This design prevents user messages from starving assistant context or vice versa.

// Budget enforcement logic (~lines 877-908)
// Walk entries backwards, newest first
for (const entry of entries.reverse()) {
  const budget = entry.role === 'user' ? USER_BUDGET : ASSISTANT_TOOL_BUDGET;
  if (runningTotal + entry.estimatedTokens <= budget) {
    kept.push(entry);
    runningTotal += entry.estimatedTokens;
  }
  // Entries exceeding budget are dropped
}

Step 5: Force-Keep the Newest Entry

Regardless of budget constraints, the latest entry is always retained. The newestEntryForced logic (lines 1115–1121) ensures the current turn can continue even if that single entry exceeds its entire role budget.

Step 6: Handle Mid-Turn Pruning

When pruning occurs mid-turn, the pruner injects a synthetic continuation prompt instead of the live user prompt. This maintains consistent turn-boundary semantics for the model.

Step 7: Emit Pruned History

The final output includes:

  1. A new <conversation_summary> block with kept entries
  2. Original INSTRUCTIONS_PROMPT (if present)
  3. Either the live user prompt or continuation prompt

The pruner calls set_messages (lines 1089–1095) to replace the parent's message history.

Key Decision Factors in Context Pruning

Factor Implementation Detail
Token count with fudge factor agentState.contextTokenCount + TOKEN_COUNT_FUDGE_FACTOR > maxContextLength
Cache expiry timing CACHE_EXPIRY_MS default of 300,000ms (5 minutes)
Role-specific budgets Independent 50K/20K token caps prevent starvation
Per-type message limits Hard character caps on user, assistant, and tool entries
Agent output blacklist Certain agents (file-picker, researcher-web) excluded from summarization
Mid-turn awareness Continuation prompt injection for interrupted turns

Where Context Pruning Happens

File Purpose
agents/context-pruner.ts Core agent implementation—trigger logic, summarization, budgeting, output
packages/agent-runtime/src/compact-history.ts In-process equivalent for agents with compactContext: true
agents/general-agent/general-agent.ts Shows auto-spawn configuration for typical agents
__tests__/context-pruner-parity.test.ts Verifies behavioral parity between agent and in-process versions
e2e/context-pruner.e2e.test.ts End-to-end pruner behavior validation

Agent Auto-Spawn Configuration

Most agents automatically invoke the context pruner through their spawnableAgents array:

// From agents/general-agent/general-agent.ts (line 86)
{
  spawnableAgents: ['context-pruner'],
  // Parent agent automatically invokes pruner before each step
}

Agents can opt into lighter in-process pruning instead by setting compactContext: true, which uses the compact-history.ts implementation without spawning a subagent.

Summary

  • The context pruner triggers on token overflow or cache expiry, not every turn
  • It removes control messages first, then summarizes remaining content with role-specific length limits
  • Two independent budgets (50K user, 20K assistant/tool) are enforced by walking entries newest-first
  • The latest entry is always kept, even if it alone exceeds budget
  • Mid-turn pruning injects a continuation prompt to preserve turn semantics
  • Implementation spans both the standalone context-pruner.ts agent and the in-process compact-history.ts utility

Frequently Asked Questions

What triggers the context pruner to run?

The pruner runs when either: (1) the token count plus fudge factor exceeds maxContextLength, or (2) the time since the last assistant message exceeds CACHE_EXPIRY_MS (5 minutes). These checks appear in cacheWillMiss and contextLimitExceeded within agents/context-pruner.ts.

Why are there separate budgets for user versus assistant/tool messages?

Independent budgets prevent one message type from monopolizing context window. The 50,000-token user budget and 20,000-token assistant-tool budget ensure both conversation history (what the user asked) and execution context (what the assistant did) remain accessible.

What happens if the newest message alone exceeds its role's budget?

The newestEntryForced logic (lines 1115–1121) retains it regardless. This guarantees the current turn can proceed, though earlier entries in that role's budget may be entirely discarded.

How does mid-turn pruning differ from end-of-turn pruning?

Mid-turn pruning occurs when the agent has made partial progress (tool calls issued but not completed). Instead of the live user prompt, the pruner injects a synthetic continuation prompt to maintain consistent turn boundaries for the model.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →