{"slug": "cache-break-notifier-plugin-for-github-copilot-cli", "title": "Cache Break Notifier plugin for GitHub Copilot CLI", "summary": "A developer created a Cache Break Notifier plugin for GitHub Copilot CLI that warns users when the AI assistant's context cache drops significantly between turns, helping to identify costly cache misses. The plugin monitors cache read tokens per model and logs warnings when a drop of 4096 or more tokens is detected.", "body_md": "|\nimport { joinSession } from \"@github/copilot-sdk/extension\"; |\n|\n|\n|\nconst DROP_TOKENS = 4096; // the only tunable: how big a cache-read drop is worth a warning |\n|\n|\n|\n/** @type {Map<string, number>} last turn-entry cacheRead per model */ |\n|\nconst perModel = new Map(); |\n|\n|\n|\n// Only the FIRST eligible main-agent call of each turn is evaluated. |\n|\nlet acceptingTurnEntry = false; |\n|\n|\n|\nconst session = await joinSession({}); |\n|\nawait session.log(\"cache-drop-notifier active\", { ephemeral: true }); |\n|\n|\n|\nsession.on(\"assistant.turn_start\", () => { |\n|\nacceptingTurnEntry = true; |\n|\n}); |\n|\nsession.on(\"assistant.turn_end\", () => { |\n|\nacceptingTurnEntry = false; |\n|\n}); |\n|\n|\n|\nsession.on(\"assistant.usage\", (event) => { |\n|\nconst d = event.data ?? {}; |\n|\n|\n|\n// Main context only: skip separate-context calls entirely (don't consume the slot). |\n|\nconst initiator = d.initiator; |\n|\nconst isMain = |\n|\ninitiator !== \"sub-agent\" && |\n|\ninitiator !== \"mcp-sampling\" && |\n|\nd.parentToolCallId == null; |\n|\nif (!acceptingTurnEntry || !isMain) return; |\n|\n|\n|\n// This is the turn-entry sample — consume the slot even if the metrics are unusable, |\n|\n// so a later continuation call can't masquerade as the entry. |\n|\nacceptingTurnEntry = false; |\n|\n|\n|\n// Unknown != zero: a missing counter means \"no data,\" so we skip this turn's line. |\n|\n// Only cacheReadTokens matters now (inputTokens is no longer part of detection), so a |\n|\n// missing inputTokens must not be allowed to suppress a real drop. |\n|\nconst cached = d.cacheReadTokens; |\n|\nif (!Number.isFinite(cached) || cached < 0) return; |\n|\n|\n|\nconst model = d.model ?? \"unknown\"; |\n|\nconst prev = perModel.get(model); |\n|\n|\n|\n// A \"drop\" (not a \"miss\"): reused tokens fell by a large absolute amount versus the last |\n|\n// turn on this model. An intentional context shrink looks identical in the counts. |\n|\nconst isDrop = prev !== undefined && prev - cached >= DROP_TOKENS; |\n|\n|\n|\nif (isDrop) { |\n|\nsession.log( |\n|\n`Cache break detected on ${model}.`, |\n|\n{ level: \"warning\" } |\n|\n).catch(() => {}); |\n|\n} else if (cached === 0) { |\n|\nsession.log( |\n|\n`No cache tokens reused on ${model}.`, |\n|\n{ level: \"warning\" } |\n|\n).catch(() => {}); |\n|\n} |\n|\n|\n|\n// Always update — this is why a drop is reported once and the baseline self-heals. |\n|\nperModel.set(model, cached); |\n|\n}); |", "url": "https://wpnews.pro/news/cache-break-notifier-plugin-for-github-copilot-cli", "canonical_source": "https://gist.github.com/burkeholland/647ad0e579c06a43346ce6a373261eba", "published_at": "2026-07-20 16:10:18+00:00", "updated_at": "2026-07-21 09:29:23.380833+00:00", "lang": "en", "topics": ["developer-tools", "large-language-models", "ai-tools"], "entities": ["GitHub Copilot", "Cache Break Notifier"], "alternates": {"html": "https://wpnews.pro/news/cache-break-notifier-plugin-for-github-copilot-cli", "markdown": "https://wpnews.pro/news/cache-break-notifier-plugin-for-github-copilot-cli.md", "text": "https://wpnews.pro/news/cache-break-notifier-plugin-for-github-copilot-cli.txt", "jsonld": "https://wpnews.pro/news/cache-break-notifier-plugin-for-github-copilot-cli.jsonld"}}