Skip to content

feat: show local plan usage for Claude, Cursor, Codex, and more signed-in CLIs - #8679

Open
marcelocecin wants to merge 13 commits into
multica-ai:mainfrom
marcelocecin:feat/provider-plan-usage
Open

marcelocecin wants to merge 13 commits into
multica-ai:mainfrom
marcelocecin:feat/provider-plan-usage

Conversation

@marcelocecin

@marcelocecin marcelocecin commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

Shows plan usage for signed-in local CLIs/editors inside Multica. The daemon on the host reads the session already present on that machine and uploads only derived fields: provider, window, percent used, reset time, plan name, and collected_at. The server does not call vendor APIs and does not store bearer tokens, cookies, or auth files.

A missing or unusable session (not signed in, API-key-only auth, CLI missing, expired local token) is an empty snapshot. It does not fail agent tasks. An expired token is not refreshed and does not replace the last good snapshot.

Related Issue

Closes #

Type of Change

  • New feature (non-breaking change that adds functionality)

Changes Made

  • Daemon collectors (host where the CLI/editor is signed in):
    • Claude Code: parse stdout of claude --print --no-session-persistence --strict-mcp-config /usage. No keychain read and no login refresh.
    • Cursor: read the local editor session and call GET https://cursor.com/api/usage-summary. Upload autoPercentUsed / apiPercentUsed, billing-cycle end, and membership type. Cookie stays in process memory.
    • Codex: GET https://chatgpt.com/backend-api/wham/usage using ~/.codex/auth.json. Map primary_window and secondary_window. API-key-only auth reports empty and does not call the API.
    • GitHub Copilot: gh hosts file or gh auth token, then the Copilot quota API. GH_TOKEN / GITHUB_TOKEN are ignored.
    • Antigravity: ~/.gemini/oauth_creds.json only, then Cloud Code quota. No keychain prompt and no language-server scan.
    • Grok: trusted https://auth.x.ai entry in ~/.grok/auth.json. Expired tokens are not refreshed (unlike CodeNotch fix(daemon): support Windows by extracting platform-specific SysProcAttr #393 in-memory refresh).
    • Kimi: ~/.kimi-code/credentials/kimi-code.json (KIMI_CODE_HOME).
    • Kiro: kiro-cli chat --no-interactive /usage stdout. The sqlite token store is not opened.
    • OpenCode: per data directory, in order: auth.json key opencode-go, then SQLite opencode.db table credential (read-only), then the mirrored opencode OAuth entry in auth.json. Go key → https://opencode.ai/zen/go/v1/usage; OAuth → https://opencode.ai/inference/go/v1/usage with x-opencode-org-id when metadata.orgID is present. Expired credentials upload credential_expired and are not refreshed (needed for OpenCode 1.18+ / 2.x, which stopped writing auth.json on sign-in — CodeNotch v1.20.0 / fix(inbox): archive at issue level instead of event level #398).
  • Poll about every five minutes, back off on HTTP 429, keep the last good snapshot.
  • New runtime_provider_usage_snapshot rows, separate from task_usage. No foreign keys. 564 creates the table; concurrent indexes are 565 and 566. Upstream main occupies 548–563; open PR feat(agent): add Prime Agent as a native ACP provider #6641 uses 567.
  • POST /api/daemon/runtimes/{runtimeId}/provider-usage and GET /api/runtimes/{runtimeId}/provider-usage, plus GET /api/runtimes/provider-usage for the list. The handler rejects token-like fields.
  • Runtimes list: a Usage column, one cell per row.
  • Runtime detail: Plan usage shows only that runtime’s provider.

Sync with main

Merged current multica-ai/multica main through 2ea01ae4e (changelog v0.6.1, 2026-10-01) into feat/provider-plan-usage as 17d4f775a. Catch-up merges; no content conflicts on the latest sync.

Provider-usage migrations stay 564 / 565 / 566. Main still ends at 563. Sibling PR #6641 owns 567.

Contracts kept: derived upload fields only; token-like fields rejected; OpenCode order above; no token refresh.

CodeNotch follow-ups

  • v1.19.0: compared v1.18.0...v1.19.0. No collector change (notch UI + Settings hang unrelated to our reads).
  • v1.20.0: OpenCode credential path updated as above. Grok in-memory refresh and Abacus.AI left out of scope.

How to Test

  1. cd server && go test -count=1 -timeout 180s ./internal/daemon/providerusage/
  2. cd server && go test -count=1 -timeout 120s ./internal/handler/ -run 'TestRejectCredentialFields|TestNormalizeProviderUsageReport|TestProviderUsageErrorDoesNotEchoSecrets|TestWorkspaceDeletionManifestCoversPublicSchema|TestProviderUsageBatchGroupsByRuntime'
  3. cd server && go test -count=1 -timeout 60s ./cmd/migrate/ -run TestEveryConcurrentUpBuildHasCleanup
  4. pnpm --filter @multica/core exec vitest run api/schemas.test.ts
  5. pnpm --filter @multica/views exec vitest run runtimes/components/provider-usage-block.test.tsx runtimes/components/runtime-plan-usage-cell.test.tsx locales/parity.test.ts
  6. On a machine with any of the supported CLIs already signed in, run this daemon and open the runtimes list, then each runtime page. Confirm the list cell matches that provider only, and the detail page does not show other providers.

Default tests use fixture stdout and HTTP responses. They do not run a real CLI or call vendor APIs.

