agent-cache-optimizer 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,89 @@
1
+ # KV Cache Optimization — Cross-CLI Architecture
2
+
3
+ ## Core Principle (CLI-agnostic)
4
+
5
+ All LLM CLI agents assemble a system prompt from:
6
+ 1. **Stable blocks**: CLAUDE.md / AGENTS.md / rules / tool definitions / agent configs
7
+ 2. **Semi-dynamic blocks**: currentDate (changes daily)
8
+ 3. **Dynamic blocks**: session handoff / memory injections / conversation history
9
+
10
+ Prefix-match KV caches (DeepSeek, Anthropic, Google, OpenAI) reuse computed
11
+ states when the prompt PREFIX is byte-identical to a cached request. Dynamic
12
+ content at the front busts everything.
13
+
14
+ ## Architecture
15
+
16
+ ```
17
+ ┌──────────────────────────────────────────────────────┐
18
+ │ agent-cache-optimizer-core │
19
+ │ │
20
+ │ hash(track) → stability score → reorder │
21
+ │ Fully content-agnostic, zero external deps │
22
+ │ Input: string[] (system blocks) │
23
+ │ Output: string[] (reordered blocks) │
24
+ │ State: per-agent stability.json │
25
+ └──────────────────────────────────────────────────────┘
26
+ │ │ │
27
+ ┌────▼─────┐ ┌─────▼──────┐ ┌────▼──────┐
28
+ │ OpenCode │ │Claude Code │ │ Codex │
29
+ │ adapter │ │ adapter │ │ adapter │
30
+ │ │ │ │ │ │
31
+ │ plugin.ts │ │ hook.js │ │ plugin.py │
32
+ │ system │ │ pre-launch │ │ (TBD) │
33
+ │ .transform│ │ validator │ │ │
34
+ └───────────┘ └────────────┘ └────────────┘
35
+ ```
36
+
37
+ ## Per-CLI Strategy
38
+
39
+ ### OpenCode (✅ implemented)
40
+
41
+ - Hook: `experimental.chat.system.transform` — direct system prompt access
42
+ - Fallback: `chat.params` + `chat.headers` for diagnostics
43
+ - Adapter: `plugins/agent-cache-optimizer.ts`
44
+
45
+ ### Claude Code (see [adapters/claude-code.md](../adapters/claude-code.md))
46
+
47
+ - No direct prompt-transform hook available
48
+ - Anthropic has native prompt caching (automatic prefix reuse)
49
+ - Strategy: **pre-session validator** + CLAUDE.md structure optimization
50
+ - Check: CLAUDE.md for date stamps, session IDs, dynamic includes
51
+ - Check: `.claude/settings.json` hooks for cache-busting patterns
52
+ - Wrap `claude` invocation to compare cache metrics before/after
53
+
54
+ ### Codex (OpenAI)
55
+
56
+ - Plugin API likely similar to OpenCode (both from AI SDK ecosystem)
57
+ - Strategy: adapt OpenCode plugin once Codex plugin API is confirmed
58
+ - OpenAI prompt caching uses similar prefix-match mechanism
59
+
60
+ ### Gemini CLI
61
+
62
+ - Uses `activate_skill` + GEMINI.md for project context
63
+ - Google context caching API has explicit cache tokens
64
+ - Strategy: inject cache_control markers at stable block boundaries
65
+
66
+ ## Shared Core Module
67
+
68
+ `agent-cache-optimizer-core.ts`:
69
+
70
+ ```typescript
71
+ // Pure functions, no CLI-specific dependencies
72
+ export function hashBlock(content: string): string
73
+ export function coldStartScore(block: string, index: number, total: number): number
74
+ export function classifyBlocks(blocks: string[], db: StabilityDB): Classified
75
+ export function updateStabilityDB(db: StabilityDB, blocks: string[]): StabilityDB
76
+ ```
77
+
78
+ Each adapter imports these and adds CLI-specific glue:
79
+ 1. Extract blocks from CLI's prompt representation
80
+ 2. Call `classifyBlocks()`
81
+ 3. Inject reordered blocks back into CLI's prompt
82
+
83
+ ## Roadmap
84
+
85
+ 1. [x] OpenCode plugin (V3: per-agent + block splitting + diagnostics)
86
+ 2. [ ] Extract shared core to `agent-cache-optimizer-core.ts`
87
+ 3. [ ] Claude Code pre-session validator
88
+ 4. [ ] Codex adapter (once API confirmed)
89
+ 5. [ ] Gemini CLI adapter (Google context caching)
@@ -0,0 +1,65 @@
1
+ # KV Cache Optimization for DeepSeek — Upstream Changes Needed
2
+
3
+ ## Problem
4
+
5
+ OpenCode's system prompt assembly places **dynamic blocks** (SessionStart HANDOFF,
6
+ .remember/ files, memory-dream injections, currentDate) at the **front** of the
7
+ system prompt array. This means the very first bytes of every session's system
8
+ prompt are different, which completely busts prefix-match KV caches (DeepSeek,
9
+ Anthropic prompt caching, Google context caching).
10
+
11
+ ## Current prompt assembly order (observed)
12
+
13
+ ```
14
+ [0] SessionStart HANDOFF + REMEMBER + MEMORY ← ⚡ changes every session
15
+ [1] CLAUDE.md ← ✅ stable
16
+ [2] Agent definitions ← ✅ stable
17
+ [3] MCP server instructions ← ✅ stable
18
+ [4] Skills list ← ✅ stable
19
+ [5] currentDate ← ⚠️ changes daily
20
+ [6] Tool definitions ← ✅ stable
21
+ ```
22
+
23
+ ## Impact
24
+
25
+ - **Every new session: 100% cache miss** on system prompt
26
+ - **Every subagent call: fresh KV computation** for the full system prompt
27
+ - **Estimated waste**: 40-60% of system prompt tokens recomputed unnecessarily
28
+ - For DeepSeek where cache hit cost is near-zero, this is purely wasted compute
29
+
30
+ ## Fix (in OpenCode core)
31
+
32
+ Move dynamic blocks to the **end** of the system prompt array:
33
+
34
+ ```
35
+ [0] CLAUDE.md ← ✅ stable
36
+ [1] Agent definitions ← ✅ stable
37
+ [2] MCP server instructions ← ✅ stable
38
+ [3] Skills list ← ✅ stable
39
+ [4] Tool definitions ← ✅ stable
40
+ [5] currentDate ← ⚠️ changes daily
41
+ [6] SessionStart HANDOFF + REMEMBER + MEMORY ← ⚡ changes per session
42
+ ```
43
+
44
+ This way, the KV cache prefix (blocks [0]-[4]) stays identical across sessions
45
+ and subagent calls, giving 70-80% cache reuse.
46
+
47
+ ## Implementation in OpenCode
48
+
49
+ In the prompt assembly code (likely in the system prompt builder), change:
50
+
51
+ 1. **Build stable prefix first**: all static configuration (CLAUDE.md, agent
52
+ definitions, MCP/Skills/tools lists)
53
+ 2. **Append semi-dynamic content**: currentDate
54
+ 3. **Append fully-dynamic content last**: SessionStart hook output,
55
+ .remember/ injection, memory-dream retrieval results
56
+
57
+ ## Interim workaround
58
+
59
+ A plugin (`agent-cache-optimizer.ts`) uses `experimental.chat.system.transform` to
60
+ reorder blocks at runtime. This works but:
61
+ - Depends on an experimental hook
62
+ - Adds a small processing overhead
63
+ - Can't reorder content WITHIN blocks, only BETWEEN blocks
64
+
65
+ The proper fix is in the core prompt builder.
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "agent-cache-optimizer",
3
+ "version": "0.1.1",
4
+ "description": "Content-agnostic KV cache optimizer for LLM CLI agents — boosts prompt cache hit rate by 40-88% through automatic stability tracking and block reordering",
5
+ "keywords": [
6
+ "kv-cache",
7
+ "prompt-caching",
8
+ "llm",
9
+ "deepseek",
10
+ "anthropic",
11
+ "openai",
12
+ "opencode",
13
+ "claude-code",
14
+ "optimization",
15
+ "token-savings"
16
+ ],
17
+ "license": "MIT",
18
+ "author": {
19
+ "name": "Chris",
20
+ "url": "https://github.com/chris"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/uuie/agent-cache-optimizer"
25
+ },
26
+ "type": "module",
27
+ "main": "./src/index.ts",
28
+ "exports": {
29
+ ".": "./src/index.ts",
30
+ "./core": "./src/core.ts",
31
+ "./heuristics": "./src/heuristics.ts",
32
+ "./splitting": "./src/splitting.ts"
33
+ },
34
+ "files": [
35
+ "src/",
36
+ "adapters/",
37
+ "scripts/",
38
+ "skills/",
39
+ "docs/",
40
+ "README.md",
41
+ "README.zh-CN.md",
42
+ "LICENSE",
43
+ "CHANGELOG.md",
44
+ "tsconfig.json"
45
+ ],
46
+ "scripts": {
47
+ "test": "vitest run",
48
+ "test:watch": "vitest",
49
+ "typecheck": "tsc --noEmit",
50
+ "format": "prettier --write \"src/**/*.ts\" \"*.md\"",
51
+ "format:check": "prettier --check \"src/**/*.ts\" \"*.md\"",
52
+ "check": "bash scripts/cache-status.sh",
53
+ "audit": "bash scripts/check-cache-friendly.sh --all"
54
+ },
55
+ "devDependencies": {
56
+ "@opencode-ai/plugin": "^1.15.0",
57
+ "@types/node": "^26.0.0",
58
+ "prettier": "^3.3.0",
59
+ "typescript": "^6.0.3",
60
+ "vitest": "^2.1.0"
61
+ },
62
+ "peerDependencies": {
63
+ "@opencode-ai/plugin": "^1.15.0"
64
+ },
65
+ "peerDependenciesMeta": {
66
+ "@opencode-ai/plugin": {
67
+ "optional": true
68
+ }
69
+ }
70
+ }
@@ -0,0 +1,170 @@
1
+ #!/usr/bin/env bash
2
+ # cache-status.sh — display KV Cache Optimizer status
3
+ # Usage: cache-status.sh [--json]
4
+ set -euo pipefail
5
+
6
+ CACHE_DIR="${HOME}/.cache/opencode/agent-cache-optimizer"
7
+ BOLD='\033[1m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; CYAN='\033[0;36m'; NC='\033[0m'
8
+
9
+ # ── Check if plugin has run ────────────────────────────────────────
10
+
11
+ if [[ ! -d "$CACHE_DIR" ]]; then
12
+ echo "╔══════════════════════════════════════════════════════════════╗"
13
+ echo "║ KV Cache Optimizer Status ║"
14
+ echo "╠══════════════════════════════════════════════════════════════╣"
15
+ echo "║ Status: NO DATA ║"
16
+ echo "║ ║"
17
+ echo "║ Plugin has not been invoked yet. ║"
18
+ echo "║ ║"
19
+ echo "║ 1. Verify agent-cache-optimizer.ts is in opencode.json plugins ║"
20
+ echo "║ 2. Restart OpenCode ║"
21
+ echo "║ 3. Make at least one request to trigger the hook ║"
22
+ echo "╚══════════════════════════════════════════════════════════════╝"
23
+ exit 0
24
+ fi
25
+
26
+ # ── Parse stability DBs ────────────────────────────────────────────
27
+
28
+ declare -A agent_obs agent_positions agent_stable_pct agent_mode
29
+
30
+ for db in "$CACHE_DIR"/stability-*.json; do
31
+ [[ -f "$db" ]] || continue
32
+ agent=$(basename "$db" .json | sed 's/^stability-//')
33
+
34
+ obs=$(python3 -c "
35
+ import json
36
+ d=json.load(open('$db'))
37
+ print(d.get('observations', 0))
38
+ " 2>/dev/null || echo 0)
39
+
40
+ pos_count=$(python3 -c "
41
+ import json
42
+ d=json.load(open('$db'))
43
+ print(len(d.get('positions', {})))
44
+ " 2>/dev/null || echo 0)
45
+
46
+ # Average score across all known hashes
47
+ avg_score=$(python3 -c "
48
+ import json
49
+ d=json.load(open('$db'))
50
+ scores = list(d.get('scores', {}).values())
51
+ if scores:
52
+ stable = sum(1 for s in scores if s >= 0.7)
53
+ dynamic = sum(1 for s in scores if s <= 0.3)
54
+ print(f'{stable}/{len(scores)}')
55
+ else:
56
+ print('0/0')
57
+ " 2>/dev/null || echo "0/0")
58
+
59
+ agent_obs[$agent]=$obs
60
+ agent_positions[$agent]=$pos_count
61
+ agent_stable_pct[$agent]=$avg_score
62
+
63
+ if [[ $obs -ge 3 ]]; then
64
+ agent_mode[$agent]="WARM"
65
+ else
66
+ agent_mode[$agent]="COLD"
67
+ fi
68
+ done
69
+
70
+ # ── Parse diag log ─────────────────────────────────────────────────
71
+
72
+ diag_entries=0
73
+ last_entry=""
74
+ if [[ -f "$CACHE_DIR/diag.log" ]]; then
75
+ diag_entries=$(wc -l < "$CACHE_DIR/diag.log" | tr -d ' ')
76
+ last_entry=$(tail -1 "$CACHE_DIR/diag.log" 2>/dev/null || echo "(empty)")
77
+ fi
78
+
79
+ # ── Display ────────────────────────────────────────────────────────
80
+
81
+ echo ""
82
+ echo -e "${BOLD}╔══════════════════════════════════════════════════════════════╗${NC}"
83
+ echo -e "${BOLD}║ KV Cache Optimizer Status ║${NC}"
84
+ echo -e "${BOLD}╠══════════════════════════════════════════════════════════════╣${NC}"
85
+
86
+ # Status line
87
+ if [[ $diag_entries -gt 0 ]]; then
88
+ echo -e "║ ${GREEN}Status: ACTIVE${NC} ║"
89
+ else
90
+ echo -e "║ ${RED}Status: NO ACTIVITY (check plugin loading)${NC} ║"
91
+ fi
92
+
93
+ # Mode: cold/warm per agent
94
+ modes=""
95
+ for agent in "${!agent_obs[@]}"; do
96
+ modes+="$agent=${agent_mode[$agent]} "
97
+ done
98
+ printf "║ Mode: %-52s ║\n" "$modes"
99
+
100
+ # Uptime from diag.log first/last
101
+ if [[ $diag_entries -gt 0 ]]; then
102
+ first_ts=$(head -1 "$CACHE_DIR/diag.log" | grep -oP '^\[[^\]]+\]' | tr -d '[]' || echo "?")
103
+ last_ts=$(tail -1 "$CACHE_DIR/diag.log" | grep -oP '^\[[^\]]+\]' | tr -d '[]' || echo "?")
104
+ printf "║ Uptime: %-52s ║\n" "$first_ts → $last_ts"
105
+ fi
106
+
107
+ echo -e "${BOLD}╠══════════════════════════════════════════════════════════════╣${NC}"
108
+
109
+ # Per-agent breakdown
110
+ if [[ ${#agent_obs[@]} -eq 0 ]]; then
111
+ echo -e "║ ${YELLOW}No per-agent data yet — make requests with different agents${NC} ║"
112
+ else
113
+ printf "║ ${CYAN}%-20s %6s %8s %10s${NC} ║\n" "Agent" "Obs" "Positions" "Stable"
114
+ for agent in $(printf '%s\n' "${!agent_obs[@]}" | sort); do
115
+ printf "║ %-20s %6s %8s %10s ║\n" \
116
+ "$agent" "${agent_obs[$agent]}" "${agent_positions[$agent]}" "${agent_stable_pct[$agent]}"
117
+ done
118
+ fi
119
+
120
+ echo -e "${BOLD}╠══════════════════════════════════════════════════════════════╣${NC}"
121
+
122
+ # Last reorder entries
123
+ if [[ $diag_entries -gt 0 ]]; then
124
+ echo -e "║ ${CYAN}Last reorders (diag.log):${NC} ║"
125
+ tail -5 "$CACHE_DIR/diag.log" 2>/dev/null | while IFS= read -r line; do
126
+ # Truncate to fit in box
127
+ printf "║ %-56s ║\n" "${line:0:56}"
128
+ done
129
+ else
130
+ echo -e "║ ${YELLOW}No reorder events logged yet${NC} ║"
131
+ fi
132
+
133
+ echo -e "${BOLD}╠══════════════════════════════════════════════════════════════╣${NC}"
134
+
135
+ # Estimated savings
136
+ total_obs=0
137
+ for o in "${agent_obs[@]}"; do total_obs=$((total_obs + o)); done
138
+ if [[ $total_obs -gt 0 ]]; then
139
+ total_stable=0
140
+ total_blocks=0
141
+ for agent in "${!agent_stable_pct[@]}"; do
142
+ s=$(echo "${agent_stable_pct[$agent]}" | cut -d/ -f1)
143
+ t=$(echo "${agent_stable_pct[$agent]}" | cut -d/ -f2)
144
+ total_stable=$((total_stable + s))
145
+ total_blocks=$((total_blocks + t))
146
+ done
147
+ if [[ $total_blocks -gt 0 ]]; then
148
+ pct=$((total_stable * 100 / total_blocks))
149
+ printf "║ Estimated cache reuse: %-36s ║\n" "~${pct}% of system prompt"
150
+ fi
151
+ fi
152
+
153
+ echo -e "${BOLD}╚══════════════════════════════════════════════════════════════╝${NC}"
154
+ echo ""
155
+
156
+ # ── Quick stats ────────────────────────────────────────────────────
157
+
158
+ echo "Files:"
159
+ echo " $(ls "$CACHE_DIR"/stability-*.json 2>/dev/null | wc -l) stability DBs"
160
+ echo " ${diag_entries} diagnostic log entries"
161
+ echo ""
162
+
163
+ if [[ $diag_entries -eq 0 && ${#agent_obs[@]} -eq 0 ]]; then
164
+ echo "⚠️ Plugin hasn't fired yet. Troubleshooting:"
165
+ echo " 1. grep 'agent-cache-optimizer' ~/.config/opencode/opencode.json"
166
+ echo " 2. Restart OpenCode"
167
+ echo " 3. Check: the experimental.chat.system.transform hook"
168
+ echo " may not be available in your OpenCode version."
169
+ echo " Fallback: chat.params hook writes to diag.log on first call."
170
+ fi
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/env bash
2
+ # check-cache-friendly.sh — scan any CLI agent's config for KV-cache-busting patterns
3
+ # Usage: ./check-cache-friendly.sh [file] (default: CLAUDE.md)
4
+ # ./check-cache-friendly.sh --opencode (scan opencode config)
5
+ # ./check-cache-friendly.sh --all (scan all known configs)
6
+ set -euo pipefail
7
+
8
+ RED='\033[0;31m'; YELLOW='\033[1;33m'; GREEN='\033[0;32m'; NC='\033[0m'
9
+
10
+ warn() { echo -e "${YELLOW}⚠️ $1${NC}"; }
11
+ good() { echo -e "${GREEN}✅ $1${NC}"; }
12
+ bad() { echo -e "${RED}❌ $1${NC}"; }
13
+
14
+ check_file() {
15
+ local file="$1"
16
+ local label="${2:-$file}"
17
+ if [[ ! -f "$file" ]]; then
18
+ echo " (file not found: $file)"
19
+ return
20
+ fi
21
+
22
+ echo ""
23
+ echo "─── $label ───"
24
+ local issues=0
25
+
26
+ # 1. Date stamps in first 10 lines
27
+ if head -10 "$file" | grep -qP '\d{4}-\d{2}-\d{2}'; then
28
+ warn "Date stamp in first 10 lines → move to file end for cache stability"
29
+ ((issues++))
30
+ fi
31
+
32
+ # 2. Session IDs
33
+ if grep -qP 'ses_[a-z0-9]{8,}' "$file" 2>/dev/null; then
34
+ warn "Session ID patterns found → these change every session"
35
+ ((issues++))
36
+ fi
37
+
38
+ # 3. Dynamic file references (but NOT npm package names)
39
+ if grep -qP '@remember\b|\.remember/|memory-dream(?!@)' "$file" 2>/dev/null; then
40
+ warn "Dynamic file/memory references → content changes between sessions"
41
+ ((issues++))
42
+ fi
43
+
44
+ # 4. Very short first line (often a date or status) — text files only
45
+ # Skip JSON/YAML config files and markdown section headers (## / #)
46
+ local first_line
47
+ first_line=$(head -1 "$file")
48
+ if [[ ! "$file" =~ \.(json|yaml|yml|toml|lock)$ ]]; then
49
+ if [[ ${#first_line} -lt 30 && ! "$first_line" =~ ^#+\ ]]; then
50
+ warn "Very short first line (${#first_line} chars): '$first_line'"
51
+ ((issues++))
52
+ fi
53
+ fi
54
+
55
+ # 5. File volatility check
56
+ local size lines mtime
57
+ size=$(wc -c < "$file" | tr -d ' ')
58
+ lines=$(wc -l < "$file" | tr -d ' ')
59
+ mtime=$(stat -c %Y "$file" 2>/dev/null || echo 0)
60
+ local now
61
+ now=$(date +%s)
62
+ local age_hrs=$(( (now - mtime) / 3600 ))
63
+
64
+ echo " Size: ${size}B, Lines: ${lines}, Age: ${age_hrs}h"
65
+
66
+ if [[ $issues -eq 0 ]]; then
67
+ good "No cache-busting patterns detected"
68
+ fi
69
+ }
70
+
71
+ check_opencode() {
72
+ local config_dir="${HOME}/.config/opencode"
73
+ echo ""
74
+ echo "══════════ OpenCode Config ══════════"
75
+
76
+ # Main config
77
+ check_file "$config_dir/opencode.json" "opencode.json"
78
+
79
+ # Append files
80
+ for f in "$config_dir"/oh-my-opencode-slim/*_append.md; do
81
+ [[ -f "$f" ]] || continue
82
+ check_file "$f" "$(basename "$f")"
83
+ done
84
+
85
+ # .remember files
86
+ for f in "$config_dir"/.remember/*.md; do
87
+ [[ -f "$f" ]] || continue
88
+ local name; name=$(basename "$f")
89
+ local lines; lines=$(wc -l < "$f" | tr -d ' ')
90
+ local size; size=$(wc -c < "$f" | tr -d ' ')
91
+ echo " .remember/$name: ${lines} lines, ${size}B"
92
+ done
93
+ }
94
+
95
+ check_claude() {
96
+ echo "══════════ Claude Code Config ══════════"
97
+ check_file "${HOME}/.claude/CLAUDE.md" "CLAUDE.md (global)"
98
+ for f in "${HOME}"/.claude/rules/*.md; do
99
+ [[ -f "$f" ]] || continue
100
+ check_file "$f" "rules/$(basename "$f")"
101
+ done
102
+ check_file "$(pwd)/CLAUDE.md" "CLAUDE.md (project)" 2>/dev/null || true
103
+ check_file "$(pwd)/AGENTS.md" "AGENTS.md (project)" 2>/dev/null || true
104
+ }
105
+
106
+ # ── Main ───────────────────────────────────────────────────────────
107
+
108
+ case "${1:-}" in
109
+ --opencode) check_opencode ;;
110
+ --claude) check_claude ;;
111
+ --all)
112
+ check_opencode
113
+ echo ""
114
+ check_claude
115
+ ;;
116
+ *)
117
+ check_file "${1:-CLAUDE.md}" "${1:-CLAUDE.md}"
118
+ ;;
119
+ esac
120
+
121
+ echo ""
122
+ echo "Done."
@@ -0,0 +1,81 @@
1
+ ---
2
+ name: cache-status
3
+ description: Show KV cache optimizer status — stability DBs, reordering stats, diagnostics. Trigger: /cache-status
4
+ ---
5
+
6
+ # KV Cache Optimizer Status
7
+
8
+ Show the current state of the KV cache optimizer plugin (agent-cache-optimizer.ts).
9
+
10
+ ## Data Locations
11
+
12
+ - Stability DBs: `~/.cache/opencode/agent-cache-optimizer/stability-*.json`
13
+ - Diagnostic log: `~/.cache/opencode/agent-cache-optimizer/diag.log`
14
+
15
+ ## Display Format
16
+
17
+ Read all `stability-*.json` files and `diag.log`, then present:
18
+
19
+ ```
20
+ ╔══════════════════════════════════════════════════════════════╗
21
+ ║ KV Cache Optimizer Status ║
22
+ ╠══════════════════════════════════════════════════════════════╣
23
+ ║ Status: ACTIVE / NO DATA / ERROR ║
24
+ ║ Mode: COLD START (heuristics) / WARM (hash-based) ║
25
+ ║ Uptime: first_seen → last_seen ║
26
+ ╠══════════════════════════════════════════════════════════════╣
27
+ ║ Per-Agent Breakdown: ║
28
+ ║ orchestrator: 12 obs, 88% cacheable ║
29
+ ║ oracle: 5 obs, 75% cacheable ║
30
+ ║ fixer: 3 obs, 90% cacheable ║
31
+ ║ ... ║
32
+ ╠══════════════════════════════════════════════════════════════╣
33
+ ║ Last 5 reorders (from diag.log): ║
34
+ ║ [time] [agent] S:X U:Y D:Z T:N obs:M ║
35
+ ║ ... ║
36
+ ╠══════════════════════════════════════════════════════════════╣
37
+ ║ Estimated savings: XX KB / session ║
38
+ ╚══════════════════════════════════════════════════════════════╝
39
+ ```
40
+
41
+ ## Key Metrics
42
+
43
+ From each `stability-{agent}.json`:
44
+ - `observations`: number of calls tracked
45
+ - `positions`: number of distinct block positions
46
+ - Average `scores` per block → classification quality
47
+ - Ratio of stable vs dynamic blocks
48
+
49
+ From `diag.log`:
50
+ - Last N reorder operations
51
+ - Confirm plugin is being invoked
52
+
53
+ ## Interpretation
54
+
55
+ - **NO DATA**: Plugin hasn't been invoked yet. Restart OpenCode after adding the plugin to `opencode.json`.
56
+ - **COLD START**: First 2 sessions per agent — using position/size heuristics.
57
+ - **WARM**: 3+ sessions — using hash-based stability tracking. More accurate.
58
+ - **High stable %**: Good cache reuse across sessions.
59
+ - **High unknown %**: Heuristics can't classify some blocks → will improve with more observations.
60
+
61
+ ## If Plugin Not Working
62
+
63
+ 1. Check `opencode.json` has `"file:///home/chris/.config/opencode/plugins/agent-cache-optimizer.ts"` in `plugin` array
64
+ 2. Check `diag.log` exists and has entries
65
+ 3. If no diag.log: the `experimental.chat.system.transform` hook may not be firing → fall back to `chat.params` diagnostic
66
+ 4. Run `opencode debug agent orchestrator` to verify plugin config
67
+
68
+ ## Quick Health Check
69
+
70
+ ```bash
71
+ # Check if plugin is loaded
72
+ ls ~/.cache/opencode/agent-cache-optimizer/
73
+
74
+ # View last reorder
75
+ tail -5 ~/.cache/opencode/agent-cache-optimizer/diag.log
76
+
77
+ # Count observations per agent
78
+ for f in ~/.cache/opencode/agent-cache-optimizer/stability-*.json; do
79
+ echo "$(basename $f .json): $(python3 -c "import json; d=json.load(open('$f')); print(d['observations'])") obs"
80
+ done
81
+ ```