> ## Documentation Index
> Fetch the complete documentation index at: https://chatcli.edilsonfreitas.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Chat Prefix Curation

> What the chat prompt caches, what it defers, and how the model gets back anything that was curated out.

A chat turn re-sends its whole prefix on every request. Measured on a real 10-turn session, that prefix carried **37.823 characters** of injected context, and most of it was paid for repeatedly without changing:

| Block | Share of the injected context |
| - | - |
| Auto-activated skill bodies | 32% |
| Memory index | 22% |
| Proactive memory recall | 18% |
| Knowledge-graph card | 13% |
| Session recall | 12% |

Two different problems hide in that table. The memory index and the graph card **never change during a session** — they were re-sent every turn and never cached. The skill bodies **were already in the conversation** — the same skills matched again and again, shipping their full text each time.

Curation fixes both under one rule:

<Warning>
  Material leaves the prompt **only while it stays reachable**. A summary the model cannot expand is a capability loss, not a saving — so every trade on this page disarms itself when the recovery path is off, restoring the previous prompt byte for byte.
</Warning>

***

## What the model still receives

Nothing is removed from the model's reach. What changes is **where** each block lives and **when** it is paid for.

<Tabs>
  <Tab title="Cached prefix">
    Stable for the whole conversation, so the provider serves it as a warm-cache read (\~0.1x input) instead of full price on every turn:

    * the mode banner and language directive
    * **the workspace half that does not depend on the question** — bootstrap files (`SOUL.md`, `USER.md`, `AGENTS.md`…), the memory index, the knowledge-graph card
    * `/context` attachments, pinned skills
    * the MCP tool catalog, listed by name
  </Tab>

  <Tab title="Per-turn suffix">
    Genuinely varies with the question, so it carries no cache hint and never invalidates the prefix above it:

    * path-matched rules for this turn's file hints
    * in `full` memory mode, the query-driven retrieval
    * proactive memory auto-recall and session recall
    * auto-activated skills (bodies, or a note that the body is already above)
    * MCP channel pushes, watcher context, the date

    In chat the suffix travels as one flagged `[TURN CONTEXT]` user-role message right before your turn. In agent and coder mode the blocks that belong to a whole run — the query-driven retrieval and the skills block — travel as flagged history messages appended once per run (`[RUN CONTEXT]` and the skills message), never inside the system message: the system array sits ahead of every message, so even an uncached block there rewrote the entire cached conversation on each new query. As history they are append-only, the previous run's prefix stays a cache read, and they stay in front of the model for the whole tool loop.
  </Tab>

  <Tab title="One pull away">
    Reachable through `context_pull` whenever the model decides it needs it:

    * the full instructions of any installed skill
    * the catalog of every installed skill with descriptions
    * the full MCP tool catalog with descriptions
  </Tab>
</Tabs>

***

## How the model knows when to pull

It is never left guessing. Every deferral in the prompt **names the exact call that recovers it**:

* a skill body that is already earlier in the conversation renders as *"Body already provided earlier in this conversation — follow that copy; it has not changed. If it is no longer visible above, call context\_pull with kind=skill and this skill's name."*
* a skill body left out by the injection budget renders as *"Before applying this skill, call context\_pull with kind=skill…"*
* the MCP block says how many tools exist and that `context_pull` with `kind=mcp_tools` reads what any of them does

So the trigger is the prompt itself, not a heuristic. The tool description states the same contract, and the model may chain up to **3 recoveries per turn** before it has to answer.

***

## `context_pull` — the third sanctioned chat exception

Chat is tool-less by design. `context_pull` joins [`ask_user`](/coder/interactive-ask), [knowledge](/context/knowledge-base), [`@graphview`](/tools/graphview) and [memory](/context/bootstrap-memory) as a sanctioned exception, and it is the narrowest of them: it executes nothing, writes nothing and reaches nothing outside the process. It re-reads material ChatCLI itself curated out of the current turn.

```json theme={"system"}
{"kind": "skill", "name": "chatcli-coder-patch-flow"}
{"kind": "skills"}
{"kind": "mcp_tools"}
```

Providers with native tool use get a tool definition; the rest get the XML transport the other exceptions already use, so **every provider is covered**. Argument parsing is deliberately lenient — `{"cmd":"skill","skill":"x"}` and `{"kind":"skill","args":{"name":"x"}}` both work, because strict parsing only teaches the model to retry at the cost of a round trip.

