sandbase-harness
sandbaseai
Local-first, self-hosted AI agent runtime and MCP bridge with sandboxed sessions, memory, credentials, audit/replay, and a local Console.
PROJECT TOPICS
INSTALL REFERENCE
dsh plugin --profile web add github:w2327644822-png/dsh-usage-analytics
该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。
PROJECT README
Personal Agent usage analytics & activity dashboard for the DeepSeek Harness (dsh) Web GUI. 中文文档 | 中文版
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:
ctx.sessionPersistence) — no dsh core changes, no prompt content ever leaves your machine (only event metadata and numeric usage are aggregated).Today / 24h / 7d / 30d / 90d / All time), so the headline and the charts always agree.aaa/…, acme-gateway/…) collapse into one row by their real model name; the provider list is kept as a hover tooltip, never shown inline.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".
dsh plugin --profile <name> add dsh-usage-analytics
Then restart the web service (profile bundles load at startup).
data/profiles/web/plugins/dsh-usage-analytics/ (pure JS, no build step).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).dsh-usage-analytics to dsh.profile.bundles in data/profiles/web/package.json (plus a file: dependency if you use pnpm install).After the restart:
GET /api/dsh-usage-analytics/stats returns the aggregate JSON (?force=1 triggers a full rescan);/plugins/@local/dsh-usage-analytics/client.js (per the profile bundle roster).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).
| 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) |
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
readFrom(fromSeq); the watermark advances only from persisted reads, so double counting is impossible.sessionPersistence.listSnapshots() exposes per-session stat revisions (header-only reads); unchanged sessions are skipped entirely, changed ones re-fold only their tail.<DSH_HOME>/usage-analytics/agg.json (atomic write); CACHE_VERSION bumps rebuild the cache automatically.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).
CACHE_VERSION bump or clearing agg.json + restart). The dashboard itself already excludes ghost forks at snapshot time.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.
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.
Apache-2.0 — see LICENSE.
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: token-usage。