# Keep long agent sessions inside the context window with compaction

> Agno's `Agent(compaction=...)` sends the model a summary of older turns plus the newest runs word for word. The stored session never changes, and each fold is one row in the `agno_compactions` table.

- Published: 2026-10-08
- Author: Kaustubh Shukla
- Categories: Changelog
- Canonical: https://www.agno.com/blog/keep-long-agent-sessions-inside-the-context-window-with-compaction
- Markdown: https://www.agno.com/blog/keep-long-agent-sessions-inside-the-context-window-with-compaction.md

With `Agent(compaction=True)`, Agno waits for the provider to reject a request as too long, summarizes the older turns with the agent's own model, and retries with that summary plus up to the five most recent runs.

Picture a support agent that stays in one session all week. Every run replays the history, and one day the request no longer fits the model. Pass a `Compaction` to fold at a size you pick, before the provider ever says no:

```python
from agno.agent import Agent
from agno.compaction import Compaction
from agno.db.sqlite import SqliteDb
from agno.models.openai import OpenAIResponses

agent = Agent(
    model=OpenAIResponses(id="gpt-5.6-luna"),
    db=SqliteDb(db_file="tmp/agent.db"),
    session_id="trip",
    add_history_to_context=True,
    compaction=Compaction(compact_at_tokens=100_000, uncompacted_runs=2),
)

agent.print_response("I am planning a trip to Japan in April. My budget is 4000 dollars.")

# Fold now, at a point you choose, without waiting for the threshold
result = agent.compact(session_id="trip")
print(result.status.value, result.message)
```

At the start of each run Agno counts the request, with a local tiktoken estimate by default. Once it reaches `compact_at_tokens`, everything older than the kept runs becomes one summary. `agent.compact()` and `agent.acompact()` fold on demand and return a `CompactionResult`, whose `status` is `compacted` or names why the fold declined, such as `nothing_to_fold` or `already_compacted`.

| Setting                     | Default                                | What it does                                                         |
| --------------------------- | -------------------------------------- | -------------------------------------------------------------------- |
| `compact_at_tokens`         | `150_000`                              | Fold when the request reaches this size; `None` folds only on demand |
| `uncompacted_runs`          | `5`                                    | Recent runs sent word for word                                       |
| `uncompacted_tokens`        | `None`                                 | Size the kept tail in tokens; can't be set with `uncompacted_runs`   |
| `model`                     | The agent's model                      | Writes the summary, so a cheaper model can do it                     |
| `instructions`              | `None`                                 | Extra guidance for the summarizer, like "Keep every ticket id"       |
| `compacted_token_budget`    | `None`                                 | Length target for the summary, cut at a line break when it runs over |
| `store_compacted_messages`  | `True`                                 | Keep the folded messages in the database                             |
| `search_compacted_messages` | Follows `store_compacted_messages`     | Give the agent a tool to search those messages                       |
| `on_context_overflow`       | `False`, `True` with `compaction=True` | Fold and retry when the provider rejects a request as too long       |

## What gets stored where

Compaction never edits the session. Each fold writes one row to a new `agno_compactions` table with the summary, the folded messages rendered as markdown in `archived_messages`, the id of the first message it kept, and the token counts on both sides. Agno rebuilds every request from the stored runs plus the newest row, so the summary never lands in the session as a message. On the next fold the summarizer reads the previous summary together with the newer turns and writes a single updated one.

![Diagram of one compaction fold for a six-run session with uncompacted_runs set to 2. On the left, the session still stores all six runs, two messages each. In the middle, one agno_compactions row holds the summary of runs 1 to 4, those runs verbatim in archived_messages, and a first_kept_message_id pointing at run 5. On the right, the model payload is the summary, runs 5 and 6, and the new question. A note says deleting the row makes the next request use the stored runs again.](https://www.agno.com/images/v3-1-2-compaction-payload.png)

We ran a six-run session on SQLite with an offline stand-in model and a summarizer that returns fixed text, since no API key was available. After `agent.compact()` the next question went out like this (real output, trimmed):

```text
agent.compact(): compacted | Compacted 8 messages (85 -> 44 tokens).
agno_compactions columns: compaction_id, session_id, run_id, user_id,
  first_kept_message_id, summary, archived_messages, messages_compacted,
  tokens_before, tokens_after, created_at
request after the fold: 6 messages
  user       Summary of earlier conversation (compacted): ...
  user       Do I need a rail pass?
  assistant  Noted.
  user       Which airport should I fly into?
  assistant  Noted.
  user       Remind me what my budget was.
tools sent: search_compacted_history
messages stored in the session: 14
```

A run that folds on its own carries the record on `run.compaction` and streams `CompactionStarted` and `CompactionCompleted` events. AgentOS exposes the manual fold as `POST /agents/{agent_id}/sessions/{session_id}/compact`.

## Undo a fold

The row is the only thing that changes what the model sees, so deleting it undoes the fold. `db.delete_compactions_for_session("trip")` returned `1` in the run above, and the following request carried 15 messages, every stored run plus the new question.

## Search the folded turns

`search_compacted_messages` follows `store_compacted_messages`, so it is on by default. After the first fold archives something, the agent gets `search_compacted_history(pattern, context_lines=2)`, scoped to its own session. The summary message tells the model to search before it answers any question that needs an exact value from the folded turns, like an identifier, a figure or an error message. The pattern is literal, case-insensitive text with `|` between alternatives, and one search returns at most 8,000 characters. A search for the booking reference from the first turn found it in the archive (real output):

```text
1: ## user
2:
3: I am planning a trip to Japan in April. My budget is 4000 dollars. Booking ref JP-7731.
4:
5: ## assistant
```

Set `search_compacted_messages=False` to keep the archive for your own debugging without the tool, or `store_compacted_messages=False` to keep only the summary. Compaction runs on `SqliteDb` and `PostgresDb`; on any other database Agno logs a warning and turns it off.

See the [cookbook](https://github.com/agno-agi/agno/blob/main/cookbook/02_agents/03_context_management/compaction/compaction.py), and learn more about [chat history](https://docs.agno.com/database/chat-history) in the documentation.
