|
import { joinSession } from "@github/copilot-sdk/extension"; |
|
|
|
const DROP_TOKENS = 4096; // the only tunable: how big a cache-read drop is worth a warning |
|
|
|
/** @type {Map<string, number>} last turn-entry cacheRead per model */ |
|
const perModel = new Map(); |
|
|
|
// Only the FIRST eligible main-agent call of each turn is evaluated. |
|
let acceptingTurnEntry = false; |
|
|
|
const session = await joinSession({}); |
|
await session.log("cache-drop-notifier active", { ephemeral: true }); |
|
|
|
session.on("assistant.turn_start", () => { |
|
acceptingTurnEntry = true; |
|
}); |
|
session.on("assistant.turn_end", () => { |
|
acceptingTurnEntry = false; |
|
}); |
|
|
|
session.on("assistant.usage", (event) => { |
|
const d = event.data ?? {}; |
|
|
|
// Main context only: skip separate-context calls entirely (don't consume the slot). |
|
const initiator = d.initiator; |
|
const isMain = |
|
initiator !== "sub-agent" && |
|
initiator !== "mcp-sampling" && |
|
d.parentToolCallId == null; |
|
if (!acceptingTurnEntry || !isMain) return; |
|
|
|
// This is the turn-entry sample — consume the slot even if the metrics are unusable, |
|
// so a later continuation call can't masquerade as the entry. |
|
acceptingTurnEntry = false; |
|
|
|
// Unknown != zero: a missing counter means "no data," so we skip this turn's line. |
|
// Only cacheReadTokens matters now (inputTokens is no longer part of detection), so a |
|
// missing inputTokens must not be allowed to suppress a real drop. |
|
const cached = d.cacheReadTokens; |
|
if (!Number.isFinite(cached) || cached < 0) return; |
|
|
|
const model = d.model ?? "unknown"; |
|
const prev = perModel.get(model); |
|
|
|
// A "drop" (not a "miss"): reused tokens fell by a large absolute amount versus the last |
|
// turn on this model. An intentional context shrink looks identical in the counts. |
|
const isDrop = prev !== undefined && prev - cached >= DROP_TOKENS; |
|
|
|
if (isDrop) { |
|
session.log( |
|
Cache break detected on ${model}., |
|
{ level: "warning" } |
|
).catch(() => {}); |
|
} else if (cached === 0) { |
|
session.log( |
|
No cache tokens reused on ${model}., |
|
{ level: "warning" } |
|
).catch(() => {}); |
|
} |
|
|
|
// Always update — this is why a drop is reported once and the baseline self-heals. |
|
perModel.set(model, cached); |
|
}); |
Integrating Claude Code into Your Full-Stack Development Workflow