Skip to main content

Strategy

A small, self-contained transform that runs inside the optimizer's pipeline.

What a strategy is

A strategy is the unit of optimization: a self-contained module that inspects one request, transforms it (the request, or the response at the output stage) or leaves it untouched, and emits a decision recording what it did.

Every strategy is:

  • Self-gating — applies only when its condition matches.
  • Metadata-only — its decision carries a kind, a short summary, and token/cost estimates, never prompt or tool bytes.
  • Fail-open — a strategy that throws is skipped. A broken strategy can never break inference.

The Library

Each strategy is registered in the optimizer's REGISTRY (optimizer/src/strategies/index.ts). A new optimization is a new strategy plus a registry entry.

StrategyWhat it does
prompt_compressionShortens long prompts and system messages.
context_dedupeCollapses repeated identical or near-identical tool outputs to the first copy plus changed lines.
observation_maskRetires stale tool observations behind a retrievable marker.
context_compressionShrinks tool outputs, logs, and RAG chunks.
output_externalizeExternalizes bulky tool output to a retrievable handle, losslessly.
command_digestDigests recognized dev-command output (test runs, grep, logs).
code_graphKeeps relevant symbols across multi-file reads.
relevance_filterKeeps only the relevant lines of a tool output.
tool_pruningDrops tools unlikely to be called.
tool_schema_compressionShrinks tool definitions while keeping all tools.
semantic_cacheServes a cached response for duplicate requests.
param_tuningCaps an over-large max_tokens.
vision_ocrReplaces text-only images with OCR'd text.
window_budgetCrops low-relevance history to a token budget.
provider_context_trimAsks Anthropic to clear aged tool results provider-side, before billing.
output_shapingSteers the model not to restate context in its replies. (off by default)
reasoning_budgetDownshifts reasoning effort on routine tool-resume turns.
cache_optimizerStabilizes the request's start so providers reuse their cache.
Cache-safe by default

Provider prompt caches bill only the new part of a request at full price and everything before it as cheap cache reads. Anyray reads the cache_control breakpoints and never edits anything before them, so it can't invalidate the cache — optimizations that would change the cached prefix are skipped.

Strategies in detail

context_dedupe

Never pays for the same page twice.

Collapses a tool output the agent has already seen — a re-read file, a re-run git status or test suite — into a marker pointing at the first copy. Exact duplicates become a one-line marker; near-duplicates become a marker header plus a zero-context diff, so the re-read reads as "unchanged" or "changed only here."

The first copy stays verbatim, collapsed copies are stashed and retrievable, and the collapse depends only on earlier turns — so it's cache-safe. It runs before the digesters and filters so both copies are compared on their original bytes. An original the agent explicitly pulled back with anyray_retrieve is pinned verbatim permanently — it is never re-collapsed as a duplicate, so a retrieval can't loop back into the marker it resolved.

Params: keepRecentTurns (default 2 — recent outputs kept verbatim), nearDupe (disables the near-match path), and the near-match bounds nearMinChars, nearSimilarity, maxAnchors, maxDeltaLines.

Before
turn 3 [tool] Read src/billing.ts → 412 lines (≈ 11 KB)

turn 19 [tool] Read src/billing.ts → the same 412 lines, barely changed (≈ 11 KB)
After
turn 3 [tool] Read src/billing.ts → 412 lines (≈ 11 KB)

turn 19 [tool] [anyray: near-duplicate delta-collapsed — 98% identical to the
412-line output above; 3 changed line(s) below · retrieve ctx_8k2p…]
@@ -41,1 +41,1 @@ -const RATE = 0.07; +const RATE = 0.075;
@@ -388,0 +389,1 @@ +export const auditRate = () => RATE;

observation_mask

Files away stale results the conversation moved past — failures and fresh input always stay.

When a tool result is old enough and large enough that the conversation has moved past it, observation_mask stashes it and replaces it with a one-line retrieve marker, so it stops being rebilled on every later turn. The stashed original can be retrieved. It always keeps provider-flagged failures (Anthropic is_error: true), the current turn's fresh input, and anything already behind a marker.

It runs after context_dedupe and the specialists that keep more structure — source files go to code_graph, test and grep output to command_digest — then before context_compression. Those specialists claim the exact spans they handle, so observation_mask trims only the remaining stale observations. It also defers when provider_context_trim fires, and edits only the newest, uncached part of the request so it stays cache-safe.

Params: keepRecentTurns (default 3 — turns kept before masking), minChars (default 600 — smallest result worth masking), mintPaybackRatio (default 0.25 — on cached sessions, skip masking unless it frees enough text to be worth it; 0 disables), ttlSeconds (default 1800).

Before
turn 2 [tool] Read src/checkout.ts → 380 lines (≈ 9 KB)
turn 5 [tool] npm test → 2 failed, 126 passed (≈ 6 KB)

turn 12 [tool] Read src/tax.ts ← fresh input
After
turn 2 [tool] [anyray: observation masked · 9214 chars · retrieve ctx_9f…]
turn 5 [tool] npm test → 2 failed, 126 passed ← kept: error

turn 12 [tool] Read src/tax.ts ← kept verbatim

context_compression

Shrinks bulky data to its shape — the pattern stays, the repetition goes.

The generic catch-all for bulky tool output: it minifies JSON, caps oversized arrays and fields, and routes common shapes — diffs, grep, lint reports, stack traces, repetitive logs — to keep the useful lines and elide the rest (stashed and retrievable).

Runs after the specialists (command_digest, code_graph, relevance_filter); the optimizer enforces that order so the catch-all can't mangle a specialist's input — e.g. truncating code before code_graph can parse it.

On clients that can't call POST /v1/retrieve (coding assistants, subscription seats) it runs degraded: same deterministic compression of already-read history, but nothing is stashed and no retrieve handles are emitted; the current turn's fresh tool output is never touched.

Before
[
{ "id": 1, "name": "alice", "email": "a@ex.com", "active": true },
{ "id": 2, "name": "bob", "email": "b@ex.com", "active": true },
{ "id": 3, "name": "carol", "email": "c@ex.com", "active": false },
797 more rows …
] (≈ 41 KB)
After
[{"id":1,"name":"alice","email":"a@ex.com","active":true},
{"id":2,"name":"bob","email":"b@ex.com","active":true},
{"__anyray_elided__":798,"of":800,"retrieve":"h_3f9a"}] (≈ 0.2 KB)

output_externalize

Swaps a giant dump for a claim ticket — the full copy is stored, encrypted, one call away.

