Skip to content

feat: cache-stable memory snapshot as first message of history - #26

Open
caos30 wants to merge 1 commit into
joshuadavidthomas:mainfrom
caos30:cache-stable-snapshot
Open

feat: cache-stable memory snapshot as first message of history#26
caos30 wants to merge 1 commit into
joshuadavidthomas:mainfrom
caos30:cache-stable-snapshot

Conversation

@caos30

@caos30 caos30 commented Aug 19, 2026

Copy link
Copy Markdown

Problem

The system prompt sits before the message history in the request payload, so injecting memory blocks into the system prompt breaks the provider prompt cache in two ways:

This plugin encourages frequent memory updates, which makes the problem worse the more it is used as intended.

Approach

Inject the memory blocks as a synthetic first message of the conversation (experimental.chat.messages.transform) instead of the system prompt, and freeze them per session:

  • Once rendered, the exact same bytes are re-injected on every request. The system prompt and history stay a stable cache prefix for the whole session, even while memory is being edited.
  • In-session edits remain visible to the model as natural amendments in the history — a coherent timeline: the model still sees what a block said when the session started, and then sees it being updated. (Re-injecting the latest state every turn, as the current system-prompt approach effectively does, rewrites the past the model already narrated.)
  • A versioned HTML-comment marker (<!-- opencode-agent-memory:snapshot:1 -->) lets the plugin recognise its own injection and never clobber messages injected by other plugins.
  • Snapshots persist to ~/.local/share/opencode/agent-memory/snapshots/<sessionID>.xml (atomic writes, 30-day purge) so they survive opencode/VM restarts byte-identically — a restart does not bust the cache either. In-memory Map is only an L1 cache.
  • session.compacted invalidates the snapshot so post-compaction requests render a fresh one.
  • The optional journal note stays in the system prompt (static text, cache-safe).

Validation

Real 8-turn session (4 user / 4 agent, 80 API requests, ~10.9M tokens) against a DeepSeek usage panel:

Cache hit Cache miss Hit/input
9,476,480 126,870 98.2%

The cache prefix never broke across: memory block edits mid-session, AGENTS.md edits and project skill edits. Before this change the same workload measured ~10% cache hit.

Unit tests cover freeze/re-use, simulated restart (new store instance, same disk → no re-render), invalidation and purge. bun test: 32 pass. tsc --noEmit: clean.

Refs #8, #24. This is a larger but more complete alternative to #25 (which fixes injection placement only); if this lands, #25 can be closed.

Replace the per-request system-prompt injection with a frozen snapshot
injected as a synthetic first message of the conversation.

Why: the system prompt sits BEFORE the message history in the request
payload, so any memory edit (or the ms-precision timestamps in the
injected metadata, joshuadavidthomas#24) invalidated the provider prompt cache for the
entire session — including the multi-million-token histories where cache
matters most. Users measured ~10% cache hit where ~90% was expected.

Design:
- experimental.chat.messages.transform unshifts a synthetic user message
  containing the rendered memory blocks, prefixed with a versioned HTML
  comment marker so the plugin can recognise (and never clobber) its own
  injection among messages injected by other plugins
- the snapshot is frozen per session: once rendered, the exact same bytes
  are re-injected on every request, keeping the system prompt + history a
  stable cache prefix. In-session memory edits remain visible to the model
  as natural amendments in the conversation history (coherent timeline:
  the model still sees what a block said when the session started)
- snapshots persist to ~/.local/share/opencode/agent-memory/snapshots/
  (atomic writes) so they survive opencode/VM restarts byte-identically;
  in-memory Map is only an L1 cache
- session.compacted invalidates the snapshot so post-compaction requests
  get a fresh one
- journal system note stays in the system prompt (static text, cache-safe)

Validation: 8-turn real session against a DeepSeek provider panel —
90.6% cache hit on the first turn, 98.2% overall, prefix never broke
across memory block edits, AGENTS.md edits and project skill edits.

Refs joshuadavidthomas#8, joshuadavidthomas#24. Alternative to joshuadavidthomas#25 (which fixes the injection placement
only); if this lands, joshuadavidthomas#25 can be closed.
@assert-app

assert-app Bot commented Aug 19, 2026

Copy link
Copy Markdown

Review on Assert →

2 clusters identified

@caos30

caos30 commented Aug 19, 2026

Copy link
Copy Markdown
Author
Screenshot_20260819_163927

My DeepSeek test today: $0.13 USD for almost 11 million tokens! There are two bars because it took me about half an hour to implement a fix in the app I’m working on. As you can see, each bar represents roughly 5 million tokens. Total cache hit: 98,2% !! wowww 🤩

Most of the cache misses are in the first bar, since it includes loading the system context, skills, tools, and memory blocks. 😊

Honestly, I can now experiment with models more intensively without needing a fixed subscription plan with token quotas.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant