dsh-plugin-session-token-stats
English | 中文
A DeepSeek Harness (dsh) Web plugin that turns your real session logs into a token-usage dashboard: total tokens, session count, a daily usage timeline, per-session breakdown with cache-hit rate, and live CPU/memory monitoring.
It counts tokens only. There is no reliable billing API for these sessions, so the page deliberately shows no estimated cost and no budget instead of presenting a guess as a fact.
What it shows
- Summary cards — total tokens (uncached input / cache-hit input / output), sessions (turns plus how many subagent sessions were merged), tokens used this month, and the overall cache-hit rate.
- Token timeline — daily stacked bars for the last 7 / 30 / 90 days (uncached, cache hit, output). Hover a bar for that day's exact counts; the range is a per-browser setting.
- Per-session usage — one row per main conversation, newest first. Subagent sessions are merged into the conversation that spawned them (the whole
parentSessionchain is folded, including multi-level delegation) and never listed separately. Select a row to expand the per-model breakdown, merged subagent count, creation/last-activity timestamps, and session id. - CPU & memory — system CPU usage with a 120-second sparkline, 1/5/15-minute load average, DSH process CPU and RSS, system memory, core count, process uptime, and host identity.
Counting rules
The Host half reads durable session logs through ctx.sessionQuery.listSessions() / readSession(id) and counts every assistant/message usage report:
| Field | Meaning |
|---|---|
| uncached input | usage.inputTokens (DeepSeek excludes cache hits from this field) |
| cache hit | usage.cacheReadTokens |
| output | usage.outputTokens |
| total | uncached + cache hit + output (+ cacheWriteTokens where a provider reports it) |
| cache-hit rate | cache hit ÷ (uncached + cache hit) |
Two deduplication rules keep the numbers honest:
- Fork / seeded sessions — the log starts with a prefix copied from the parent session, whose usage was already billed there. Only events after
inheritedEventCountare counted (the latestsession/titleis still read from the whole log). - Subagent sessions —
header.parentSessionidentifies the spawning conversation; sessions are grouped up that chain to the top-most main session and reported as one row.
Persisted sessions are immutable, so each log is read once and cached; live sessions are re-read on every refresh. The first open decompresses and replay-validates every log (roughly 10–20 seconds for 500+ sessions) and reports { done, total } progress while polling quickly; afterwards refreshes only re-read running sessions.
Requirements
- A DeepSeek Harness Web profile,
dsh >= 0.2.1-rc.2. - Host services
sessionQuery,webServer, andconnection(all present in the shipped Web profile).
Install
The plugin is a normal dsh bundle: it registers one Host row and one client half, and does not override any shipped component.
Plugin UI
Open Plugins → Add plugin → Install third-party plugin and paste one of:
dsh-plugin-session-token-stats
The same field also accepts a local directory or a Git URL.
CLI
# From a local checkout (development)
dsh plugin --profile web add /absolute/path/to/dsh-plugins/session-token-stats
# Or directly from this repository
dsh plugin --profile web add https://github.com/genius-alray/dsh-plugin-session-token-stats
After installation, reload the page; a bar-chart entry (Token stats) appears at the bottom of the left sidebar.
Host routes
Both routes are same-origin and owner-authenticated through the Connection service (unauthenticated → 401; missing Connection service → 403, fail-closed):
GET /session-token-stats/data— totals, daily buckets, grouped per-session aggregates, scan progress.GET /session-token-stats/live— CPU / load / memory sample ring (1 Hz, last 120 samples).
Project layout
server.js Host: log folding, subagent grouping, CPU sampling, the two JSON routes
client.js Client: main panel + sidebar entry, timeline, table, settings
locale/*.json Plugin catalog metadata (Chinese / English)
cordis.patch.yml Inserts this plugin's Host row only
icon.svg Plugin icon
tests/ Host and Client logic tests (node:test)
Development
npm run check # node --check on both halves
npm test # node --test tests/
Tests need no third-party dependencies. The Host suite drives apply() with a synthetic corpus through a fake Cordis context (usage folding, peak/off-peak day buckets, inherited-prefix exclusion, subagent grouping, scan caching, route auth); the Client suite loads the browser bundle with a stub module loader and covers token formatting, cache-hit rate, timeline zero-fill, per-browser preferences, and dictionary parity.
Limitations
- The first scan of 500+ logs takes a few seconds (decompress + replay validation); later refreshes only re-read running sessions.
- The timeline keeps 120 days; older usage still appears in session rows and totals but not in the daily chart.
- When a parent session is no longer in the local corpus (archived or pruned), its subagent sessions become their own top-most row.
- Titles follow the latest
session/titleevent, falling back to the first user message for older logs. - No cost/budget is shown by design; use the token breakdown if you want to price sessions yourself.
No comments yet. Be the first to write one.