Home/Features/Cache Clock
Copilot extension · CLI and VS Code

Cache Clock

See your Copilot prompt cache expire, and get asked before a costly cold send.

Install → Visual explainer View source ↗
25
× write vs read, Claude Sonnet 5.5
300
s cache TTL on Claude
0
AI credits when you don't send
104
offline tests

01 Overview

Cache Clock is an extension for GitHub Copilot CLI and VS Code's Copilot chat, modelled on cache-tax for Claude Code. Copilot bills tokens in AI credits, and on Claude and newer GPT models a cache write costs far more than a cache read. Step away longer than the cache lives and your next message, even "give me a recap", rewrites the whole conversation at the write price. Cache Clock shows that clock and that price, and asks before a costly cold send goes out.

Download the MP4 ↓ (17 s, silent). Made with make-motion from this spec.

Companion pages go deeper: the visual explainer walks through the clock, the guard and the report step by step; the diagrams map the architecture, every install case, every prompt, the clock and heads-up, report and VS Code.

02 Problem & why it's needed

The friction

Prompt caches expire silently. Through Copilot, a Claude Sonnet 5.5 cache lives 5 minutes and a write costs 25× a read; GPT-6 Luna lives 30 minutes at 12.5×. Copilot shows neither the clock nor the price, so a coffee break turns the next "carry on" into a full rewrite of the context at the write price.

Why it mattersOn a real 37.2k-token Claude session the next send would have read the context for $0.003 warm. Sent 20 seconds after the cache expired, it read 0 cached tokens and wrote 44,388: about $0.08 extra. That one sits under the default $0.10 threshold, so it passes with defaults; longer sessions cost more, and /cache-clock min lowers the bar. Above it, Cache Clock warns before expiry, prices the cold send and asks before sending; not sending costs 0 AI credits.

03 Install

One line, once per machine. No clone and no Node needed: the extension runs inside Copilot's own runtime.

Install

curl -fsSL https://raw.githubusercontent.com/abhijitbansal/claude-skills/main/tools/cache-clock/install.sh | sh

Downloads a copy into ~/.copilot/extensions/cache-clock. A failed download leaves nothing behind. COPILOT_HOME moves ~/.copilot.

Copilot CLI

Start a new copilot session. Copilot asks once whether the extension may "register hooks": allow it (option 2 remembers it for that repo). Then:

/cache-clock help

VS Code Copilot chat

VS Code loads the extension from the project, not from ~/.copilot/extensions. From each repo you use it in, install a copy into .github/extensions/cache-clock (commit it to share it, or add it to .git/info/exclude to keep it local):

curl -fsSL https://raw.githubusercontent.com/abhijitbansal/claude-skills/main/tools/cache-clock/install.sh | sh -s -- --project

Then add this to your VS Code settings.json, run "Developer: Reload Window", start a new chat and type /cache-clock in full (VS Code does not list it in the / menu).

"chat.canvases.enabled": true

From a clone, or to remove

sh claude-skills/tools/cache-clock/install.sh
sh claude-skills/tools/cache-clock/install.sh --uninstall

From a clone the installer links the extension, so a git pull updates it; --copy copies instead; --project [DIR] copies into DIR's git root, and --uninstall --project removes that copy. It replaces only its own install (one with cache-clock's own files) or a dangling link, and refuses any other folder.

If /cache-clock does nothing after installCopilot CLI may still need experimental mode before it loads the extension. Run /experimental on, reload Copilot CLI, and accept the permission prompts when it asks.
/experimental on

04 Usability guide

One slash command. Settings live in ~/.copilot/cache-clock/config.json and apply to every session.

A costly cold send: the question

When the cache has expired and the rewrite costs at least the threshold more than a warm send, Cache Clock asks in the chat before anything goes out: Send anyway or Don't send. Don't send stops it before any model call, so it costs 0 AI credits; dismissing the question or leaving it for 5 minutes does the same. It asks once per cold cache; after that, sends go through with a price note. Checked live on 12 October 2026 in Copilot CLI and VS Code.

