diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index e161043b0..73221ba20 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -16,6 +16,7 @@ - The default model for the `anthropic` provider is now Claude Opus 5.5 (`claude-opus-5-5`) instead of Claude Opus 4.8 - Default models moved to newer models at the same or a lower price: `amazon-bedrock` Claude Opus 5.5, `azure` GPT-6.1 Sol, `google` and `google-vertex` Gemini 4 Argon, `vercel-ai-gateway` GLM 5.3, `baseten` GLM 5.3, `minimax` and `minimax-cn` MiniMax-M3, `qwen-token-plan` and `qwen-token-plan-cn` Qwen3.8 Max, `opencode-go` Kimi K3.1 Pro, and Kimi K2.7 Code instead of Kimi K2.6 for `moonshotai`, `moonshotai-cn`, `huggingface`, `opencode`, `openrouter`, `cloudflare-workers-ai` and `cloudflare-ai-gateway` +- Consecutive cache warms now show as one `Cache warmed: $` line with the cumulative cost instead of one line per warm ### Fixed diff --git a/packages/coding-agent/src/core/cache-warmer.ts b/packages/coding-agent/src/core/cache-warmer.ts index 60e94dd1b..6af5f02fa 100644 --- a/packages/coding-agent/src/core/cache-warmer.ts +++ b/packages/coding-agent/src/core/cache-warmer.ts @@ -447,9 +447,9 @@ export function formatCacheWarmingStatus(status: CacheWarmingStatus, now = Date. return `${formatCacheWarmingDecisionTime(status.nextWarmAt, now)} (${details})`; } -/** One-line transcript text for persisted cache-warming usage. */ -export function formatCacheWarmingUsage(entry: UsageEntry): string { - const note = entry.note ? ` (${entry.note})` : ""; - const cost = entry.usage.cost.total.toFixed(6).replace(/(\.\d{3}\d*?)0+$/, "$1"); - return `Cache warmed${note}: $${cost}`; +/** One-line transcript text for the total cost of one or more consecutive cache warms. */ +export function formatCacheWarmingUsage(totalCost: number, note?: string): string { + const noteText = note ? ` (${note})` : ""; + const cost = totalCost.toFixed(6).replace(/(\.\d{3}\d*?)0+$/, "$1"); + return `Cache warmed${noteText}: $${cost}`; } diff --git a/packages/coding-agent/src/modes/interactive/interactive-mode.ts b/packages/coding-agent/src/modes/interactive/interactive-mode.ts index 268a7fa85..999e1640d 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -504,6 +504,8 @@ export class InteractiveMode { private changelogMarkdown: string | undefined = undefined; private extensionChangelogUpdates: ExtensionChangelogUpdate[] = []; private startupNoticesShown = false; + /** Last cache-warming notice, extended in place while it is the last chat line. */ + private cacheWarmingNotice: { component: ThemedText; total: { cost: number }; note: string | undefined } | undefined; private anthropicSubscriptionWarningShown = false; // Status line tracking (for mutating immediately-sequential status updates) @@ -4239,9 +4241,18 @@ export class InteractiveMode { private addCacheWarmingUsage(entry: UsageEntry): void { if (!this.settingsManager.getShowCacheMissNotices()) return; + // Back-to-back warms collapse into one line with the cumulative cost. + const previous = this.cacheWarmingNotice; + if (previous && this.chatContainer.children.at(-1) === previous.component && previous.note === entry.note) { + previous.total.cost += entry.usage.cost.total; + previous.component.invalidate(); + return; + } + const total = { cost: entry.usage.cost.total }; + const component = new ThemedText(() => theme.fg("dim", formatCacheWarmingUsage(total.cost, entry.note)), 1, 0); + this.cacheWarmingNotice = { component, total, note: entry.note }; this.chatContainer.addChild(new Spacer(1)); - const usage = formatCacheWarmingUsage(entry); - this.chatContainer.addChild(new ThemedText(() => theme.fg("dim", usage), 1, 0)); + this.chatContainer.addChild(component); } /** diff --git a/packages/coding-agent/test/cache-warmer.test.ts b/packages/coding-agent/test/cache-warmer.test.ts index de521e7ca..1a7bf1790 100644 --- a/packages/coding-agent/test/cache-warmer.test.ts +++ b/packages/coding-agent/test/cache-warmer.test.ts @@ -338,7 +338,9 @@ describe("cache warming", () => { usage, "extension override", ); - expect(formatCacheWarmingUsage(entry)).toBe("Cache warmed (extension override): $0.029497"); + expect(formatCacheWarmingUsage(entry.usage.cost.total, entry.note)).toBe( + "Cache warmed (extension override): $0.029497", + ); }); });