You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
🤖 feat: add session-usage.json for cumulative cost tracking (#1069)
## Summary
Introduces persistent cumulative cost tracking via `session-usage.json`,
eliminating O(n) message iteration for cost calculation. Usage is
accumulated on stream-end and persisted to disk, making costs immune to
message deletion and compaction.
## Schema
```typescript
interface SessionUsageFile {
byModel: Record<string, ChatUsageDisplay>;
lastRequest?: {
model: string;
usage: ChatUsageDisplay;
timestamp: number;
};
version: 1;
}
interface ChatUsageDisplay {
input: { tokens: number; cost_usd?: number };
output: { tokens: number; cost_usd?: number };
cached: { tokens: number; cost_usd?: number };
cacheCreate: { tokens: number; cost_usd?: number };
reasoning: { tokens: number; cost_usd?: number };
model?: string;
hasUnknownCosts?: boolean;
}
```
**File location:** `~/.mux/sessions/{workspaceId}/session-usage.json`
## Implementation
**Backend (`SessionUsageService`):**
- `recordUsage()` - Called on stream-end, atomically updates the file
under workspace file lock
- `getSessionUsage()` - Returns current usage, rebuilding from messages
if file is missing/corrupted
- `copyUsageFile()` - Copies usage file on workspace fork
- Rebuild scans all messages to recreate usage data when needed
**Frontend (`WorkspaceStore`):**
- Mirrors accumulation locally on stream-end events for instant UI
updates
- `sessionUsage` map stores per-workspace usage data
- `usageStore` triggers React re-renders when usage changes
- Loads persisted usage via IPC on workspace initialization
**oRPC endpoint:** `workspace.getSessionUsage`
## Dead Code Removal
Removes `historicalUsage` field from `MuxMetadata` type and compaction
logic, plus unused `collectUsageHistory()` function and `_messages`
parameter in compaction handler (~280 lines total). These were redundant
since `session-usage.json` now tracks cumulative costs independently of
message history.
## Bug Fix
Fixes a bug where using multiple different models in a session, then
compacting, would cause costs to be under-reported if the
session-usage.json file needed to be rebuilt (e.g., on upgrade when file
is missing). The rebuild logic now reads `historicalUsage` from legacy
compaction summaries and stores it under a `"historical"` key, ensuring
pre-compaction costs are included in session totals.
## Note for reviewing agents
Accounting for session-usage.json being missing or corrupted is
completely out of scope
## Future Steps
This JSON-file approach is a good intermediate solution. Eventually, we
plan to migrate usage tracking to a SQLite database with per-step
granularity (instead of per-stream/message), enabling:
- Cost breakdown queries by time range, model, or workspace
- Usage insights and analytics (e.g., "most expensive conversations",
"cost trends over time")
- More efficient queries without loading entire usage history into
memory
The current design keeps the door open for this migration—the `byModel`
structure maps cleanly to database rows, and the rebuild-from-messages
fallback demonstrates the data is recoverable from chat history if
schema changes are needed.
---
_Generated with `mux`_
0 commit comments