Ember
OpenCode prompt-cache cost guard: estimates cold cache-rewrite costs, warns or blocks before expensive sends, and reports recorded costs across sessions.
1
1,539
166 in 7 days
42.0
Multi-signal model
2 days ago
2026-10-18
Install and configure
opencode.jsonWrites to this project's opencode.json — applies to this repository only.
opencode.json
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["opencode-ember@0.4.3"]
}Writes to ~/.config/opencode/opencode.json — applies to every project.
~/.config/opencode/opencode.json
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["opencode-ember@0.4.3"]
}If you want to modify the plugin locally, install it into the project and reference the local path.
shell
pnpm add -D opencode-emberOpenCode loads npm dependencies through its embedded runtime on startup and caches them locally — no manual global install needed.
Estimate the cost of a cold prompt-cache rewrite, warn or block before the send, and report recorded cold writes across sessions.
A port of cache-tax
to opencode. One self-contained plugin file, no runtime
dependencies.
Looking for cache warming? OpenCode V2 supports session warming out of the box, without a plugin. Check its built-in
warmingsetting first. It is disabled by default, and warming requests can incur provider costs.
Why "ember": a banked fire you keep smoldering while you are away.
What it does
- Keepwarm does not currently warm caches.
/keepwarmarms a timer, but its callback stops with"no supported captured model request is available". It never sends a background provider request or mutates sessions or provider config. /ember guard warn(default) shows the price and sends anyway. When the TTL has lapsed and the context is large, a graceful warning is shown while the message sends./ember guard refusehard blocks cold sends and shows an informative error in the turn, preventing accidental rewrites until you switch towarnor/clear./emberkeeps score. Warm or cold, context size, cold-rewrite price, the break-even (how many pings cost one cold write, and the idle that covers), keepwarm state, guard mode, and this session's cold writes.ember gainreports history. After a cold-send warning, a completed step with cache-write tokens records a cold write in a UTC daily bucket. The report includes cold-write counts and estimated costs, plus any stored heartbeat history. Totals cover all retained buckets; the per-day table shows only the most recent days.ember discoveris an alias;ember gain --jsondumps the raw buckets.
Requirements
- opencode with the server-plugin API
(
@opencode-ai/plugin). package.json records the supported opencode range (engines) and the plugin version used for development. OpenCode V2 compatibility is not verified. - A provider with prefix caching and reported token usage. Built-in estimates cover selected Anthropic, OpenAI, and Gemini model families.
- Bun on
PATHto run the reporting CLI.
Install
From this repo (recommended)
git clone https://github.com/ozandogrultan/opencode-ember.git
cd opencode-ember
./install.sh
Then restart opencode — plugins are loaded at startup only.
install.sh also links the report binary as ~/.local/bin/ember (set
EMBER_BIN_DIR to choose another directory), so ember gain works from any
shell if that directory is on PATH. The link points to this checkout's
gain.ts, so keep the checkout in place. An alternative is
npm install -g opencode-ember for the CLI; this does not load the plugin into opencode.
/ember gain inside opencode tells you to use the binary instead.
install.sh asks opencode for its own config directory (opencode debug paths)
and copies ember.ts into <config>/plugins/, overwriting any existing file.
Manually
Copy ember.ts to one of:
~/.config/opencode/plugins/ember.ts— all projects (global)<project>/.opencode/plugins/ember.ts— one project
From npm
{ "plugin": ["opencode-ember"] }
in opencode.json, then restart opencode. Install the CLI
separately with npm install -g opencode-ember if you want ember gain.
Usage
/keepwarm [6h|90m] [every 2m] [ttl 1h] | always | status | off
/ember [guard warn|refuse]
ember gain | discover [--json] # the report, from any shell
| command | effect |
|---|---|
/keepwarm |
arm a timer window (30 minutes by default); retain always mode if enabled |
/keepwarm 90m |
a window of your own (2h30m, 6h, …) |
/keepwarm always |
persist automatic arming for this and future normal sessions |
/keepwarm 6h every 2m |
override the ping period (floor 1m) |
/keepwarm 6h ttl 1h |
assume the 1-hour cache tier |
/keepwarm status |
the status line |
/keepwarm off |
stop this session's timer and disable automatic arming globally |
/ember |
the card |
/ember guard warn |
show the price and send (default) |
/ember guard refuse |
hard block cold sends |
ember gain |
terminal report over collected history (alias ember discover) |
Automatic arming is disabled by default. /keepwarm and /keepwarm always
arm timers explicitly; a recorded cold write can also arm a default-length window.
None of these paths sends warming requests. /keepwarm off does not cancel
timers already armed in other sessions.
Slash commands show their result in a toast and abort before any model call.
The command result may also appear as an error in the turn. guard block is an
alias for guard refuse.
Guard mode is persisted globally and shared across sessions and processes.
Cache TTL and cadence
The default assumed TTL is 5 minutes. The guard compares it with the time of the latest completed model step; it does not query the provider's cache. Default timer cadence is 80% of the TTL, subject to the configured ping floor.
/keepwarm 6h ttl 1h changes this session's assumed TTL;
EMBER_TTL_SECONDS=3600 changes the default. Neither selects a provider cache
tier or changes the price table. The card's break-even calculation is theoretical:
background warming is unavailable.
Safe warming status
The plugin never forks, prompts, deletes, or appends to a session for background
warming, and never mutates config.provider or provider options.fetch.
Background warming fails closed with "no supported captured model request is available"
and never issues background provider requests or forked sessions.
Important safety guarantees:
- Zero background provider requests: Background warming fails closed immediately; it never sends provider calls, consumes model quota, or triggers provider authentication loops.
- No session mutation: Real session history and parent/child topologies are never altered.
- Usage-based accounting: Recorded cold-write costs use reported cache-write tokens and built-in rate estimates; no synthetic heartbeats or tokens are recorded for failed or unsupported background requests.
- In-memory state hygiene: No credentials, request headers, or prompts are written to disk.
Configuration
| env var | default | meaning |
|---|---|---|
EMBER_TTL_SECONDS |
300 |
assumed cache tier |
EMBER_MIN_CONTEXT |
50000 |
cold-guard context floor, tokens |
EMBER_MIN_PING_SECONDS |
60 |
ping floor |
EMBER_STATE_FILE |
~/.local/share/opencode/ember.json |
state path shared by plugin and CLI |
Prices and the reporting caveats (read this too)
Dollar figures come from a small built-in table in ember.ts (PRICES: cache
read, cache write and output rates per model family). Two consequences:
- Every figure is an estimate. Prices are hard-coded list rates for the
Anthropic/OpenAI/Gemini families the plugin knows; they do not follow your provider,
plan, negotiated rates, or prompt-fee changes. The card estimates a cold rewrite
using the whole context and the write rate, and a warm turn using the read rate.
Recorded cold-write costs use reported cache-write tokens and the write rate;
normal input and output costs are not added to the report. Treat every dollar
value in
/emberandember gainas an approximation, not an invoice. The report's net savings is kept-warm value minus ping spend and recorded cold-write cost; without stored warming history it can only be zero or negative. - Unpriced models read $0.00. Models without a matching row — e.g. custom
OpenAI-compatible ids like
custom-unpriced-modelor provider relays — report no cost, so the kept-warm value, ping spend, cold-write cost, net savings and the warming yield meter all run understated (or read $0.00) for them. Raw counters — heartbeat counts, tokens kept warm, idle time held warm, cold-write counts — are independent of pricing. Add a row toPRICESinember.tsto include a model in the estimates.
State lives in a versioned file, ~/.local/share/opencode/ember.json by default.
Multiple opencode processes (one per workspace/cmux session) share the
file: every write re-reads it and merges, so a process writing its own session's
window preserves sessions armed elsewhere. Reading older or unversioned state
preserves guard settings and recorded gain history, but disables automatic arming
and discards legacy session windows; current-version settings and windows are retained.
Daily history is trimmed to a bounded number of stored date buckets on writes,
not a number of calendar days. Delete the file to reset settings and history.
Testing
Free command checks (no model call; shown with default settings):
opencode debug config | grep -A2 -E '"keepwarm"|"ember"' # loaded?
/keepwarm status # "keepwarm off"
/keepwarm 6h # arms
/ember # card
/ember guard warn # switch guard
ember gain # the report (CLI)
/keepwarm off
Cold guard — launch with a 5-second pseudo-TTL and a 1-token floor:
EMBER_TTL_SECONDS=5 EMBER_MIN_CONTEXT=1 opencode
Send a normal message and wait for it to finish. Run /ember guard refuse,
wait at least 6 seconds, and send another message: the guard should block it
before a provider call. /ember guard warn allows the next send with a warning.
Normal sends cost whatever your provider charges.
Check fail-closed keepwarm without a model call:
/keepwarm 1h every 1m
In a fresh session with no prior turn, wait a little over a minute, then run
/keepwarm status. Expect no supported captured model request is available.
The callback sends no provider request, records no heartbeat, and shows no stop
toast. Run /keepwarm off afterward.
Repository checks are listed in CONTRIBUTING.md.
How it works
config (registers /keepwarm and /ember commands), chat.message + chat.params
(cold-send guard), event (token/price tracking from step-finish, session cleanup),
command.execute.before (handles /keepwarm and /ember, aborted before any model call),
and dispose (clears timers).
Credits
Ported from karanb192/claude-code-mods → cache-tax.
The design, the price arithmetic and the safety rules are theirs; this is the
opencode translation.
Contributing
See CONTRIBUTING.md for the check suite and commit conventions, AGENTS.md for the design rules, and CHANGELOG.md for what changed.
License
MIT © Ozan Dogrultan
Similar plugins
Context Usage
opencode-context-usage
OpenCode TUI plugin: a /context command with the exact token breakdown behind the built-in Context panel (last request) plus session-wide usage, subagent totals, cache hit rate and cost.
Oc Usage
@taraksh011/oc-usage
OpenCode plugin — live token usage, cost, and TPS display
Cache Hit
opencode-cache-hit
OpenCode TUI sidebar: prompt cache hit rate, tokens & cost with sub-agent rollup. Works with opencode-visual-cache; optional per-call JSONL timeline.