Replaces a large tool-output message with a compact handle and stashes the full original; the agent fetches the exact bytes back on demand via POST /v1/retrieve. The handle is a deterministic HMAC of the content (so it's cache-safe) and it skips segments that already carry a retrieve marker, keeping retrieval one hop deep.

Before
{"query":"SELECT * FROM events WHERE day='2026-06-01'", "rowCount":5000,
"rows":[{"id":1,"ts":"…","actor":"…","payload":{…}}, … 4,999 more rows …]}
(184,320 chars ≈ 180 KB)
After
[anyray: externalized 184320 chars · retrieve ctx_9f3a…] (51 chars on the wire)

command_digest

Keeps a command's verdict — the failures and the score line — and drops the scroll.

Recognizes known dev-command output and applies that command's own "keep what matters" rule: a test run keeps its failures and the count line, grep is bucketed by file, and repetitive logs collapse to [×N] runs. Deterministic and query-independent — it fires even with no question attached — and elided output is stashed and retrievable.

Grep hits collapse the same way. Matches that differ only in per-record bookkeeping — timestamp, duration, client IP — fold into one representative carrying its count and line span, so 21 identical POST /api/checkout … 500 lines become a single [×21] entry. What a reader searches for is never folded away: hits differing by path, identifier, or status code stay separate findings, so 500 and 200 never merge. Folding runs before the per-file cap, so the cap bounds distinct findings rather than being spent on repetition, and every count stays denominated in matches.

When the request carries the originating tool_use, the command name routes the output (a grep command whose output doesn't parse as hits is left untouched rather than summarized away). History only — the current turn's tool results always pass through.

Before
============ test session starts ============
platform linux — Python 3.11.8, pytest-8.1.1 · collected 128 items
tests/test_auth.py ......F............... [ 22%]
tests/test_billing.py .................... [ 71%]
tests/test_api.py ..........F......... [100%]
================== FAILURES ==================
FAIL: test_login_lockout — assert False is True (tests/test_auth.py:42)
FAIL: test_refund_rounding — assert 999 == 1000 (tests/test_billing.py:88)
====== 2 failed, 126 passed in 3.41s ======
After
FAIL test_login_lockout tests/test_auth.py:42 assert False is True
FAIL test_refund_rounding tests/test_billing.py:88 assert 999 == 1000
2 failed, 126 passed in 3.41s
… digest — full output retrievable (retrieve ctx_a4f1)

code_graph

Keeps the functions being worked on in full — and outlines the rest.

When an agent re-reads several source files in one request, code_graph keeps the functions the agent is working on — and the ones they call — in full, and replaces the other function bodies with a short marker while leaving their signatures in place. The hidden bodies are stashed and can be retrieved.

It picks what to keep by building a reference graph across the files in the request and matching it to what the current turn is about — not by how recent each file is. With no specific target, it outlines the whole file instead. It's deterministic and text-based — no LLM or embeddings — and works for both brace and indentation languages.

Before
# 6 files re-read this session · ≈ 48,000 chars
checkout.ts (asked about) charge(), refund()
pricing.ts (asked about) price()
tax.ts (imported) calcTax() ← called by price()
audit.ts (imported) log(), flush()
loyalty.ts (imported) award()
email.ts (imported) send()
After
# working set kept in full · ≈ 11,000 chars
checkout.ts charge(), refund() full body
pricing.ts price() full body
tax.ts calcTax() ← neighbor of price() full body
audit.ts log() → "[8 lines · retrieve ctx_7c]"
loyalty.ts, email.ts → outlined, bodies elided

relevance_filter

Keeps the lines that answer the live question — the rest is filed, not deleted.

Keeps only the tool-output lines that answer the current turn: it builds a query from the latest message, ranks every line with our own ranking algorithm, and keeps the top scorers plus a few leading lines for orientation. The human's own words (the "lead") are never ranked or trimmed, and elided lines are stashed and retrievable.

It runs ahead of code_graph as a coarse pre-filter and only touches history — the current turn's read always arrives intact — and on cached traffic it ranks just the uncached suffix.

Hybrid mode (opt-in): adds a local embedding re-rank to catch vocabulary mismatches (paraphrase, synonym); runs in-network and fails open to lexical-only.

Before
find the 500 errors on /checkout

10.0.2.14 GET /health 200 2ms
10.0.2.14 GET /assets/app.js 200 5ms
10.0.9.3 POST /checkout 500 812ms
10.0.2.14 GET /home 200 8ms
10.0.9.7 POST /checkout 500 903ms
… 1,195 more lines … (≈ 52,000 chars)
After
find the 500 errors on /checkout ← kept verbatim

… [312 line(s) elided as off-intent · retrieve ctx_5a]
10.0.9.3 POST /checkout 500 812ms
10.0.9.7 POST /checkout 500 903ms
10.0.9.7 POST /checkout 500 774ms
… [180 line(s) elided as off-intent · retrieve ctx_5a]
(≈ 3,800 chars)

tool_pruning

Benches the tools this conversation can't use — plug-in (MCP) tools never.

Drops tools unlikely to be called, trimming schema overhead on every request. It errs toward keeping: a tool survives if its name or description shares a topic word with the conversation. Generic words ("use"/"get"/"find") don't count, and neither does wording most of the registry's descriptions share — a shared preamble reading "travel assistant" can't keep all 16 travel tools; the tie must be to the individual tool's purpose. Namespaced MCP tools (mcp__…) are never pruned.

Re-deciding every turn would be cache-busting — the kept set shifts the tools prefix as the conversation grows — so on pinned sessions it decides once and replays the same selection byte-for-byte (a request-global decision pin; re-decided when the tool list changes, when tool_choice forces a pruned tool, or on a tool-miss regret signal). Without pins it stands aside on warm cached traffic and when cache_optimizer is stabilizing a prefix cache.

Before
task: "comment on the P0 bugs in the PAY project"

tools (3): (≈ 2,400 schema tokens)
jira_search_issues "Search issues in a Jira project by JQL."
slack_post_message "Post a message to a Slack channel."
datadog_query_metric "Query a Datadog metric over a range."
After
tools (1): (≈ 800 schema tokens)
jira_search_issues "Search issues in a Jira project by JQL."

prompt_compression

Tidies wordy instructions — same meaning, fewer words.

Collapses repeated whitespace and drops exact-duplicate sentences and paragraphs (keeping the first) in system/user messages over 400 chars, preserving line and list structure. Deterministic and lossless — fenced, inline, and indented code pass through byte-for-byte and tool output is never touched, so it stacks with the heavier strategies instead of overlapping them.

Before
You are a senior support engineer.


Read the ticket below and reply. Be concise. Be concise. Be concise.
Then suggest next steps. Then suggest next steps.
After
You are a senior support engineer.

Read the ticket below and reply. Be concise.
Then suggest next steps.

tool_schema_compression

Trims tool manuals, never the controls — types, enums, and required fields stay byte-for-byte.

Shrinks each tool's free-text description — collapses whitespace, strips boilerplate like "This tool allows you to…" — while leaving the structural schema (types, enums, required fields) byte-for-byte intact, so every tool stays callable.

Complements tool_pruning: pruning drops irrelevant tools, this shrinks the survivors. It depends only on each tool's own bytes, so it's deterministic and cache-safe.

Before
search_kb: "This tool allows you to search the knowledge base. Use it
whenever you need a citation." (~110 chars)
create_ticket: "This tool allows you to create a support ticket. Please be
sure to fill every field." (~102 chars)
After
search_kb: "search the knowledge base. Use it whenever you need
a citation." (~76 chars)
create_ticket: "create a support ticket. Fill every field." (~44 chars)

semantic_cache

Serves an exact repeat from memory — zero provider tokens, near-zero latency.

When a request exactly repeats one already seen, semantic_cache returns the stored response and skips the provider — zero inference tokens, near-zero latency — writing live responses back via /v1/cache. A hit requires an exact canonicalized match: scope, model, system/messages, tools, and answer-shaping params must all line up.

On a miss it also computes a shadow key — a volatile-normalized hash over tool output and system text (canonicalizing timestamps, UUIDs, request IDs, temp paths, retry counters) — and records whether that key would have hit. It's measurement only: it never serves a normalized hit or stores a shadow response, and normalized serve mode is not built.

Params: shadowNormalized (default on — toggles the shadow measurement lane), shadowMaxScanChars (default 200000 — caps the shadow scan).

Example
request #1 "What is our refund policy?" (gpt-4o) → MISS → provider (~2.3 s)
request #2 "What is our refund policy?" (gpt-4o) → HIT → served from cache
• provider skipped → 0 tokens billed
• ~0 ms vs ~2.3 s upstream

param_tuning

Caps runaway completion ceilings — a guardrail that honestly claims $0 saved.

Caps an over-large max_tokens (default cap 65536). A guardrail against runaway completions, not a spend saver — you're billed on tokens actually generated, so lowering the ceiling doesn't lower the bill. Requests already under the cap pass through unchanged, and no dashboard shows a saving that isn't real.

vision_ocr

Reads text-only screenshots into cheap text — diagrams and photos pass through untouched.

Swaps a text-only image — screenshot, stack trace, log — for its OCR'd text via local Tesseract, turning costly vision tokens into cheap text tokens. Only high-confidence text images are swapped; diagrams, charts, and photos pass through untouched. The original is stashed and retrievable, and it's fully local — no cloud OCR, no extra model call.

window_budget

Fits the conversation to a token budget — pins the ends, crops the low-relevance middle.

Off by default

window_budget is opt-in. Enable it in Settings → Optimizer when server-side context fitting is preferred; otherwise the coding client keeps ownership of its native transcript compaction.

When the conversation is over budget, window_budget crops the least useful middle messages until it fits — the first setup turns and the most recent turns stay pinned. Messages in between are scored by relevance × recency — our ranking algorithm weighs each against the latest turn and blends in position — so a stale-but-relevant message can outrank a fresh-but-noisy tool dump. Cropped originals are stashed and retrievable, tool-call/result pairs stay intact so the request stays valid, and it only evicts after the cached prefix — so it won't invalidate the provider's prompt cache.

The budget is model- and client-aware: when the gateway reports the model's real context window it targets 95% of that (so a 1M-context session isn't cropped to a 200k default). For a recognized Codex client, the gateway derives the client ceiling from model_auto_compact_token_limit, then model_context_window, then the resolved request-model window. Each adaptive value keeps the same 5% safety margin, so a configured 1M window yields a 950k budget rather than the old fixed 190k ceiling. Only a Codex request with no configured or resolvable window uses the 190k fallback. Without any model or client ceiling, the strategy uses maxTokens.

Fit estimates use the shared calibrated 3.3 Latin characters/token heuristic with CJK-aware sizing and fixed media costs. Operators can still tune charsPerToken when a workload needs a different calibration.

Upgrading from the old 4 chars/token default

charsPerToken used to default to 4, which under-counted real tokens by roughly 21% — that undershoot is what let Codex sessions grow past the client's own compaction boundary. With the calibrated 3.3 default, the same conversation estimates ~21% more tokens, so window_budget starts cropping sooner at an unchanged maxTokens. If you tuned maxTokens against the old sizing, either raise it by ~21% or set charsPerToken: 4 explicitly — the knob is a relative calibration on top of the shared estimator, so an explicit 4 restores the previous characters-per-token sizing (CJK-aware sizing and fixed media costs still apply). Larger values estimate fewer tokens and crop later; smaller values crop earlier.

Reported savings are capped at the billable window headroom: a conversation that outgrew the model's context window would have been rejected at full size — not billed — so the claimed saving never exceeds window − final size. Without a reported window, the claim is the cropped estimate as-is.

Params: maxTokens, modelAwareBudget (default on), charsPerToken (default 3.3), keepLeading, keepRecent, relevanceWeight (default 0.6; 0 = pure oldest-first), intentChars.

Example
[system] project setup + house rules ← pinned
[user] "refactor the checkout flow" ← pinned
[tool] read package.json (1.2k tok)
[tool] read 14 old source files (26k tok) ← stale but relevant
[tool] grep results ×9 (9k tok) ← fresh noise
[user] "now fix the failing test" ← pinned
40 messages · ≈ 41,000 tokens (over budget)

provider_context_trim

Asks Anthropic to clear aged tool results before billing — annotation-only; content is never read.

On Claude requests over a size threshold — any billing mode, it injects Anthropic's native context-management edit (clear_tool_uses_20250919) plus the context-management-2025-06-27 beta header — so Anthropic clears aged tool results before billing. API-billed requests stop paying for the cleared tokens; subscription seats stop burning usage-limit weight on them.

Annotation-only: it labels the request and never reads, rewrites, or removes message content — the privacy-cleanest strategy in the registry. A request carrying its own context_management block passes through verbatim (the client's config wins), and observation_mask defers when this strategy fires.

Params: triggerTokens (default 60000 — estimated-input threshold), keepToolUses (default 3), clearAtLeastTokens (default 5000).

Example
POST /v1/messages claude-* (metered API key)
long agent trajectory ≈ 72,000 input tokens — aged tool results re-billed every turn

+ injected context_management (body unchanged):
edits: [{ type: clear_tool_uses_20250919, trigger: 60,000 input tokens,
keep: 3 tool uses, clear_at_least: 5,000 tokens }]
→ Anthropic clears aged tool results pre-billing

output_shaping

Steers replies toward paths and diffs instead of re-printed files — the output lane's trim.

Targets the output lane — completion tokens cost well above input, and no input-side strategy touches them. On a routine tool-resume turn it appends one constant advisory note at the request tail: reference paths and diffs instead of restating context or re-printing unchanged files. The append is cache-neutral (it lands after the cached prefix).

:::warning Off by default This strategy is disabled by default. The advisory is instruction-shaped text placed in message content, and agentic clients (opencode, Claude Code, and coding agents generally) now treat embedded out-of-band instructions as a prompt-injection attempt and refuse them — so the steer never lands and the agent can waste a turn on injection-defense reasoning. Enable it per deployment only for non-agentic traffic, from Console → Optimizer → Strategies → Output shaping. :::

It deliberately claims zero estimated savings — steering output is real money but not provable per request — so the win shows up in measured steered-vs-unsteered reporting behind the task-outcome regression guard.

Params: minAssistantTurns (default 2). Only fires on a routine tool-resume turn — the fresh region is tool results, not a new question, and no fresh tool result is flagged failed.

Before
[assistant] edit applied → run tests
[tool] 128 passed (fresh input)
→ reply restates the plan + re-prints the whole edited file (≈ 1,900 output tokens)
After
[assistant] edit applied → run tests
[tool] 128 passed (fresh input)
+ one constant tail note: reference paths and diffs; don't re-print unchanged content
→ "Tests pass. Changed: src/tax.ts (+4 −1)." (≈ 300 output tokens)

reasoning_budget

Downshifts thinking on routine tool-resume turns — never adds it, never disables it, never raises it; a client-set value only ever moves down.

Downshifts reasoning effort on routine tool-resume turns for metered reasoning models (same qualification as output_shaping). Three practical lanes:

  • Anthropic thinking budget: caps a client-set thinking.budget_tokens down to anthropicBudgetCap for old clients that already send manual thinking. It never adds or removes thinking.
  • Anthropic effort: sets output_config.effort to anthropicEffort when absent (only for the allow-listed Claude 4.5/4.6/4.7/4.8/5/Fable models that accept the parameter), and clamps a client-set effort above the target down to it — e.g. a harness-pinned xhigh becomes medium on routine turns. Clamping needs no allowlist: the parameter's presence proves the model accepts it. Values at or below the target, and values outside the known ladder, are untouched.
  • OpenAI output controls: sets GPT-5 verbosity to verbosityLevel when absent, or reasoning_effort to openaiEffort for allow-listed reasoning models; client-set values above either target are clamped down to it the same way.

On pinned sessions the first qualifying decision mints one request-global pin containing only {lane, param, value} plus a model fingerprint. The thinking-budget pin replays the same scalar on every later turn even when the routine-turn gate would not qualify — thinking.budget_tokens is a messages-tier cache invalidator, so byte-stable replay keeps a session at at most one message-cache invalidation per session/model. Effort and verbosity are sampling parameters outside the provider's cached prompt prefix, so their pins are turn-scoped without cache cost: injected values back off on a turn whose fresh tool result carries a provider error flag (where fuller reasoning is useful), and clamp pins re-apply only on routine tool-resume turns — a new user question or an error turn keeps the client's own value. A model switch invalidates the pin; a downshift-regret signal replaces it with {lane: "suppressed"} so the session does not oscillate.

Default off — parameter steering can cause invisible quality regressions, so enable it first as a dogfood canary. It claims zero estimated savings for the new effort/verbosity lanes because their output-token effect is not measurable per request yet; the KPI is measured output-token and session-length deltas.

Params: minAssistantTurns (default 2), skipOnErrorTurn (default true — injections back off on error turns), anthropicBudgetCap (default 8192), anthropicEffort (default medium), anthropicEffortModels (allow-list), openaiEffort (default low), openaiModels (allow-list), verbosityLevel (default low), verbosityModels (default gpt-5*).

Before
[assistant] run tests with high reasoning
[tool] 128 passed (fresh input)
thinking.budget_tokens: 31999
After
[assistant] run tests with high reasoning
[tool] 128 passed (fresh input)
thinking.budget_tokens: 8192

cache_optimizer

Runs last and keeps the provider's cache discount alive — byte-identical prefixes, every turn.

Runs last. Stabilizes the request prefix so the provider reuses its prompt cache: it fixes the tools block into a byte-identical order every turn and, on Anthropic, inserts cache breakpoints for a cached-read discount. On selected OpenAI chat-completion model globs, when the client has not set one, it stamps a deterministic prompt_cache_key from tenant/session attribution so same-session turns route to the provider cache bucket; selected models can also receive prompt_cache_retention: "24h". Lossless — it only reorders tools and adds cache hints, never touching prompt content — and it backs off when the client already manages caching.

Params: injectBreakpoints, injectOnSubscription, minPrefixTokens, anchorQuantumMessages, conversationStableTurns (keep above observation_mask.keepRecentTurns), stampCacheKey, cacheKeyModels, cacheRetentionModels.

Read-only signals

These four return the request byte-identical and only emit signals — they never transform the payload, so there is no before/after.

audited_holdout (read only)

The control group — left unoptimized so savings are measured, not estimated.

A read-only marker for requests in the audited-savings holdout control arm. It's registered so its decisions have a stable id, but you don't enable it in the strategies array — the top-level holdout config block owns assignment.

Default off. When enabled, a holdout request skips every mutating and short-circuiting strategy, keeps read-only signals like content_census and cache_lint, and emits a zero-savings decision. No would-have-saved number is fabricated — the real delta is computed downstream from actual tokens and cost against the treated cohort.

context_quality (read only)

A 0–100 health score for every conversation — no transform, just a signal.

Returns the request unchanged and emits one decision carrying a 0–100 context-health score, built from token-window fill, message count, and bloated/duplicate tool outputs. The score rides the decision's metric field, so it reaches the console even with content mode off.

content_census (read only)

Counts what kinds of content flow through — to size the next strategy before building it.

Default on. Returns the request unchanged and classifies tool-output segments by shape (json_array, json_object, unified_diff, search_grep, build_test_log, source_code, prose, and more) to size future strategies before they're built. It scans only each segment's head and rolls class counts into an in-memory per-tenant window — never recording prompts, responses, filenames, tool names, arguments, JSON keys, or handles. No savings are claimed.

cache_lint (read only)

Detects per-session prefix churn before fleet-level cache drift.

Default on. Returns the request unchanged and compares only 64-bit hashes of the session's tools block, system prompt, and stable message head (excluding the trailing live turn). When a region changes between turns, it emits a zero-savings decision with booleans for the changed regions and classifies the churn as optimizer-explained or client-volatile. It rolls per-tenant counts into the admin API without recording prompt text, tool names, arguments, JSON keys, or message content.