Checklist

  • I have included a thinking path that traces from project context to this change
  • I have run tests locally and they pass
  • I have added or updated tests where applicable
  • If this change affects the UI, I have included before/after screenshots
  • I have updated relevant documentation to reflect my changes
  • If I added a new runtime / coding tool / UI tab, I synced the change to landing copy (apps/web/features/landing/i18n/) and relevant docs (apps/docs/content/docs/)
  • If this PR touches Chinese product copy, I checked it against apps/docs/content/docs/developers/conventions.zh.mdx (terminology, mixed-rule for task / issue / skill)
  • I have considered and documented any risks above
  • I will address all reviewer comments before requesting merge

AI Disclosure

AI tool used: Cursor

Prompt / approach:
Collect plan limits only on the local daemon from the already signed-in CLI or editor session. Extend collectors for additional Multica-relevant providers that expose a local session (inspired by CodeNotch’s provider surface). Upload derived percents, reset time, and plan name. Keep vendor tokens off the server. Show one usage cell per runtime in the list, and only that runtime on its detail page.

Screenshots (optional)

Risks

  • Several collectors use undocumented HTTP endpoints and local session files already on disk. Credentials stay in process memory and are not written to the snapshot or logs. Those endpoints and file formats can change without notice. Parse failures keep the last good snapshot and do not fail tasks.
  • Claude and Kiro are parsed from CLI /usage text, which can change format.
  • A provider that cannot be read returns an empty snapshot (cli_unavailable, session_unavailable, credential_expired, or unsupported).
  • Providers without a safe local plan-percent path (e.g. Qianwen browser cookie, Gemini CLI token logs, Amp/Devin/Ollama and similar) are intentionally not collected here.

@vercel

vercel Bot commented Sep 22, 2026

Copy link
Copy Markdown

@marcelocecin is attempting to deploy a commit to the IndexLabs Team on Vercel.

A member of the Team first needs to authorize it.

cursoragent and others added 6 commits September 23, 2026 13:51
The daemon reads Claude, Cursor, and Codex usage on the machine where
those tools are already signed in, and uploads only derived percent,
reset, plan, and collected-at fields. The server stores one snapshot
per runtime and rejects token-like fields. Collection failure keeps
the last good snapshot and does not fail agent tasks.

Co-authored-by: Marcelo Cecin <marcelocecin@users.noreply.github.com>
Render the daemon's plan-limit snapshot on the existing runtime usage
section and on an agent's activity tab when a runtime is bound. The
client parses the response with zod and parseWithFallback so a partial
or malformed payload degrades to an empty state.

Co-authored-by: Marcelo Cecin <marcelocecin@users.noreply.github.com>
Concurrent snapshot indexes need an invalid-index cleanup hook, and
the workspace deletion manifest must classify the new runtime-scoped
table. Rows are already removed with the workspace.

Co-authored-by: Marcelo Cecin <marcelocecin@users.noreply.github.com>
Add a Usage column to the shared machine runtimes table. Claude, Cursor,
and Codex rows show the headline plan percent from one batch read of the
existing snapshots.

Co-authored-by: Marcelo Cecin <marcelocecin@users.noreply.github.com>
The detail block rendered every snapshot stored for the machine, so Claude also listed Codex and Cursor. Keep snapshots for that runtime's protocol family and stop the subtitle from naming the other vendors.
Read GitHub Copilot, Antigravity, Grok, Kimi, Kiro, and OpenCode from
the local session already on the daemon machine. Upload only percent,
reset, plan, and collected_at. A missing or expired session stays an
empty snapshot and does not fail agent tasks.

Co-authored-by: Marcelo Cecin <marcelocecin@users.noreply.github.com>
@cursor
cursor Bot force-pushed the feat/provider-plan-usage branch from bb79158 to 830795a Compare September 23, 2026 14:03
@marcelocecin marcelocecin changed the title feat: show local Claude, Cursor, and Codex plan usage feat: show local plan usage for Claude, Cursor, Codex, and more signed-in CLIs Sep 23, 2026
cursoragent and others added 3 commits September 24, 2026 09:35
Bring in v0.5.3 migrations 545-547 so provider usage snapshots can be renumbered after them.

Co-authored-by: Marcelo Cecin <marcelocecin@users.noreply.github.com>
Upstream main owns 545-547. The snapshot table and its concurrent indexes move to the next free prefixes.

Co-authored-by: Marcelo Cecin <marcelocecin@users.noreply.github.com>
Renumber provider usage migrations from 548-550 to 564-566 so they
follow main's 563 search-index migrations. Collector behavior and the
no-tokens-on-server contract are unchanged.

Co-authored-by: Marcelo Cecin <marcelocecin@users.noreply.github.com>
cursor Bot pushed a commit to marcelocecin/multica that referenced this pull request Sep 28, 2026
…> 567

v0.6.0 main took prefixes 548-563, and open PR multica-ai#8679 reserves 564-566
for runtime_provider_usage_snapshot. 567 stays clear of both.

Co-authored-by: Marcelo Cecin <marcelocecin@users.noreply.github.com>
cursoragent and others added 3 commits September 28, 2026 09:29
Bring in v0.6.0 (ea94c7c) and MUL-7780 (67d61a2). Provider-usage migrations stay 564–566.

Co-authored-by: Marcelo Cecin <marcelocecin@users.noreply.github.com>
Co-authored-by: Marcelo Cecin <marcelocecin@users.noreply.github.com>
OpenCode 1.18+ stores the Go or OAuth credential in opencode.db instead
of auth.json. Read that table read-only, keep the auth.json fallbacks,
and pair each credential with the usage route that accepts it. An
expired token uploads an empty snapshot and is not refreshed.

Co-authored-by: Marcelo Cecin <marcelocecin@users.noreply.github.com>
Include multica-ai/multica main through v0.6.1 (2ea01ae).
Provider-usage migrations stay 564/565/566.

Co-authored-by: Marcelo Cecin <marcelocecin@users.noreply.github.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants