dsh-cost
Account balance and cost statistics pill for DeepSeek Harness. It adds a pill at the right-hand end of the official stats row below the session composer, combining three readings: the billing period (peak / off-peak, colour-coded), the account balance, and the current session cost. Clicking the pill expands a per-session breakdown — cache-hit input, cache-miss input and output amounts, cost per model, the successful web search / web fetch call counts and their per-request fee (billed from 2026-11-01), and the token and web-call cost of the subagent sessions this session spawned (at any depth). The cost side needs no credentials; the balance side needs a DeepSeek API key.
Community plugin. Not affiliated with, endorsed by, or shipped by DeepSeek.
Screenshots


Features
- Rightmost pill in the stats row: registers into
conversation.composer.dockwithorder: 2, after both official stats pills (activity= 0,usage= 1). The period readsPeakorOff-peak, and a red / green dot distinguishes them. - Popup breakdown: billing period, account balance, current session cost, the cost split by bucket — cache-hit input, cache-miss input (cache writes included), output — a per-model breakdown whenever the host can fold one, and the session's successful web search / web fetch call counts and fee when it made such calls. Subagent spend is not a separate line: it is folded into every amount above; when subagents exist, the session-cost label reads "(incl. subagents)" and a small note says how many sessions are included.
- Subagent spend included: every descendant subagent session (identified by the
origin: subagentheader lineage, including subagents of subagents) has its token usage and web call fees folded into the existing fields — total, the three token buckets, the web rows and the per-model rows. A user fork is an independent conversation and never counts. Running subagents are read from memory; closed ones are read from their persisted logs through the session-query service and cached. - Official pricing: the 2026-09-10 price cut and the pro→flash routing are both modelled. While the session is attached, usage is folded per request, so a session spanning a price change, a routing change or a peak boundary never mixes two rate tables. The Search / Fetch call fee follows the official 2026-10-17 notice: ¥5 per 1,000 successful requests (¥0.005 each), with failures, empty searches and timeouts free, billed for DeepSeek Harness server-side tool calls from 2026-11-01 00:00 Beijing on; the plugin prices each call at its own moment and shows calls inside the free period as "Free".
- Peak / off-peak clock: workday windows
09:00–12:00and14:00–18:00; everything else, weekends and statutory holidays bill off-peak all day (including a weekend that holiday adjustment turns into a workday). The built-in 2026 PRC holiday table can be replaced throughConfig.holidays. - Rates follow official pricing: the rate table ships inside the plugin and is refreshed with plugin releases.
- Works without an API key: installs signed in with a DeepSeek account still see the billing period and the session cost; only the balance is absent.
- No credentials stored: the key is resolved through dsh's credentials seam, used in-process, and never written to disk.
- Bilingual: 20 dictionary entries under the
cost-statsnamespace, Chinese and English, following the app locale. - Host route only: the browser half reads the host route
/api/dsh-balance(once per session step plus a 60 s poll) and never contacts a third party. - No local state: nothing is persisted; uninstalling leaves no data behind.
- Native behaviour: positioning and outside-dismiss reuse the official
ui-primitiveshelpers (useAnchoredPosition,useDismissOnOutsidePointer).
Requirements
- DeepSeek Harness (dsh)
>= 0.2.1-rc.2. - A DeepSeek API key if you want the balance. It is resolved through dsh's credentials seam; the plugin falls back to the
DEEPSEEK_API_KEYenvironment variable only when the credentials service itself is unavailable — if credentials exists but holds no such key, the environment variable is not consulted. When no key resolves (for example an install signed in with a DeepSeek account), the pill keeps showing the billing period and the session cost, and only the balance is absent.
Install
From the plugin market (search dsh-cost), or from a terminal:
dsh plugin --profile <profile> add dsh-cost-pill
You can also install from the GitHub source: dsh plugin --profile <profile> add github:Arun1016/dsh-cost.
Note: the bare npm name
dsh-costbelongs to an unrelated project; this plugin is published under the@arun_1016/scope, so the two cannot be confused.
Configuration
- id: cost-stats
name: dsh-cost
config:
holidays: ['2026-01-01'] # statutory holidays bill off-peak all day
holidays is the only Config field. It defaults to the built-in 2026 PRC statutory-holiday table (State Council notice 国办发明电〔2025〕7号), as Beijing-local YYYY-MM-DD dates. A new year is a Config edit, not a code change.
Billing model
Model prices are CNY per million tokens, and every figure below is a peak rate — off-peak is exactly half, per the official rule. Search and Fetch are billed per request instead, listed at the end of the table.
| Item | Rule |
|---|---|
| Flash (from 2026-09-10 12:00 Beijing) | peak 2 / 0.04 / 8 = uncached input / cache-hit input / output |
| Flash (before the price cut) | 3.0 / 0.1 / 9.0, same three buckets |
Pro (deepseek-v4-pro, before 2026-09-14 12:00 Beijing) |
9.0 / 0.3 / 27.0, same three buckets |
| Peak window | workdays 09:00–12:00 and 14:00–18:00 |
| Off-peak | everything else; weekends and statutory holidays all day, including a weekend that holiday adjustment turns into a workday |
| Clock used | the local clock of the machine running dsh — the official windows are Beijing time, so the reading is only exact on a Beijing-time host |
| pro→flash routing | billed as Flash from 2026-09-14 12:00 Beijing |
inputTokens |
uncached input; cache writes bill at the uncached rate |
| reasoning | subset of output, not billed separately |
| Search / Fetch calls | ¥5 per 1,000 successful requests (¥0.005 each), independent of the peak / off-peak window |
| Web billing start | 2026-11-01 00:00 Beijing; earlier successful calls cost nothing and display as "Free" |
| Successful call | Search returned a non-empty results array; Fetch returned page content; failures, empty searches and timeouts are free |
| Call counting | the session log's web_search / web_fetch tool calls; one multi-query web_search counts its distinct queries |
| Scope | the session's own events (excluding any fork-inherited prefix) plus the own events of every descendant subagent session it spawned |
| Subagent identification | header lineage carrying origin: subagent, at any depth; a user fork is an independent conversation and never counts |
| Subagent annotation | with subagents, the session-cost label reads "(incl. subagents)" and a small note names how many sessions are included; their cost is never a separate line, it is folded into the amounts above |
The Search / Fetch price and billing start come from the official price page and the web search & fetch guide (announced 2026-10-17).
Privacy
- No telemetry, no analytics, no third-party endpoints.
- The only outbound request is
https://api.deepseek.com/user/balance, authenticated with your key; it times out after 15 s. - The browser half only reads the host route
/api/dsh-balance— once per session step, plus a 60 s poll. It never contacts a third-party endpoint itself. - This plugin never writes the key to disk, and balances are cached in memory only.
Known limitations
- While the session is attached the host folds usage per request, so a span across the price cut, the routing change or a peak boundary keeps its own rate table. A detached session is priced by the host as one aggregate at the current Flash rates and carries no estimate marker; the browser half's own fallback estimate uses the same current rates and is marked
(估)in Chinese /(est)in English. - Web search / fetch fees are inferred from the session log's
web_search/web_fetchtool calls: the host does not load a detached session's log and keeps no per-query results, so detached sessions and the local fallback estimate omit this fee; a log trimmed by compaction undercounts too. - A multi-query
web_searchwhose queries partly return nothing cannot be split after the results are merged, so every distinct query is billed (an overcount); a failed call bills nothing, but sibling requests that had already succeeded in the same batch cannot be counted (an undercount). - A web fee is priced by when the tool result landed, so a request straddling 2026-11-01 00:00 Beijing can land on either side of the billing start by seconds.
- Subagent spend depends on dsh's session-query service: without it only subagents still resident in memory are counted, so a finished and released subagent is missed.
- Closed subagents are read from their persisted logs and cached by session id (256 entries). A log that cannot be read (format migration, corruption) is skipped.
- Running subagents are folded from memory on each read; a subagent that finishes and is released between two cost polls may lag by one poll interval.
- A fork-inherited prefix is excluded from the session's cost (it was already billed to the parent), and a user fork is a separate conversation that never counts.
- Calls are counted by tool name, not by the underlying provider. The shipped profile uses DeepSeek's Search / Fetch APIs, where this matches the official bill; with a different search or fetch provider the plugin still counts the calls and would show uncounted calls as billed.
- Every reading is conditional: a missing balance hides only that segment; a cost of exactly
¥0.00hides the cost segment; and when the host route is missing or errors, the whole pill disappears silently, with no placeholder and no error message. After a failure it stops per-step polling and keeps only the 60 s poll. - Amounts always print with a
¥prefix, regardless of the account currency the API reports. Balance uses 2 decimals; session costs use 2 decimals, or 4 below¥0.01; web call fees use 3 decimals (the ¥0.005 unit — three calls read¥0.015, not a two-decimal¥0.01). - The per-model breakdown appears only when the host can fold the session by model, and model ids are normalised (for example
deepseek-v4-flashanddeepseek-v4-flash-vision-expboth display asdeepseek-flash). - Balance failures are silent: no key, an invalid key, a network error and the 15 s timeout all produce a
200response with nobalance_infosfield, and that empty result is cached for 30 s as well. - The balance cache is process-wide and shared across sessions; it is only invalidated by a different key, so a top-up can take up to 30 s to appear.
- Model ids the plugin does not recognise are priced with the Flash table.
- Costs accumulate per request, so rounding may differ from the account ledger.
- Events without a model source or usage are skipped for token cost; web calls are folded separately from their own events.
- The popup has no height cap and does not share the official pills' mutual-exclusion state, so it can stay open alongside another popup.
- The built-in holiday table covers 2026 only.
Development
Source lives in src/ (host half index.ts, client half client/). It is built with the official dsh monorepo toolchain: tsconfig.*.json and tsdown.config.ts reference the monorepo through relative paths (../../../tsconfig.base.json, ../../client/tsdown.client.ts), so building requires placing this package back into that tree — the repository cannot be built standalone.
The published package carries lib/ and cordis.patch.yml, plus package.json, both READMEs and LICENSE (npm always includes those).
License
MIT — see LICENSE.
No comments yet. Be the first to write one.