Skip to content
4 min read · 774 words

@nhtio/adk/batteries/context/thrift ​

Token Thrift — subtractive context management. A pure, model-free algorithm that holds a large WORKING set (messages, memories, retrievables, thoughts, an image, tools) and SUBTRACTS it down to the highest-signal slice that fits the active model's context window.

Remarks ​

The thesis ​

A context window is not a chat history — it is only what you send for ONE dispatch. Most context-management strategies either accumulate (append everything, truncate blindly when it overflows) or pay a model call to compress (summarize). Token Thrift does neither: it is a zero-model-call algorithm — every decision is a measured, evidence-based subtraction (a token count compared against a budget), never a guess and never a paid LLM round-trip. The same function runs identically at contextWindow: 4096 and contextWindow: 1_000_000; the window span is a parameter, not a rewrite.

The subtraction order (lowest-signal, most-recoverable content first): an image attachment → stale prior-turn tool results (oldest first, this-turn results protected by a newest-N backstop) → the tail of a RAG ranking → low-importance memories → stale ephemeral control messages → the oldest conversation turns → surviving guidance thoughts (oldest first) → visible tools (last resort, driven toward zero). See @nhtio/adk/batteries/context/thrift/subtractive_pass!subtractToFit for the full, step-by-step account with the rationale for each cut.

Upstream of the subtractive pass, @nhtio/adk/batteries/context/thrift/relevance!selectRelevantTurns offers a smarter alternative to plain recency for deciding which history turns are worth replaying at all — walking the ENTIRE history and keeping anything lexically relevant to the current query, not just the last N turns.

The evaluation ​

This battery is a direct extraction of the flagship reference agent's production subtractive pass — evaluated HEAD-TO-HEAD across five models against alternative context-management strategies (recency-only truncation, and a paid summarizing "compact" strategy) on a shared stress corpus. Honest results, both wins and the one loss:

  • Cost: thrift is the outright cost winner across the matrix — roughly HALF the tokens per answer of the alternatives, since it never pays for a summarizer call and never carries dead weight forward.
  • Quality at the constrained edge: at tight context budgets, thrift scored 0.82 vs. 0.75 for the next-best strategy.
  • Quality at a large (128k) window under control conditions: 1.27 vs. 1.11.
  • The one honest loss: on a SINGLE reasoning-model cell, a paid summarizing ("compact") strategy beat thrift 1.48 vs. 1.13 — reasoning models can, in some configurations, make better use of a paid compression step than of subtraction alone. Thrift does not claim to dominate every cell; it is the stronger strategy on cost everywhere and on quality almost everywhere, honestly including where it wasn't.

A paid summarizing sibling strategy ("compact") — the strategy that won that one cell — is planned as a future addition to the @nhtio/adk/batteries/context domain (see @nhtio/adk/batteries/context for the domain-level framing). This battery already carries the one cross-cutting hook a future compact strategy needs from thrift: the isSummaryMessage predicate on @nhtio/adk/batteries/context/thrift/subtractive_pass!SubtractToFitOptions, which protects a summarizing strategy's running-summary message from being shed like an ordinary old turn.

Zero-model-call guarantee ​

Every export in this battery is synchronous, pure with respect to its inputs (aside from mutating the working set it's handed), and makes no network or model calls of any kind. The only capability this battery cannot perform itself — tokenization — is an INJECTED function (@nhtio/adk/batteries/context/thrift/contracts!EstimateTokensFn), never a bundled tokenizer and never a model call.

Usage sketch ​

ts
import {
  subtractToFit,
  type WorkingSet,
} from "@nhtio/adk/batteries/context/thrift";
import { Tokenizable } from "@nhtio/adk";

const ws: WorkingSet = {
  systemPrompt: mySystemPrompt, // a Tokenizable, or a plain string
  messages: myMessages,
  memories: myMemories,
  retrievables: myRagChunks,
  thoughts: myThoughts,
  tools: myToolRegistry,
};

const trace = subtractToFit(ws, model.contextWindow, myShortlistedToolNames, {
  // Inject Tokenizable's own estimator so thrift measures byte-for-byte what your
  // own overflow guard counts — including ctx-resolved dynamic content.
  estimateTokens: (value, encoding, ctx) =>
    Tokenizable.estimateTokens(value, encoding, ctx),
  outputReserve: model.maxOutputTokens,
});

if (!trace.fits) {
  // Even the irreducible floor (system prompt + newest turn + output reserve) exceeds the
  // window — a bounded refusal, not a truncated/incoherent dispatch.
}

References ​

argText ​

Re-exports argText


BucketTrace ​

Re-exports BucketTrace


ContentLike ​

Re-exports ContentLike


contentTokens ​

Re-exports contentTokens


DEFAULT_ENCODING ​

Re-exports DEFAULT_ENCODING


DEFAULT_RESERVE_FRACTION ​

Re-exports DEFAULT_RESERVE_FRACTION


DEFAULT_THIS_TURN_RESULT_KEEP ​

Re-exports DEFAULT_THIS_TURN_RESULT_KEEP


E_CONTEXT_RESOLVER_MISSING ​

Re-exports E_CONTEXT_RESOLVER_MISSING


Estimable ​

Re-exports Estimable


EstimateTokensFn ​

Re-exports EstimateTokensFn


EstimatorOptions ​

Re-exports EstimatorOptions


groupHistoryIntoTurns ​

Re-exports groupHistoryIntoTurns


HistoryTurn ​

Re-exports HistoryTurn


IsEphemeralMessageFn ​

Re-exports IsEphemeralMessageFn


IsSummaryMessageFn ​

Re-exports IsSummaryMessageFn


MillisTimestamp ​

Re-exports MillisTimestamp


RELEVANCE_FLOOR_CURVE ​

Re-exports RELEVANCE_FLOOR_CURVE


RELEVANCE_FLOOR_MAX ​

Re-exports RELEVANCE_FLOOR_MAX


RELEVANCE_FLOOR_MIN ​

Re-exports RELEVANCE_FLOOR_MIN


RelevanceMessage ​

Re-exports RelevanceMessage


RelevanceToolCall ​

Re-exports RelevanceToolCall


relevanceToQuery ​

Re-exports relevanceToQuery


RenderToolsFn ​

Re-exports RenderToolsFn


resolveBudget ​

Re-exports resolveBudget


scaledRelevanceFloor ​

Re-exports scaledRelevanceFloor


selectNaiveTurns ​

Re-exports selectNaiveTurns


SelectNaiveTurnsOptions ​

Re-exports SelectNaiveTurnsOptions


selectRelevantTurns ​

Re-exports selectRelevantTurns


SelectRelevantTurnsOptions ​

Re-exports SelectRelevantTurnsOptions


ShedRankFn ​

Re-exports ShedRankFn


stripPriorTurnThoughts ​

Re-exports stripPriorTurnThoughts


subtractToFit ​

Re-exports subtractToFit


SubtractToFitOptions ​

Re-exports SubtractToFitOptions


ThriftTrace ​

Re-exports ThriftTrace


WorkingImage ​

Re-exports WorkingImage


WorkingMemory ​

Re-exports WorkingMemory


WorkingMessage ​

Re-exports WorkingMessage


WorkingRetrievable ​

Re-exports WorkingRetrievable


WorkingSet ​

Re-exports WorkingSet


WorkingThought ​

Re-exports WorkingThought


WorkingTool ​

Re-exports WorkingTool


WorkingToolCall ​

Re-exports WorkingToolCall


WorkingToolRegistry ​

Re-exports WorkingToolRegistry