JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 50
  • Score
    100M100P100Q92157F
  • License MIT

Content-agnostic KV cache optimizer for LLM CLI agents β€” boosts prompt cache hit rate by 40-88% through automatic stability tracking and block reordering

Package Exports

  • agent-cache-optimizer
  • agent-cache-optimizer/core
  • agent-cache-optimizer/heuristics
  • agent-cache-optimizer/splitting

Readme

npm version OpenCode Cache gain 40-88% MIT Zero dependencies

🧠 agent-cache-optimizer

Content-agnostic KV cache optimizer for LLM CLI agents

Boost prompt cache hit rates by 40–88%.
Zero config. Zero content knowledge. Works with any agent framework.


🎯 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

{
  "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 --global

Restart OpenCode β€” done. Zero config. Works immediately for DeepSeek, Anthropic, OpenAI, and any provider with prefix-match KV caching.

Verify

# Check plugin is loaded
opencode debug config | grep agent-cache-optimizer

# Watch reorder activity in real time
tail -f ~/.cache/opencode/agent-cache-optimizer/diag.log

Status dashboard

agent-cache-optimizer status           # text dashboard
agent-cache-optimizer status --json    # JSON for scripts

Output

╔══════════════════════════════════════════════════════════════╗
β•‘              KV Cache Optimizer Status                       β•‘
╠══════════════════════════════════════════════════════════════╣
β•‘ Status:  ACTIVE                                              β•‘
β•‘ Mode:    orchestrator=WARM oracle=COLD                        β•‘
β•‘ Uptime:  2026-06-24T15:30 β†’ 2026-06-24T16:45                β•‘
╠══════════════════════════════════════════════════════════════╣
β•‘ Agent              Obs  Positions     Stable                 β•‘
β•‘ orchestrator        12         11      8/11                  β•‘
β•‘ oracle               3          5      3/5                   β•‘
╠══════════════════════════════════════════════════════════════╣
β•‘ Estimated cache reuse: ~73% of system prompt                 β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

πŸ— How It Works

1. Observe (content-agnostic)

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. Classify & Reorder

                                                                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”‚ 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 agent 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.

πŸ“Š 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%
Warm (hash-based, 3+ sessions) 21.8 KB (88%) +88%

Per-agent results (3 different agent configurations):

Agent Blocks Stable Dynamic Cacheable
orchestrator 11 8 3 88%
oracle 6 3 3 88%
fixer 6 3 3 90%

πŸ”Œ Supported Platforms

Platform Status Adapter
OpenCode βœ… Plugin src/index.ts (native)
Claude Code πŸ“– Guidelines adapters/claude-code.md
Codex πŸ”œ Planned Adapt OpenCode plugin
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 point
β”‚   β”œβ”€β”€ core.ts           # Hash-tracking engine (CLI-agnostic)
β”‚   β”œβ”€β”€ heuristics.ts     # Cold-start position/size classifiers
β”‚   β”œβ”€β”€ splitting.ts      # Large block splitter (>4KB)
β”‚   └── types.ts          # TypeScript types
β”œβ”€β”€ adapters/
β”‚   └── claude-code.md    # Claude Code optimization guide
β”œβ”€β”€ bin/
β”‚   └── aco               # CLI: agent-cache-optimizer status
β”œβ”€β”€ scripts/
β”‚   β”œβ”€β”€ cache-status.sh   # Status dashboard (legacy)
β”‚   └── check-cache-friendly.sh  # Config audit tool
β”œβ”€β”€ docs/
β”‚   β”œβ”€β”€ cross-cli.md      # Cross-CLI architecture
β”‚   └── upstream.md       # Upstream fix recommendations
β”œβ”€β”€ README.md
β”œβ”€β”€ README.zh-CN.md       # δΈ­ζ–‡ζ–‡ζ‘£
β”œβ”€β”€ LICENSE               # MIT
└── CHANGELOG.md

πŸ›  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.