cache-clock: the Claude Sonnet 5.5 prompt cache went cold 2s ago. Sending this rewrites about 37.2k tokens at $2.25/M = $0.08 (a warm send would read them for $0.003). [ Send anyway ] [ Don't send ]

Where it cannot ask: refuse once

In copilot -p, which has no question UI, the send is refused once with the price, and sending it again pays. A real capture, taken with /cache-clock min 0.01:

! cache-clock: the Claude Sonnet 5.5 prompt cache went cold 2s ago. Sending this rewrites about 37.2k tokens at $2.25/M = $0.08 (a warm send would read them for $0.003). Send it again to pay it, or /clear and start fresh.

/cache-clock — the card

The last model used, its cache state and time left, the context size, what the next send costs warm and cold, and the session's tally. A real card from a resumed session:

cache-clock · Claude Sonnet 5.5 cache warm, 1m17s left (ttl 5m00s) context 37.2k tokens next send warm $0.003 · cold $0.08 (25× per token) session 5 model calls · 0 cold after idle · 0 prefix breaks · $0 extra paid guard ask before a cold send above $0.10 · heads-up 5m before expiry

/cache-clock help

How it works, what a cold send triggers, every command, the report and the VS Code setup, in the chat.

/cache-clock help

/cache-clock guard refuse | warn | off

refuse (default) asks before a costly cold send, or refuses it once where it cannot ask. warn sends it and prints the price. off disables the guard; the heads-up keeps its own switch.

/cache-clock guard warn
/cache-clock guard refuse

/cache-clock min <usd>

Guard only cold sends whose extra cost is at least this many dollars (default $0.10).

/cache-clock min 0.25

/cache-clock headsup <min> | off

One ephemeral line before expiry, when the cold send's extra cost is at least the threshold. The lead is the smaller of this setting and a fifth of the TTL, so a 5-minute Claude cache warns 1 minute ahead.

/cache-clock headsup 5
/cache-clock headsup off

05 How it works under the hood

The clock

Every main-agent model call emits assistant.usage with cacheReadTokens, cacheWriteTokens, cacheExpiresAt, cacheTtlSeconds and prices in nano-AIU per batch of tokens. Cache Clock keeps the last call per model. Nothing is guessed: the TTL, the expiry and the prices all come from Copilot. Sub-agent and background calls are ignored.

The guard

onUserPromptSubmitted checks the last model's clock. If the cache has expired and rewriting it would cost at least the threshold more than reading it, the hook asks with session.ui.select and waits for the answer. To stop the send it calls session.abort() without awaiting it (awaiting inside the hook deadlocks). The cold episode is remembered per session in ~/.copilot/cache-clock/refused.json for a week, even across processes.

What is never guarded

Slash commands and ! shell escapes. A message queued while a turn is running only gets the note, because stopping it then would cancel the running turn. The first send after a compaction, since the context was rewritten anyway.

Resume

assistant.usage is not replayed on copilot --resume, and getEvents() omits usage records, so the extension seeds the clock from the session's own ~/.copilot/session-state/<id>/events.jsonl.

Architecture

Seven small modules: wiring attaches to the session, clock keeps per-model state, guard decides, usage normalises events and prices, format writes every message, config stores settings and refusals, history feeds the report. Open the architecture diagram →

06 VS Code

Measured on 11–12 October 2026 with VS Code's Copilot agent host (bundled runtime 1.0.92-4.unstable).

Turn it onVS Code loads Copilot extensions only when "chat.canvases.enabled": true (experimental).
InstallPer project: VS Code loaded a copy in the repo's .github/extensions/cache-clock, not the ~/.copilot/extensions install. Use install.sh --project in each repo, and rerun it to update the copy.
The command/cache-clock works typed in full; VS Code's / menu does not list it.
OutputVS Code shows plain extension messages only as error boxes, so the card, help and settings appear as a card with an OK button. The cold-send question works as in the CLI.
AutopilotVS Code's autopilot mode cancels extension questions, so a costly cold send is held with a note instead of asking (read from VS Code's code, not tested live).
Heads-upThe heads-up and the "sending cold" note reach only VS Code's log.
ReportVS Code sessions persist only session.usage_checkpoint totals, so the report counts them as totals. VS Code's Cache Explorer (Agent Debug Logs) shows per-turn hit rates.

