feat: use session-reported Claude CLI context windows

This commit is contained in:
amosblomqvist
2026-10-28 20:36:42 +02:00
parent 17c94be327
commit 074ba7afe4
15 changed files with 359 additions and 36 deletions
+21 -6
View File
@@ -1,7 +1,7 @@
"use client";
import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react";
import { subscriptionLabels, subscriptionLabel, type SessionPricing, type TurnUsage } from "@/src/core";
import { CLAUDE_CODE_PROVIDER, subscriptionLabels, subscriptionLabel, type cliContextWindowOf, type SessionPricing, type TurnUsage } from "@/src/core";
import { formatTokens, formatUsd, planCostLabel, prettyModel } from "@/components/usage-format";
import { ModelControl, catalogLimits, useModelCatalog } from "@/components/model-picker";
@@ -10,6 +10,15 @@ export function formatContextMeasurement(tokens: number | null, limit: number |
return limit === null ? numerator : `${numerator} / ${formatTokens(limit)}`;
}
/** CLI aliases have no global catalog limit: only this session's matching
* response can supply a denominator. Other providers keep their catalog join. */
export function contextLimitForModel(model: string | undefined, limits: ReadonlyMap<string, number> | undefined,
cliWindow: ReturnType<typeof cliContextWindowOf> = null): number | null {
if (!model) return null;
if (model.startsWith(`${CLAUDE_CODE_PROVIDER}:`)) return cliWindow?.model === model ? cliWindow.tokens : null;
return limits?.get(model) ?? null;
}
/**
* The below-composer stats bar (wave 4 observability): the session's
* money/context footprint as one quiet row beneath the composer —
@@ -24,12 +33,14 @@ export function formatContextMeasurement(tokens: number | null, limit: number |
* Spending arrives already priced by the server through Pi's catalog and
* calculator. Missing cost stays "—", never fake zero. Context is a separate
* completed-conversation measurement, never a money-log/summarizer fold or
* transcript estimate. Its catalog denominator follows the selected model;
* model changes preserve the numerator. Successful compaction clears it to ?.
* transcript estimate. Its denominator follows the selected model, from the
* catalog or the session's CLI report; model changes preserve the numerator.
* Successful compaction clears the numerator and any old CLI report.
*/
export function StatsBar({
turns,
contextTokens = null,
cliContextWindow = null,
pricing,
dictationSlot,
chatId,
@@ -38,6 +49,7 @@ export function StatsBar({
}: {
turns: TurnUsage[];
contextTokens?: number | null;
cliContextWindow?: ReturnType<typeof cliContextWindowOf>;
pricing?: SessionPricing;
/** The bound session (per-session models, owner 2026-09-28): present
* on the in-session bar — the model control binds THE SESSION'S own
@@ -86,11 +98,11 @@ export function StatsBar({
document.removeEventListener("keydown", onKeyDown);
};
}, [open]);
// Catalog-only denominator; the picker reports its effective selection.
// Missing/pending catalog means usage-only, never a hardcoded maximum.
// The picker reports its effective selection. CLI windows are session-
// scoped; unknown metadata always means usage-only, never a guessed maximum.
const { catalog } = useModelCatalog();
const limits = useMemo(() => (catalog ? catalogLimits(catalog) : undefined), [catalog]);
const limit = selectedModel ? limits?.get(selectedModel) ?? null : null;
const limit = contextLimitForModel(selectedModel, limits, cliContextWindow);
const contextLabel = formatContextMeasurement(contextTokens, limit);
const { cost = 0, unpriced = turns.length, subscription = 0 } = pricing ?? {};
// Honest cost: show dollars only when at least one turn priced;
@@ -151,6 +163,9 @@ export function StatsBar({
{contextLabel}
</span>
</div>
{selectedModel?.startsWith(`${CLAUDE_CODE_PROVIDER}:`) && (
<p>The window is reported by Claude Code’s local summary and may reflect its auto-compaction budget rather than the model’s full maximum. Unavailable windows are omitted.</p>
)}
<div className="flex justify-between gap-2">
<span>{subscription ? "Usage" : "Total cost"}</span>
<span className="tabular-nums text-card-foreground">{costLabel}</span>