All notes

#204 Pi: How compaction works

August 20, 2026·5 min read

#204 — Pi: How compaction works

The big picture

Coding AI agents hit a hard ceiling when conversations run too long, and their handling of that ceiling determines whether automated tools remain viable during extended build sessions.

Why it matters: Long AI-assisted coding sessions frequently trigger an error stating "Request exceeds the maximum size." This is a structural limit of the transformer context window, which must process every preceding token on each turn.

How context accumulates

Each turn in a coding agent is an compounding execution chain.

  • Request 1 sends the system prompt, tool definitions, instruction files like AGENTS.md, and the initial user message.
  • The model outputs tool calls, which the agent executes locally before returning the raw outputs to the model.
  • That entire chain resends on request 2 alongside your next message.
  • The conversation history expands with every subsequent exchange.

Between the lines: Research from Chroma on context rot demonstrates that model precision degrades as input token counts increase, well before reaching the hard window limit.

Two paths at the limit

When the accumulated token count reaches the context boundary, two recovery options exist:

  • Clear the session to restore baseline model reasoning, which discards prior technical decisions and working context.
  • Run compaction to compress older history into a structured summary and continue working.

Pi defaults to compaction because discarding context mid-task removes constraints that the next code generation step requires.

What compaction does

Compaction runs as an independent LLM call dedicated to summarization, rather than a mechanical script that trims messages.

How it works:

  • Auto-compaction triggers when contextTokens > contextWindow - reserveTokens, with reserveTokens defaulting to 16,384 tokens reserved for model output.
  • Manual compaction can be forced at any point using the /compact command.
  • Pi checks context size after each turn ends, and can trigger mid-turn if a large tool output causes an immediate overflow error.
  • Recent turns remain untouched based on a configurable budget defaulting to 20,000 tokens (roughly 5 to 20 turns).
  • The compaction request routes through a fresh routing session ID with the system prompt "you are a context summarization assistant."
  • Because the call is decoupled from main session state, Pi can route summarization to a smaller, less expensive model.

How the summary writes back into the session

Before sending the summarization request, Pi formats raw message objects into flat text using serializeConversation():

[User]: What they said
[Assistant thinking]: Internal reasoning
[Assistant]: Response text
[Assistant tool calls]: read(path="foo.ts"); edit(path="bar.ts", ...)
[Tool result]: Output from tool

Cost control: Tool outputs truncate to 2,000 characters during serialization, accompanied by a character-cut notice, preventing large bash and file reads from expanding summarization costs.

Once the model returns the summary, Pi appends a structured CompactionEntry to the session:

interface CompactionEntry {
  type: "compaction";
  id: string;
  parentId: string;
  summary: string;
  firstKeptEntryId: string;
  tokensBefore: number;
  usage?: Usage;
  details?: { readFiles: string[]; modifiedFiles: string[] };
}

The mechanism: The firstKeptEntryId field serves as a pointer marking the exact boundary where unsummarized history begins. Pi reconstructs the next prompt as [system prompt] + [compaction summary] + [everything from firstKeptEntryId onward].

To protect message integrity, Pi restricts cut boundaries to user messages, assistant messages, bash commands, or custom messages, preventing tool outputs from being separated from their parent calls. When a single turn exceeds the retention budget, Pi executes a split-turn process to merge two smaller summaries.

The handoff summary format

Pi enforces a standardized Markdown structure for the summary:

## Goal

Migrate the checkout flow from Stripe to a custom payment orchestration layer.

## Constraints & Preferences

- Must support Canadian and US currency without a re-architecture
- No breaking changes to the existing webhook contract

## Progress

### Done

- [x] Implemented base PaymentProvider interface
- [x] Wired up Stripe adapter behind the new interface

### In Progress

- [ ] Building the retry/idempotency layer for failed captures

### Blocked

- Waiting on merchant account approval to test live payouts

## Key Decisions

- Idempotency keys stored in Postgres, not Redis: needed durability across deploys, not just speed

## Next Steps

1. Finish idempotency layer
2. Add integration tests for the retry path

## Critical Context

- Payment orchestration lives in `packages/payments/`, not the main API package

<read-files>
packages/payments/src/provider.ts
packages/payments/src/stripe-adapter.ts
</read-files>

<modified-files>
packages/payments/src/provider.ts
</modified-files>

Cumulative state: The <read-files> and <modified-files> lists persist across multiple compactions by aggregating entries from the current batch and the prior summary's details field, maintaining a complete record of touched files throughout the session.

The prompt cache impact

Prompt caching requires an exact token-for-token prefix match with a previous request to grant cost discounts.

  • Before compaction, the expanding message history shares an unbroken prefix and qualifies for cached rates.
  • After compaction, the newly generated summary replaces the earlier prefix, requiring the retained turns to be processed at full price on the first post-compaction request.
  • Pi explicitly disables prompt-cache writes on the compaction request itself because a one-off summary is not reused.
  • Subsequent turns establish a new prefix and resume cached rates.

The financial takeaway: Post-session billing spikes represent prompt-cache resets caused by prefix replacement, rather than billing calculation errors.

Founder playbook

When building custom orchestration layers or evaluating AI coding agents, compaction acts as a direct control over user experience and inference costs.

  • Account for uncached request spikes following extended sessions in unit-economic projections.
  • If model precision degrades during long tasks, clearing the session provides better results than operating within a heavily compacted context.
  • Use the session_before_compact extension hook in Pi to inject custom summarization prompts or alternative models tailored to specific domain constraints.
  • Storing summaries as plain text keeps context portable, allowing sessions to switch between underlying model providers without schema transformations.