07 What Copilot reported, measured

Measured on Copilot CLI 1.0.95, 11 October 2026. Each boundary result is a single run.

Claude Sonnet 5.5TTL 300 s, write ÷ read 25×. Sent 20 s after cacheExpiresAt: read 0, wrote 44,388. The reported expiry held to the second.
GPT-6 LunaTTL 1800 s, 12.5×. Resumed after 29 min (1 min before the reported expiry): already cold, read 0, wrote 20,771. After 31 min: refused at 0 credits; the resend from a new process went through.
PricesThe log already includes discounts: through auto, Sonnet's write was $2.25/M (list $2.50), auto's 10% off. 1 AI credit = $0.01.
Shared prefixSeparate sessions share the cached system-prompt prefix: a brand-new session's first call read 10,157 tokens from cache.

08 Report: what cold sends already cost you

The extension only sees sessions it was loaded in. The report reads every session Copilot has persisted, CLI and VS Code alike, and sends nothing anywhere. It ships with every install and needs Node 18+.

node ~/.copilot/extensions/cache-clock/report.mjs
node ~/.copilot/extensions/cache-clock/report.mjs --since 7d
node ~/.copilot/extensions/cache-clock/report.mjs --json
model calls cold after idle prefix breaks rewritten extra paid total spent idle before cold sends Claude Sonnet 5.5 5 1 0 37.1k $0.08 $0.16 5m GPT-6 Luna 201 1 2 44.9k $0.005 $0.14 31m extra paid for cold rewrites: $0.08 of $0.29 spent · about $0.46 per week at this rate

"Extra paid" is what each cold call cost above a warm read of the same tokens. "Prefix breaks" are cold calls inside the reported TTL: something changed the prompt prefix, or the provider served it cold early.

09 Limits

No keep-warm yet

cache-tax pings to hold the cache during a break. An extension can only ping with session.send, which adds a user and an assistant turn to your conversation each time. Run the report first: whether pings pay off depends on your own tax.

Auto routes per prompt

With auto, the next prompt may go to a different model than the last. The guard prices the last model used.

The estimate is approximate

It is the last call's input plus its answer. The real resend adds your new message and anything Copilot attaches (44.4k actual vs 37.2k estimated in the capture above).

Early eviction is invisible to the guard

It trusts Copilot's cacheExpiresAt. When a provider serves a cache cold sooner (Luna at 29 of 30 minutes, once), the send is billed cold and the guard stays quiet; the report counts it as a prefix break.

VS Code relies on an experimental setting

chat.canvases.enabled may be renamed or removed in a later VS Code release.

-p mode

The guard refuses once in copilot -p, but slash commands there go to the model as text, so /cache-clock needs the interactive CLI.

10 What's inside

Copilot extension

/cache-clockCard, plus help, guard, min and headsup subcommands
onUserPromptSubmittedThe cold-send guard: ask (or refuse once), warn, or off
report.mjsPer-model cold-send report over every persisted session: --since, --dir, --json
lib/wiring, clock, guard, usage, format, config, history

Install and tests

install.shPiped: downloads a copy. From a clone: link (default), --copy or --uninstall. Replaces only its own install or a dangling link.
test/104 node tests through a fake Copilot session and a fake clock; no network. CI runs them on every PR.

Explore the other plugins

Browse the full skills & tools catalog →