#20650: fix(gateway): include deleted/reset sessions in usage.cost RPC (#20599)
gateway
size: S
experienced-contributor
Cluster:
Usage Cost Enhancements
## Summary
- **Bug**: `usage.cost` RPC undercounts tokens and cost by ignoring deleted/reset session transcripts
- **Root cause**: `loadCostUsageSummary` in `src/infra/session-cost-usage.ts` filters files with `.endsWith(".jsonl")`, excluding `*.jsonl.deleted.*` and `*.jsonl.reset.*` archives
- **Fix**: Widen the file filter to include archived session transcripts
Fixes #20599
## Problem
When sessions are deleted or reset, their transcript files are renamed from `foo.jsonl` to `foo.jsonl.deleted.<timestamp>` or `foo.jsonl.reset.<timestamp>` (via `archiveFileOnDisk` in `session-utils.fs.ts`). These files remain on disk with full usage data intact.
However, `loadCostUsageSummary` only scans files ending in `.jsonl`:
```typescript
.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl"))
```
This means all usage from deleted/reset sessions is invisible to the dashboard. In the reporter's deployment, this resulted in 85% of actual cost being hidden.
**Before fix:**
- Active sessions: 100 tokens, $0.10
- Deleted session (on disk as `*.jsonl.deleted.*`): 200 tokens, $0.20
- Reset session (on disk as `*.jsonl.reset.*`): 300 tokens, $0.30
- `usage.cost` RPC returns: 100 tokens, $0.10
## Changes
- `src/infra/session-cost-usage.ts` — Add `isSessionTranscriptFile()` helper that matches `*.jsonl`, `*.jsonl.deleted.*`, and `*.jsonl.reset.*`; use it in `loadCostUsageSummary` file filter
- `src/gateway/server.usage-cost-deleted-sessions.e2e.test.ts` — E2E test: starts real gateway, creates active + deleted + reset session files, calls `usage.cost` RPC, verifies all tokens are counted
- `CHANGELOG.md` — Add fix entry
**After fix:**
- `usage.cost` RPC returns: 600 tokens, $0.60 (all sessions counted)
## Test plan
- [x] New e2e test: real gateway with active + deleted + reset sessions, verifies totalTokens >= 600
- [x] All 8 existing session-cost-usage tests pass
- [x] All 5 existing usage handler tests pass
- [x] Format check passes
- [x] Lint passes
## Effect on User Experience
**Before:** Usage dashboard shows only active session costs. Deleted/reset sessions (which can represent 85%+ of actual spend) are invisible, giving users a false sense of low usage.
**After:** Dashboard accurately reports total cost across all sessions, including those that have been deleted or reset.
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR fixes a critical bug where the `usage.cost` RPC undercounted tokens and costs by ignoring deleted and reset session transcripts.
**Root cause:** The file filter in `loadCostUsageSummary` (line 329) only matched `*.jsonl` files, excluding archived sessions (`*.jsonl.deleted.*` and `*.jsonl.reset.*`) that are created when sessions are deleted or reset via `archiveFileOnDisk`.
**Changes:**
- Added `isSessionTranscriptFile()` helper function that matches all three file patterns: active (`*.jsonl`), deleted (`*.jsonl.deleted.*`), and reset (`*.jsonl.reset.*`) sessions
- Updated the file filter in `loadCostUsageSummary` to use this new helper
- Added comprehensive E2E test verifying all session types are counted
- Updated CHANGELOG with user-facing fix description
**Impact:** Users previously saw only active session costs in their dashboard. This bug could hide 85%+ of actual usage if many sessions were deleted/reset. After this fix, the dashboard accurately reflects total cost across all sessions.
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk
- The fix is surgical, well-tested, and addresses a clearly-defined bug. The new helper function correctly matches the exact naming patterns used by `archiveFileOnDisk`, the E2E test provides comprehensive coverage with all three file types, and the change is backward-compatible (active `.jsonl` files continue to work). The fix is isolated to the cost calculation path and doesn't affect session discovery/listing functionality.
- No files require special attention
<sub>Last reviewed commit: cf74cd1</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#20603: fix(gateway): scan all agents in usage.cost RPC (#20558)
by lailoo · 2026-02-19
83.6%
#13215: fix: pass agentId to loadCostUsageSummary in /usage cost command
by veast · 2026-02-10
80.9%
#22387: fix: session_status context tracking undercount for cached providers
by 1ucian · 2026-02-21
79.8%
#17109: fix: preserve responseUsage across session resets
by Limitless2023 · 2026-02-15
76.5%
#11175: fix: add sessions.usage methods to READ_METHODS for authorization
by Yida-Dev · 2026-02-07
75.9%
#13895: fix(usage): exclude cache tokens from context-window accounting
by zerone0x · 2026-02-11
75.0%
#20336: fix(sessions): resolve transcriptPath using agentId when storePath ...
by Limitless2023 · 2026-02-18
74.8%
#7537: fix(sessions): reset token counts on /new for channel sessions
by SoniAssist · 2026-02-02
74.3%
#20188: fix: Update sessionFile path when rolling to new session in cron jobs
by jriff · 2026-02-18
74.3%
#8477: TUI: persist session token totals when usage metadata is missing
by LarHope · 2026-02-04
74.0%