Compaction
As a conversation grows, its history eventually approaches the model's context window. Compaction keeps a session going by replacing older turns with a single checkpoint summary produced by the agent's own model, while keeping the most recent turns verbatim. The work done so far is preserved as a concise handoff; the tokens it used are freed.
Compaction is built in. It runs automatically when context fills up, and you can also trigger it on demand from the TUI or the Harness API.
For OpenAI and Codex Responses, see prompt caching and Astra effort changes for cache behavior across compaction. Fast-agent's summary compaction is distinct from OpenAI's native compaction endpoints.
How it works
A compaction does three things:
- Plans retention. Leading template messages (system-prompt-like content)
are always kept. The most recent
keep_turnsturns are kept verbatim. The turns in between are the compact region. The boundary always falls on a user turn, so an assistant tool call is never separated from its tool result. - Summarizes. The compact region is sent to the agent's model with a summarization prompt asking for a handoff summary for "another LLM that will resume the work." This is a side-channel call — it does not add the request to your history, and uses no tools.
- Replaces. History becomes
templates + summary + recent turns. The summary is a clearly-marked message (it shows ascompactedin/history), not an ordinary user message. The original pre-compaction history is archived to acompacted_*.jsonfile in the session directory when session history persistence is enabled.
If the summarization call fails or returns nothing, history is left untouched. With session history enabled, an archive failure also leaves history unchanged. Archives are published atomically under collision-resistant names; each new summary records the archive filename, SHA-256 digest, and retention boundaries. Prior summaries in that archive link to earlier archives.
Automatic compaction
By default (compaction.auto: true), fast-agent checks context usage after each
completed turn and compacts when usage crosses compaction.threshold (see the
generated default below). The trigger uses server-observed token usage from
the last response, not an estimate, so it reflects what the provider actually
charged.
This applies everywhere agents run: the TUI, fast.run(), and the Harness API.
There is nothing to enable.
compaction:
auto: true # automatically compact when the threshold is crossed
# threshold: null # context-window fraction that triggers compaction; unset: model default
keep_turns: 2 # recent turns kept verbatim after compaction
strategy: 'auto' # auto: provider-native where supported; client: always summarize
# prompt: null # built-in prompt; set inline text or a relative file path
To turn auto-compaction off and rely solely on manual /compact, set
compaction.auto: false.
compaction.strategy chooses how the summary is produced. With auto, a
provider that compacts natively can take over: if it compacts within normal
requests, fast-agent's own auto-compaction stands aside so the two never compete.
Set strategy: client to always use fast-agent's summary. Native provider
compaction is being introduced provider by provider:
- Claude models on GitHub Copilot
compact within requests when the context window is known. A custom
compaction.promptselects fast-agent's summary instead. - OpenAI GPT models on Codex Responses, Responses (API key), and GitHub Copilot use OpenAI provider-native checkpoints.
- xAI Grok models use xAI provider-native checkpoints.
Elsewhere both settings behave the same.
OpenAI provider-native checkpoints
On by default for OpenAI GPT models on Codex Responses (codexresponses.*),
Responses (responses.*, API key), and GitHub Copilot (copilot.gpt-*). fast-agent
still decides when to compact and what to keep, but the compacted region is
replaced by an opaque, encrypted checkpoint that OpenAI produces (a request ending
in a compaction_trigger item), instead of a written summary.
Choose per model with the model string, or per route in config:
fast-agent go --model 'sol?compaction=client' # fast-agent's summary
fast-agent go --model 'copilot.gpt-6-sol?compaction=native' # provider-native
codexresponses: # ChatGPT login (codexresponses.*)
native_compaction: false
responses: # OpenAI API key (responses.*)
native_compaction: false
copilot: # GitHub Copilot GPT models (copilot.gpt-*)
native_compaction: false
Precedence: compaction.strategy: client always uses fast-agent's summary; otherwise
the model string's compaction=native|client wins over the route's
native_compaction setting, which defaults to true. compaction=native is rejected
for providers without native compaction. On Copilot Claude models, compaction=client
turns off server-side threshold compaction in favour of fast-agent's summary.
- Templates and the most recent
keep_turnsturns stay verbatim around the checkpoint, exactly as with a summary. Archiving,/compact,/compact preview, mid-turn compaction, and session save/resume work the same way. - The checkpoint is not human-readable.
/historyshows its strategy, encrypted size, and archive file; the full prior history remains in that archive. The compaction result shows—for the size afterwards, because an opaque checkpoint's token cost is only known once the next request reports usage. - Compaction quality / recall is model dependent with larger models performing better.
- A native checkpoint takes no prompt. A custom
compaction.promptor/compact <instructions>asks for a written summary, so either uses fast-agent's summary instead;/compact previewshows which applies. - For
gpt-6-astra, fast-agent re-sends the desired reasoning effort as aconfiguration_updatebefore the next user message, as OpenAI requires after compaction. - Only OpenAI can read the checkpoint. Switching this conversation to another provider leaves it with the placeholder text (xAI, which has its own checkpoints, stops with an explanation instead); restore the archive if needed.
- Checkpoints from
codexresponsesandresponsescan be read on all three routes. Copilot's checkpoints can only be read on Copilot: switching a Copilot-compacted conversation tocodexresponsesorresponsesstops with an explanation; switch back to a Copilot model or restore the archive with/history load <archive>.
xAI provider-native checkpoints
On by default for xAI Grok models (xai.*). xAI rejects compaction_trigger
items, so fast-agent sends the compact region to xAI's POST /responses/compact
endpoint and replays the returned compaction item in its place. Turn it off with
xai: native_compaction: false, or per model with ?compaction=client.
- xAI's compaction drops tool outputs: it keeps what the user and model said
(and the model's reasoning), not tool results. A detail that appeared only in a
tool result is lost unless the model restated it; the model will typically
re-run the tool. Use
?compaction=clientwhere older tool results must survive. - The checkpoint can only be read by xAI, and xAI cannot read OpenAI's: switching
provider stops with an explanation; restore the archive with
/history load. - The checkpoint is recorded with the
openai_compactionstrategy (the shared item format) in history metadata and ATIF; its description names xAI.
Tuning the trigger
The point at which compaction kicks in is compaction.threshold. Lower it to
compact earlier (more headroom, more frequent summarization); raise it to let
context fill closer to the limit before compacting.
To set the trigger for one model, add compact_at to the model string, either in
input tokens or as a context-window fraction:
fast-agent go --model 'grok?compact_at=150k' # compact at 150,000 tokens
fast-agent go --model 'sonnet?compact_at=0.7' # or 70% of the context window
Precedence: the model string's compact_at, then compaction.threshold (which applies
to every model), then the model's own default, then 0.85. Most models have no default
of their own; Grok 4.7 compacts at 190,000 tokens to stay below its 200,000-token
long-context rate. A token trigger is independent of the context window, so it holds
when a price tier is an absolute size. Model overlays can set a default with
metadata.compaction_trigger_tokens. /model and /compact preview show the
effective trigger, e.g. 190,000 tokens (38% of context).
| Setting | Default | Description |
|---|---|---|
compaction.auto |
true |
Automatically compact history when context usage crosses the threshold |
compaction.threshold |
null |
Fraction of the model context window that triggers auto-compaction, for every model. Unset uses the model's default (e.g. Grok 4.7 compacts at 190k tokens), else 0.85. A model string's compact_at overrides it |
compaction.keep_turns |
2 |
Number of recent complete turns kept verbatim after compaction |
compaction.strategy |
'auto' |
How compaction is performed. 'auto' uses provider-native compaction where the model supports it and fast-agent's summary otherwise; 'client' always uses fast-agent's summary |
compaction.prompt |
null |
Custom summarization prompt for compaction. Inline text, or a path to a text/markdown file. None uses the built-in prompt (see /compact prompt). |
The summarization prompt
The built-in prompt asks the model for a structured handoff summary covering goals, decisions, progress, what remains, and any critical data needed to continue. You can see the exact prompt in use at any time:
To customize it, set compaction.prompt to either inline text or a path to a
text/markdown file. Relative file paths resolve from the directory of the loaded
config file (not the process working directory):
Manual compaction in the TUI
| Command | Effect |
|---|---|
/compact |
Compact now, showing the before → after context usage. |
/compact <instructions> |
Steer the summary, e.g. /compact focus on the database migration. |
/compact preview |
Show what would be kept and dropped — no model call. |
/compact prompt |
Print the active summarization prompt. |
/compact preview is free: it reports which turns would be summarized and an
estimated before → after token count without calling the model. Use it to decide
whether compaction is worthwhile before paying for it.
While /compact runs, the streaming token progress display is shown — the same
live indicator as a normal turn — so you can watch the summary being generated.
After compaction, the before/after context window is visualized, and the summary remains in history. Because the summary is an ordinary part of the conversation, you can correct or extend it with your next message if the model missed something.
How /history behaves after compaction
The checkpoint summary appears in /history as a compacted row (marked with
≡), distinct from user and assistant turns, with a short preview of the summary
content. Turns that were folded into the summary no longer appear individually —
they live in the compacted_*.json archive instead. The recent turns kept
verbatim continue to display normally.
To recover the full pre-compaction transcript, load the archive:
ATIF export after compaction
Live ATIF output and persisted session ATIF export reconstruct archived history
through the same verifier. Original messages and tool results appear once, in
their original positions. Each summary becomes an ATIF context_management
boundary (type: "compaction", boundary: "replace") following the ATIF v1.7
convention:
- the boundary step's
observationholds the summary the model saw; - the system prompt, templates, and retained recent turns that stayed in the
model's context follow the boundary as
is_copied_contextsteps, which carry no usage and are excluded from SFT by spec-following consumers; extra.context_managementlistsremoved_step_ids,retained_step_ids, andcopied_step_ids, and each copy recordscopied_from_step_id;- the summarization model call is embedded in
subagent_trajectories, referenced from the boundary observation, and included in final metrics.
A consumer applying the spec's replace-boundary rule therefore reconstructs the same context fast-agent sent to the model.
context_management.strategy is summary for fast-agent's own summary. A
provider-native checkpoint records its strategy (openai_compaction): its
observation describes the opaque checkpoint instead of quoting a summary, the
embedded call is the provider's compaction request, and
final_metrics.extra.provider_checkpoint_compactions counts these boundaries. Process-poll fold audits are still
expanded, including when reconciling a raw transient final turn.
Recovery follows linked archives and verifies exact template and retained-tail sequences; it does not concatenate current/previous snapshots or globally deduplicate messages. Legacy unlinked archives require a unique exact positive tail overlap, timestamped originals, and an archive filename timestamp within five seconds before the summary. Missing, malformed, conflicting, or ambiguous evidence fails the full export rather than silently producing a partial trace. This includes legacy summary-only histories and retained tails whose exact overlap can no longer be verified.
Disabling session history still permits ordinary compaction, but a full ATIF
export cannot recover discarded history. Persisted recovery covers saved
evidence only, not unsaved messages lost on hard termination. It does not merge
existing ATIF files: preserve those separately when they contain extra transient
evidence. Checkpoints written before summary calls were recorded have no
summary usage: their boundaries report summary_usage: "unavailable" and
final metrics mark the accounting as excluding those calls.
Manual compaction from the Harness API
Under the Harness API, auto-compaction is on by default just as in the TUI. To compact a session explicitly:
result = await session.compact()
print(f"{result.messages_before} → {result.messages_after} messages")
compact() accepts optional instructions (one-off focus for the summary) and
agent_name (target a specific agent in the session). It returns a
CompactionResult and raises CompactionSkipped when there is nothing worth
compacting. See the Harness API
guide for details.
For building custom triggers or tooling, the primitives in
fast_agent.history.compaction are importable directly — including
plan_compaction for a model-call-free retention preview and should_auto_compact
for the threshold check.
What is kept, summarized, and archived
| Kept verbatim | Summarized | Archived | |
|---|---|---|---|
| Leading template / system messages | ✅ | ||
| Older turns (the compact region) | ✅ | ✅ | |
Most recent keep_turns turns |
✅ | ||
| Full pre-compaction history | ✅ |
The summary itself is retained in the live history as the checkpoint. Tool calls
and their results are summarized together (never split). A single very large turn
is always eligible for compaction, even when keep_turns would otherwise retain
it, so a runaway tool loop can still be brought back under the limit.