JSPM

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

OpenCode plugin for LLM prompt caching, token savings, and KV cache reuse across AI agent sessions

Package Exports

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

Readme

npm version OpenCode Multi-CLI DeepSeek Anthropic OpenAI Cache gain 40-88% MIT Zero dependencies

🧠 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 --global

Restart 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 --json

Inside 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 scripts

Output

╔══════════════════════════════════════════════════════════════╗
β•‘              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__deepseek

The 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.