Context meter A budget split between the document, the standing instructions and the conversation, with a threshold at which the meter appears at all. The line in the transcript marks where earlier turns stopped being visible to the model. npx shadcn@latest add button Tokens this needs: --card, --muted, --muted-foreground, --border, --chart-1, --scale-seq-1, --scale-seq-2, --scale-seq-3 The status, chart, scale, state and direction names are an extension, not a rename. shadcn has --destructive and five --chart-* and nothing else in this territory. ──────────────────────────────────────────────────────────────────────── // ContextMeter.tsx ──────────────────────────────────────────────────────────────────────── import { Button } from "@/components/ui/button"; /** * What the product does when the window is full. The set is closed because * each one is a different experience for the viewer, and the boundary marker * has to say which happened. */ export type Overflow = "truncate" | "summarise" | "refuse"; /** Everything that draws on the window. Order is display order. */ export type Segment = { key: string; /** What the viewer sees under the bar: a file name, "rules", "turns". */ label: string; tokens: number; }; const SEGMENT_COLOR = ["bg-scale-seq-3", "bg-scale-seq-2", "bg-scale-seq-1", "bg-scale-seq-4"]; /** * A stacked budget bar. It stays out of the way until `showAt` of the window is * spent, because a meter that never moves off empty teaches people to ignore * it, and once visible it offers the one action that still helps. */ export function ContextMeter({ budget, segments, showAt = 2 / 3, onStartFresh, }: { /** The window, in tokens, for the model in use. */ budget: number; segments: Segment[]; /** Fraction of the window spent before the meter appears at all. */ showAt?: number; /** Continue in a new conversation carrying a summary. */ onStartFresh: () => void; }) { const used = segments.reduce((sum, s) => sum + s.tokens, 0); const pct = (n: number) => `${(100 * n) / budget}%`; if (used / budget < showAt) return null; // Percent, never tokens: a token count is precise and meaningless to almost everyone. const usedPct = Math.round((100 * used) / budget); return (
{segments.map((s, i) => ( ))}
{segments.map((s, i) => ( {s.label} ))}
{usedPct}% of the window used
); } const BOUNDARY: Record = { truncate: "the model can no longer see anything above this line", summarise: "everything above this line has been summarised for the model", refuse: "this conversation has reached its limit", }; /** * The marker that goes in the transcript at the point where earlier turns * stopped being included. Nobody watches a meter; everybody sees a line when * they scroll back. */ export function ContextBoundary({ overflow }: { overflow: Overflow }) { return (

{BOUNDARY[overflow]}

); } ──────────────────────────────────────────────────────────────────────── // demo.tsx ──────────────────────────────────────────────────────────────────────── import { ContextBoundary, ContextMeter } from "./ContextMeter"; /** A 200k window with a lease attached. Two early turns have already fallen out. */ const BUDGET = 200_000; const SEGMENTS = [ { key: "lease", label: "lease.pdf", tokens: 98_600 }, { key: "rules", label: "rules", tokens: 18_400 }, { key: "turns", label: "turns", tokens: 46_000 }, ]; const TURNS = [ { who: "you", text: "Summarise the break clause in lease.pdf." }, { who: "assistant", text: "The tenant can end the lease at the end of year two on six months' notice, if rent is paid up and the premises are handed back empty." }, { who: "you", text: "What about the rent review?" }, { who: "assistant", text: "Upward only, at the third anniversary, to the higher of open market rent and the passing rent." }, ]; /** Index of the first turn the model can still see. */ const VISIBLE_FROM = 2; export default function Demo() { return (
console.log("start fresh with a summary")} />
{TURNS.map((t, i) => (
{i === VISIBLE_FROM && }

{t.who}: {t.text}

))}
); }