dsh-all-usage 1.1.18 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CHANGELOG.md +469 -394
  2. package/LICENSE +21 -21
  3. package/README.en.md +273 -0
  4. package/README.md +145 -279
  5. package/assets/model-icons/ai2-color.svg +1 -0
  6. package/assets/model-icons/ai21-brand-color.svg +1 -0
  7. package/assets/model-icons/aionlabs.svg +1 -0
  8. package/assets/model-icons/alephalpha.svg +1 -0
  9. package/assets/model-icons/baai.svg +1 -0
  10. package/assets/model-icons/baichuan-color.svg +1 -0
  11. package/assets/model-icons/baidu-color.svg +1 -0
  12. package/assets/model-icons/bytedance-color.svg +1 -0
  13. package/assets/model-icons/cohere-color.svg +1 -0
  14. package/assets/model-icons/hunyuan-color.svg +1 -0
  15. package/assets/model-icons/ibm.svg +1 -0
  16. package/assets/model-icons/iflytekcloud-color.svg +1 -0
  17. package/assets/model-icons/internlm-color.svg +1 -0
  18. package/assets/model-icons/kling-color.svg +1 -0
  19. package/assets/model-icons/liquid.svg +1 -0
  20. package/assets/model-icons/longcat-color.svg +1 -0
  21. package/assets/model-icons/manifest.json +739 -213
  22. package/assets/model-icons/microsoft-color.svg +1 -0
  23. package/assets/model-icons/mistral-color.svg +1 -0
  24. package/assets/model-icons/nousresearch.svg +1 -0
  25. package/assets/model-icons/nova-color.svg +1 -0
  26. package/assets/model-icons/nvidia-color.svg +1 -0
  27. package/assets/model-icons/perplexity-color.svg +1 -0
  28. package/assets/model-icons/sensenova-color.svg +1 -0
  29. package/assets/model-icons/skywork-color.svg +1 -0
  30. package/assets/model-icons/snowflake-color.svg +1 -0
  31. package/assets/model-icons/stepfun-color.svg +1 -0
  32. package/assets/model-icons/tencent-color.svg +1 -0
  33. package/assets/model-icons/tii-color.svg +1 -0
  34. package/assets/model-icons/upstage-color.svg +1 -0
  35. package/assets/model-icons/voyage-color.svg +1 -0
  36. package/assets/model-icons/xiaomimimo.svg +1 -0
  37. package/assets/model-icons/yandex.svg +1 -0
  38. package/assets/readme-architecture-en.svg +95 -0
  39. package/assets/readme-architecture-zh.svg +95 -0
  40. package/assets/readme-sync-en.svg +81 -0
  41. package/assets/readme-sync-zh.svg +81 -0
  42. package/assets/screenshots/en/01-overview.png +0 -0
  43. package/assets/screenshots/en/02-heatmap.png +0 -0
  44. package/assets/screenshots/en/03-model-usage.png +0 -0
  45. package/assets/screenshots/en/04-request-logs.png +0 -0
  46. package/assets/screenshots/en/05-cost-settings.png +0 -0
  47. package/assets/screenshots/zh/01-overview.png +0 -0
  48. package/assets/screenshots/zh/02-heatmap.png +0 -0
  49. package/assets/screenshots/zh/03-model-usage.png +0 -0
  50. package/assets/screenshots/zh/04-request-logs.png +0 -0
  51. package/assets/screenshots/zh/05-cost-settings.png +0 -0
  52. package/cordis.patch.yml +5 -5
  53. package/lib/aggregation.js +1850 -1112
  54. package/lib/client.js +1 -1
  55. package/lib/http.js +417 -385
  56. package/lib/icons.js +40 -0
  57. package/lib/ledger.js +1000 -531
  58. package/lib/plugin.js +348 -327
  59. package/lib/pricing-runtime.js +645 -551
  60. package/lib/pricing.js +1488 -1412
  61. package/lib/records-cache.js +113 -0
  62. package/lib/session-sync.js +976 -841
  63. package/lib/usage-core.js +33 -10
  64. package/lib/version-check.js +133 -133
  65. package/package.json +7 -3
  66. package/screenshots.json +10 -3
  67. package/assets/screenshot-1.png +0 -0
  68. package/assets/screenshot-2.png +0 -0
  69. package/assets/screenshot-3.png +0 -0
package/CHANGELOG.md CHANGED
@@ -1,397 +1,472 @@
1
1
  # Changelog
2
2
 
