anomalyco / anomalyco/opencode

Compaction requests write an Anthropic prompt cache entry that is never read (1.28M cache-write tokens, 0 reads in 7 days)

Open
#48,542 1 comment 0 reactions 1 assignee View on GitHub

@MrMushrooooom is already working on this.

Since Sep 11, 2026.

Dominant language
TypeScript
Stars
209k
Forks
27.5k
PR merge metrics
PR metrics pending

Description

Summary

When compaction runs against an Anthropic model, every request writes a prompt cache entry that is never read back. Over 7 days on this machine, 70 successful compactions wrote 1,278,055 cache tokens and read exactly 0.

Cache writes are billed at a premium over ordinary input tokens, so this is a pure surcharge on every compaction with no possibility of payback. The compaction payload is unique by construction, because the window that selects the conversation slice slides forward each time, so no prefix is ever shared between two compaction requests.

Measurement

Read from ~/.local/share/opencode/opencode.db, 7-day window, provider anthropic, model claude-haiku-4-5 configured as agent.compaction.model.

Class Count tokens.total cache.write cache.read
summary produced 70 present 1,278,055 total 0 in all 70
summary empty 15 key absent 0 0

Representative rows from the successful group, showing that essentially the whole prompt goes to a cache write while billed input stays at 3 tokens:

3780 chars  {"total": 8995,  "input": 3, "output": 1486, "cache": {"write": 7506,  "read": 0}}
7675 chars  {"total": 14596, "input": 3, "output": 3049, "cache": {"write": 11544, "read": 0}}
7637 chars  {"total": 19457, "input": 3, "output": 2985, "cache": {"write": 16469, "read": 0}}
7586 chars  {"total": 19631, "input": 3, "output": 2971, "cache": {"write": 16657, "read": 0}}
8894 chars  {"total": 27165, "input": 3, "output": 3533, "cache": {"write": 23629, "read": 0}}

cache.read is 0 in every one of the 70, not merely low on average.

Reproduction

Configure an Anthropic model as the compaction model, run sessions long enough to compact several times, then query the database:

import sqlite3, json, time
from pathlib import Path

db = Path.home() / ".local/share/opencode/opencode.db"
c = sqlite3.connect(f"file:{db}?mode=ro", uri=True)
since = int((time.time() - 7 * 86400) * 1000)

reads = writes = 0
for mid, _, data in c.execute(
    "select id,time_created,data from message where time_created>?", (since,)
):
    r = json.loads(data)
    if not (r.get("summary") is True and r.get("mode") == "compaction"):
        continue
    if r.get("providerID") != "anthropic":
        continue
    cache = (r.get("tokens") or {}).get("cache") or {}
    reads += cache.get("read", 0)
    writes += cache.get("write", 0)

print("cache read:", reads, " cache write:", writes)

Expected

Compaction requests should not request prompt caching, or the cache scope for them should be configurable. The summarization system prompt is small and stable and could reasonably stay cached; the conversation payload is what carries the volume and it is never reused.

Notes on cause

I did not trace this in source, so treat this part as a hypothesis rather than a finding. Reading the shipped bundle, the Anthropic request builder appears to attach cache_control: {type: "ephemeral"} to the system prompt and to the last two messages without a condition that would exclude compaction requests. The measurement above stands on its own regardless of where the marker is attached.

I also could not find any user-facing setting that would turn this off. The top-level config keys in the shipped build are logLevel, server, command, reference, snapshot, plugin, autoshare, disabled_providers, enabled_providers, small_model, mode, agent, provider, permission, tools, attachment and layout, none of which covers caching. There is an internal structure shaped like {tools, system, messages, ttlSeconds} that looks purpose-built for this, but I found nothing wiring it to user configuration.

Environment

  • opencode-ai 1.18.30, Linux x64
  • Compaction model: anthropic/claude-haiku-4-5, set via agent.compaction.model

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.