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.
+
Window reported by Claude Code; may be lower for auto-compaction.
)}
{subscription ? "Usage" : "Total cost"}
@@ -193,7 +194,7 @@ export function StatsBar({
{turn.steps.length} steps
- {subscriptionLabel(turn.model) ?? (c === null ? "—" : formatUsd(c))}
+ {turn.model.startsWith(`${CLAUDE_CODE_PROVIDER}:`) ? "CLI" : subscriptionLabel(turn.model) ?? (c === null ? "—" : formatUsd(c))}
);
diff --git a/tests/claude-code-context-ui.test.tsx b/tests/claude-code-context-ui.test.tsx
index 3b026ac..9aa62f3 100644
--- a/tests/claude-code-context-ui.test.tsx
+++ b/tests/claude-code-context-ui.test.tsx
@@ -7,7 +7,7 @@ import { parseHTML } from "linkedom";
import { StatsBar } from "../components/stats-bar.tsx";
import { CLAUDE_CODE_MODELS } from "../src/agent/claude-code/provider.ts";
import { toModelDto } from "../src/server/model-catalog.ts";
-import type { ModelCatalogResponse } from "../src/core/index.ts";
+import type { ModelCatalogResponse, TurnUsage } from "../src/core/index.ts";
const dom = parseHTML("");
Object.assign(globalThis, { window: dom.window, document: dom.document, Element: dom.window.Element, HTMLElement: dom.window.HTMLElement,
@@ -27,14 +27,17 @@ test("compact CLI context is numeric only; effective-window explanation appears
throw new Error(`unexpected request ${url}`);
}) as typeof fetch;
host = document.createElement("div"); document.body.append(host); root = createRoot(host);
- const render = (window: { model: string; tokens: number } | null) => root!.render();
+ const turn: TurnUsage = { id: "cli-turn", messageId: "cli-message", model: "claude-code:opus", at: "2026-10-28T00:00:00Z", steps: [{ inputTokens: 42000, outputTokens: 1, cacheReadTokens: undefined, cacheWriteTokens: undefined, reasoningTokens: undefined }] };
+ const render = (window: { model: string; tokens: number } | null) => root!.render();
await act(async () => render({ model: "claude-code:opus", tokens: 200000 }));
assert.equal(document.querySelector('[data-testid="stats-context"]')?.textContent, "42k / 200k");
assert(!document.body.textContent?.includes("auto-compaction"));
+ assert.equal(document.querySelector('[data-testid="stats-cost"]')?.textContent, "CLI");
const summary = document.querySelector('[data-testid="stats-summary"]')!;
assert(!summary.textContent?.includes("window")); assert(!summary.textContent?.includes("budget"));
await act(async () => summary.click());
- assert.match(document.querySelector('[data-testid="stats-detail"]')?.textContent ?? "", /auto-compaction budget rather than the model’s full maximum/);
+ assert.match(document.querySelector('[data-testid="stats-detail"]')?.textContent ?? "", /Window reported by Claude Code; may be lower for auto-compaction\./);
+ assert.equal(document.querySelector('[data-testid="stats-detail"] li > span:last-child > span:last-child')?.textContent, "CLI");
assert.equal(document.querySelector('[data-testid="stats-context"]')?.textContent, "42k / 200k");
await act(async () => render(null));
assert.equal(document.querySelector('[data-testid="stats-context"]')?.textContent, "42k");
diff --git a/tests/claude-code-status-ui.test.tsx b/tests/claude-code-status-ui.test.tsx
index 015fe66..1afbf0d 100644
--- a/tests/claude-code-status-ui.test.tsx
+++ b/tests/claude-code-status-ui.test.tsx
@@ -6,6 +6,7 @@ import { createRoot, type Root } from "react-dom/client";
import { renderToStaticMarkup } from "react-dom/server";
import { parseHTML } from "linkedom";
import { ClaudeCodeInstallationStatus, claudeCodeStatusLabel } from "../components/settings/claude-code-status.tsx";
+import { ConnectionRow } from "../components/settings/connection-controls.tsx";
import type { ClaudeCodeStatus } from "../src/core/contracts/claude-code.ts";
const dom = parseHTML("");
@@ -33,6 +34,18 @@ test("row is informational, disclaims auth/billing readiness, and links only to
assert.equal((html.match(/"));
+ assert.match(html, /data-variant="outline"/);
+ assert.match(html, /data-size="xs"/);
+});
+
+test("recheck matches the existing Connect button styling without restyling Manage actions", () => {
+ const row = (action: string) => renderToStaticMarkup( {}} />);
+ const connect = parseHTML(row("Connect")).document.querySelector("button")!;
+ const manage = parseHTML(row("Manage")).document.querySelector("button")!;
+ const check = parseHTML(renderToStaticMarkup()).document.querySelector("button")!;
+ assert.equal(check.getAttribute("class"), connect.getAttribute("class"));
+ assert.equal(check.getAttribute("data-variant"), "outline");
+ assert.equal(manage.getAttribute("data-variant"), "ghost");
});
for (const [value, text] of [
diff --git a/tests/claude-code-usage.test.tsx b/tests/claude-code-usage.test.tsx
index 7c781fc..8415d2f 100644
--- a/tests/claude-code-usage.test.tsx
+++ b/tests/claude-code-usage.test.tsx
@@ -25,6 +25,7 @@ for (const model of CLAUDE_CODE_MODELS) test(`${model.id}: token usage is retain
assert.equal(jobCost({ usage: accumulateJobUsage(undefined, id, step) }), null);
const html = renderToStaticMarkup();
assert(!html.includes("$0"));
+ assert.match(html, /data-testid="stats-cost">CLI<\/span>/);
const summary = graphCostSummary({ chats: [{ title: "CLI fixture", extractions: [{ startedAt: 1, at: turn.at, durationMs: 1,
stages: { extractor: { model: id, steps: [step] }, auditor: { model: id, steps: [step] } },
}] }], sources: [] }, callCost);
@@ -32,6 +33,18 @@ for (const model of CLAUDE_CODE_MODELS) test(`${model.id}: token usage is retain
assert.equal(summary.runs[0]!.dollars, null);
});
+test("mixed session totals retain known API dollars and subscription labels alongside CLI billing", () => {
+ const cli: TurnUsage = { id: "cli", messageId: "cli", at: "2026-10-28T00:00:00Z", model: "claude-code:sonnet", steps: [step] };
+ const api = { ...cli, id: "api", messageId: "api", model: "openai:fixture" };
+ const pricing = { cost: 0.12, unpriced: 1, turnCosts: { cli: null, api: 0.12 } };
+ const html = renderToStaticMarkup();
+ assert.match(html, /data-testid="stats-cost">\$0\.1200 \+ CLI<\/span>/);
+ const subscription = { ...cli, id: "plan", messageId: "plan", model: "chatgpt:fixture" };
+ const mixed = renderToStaticMarkup();
+ assert.match(mixed, /data-testid="stats-cost">\$0\.1200 \+ ChatGPT \+ CLI<\/span>/);
+ assert.equal(callCost(step, cli.model), null, "CLI stays unpriced in accounting");
+});
+
test("model-catalog API and menus never advertise the CLI adapter's placeholder zero rates", () => {
const models = CLAUDE_CODE_MODELS.map(toModelDto);
assert(models.every((model) => model.cost === null));