diff --git a/src/__tests__/main/parsers/usage-aggregator.test.ts b/src/__tests__/main/parsers/usage-aggregator.test.ts
index 7e0e0c6e..2ba0d20e 100644
--- a/src/__tests__/main/parsers/usage-aggregator.test.ts
+++ b/src/__tests__/main/parsers/usage-aggregator.test.ts
@@ -99,7 +99,8 @@ describe('estimateContextUsage', () => {
contextWindow: 200000,
});
const result = estimateContextUsage(stats, 'claude-code');
- expect(result).toBe(100);
+ // Output tokens excluded; 150k / 200k = 75%
+ expect(result).toBe(75);
});
});
@@ -113,7 +114,7 @@ describe('estimateContextUsage', () => {
it('should use codex default context window (200k)', () => {
const stats = createStats({ contextWindow: 0 });
const result = estimateContextUsage(stats, 'codex');
- expect(result).toBe(8);
+ expect(result).toBe(5);
});
it('should use opencode default context window (128k)', () => {
diff --git a/src/__tests__/renderer/components/HistoryDetailModal.test.tsx b/src/__tests__/renderer/components/HistoryDetailModal.test.tsx
index f12cfd85..eee8f8a2 100644
--- a/src/__tests__/renderer/components/HistoryDetailModal.test.tsx
+++ b/src/__tests__/renderer/components/HistoryDetailModal.test.tsx
@@ -642,7 +642,7 @@ describe('HistoryDetailModal', () => {
usageStats: {
inputTokens: 74000,
outputTokens: 1000,
- cacheReadInputTokens: 0,
+ cacheReadInputTokens: 1000,
cacheCreationInputTokens: 0,
contextWindow: 100000,
totalCostUsd: 0,
@@ -652,7 +652,7 @@ describe('HistoryDetailModal', () => {
/>
);
- // (74000 + 1000) / 100000 = 75%
+ // (74000 + 1000 cache read) / 100000 = 75%
expect(screen.getByText('75%')).toBeInTheDocument();
});
diff --git a/src/__tests__/renderer/components/MainPanel.test.tsx b/src/__tests__/renderer/components/MainPanel.test.tsx
index afeb7dcd..891b01dd 100644
--- a/src/__tests__/renderer/components/MainPanel.test.tsx
+++ b/src/__tests__/renderer/components/MainPanel.test.tsx
@@ -1794,8 +1794,8 @@ describe('MainPanel', () => {
createdAt: Date.now(),
usageStats: {
inputTokens: 50000,
- outputTokens: 25000,
- cacheReadInputTokens: 0,
+ outputTokens: 0,
+ cacheReadInputTokens: 25000,
cacheCreationInputTokens: 0,
totalCostUsd: 0.05,
contextWindow: 200000,
@@ -1806,7 +1806,7 @@ describe('MainPanel', () => {
render();
- // Context usage should be (50000 + 25000) / 200000 * 100 = 37.5%
+ // Context usage should be (50000 + 25000 cache read) / 200000 * 100 = 37.5%
expect(getContextColor).toHaveBeenCalledWith(38, theme); // Rounded to 38
});
});
diff --git a/src/__tests__/renderer/components/TabSwitcherModal.test.tsx b/src/__tests__/renderer/components/TabSwitcherModal.test.tsx
index 2644c8e7..daeea226 100644
--- a/src/__tests__/renderer/components/TabSwitcherModal.test.tsx
+++ b/src/__tests__/renderer/components/TabSwitcherModal.test.tsx
@@ -61,8 +61,8 @@ const createTestTab = (overrides: Partial = {}): AITab => ({
usageStats: {
inputTokens: 1000,
outputTokens: 500,
- cacheReadTokens: 0,
- cacheWriteTokens: 0,
+ cacheReadInputTokens: 0,
+ cacheCreationInputTokens: 0,
totalCostUsd: 0.05,
contextWindow: 200000,
},
@@ -104,8 +104,8 @@ describe('TabSwitcherModal', () => {
usageStats: {
inputTokens: 500,
outputTokens: 200,
- cacheReadTokens: 0,
- cacheWriteTokens: 0,
+ cacheReadInputTokens: 0,
+ cacheCreationInputTokens: 0,
totalCostUsd: 0.01,
contextWindow: 200000,
},
@@ -132,8 +132,8 @@ describe('TabSwitcherModal', () => {
usageStats: {
inputTokens: 5000,
outputTokens: 3500,
- cacheReadTokens: 0,
- cacheWriteTokens: 0,
+ cacheReadInputTokens: 0,
+ cacheCreationInputTokens: 0,
totalCostUsd: 0.10,
contextWindow: 200000,
},
@@ -160,8 +160,8 @@ describe('TabSwitcherModal', () => {
usageStats: {
inputTokens: 700,
outputTokens: 300,
- cacheReadTokens: 0,
- cacheWriteTokens: 0,
+ cacheReadInputTokens: 0,
+ cacheCreationInputTokens: 0,
totalCostUsd: 0.02,
contextWindow: 200000,
},
@@ -189,8 +189,8 @@ describe('TabSwitcherModal', () => {
usageStats: {
inputTokens: 100,
outputTokens: 50,
- cacheReadTokens: 0,
- cacheWriteTokens: 0,
+ cacheReadInputTokens: 0,
+ cacheCreationInputTokens: 0,
totalCostUsd: 0,
contextWindow: 200000,
},
@@ -216,8 +216,8 @@ describe('TabSwitcherModal', () => {
usageStats: {
inputTokens: 100,
outputTokens: 50,
- cacheReadTokens: 0,
- cacheWriteTokens: 0,
+ cacheReadInputTokens: 0,
+ cacheCreationInputTokens: 0,
totalCostUsd: 0.005,
contextWindow: 200000,
},
@@ -243,8 +243,8 @@ describe('TabSwitcherModal', () => {
usageStats: {
inputTokens: 1000,
outputTokens: 500,
- cacheReadTokens: 0,
- cacheWriteTokens: 0,
+ cacheReadInputTokens: 0,
+ cacheCreationInputTokens: 0,
totalCostUsd: 1.23,
contextWindow: 200000,
},
@@ -501,8 +501,8 @@ describe('TabSwitcherModal', () => {
usageStats: {
inputTokens: 1000,
outputTokens: 500,
- cacheReadTokens: 0,
- cacheWriteTokens: 0,
+ cacheReadInputTokens: 0,
+ cacheCreationInputTokens: 0,
totalCostUsd: 0.05,
contextWindow: 0,
},
@@ -527,10 +527,10 @@ describe('TabSwitcherModal', () => {
it('calculates correct percentage', () => {
const tab = createTestTab({
usageStats: {
- inputTokens: 10000,
+ inputTokens: 20000,
outputTokens: 10000,
- cacheReadTokens: 0,
- cacheWriteTokens: 0,
+ cacheReadInputTokens: 0,
+ cacheCreationInputTokens: 0,
totalCostUsd: 0.50,
contextWindow: 100000,
},
@@ -556,9 +556,9 @@ describe('TabSwitcherModal', () => {
const tab = createTestTab({
usageStats: {
inputTokens: 150000,
- outputTokens: 100000,
- cacheReadTokens: 0,
- cacheWriteTokens: 0,
+ outputTokens: 0,
+ cacheReadInputTokens: 100000,
+ cacheCreationInputTokens: 0,
totalCostUsd: 5.00,
contextWindow: 200000,
},
@@ -810,8 +810,8 @@ describe('TabSwitcherModal', () => {
usageStats: {
inputTokens: 10000,
outputTokens: 10000,
- cacheReadTokens: 0,
- cacheWriteTokens: 0,
+ cacheReadInputTokens: 0,
+ cacheCreationInputTokens: 0,
totalCostUsd: 0.50,
contextWindow: 100000,
},
@@ -842,10 +842,10 @@ describe('TabSwitcherModal', () => {
it('applies color based on percentage (success for low)', () => {
const tab = createTestTab({
usageStats: {
- inputTokens: 5000,
+ inputTokens: 10000,
outputTokens: 5000,
- cacheReadTokens: 0,
- cacheWriteTokens: 0,
+ cacheReadInputTokens: 0,
+ cacheCreationInputTokens: 0,
totalCostUsd: 0.25,
contextWindow: 200000,
},
diff --git a/src/__tests__/renderer/utils/contextExtractor.test.ts b/src/__tests__/renderer/utils/contextExtractor.test.ts
index 57535841..6d46e355 100644
--- a/src/__tests__/renderer/utils/contextExtractor.test.ts
+++ b/src/__tests__/renderer/utils/contextExtractor.test.ts
@@ -94,8 +94,8 @@ describe('extractTabContext', () => {
usageStats: {
inputTokens: 100,
outputTokens: 200,
- cacheReadTokens: 50,
- cacheCreationTokens: 0,
+ cacheReadInputTokens: 50,
+ cacheCreationInputTokens: 0,
costUsd: 0.01,
},
});
@@ -424,15 +424,15 @@ describe('estimateTokenCount', () => {
usageStats: {
inputTokens: 500,
outputTokens: 1000,
- cacheReadTokens: 0,
- cacheCreationTokens: 0,
+ cacheReadInputTokens: 0,
+ cacheCreationInputTokens: 200,
costUsd: 0.05,
},
};
const tokens = estimateTokenCount(context);
- expect(tokens).toBe(1500);
+ expect(tokens).toBe(700); // input + cacheCreation + cacheRead
});
it('should estimate from log content when no usage stats', () => {
@@ -614,7 +614,13 @@ describe('calculateTotalTokens', () => {
name: 'Context 1',
logs: [],
agentType: 'claude-code',
- usageStats: { inputTokens: 100, outputTokens: 200, cacheReadTokens: 0, cacheCreationTokens: 0, costUsd: 0 },
+ usageStats: {
+ inputTokens: 100,
+ outputTokens: 200,
+ cacheReadInputTokens: 50,
+ cacheCreationInputTokens: 25,
+ costUsd: 0,
+ },
},
{
type: 'tab',
@@ -623,13 +629,20 @@ describe('calculateTotalTokens', () => {
name: 'Context 2',
logs: [],
agentType: 'claude-code',
- usageStats: { inputTokens: 300, outputTokens: 400, cacheReadTokens: 0, cacheCreationTokens: 0, costUsd: 0 },
+ usageStats: {
+ inputTokens: 300,
+ outputTokens: 400,
+ cacheReadInputTokens: 75,
+ cacheCreationInputTokens: 25,
+ costUsd: 0,
+ },
},
];
const total = calculateTotalTokens(contexts);
- expect(total).toBe(1000); // (100+200) + (300+400)
+ // input + cacheCreation + cacheRead for each context
+ expect(total).toBe(575); // (100+25+50) + (300+25+75)
});
});
@@ -643,7 +656,13 @@ describe('getContextSummary', () => {
name: 'Context 1',
logs: [createMockLog(), createMockLog()],
agentType: 'claude-code',
- usageStats: { inputTokens: 100, outputTokens: 100, cacheReadTokens: 0, cacheCreationTokens: 0, costUsd: 0 },
+ usageStats: {
+ inputTokens: 100,
+ outputTokens: 100,
+ cacheReadInputTokens: 50,
+ cacheCreationInputTokens: 25,
+ costUsd: 0,
+ },
},
{
type: 'session',
@@ -652,7 +671,13 @@ describe('getContextSummary', () => {
name: 'Context 2',
logs: [createMockLog(), createMockLog(), createMockLog()],
agentType: 'opencode',
- usageStats: { inputTokens: 200, outputTokens: 200, cacheReadTokens: 0, cacheCreationTokens: 0, costUsd: 0 },
+ usageStats: {
+ inputTokens: 200,
+ outputTokens: 200,
+ cacheReadInputTokens: 75,
+ cacheCreationInputTokens: 25,
+ costUsd: 0,
+ },
},
];
@@ -660,7 +685,7 @@ describe('getContextSummary', () => {
expect(summary.totalSources).toBe(2);
expect(summary.totalLogs).toBe(5);
- expect(summary.estimatedTokens).toBe(600);
+ expect(summary.estimatedTokens).toBe(475);
expect(summary.byAgent['claude-code']).toBe(1);
expect(summary.byAgent['opencode']).toBe(1);
});
diff --git a/src/__tests__/renderer/utils/contextUsage.test.ts b/src/__tests__/renderer/utils/contextUsage.test.ts
index bd78013f..f1893c4a 100644
--- a/src/__tests__/renderer/utils/contextUsage.test.ts
+++ b/src/__tests__/renderer/utils/contextUsage.test.ts
@@ -72,13 +72,13 @@ describe('estimateContextUsage', () => {
it('should use claude default context window (200k)', () => {
const stats = createStats({ contextWindow: 0 });
const result = estimateContextUsage(stats, 'claude');
- expect(result).toBe(8);
+ expect(result).toBe(5);
});
it('should use codex default context window (200k)', () => {
const stats = createStats({ contextWindow: 0 });
const result = estimateContextUsage(stats, 'codex');
- expect(result).toBe(8);
+ expect(result).toBe(5);
});
it('should use opencode default context window (128k)', () => {
@@ -91,7 +91,7 @@ describe('estimateContextUsage', () => {
it('should use aider default context window (128k)', () => {
const stats = createStats({ contextWindow: 0 });
const result = estimateContextUsage(stats, 'aider');
- expect(result).toBe(12);
+ expect(result).toBe(8);
});
it('should return null for terminal agent', () => {
@@ -151,7 +151,7 @@ describe('estimateContextUsage', () => {
const stats = createStats({ contextWindow: -100 });
const result = estimateContextUsage(stats, 'claude-code');
// Should use fallback since contextWindow is invalid
- expect(result).toBe(8);
+ expect(result).toBe(5);
});
it('should handle undefined context window', () => {
@@ -160,7 +160,7 @@ describe('estimateContextUsage', () => {
stats.contextWindow = undefined;
const result = estimateContextUsage(stats, 'claude-code');
// Should use fallback
- expect(result).toBe(8);
+ expect(result).toBe(5);
});
it('should handle very large token counts', () => {
diff --git a/src/__tests__/web/mobile/SessionStatusBanner.test.tsx b/src/__tests__/web/mobile/SessionStatusBanner.test.tsx
index 96f79e10..24a6cc1b 100644
--- a/src/__tests__/web/mobile/SessionStatusBanner.test.tsx
+++ b/src/__tests__/web/mobile/SessionStatusBanner.test.tsx
@@ -417,7 +417,7 @@ describe('SessionStatusBanner', () => {
it('shows warning color (yellow) for usage 70-89%', () => {
const usageStats = createUsageStats({
- inputTokens: 100000,
+ inputTokens: 160000,
outputTokens: 60000,
contextWindow: 200000,
});
@@ -464,7 +464,7 @@ describe('SessionStatusBanner', () => {
it('shows warning color at exactly 70%', () => {
const usageStats = createUsageStats({
- inputTokens: 70000,
+ inputTokens: 140000,
outputTokens: 70000,
contextWindow: 200000,
});
@@ -770,7 +770,7 @@ describe('SessionStatusBanner', () => {
describe('ContextUsageBar component', () => {
it('renders progressbar with correct ARIA attributes', () => {
const usageStats = createUsageStats({
- inputTokens: 50000,
+ inputTokens: 100000,
outputTokens: 50000,
contextWindow: 200000,
});
@@ -786,7 +786,7 @@ describe('SessionStatusBanner', () => {
it('has accessible aria-label', () => {
const usageStats = createUsageStats({
- inputTokens: 50000,
+ inputTokens: 100000,
outputTokens: 50000,
contextWindow: 200000,
});
@@ -799,7 +799,7 @@ describe('SessionStatusBanner', () => {
it('has descriptive title', () => {
const usageStats = createUsageStats({
- inputTokens: 50000,
+ inputTokens: 100000,
outputTokens: 50000,
contextWindow: 200000,
});
@@ -1285,7 +1285,7 @@ describe('SessionStatusBanner', () => {
// Tokens
expect(screen.getByText('7.0K')).toBeInTheDocument();
// Context usage
- expect(screen.getByText('4%')).toBeInTheDocument();
+ expect(screen.getByText('3%')).toBeInTheDocument();
// Last response section
expect(screen.getByText(/5m ago/)).toBeInTheDocument();
});
@@ -1443,7 +1443,7 @@ describe('SessionStatusBanner', () => {
expect(screen.getByText('AI')).toBeInTheDocument();
expect(screen.getByText('$2.50')).toBeInTheDocument();
expect(screen.getByText('180.0K')).toBeInTheDocument();
- expect(screen.getByText('90%')).toBeInTheDocument();
+ expect(screen.getByText('75%')).toBeInTheDocument();
expect(screen.getByText('1:05')).toBeInTheDocument();
expect(screen.getByLabelText('AI is thinking')).toBeInTheDocument();
});
diff --git a/src/renderer/components/HistoryDetailModal.tsx b/src/renderer/components/HistoryDetailModal.tsx
index 8d578ffe..9fed6607 100644
--- a/src/renderer/components/HistoryDetailModal.tsx
+++ b/src/renderer/components/HistoryDetailModal.tsx
@@ -343,9 +343,11 @@ export function HistoryDetailModal({
{(() => {
- // Context usage = (input + output + cache read) / context window
- // Cache read tokens represent the conversation history sent with each request
- const contextTokens = entry.usageStats!.inputTokens + entry.usageStats!.outputTokens + (entry.usageStats!.cacheReadInputTokens || 0);
+ // Context usage = (input + cache creation + cache read) / context window
+ const contextTokens =
+ entry.usageStats!.inputTokens +
+ (entry.usageStats!.cacheCreationInputTokens || 0) +
+ (entry.usageStats!.cacheReadInputTokens || 0);
const contextUsage = Math.min(100, Math.round((contextTokens / entry.usageStats!.contextWindow) * 100));
return (