Bundle
dsh-usage-analytics
Personal usage analytics & activity dashboard for the DeepSeek Harness (dsh) web GUI: token totals, session activity, GitHub-style contribution heatmap, model / reasoning-effort / tool / skill / plugin rankings, streaks and insights. Aggregates real session events through ctx.sessionPersistence; no dsh core changes, no prompt content leaves the machine.
- Source
- w2327644822-png
- stars
- 1 stars
- License
- Apache-2.0
- Updated
- Updated 21 hours ago
Readme
# dsh-usage-analytics
> Personal Agent usage analytics & activity dashboard for the DeepSeek Harness (dsh) Web GUI.
> [中文文档](./README.zh.md) | 中文版
[](https://www.npmjs.com/package/dsh-usage-analytics)
[](./LICENSE)
[](https://github.com/topics/dsh-plugin)
A **usage statistics / activity dashboard** for DeepSeek Harness. It adds a **Usage** entry at the bottom of the sidebar (next to Settings) that opens a full-screen dashboard aggregating your **real** Harness usage:
- **Token totals** (input / output / cache hits / reasoning), **session activity**, a GitHub-style **contribution heatmap**, a **token trend** chart, a **token mix** breakdown, a **model share** ranking, plus **insights** (streaks, peak day, most-used model/tool/skill, …).
- Data comes **only** from your local session event logs (`ctx.sessionPersistence`) — **no dsh core changes**, **no prompt content ever leaves your machine** (only event metadata and numeric usage are aggregated).
## Features
- **Full dashboard** — contribution heatmap, token trend (daily/weekly/hourly), token mix, model share, reasoning-effort / tool / skill / dynamic-plugin rankings, streaks and personal insights.
- **Period filtering** — every chart follows the selected range (`Today` / `24h` / `7d` / `30d` / `90d` / `All time`), so the headline and the charts always agree.
- **Model grouping by real name** — models served through several providers (e.g. `aaa/…`, `acme-gateway/…`) collapse into one row by their real model name; the provider list is kept as a hover tooltip, never shown inline.
- **Ghost-session detection** — forked sessions that were copied but never ran are excluded from every aggregate, so duplicated logs can never inflate your numbers.
- **Provider-console accounting** — the headline and heatmap use the raw convention (input incl. cache hits, matching what a billing console shows); the new-token figure is always available separately, so you can tell "new tokens" from "context re-reads".
- **Local & private** — loopback-only API, no telemetry, no uploads, no prompt content collected or persisted.
- **Incremental & instant** — per-session revision diffing + resumable folds; the dashboard serves the cached snapshot immediately and finishes scans in the background.
## Understanding the numbers (important)
DeepSeek-style APIs count **cache-hit prompt tokens as input** in their consoles. In long-running sessions with large contexts, every tool call re-sends the whole conversation, so **99%+ of "input" can be cache reads** — that is why a busy day can show *billions* of raw tokens while your actual **new** tokens are only tens of millions.
This plugin follows that convention deliberately (so the dashboard matches your provider console), but always separates the three numbers:
| Term | Meaning |
| --- | --- |
| **Input** | uncached (new) input tokens only |
| **Cache hits** | prompt tokens served from the provider cache (re-reads) |
| **Output** | generated tokens |
If the raw totals feel too large, look at the **new-token** figures (day cells, hour buckets, and the "Input" line) — that is the usage you intuitively "produced".
## Install
### From npm (recommended)
```bash
dsh plugin --profile <name> add dsh-usage-analytics
```
Then restart the web service (profile bundles load at startup).
### Manual / local development
1. Copy the package to `data/profiles/web/plugins/dsh-usage-analytics/` (pure JS, no build step).
2. Make it resolvable from the web profile's `node_modules/@local/dsh-usage-analytics` (a directory junction/symlink to step 1, or an actual copy — both work; note the two locations are **not** auto-synced, keep them in step).
3. Add `dsh-usage-analytics` to `dsh.profile.bundles` in `data/profiles/web/package.json` (plus a `file:` dependency if you use `pnpm install`).
4. Restart the web service.
After the restart:
- The sidebar footer shows a **Usage** entry → opens the full-screen dashboard;
- `GET /api/dsh-usage-analytics/stats` returns the aggregate JSON (`?force=1` triggers a full rescan);
- The browser bundle is served at `/plugins/@local/dsh-usage-analytics/client.js` (per the profile bundle roster).
## Usage
Click **Usage** in the sidebar footer. Use the period pills in the header to filter every chart (`Today`, `24h`, `7 days`, `30 days`, `90 days`, `All time`). Hover the heatmap cells for per-day details. The **刷新** button re-syncs from the session logs (first open after a cache-version bump rebuilds the aggregate in a few seconds).
## Data & privacy
| Guarantee | How |
| --- | --- |
| Local only | Reads session logs through `ctx.sessionPersistence`; never writes into sessions; loopback-only HTTP routes with a same-origin fence |
| Metadata only | Consumes event types and numeric `usage` fields only — no user-authored prompt content is collected, persisted, or served |
| Failure-isolated | Every fold/listener is try/catch-contained; a failing analytics never affects the agent loop or the GUI (worst case: a stale cache served with `stale: true`) |
## Architecture
```
Session events / sessions
└─> lib/aggregate.js pure-function aggregation core (foldEvent / mergeInto / computeInsights / streaks)
└─> lib/store.js incremental cache: revision diffing + readFrom(fromSeq) single-source fold + JSON persistence
└─> lib/index.js host plugin: /api/dsh-usage-analytics/stats route + background catch-up folding
└─> lib/client.js browser bundle: sidebar.footer.action + shell.overlay official slots
```
- **Single-source fold (v3)** — tokens are folded only from the persisted log via `readFrom(fromSeq)`; the watermark advances only from persisted reads, so double counting is impossible.
- **Incremental** — `sessionPersistence.listSnapshots()` exposes per-session stat revisions (header-only reads); unchanged sessions are skipped entirely, changed ones re-fold only their tail.
- **Cache** — per-session fold results + revision watermarks are persisted to `<DSH_HOME>/usage-analytics/agg.json` (atomic write); `CACHE_VERSION` bumps rebuild the cache automatically.
- **Per-model per-day buckets (v9)** — each model records daily new/raw/output/call totals so the model-share chart can follow the period selector.
- **Ghost-fork detection (v6)** — a forked session whose whole usage predates its own creation is treated as a copied seed and excluded.
## Development
```bash
npm test # node --test: aggregation core + client bundle smoke (zero dependencies)
node scripts/verify-data.mjs # print what the dashboard would show from real session data
node scripts/smoke-host.mjs # end-to-end smoke: real persistence + plugin apply + route handler
```
Layout: `lib/` (host + client), `test/` (unit tests), `scripts/` (dev verification tools, not published).
## Known limitations
- **No cost estimates** — no local pricing table; a provider→price mapping extension point could be added later.
- **Skill-level token attribution** — only skill *call counts* are tracked; tokens cannot be attributed to a single skill.
- **Session duration** — approximated by the wall-clock span between the first and last event.
- **Deleting a session does not immediately shrink the stats** — the aggregate cache keeps folded results until a full rebuild (`CACHE_VERSION` bump or clearing `agg.json` + restart). The dashboard itself already excludes ghost forks at snapshot time.
- **Multi-window freshness** — host aggregation is in-process; concurrent writers to the cache file are safe (atomic rename) but last write wins.
## FAQ
**Why does a single day show billions of tokens?**
That is the provider-console raw convention: it includes cache-hit prompt tokens. In long sessions with ~100k–800k token contexts, every call re-reads most of the context. Your actual new tokens that day are usually two orders of magnitude smaller — see [Understanding the numbers](#understanding-the-numbers-important).
**Are the numbers fabricated?**
No — every figure is summed from `assistant/message.usage` events recorded in your own session logs, per API call. Nothing is estimated, extrapolated, or injected.
**Why don't deleted conversations reduce the totals?**
Deletion removes the log, but the analytics cache keeps the already-folded statistics until a full rebuild. This is a documented limitation (see above).
**What are "ghost sessions"?**
Session forks that were created but never ran — their entire log is a copied seed of a parent conversation. Counting them would double-count the parent's tokens, so they are excluded automatically.
## License
Apache-2.0 — see [LICENSE](./LICENSE).
Install
dsh plugin --profile web add github:w2327644822-png/dsh-usage-analytics
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-usage-analytics from the hub
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.