3
- All notable changes to `dsh-all-usage` are documented here.
4
-
5
- ## [1.1.18] - 2026-10-07
6
-
7
- ### Added
8
-
9
- - **A seven-day range.** The time-range presets gain 近 7 天 / Last 7 Days next to today, 30 days, 90 days and all time; it scopes the summary, trend, model tables, audit log, filters and CSV export like every other preset (seven calendar days, today included), and the choice persists in the browser like the others.
10
- - **A switchable heatmap span.** The activity heatmap no longer forces 53 columns: 30 天 / 90 天 / 12 个月 buttons redraw it with 5, 13 or 53 week columns. The grid follows the span (each column is a week and every cell keeps a 1:1 aspect ratio), so a shorter span also gets taller, readable cells instead of 10px squares. The span is stored with the other UI preferences and is independent of the time range above: changing the range never resizes the heatmap and vice versa.
11
- - **A version indicator in the header.** The dashboard title now carries the running plugin version: “已是最新 / Up to date” in small text when it matches the newest release on npm, “最新 vX.Y.Z / Latest vX.Y.Z” highlighted when it is behind, and clicking it opens the GitHub repository. The host performs the check (`GET /api/all-usage/version`, one read-only request to `registry.npmjs.org`, cached for 6 hours, never initiated by the page itself), so a desktop shell that cannot reach the network still shows its running version, and the refresh button forces a fresh check. An unreachable registry or an unparseable tag degrades to “unknown” (the chip shows the version alone) rather than claiming either verdict.
12
-
13
- ## [1.1.17] - 2026-10-01
14
-
15
- ### Fixed
16
-
17
- - **Writes work in the DSH desktop client again.** The write routes (price save, immediate sync, holiday fetch, alias) required an `Origin` header equal to the loopback host. The desktop shell serves the UI on its own scheme and forwards page requests through Electron, which strips `Origin` along with `Host`, `Cookie` and `Sec-Fetch-*`, so every desktop write was rejected with 403: saving prices reported "no permission", the models.dev catalog could never be synced, and the auto-sync checkbox rolled back. Writes are now authorized by the process capability alone — an unguessable secret that a cross-origin page can neither read (the read routes send no CORS headers) nor attach to a form POST — while a present `Origin` must still be loopback or the shell's own scheme, and `Host` must still be loopback so a rebound DNS name cannot reach the API.
18
- - **A missing `usageBacked` no longer reads as a verdict.** A host that predates the merged price table sends no `usageBacked`, and the panel rendered that as "no ledger usage" even for rows with recorded calls; it now degrades to "usage unknown" (distinct flag, row style and footer count), and says so when the payload carries no model list at all.
19
- - **A rotated write capability heals itself.** The status poll now carries a derived `capabilityId` (never the capability itself), the page treats a rotation as a full refresh, and a write rejected with 403 re-reads the capability from the full snapshot and retries once before reporting anything; the message now names a stale capability instead of claiming a permission problem.
20
-
21
- ### Added
22
-
23
- - `GET /api/all-usage/status` reports the running plugin version and the derived capability id, so a page served by a newer package than the running host can say "host plugin vX / this page vY differ — restart DSH" instead of leaving that mismatch invisible.
24
-
25
- ## [1.1.16] - 2026-09-29
26
-
27
- ### Added
28
-
29
- - **DSH 0.2.0-rc.2 is a supported runtime.** The real Cordis smoke passes on it — every route, the session hooks, the ledger flush, the revision fast path and disposal — so `dsh.compatibility.runtime` gains `>=0.2.0-rc.2 <0.2.1-0`, `0.2.0-rc.2` joins the verified list (now six runtimes) and the CI smoke matrix (Node 22/24, twelve jobs). The line keeps the 0.1.7 Cordis pairing (cordis 4.0.4 / loader 1.0.5 / timer 1.1.6) and the abstract settings seam instead of the file-backed provider, which is exactly what the smoke runs against. README (both languages, including the badge and the verified matrix) follows the new declaration.
30
- ## [1.1.15] - 2026-09-28
31
-
32
- ### Added
33
-
34
- - **Chinese statutory holidays can price as a full off-peak day.** A peak plan now carries an explicit `holidays` date list (China Standard Time calendar days: validated, sorted, de-duplicated, at most 400 entries, and part of the policy hash so the existing audit/repricing rules apply unchanged). On those days no rule can claim an instant, and each cost snapshot records `pricingHoliday`, so the ledger distinguishes a holiday off-peak band from an ordinary one. The panel exposes it as a switch plus a date list that also accepts `start..end` ranges, and `POST /api/all-usage/pricing/holidays` fetches one year of the State Council arrangement (the holiday-cn dataset: jsDelivr first, GitHub raw as a fallback, 24-hour host-side cache) so the list can be filled with one click; the fetch writes nothing — only saving freezes the dates (plus a provenance note that stays out of the policy hash) into the plan. The built-in DeepSeek peak plan now ships the 2026 arrangement itself (33 off-days taken from the State Council notice, with the provenance stored next to the dates), because DeepSeek's own rule is "Monday to Friday excluding Chinese public holidays": first-party and mapped DeepSeek routes therefore price holidays as off-peak with no configuration at all, and snapshots priced under the previous plan are migrated once. The official wording keeps weekends off-peak in full, so swapped-in working weekends (调休) need no extra handling.
35
-
36
- ### Changed
37
-
38
- - **Cost Settings is now one editable price table.** The separate "Model mappings" and "Explicit price overrides" sections are gone: every ledger model gets a row whose four price boxes write that model's manual price, whose official-model column sets or clears the identity mapping (and previews the target rates immediately), and whose two chips expand a context-rate-band editor and a UTC peak/off-peak editor. Rows that exist only in the saved configuration (an override or mapping with no ledger usage yet) are returned with `usageBacked: false` so they stay visible, editable and removable.
39
- - **Peak/off-peak plans are editable per model.** `GET /api/all-usage/pricing` now carries `temporalSchedules` (deduplicated like `tierSchedules`) plus `temporalExplicit`, `temporalBuiltin` and `temporalConfigInvalid` on every row, so the panel can show whether a row uses the built-in DeepSeek table or a custom plan, copy the built-in table for customisation, restore it, or drop the plan. Changing a plan reconciles that model's history against the new policy (the documented audit semantics); price edits still never rewrite existing positive costs, and the panel says so.
40
-
41
- ### Fixed
42
-
43
- - The pricing configuration round-trips `providerId` and `temporalPricing` on overrides. Dropping `providerId` from the snapshot turned a provider-scoped manual price into a provider-agnostic one on the next save and stopped it from being found as a mapping target, so a saved mapping could silently fall back to the catalog price.
44
- - `GET /api/all-usage/pricing/models` returns `rates`, `providerName`, `tiered`/`tierCount` and `builtinTemporal` per match, so the mapping column can preview a price before saving.
45
-
46
- ### Internal
47
-
48
- - The compact `/api/all-usage` snapshot keeps its shape: temporal detail, schedule ids and configuration-only rows are only emitted for the detailed pricing payload, and `test/pricing-editor.test.js` guards that boundary.
49
- ## [1.1.14] - 2026-09-27
50
-
51
- ### Fixed
52
-
53
- - **`deepseek-flash` gets the DeepSeek peak/off-peak plan.** models.dev lists it (DeepSeek V4.1 Flash) in the same `deepseek-flash` family and at the same standard rates as `deepseek-v4-flash`, but the built-in table only carried the v4 ids, so every record for that model was priced flat and the cost panel reported `temporalExemptReason: "no-temporal-profile"` — a peak-hour request was under-reported by roughly 57%. The table now declares the model; the off-peak rates still come from the live catalog entry, and the plan still applies to first-party routes only. Records already stored with that exempt verdict are migrated once against their own usage instant by `reconcileTemporalPricing()` and written back to the ledger, so existing history is corrected on the first start after upgrading. Reported by @baileyh8 in #2.
54
-
55
- ## [1.1.13] - 2026-09-25
56
-
57
- ### Added
58
-
59
- - **DSH 0.1.7-rc.2 is a supported runtime.** The real Cordis smoke passes on it — every route, the session hooks, the ledger flush, the revision fast path and disposal — so `dsh.compatibility.runtime` gains `>=0.1.7-rc.2 <0.1.8-0`, and `0.1.7-rc.2` joins the verified list and the CI smoke matrix. The DSH desktop client runs this runtime line, so the caption-strip work from v1.1.11/v1.1.12 is covered by the same declaration.
60
-
61
- ### Changed
62
-
63
- - The runtime smoke is version-aware about settings. 0.1.7 replaced the file-backed `@deepseek-ai/dsh-settings-file` provider with an abstract settings seam that no runtime package instantiates; the smoke runs without a settings service on that line and asserts that the plugin stays healthy without one, which is the behaviour the plugin already implements (`ctx.settings` is optional).
64
- - `0.1.5-rc.2` is covered by the CI smoke matrix instead of being verified locally only, so the verification matrix has no exception left.
65
- - `scripts/check-package.mjs` now fails when a runtime is called verified without appearing in both the CI matrix and the smoke profile table, so a declaration can no longer outrun its coverage.
66
-
67
- ## [1.1.12] - 2026-09-25
68
-
69
- ### Fixed
70
-
71
- - **The runtime smoke test no longer counts routes by hand.** It asserted that dispose removed exactly 9 exact routes, so v1.1.11 — which adds `/api/all-usage/client-env` — failed every Cordis smoke job and, with it, its own npm publication, while the published package itself was correct. The expected count is now derived from the route list the test already checks, so adding a route can no longer leave a stale number behind.
72
-
73
- ### Note
74
-
75
- - v1.1.11 was published to GitHub Releases but never reached npm: the publish workflow checks out the release tag and verifies it, and a published tag is never moved. The desktop caption-strip fix reaches npm users with this release; the shipped runtime code is identical to v1.1.11.
76
-
77
- ## [1.1.11] - 2026-09-25
78
-
79
- ### Fixed
80
-
81
- - **The desktop client's own close button is no longer covered.** The usage sheet is a full-window overlay, so its backdrop and grabber bar were painted straight across the 40px caption strip the DSH desktop client keeps for its window buttons (minimise / maximise / close), dimming the client's close button behind the panel. When the host marks the document with `data-windows-titlebar`, the sheet now starts below `--dsh-windows-titlebar-height` (40px on Windows), so the strip stays clear. Browsers are unaffected: without the marker nothing changes.
82
- - **The strip reservation never actually applied.** `Number(localStorage.getItem('dsh-all-usage:topInset'))` reads an unset key as `0`, which passed the `>= 0` check and returned early — the host marker, the Window Controls Overlay geometry and the user-agent fallback were dead code, so the panel reserved nothing on any desktop host even though the logic was in place. An unset key is now distinguished from an explicit `0`.
83
-
84
- ### Changed
85
-
86
- - The reserved strip is taken from the host's own declaration first (`data-windows-titlebar` plus `--dsh-windows-titlebar-height`), then the Window Controls Overlay rectangle, then the user agent. `localStorage['dsh-all-usage:topInset']` still overrides everything, so the strip can be tuned without a rebuild.
87
- - The client reports its environment through the new `GET /api/all-usage/client-env` route, surfaced as `clientEnv` in `/api/all-usage/status`: user agent, overlay height, the reserved strip **and where that number came from**, viewport, and the panel's own rectangles. It reports at load and again whenever the sheet opens — this is what exposed the reservation bug from outside the desktop app.
88
-
89
- ## [1.1.10] - 2026-09-14
90
-
91
- ### Fixed
92
-
93
- - **The ledger fast path is back.** DSH 0.1.5 renamed `sessionPersistence.listSnapshots()` to `list()`, so the plugin never obtained a per-session revision: `sessionsSkippedByRevision` stayed at 0 and every baseline re-read every session log (431 reads / ~13 minutes for 479 sessions on the maintainer's host). Both spellings are now accepted; against a real 0.1.5 service the plugin reports `persistenceSnapshotsAvailable: true` and skips unchanged sessions again.
94
- - **A registry change during a baseline is no longer dropped.** `synchronizeWorkspaceRegistry()` returned early while `scan.done` was false, so a workspace added during the (previously very long) scan stayed invisible until the next restart. Such a change is now remembered and replayed as soon as the scan settles.
95
-
96
- ### Changed
97
-
98
- - The workspace registry is additionally polled every 30 seconds, so a missed `domain/changed` event can no longer leave the workspace list stale until a restart.
99
- - `/api/all-usage/status` exposes `workspaceProbeEvents`, `workspaceSyncRuns`, `workspaceSyncAdded`, `workspaceSyncRemoved` and `workspaceSyncDeferred` (cumulative, not reset by a baseline) so the probe can be verified on a live host.
100
-
101
- ### Note
102
-
103
- - The first start after upgrading still reads every log once: ledger records written before this release carry no `lastRevision`, so the revision comparison has nothing to match against. From the next start on, unchanged sessions are applied from the ledger without re-reading their logs.
104
-
105
- ## [1.1.9] - 2026-09-12
106
-
107
- ### Fixed
108
-
109
- - Deleting a workspace no longer erases its recorded usage. The registry probe used to call `removeSession()` for every session of a deregistered workspace and drop their ledger rows, and recovery skipped any row whose `sourceCwd` no longer mapped to a registered workspace; together they destroyed history the durable ledger had already recorded. (v1.1.6 fixed a different path — the DSH 0.1.5 `session.events` removal that wrote empty ledger records.)
110
-
111
- ### Changed
112
-
113
- - Usage from every removed workspace is now summed into a single **Deleted** row instead of vanishing, and one row per dead workspace is no longer created. Retiring a workspace re-keys the live aggregate in place through the new `aggregation.retargetSession()`, so usage folded from the live feed that had not reached the ledger yet survives too.
114
- - Persisted ledger rows keep their original workspace id and cwd: retargeting happens on the in-memory copy only, the bucket is re-derived from the ledger after every restart, and a row with no usage never advertises the bucket.
115
- - The strict registration boundary is unchanged — sessions in unregistered or deleted directories are still excluded from new statistics — and legacy `unregistered:` rows are still never resurrected. Re-registering a workspace keeps its old history in the Deleted row while new sessions count under the live workspace.
116
-
117
- ## [1.1.8] - 2026-09-11
118
-
119
- ### Fixed
120
-
121
- - README.md still quoted the pre-0.1.5 compatibility declaration (`>=0.1.1-rc.1 <0.1.2`) and a verification matrix that stopped at 0.1.1, contradicting the shipped `package.json` after v1.1.6 and v1.1.7. Both language sections now record the corrected per-tuple range and list `0.1.5-rc.1` in the verified matrix.
122
-
123
- ### Changed
124
-
125
- - The CI runtime smoke matrix now covers `0.1.5-rc.1` alongside `0.1.1-rc.2` and `0.1.1-rc.1`, and selects the Cordis/loader/timer line per runtime version (0.1.5-rc.1 ships cordis 4.0.2 / loader 1.0.3 / timer 1.1.4) instead of pinning a single hard-coded set.
126
-
127
- ## [1.1.7] - 2026-09-11
128
-
129
- ### Fixed
130
-
131
- - The declared DSH runtime compatibility range now carries an explicit prerelease branch per tuple (`>=0.1.1-rc.1 <0.1.5-0 || >=0.1.5-rc.1 <0.1.6-0`). node-semver only lets a prerelease version satisfy a range when a comparator shares its `major.minor.patch` tuple and itself carries a prerelease tag, so the previous broad-looking `>=0.1.1-rc.1 <0.1.6` silently reported `0.1.5-rc.1` — the runtime this release targets — as unsupported.
132
-
133
- ## [1.1.6] - 2026-09-11
134
-
135
- ### Fixed
136
-
137
- - DSH 0.1.5 compatibility: that runtime replaced the Session `events` getter with `snapshotEvents()`, so the live flush path read an empty event log and folded every flushed session into an empty ledger record (`lastSeq: -1`, no turns, no usage). The dashboard stayed correct because live totals come from the `session/event` hook, but the durable "usage survives session deletion" guarantee silently degraded and restart lost the zero-rescan fast path. Event access now prefers `session.events` and falls back to `session.snapshotEvents()`, leaving 0.1.1 behaviour unchanged.
138
- - The runtime smoke test now asserts the persisted ledger keeps the flushed session's sequence and usage, and covers 0.1.5-rc.1 alongside the 0.1.1 line, so this regression cannot return unnoticed.
139
-
140
- ### Changed
141
-
142
- - Declared DSH runtime compatibility widened to `>=0.1.1-rc.1 <0.1.6`, with `0.1.5-rc.1` added to the verified list; the runtime smoke test pins the Cordis/loader/timer lines per runtime version.
143
-
144
- ## [1.1.5] - 2026-09-07
145
-
146
- ### Added
147
-
148
- - Workspace registries are now kept fresh by an automatic **probe**: all-usage listens to DSH's `domain/changed` event for the workspace domain and, on any durable registry write (create/delete/rename/reorder/archive/attach/detach), rereads `workspaceRegistry.list()`. The sync is incremental — only added workspaces are scanned (the sessions that belong to them) and removed workspaces have their sessions subtracted via inverse folding, while unchanged workspaces reuse their computed aggregates and ledger with zero rescan. The manual Refresh workspaces control, its `POST /api/all-usage/workspaces/refresh` route, and the refresh-workspaces client button were removed. Usage remains intentionally restricted to registered workspaces; unregistered cwds are ignored.
149
-
150
- ### Fixed
151
-
152
- - Usage is strictly limited to DSH-registered workspaces: sessions whose cwd does not map to `workspaceRegistry.list()` (including existing directories absent from the registry) are ignored, and legacy `unregistered:` ledger rows are never resurrected as history.
153
- - Ledger rows whose persisted `sourceCwd` no longer maps to a registered workspace (for example the session directory was removed) are skipped during recovery instead of being restored as stale data; the workspace source path is now persisted with every ledger record so restart recovery can validate ownership.
154
-
155
-
156
- ## [1.1.4] - 2026-09-03
157
-
158
- ### Added
159
-
160
- - DeepSeek peak/off-peak billing as a first-class, versioned **temporal pricing plan**: the cost schema is v2, and each cost snapshot records the billing instant, its time source (request-context / usage-event), the UTC band (peak / off-peak), the policy id, and a policy hash. Request logs show the band and UTC billing time, and the cost settings panel marks which usage models follow the DeepSeek band plan.
161
- - Built-in first-party DeepSeek profiles (deepseek-v4-flash, deepseek-v4-flash-vision-exp, deepseek-v4-pro) with explicit per-band rates for the UTC windows 01:00-04:00 and 06:00-10:00 on weekdays; the peak rates are stored as data, never derived as an automatic discount rule.
162
- - UTC band boundaries are half-open: 00:59:59 off-peak, 01:00:00 peak, 03:59:59 peak, 04:00:00 off-peak, 06:00:00 peak, 10:00:00 off-peak; weekends are off-peak. Band selection uses UTC fields only, so viewer timezones, DST, and fractional-hour zones cannot change a result.
163
- - Deterministic reuse: a later usage sample for the same turn/step reuses the previous cost snapshot only when identity, token buckets, band, policy id/hash, and applicability all match, so a message crossing a peak boundary never inherits the chunk price.
164
- - Requests preferred as the billing instant when the request/context time matches the usage turn/step; otherwise the usage event time is the auditable fallback. Both are persisted in the snapshot.
165
- - Controlled DeepSeek temporal reconciliation: first-party DeepSeek snapshots are re-priced against the usage instant on baseline/backfill/sync, v1 snapshots are migrated to the v2 shape in memory and in the persisted ledger, and a plan whose effective window does not cover the instant fails closed as unsupported (temporal-price-history-unavailable) instead of guessing.
166
- - Route safety boundary: the band plan applies only to first-party deepseek routes or routes explicitly mapped to the DeepSeek official entry; reseller routes (OpenRouter and other gateways) keep the static official price and are labelled route-not-official.
167
-
168
- - Respect the official effective instant: the built-in DeepSeek band plan starts exactly at 2026-08-16T16:00:00Z (V4-Flash-Vision-Exp at 2026-08-21), and usage before it fails closed as unsupported instead of inheriting today's V4 rates.
169
- - Persist the request-context archive inside ledger records and seed incremental folds from it after a restart, so an open request whose context arrived in a previous batch keeps its peak/off-peak billing instant.
170
- - Clear stale request-context instants and route identity on full folds (baseline rebuilds and live resyncs), so a history rewrite that removes a request no longer leaves its band attached to later usage.
171
- - Keep priced v2 snapshots auditable across catalog refreshes: the policy hash no longer includes live entry rates and reconciliation never rewrites priced history; an explicit repriceTemporal=true request re-estimates every snapshot.
172
- - Support an ordered policy archive (policies: [...]) with non-overlapping effective windows, reject overlapping rules across rules (JSON array order can never decide a price), keep an invalid temporal config visible and fail closed instead of silently falling back to the built-in profile, and compare temporal plans in the duplicate-candidate check so conflicting band plans resolve ambiguous.
173
- - Include pricingAt, pricingTimeSource, band, policy id and policy hash in the reconciliation equivalence check and in the CSV audit export.
174
-
175
- - Persist the invalid temporal-config sentinel through the serialize/normalize cycle: a rejected temporalPricing edit stays fail-closed across restarts instead of re-enabling the built-in DeepSeek profile.
176
- - Rebuild a session atomically on authoritative live resyncs: usage samples, turn records, date indexes and cost aggregates are removed before the snapshot is folded, so usage deleted by a rewritten history disappears from the aggregate (lagging snapshots keep upsert semantics until the follow-up resync aligns).
177
- - Base the live resync cursor on the snapshot tail (merged with an absorbed fallback event) instead of the previous cursor, so truncated or rewritten history cannot skip over missing events that arrive later.
178
- - Accept deepseek-official as a first-party catalog provider in the official provider rules, so its entries price and follow the band plan like deepseek.
179
- - Validate that a priced snapshot's instant is still covered and band-consistent when deciding whether a policy still matches: snapshots that slipped into a gap (same id/hash, window moved) fail closed instead of staying priced.
180
- - Include pricingAt and pricingTimeSource in the live/incremental reuse check, so audit metadata refreshes when the request instant or its source changes even within one band.
181
- - Use real LRU eviction (delete + set) for the bounded request-context archive, applying the same 512-entry cap to the live fold, the persisted ledger, and deserialization.
182
-
183
- - An authoritative live resync (full snapshot or a snapshot that provably contains the triggering event with a monotonic sequence) rebuilds the session's ledger record from the snapshot, replaces it in state and persists it, so a restart after a live history rewrite cannot resurrect deleted usage; persist failures keep explicit dirty/recovery state and surface as resync-ledger-persist-failed.
184
- - Live resync authority is now proof-based: snapshots that only have a higher tail, or events without a usable sequence, are treated as lagging (upsert the event, stay pending, align on the follow-up resync) instead of being destructively replaced.
185
-
186
- - Show real vendor brand icons instead of neutral dots for models in the request log, the selected-call details, the model summary table, and the cost-settings match table. The 11 brand SVGs live in assets/model-icons/ with a manifest (provider aliases, model prefixes, exact overrides, source and licence) and are validated and embedded as data: URIs by scripts/build-client.mjs, so the runtime never reads local paths or the network. Model namespaces win over the DSH provider name so reseller and gateway routes are attributed by the model they actually served; unknown or mixed-brand rows keep the neutral fallback, and workspace colour dots plus chart legend dots are unchanged.
187
-
188
- - Harden the model brand icons: the load-failure flag is scoped to one icon identity (a shared component that later shows another brand retries instead of staying neutral), an unrecognised actualModel no longer inherits the requested model's brand, model prefixes match on token boundaries only (o10-preview and o3x stay neutral), the resolution cache is bounded, and labelled icons expose the brand through the image's accessible name instead of a title on an aria-hidden host.
189
- - Build-time SVG validation now decodes XML entities before checking, rejects unquoted URI attributes, external CSS url()/image-set() and @import targets (including escaped/commented/CDO-CDC/namespaced <svg:style> forms), DOCTYPE/CDATA/processing instructions and every external-resource element; the CSS checks run only on style attributes and <style> bodies so icon text and comments stay inert. It lives in scripts/svg-guard.mjs so the tests exercise the exact same guard with adversarial fixtures.
190
- - Bundle the icon provenance required for release: the manifest pins the upstream commit, records per-file upstream paths and modification flags, and assets/model-icons/LICENSE.upstream-lobe-icons.txt ships the MIT notice, copyright, trademark disclaimer and the local modifications. The build fails if the pinned revision, licence file or per-file attribution is missing.
191
-
192
- ### Fixed
193
-
194
- - Hardened the model-icon build guard after an independent audit: the CSS checks now reject CDO/CDC-adjacent `@import`, external `@import` inside namespaced `<svg:style>` elements, CSS-escaped `url(` identifiers and external `image-set()` targets, all matching Blink's own tokenizer. Style-attribute `url(...)` references are checked on both the raw and entity-decoded document so `&quot;` quoting cannot split a payload while encoded `@import` is still caught after decoding.
195
- - CSS `url()`/`image-set()` checks now run only on style attributes and `<style>` bodies, so provenance comments, `<text>` content and CSS comments stay inert (no false positives); the adversarial fixtures assert the rejection reason instead of accepting any error.
196
- - The model filter dropdown now renders the vendor brand icon for each model option (actual model first, neutral fallback for unknown/private models) instead of the generic line icon, and the three usage-dashboard screenshots were refreshed to the current UI.
197
-
198
- ## [1.1.3] - 2026-09-01
199
-
200
- ### Added
201
-
202
- - Make Cost Statistics tier-aware: show expandable official rate schedules and support validated context bands in explicit price overrides.
203
- - Added deterministic usage invariants and a redacted fixture replay command covering failed requests, orphan usage chunks, cache buckets, replacement semantics, and exact expected totals.
204
- - Added a Node 22/24 CI matrix with package-content checks and required real DSH runtime smoke coverage for DSH 0.1.1-rc.1 and 0.1.1-rc.2.
205
- - Added community issue templates for data inconsistencies, plugin startup failures, and cost calculation issues.
206
-
207
- ### Performance Optimizations
208
-
209
- This release reduces historical scan work, storage write amplification, and Dashboard render churn while preserving durable-ledger recovery guarantees.
210
-
211
- - Avoid rebuilding and writing a clean session's derived ledger at `session/flush`; queue and coalesce dirty records, then drain pending writes during disposal.
212
- - Store derived ledger records in 32 stable-hash JSON units so a session update rewrites only its shard; retain the legacy single-unit migration path.
213
- - Build scoped query and fixed 53-week heatmap results from ingest-time date/workspace/model cubes; use exact BigInt decimal accumulators and a minimal heatmap projection instead of rescanning usage rows or parsing cost strings on cold reads.
214
- - Isolate heatmap pointer tracking from the dashboard render path: memoize the 53-week calendar, cells, lookup maps, charts, records, and pricing dialog, while tooltip coordinates are coalesced through refs and requestAnimationFrame, with a throttled fallback for background tabs.
215
- - Generate the shipped browser entry from readable `src/client.js` with pinned Terser during prepack, reducing the current raw client artifact from about 276 KB to about 177 KB; transport gzip/Brotli remains a DSH host concern.
216
-
217
- ### Correctness
218
-
219
- - Keep the usage ledger debounce timer referenced while write waiters are pending: an unreferenced timer let the event loop settle before `persistLedgerRecord` could run, cancelling Node 22 test runs and short-lived processes.
220
- - Derive the latest session sequence from the tail event instead of scanning the full session log on live flush and again while building the ledger record.
221
- - Use the previous ledger cursor for safe append-only tail folding and fall back to a full rebuild when sequence continuity or turn replacement is uncertain.
222
- - Wait for the persisted pricing configuration (and the ledger it backfills) before serving pricing reads and writes, so an early API call can neither report an empty config nor be silently overwritten by the load.
223
- - Force a full historical rebuild when the workspace behind a session path was recreated with a different id; the previous record can no longer be reused under the wrong workspace, and ledger rows whose top-level workspace differs from their historical items are marked unusable and rebuilt once.
224
- - Keep the pricing refresh classification order intact: a pricing revision change always wins over a concurrent data change so summary costs and the open settings panel cannot consume a partially-consumed baseline.
225
- - Refresh the open cost-settings panel when the pricing revision changes, merging fresh catalog data while preserving unsaved local mappings and overrides.
226
- - Invalidate every in-flight official-model search when a mapping row is deleted (row generation bump), so a stale response can never populate a shifted row even when per-index sequence numbers collide.
227
- - Skip the pricing revision bump when a catalog sync succeeds with an unchanged catalog hash and no newly priced usage.
228
- - Round hourly bucket indices against the requested range so fractional-hour zone offsets (Lord Howe) never drop or merge events, and emit real bucket boundaries as trend labels so spring-forward hours carry the true 03:00 label instead of drifting 30 minutes off.
229
- - Mark mixed-workspace ledger records as unfoldable so both the scan tail path and session flush rebuild them instead of persisting historical items under the wrong workspace.
230
- - Exclude the per-fetch fetchedAt stamp from the catalog content hash, so syncing identical models.dev contents keeps pricingRevision (and query caches) stable.
231
- - Wait for the persisted pricing and ledger state before serving the full snapshot, so the first payload cannot embed a default empty pricing summary.
232
- - Bump the official-model search row generation when the settings panel closes, so in-flight responses cannot populate a reopened panel.
233
- - Require safe-integer event sequences everywhere sequences gate folding or fast-forward comparisons, so contract-external values such as Infinity cannot freeze later flushes or poison the live-event cursor.
234
- - Keep the mixed-workspace upgrade flag through pricing backfills too: repairing costs must never re-enable tail folding of a record whose historical items still belong to another workspace.
235
- - Invalidate in-flight official-model searches whenever the catalog is replaced (manual sync, open-panel refresh, close/reopen), not only when a mapping row is deleted.
236
- - Normalize live-fallback sequences to non-negative safe integers and give positional keys to invalid events, so contract-external sequences cannot overwrite ledger turns or poison the live cursor; ledger rows with non-safe sequences are marked for rebuild.
237
- - Sort the catalog by its stable key before truncating, so models.dev catalogs above the entry cap keep the same models and content hash regardless of upstream object order.
238
- - Enforce the ledger sequence contract (-1 sentinel or non-negative safe integer) when loading persisted records, flag old Infinity/NaN/negative keys and sequences for rebuild, and never clear the rebuild flag during cost repacking or ledger recovery.
239
- - Resolve normalized duplicate catalog entries deterministically (stable sort with conflict groups comparing full content, provider id case-normalized) so enumeration order can no longer change which price survives.
240
- - Extend the ledger sequence contract to every write and read path: oversized numeric keys (past Number.MAX_SAFE_INTEGER) and unsafe numbers in usage/turn data are normalized at the entry points and flagged for rebuild; the rebuild reason is persisted so a source-read failure followed by a restart can never silently reuse a polluted record.
241
- - Persist the rebuild reason for legacy-version and invalid-updated-at records too, not only sequence/workspace damage, so a source-read failure followed by restart cannot fast-path a record that was upgraded in memory.
242
- - Recognise any non-canonical numeric text (floats, exponents, negatives) in old event keys as pollution, and forbid both memory and ledger tail-folding when an incremental log contains invalid sequences anywhere before the fold start.
243
- - Refuse to tail-fold a flush whose log tail does not advance and whose history contains invalid sequences: the whole usage index is rebuilt from source so the in-memory aggregate and the persisted ledger cannot disagree.
244
- - Detect invalid sequences on every dirty flush (not only when the tail did not advance), so a history rewrite followed by a normal appended tail also rebuilds instead of folding stale in-memory rows.
245
- - Persist an invalid-flush-sequence rebuild flag on the stale ledger record before triggering the full rebuild, so a lagging persistence revision cannot fast-path the baseline back to the old data.
246
- - Match Infinity/NaN pollution only as bare whole values, keeping composite keys such as Infinity-session:step:1:1 on the revision fast path.
247
- - Keep the server-persisted pricing auto-sync flag as the single truth: the client stops seeding the draft from its own local UI state, so saving unrelated settings cannot revert the server value.
248
- - Move in-flight official-model search state (timers, sequence guards, results) when mappings are deleted, so a stale response can no longer populate a shifted row.
249
- - Restrict pricing revision bumps to cost-affecting changes: sync attempts and failed attempts no longer invalidate scoped query caches or records cursors, while the client treats pricing changes as a full snapshot refresh so summary costs stay fresh.
250
- - Bucket hourly rows by the local hour boundary at the event's own offset, keeping both repeated hours of a DST fall-back day distinct.
251
- - Add a CI guard that rejects a missing or untracked generated client bundle (`git ls-files --error-unmatch`) before the determinism diff.
252
- - Initialized the durable ledger revision clock and reject persisted ledger records whose `updatedAt` is not finite.
253
- - Require HTTP socket peers to be loopback before accepting a request, including IPv4-mapped loopback addresses.
254
- - Preserve route-specific pricing mappings through the `identityKey` field, while accepting legacy `usageIdentityKey` data.
255
- - Add validated context-tiered model pricing using the request input context; malformed schedules remain unsupported instead of producing a guessed estimate.
256
- - Require `webServer` before Host activation so the dashboard routes cannot silently disappear when the service mounts late.
257
- - Migrate legacy tiered flat cost snapshots to `unsupported`, reject timestamps outside JavaScript's TimeClip range, and exercise the real SessionStore append/flush firehose in runtime smoke tests.
258
- - Require manual npm recovery dispatches to provide and verify the target release tag and full commit SHA.
259
-
3
+ ## v1.2.1(2026-10-18)
4
+ ### 新功能
5
+ - **支持 DSH 0.2.1-rc.1 / rc.2** —— 新运行时可直接使用。
6
+ - **首次打开跟随 DSH 的语言** —— 按 DSH 的 Language 设置选择语言;在面板里选过之后以面板为准。
7
+ ### 修复
8
+ - **用量面板不再被第三方主题压住** —— 侧栏玻璃 / 壁纸类插件不会再盖住面板,透明主题也不会把面板底色「打洞」。
9
+ - **切换语言后看板不再变空** —— 中英切换立即刷新,不必再刷新页面;英文的「今日」趋势恢复小时曲线。
10
+ - **DeepSeek Account 用量按峰谷价计价** —— 该路由此前全天按谷价计、峰时偏少;升级后历史用量会重新计价(峰时变贵)。
11
+ ### Features
12
+ - **DSH 0.2.1-rc.1 and rc.2 are supported** — the new runtime works out of the box.
13
+ - **The panel opens in DSH's language** — it follows DSH's Language setting; a choice made in the panel wins afterwards.
14
+ ### Fixes
15
+ - **Third-party themes can no longer bury the usage panel** — sidebar glass/wallpaper plugins don't cover it, and a theme that blanks the base background can't punch holes in it.
16
+ - **Switching the language no longer empties the dashboard** — data refreshes immediately without a page reload, and English keeps its hourly curve for "Today".
17
+ - **DeepSeek Account usage is priced with peak/off-peak bands** — that route was charged the off-peak rate all day; existing rows are re-priced on the next scan (peak hours become more expensive).
18
+
19
+ ## v1.2.0(2026-10-15)
20
+ ### 新功能
21
+ - **支持 DSH 0.2.1-alpha.2** —— 声明区间追加精确版本 `0.2.1-alpha.2`;本机 Node 24 严格真实 Cordis smoke 通过(Cordis `4.0.5-alpha.1` / loader `1.0.6-alpha.1` / timer `1.1.7-alpha.1`),CI 已配置 Node 22/24、待执行;仅此精确版本,不代表其他 alpha 版本。
22
+ - **模型图标扩到 43 个品牌** —— 从 lobe-icons 固定 revision 取 32 个新商标;9 个 `currentColor` 烘成固定中性色。manifest 继续钉住上游 commit 与逐文件归属,MIT 许可与商标说明随包发布。
23
+ - **网关 / 云的包裹 id 还原真实品牌** —— 覆盖 `openai-gpt-5.5`、`databricks-claude-*`、`eu.anthropic.claude-*` 等写法,原始 id 仍优先。
24
+ - **状态接口公开内存与账目诊断** —— `/api/all-usage/status` 现在一并给出 `memory`(常驻用量明细行 / 会话记录 / 会话聚合、会话记录缓存占用估算 MiB,`heapUsedMB` / `rssMB` 为整个 Host 进程)、`counters`(诊断操作计数)、`accounting`(当日聚合一致性)与 `mismatch`(首个桶增量不一致样本,无则为 null;样本含内部用量 key 与工作区 ID,不含提示词或回复正文)。
25
+ ### 优化
26
+ - **生效起点也按北京时间** —— 该日期现在表示「那天 00:00(北京时间)」,与时段同一时区。内置 DeepSeek 文本表本来就锚定在北京时间零点(存的是前一天 16:00 UTC),改前界面显示成 UTC 日期(差一天),现在显示正确、原样保存不变;英文界面仍按 UTC,留空仍表示全部历史。
27
+ - **成本设置里的时段改用北京时间** —— 峰的时段在宿主里按 UTC 存(DeepSeek 自己的峰时是北京时间 09:00–12:00 / 14:00–18:00),中文界面现在直接按北京时间显示与填写、保存时自动换回 UTC;跨午夜的时段会拆到次日并顺延星期;英文界面仍按 UTC。
28
+ - **热力图可以用键盘操作了** —— 每个日期格子对用户就是一个按钮(读屏也按按钮播报),并带读屏可读的名字(如「2026年10月12日 周一:63 次使用」);整块日历只有**一个 Tab 停靠点**,Tab 落点跟随当前选中 / 获得焦点的日期(初始为今天,今天不在当前跨度内时取最近有使用的一天);方向键按天 / 按周移动、Home / End 到首尾、回车或空格打开当日明细,悬停提示会跟着焦点走;图例对读屏隐藏(纯装饰),说明文字补上了键盘用法。
29
+ - **热力图改成蓝色** —— 方块由 GitHub 绿换成蓝色渐变(同一个强调色按 20/45/70/96% 混进面板底色,明暗主题都保持可读),图例与「今日」光晕同步换色。
30
+ - **热力图入场动画不再拖长尾、也不再无视「减少动态效果」** —— 376 格的错峰延迟从「每格 1.2ms、最长 444ms」改为按 25 格循环(最长 **144ms**):实测挂载 700ms 后仍在跑的动画 **393 → 11**(页面静置后只剩 4 个,其中含「今日」光晕);「今日」呼吸光晕在 `prefers-reduced-motion: reduce` 下不再启动(它写在行内样式里,媒体查询原本覆盖不到)。
31
+ - **趋势图点数封顶 180** —— 半年以内按天,更长先降到 3 天一点再按需扩到周 / 月 / 季 / 年;桶不重叠不丢天,面板标注汇总粒度。实测 1 年 122 点、20 年 120 点。
32
+ - **请求日志内部滚动 + 分页** —— 首屏最近 20 条,滚到底再取 20 条(实测 20 → **40 条**)。
33
+ - **概览卡片行压缩约四分之一** —— 内边距 16→10px、大数字 26→22px 等,整行 **156 → 114px**。
34
+ - **DeepSeek 余额两位小数** —— 主数值 `¥13.8600` → **`¥13.86`**;其它币种仍四位。
35
+ - **筛选下拉不再每次渲染都重建** —— 三个菜单的选项列表改由 useMemo 派生、图标在列表构建时解析一次,菜单本身用 React.memo 包裹;真机同口径(开+关各一帧 × 20 轮)**47.96 → 29.4 ms/轮**。
36
+ - **用量明细按预算常驻** —— 用量明细不再全部常驻内存:每个会话维护一份摘要(足以把它的 token 与成本从聚合里精确扣回来),**已经定稿的行**在扫描结束后交还账本,查看「请求日志 / 当日明细」时再按需从磁盘读回;启动阶段也改为只列账本目录、按会话读单条记录(原来一次性解析全部记录)。真机 6.6 万行实测这一路径:峰值 156 MB、**静置降到 87 MB**(该数值是那次真机账本的实测样本,会随账本规模与内容变化,不代表与历史无关的总体堆占用),降前降后的日志页逐行一致、总额与逐日聚合一分不差。
37
+ - **冷启动再快一倍:小数累加走快路** —— 折叠阶段每加一个成本字段都要做两次 `10n ** k` 幂运算和三次「去尾零」规整,且每条用量的成本在装载与折叠时被**解析两遍**。改为查表取 10 的幂、省掉多余的规整、并把装载时解析出的数直接交给累加缓存后:同一份真机账本(139.3 MB / 1060 条 / 6.6 万行)的**折入 3.83 → 1.80 s**、**加载 2.8 → 2.5 s**,**完整冷启动 ~6.7 → ~4.3 s**(审计基线 8.6 s)。金额逐字校验:离线折叠与线上宿主的总成本完全一致(65,633 次计价)。
38
+ - **账本加载阶段提速约 1.5×** —— 每条用量行原先要跑两遍校验 + 两次成本快照解析(每次 11 个小数域),且「已是最新格式」的记录在装载时还会被再全量解析一遍;两处都消除后,同一份 156.7 MB 真实账本、**只计加载阶段**(读盘 + 归一化 + 展开)从 **4.2 s(审计基线)降到 2.2–3.2 s**(纯加载进程 2.2 s;聚合状态同时存活时因堆压力约 3.2 s)。这组旧基线的**完整冷启动**(含把 6.6 万条折进聚合 cube)为首次 9.8 s / 稳态 7.5 s,其中折入耗时 4.3 s 未变;后续首建扫描优化见下文。
39
+ - **首次建账复用会话观察快照** —— 在提供 `observeSession` 的 runtime 上跳过未使用的投影构建,并在扫描退出时释放观察租约;较旧 runtime 仍兼容 `readSession`。固定 570 会话的隔离对照中,ready 中位数 935.753 → 95.329 秒(−89.8%),两侧均完整读取全部会话日志,18 层原始账目一致。该计时包含独立测试宿主开销,不代表在线 DSH 启动耗时。
40
+ - **冷分页先从会话摘要构建索引** —— 冷查询先用摘要定位、筛选和排序,再按页读取所需明细;摘要不能精确描述范围时仍走完整读取回退。固定 67,328 行账本的隔离对照中,首屏中位数 2.946 → 0.287 秒(−90.2%),遍历 3,367 页为 7.187 → 4.770 秒(−33.6%),行及逐层账目一致。代价是 settled heap 增约 6.05 MiB,warm 后页 p99 中位数增加约 4.14 ms。
41
+ - **定价回填与时段核对合并为一次扫描** —— 价格同步和定价设置更新共用一次用量行遍历,避免对同一账本分别做两轮核对。固定账本隔离回放中,ready 中位数 21.883 → 18.131 秒(−17.1%),原地全量聚合重建 23.445 → 19.793 秒(−15.6%);六层原始账目一致。
42
+ - **定价快照不再每次重算路由清单** —— 价格表要遍历全部用量行(本机 6.5 万条)才能列出用过的模型;该结果改为按「数据 / 定价 revision」缓存,只在两者变化时重算(模型化实测:每次 3.4 ms → 命中约 0 ms)。
43
+ - **按范围的旧查询结果不再滞留** —— 结果键里带 revision,旧条目永远读不到却仍占内存;现在数据或定价一变就清掉(每条约 30–115 KB × 最多 20 条)。
44
+ - **首屏快照只带一份时区日历** —— 面板每次开屏会请求自己那份(本地或 UTC),不再同时传两份(原来两份各占一半);按范围的「会话数」仍来自 /query,口径不变。
45
+ - **每天的会话 id 明细只保留近期** —— 超过 180 天的日子仍带数量与聚合,但不再拖着历史上所有会话 id(面板看旧日期的日志本就按需拉取)。
46
+ - **旧存储文件不再长期占盘** —— 账本改用「一条记录一个文件」后,原来的整单位文件(本机 32 个分片 + 一个遗留大文件,合计约 178 MB)已被逐条播种成新结构;账本现在会在**确认新结构已有记录**后删除这些旧文件并留下一次性的标记,失败则下次启动重试。
47
+ - **账本体积按路由去重** —— 同一条路由(供应商 / 模型)以前在每条用量里都重复一份身份对象;改为每条记录共享一张身份表、用量只存索引。实测本机 1059 条记录 **91.3 → 74.7 MB(−18.2%)**。
48
+ - **账本改写方案已就位(性能数字来自隔离的真实账本副本基准,未测在线写入时延)** —— 会话记录改一条一文件存储,替代「每次更新重写整个分片」:实测单次更新 **273 ms → 中位 4.6 ms**(最大的会话 74 ms),整个分片目录 **13.27 → 11.88 MB**;冷启动在同一份真实数据上从 **8–9 s 降到 3.0 s**(首次含迁移 5.5 s)。
49
+ - **客户端 bundle 缩小约 7.6%** —— 品牌图标改为由宿主按需提供(`GET /api/all-usage/icon?key=`,新增品牌不再增加体积),并清掉一批无人调用的客户端函数和再也渲染不出的样式规则;`lib/client.js` 从 v1.1.18 的 257,854 B 降到 **238,143 B(约 7.6%)**。
50
+ - **渲染不再被无关状态带着重画** —— 趋势 / 热力图(376 格)入参改为稳定引用,图标几何提到模块级;真机实测整页 DOM **6266 → 1719 节点**。
51
+ - **品牌图标规则更省心** —— 图标清单改由 manifest / 目录推导,以后加品牌不用手改清单;没有品牌图标的模型显示插件自己的**层叠标记**(不是空圆点),不指向任何品牌。
52
+ ### 修复
53
+ - **修复「谷时段被记成峰时」** —— DSH 可能整场会话只发一次 `request/context`,而插件把这条**会话级**时间戳当成每次请求的计费时刻:实测 20:02 的调用按 14:10 的峰时窗口计价,峰谷**费率**与「峰/谷」标记一起错(本机 6.6 万条里有 1.6 万条受此影响)。现在会话级时间戳只在 30 分钟内有效(超时改用调用自身的时间),对账过程也会把历史里「比调用早 30 分钟以上、且该会话没有与这条记录 turn/step 对应的时间戳」的行按调用时间重新计价一次。逐请求(turn/step)时间戳的语义不变。
54
+ - **同一模型的多个路由合并为一个筛选项** —— 按模型名归并(忽略大小写与 `供应商/` 前缀),宿主同样按模型名匹配。
55
+ - **模型筛选不再显示供应商命名空间** —— 只显示模型名,取值仍是原始 id(实测 25 项里含 `/` 的为 0)。
56
+ - **供应商 / 模型下拉不再无限下延** —— 限高 `min(60vh, 420px)` 内滚,打开时滚到当前选中项。
57
+ ### Features
58
+ - **DSH 0.2.1-alpha.2 is supported** — the declared range gains the exact version `0.2.1-alpha.2`; the strict real Cordis smoke passes locally on Node 24 (Cordis `4.0.5-alpha.1` / loader `1.0.6-alpha.1` / timer `1.1.7-alpha.1`), and CI is configured for Node 22/24 pending execution; this exact version only — no other alpha versions implied.
59
+ - **43 model brands** — 32 new marks from the pinned lobe-icons revision; nine had currentColor baked to a fixed neutral. The manifest still pins the upstream commit and its per-file attribution, and the MIT licence and trademark notice ship with the package.
60
+ - **Gateway and cloud ids resolve to the real brand** — covers openai-gpt-5.5, databricks-claude-*, eu.anthropic.claude-*; the raw id still wins.
61
+ - **The status endpoint exposes memory and accounting diagnostics** — `/api/all-usage/status` now also reports `memory` (resident usage detail rows, session records and session aggregates, the estimated session-record cache size in MiB, and `heapUsedMB` / `rssMB` for the whole Host process), `counters` (diagnostic operation counts), `accounting` (today's aggregate consistency) and `mismatch` (the first bucket-delta inconsistency sample, or null; the sample carries the internal usage key and workspace ID, never prompts or reply bodies).
62
+ ### Improvements
63
+ - **The effective-from date follows Beijing time too** — the date now means 00:00 Beijing, the same zone as the windows. DeepSeek's built-in text plan already anchors at Beijing midnight (stored as 16:00 UTC the day before) and used to display as the previous calendar day; it reads correctly now and saves back unchanged. English keeps UTC, and an empty field still means the whole history.
64
+ - **Time-of-day pricing is edited in Beijing time** — the host stores peak plans in UTC while DeepSeek's own peak hours are 09:00-12:00 and 14:00-18:00 Beijing time, so Chinese now shows and accepts those times directly and converts back on save (a window crossing midnight moves to the next weekday); English keeps UTC.
65
+ - **The heatmap is keyboard-operable** — every date cell is a button to the user (screen readers announce it as one) and carries a screen-reader name (for example "2026-10-12 Monday: 63 uses"); the calendar has a **single tab stop**, so Tab lands on the selected/focused day (today initially, or the most recent day with usage when today is outside the span); arrows move by a day or a week, Home/End jump to the ends, Enter or Space opens the day's detail, and the tooltip follows focus; the legend is hidden from screen readers because it is decorative, and the note mentions the keyboard.
66
+ - **The heatmap is blue now** — cells switch from GitHub green to a blue ramp (one accent mixed into the panel surface at 20/45/70/96%, so it stays readable in both themes), and the legend and today's glow follow.
67
+ - **The heatmap entrance no longer trails and no longer ignores reduced motion** — the 376-cell stagger went from 1.2 ms per cell (a 444 ms tail) to a 25-cell wrap (144 ms); measured animations still running 700 ms after mount drop from **393 to 11** (four once the page settles, the today pulse among them), and that pulse is skipped under prefers-reduced-motion because an inline style had put it out of the media query's reach.
68
+ - **The trend chart is capped at 180 points** — a span up to half a year stays daily, a longer one drops to three days first and widens on demand; buckets never overlap or drop a day, and the panel names the stride.
69
+ - **The request log scrolls in its own box and pages as you go** — newest 20 rows first, 20 more at the end (20 to 40 measured).
70
+ - **The summary row is about a quarter shorter** — padding 16 to 10px, big numbers 26 to 22px: 156 down to 114px.
71
+ - **The DeepSeek balance reads with two decimals** — ¥13.8600 becomes ¥13.86, while any other currency keeps the four the API reports.
72
+ - **The filter menus stop rebuilding on every render** — their option lists are derived with useMemo (icons resolve once, when the list is built) and the menu itself is wrapped in React.memo; measured in the panel over 20 open/close cycles: **47.96 ms down to 29.4 ms** per cycle.
73
+ - **Usage detail rows follow a residency budget** — usage detail rows are no longer all resident: each session keeps a digest (enough to unwind its tokens and cost from the aggregate exactly), rows whose pricing is final are handed back to the ledger once a scan finishes, and the request log reads them from disk again on demand; a cold start now lists the ledger directory and reads one record per session instead of parsing every record at once. Measured on a real 66k-row ledger for this path: peak 156 MB, **steady state 87 MB** (a measured sample for that ledger that moves with ledger size and content, not a total-heap figure independent of history), with the log pages byte-identical and the totals unchanged.
74
+ - **Cold start halves again: the decimal path takes a fast route** — every accumulated cost field paid for two `10n ** k` powers and three trailing-zero normalizations, and each usage row's cost was parsed twice (once on load, once when folded). A power table, fewer normalizing passes and handing the load-time parse to the accumulator cache took the same real ledger (139.3 MB, 1060 records, 66k rows) from a **3.83 s fold to 1.80 s** and a **2.8 s load to 2.5 s**: a **full cold start of about 4.3 s instead of 6.7 s** (the audit measured 8.6 s). The money is unchanged to the last digit — an offline fold and the live host agree exactly (65,633 priced calls).
75
+ - **The ledger's load phase got about 1.5x faster** — every usage row ran two validation passes plus two cost-snapshot parses (eleven decimal fields each), and a record already in the current format was parsed in full again when it was applied; both are gone. On the same 156.7 MB of real data, the **load phase alone** (read, normalize, expand) went from the audit's **4.2 s to 2.2-3.2 s** (2.2 s in a lean process, about 3.2 s while the aggregation state is live). That earlier **full cold start** (including folding 66k rows into the cubes) measured **9.8 s on the first start and 7.5 s in steady state**, with folding unchanged at 4.3 s; the later first-scan optimization is described below.
76
+ - **First-install scans reuse observed session snapshots** — on runtimes providing `observeSession`, the scan skips unused projection construction and releases the observation lease on exit; older runtimes continue to use `readSession`. In an isolated, fixed 570-session comparison, median ready time fell from 935.753 to 95.329 seconds (−89.8%); both variants still read every session log and matched across all 18 raw accounting layers. The measurement includes the isolated harness and is not an online DSH startup time.
77
+ - **Cold request-log pages index from session digests** — cold queries use digest metadata to locate, filter and order rows, then load page payloads on demand; an incomplete digest still falls back to the full read. In an isolated 67,328-row ledger comparison, median first-page time fell from 2.946 to 0.287 seconds (−90.2%) and all 3,367 pages from 7.187 to 4.770 seconds (−33.6%), with rows and layered totals matching. The trade-off was about 6.05 MiB more settled heap and a 4.14 ms increase in median warm-page p99.
78
+ - **Pricing backfill and temporal reconciliation share one scan** — pricing sync and settings updates visit the usage rows once instead of running separate passes. In an isolated replay of a fixed ledger, median ready time fell from 21.883 to 18.131 seconds (−17.1%) and an in-place full aggregation rebuild from 23.445 to 19.793 seconds (−15.6%); all six raw accounting layers matched.
79
+ - **The pricing snapshot stops rebuilding its route list every time** — listing the models in use walks every usage row (65k here); the result is cached per data/pricing revision now and only rebuilt when either changes (modelled: 3.4 ms per call to about 0 ms when cached).
80
+ - **Superseded scope results no longer linger** — the cache key carries the revision, so an old entry could never be read again while still holding 30-115 KB apiece (up to 20); a data or pricing change clears them now.
81
+ - **The first-paint snapshot carries one timezone calendar** — the panel asks for its own copy (local or UTC) instead of both, which used to split the payload in half; per-range session counts still come from /query, so the numbers are unchanged.
82
+ - **Per-day session ids ride along only for recent history** — a day older than 180 days keeps its counts and aggregates but no longer drags every session id ever recorded (opening an old day's log already fetches it on demand).
83
+ - **The old storage files no longer stay on disk** — after the ledger moved to one document per record, each legacy whole-unit file (32 shards plus a leftover monolith, about 178 MB here) has been seeded into the new tree; the ledger now removes those files once the tree provably holds records, leaves a one-time marker, and retries on the next start if anything fails.
84
+ - **Ledger rows share one identity table per session** — a route (provider / model) used to repeat its identity object on every usage item; rows now carry an index into one shared list. Measured: 1059 records drop from **91.3 to 74.7 MB (−18.2%)**.
85
+ - **The ledger write-amplification fix is ready (benchmarked on an isolated real-ledger copy; online write latency was not measured)** — one file per session record replaces rewriting a whole shard on every update: measured **273 ms to a 4.6 ms median** (74 ms for the largest session), the shard directory drops from **13.27 to 11.88 MB**, and a cold start on the same real data falls from **8-9 s to 3.0 s** (5.5 s for the first one, which migrates).
86
+ - **The client bundle is about 7.6% smaller** — brand marks are now served by the host on demand (GET /api/all-usage/icon?key=, so a new brand no longer grows the bundle), and a batch of client helpers nobody calls plus orphaned style rules no markup can render is gone; `lib/client.js` drops from the 257,854 B of v1.1.18 to **238,143 B (about 7.6%)**.
87
+ - **Unrelated state no longer redraws the charts** — the trend chart and the 376-cell heatmap take stable props now and icon geometry lives at module level; measured: the page DOM drops from **6266 to 1719 nodes**.
88
+ - **Brand icon rules need less care** — the icon list is derived from the manifest and the directory, so a new brand needs no hand-edited list; a model without a bundled brand shows the plugin's own stacked-layers mark (never an empty dot), which implies no brand.
89
+ ### Fixes
90
+ - **Fixed calls labelled peak that actually ran off-peak** — DSH can emit `request/context` once per session, and the plugin billed every later call at that session-level instant: a 20:02 call was priced in the 14:10 peak window, so both the peak rate and the badge were wrong (16k of this machine's 66k rows were affected). A session-level stamp now only counts within 30 minutes (past that the call's own time is used), and the reconciliation pass re-prices stored rows whose stamp is more than 30 minutes older than the call and which have no turn/step stamp of their own. Per-request stamps keep their meaning.
91
+ - **Routes of one model share a single filter entry** — grouped by model name (case ignored, vendor namespace dropped), matched the same way on the host.
92
+ - **The model filter no longer shows the vendor namespace** — the model name only, while the value stays the raw id (0 of 25 entries still contain a slash).
93
+ - **The provider and model menus no longer run past the window** — capped at min(60vh, 420px) with their own scroll, and they open on the current selection (scrolled into view).
94
+
95
+ ## v1.1.18 (2026-10-07)
96
+ ### 新功能
97
+ - **「近 7 天」范围** —— 含今天的 7 个日历日,作用于摘要 / 趋势 / 模型表 / 日志 / 筛选 / CSV。
98
+ - **热力图跨度可切换** —— 30 天 / 90 天 / 12 个月 = 5 / 13 / 53 列,格子随之变大(实测 34px vs 18px);默认仍 12 个月,与时间范围互不影响。
99
+ - **标题旁版本指示** —— 显示运行版本;一致时小字「已是最新」,落后时高亮「最新 vX.Y.Z」,点击开 GitHub 仓库。
100
+
101
+ ### Features
102
+ - **A seven-day range** — seven calendar days including today; scopes the summary, trend, model tables, request log, filters and CSV.
103
+ - **A switchable heatmap span** — 30 days / 90 days / 12 months draw 5 / 13 / 53 columns, so shorter spans get larger cells (34px vs 18px measured); twelve months stays the default and the span is independent of the time range.
104
+ - **A version indicator in the header** — the running version, plus “Up to date” in small text or a highlighted “Latest vX.Y.Z”; clicking it opens the GitHub repository.
105
+
106
+ ## v1.1.17 (2026-10-01)
107
+ ### 修复
108
+ - **桌面端又能保存了** —— 写接口原要求 `Origin` 与 loopback 同源,而 Electron 转发时会删掉它;改为以进程令牌授权,`Origin` 存在时仍校验,`Host` 仍限 loopback。
109
+ - **缺 `usageBacked` 不再误判** —— 旧宿主不发该字段,面板不再渲染成「账本无用量」,改为「用量未知」三态。
110
+ - **令牌轮换自愈** —— 状态轮询带派生 `capabilityId`;写入遇 403 自动重取令牌并重试一次,文案改为「进程令牌已失效」。
111
+ ### 新功能
112
+ - `/api/all-usage/status` 增加 `pluginVersion` 与 `capabilityId`,宿主与页面版本不一致时面板会提示重启。
113
+
114
+ ### Fixes
115
+ - **The desktop client can save again** — writes required an `Origin` matching the loopback host, which Electron strips when forwarding; they are now authorized by the process capability, a present `Origin` is still validated and `Host` stays loopback-only.
116
+ - **A missing `usageBacked` is no longer a verdict** — an older host omits the field and the panel rendered it as “no ledger usage”; it now degrades to a distinct “usage unknown” state.
117
+ - **A rotated capability heals itself** — the status poll carries a derived `capabilityId`; a write rejected with 403 re-reads the capability and retries once, and the message names a stale capability.
118
+ ### Features
119
+ - `/api/all-usage/status` reports `pluginVersion` and `capabilityId`, so a host/page version mismatch can say “restart DSH” instead of staying invisible.
120
+
121
+ ## v1.1.16 (2026-09-29)
122
+ ### 新功能
123
+ - **支持 DSH 0.2.0-rc.2** —— 真机 Cordis smoke 全过;区间加 `>=0.2.0-rc.2 <0.2.1-0`,已验证增至 6 个运行时,CI 12 个作业。
124
+
125
+ ### Features
126
+ - **DSH 0.2.0-rc.2 is supported** — the real Cordis smoke passes; the declared range gains `>=0.2.0-rc.2 <0.2.1-0`, the verified list grows to six runtimes and CI to twelve jobs.
127
+
128
+ ## v1.1.15 (2026-09-28)
129
+ ### 新功能
130
+ - **中国法定节假日全天谷价** —— 峰谷计划可带显式节假日日期(支持区间写法与一键抓取当年安排);内置 DeepSeek 表自带 2026 年 33 天官方安排,官方直连与已映射行零配置生效,快照记录 `pricingHoliday`。
131
+ ### 优化
132
+ - **成本设置合并成一张可编辑价格表** —— 映射列 + 四个价格框 + 上下文档位 / UTC 峰谷编辑器;仅存在于配置的行仍可见、可改、可删。
133
+ - **峰谷计划可按模型编辑** —— 接口返回去重的 `temporalSchedules` 与每行来源标记;改计划按文档口径对账历史,改价格永不改写历史。
134
+
135
+ ### Features
136
+ - **Chinese statutory holidays price as a full off-peak day** — a peak plan can carry explicit holiday dates (ranges and a one-click fetch of the year's arrangement); the built-in DeepSeek plan ships the 2026 33-day arrangement, so first-party and mapped rows need no configuration, and snapshots record `pricingHoliday`.
137
+ ### Improvements
138
+ - **Cost Settings is one editable price table** — a mapping column, four price boxes and context-band / UTC peak editors; rows that exist only in the configuration stay visible, editable and removable.
139
+ - **Peak plans are editable per model** — the payload carries deduplicated `temporalSchedules` plus per-row provenance; changing a plan reconciles that model's history, while price edits never rewrite history.
140
+
141
+ ## v1.1.14 (2026-09-27)
142
+ ### 修复
143
+ - **`deepseek-flash` 补进内置峰谷表** —— 峰值时段成本此前被少算约 57%;已带豁免结论落盘的记录一次性迁移。报告者 @baileyh8(#2)。
144
+
145
+ ### Fixes
146
+ - **`deepseek-flash` joins the built-in peak table** — peak-hour cost had been under-reported by roughly 57%; records stored with the exempt verdict are migrated once. Reported by @baileyh8 (#2).
147
+
148
+ ## v1.1.13 (2026-09-25)
149
+ ### 新功能
150
+ - **支持 DSH 0.1.7-rc.2** —— 真机 smoke 全过,进入声明区间与 CI 矩阵(桌面客户端即该运行时线)。
151
+ ### 优化
152
+ - smoke 对 0.1.7 的抽象 settings seam 版本感知;`0.1.5-rc.2` 纳入 CI;`check-package` 会拦下「声明已验证却不在 CI 矩阵 / 档位表」的情况。
153
+
154
+ ### Features
155
+ - **DSH 0.1.7-rc.2 is supported** — the real smoke passes, and the line joins the declared range and the CI matrix (the desktop client runs on it).
156
+ ### Improvements
157
+ - The smoke is aware of 0.1.7's abstract settings seam; `0.1.5-rc.2` joins CI; and `check-package` now fails when a runtime is called verified without appearing in the CI matrix and the profile table.
158
+
159
+ ## v1.1.12 (2026-09-25)
160
+ ### 修复
161
+ - **runtime smoke 不再手数路由** —— 此前断言 dispose 恰好移除 9 条,v1.1.11 加路由后所有 smoke 作业失败并连带发布失败;期望值改由路由清单推导。
162
+ ### 说明
163
+ - v1.1.11 只发了 GitHub Release,**未进 npm**(发布工作流校验 tag,已发布的 tag 不移动),其修复随本版到达 npm。
164
+
165
+ ### Fixes
166
+ - **The runtime smoke no longer counts routes by hand** — it asserted that dispose removed exactly nine, so v1.1.11's new route failed every smoke job and its own publication; the expectation is now derived from the route list.
167
+ ### Notes
168
+ - v1.1.11 reached GitHub Releases but **never npm** (the publish workflow verifies the tag and a published tag is never moved); its fix reaches npm users with this release.
169
+
170
+ ## v1.1.11 (2026-09-25)
171
+ ### 修复
172
+ - **桌面端关闭按钮不再被盖** —— 面板改为从 `--dsh-windows-titlebar-height`(40px)之下开始。
173
+ - **预留标题条其实从未生效** —— 未设置的 localStorage 键被读成 `0` 并通过了 `>= 0` 判断,宿主标记 / Overlay / UA 兜底全是死代码。
174
+ ### 优化
175
+ - 预留高度来源顺序改为宿主声明 → Overlay 矩形 → UA,localStorage 仍可覆盖。
176
+ - 新增 `GET /api/all-usage/client-env`(并以 `clientEnv` 出现在 status),正是它从桌面应用之外暴露了上面的 bug。
177
+
178
+ ### Fixes
179
+ - **The desktop client's close button is no longer covered** — the sheet now starts below `--dsh-windows-titlebar-height` (40px on Windows).
180
+ - **The reserved strip never actually applied** — an unset localStorage key read as `0`, passed the `>= 0` check and returned early, leaving the host marker, the overlay geometry and the user-agent fallback as dead code.
181
+ ### Improvements
182
+ - The reserved height now comes from the host declaration, then the overlay rectangle, then the user agent, with localStorage still overriding everything.
183
+ - A new `GET /api/all-usage/client-env` route (surfaced as `clientEnv`) is what exposed that bug from outside the desktop app.
184
+
185
+ ## v1.1.10 (2026-09-14)
186
+ ### 修复
187
+ - **账本快路径回归** —— 0.1.5 把 `listSnapshots()` 改名 `list()`,修订号拿不到导致每次基线重读全部日志(479 会话 / 431 次读取 / 约 13 分钟);两种拼写都接受后恢复跳过。
188
+ - **基线期间的工作区变更不再丢** —— 扫描结束后立刻重放。
189
+ ### 优化
190
+ - 工作区注册表每 30 秒额外轮询一次;status 暴露探针计数(累计,不被基线重置)。
191
+ ### 说明
192
+ - 升级后首次启动仍会读一遍全部日志(旧账本没有 `lastRevision`),从下一次启动起才免读。
193
+
194
+ ### Fixes
195
+ - **The ledger fast path is back** — DSH 0.1.5 renamed `listSnapshots()` to `list()`, so no revision was available and every baseline re-read every log (479 sessions, 431 reads, about 13 minutes); both spellings are accepted again and unchanged sessions are skipped.
196
+ - **A registry change during a baseline is no longer dropped** — it is remembered and replayed as soon as the scan settles.
197
+ ### Improvements
198
+ - The workspace registry is polled every 30 seconds as well, and status exposes the probe counters (cumulative, never reset by a baseline).
199
+ ### Notes
200
+ - The first start after upgrading still reads every log once (older records carry no `lastRevision`); re-read avoidance starts from the next start.
201
+
202
+ ## v1.1.9 (2026-09-12)
203
+ ### 修复
204
+ - **删除工作区不再抹掉历史** —— 探针丢弃其账本行、恢复又跳过 `sourceCwd` 失效的行,两者叠加销毁了已记录的历史。
205
+ ### 优化
206
+ - 被移除工作区的用量汇总成一行「已删除」;`retargetSession()` 就地改键,未落账的实时用量也保留。
207
+ - 账本行保留原 workspace id 与 cwd(改键只在内存副本);严格注册边界不变,旧 `unregistered:` 行不复活。
208
+
209
+ ### Fixes
210
+ - **Deleting a workspace no longer erases its history** — the probe dropped its ledger rows while recovery skipped rows whose `sourceCwd` no longer mapped, and together they destroyed recorded history.
211
+ ### Improvements
212
+ - Usage from removed workspaces is summed into a single **Deleted** row, and `retargetSession()` re-keys live aggregates in place so usage that had not reached the ledger survives too.
213
+ - Ledger rows keep their original workspace id and cwd (re-keying happens on the in-memory copy only), the strict registration boundary is unchanged and legacy `unregistered:` rows are never resurrected.
214
+
215
+ ## v1.1.8 (2026-09-11)
216
+ ### 修复
217
+ - README 仍引用 0.1.5 之前的兼容声明与停在 0.1.1 的验证矩阵,与 v1.1.6 / v1.1.7 之后的 `package.json` 矛盾;中英已改齐。
218
+ ### 优化
219
+ - CI smoke 覆盖 `0.1.5-rc.1`,并按运行时版本选择 cordis / loader / timer 线。
220
+
221
+ ### Fixes
222
+ - The README still quoted the pre-0.1.5 compatibility declaration and a matrix that stopped at 0.1.1, contradicting `package.json` after v1.1.6/v1.1.7; both languages now agree.
223
+ ### Improvements
224
+ - CI smoke covers `0.1.5-rc.1` and selects the cordis/loader/timer line per runtime version.
225
+
226
+ ## v1.1.7 (2026-09-11)
227
+ ### 修复
228
+ - 兼容区间改为按元组带预发布分支 —— node-semver 只放行「同 `major.minor.patch` 且自带预发布标签」的比较符,旧的宽区间把目标运行时 `0.1.5-rc.1` 静默判为不支持。
229
+
230
+ ### Fixes
231
+ - The compatibility range now carries an explicit prerelease branch per tuple: node-semver only admits a prerelease when a comparator shares its `major.minor.patch` and carries a prerelease tag, so the previous broad range silently rejected the target runtime `0.1.5-rc.1`.
232
+
233
+ ## v1.1.6 (2026-09-11)
234
+ ### 修复
235
+ - **DSH 0.1.5 兼容** —— 该版本用 `snapshotEvents()` 取代 `events`,实时 flush 会写空账本记录(`lastSeq: -1`);现在优先 `events`、否则退回 `snapshotEvents()`。
236
+ - smoke 断言账本保留被 flush 会话的序号与用量,并覆盖 0.1.5-rc.1。
237
+ ### 优化
238
+ - 兼容区间放宽到 `>=0.1.1-rc.1 <0.1.6`,`0.1.5-rc.1` 进入已验证列表。
239
+
240
+ ### Fixes
241
+ - **DSH 0.1.5 compatibility** — it replaces `events` with `snapshotEvents()`, so live flushes wrote empty ledger records (`lastSeq: -1`); event access now prefers `events` and falls back to `snapshotEvents()`.
242
+ - The smoke asserts the persisted ledger keeps a flushed session's sequence and usage, and covers 0.1.5-rc.1.
243
+ ### Improvements
244
+ - The declared range widens to `>=0.1.1-rc.1 <0.1.6` and `0.1.5-rc.1` joins the verified list.
245
+
246
+ ## v1.1.5 (2026-09-07)
247
+ ### 新功能
248
+ - **工作区注册表自动探针** —— 监听 `domain/changed`,增量同步(只扫新增、逆折叠移除、未变化零重扫);手动刷新控件 / 路由 / 按钮一并移除。
249
+ ### 修复
250
+ - 用量严格限定于已注册工作区;`sourceCwd` 失效的账本行恢复时跳过,并按源路径校验归属。
251
+
252
+ ### Features
253
+ - **An automatic workspace-registry probe** — it follows `domain/changed` and syncs incrementally (only new workspaces are scanned, removals are subtracted by inverse folding, unchanged ones are reused); the manual refresh control, route and button are gone.
254
+ ### Fixes
255
+ - Usage is strictly limited to registered workspaces; ledger rows whose `sourceCwd` no longer maps are skipped during recovery, and the source path is persisted so ownership can be validated.
256
+
257
+ ## v1.1.4 (2026-09-03)
258
+ ### 新功能
259
+ - **DeepSeek 峰谷计费成为一等、带版本的「分时价格计划」** —— cost schema v2,快照记录计费时刻、时间来源、UTC 档位与策略 id / 哈希。
260
+ - **内置一线档位表** —— v4-flash / v4-flash-vision-exp / v4-pro,按 UTC 工作日 01:00-04:00 与 06:00-10:00;峰值费率是数据,不是自动折扣。
261
+ - **半开边界且只看 UTC** —— 周末全天谷时,观众时区与夏令时都影响不了结果。
262
+ - **确定性复用** —— 同一 turn/step 的后续样本仅在 identity / 桶 / 档位 / 策略全等时复用,跨峰值边界不继承价格。
263
+ - **受控对账** —— 一线快照按用量时刻重定价,v1 快照迁移到 v2,窗口不覆盖则 fail closed 为 `unsupported`。
264
+ - **路由安全边界** —— 只对一线 deepseek 或显式映射到官方条目的路由生效;中转路由保持静态价并标 `route-not-official`。
265
+ - **尊重官方生效时刻** —— 从 2026-08-16T16:00:00Z 起算(vision-exp 为 08-21),更早的用量 fail closed。
266
+ - **真实厂商品牌图标** —— 请求日志 / 调用详情 / 模型表 / 成本设置表改用 11 个品牌 SVG(构建期内嵌 data URI,运行时不联网)。
267
+ - **图标来源随包发布** —— manifest 钉住上游 commit 与逐文件归属,缺许可或归属即构建失败。
268
+ ### 优化
269
+ - 请求上下文归档持久化并参与增量播种;真 LRU + 512 条上限;整折时清理陈旧上下文。
270
+ - 已定价的 v2 快照跨目录刷新仍可审计:policy hash 不含实时费率,只有显式 `repriceTemporal` 才重估。
271
+ - 策略归档支持有序窗口并拒绝跨规则重叠;非法时段配置保持可见并 fail closed。
272
+ - 实时重同步原子化(权威快照重建并持久化账本,失败保留 dirty 状态);权威性改为基于证明,滞后快照只 upsert。
273
+ - 构建期 SVG 守卫(`scripts/svg-guard.mjs`)拒绝外部 `url()` / `image-set()` / `@import`、DOCTYPE / CDATA / 处理指令与外部资源元素。
274
+ ### 修复
275
+ - 审计后加固:CDO/CDC 相邻 `@import`、命名空间 `<svg:style>` 内的 `@import`、CSS 转义 `url(`、外部 `image-set()` 全部拒绝;style 属性在原始与实体解码两份文档上检查。
276
+ - CSS 检查只在 style 属性与 `<style>` 体内运行(注释与文本不误报);对抗样本断言的是拒绝原因。
277
+ - 模型筛选下拉也显示品牌图标,三张看板截图刷新。
278
+
279
+ ### Features
280
+ - **DeepSeek peak/off-peak billing became a first-class, versioned temporal plan** — cost schema v2 records the billing instant, its time source, the UTC band and the policy id/hash in every snapshot.
281
+ - **A built-in first-party rate table** — v4-flash / v4-flash-vision-exp / v4-pro for the UTC weekday windows 01:00-04:00 and 06:00-10:00; peak rates are data, never an automatic discount.
282
+ - **Half-open boundaries, UTC only** — weekends are off-peak all day and neither the viewer's timezone nor DST can change a result.
283
+ - **Deterministic reuse** — a later sample for the same turn/step reuses a snapshot only when identity, buckets, band and policy all match, so a message crossing a peak boundary never inherits the price.
284
+ - **Controlled reconciliation** — first-party snapshots are re-priced against their usage instant, v1 snapshots migrate to v2, and an uncovered instant fails closed as `unsupported`.
285
+ - **Route safety boundary** — the plan applies only to first-party deepseek routes or routes explicitly mapped to the official entry; reseller routes keep the static price and are labelled `route-not-official`.
286
+ - **The official effective instant is respected** — the plan starts at 2026-08-16T16:00:00Z (08-21 for vision-exp) and earlier usage fails closed.
287
+ - **Real vendor brand icons** — the request log, call details, model table and cost-settings table use eleven brand SVGs (embedded as data URIs at build time; the runtime never touches the network).
288
+ - **Icon provenance ships with the package** — the manifest pins the upstream commit and per-file attribution, and the build fails without the licence or attribution.
289
+ ### Improvements
290
+ - The request-context archive is persisted and seeds incremental folds; a real LRU with a 512-entry cap; full folds clear stale contexts.
291
+ - Priced v2 snapshots stay auditable across catalog refreshes: the policy hash excludes live rates and only an explicit `repriceTemporal` re-estimates them.
292
+ - Policy archives support ordered, non-overlapping windows and reject cross-rule overlap; an invalid temporal config stays visible and fails closed.
293
+ - Live resyncs are atomic (an authoritative snapshot rebuilds and persists the ledger record, failures stay dirty) and authority is proof-based: lagging snapshots only upsert.
294
+ - A build-time SVG guard (`scripts/svg-guard.mjs`) rejects external `url()` / `image-set()` / `@import`, DOCTYPE / CDATA / processing instructions and every external-resource element.
295
+ ### Fixes
296
+ - Hardened after an independent audit: CDO/CDC-adjacent `@import`, `@import` inside a namespaced `<svg:style>`, CSS-escaped `url(` and external `image-set()` targets are all rejected, and style attributes are checked on both the raw and entity-decoded document.
297
+ - CSS checks run only on style attributes and `<style>` bodies (comments and text cannot false-positive), and the adversarial fixtures assert the rejection reason.
298
+ - The model filter dropdown shows brand icons too, and the three dashboard screenshots were refreshed.
299
+
300
+ ## v1.1.3 (2026-09-01)
301
+ ### 新功能
302
+ - 成本统计支持上下文档位,可展开官方费率表。
303
+ - 确定性用量不变量 + 脱敏 fixture 回放命令。
304
+ - Node 22/24 CI 矩阵(含包内容检查与 0.1.1-rc.1 / rc.2 真机 smoke)。
305
+ - 三份社区 issue 模板(数据不一致 / 启动失败 / 成本计算)。
306
+ ### 优化
307
+ - flush 不再重建干净会话的账本(脏记录合并,退出时 drain)。
308
+ - 派生账本分 32 个稳定哈希分片,只重写受影响的分片。
309
+ - scope query 与 53 周热力图改用 ingest 期 cube + BigInt 精确累加。
310
+ - 热力图指针跟踪与渲染路径隔离(memo + rAF 合并,后台标签节流兜底)。
311
+ - 客户端产物 276 KB → 177 KB(固定版本 Terser)。
312
+ ### 修复
313
+ - **账本序号契约贯穿所有读写路径** —— 只接受 `-1` 哨兵或非负安全整数;污染键与非规范数字一律标记重建并持久化原因。
314
+ - **折叠存疑时全量重建** —— 序号不连续、折叠起点之前有非法序号、尾部未前进,都不再尾部折叠。
315
+ - **定价与刷新时序** —— 读写前等待持久化配置;定价 revision 变化优先;只在影响成本时提升 revision;目录哈希排除 `fetchedAt`。
316
+ - **在飞的官方模型搜索彻底失效化** —— 删行、关面板、目录替换都会提升行代际。
317
+ - **服务端 auto-sync 标志是唯一真相** —— 客户端不再用本地状态播种草稿。
318
+ - **时区与时间桶** —— 半小时偏移不丢事件;趋势标签用真实桶边界;DST 回拨的重复小时分别成桶。
319
+ - **工作区身份** —— 以不同 id 重建时强制全量重建;顶层与历史条目不一致的行标为不可用。
320
+ - **HTTP 与激活边界** —— peer 必须是 loopback(含 IPv4-mapped);激活前必须有 `webServer`。
321
+ - **发布与构建守卫** —— 拒绝缺失或未跟踪的生成 bundle;手动 npm 恢复必须校验 tag 与完整 SHA。
322
+ ### 文档
323
+ - 记录兼容矩阵、本地可复算与供应商账单的区别、缓存 / Token 示例与 fixture 工作流。
324
+
325
+ ### Features
326
+ - Cost statistics became tier-aware with expandable official rate schedules.
327
+ - Deterministic usage invariants plus a redacted fixture replay command.
328
+ - A Node 22/24 CI matrix with package-content checks and required real DSH smoke coverage for 0.1.1-rc.1 and rc.2.
329
+ - Three community issue templates (data inconsistency, startup failure, cost calculation).
330
+ ### Improvements
331
+ - A clean session's ledger is no longer rebuilt at flush: dirty records are coalesced and drained on disposal.
332
+ - The ledger is stored in 32 stable-hash shards, so an update rewrites only its own shard.
333
+ - Scoped queries and the heatmap build from ingest-time cubes with exact BigInt accumulators.
334
+ - Heatmap pointer tracking is isolated from the render path (memoized parts, rAF-coalesced coordinates, a throttled fallback for background tabs).
335
+ - The shipped client artifact dropped from about 276 KB to about 177 KB with pinned Terser.
336
+ ### Fixes
337
+ - **The ledger sequence contract covers every read and write path** — only the `-1` sentinel or a non-negative safe integer is accepted; polluted keys and non-canonical numbers are flagged for rebuild with the reason persisted.
338
+ - **Any doubt about folding triggers a full rebuild** — broken sequence continuity, an invalid sequence before the fold start, or a tail that did not advance no longer tail-folds.
339
+ - **Pricing and refresh ordering** — reads and writes wait for the persisted configuration, a pricing revision change wins over concurrent data, revisions only bump for cost-affecting changes, and the catalog hash excludes `fetchedAt`.
340
+ - **In-flight official-model searches are invalidated for good** — deleting a row, closing the panel and replacing the catalog all bump the row generation.
341
+ - **The server-persisted auto-sync flag is the single truth** — the client no longer seeds the draft from local state.
342
+ - **Timezones and buckets** — fractional-hour offsets no longer drop events, trend labels use real bucket boundaries, and both repeated hours of a DST fall-back day stay distinct.
343
+ - **Workspace identity** — a workspace recreated with a different id forces a full rebuild, and rows whose top-level workspace disagrees with their items are marked unusable.
344
+ - **HTTP and activation boundaries** — the socket peer must be loopback (IPv4-mapped included) and `webServer` must exist before the Host activates.
345
+ - **Release and build guards** — a missing or untracked generated bundle is rejected, and manual npm recovery must verify the tag and full commit SHA.
260
346
  ### Documentation
261
-
262
- - Documented the DSH compatibility matrix, the difference between local replayable statistics and provider billing, cache/token examples, and the fixture workflow.
263
-
264
- ## [1.1.2] - 2026-08-30
265
-
266
- ### Added
267
-
268
- - Added models.dev price catalog synchronization with a persisted last-good cache, optional 6-hour automatic sync, model-only official-vendor matching, explicit model mappings, and manual overrides for authoritative special pricing.
269
- - Added cc-switch-compatible four-bucket cost snapshots: input, output, cache read, and cache write; cost multipliers apply only to the final total.
270
- - Added ledger v3 cost snapshots, zero-cost/unpriced coverage counters, cost fields in scoped query and privacy-safe records responses, cost columns in exports, and a dashboard pricing settings panel.
271
- - Added searchable current-model selectors for mappings and overrides, automatic official model lookup, compact price-field labels, and persisted mapping display recovery.
272
- - Added Token and cost details to model and workspace donut legends and hover menus, including aggregated values for the other bucket.
273
- - Added date-indexed usage and turn queries, revision-scoped snapshot and records caching, debounced official model search, and complete write-chain draining during disposal.
274
-
275
- ### Fixed
276
-
277
- - Preserved DSH's already-normalized fresh input semantics and avoided charging reasoning tokens twice when provider output counts already include completion/thoughts.
278
- - Kept positive historical cost snapshots stable across pricing catalog updates; only unpriced usage is eligible for explicit backfill.
279
- - Fixed saved model mappings reopening with an empty current-model field by deriving the display from the persisted model identity.
280
- - Prevented first live events from being lost during session bootstrap or read failures, and added generation-scoped recovery for baseline/live races.
281
- - Rejected oversized state bodies and malformed alias parameters, reported failed session reads accurately, ignored invalid event timestamps, and kept daily session counts correct after replacements.
282
-
283
- ## [1.1.1] - 2026-08-29
284
-
285
- ### Fixed
286
-
287
- - Reissued the v1.1 dashboard package under a new patch version for registry compatibility.
288
-
289
- ## [1.1.0] - 2026-08-28
290
-
291
- ### Added
292
-
293
- - Added structured Provider, requested-model, actual-model, and display-model identity fields while preserving the legacy `model` field.
294
- - Added a backward-compatible ledger v2 adapter; v1 rows are read once, canonicalized, and upgraded before revision skipping resumes.
295
- - Added shared scoped usage and privacy-safe paginated records APIs: `/api/all-usage/query` and `/api/all-usage/records`; query responses now add hourly trend rows for single-day scopes while preserving the existing daily rows.
296
- - Kept workspace, provider, and model filters independent so users can combine all three dimensions in one scope.
297
- - Limited workspace, provider, and model filter options to values used in the selected date range and clear selections that become unavailable after a range change.
298
- - Added unified Workspace, Provider, model, date, and timezone filtering for dashboard aggregates, the fixed 53-week heatmap, trend data, and exports.
299
- - Added a responsive Token trend chart with smooth monotone cubic curves, staged draw animation, selectable input, cache-read, cache-write, output, reasoning, and total series; single-day scopes use hourly buckets while cross-day scopes use daily buckets.
300
- - Added Token-share donut charts with ranked legends to the model and workspace detail panels, including high-contrast remainder segments, animated arc reveals, and cursor-following hover details.
301
- - Added a persistent Request Logs tab with a compact paginated table, selected-row Token detail, opaque row IDs, materialization-source labels, and scoped detail CSV export.
302
- - Optimized Host records pagination with revision-scoped ordering and precomputed identity/date values; optimized Client derived aggregates with memoization and active-tab rendering.
303
-
304
- ### Fixed
305
-
306
- - Prevented model identity collisions caused by parsing display labels containing ` / `.
307
- - Detects truncated or replaced session logs instead of overlaying a newer full rebuild with a stale ledger row.
308
- - Keeps turn counts, call counts, and distinct session counts separately defined in scoped results.
309
- - Keeps complete trend paths visible while per-series reveal animation restarts during live refreshes, preventing the final chart segment from disappearing.
310
- - Reserves the trend chart height during query loading and replaces floating loading text/legend controls with a centered spinner.
311
- - Keeps hover intersection markers static so they do not jump from the SVG origin into position on every hover.
312
- - Reuses one anchored trend tooltip with short position and opacity transitions, avoiding abrupt remounts while switching dates.
313
- - Isolates dashboard render failures behind the sidebar entry so a bad range response cannot remove the usage entry itself.
314
-
315
- ## [1.0.9] - 2026-08-26
316
-
317
- ### Added
318
-
319
- - Added a lightweight, guarded `/api/all-usage/status` contract with a per-Host instance ID, monotonic stats revision, scan progress, and non-sensitive sync-health counters.
320
- - Added sync-health visibility for revision-skipped sessions, actual log reads, ledger-only recoveries, failures, persistence snapshot availability, and last completed baseline time.
321
- - Added revision-driven Client refresh: after the initial scan, the dashboard polls only lightweight status and fetches the full historical snapshot only when the Host instance or stats revision changes.
322
-
323
- ### Fixed
324
-
325
- - Preserved the last successful usage view after status or full-snapshot failures, exposed a retry action, and added bounded retry backoff instead of silently discarding refresh errors.
326
-
327
- ## [1.0.8] - 2026-08-23
328
-
329
- ### Added
330
-
331
- - Added a restart read-avoidance cursor: on startup the plugin uses the persisted log revision (via `sessionPersistence.listSnapshots()`, a header-line + stat read) as a per-session change signal. Sessions whose log is unchanged since the last ledger write are applied straight from the durable ledger without reading their full event log; only changed or new sessions are read (incrementally).
332
- - Stored the log revision in each durable ledger record (`lastRevision`); existing rows without it are re-read once and backfilled, so behavior is backward compatible.
333
-
334
- ### Fixed
335
-
336
- - Eliminated the per-session full-event read at restart for unchanged persisted sessions, so DSH restarts no longer re-read every session's history from scratch.
337
-
338
- ## [1.0.7] - 2026-08-23
339
-
340
- ### Added
341
-
342
- - Added an incremental ledger cursor: baseline rescans reuse the durable ledger as a per-session cursor, so unchanged sessions seed from the ledger instead of re-folding every historical event, and changed sessions fold only the new tail.
343
- - Added an all-zero usage guard: zero-token usage replays no longer overwrite already-recorded turn/step usage, while pure cache-read requests (billable cache hits with no input/output) are still counted.
344
- - Added a structured, machine-readable token semantics declaration (`tokenSemantics.semantics`) to the `/api/all-usage` snapshot: input is fresh (excludes cache), cache and reasoning are separate buckets.
345
-
346
- ### Fixed
347
-
348
- - Reduced startup scan cost on long usage history: sessions whose events are unchanged since the last ledger write are no longer re-folded or rewritten.
349
- - Prevented a live retry of an already-recorded step from double-counting after a fresh-instance seed.
350
-
351
- ## [1.0.6] - 2026-08-23
352
-
353
- ### Added
354
-
355
- - Added a durable per-session usage ledger written at the awaited `session/flush` boundary.
356
- - Preserved successfully flushed turns and token usage after conversation logs are deleted.
357
- - Added automatic rebuilds from ledger records after session disposal or reconciliation detects missing logs.
358
-
359
- ### Fixed
360
-
361
- - Prevented stale baseline snapshots from overwriting newer flushed ledger records.
362
- - Normalized duplicate ledger keys during hydration and guarded ledger writes during plugin disposal.
363
- - Prevented inactive Cordis timer contexts from escalating into fatal DSH load failures.
364
- - Replaced invalid hyphenated storage unit names with valid underscore names.
365
-
366
- ## [1.0.5] - 2026-08-20
367
-
368
- ### Added
369
-
370
- - Added custom start/end date ranges across all available historical daily data.
371
- - Kept the heatmap as a fixed latest-53-week view while allowing older range aggregation and CSV export.
372
- - Added distinct session counts for bounded ranges.
373
-
374
- ### Fixed
375
-
376
- - Added baseline retry with backoff after transient session registry failures.
377
- - Stopped in-flight scans and stale polling responses after plugin disposal or refresh races.
378
-
379
- ## [1.0.4] - 2026-08-19
380
-
381
- ### Fixed
382
-
383
- - Hardened loopback, same-origin, method, and process-token checks for dashboard API routes.
384
- - Allowed same-origin browser balance GET requests that omit `Origin` while retaining token protection.
385
- - Standardized English date buckets, range filters, streaks, heatmap dates, and export timestamps on UTC.
386
-
387
- [1.1.5]: https://github.com/ParticleLight/dsh-all-usage/releases/tag/v1.1.5
388
- [1.1.4]: https://github.com/ParticleLight/dsh-all-usage/releases/tag/v1.1.4
389
- [1.1.3]: https://github.com/ParticleLight/dsh-all-usage/releases/tag/v1.1.3
390
- [1.1.2]: https://github.com/ParticleLight/dsh-all-usage/releases/tag/v1.1.2
391
- [1.1.0]: https://github.com/ParticleLight/dsh-all-usage/releases/tag/v1.1.0
392
- [1.0.9]: https://github.com/ParticleLight/dsh-all-usage/releases/tag/v1.0.9
393
- [1.0.8]: https://github.com/ParticleLight/dsh-all-usage/releases/tag/v1.0.8
394
- [1.0.7]: https://github.com/ParticleLight/dsh-all-usage/releases/tag/v1.0.7
395
- [1.0.6]: https://github.com/ParticleLight/dsh-all-usage/releases/tag/v1.0.6
396
- [1.0.5]: https://github.com/ParticleLight/dsh-all-usage/releases/tag/v1.0.5
397
- [1.0.4]: https://github.com/ParticleLight/dsh-all-usage/releases/tag/v1.0.4
347
+ - Documented the compatibility matrix, the difference between local replayable statistics and provider billing, cache/token examples and the fixture workflow.
348
+
349
+ ## v1.1.2 (2026-08-30)
350
+ ### 新功能
351
+ - models.dev 价格目录同步(持久缓存、可选 6 小时自动同步、模型映射与手工覆盖)。
352
+ - cc-switch 兼容的四桶成本快照(倍率只作用于总额)。
353
+ - 账本 v3 成本快照、未计价计数、导出成本列与成本设置面板。
354
+ - 可搜索的当前模型选择器与自动官方模型查找。
355
+ - 环形图的 Token / 成本明细;按日期索引的查询与 revision 作用域缓存。
356
+ ### 修复
357
+ - 不再把推理 Token 收两次费;已定价历史在目录更新后保持稳定。
358
+ - 已保存映射不再显示空模型字段;会话引导或读取失败不丢首批实时事件;拒绝超大请求体与非法时间戳。
359
+
360
+ ### Features
361
+ - models.dev catalog sync with a persisted last-good cache, optional 6-hour auto sync, mappings and manual overrides.
362
+ - cc-switch-compatible four-bucket cost snapshots, with multipliers applied to the total only.
363
+ - Ledger v3 cost snapshots, unpriced counters, cost columns in exports and the cost-settings panel.
364
+ - Searchable current-model selectors and automatic official-model lookup.
365
+ - Token/cost details in the donut legends, date-indexed queries and revision-scoped caches.
366
+ ### Fixes
367
+ - Reasoning tokens are no longer charged twice, and priced history stays stable across catalog updates.
368
+ - Saved mappings no longer reopen with an empty model field, first live events survive bootstrap or read failures, and oversized bodies and invalid timestamps are rejected.
369
+
370
+ ## v1.1.1 (2026-08-29)
371
+ ### 修复
372
+ - 为兼容注册表,以新的修订号重发 v1.1 包。
373
+
374
+ ### Fixes
375
+ - The v1.1 package was reissued under a new patch version for registry compatibility.
376
+
377
+ ## v1.1.0 (2026-08-29)
378
+ ### 新功能
379
+ - 结构化 Provider / requested / actual / display 身份字段(保留旧 `model`)。
380
+ - 向后兼容的账本 v2 适配器。
381
+ - 共享 scope 查询与隐私安全分页接口(`/query`、`/records`),单日 scope 增加小时行。
382
+ - 三个筛选维度互相独立可组合,选项只列当前范围内实际用过的值。
383
+ - 统一筛选贯穿聚合、热力图、趋势与导出。
384
+ - 可选 Token 趋势图(平滑曲线、可切序列、单日用小时桶)。
385
+ - Token 占比环形图(排名图例与悬停明细)。
386
+ - 常驻请求日志标签页(分页表、选中明细、CSV 导出)。
387
+ ### 优化
388
+ - records 分页改 revision 排序 + 预计算;聚合加 memo 并只渲染活动标签页。
389
+ ### 修复
390
+ - 修复显示标签含 ` / ` 的身份碰撞;检测被截断或替换的日志;三类计数分开定义;趋势与 tooltip 的一系列渲染修复。
391
+
392
+ ### Features
393
+ - Structured Provider / requested / actual / display identity fields, keeping the legacy `model`.
394
+ - A backward-compatible ledger v2 adapter.
395
+ - Shared scoped query and privacy-safe paged APIs (`/query`, `/records`), with hourly rows for single-day scopes.
396
+ - Workspace, provider and model filters are independent and combinable, and options list only values used in the selected range.
397
+ - Unified filtering spans aggregates, the heatmap, trends and exports.
398
+ - A selectable Token trend chart (smooth curves, switchable series, hourly buckets for single days).
399
+ - Token-share donut charts with ranked legends and hover details.
400
+ - A persistent Request Logs tab (paged table, selected-row detail, CSV export).
401
+ ### Improvements
402
+ - Records pagination uses revision ordering and precomputed values; aggregates are memoized and only the active tab renders.
403
+ ### Fixes
404
+ - Fixed identity collisions from display labels containing ` / `, detected truncated or replaced logs, kept the three counters separately defined, and fixed a series of trend/tooltip rendering issues.
405
+
406
+ ## v1.0.9 (2026-08-26)
407
+ ### 新功能
408
+ - 轻量 `/api/all-usage/status` 契约(实例 ID、单调 revision、扫描进度、同步健康)。
409
+ - 同步健康可见性(跳过 / 读取 / 账本恢复 / 失败 / 快照可用性)。
410
+ - revision 驱动的客户端刷新(只在实例或 revision 变化时取全量)。
411
+ ### 修复
412
+ - 失败时保留上一次视图,提供重试与有界退避。
413
+
414
+ ### Features
415
+ - A lightweight `/api/all-usage/status` contract (instance id, monotonic revision, scan progress, sync health).
416
+ - Sync-health visibility (skipped, read, ledger-recovered, failed, snapshot availability).
417
+ - Revision-driven client refresh: the full snapshot is fetched only when the instance or revision changes.
418
+ ### Fixes
419
+ - The last successful view survives failures, with a retry action and bounded backoff.
420
+
421
+ ## v1.0.8 (2026-08-24)
422
+ ### 新功能
423
+ - **重启免读游标** —— 用持久化日志修订号作为变更信号,未变更会话直接从账本应用。
424
+ ### 修复
425
+ - 重启不再重读每个会话的历史。
426
+
427
+ ### Features
428
+ - **A restart read-avoidance cursor** — the persisted log revision is the change signal, so unchanged sessions are applied straight from the ledger.
429
+ ### Fixes
430
+ - Restarts no longer re-read every session's history.
431
+
432
+ ## v1.0.7 (2026-08-23)
433
+ ### 新功能
434
+ - 增量账本游标(未变会话从账本播种,变化会话只折叠新尾部)。
435
+ - 全零用量守卫;`tokenSemantics` 语义声明。
436
+ ### 修复
437
+ - 降低长历史的启动扫描成本;防止实时重试重复计数。
438
+
439
+ ### Features
440
+ - An incremental ledger cursor: unchanged sessions seed from the ledger and changed ones fold only the new tail.
441
+ - An all-zero usage guard and a machine-readable `tokenSemantics` declaration.
442
+ ### Fixes
443
+ - Startup scan cost on long histories is reduced, and live retries no longer double-count.
444
+
445
+ ## v1.0.6 (2026-08-23)
446
+ ### 新功能
447
+ - 持久化逐会话用量账本(在 `session/flush` 边界写入);日志删除后用量仍在;缺失时自动重建。
448
+ ### 修复
449
+ - 防陈旧基线覆盖新账本;重复键规范化与退出期写入守卫;非法存储单元名;Cordis 定时器上下文不再致命。
450
+
451
+ ### Features
452
+ - A durable per-session usage ledger written at the `session/flush` boundary; flushed usage survives log deletion and is rebuilt automatically when logs are missing.
453
+ ### Fixes
454
+ - Stale baselines no longer overwrite newer ledger records; duplicate keys are normalized and writes are guarded during disposal; invalid storage unit names are fixed; inactive Cordis timer contexts are no longer fatal.
455
+
456
+ ## v1.0.5 (2026-08-21)
457
+ ### 新功能
458
+ - 自定义起止日期范围;热力图仍固定最近 53 周;有界范围给出独立会话计数。
459
+ ### 修复
460
+ - 基线重试带退避;退出或刷新竞争后停止在飞扫描与陈旧轮询。
461
+
462
+ ### Features
463
+ - Custom start/end ranges, a heatmap that stays a fixed latest-53-week view, and distinct session counts for bounded ranges.
464
+ ### Fixes
465
+ - Baseline retries back off, and in-flight scans and stale polling stop after disposal or refresh races.
466
+
467
+ ## v1.0.4 (2026-08-18)
468
+ ### 修复
469
+ - 加固 loopback / 同源 / 方法 / 令牌检查;允许省略 `Origin` 的同源余额 GET;英文模式统一 UTC。
470
+
471
+ ### Fixes
472
+ - Hardened loopback, same-origin, method and token checks; allowed same-origin balance GETs without `Origin`; and standardised English mode on UTC.