Repository navigation
feat(usage): add Muse Code local usage provider - #22379
Conversation
Scan Muse session logs (including subagent logs, which hold usage the parent log does not) for model_completed token events and surface them as a fourth local usage provider: shared scan worker, persisted per-file cache reused by mtime/size, cross-log dedupe, Stats tab, and Usage Overview integration. Muse logs carry no price, so the provider reports tokens only.
…g when nothing is priced
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (3)
Included review availability: Your plan provides up to 10 included reviews per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe change adds a Muse usage provider that scans local session logs, extracts token events, attributes them to worktrees, and builds summaries, daily totals, breakdowns, and recent-session data. It connects the provider to the main-process store, worker, IPC, and preload API. The renderer adds Muse to usage overviews and provides a dedicated pane with filtering, status, token statistics, and session details. Tests and translations are also added or updated. Priority: ➖ Normal Merge Risk: ⚪ Minimal · up to The Muse usage change has no identified merge-blocking issue; it is ready for normal checks. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Advanced
Run ID: 45902896-c4be-4820-91c0-88eb23c4dd98
📒 Files selected for processing (57)
src/main/ipc/register-core-handlers/register-core-handlers.test.tssrc/main/ipc/register-core-handlers/register-core-handlers.tssrc/main/ipc/usage-provider-handlers.test.tssrc/main/ipc/usage-provider-handlers.tssrc/main/muse-usage/muse-session-log-discovery.tssrc/main/muse-usage/muse-usage-provider.tssrc/main/muse-usage/muse-usage-record-parser.tssrc/main/muse-usage/persisted-state-normalization.tssrc/main/muse-usage/scanner.test.tssrc/main/muse-usage/scanner.tssrc/main/muse-usage/snapshot-rollups.test.tssrc/main/muse-usage/snapshot-rollups.tssrc/main/muse-usage/store.tssrc/main/muse-usage/types.tssrc/main/startup/main-process-account-services.tssrc/main/startup/main-process-observers.tssrc/main/startup/main-process-preflight.tssrc/main/startup/main-process-quit.tssrc/main/startup/main-process-state.tssrc/main/startup/main-window-controller.tssrc/main/startup/main-window-core-services.tssrc/main/startup/main-window-service-readiness.tssrc/main/usage/usage-provider-contract.tssrc/main/usage/usage-scan-worker-client.tssrc/main/usage/usage-scan-worker-entry.tssrc/main/usage/usage-scan-worker-protocol.tssrc/main/usage/usage-scan-worker-spawn.tssrc/preload/api-types.tssrc/preload/api/agent-usage-api.tssrc/preload/api/muse-usage-bridge.tssrc/preload/index.tssrc/preload/usage-provider-api.test.tssrc/preload/usage-provider-api.tssrc/renderer/src/components/settings/settings-interface-secondary-section-renderers.tsxsrc/renderer/src/components/stats/MuseUsageDetails.tsxsrc/renderer/src/components/stats/MuseUsagePane.tsxsrc/renderer/src/components/stats/StatsPane.tsxsrc/renderer/src/components/stats/UsageOverviewPane.tsxsrc/renderer/src/components/stats/stats-search.tssrc/renderer/src/components/stats/usage-overview-daily-series.tssrc/renderer/src/components/stats/usage-overview-model.test.tssrc/renderer/src/components/stats/usage-overview-model.tssrc/renderer/src/components/stats/usage-overview-sections.tsxsrc/renderer/src/components/stats/usage-overview-types.tssrc/renderer/src/components/stats/usage-provider-normalization.tssrc/renderer/src/i18n/locales/en.jsonsrc/renderer/src/i18n/locales/es.jsonsrc/renderer/src/i18n/locales/fr.jsonsrc/renderer/src/i18n/locales/ja.jsonsrc/renderer/src/i18n/locales/ko.jsonsrc/renderer/src/i18n/locales/zh.jsonsrc/renderer/src/store/index.tssrc/renderer/src/store/slices/store-test-helpers.tssrc/renderer/src/store/slices/usage-provider-slices.tssrc/renderer/src/store/slices/usage-web-client-fallback.test.tssrc/renderer/src/store/types.tssrc/shared/muse-usage-types.ts
Included review availability: Your plan provides up to 10 included reviews per hour; 6 remain after this review.
There was a problem hiding this comment.
ℹ️ No critical issues — two minor rough edges worth a look.
Reviewed changes
- New Muse Code local usage provider (
src/main/muse-usage/): readssession.jsonllogs fromresolveMuseSessionsDir(), parsesmodel_completedusage (cached nested in input, reasoning nested in output), unwrapsretained_framebatches, rolls subagent logs into the parent session/cwd, dedupes records copied across logs, and caches per-file bymtimeMs/size. - Shared-worker integration:
usage-scan-worker-{protocol,client,spawn,entry}.tslearn a'muse'provider; the worker graph stays electron-free (enforced byplain-node-entry-guard.ts). - Main-process wiring: IPC
registerUsageProviderHandlers, preload bridge/PreloadApi, preflight path init, observers, account-services guard, window readiness, quit flush, and settings persistence all follow the existing provider pattern. - Renderer: new Muse stats tab (
MuseUsagePane/MuseUsageDetails), Muse folded into the Usage Overview providers/heatmap/token mix (museTokenson daily points), store slice, and search keyword. - Overview cost warning:
hasPartialCostnow also requireshasKnownCost, so "some model prices are unavailable" no longer shows when no provider is priced. - i18n: 13 new strings across en/es/fr/ja/ko/zh; two hashed keys replaced with named keys.
ℹ️ A deferring log is not reparsed when its owner is rewritten in place
Reclaim of a shared record only fires when the owning log's path disappears (lostOwnerPath). If an owner is later rewritten/truncated so it no longer contains a record that a sibling also holds — while the sibling's mtimeMs/size are unchanged — the sibling is reused without re-claiming it, so the record is under-counted until the sibling's stat changes. Under the stated append-only assumption this is unreachable; flagging only in case Muse compacts or rotates these logs.
Technical details
# Reclaim gate keys on a vanished path, not on changed owner content
## Affected sites
- `src/main/muse-usage/scanner.ts:125-129` — `lostOwnerPath` is true only when a previously-owned path is absent from `currentPaths`
- `src/main/muse-usage/scanner.ts:142-151` — `isStatUnchanged` gates reclaim on `mustReclaimDeferred`, which is false when no path vanished
- `src/main/muse-usage/scanner.ts:153-161,175-176` — pre-registration + reuse let an unchanged deferring file keep `ownedEventKeys` without re-claiming
- `src/main/muse-usage/scanner.ts:74-85` — `eventKey` is content-based, so the same record in two logs collides as intended
## Required outcome
- A shared record present in a deferring log should still be counted if its current owner no longer contains it, even when the owner's path still exists.
## Open questions for the human
- Are Muse `session.jsonl` files strictly append-only (grow-only)? If so, no change is needed and the reuse optimization is sound.ℹ️ Nitpicks
- Two parallel "Stats & Usage" descriptions still enumerate only Claude/Codex/OpenCode:
settings-navigation-interface-sections.ts:81-84(keyb351014180) and the search entrystats-search.ts:7-10(key26bb901fcd). Inline note on the changed file.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
…ning Stats & Usage copy
|
Re: pullfrog's note about a log rewritten in place being under-counted. Muse's |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
Delta since the prior Pullfrog review (54ec10b → cbe03ad):
- Unreadable sessions root now surfaces instead of reading as empty —
listSubdirectoriesgained a{ reportUnreadable }option; only the sessions-root read rethrows non-ENOENTerrors (EACCES/ENOTDIR) so they reachscanState.lastScanError, while nested level reads stay tolerant. A scanner test proves a file in place of the root rejects withENOTDIR. - Stats & Usage copy completed —
stats-search.ts(key26bb901fcd) andsettings-navigation-interface-sections.ts(keyb351014180) now name Muse alongside Claude/Codex/OpenCode, in the source default and all six locale catalogs, matching the equivalent string updated earlier insettings-interface-secondary-section-renderers.tsx.
This resolves the one prior inline note (thread resolved). The new error flows through the existing worker → UsageProviderStoreLifecycle → lastScanError path unchanged from the other local providers, so the fix is consistent with the established pattern.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to GitHub limitations.
🟡 Minor · Do not discard repeated keys within one session log. · muse-usage-record-parser.ts:62-110
src/main/muse-usage/muse-usage-record-parser.ts:62-110
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winDo not discard repeated keys within one session log.
retained_frameunpacks each child as a separate record. Twomodel_completedchildren can sharerecorded_atand token counts while having different models or run data. The current localownedEventKeyscheck drops the second event beforeclaimEventKeyruns. SinceclaimEventKeyaccepts a key already owned by the same path, removing this check counts both events and preserves cross-file copy deduplication.Suggested fix
for (const parsed of parseMuseUsageLine(line, context)) { - if (ownedEventKeys.has(parsed.eventKey)) { - continue - } if (!input.claimEventKey(parsed.eventKey)) { hasDeferredClaims = true continue
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Advanced
Run ID: 5cc602ae-aae1-49df-a2f5-493cf2fa7cd6
📒 Files selected for processing (10)
src/main/muse-usage/muse-session-log-discovery.tssrc/main/muse-usage/scanner.test.tssrc/renderer/src/components/stats/stats-search.tssrc/renderer/src/hooks/settings-navigation-interface-sections.tssrc/renderer/src/i18n/locales/en.jsonsrc/renderer/src/i18n/locales/es.jsonsrc/renderer/src/i18n/locales/fr.jsonsrc/renderer/src/i18n/locales/ja.jsonsrc/renderer/src/i18n/locales/ko.jsonsrc/renderer/src/i18n/locales/zh.json
🚧 Files skipped from review as they are similar to previous changes (9)
- src/main/muse-usage/scanner.test.ts
- src/renderer/src/components/stats/stats-search.ts
- src/renderer/src/i18n/locales/en.json
- src/renderer/src/i18n/locales/es.json
- src/main/muse-usage/muse-session-log-discovery.ts
- src/renderer/src/i18n/locales/fr.json
- src/renderer/src/i18n/locales/ko.json
- src/renderer/src/i18n/locales/ja.json
- src/renderer/src/i18n/locales/zh.json
Included review availability: Your plan provides up to 10 included reviews per hour; 5 remain after this review.
|
Re @coderabbitai outside-diff "Do not discard repeated keys within one session log" (muse-usage-record-parser.ts:62-110): fixed in 6d6a0c6.
|
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
Delta since the prior Pullfrog review (cbe03ad → 6d6a0c6):
- Same-content records in one log now count separately —
parseMuseUsageFilederiveseventKeyas<content>:<ordinal>from a per-logoccurrencesByContentKeycounter, replacing theownedEventKeys.hasskip that silently dropped a distinct secondmodel_completedsharingrecorded_atand token counts (the CodeRabbit outside-diff finding). A fork that copies the same sequence numbers occurrences identically, so its copies still map onto the owner's keys and defer — the cross-log dedup invariant holds.listMuseSessionLogFilessorts by path, so ownership stays deterministic. - Cache schema bumped to v2 —
MUSE_USAGE_SCHEMA_VERSIONmoves to 2 sonormalizeMuseUsageStatediscards v1's unsuffixedownedEventKeysand rescans instead of mixing key formats, while preserving the user's opt-in (scanState.enabled). - New test —
scanner.test.tscovers aretained_frameholding two identical turns in the parent plus a fork with copies: the first scan counts 220, an unchanged rescan stays 220, and appending a third turn yields 330. It fails with the old behavior, where the parent's second turn was skipped.
Verified locally: pnpm test src/main/muse-usage/scanner.test.ts (10 passing). The only prior Pullfrog thread (Stats & Usage copy listing three providers) was resolved in cbe03ad; nothing else is outstanding.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
fd -p 'muse-usage-record-parser.ts|scanner.ts|scanner.test.ts|muse-usage-provider.ts' src/main/muse-usage || true
ast-grep outline src/main/muse-usage/muse-usage-record-parser.ts --items all
ast-grep outline src/main/muse-usage/scanner.ts --items all
rg -n -C 5 'occurrencesByContentKey|ownedEventKeys|hasDeferredClaims|claimEventKey|MUSE_USAGE_SCHEMA_VERSION|identical|fork|append|rescan' src/main/muse-usage/scanner.ts src/main/muse-usage/scanner.test.ts src/main/muse-usage/muse-usage-provider.ts
sed -n '50,120p' src/main/muse-usage/muse-usage-record-parser.tsLength of output: 18636
✏️ Learnings added
If you found this review helpful, would you consider giving us a shout-out on X? Thank you for using CodeRabbit! You are interacting with an AI system. |

ELI5
Orca's Stats & Usage page can now show how many tokens you spent in Muse Code, next to Claude, Codex and OpenCode. It reads the session logs Muse already writes on your computer, so there's nothing to set up beyond turning it on.
What Changed
src/main/muse-usage/) reads themodel_completedevents that Muse writes to~/.local/share/muse/sessions/YYYY/MM/DD/<session>/session.jsonl($XDG_DATA_HOMEis respected).retained_framerecords.subagent/<child>/session.jsonlfiles. It adds those to the parent session, because those files don't record a workspace.Why
Issue #19224 proposed this local-log approach as Option A. Muse's logs already contain exact per-call token counts, which is the same kind of data the existing local providers read, so this fits the existing design with no new moving parts.
Live rate limits (Option B) are left out on purpose. Muse's host protocol has
usage/readfor its 5-hour and weekly windows, but only inside a runningmuse serveprocess, and only after it has made a model request. Nothing is saved to disk, and Orca runs the normal Muse terminal UI, notmuse serve. Reading them would mean running Orca's own Muse sessions through that protocol, or waiting for Muse to add a standalone way to read usage. Orca doesn't touch Muse's credentials.Linked Issue
Fixes #19224
Visual Proof
main)After, Muse tab (all local Muse usage) with the updated description:
With only Muse enabled, the Overview shows cost as n/a without the "some model prices are unavailable" note.
Testing
I manually tested these changes locally
Automated tests added/updated, or explained why not below
Manual check (macOS): I ran a dev build against real Muse 1.3.0 logs. A separate Python count over all 85 log files (including 51 subagent logs) matches the app exactly: 1,406,537 total tokens, 1,376,190 input, 30,347 output, 928,260 cached, 17 sessions, 85 events, and the same per-day totals.
Scan speed on those logs: 40 ms the first time and 4 ms after that, with nothing re-read on the second pass.
Automated:
retained_frame, subagent roll-up and duplicate events), the scanner (on-disk log trees), and the summary/breakdown maths.pnpm tc, the localization checks, the max-lines ratchet and the changed-code quality gate pass.AI Disclosure
Anthropic Claude assisted with implementation, validation, and this pull request description.
Review
Follows the provider pattern from the existing Claude/Codex/OpenCode usage providers and the community DeepSeek provider (#12873).
Agent skill upstream boundary
docs/reference/agent-skill-sharing-upstream-boundary.mdand copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.Notes
%USERPROFILE%\.local\share\muse, which I checked on a Windows host for feat(agents): add first-class Muse Code harness #22216.Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (typecheck, focused suites and quality gates pass locally; CI covers the full suite and build)