Package Exports
- agent-cache-optimizer
- agent-cache-optimizer/core
- agent-cache-optimizer/heuristics
- agent-cache-optimizer/splitting
Readme
π§ agent-cache-optimizer
OpenCode plugin for LLM prompt caching, token savings, and KV cache reuse
Automatically reorders stable system prompt blocks so DeepSeek, Anthropic, OpenAI, and other prefix-cache providers can reuse more cache across agent sessions.
Boost prompt cache hit rates by 40β88%.
Zero config. Zero content knowledge. Works with any agent framework.
English | δΈζ
π€ Who needs this?
Use this if your CLI agent has large system prompts, MCP tools, skills,
CLAUDE.md files, handoff memory, or frequently changing context blocks.
Typical users run OpenCode, Claude Code, Codex CLI, Gemini CLI, or custom LLM agents and want higher prompt cache hit rates, fewer recomputed input tokens, and lower API cost.
π― The Problem
LLM providers (DeepSeek, Anthropic, OpenAI, Google) use prefix-match KV caching: if your prompt starts with the same bytes as a previous request, the computed key-value states are reused β cache hits cost near-zero tokens.
But every CLI agent puts dynamic content at the FRONT of the system prompt:
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β β‘ HANDOFF block (changes every session) β β Cache BUSTED
β β‘ REMEMBER / MEMORY (changes throughout day) β β Cache BUSTED
βββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
CLAUDE.md (changes weekly) β β Never reached
β β
Agent definitions (static) β β Never reached
β β
MCP / Skills / Tools (static) β β Never reached
β β‘ currentDate (changes daily) β
β β‘ Memory injection (changes per query) β
βββββββββββββββββββββββββββββββββββββββββββββββββββResult: 0% cache reuse across sessions. Every session recomputes the entire system prompt from scratch, even though 70-90% of it hasn't changed.
π‘ The Fix
agent-cache-optimizer reorders the system prompt at runtime:
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
CLAUDE.md (stable) β Cached β
β β
Agent definitions (stable) β Cached β
β β
MCP / Skills / Tools β Cached β
β β
Tool definitions β Cached β
βββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β‘ currentDate β
β β‘ HANDOFF / REMEMBER / MEMORY β
β β‘ Memory injection β
βββββββββββββββββββββββββββββββββββββββββββββββββββStable blocks first β prefix survives session changes β 40-88% cache reuse.
π Install
OpenCode
{
"plugin": ["agent-cache-optimizer"]
}Add to ~/.config/opencode/opencode.json. OpenCode auto-installs from npm on next startup.
# Or via CLI
opencode plugin agent-cache-optimizer --globalRestart OpenCode β done. Zero config. Works immediately for DeepSeek, Anthropic, OpenAI, and any provider with prefix-match KV caching.
Claude Code
Claude Code does not expose a runtime prompt-transform hook, so this package loads as a Claude Code companion plugin for status and cache-friendly prompt guidance rather than automatic system block reordering.
From this checkout:
PLUGIN_DIR="$(pwd)"
# Validate plugin metadata without starting a model session
claude plugin validate "$PLUGIN_DIR"
# Confirm Claude Code can see the session-only plugin
claude --plugin-dir "$PLUGIN_DIR" plugin list
claude --plugin-dir "$PLUGIN_DIR" plugin details agent-cache-optimizer
# Start Claude Code with the plugin loaded for this session
claude --plugin-dir "$PLUGIN_DIR"Inside Claude Code, use /agent-cache-status to inspect local optimizer status.
For cache-friendly Claude sessions, structure stable CLAUDE.md content first
and use Claude Code's --exclude-dynamic-system-prompt-sections option when you
are using the default system prompt.
Codex CLI
Codex CLI loads reusable workflows as skills or plugins. This package ships a
Codex plugin manifest and the agent-cache-status skill, so it can be exposed
through a local Codex plugin marketplace during development.
From this checkout:
PLUGIN_DIR="$(pwd)"
MARKETPLACE_ROOT="${HOME}/.local/share/agent-cache-optimizer-codex"
# Copy this plugin into a local Codex marketplace layout.
mkdir -p "$MARKETPLACE_ROOT/plugins/agent-cache-optimizer"
mkdir -p "$MARKETPLACE_ROOT/.agents/plugins"
rsync -a --delete \
--exclude .git \
--exclude node_modules \
"$PLUGIN_DIR"/ "$MARKETPLACE_ROOT/plugins/agent-cache-optimizer"/
cat > "$MARKETPLACE_ROOT/.agents/plugins/marketplace.json" <<'JSON'
{
"name": "agent-cache-optimizer-local",
"interface": {
"displayName": "Agent Cache Optimizer Local"
},
"plugins": [
{
"name": "agent-cache-optimizer",
"source": {
"source": "local",
"path": "./plugins/agent-cache-optimizer"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Productivity"
}
]
}
JSON
# Register the marketplace and install the plugin.
codex plugin marketplace add "$MARKETPLACE_ROOT"
codex plugin add agent-cache-optimizer@agent-cache-optimizer-local
# Confirm Codex installed and enabled it.
codex plugin list --jsonInside Codex, invoke $agent-cache-optimizer:agent-cache-status or use
/skills to select the skill. Start a new Codex thread after installing or
updating the plugin so the skill list is refreshed.
Verify
# OpenCode: check plugin is loaded
opencode debug config | grep agent-cache-optimizer
# OpenCode: watch reorder activity in real time
tail -f ~/.cache/opencode/agent-cache-optimizer/diag.log
# Claude Code: inspect the session-only plugin
claude --plugin-dir "$(pwd)" plugin details agent-cache-optimizer
# Codex CLI: confirm the skill is visible in the prompt input
codex debug prompt-input "Use agent-cache-optimizer status" \
| grep 'agent-cache-optimizer:agent-cache-status'Status dashboard
agent-cache-optimizer status # text dashboard
agent-cache-optimizer status --json # JSON for scriptsOutput
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β KV Cache Optimizer Status β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£
β Status: ACTIVE β
β Mode: WARM (12 scopes, 150 observations) β
β Uptime: 2026-06-24T15:30 β 2026-06-25T16:45 β
β Structured events: 1267 jsonl records β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£
β Scope Obs Positions Stable β
β deepseek__deepseek-chat__orch 12 25 25/25 β
β deepseek__deepseek-chat__oracle 3 5 5/5 β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£
β Est. savings: $1.2345 over 50 calls β
β Warm cache: 52 stable hashes pinned (18 global + 34 scoped) β
β Cache hit: 96.4% (29952/31061 input tokens) β
β Last reorder: S:25 U:0 D:0 T:25 obs:150 β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββπ How It Works
1. Observe (content-addressed, position-independent)
The plugin never reads the content of your prompts. It only hashes each system block and tracks which hashes stay the same vs change across calls.
Session 1: [H1, CLAUDE-A, AGENT-X, TOOLS-V1, DATE-1, MEM-1]
Session 2: [H2, CLAUDE-A, AGENT-X, TOOLS-V1, DATE-2, MEM-2]
Session 3: [H3, CLAUDE-A, AGENT-X, TOOLS-V1, DATE-2, MEM-3]
After 3 observations:
H1/H2/H3 at position 0 change every time β score 0.0 (dynamic)
CLAUDE-A at position 1 never changes β score 1.0 (stable)
AGENT-X at position 2 never changes β score 1.0 (stable)
...2. Split & Classify
Large blocks (>4KB) are split at structural boundaries β JSON arrays, Markdown headings, XML elements, and long lists β using a robust brace-depth parser that handles arbitrary nesting and fenced code blocks.
Cold-start heuristics detect volatile metadata patterns (currentDate,
session ID, timestamp) and cap their scores to prevent structural
boosts from misclassifying them as stable.
ββββββββββββ
ββββββββββββ ββββββββββββ ββββββββββββ ββββββββββββ β STABLE β
β HANDOFF β β CLAUDE β β TOOLS β β MEMORY β βββΆ β CLAUDE β
β (dynamic)β β (stable) β β (stable) β β (dynamic)β β TOOLS β
ββββββββββββ ββββββββββββ ββββββββββββ ββββββββββββ ββββββββββββ
β‘ β
β
β‘ β DYNAMIC β
β HANDOFF β
β MEMORY β
ββββββββββββ3. Two-phase decision
| Phase | Trigger | Method |
|---|---|---|
| Cold start | First 2 calls per scope | Universal position/size/structure heuristics |
| Warm | 3+ calls | Hash-based stability scores |
The cold-start heuristics use only structural signals (position, size, delimiters, line density) β no keyword matching, no config awareness. This means the plugin works immediately with any agent setup.
4. Provider Cache Metrics
Real cache hit rates are tracked from OpenCode provider events β no
estimation needed. cache-metrics.json records per-scope and
total cacheReadTokens, cacheWriteTokens, and cacheHitRate.
All session and message IDs are content-hashed for privacy.
π Benchmarks
Tested on a realistic OpenCode orchestrator prompt (~25KB system prompt):
| Scenario | Cacheable prefix | Improvement |
|---|---|---|
| Original (no reorder) | 0 KB (0%) | β |
| Cold start (heuristics) | 21.8 KB (88%) | +88% |
| Content-addressed (v0.5) | 52.9 KB (100%) | +100% |
Production results (155 observations, deepseek-v4-pro):
| Phase | S | U | D | Stable KB |
|---|---|---|---|---|
| Pre-v0.5 (position-based) | 1 | 0 | 24 | ~2 KB |
| v0.5 (content-addressed) | 25 | 0 | 0 | 52.9 KB |
v0.6.1 real-world DeepSeek run (2026-06-26)
The v0.6.1 tag documents provider-reported cache metrics from a local
OpenCode run, filtered with the same command used during testing:
cat ~/.cache/opencode/agent-cache-optimizer/diag.log \
| grep hit \
| grep 2026-06-26 \
| grep deepseek__deepseekThe filtered run contained 309 cache-hit metric samples:
| Scope | Samples | Hit rate observed | Latest metric |
|---|---|---|---|
deepseek-v4-pro / orchestrator |
86 | 98.0-98.3% | 98.1% hit rate, 39,084,800 cache-read tokens |
deepseek-v4-flash / fixer |
196 | 86.1-99.2% | 99.0% hit rate, 21,218,432 cache-read tokens |
deepseek-v4-flash / explorer |
27 | 0.0-90.3% | 90.3% hit rate after warm-up, 1,229,696 cache-read tokens |
The explorer scope shows cold-start behavior: the first sample was 0.0%
before the provider cache warmed, and later samples reached 90%+ hit rate.
π Supported Platforms
| Platform | Status | Adapter |
|---|---|---|
| OpenCode | β Plugin | src/index.ts (native) |
| Claude Code | β Companion plugin + guidelines | adapters/claude-code.md |
| Codex | β Companion plugin + skill | .codex-plugin/plugin.json + skills/ |
| Gemini CLI | π Planned | Google context caching |
π§© API (standalone usage)
The core engine is CLI-agnostic. Use it in any project:
import { emptyDB, updateDB, classify } from "agent-cache-optimizer"
// Track stability
let db = emptyDB()
const blocks = ["HANDOFF...", "CLAUDE.md...", "AGENT...", "MEMORY..."]
// Classify and reorder
const classified = classify(blocks, db)
const optimized = [...classified.stable, ...classified.unknown, ...classified.dynamic]
// Update for next call
db = updateDB(db, optimized)π Project Structure
agent-cache-optimizer/
βββ src/
β βββ index.ts # OpenCode plugin entry
β βββ core.ts # Content-addressed hash engine
β βββ heuristics.ts # Cold-start + content classifiers
β βββ splitting.ts # Large block splitter (brace-depth parser)
β βββ types.ts # TypeScript types
β βββ __tests__/ # Unit tests (vitest)
β βββ plugin.test.ts
β βββ heuristics-splitting.test.ts
βββ adapters/
β βββ claude-code.md # Claude Code optimization guide
β βββ conversation-log.md # Append-only log guidelines
βββ bin/
β βββ aco # CLI: agent-cache-optimizer status
βββ scripts/
β βββ cache-status.sh # Legacy status script
β βββ check-cache-friendly.sh # Config audit tool
βββ docs/
β βββ deep-research-kv-cache.md # DeepSeek KV cache research
β βββ cross-cli.md # Cross-CLI architecture
β βββ upstream.md # Upstream fix recommendations
βββ README.md + README.zh-CN.md
βββ CHANGELOG.md
βββ LICENSE (MIT)π Cache-Friendliness Audit
Check any config file for patterns that bust the KV cache:
bash scripts/check-cache-friendly.sh CLAUDE.md
bash scripts/check-cache-friendly.sh --opencode
bash scripts/check-cache-friendly.sh --allπ FAQ
Q: Does this change my prompts? A: Only the ORDER of system blocks. Content is never modified.
Q: Will it break my agent? A: No. The LLM sees the same blocks, just in a different order. System prompts are position-independent by design.
Q: Does it work with non-OpenCode agents? A: The core engine is CLI-agnostic. Adapters exist for OpenCode (plugin) and Claude Code (guidelines). Codex and Gemini CLI adapters are planned.
Q: What if my prompts change? A: The hash-based tracking adapts automatically. If a previously-stable block starts changing, its score drops and it moves to dynamic. If a new stable block is added, it converges to stable after a few observations.
Q: Does this work with Anthropic's prompt caching?
A: Yes β the chat.headers hook adds the prompt-caching-2024-07-31 beta
header automatically for Anthropic providers.
π License
MIT β use it, fork it, ship it, star it β
Built with β€οΈ for the LLM CLI ecosystem. If this saved you tokens,
drop a β on GitHub β it helps more people find it.