Ports the menubar's Kimi flow to the Electron quota module: read-only
~/.kimi-code/credentials (KIMI_CODE_HOME honored), api.kimi.com usages
snapshot, terminal expired-login state that never refreshes the CLI's
short-lived token, 5-hour/weekly window mapping with over-limit clamp,
membership plan label, and the standard per-provider Settings toggle.
The renderer memo was an LRU capped at 8-24 entries, so cycling periods
evicted payloads and returning to one forced a skeleton plus a 1.5-6s
recompute. The memo is now one latest payload per key for the app
session: a revisited period paints its cached numbers in the same
commit, skips the CLI entirely when the payload is under 30s old, and
otherwise revalidates behind the painted data and swaps in place.
Failed revalidates keep last-good data with the existing error surface;
first visits keep the loading state. (#1135 part 1)
Version 0.9.21 across CLI, desktop app, and Windows menubar; refreshed
LiteLLM pricing snapshot and fallback (catches the 2026-08-24 OpenAI
GPT-5.6 cut and DeepSeek v4 repricing mid-rollout, #1134 tracks
re-tightening); CHANGELOG stamped for 0.9.21; README release links to
0.9.21. Snapshot-pinned model tests updated to current catalog truth.
Settings window titles itself after the visible pane like System
Settings; the popover subtitle adopts the website tagline (Your AI
Bill, Itemized); About stops printing dev (dev) when version and build
match; remaining user-facing em-dashes across popover and service
strings become sentence breaks.
Status item returns to the SF flame (the 0101 mark blurs at menubar
size); About hero shows the full-color binary flame and the About
sidebar row its bare white template, no chip; footer credits Resham
Joshi (iamtoruk) / AgentSeal, MIT; About tagline matches the website
(Your AI Bill, Itemized); UI copy drops em-dashes (currency rows use
middots); Settings window is resizable and fullscreen-capable and
recenters when restored off-screen.
Ports the desktop app's Antigravity flow: ps+lsof discovery of the
local language server, Connect-RPC quota/status probes over loopback
HTTPS with the self-signed-cert trust override scoped strictly to
127.0.0.1, disconnected-state UX when the app is not running. Also
swaps all provider sidebar marks to the official LobeHub icon set.
Settings window now replicates CodexBar's System Settings layout
(880x620 fullSizeContentView window, transparent titlebar, fixed 260pt
sidebar, SF Symbol gradient chips, native search field, grouped About
pane with Updates/Links). Status item renders the 0101 flame template
with severity tints. package-app.sh ships the SwiftPM resource bundle,
fixing the Bundle.module launch crash. Sidebar layout modeled on
CodexBar (MIT, steipete/CodexBar).
Ports the desktop app's Copilot flow (editor-plugin OAuth token from
~/.config/github-copilot, copilot_internal/user snapshot) read-only,
with github.com-host preference and UI-string sanitization.
Replaces the tab strip with a NavigationSplitView shell: search field,
General/About tile rows, and a data-driven Providers section whose real
logo marks render gray at rest and accent-tinted on hover, with green
connected dots and an N-on counter. Bundles template icon assets.
Ports the desktop app's Gemini Code Assist quota flow (loadCodeAssist
tier + retrieveUserQuota buckets, ~/.gemini/oauth_creds.json read-only,
in-memory-only refresh) to the menubar. Plan tab and empty-state gating
now treat Gemini as plan-capable.
The #1126 test pinned basename presence; under #1128's fail-closed
provenance the same fixture (no trusted provider-recorded cwd) correctly
omits the field entirely — stronger privacy, same zero-paths guarantee.
The positive basename-on-the-wire case is covered by
sync-project-provenance.
A turn anchored before the range start survives range slicing whole, so
the aggregator can emit a residue day the parse never covered (#1130,
found in #1127's root-cause work). The merge guards already defuse the
overwrite; this stops the residue from reaching durable history at all.
Applied at both ranged call sites (re-derive and gap); the aggregator
and slicer semantics are untouched — whole-corpus callers still
aggregate undated parses freely.
Diagnosed via #1127. Three guards:
- The gap-fill path no longer overwrites cached days wholesale: a
complete parse wins per (date, provider) through the same guarded
merge the re-derive path uses; a partial parse only fills days and
slices the baseline lacks. Previously a parse degraded by the
refresh lock (menubar running) could replace a good day with its
undercount permanently. The gap path also stops dropping
pendingRederive.
- A day whose only content is turn-anchored residue (a midnight-
straddling turn's category counts with zero cost/calls/tokens) is
never sealed: the watermark pulls back so the next launch re-derives
it — self-healing for every broken cache in the wild, no version
bump. Bounded to the settle window and never for the oldest cached
day.
- codeburn doctor gains a read-only Cache health section listing
residue-only dates and failed/empty session-cache entries, so the
next report like #1127 is diagnosable in one command.
Tests: residue-shape pin (day-aggregator), partial-gap cannot shrink a
populated day (fails on the old code), complete-gap parity, watermark
pull-back re-derive.
rolloutFileSessionId sliced 8192 bytes then JSON.parse. Production
session_meta first lines are 22-27 KB, so both trees returned
undefined ids and the overlapping nest source was kept. Read up to
the existing 1 MiB first-line cap and parse payload.session_id from
the complete line. Pin billed-A / nest-A / nest-B at 30 KB on the
exported no-arg singleton.
foldCopilotSupplementaryTurns will not fold a supplementary twin across
a local-day boundary, so the pair can occupy separate turns. Credit
spend now groups at session scope.
filterOverlap required an explicit nest path, so export const codex =
createCodexProvider() never deduped. When the resolved dir is a launcher
nest and ~/.codex is a distinct existing tree, discover both and drop nest
sources whose session id is already billed. Unique nest and billed sessions
stay. Soften the Buzz doctor note to a presence heuristic.
Providers that bill reasoning separately from output (the exclusive set)
were under-reported on the wire by exactly their reasoning volume —
measured on a real pilot push: opencode short by 37,848 tokens, the
summed reasoningTokens of the window. Cost was already correct (priced
locally before export). Route ai.output_tokens through
billableOutputTokens, the same correction the display layer took in
PR #1115. Inclusive providers (claude, codex, copilot) are byte-identical.
Span ids derive from dedup keys that carry no token counts, so nothing
re-sends and nothing duplicates.
Several providers key a project by its sanitized absolute path
("-Users-me-Projects-app"), and sync shipped that verbatim as ai.project
on usage and attribution spans — contradicting docs/sync/README.md's
promise that file paths stay local. Confirmed on real exports.
One choke point after parseAllSessions maps project to the leaf directory
name before either wire path reads it; codeburn yield's local output is
untouched. Never-lose: usage span ids and commit attribution keys carry
no project (unchanged, no re-send); session attribution keys hash the
record, so each already-synced session re-emits once and the receiver
upserts by (org, trace) — replaced, not duplicated. Two directories with
the same leaf name now collapse to one wire project identity; that is
the documented contract, and git.repo still disambiguates.
Owner ruling: state.db restored from backup without wiping the ledger
re-credits regrowth. Wipe both stores or accept the overcount. A
high-water-mark guard is rejected because it would eat 150→0→40 resets.