/** * usage-status — footer block showing live token + $ usage for the session. * * Accumulates `usage` totals from every assistant `turn_end` event and * renders them into a `setStatus("usage", ...)` entry. Pairs with * `status-line.ts` (turn indicator) and `model-status.ts` (model badge) — * three independent footer slots, three independent extensions. * * Format: * $0.043 12.3k tok (1.1k in / 240 out) * * Counts session-cumulative cost; resets on every new session_start. */ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; const KEY = "usage"; function fmtTokens(n: number): string { if (n < 1000) return String(n); if (n < 10_000) return `${(n / 1000).toFixed(1)}k`; return `${Math.round(n / 1000)}k`; } function fmtCost(c: number): string { if (c >= 1) return `$${c.toFixed(2)}`; return `$${c.toFixed(4)}`; } export default function usageStatusExtension(pi: ExtensionAPI) { let costTotal = 0; let inputTotal = 0; let outputTotal = 0; let cacheReadTotal = 0; let cacheWriteTotal = 0; const render = (ctx: any) => { const total = inputTotal + outputTotal + cacheReadTotal + cacheWriteTotal; if (total === 0 && costTotal === 0) { ctx.ui.setStatus(KEY, undefined); return; } const theme = ctx.ui.theme; const costStr = theme.fg("accent", fmtCost(costTotal)); const tokStr = theme.fg("dim", `${fmtTokens(total)} tok`); const breakdown = theme.fg("dim", `(${fmtTokens(inputTotal)} in / ${fmtTokens(outputTotal)} out)`); ctx.ui.setStatus(KEY, `${costStr} ${tokStr} ${breakdown}`); }; const reset = () => { costTotal = 0; inputTotal = 0; outputTotal = 0; cacheReadTotal = 0; cacheWriteTotal = 0; }; pi.on("session_start", (_event, ctx) => { reset(); render(ctx); }); pi.on("turn_end", (event, ctx) => { const msg: any = (event as any).message; const usage: any = msg?.usage; if (!usage) return; const cost = usage.cost?.total ?? 0; costTotal += Number(cost) || 0; inputTotal += Number(usage.input) || 0; outputTotal += Number(usage.output) || 0; cacheReadTotal += Number(usage.cacheRead) || 0; cacheWriteTotal += Number(usage.cacheWrite) || 0; render(ctx); }); }