Frequently asked questions

What is the difference between compaction and RAG (retrieval-augmented generation)?

Compaction and RAG solve different problems. RAG retrieves external documents or data the model never saw before, pulling relevant chunks into context on demand. Compaction compresses context the model already generated your own conversation history into a smaller summary so the session can continue. You'll often see both in production agents: RAG for knowledge, compaction for memory management.

Does Claude Code or Codex handle context compaction the same way as Pi?

The core mechanism is similar across coding agents Claude Code, Codex, and Pi all use an LLM call to summarize older conversation history once the context window nears its limit. The implementation details differ: Pi's default retention window is a 20,000 token budget (roughly 5-20 turns), uses a distinct 'context summarization assistant' system prompt, and stores summaries as portable plain text, while other agents may tune retention size, trigger thresholds, or summary structure differently.

How do I manually trigger compaction instead of waiting for auto-compaction?

In Pi, you can force compaction at any point using the /compact command rather than waiting for the automatic trigger, which only checks after a turn ends. This is useful before starting a new complex task, since you get a clean handoff summary at a moment you choose rather than mid-task.

Why did my AI API bill spike after a long coding session?

That spike is almost certainly caused by compaction breaking your prompt cache. Prompt caching only discounts requests that share an exact token-for-token prefix with a previous request once compaction replaces old history with a summary, the prefix changes and the first post-compaction request must be fully recomputed at full price. Costs return to normal on subsequent requests once a new cached prefix is established.

What is context rot and how much does it actually hurt LLM performance?

Context rot refers to the measurable decline in LLM output quality as context size grows, even well before the model hits its hard token limit. Chroma's context rot research found accuracy degradation on long-context tasks scales with input length across major models meaning your coding agent can feel noticeably 'dumber' in hour three of a session purely from context bloat, not a model downgrade.

Can I customize or replace how compaction summarizes my conversation?

Yes Pi is extensible, so you can ask it to create a custom extension with your own compaction prompt instead of the default 'goal, progress, key decisions' structure. This matters for founders building domain-specific agents: a legal-review agent or a biotech research agent may need different fields preserved (e.g., citations, compliance flags) than a general coding assistant.

How many tokens does Pi keep before triggering compaction, and can I change it?

Pi's default token budget for retained recent messages is 20,000 tokens, which typically covers 5 to 20 turns depending on message density, and this is configurable rather than fixed. Lowering the budget triggers compaction more frequently (more caching resets, but tighter context), while raising it delays compaction (fewer resets, but more context rot risk before the summary kicks in).

Is it better to start a fresh conversation or let the agent compact automatically?

It depends on whether you need continuity. Starting fresh restores peak model performance instantly but discards prior decisions, open TODOs, and constraints you've established risky mid-build. Compaction preserves a summarized handoff so work continues uninterrupted, at the cost of some fidelity and a one-time cache-reset fee; most founders should default to compaction for active builds and reserve fresh starts for genuinely new tasks.

Why does Pi store compaction summaries as plain text instead of a structured format like JSON?

Plain text keeps the summary model-agnostic and portable since the compaction result isn't tied to any particular model's expected input schema, you can switch the underlying LLM mid-session (e.g., from a frontier model to a cheaper one) and the new model can still parse and use the handoff summary without a translation step.

Does switching to a cheaper model for compaction hurt summary quality?

Not necessarily, and it's a deliberate design choice in Pi. Because the compaction request is standalone and doesn't depend on the existing conversation's model context, it can be routed to a smaller, cheaper model purely for the summarization task without degrading the main session's reasoning quality the expensive model stays reserved for actual coding work.

What exactly is a 'turn' in an LLM conversation?

A turn spans one full user message through the final assistant response, including everything in between. It starts with [system][tools][user], may include one or more assistant tool-call and tool-result cycles as the agent executes actions and feeds results back to the model, and ends once the assistant produces its final output. Each new turn appends another full cycle to the conversation history, which is why token counts grow non-linearly in agentic coding sessions compared to simple chatbot Q&A.

What is AGENTS.md and why does it count against my context window?

AGENTS.md is a convention for a project-level instructions file that coding agents like Pi load automatically at the start of a session, similar to a README aimed at the AI rather than humans. It gets included in every request's context alongside the system prompt and tool definitions, so a bloated AGENTS.md file effectively taxes your available context budget before you've written a single message keeping it concise directly delays when compaction triggers.

How does prompt caching actually reduce LLM API costs?

Prompt caching lets providers skip recomputing tokens that exactly match the prefix of a previous request, charging a fraction of the normal rate for the cached portion. In an active coding session where history mostly just grows by appending, this means each new request only pays full price for the newly added tokens until something like compaction changes the prefix and forces a full recompute on the next request.

Why does my coding agent occasionally fail mid-task with a context overflow error?

This happens when a single turn not just the cumulative history pushes past the context limit before the agent's normal end-of-turn compaction check runs, for example during a turn with many tool calls or a large file load. Pi handles this by triggering compaction mid-turn rather than only checking after a turn completes, preventing the session from dying outright on an overflow.

What happens to tool call results and file contents during compaction?

Tool results and loaded file contents inside the compacted range are folded into the summary rather than preserved verbatim only the retained recent messages keep their original tool outputs untouched. This means if you need an agent to recall an exact file diff or command output from far earlier in a session, that specificity may be lost to summarization unless it's still within the retention window.

more than just words|

We're here to help you grow better at every stage of the climb.

let's go to market

Whether you're finding problem-market fit, refining your positioning, shipping product, or scaling go-to-market we're built for every stage of the journey.