```bash theme={"system"}
/config chat pull status      # is recovery active for this session
/config chat pull off         # disable it — curation disarms with it
```

Persist with `CHATCLI_CHAT_CONTEXT_PULL=false`.

***

## Skill de-duplication is checked against the live history

A skill body is reduced to *"you already have this"* only while the earlier copy is genuinely still in the conversation. That is verified against the **live history** on every turn, not tracked through the places that can drop a message — so [compaction](/context/context-compression), `/clear`, `/rewind`, a checkpoint restore and a session load all make the body re-inline automatically. Bodies are fingerprinted, so editing a skill on disk re-inlines it too.

***

## The prompt-cache lifetime is chosen from evidence

Anthropic's hour-long cache entry costs **2x** the write instead of 1.25x. Choosing it up front is a bet: pure loss on a session that never pauses, a large win on one that does.

ChatCLI now waits for the answer, on every surface. When a session watches its own prefix expire during a gap that an hour would have covered, it asks for the hour — **once**, one-way, never over an explicit `CHATCLI_PROMPT_CACHE_TTL`, and never on a model that cannot carry it. A one-shot run can never reach that second observation, so it stays on the short lifetime. Agent and coder runs used to ask for the hour up front, on the bet that they pause between tool rounds; a two-request run paid the doubled write for nothing, so they now follow the same evidence as chat.

`/cost` shows the lifetime in effect on the cache line.

**A tool call that outlives the entry is covered by a refresh, on every provider that can.** A cache read refreshes the entry's timer for free on every prompt cache. A test suite that runs six minutes inside a coder turn would expire a 5-minute entry before the next request, and the whole prefix would be rewritten — at the write price on Anthropic and Bedrock, at full price on the subset caches of OpenAI and the others. So 4.5 minutes after each request, while a run is still going, ChatCLI re-sends the previous request asking for no output (`max_tokens: 0` on Anthropic's wire, the one token the Chat Completions and Bedrock Mantle wires accept) and pays the read price of the prefix instead, up to twelve times per stretch. The body is the same one the turn sent, thinking and effort included, because they are part of the cache key. Each refresh is booked like any request, so `/cost` shows what it cost. Adapters opt in through a small interface; the Anthropic API, OpenAI Chat Completions and Bedrock Mantle refresh today, and an adapter that cannot says so once and is not asked again.

**Where a read is cheap enough, the entry is kept warm while you are idle too.** On Claude Fable 5.1 a read is 2.5% of the input price, which beats paying 2x on every written token for the hour-long entry unless pauses run toward an hour. So on such models the interactive REPL keeps refreshing between turns as well, and the hour-long promotion stands down. On the other models a read is 10% of input or more and the arithmetic favors the hour, so they keep the promotion above. `CHATCLI_PROMPT_CACHE_KEEPALIVE` is `auto` (long tool calls everywhere, idle by read price), `on` (idle on every client that can refresh) or `off`; the gateway never refreshes, because one client serves every tenant there.

**The decision belongs to the conversation.** The lifetime "auto" settled on is part of every cache marker the request carries. Under the gateway, where one process serves many principals, it travels with each tenant's store set: a tenant's `/coder` run decides for that tenant's conversation only, and the base conversation gets its own decision back when the tenant leaves.

***

## Seeing it

`/context status` breaks the current prompt down section by section, marks which sections sit in the cached prefix, and shows **what the provider actually counted** for the last call next to the local projection — so the estimate can be seen drifting instead of discovered when compaction fires early.

***

## Agent and coder

The same two moves apply, adapted to what those loops already had.

**The workspace split is identical.** `buildAgentSystemMessage` had the whole workspace block on the volatile side, so its turn-independent half was re-sent at full price on every turn of a run. It now carries a cache breakpoint, placed **last in the cached region** so the core/tools/orchestrator prefix stays byte-identical and keeps hitting. Agent runs have many turns, so the saving compounds further than in chat.

**Skill de-duplication is cross-run there.** The mid-loop re-scan already guarantees a skill fires at most once per run (its own dedup set, a per-run byte budget, and [skill aging](/tools/builtin-skills) that collapses stale blocks). What it could not see is the run *before* it: a session with several `/coder` runs rebuilt the initial skills block from scratch and re-shipped bodies the conversation was already carrying. The check is the same live-history one chat uses; the deferred form points at the skill's source path, which agent and coder can simply read — so it arms unconditionally, with no dependency on `context_pull`.

The MCP catalog is untouched there: agent and coder receive real tool definitions, not a prose catalog.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.