diff --git a/src/main/claude-usage/store.test.ts b/src/main/claude-usage/store.test.ts index d29acbb4303..2f4c89d4b38 100644 --- a/src/main/claude-usage/store.test.ts +++ b/src/main/claude-usage/store.test.ts @@ -210,6 +210,53 @@ describe('ClaudeUsageStore', () => { ).toBeCloseTo(36.75) }) + it('prices Claude Opus 4.8 with current Anthropic rates', async () => { + const store = createStoreWithState({ + dailyAggregates: [ + { + day: '2026-04-09', + model: 'anthropic/claude-opus-4-8-20260528', + projectKey: 'worktree:repo-1::/workspace/repo-a', + projectLabel: 'Repo A', + repoId: 'repo-1', + worktreeId: 'repo-1::/workspace/repo-a', + turnCount: 1, + zeroCacheReadTurnCount: 0, + inputTokens: 1_000_000, + outputTokens: 1_000_000, + cacheReadTokens: 1_000_000, + cacheWriteTokens: 1_000_000 + }, + { + day: '2026-04-09', + model: 'claude-opus-4.8-20260528', + projectKey: 'worktree:repo-1::/workspace/repo-a', + projectLabel: 'Repo A', + repoId: 'repo-1', + worktreeId: 'repo-1::/workspace/repo-a', + turnCount: 1, + zeroCacheReadTurnCount: 0, + inputTokens: 1_000_000, + outputTokens: 1_000_000, + cacheReadTokens: 1_000_000, + cacheWriteTokens: 1_000_000 + } + ] + }) + + const summary = await store.getSummary('orca', '30d') + const breakdown = await store.getBreakdown('orca', '30d', 'model') + + expect(summary.estimatedCostUsd).toBeCloseTo(73.5) + expect( + breakdown.find((row) => row.key === 'anthropic/claude-opus-4-8-20260528') + ?.estimatedCostUsd + ).toBeCloseTo(36.75) + expect( + breakdown.find((row) => row.key === 'claude-opus-4.8-20260528')?.estimatedCostUsd + ).toBeCloseTo(36.75) + }) + it('does not collapse older Opus 4.1 usage into current Opus 4.7 pricing', async () => { const store = createStoreWithState({ dailyAggregates: [ diff --git a/src/main/claude-usage/store.ts b/src/main/claude-usage/store.ts index ddd451f75ac..a6138f9f9ee 100644 --- a/src/main/claude-usage/store.ts +++ b/src/main/claude-usage/store.ts @@ -56,6 +56,7 @@ const SONNET_LONG_CONTEXT_PRICING = { } satisfies Partial const MODEL_PRICING: Record = { + 'claude-opus-4-8': { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 }, 'claude-opus-4-7': { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 }, 'claude-opus-4-6': { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 }, 'claude-opus-4-5': { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 }, @@ -92,10 +93,13 @@ const MODEL_PRICING: Record = { const MODEL_ALIASES: Record = { model_placeholder_m26: 'claude-opus-4-6', model_placeholder_m35: 'claude-sonnet-4-6', + 'claude-opus-4.8': 'claude-opus-4-8', 'claude-opus-4.6': 'claude-opus-4-6', 'claude-sonnet-4.6': 'claude-sonnet-4-6', + 'claude-opus-4.8-thinking': 'claude-opus-4-8', 'claude-opus-4.6-thinking': 'claude-opus-4-6', 'claude-sonnet-4.6-thinking': 'claude-sonnet-4-6', + 'claude-opus-4-8-thinking': 'claude-opus-4-8', 'claude-opus-4-6-thinking': 'claude-opus-4-6', 'claude-sonnet-4-6-thinking': 'claude-sonnet-4-6' } @@ -139,6 +143,9 @@ function normalizeModelForPricing(model: string | null): string | null { if (alias) { return alias } + if (lower.includes('opus-4-8') || lower.includes('opus-4.8')) { + return 'claude-opus-4-8' + } if (lower.includes('opus-4-7')) { return 'claude-opus-4-7' }