Context Shifting
Context shifting fits the assembled conversation into the model's token budget by removing or compressing the least important context while keeping everything that must stay.
Pre-Request Steps
Before a request is sent, the pipeline follows these steps:
- Determine the tokenizer profile and the context limit of the model.
- Reserve space for the response.
- Keep the system prompt, the character, required lorebook entries, and pinned messages.
- Remove or compress the oldest unpinned blocks first.
- Remove tool-call and tool-result messages only as a pair.
- Recount tokens after every change.
- Show the user what was excluded or summarized.
If the protected context alone exceeds the budget, generation ends with the
stable TOKEN_BUDGET_EXCEEDED error instead of sending an over-budget
request to the provider.
How Shifting Works
shiftContext(messages, countTokens, budget) adjusts the dialogue to the
token budget. It returns three lists:
kept— the messages that fit;excluded— the messages removed, shown to the user;truncated— blocks that were compressed rather than dropped.
System messages and pinned messages are always protected. The oldest
unpinned blocks are removed first. Tool calls and their results are linked
through toolCallId, tool_call_id, or callId and removed as one group,
even when they are not adjacent.
Built-in Strategies
The strategy is selected by the contextStrategy setting and applied
through the ContextStrategyRegistry:
- truncate — removes the oldest unpinned groups.
- summarize — builds a local extractive summary of the excluded history and keeps it before the current user input.
- vector-recall — drops low-relevance lorebook and memory blocks before high-relevance ones, then shortens old history.
- manual — first excludes messages flagged
meta.manualExcluded: true(including their paired tool-call and tool-result), then continues with normal reduction if more space is needed.
Plugins and the Budget
Plugins may register additional strategies; registering returns a cleanup function. A plugin strategy cannot bypass the budget:
- the host restores required messages and rejects a strategy that removed protected context;
- the host independently recounts the real budget;
- counting and shifting run before plugin interceptors, and a mandatory re-count with a final shift runs after them — a plugin cannot add messages late to sneak past the limit.
The Context Audit
Every generation creates a PromptContextAudit before the network call and
finishes it with one terminal status: completed, failed, or cancelled.
The audit records:
- the generation ID, provider, and model;
- every prompt block in actual order, with token counts and the stable reason for inclusion or exclusion;
- the context limit, the response reserve, and the final prompt-token count;
- the tokenizer profile and whether it is approximate;
- the final provider messages and the plugin-interceptor diagnostics;
- a normalized provider error code, without upstream response bodies.
Only the last complete audit per chat is kept in the database; a new
request atomically replaces the old one, and deleting the chat deletes the
audit. The UI reads it through GET /api/v2/chats/:id/context-audit.
A live preview endpoint, POST /api/v2/context-preview, runs the same
persona, lorebook, memory, template, tokenizer, and shifting stages without
creating messages, branches, or audits.
See Also
- Pipeline Stages for where shifting sits in the stage order.
- Tokenization for how tokens are counted.
- Data & Storage for where audits are stored.