Conversation
|
Heads-up on the pricing commit ( |
|
Hi @DyRize! First off, thank you for tackling multi-account tracking here. The scope, attention to edge cases, and test suite are really impressive. Having recently touched the Keychain prompt handling and session-key flow in #275 and #280, I wanted to share a few thoughts and trade-offs on the open questions you raised:
Really excited about this direction. Hope these observations are helpful, and happy to help test the Keychain flow if needed! 🤖 Generated with Antigravity 2.0 (AGY 2.0) |
Two Claude Code logins side by side (CLAUDE_CONFIG_DIR) only showed the default account's limits. Additional config folders are now detected (~/.claude-* and ~/.claude_* folders Claude Code is logged in to, plus an exported CLAUDE_CONFIG_DIR) or listed in Settings > Advanced, and each account gets its own tab in the official limits. - One token cache per folder: its .credentials.json, or the "Claude Code-credentials-<sha256 prefix>" Keychain item. Automatic polls never read the Keychain; a manual refresh may prompt once per folder and stops after a declined prompt (chattymin#280). - Candy and threshold alerts cover every account's gauges. The default account keeps its historical keys, so existing saves carry over. - A rejected token marks that account expired (dimmed values, retry banner) instead of pinning the refresh row. A 429 pauses that account only. The default tab stays while its limits load. - The refresh row shows why a manual refresh failed. - The profile cache keeps one identity per token. - Menu bar percentage, warning state, companion mood, floating pet hover and the 5h forecast still follow the default account.
With several Claude accounts, the menu bar percentage, warning state, companion mood, floating pet hover and 5h forecast followed the default account only, even while working on another one. A "Tracked Claude account" setting, shown when several accounts are listed, now drives them: - Automatic (default): the account that received the latest prompt, read from the modification date of each config folder's history.jsonl, which Claude Code never shares between logins. - The default account, a pinned account (back to automatic when it disappears), or the highest usage across accounts. The tracked tab gets a dot, the 5h forecast shows in its tab, and each additional account shows its own stale label after 15 minutes.
Transcripts carry no account and projects/ may be shared between logins, but each login's history.jsonl lists its prompts with their session and time. Usage entries now keep their session (from the transcript path, also for blobs cached before this change, without a re-parse), and each turn goes to the login that sent the latest prompt of its session before it, so a session resumed on another login changes owner at that point. Each account tab shows its today and month tokens and cost; turns no history explains are counted as unattributed and shown under the tabs. The header totals stay machine-wide. The default account's placeholder tab now only appears when that login exists. README (EN/KO/JA) documents the new data sources and the Keychain reads per config folder.
Account folders that keep their own projects/ were never scanned, so their turns were missing from every total. Each config folder now adds its projects/ to the Claude scan roots; a symlinked shared folder folds into the default one. Review fixes on the multi-account work: - history.jsonl is read again only for its new complete lines, and a saved login is read again only when its file changes. - The 5h forecast uses the tracked account's own block with several accounts, and the scan covers a block that started last month. - Additional account refreshes run one after the other. Turning Keychain access off clears them. With a session key, only folders listed in Settings may prompt. - The same email in another organization is another account. A folder of an account already shown adds its activity and usage to that tab; usage of an account without a tab is unattributed. - An account first seen at its limit earns no candy until seen below it. - Changing the tracked account updates the companion at once, an automatic success clears the manual refresh error, and the refresh row waits for default limits only when the default login exists. - History, usage and last prompt dates are read from the user's folders only inside the app, like the folder discovery. README (EN/KO/JA) lists the per-folder projects/ and the incremental history read.
With several accounts the refresh error sits above the account tabs and read as everyone's failure. It now names the default account, the only one it is about. The 5h block row says it sums all accounts, the session key hint says it only covers ~/.claude, and additional account refreshes are logged like the default one.
Claude Code hashes CLAUDE_CONFIG_DIR as given, so a folder exported with a trailing slash (as shell completion writes it) lives under another Keychain item. Both names are tried; a missing item moves on, a cancelled prompt stops the lookup. The menu bar percentage names its account in a tooltip when there are several, and the Settings refresh error names the account too and wraps instead of being cut at two lines.
…centage With several accounts, only the additional ones were named in alerts and candy notifications, and the name sat before the percentage: the floating pet bubble, cut at two lines, could lose the percentage. Every account is now named next to others, alerts put the account after the percentage, and the bubble drops an account name that does not fit.
The store already falls back to the last used account; the picker showed an empty selection instead.
macOS can ask twice per Keychain item (access, then partition), and again after every "Allow" that was not "Always Allow", so the hint now says a manual refresh may ask for each folder.
…le session With several accounts the current 5h block summed every account and ended at neither account's reset. Each tab now shows its own block, like the single account view. A 5-hour window at 0% without a reset date has not started: the row now says it starts with the next message instead of showing nothing.
The local block starts at the first turn of the last five hours, so its reset rarely matched the account's real one. A tab now counts the turns of the account's running official window and ends at its reset. With no session running there is no block; expired or missing limits keep the rolling block.
61d8573 to
8edaca2
Compare
|
Thanks @justinjeong5, this is exactly the feedback I was hoping for, and #280's prompt handling is what this branch builds on.
The branch is now rebased on main: the pricing commit is dropped since #304 landed, and the account tabs are merged with the new pace marks, so the 5h row shows both the pace tick and the idle session hint. I would happily take you up on the Keychain testing offer, the two-folder setup is the one path I cannot fully cover with tests. |
|
Hi @DyRize, I ran a local test of the two-folder setup on macOS (Apple Silicon) using a simulated secondary directory (
(Note: Since I tested with a simulated secondary folder, I haven't completed an end-to-end quota fetch with a live second Anthropic account yet, but the discovery pipeline and silent Keychain guard contracts hold up solidly on real macOS!) 🤖 Generated with Antigravity 2.0 (AGY 2.0) |
Summary
Many people run two Claude subscriptions side by side, for example an employer Team plan and a personal Pro plan, each logged in its own
CLAUDE_CONFIG_DIR. Token counting already follows extra config folders, but the official limits only read the default login, so the popover and the menu bar keep showing that account while you work on the other one. This PR shows every Claude account the machine is logged in to.Finding the accounts
~/.claude-*and~/.claude_*folders whose.claude.jsonholds a signed-in account, plus theCLAUDE_CONFIG_DIRexported by the login shell (already resolved byUsageEnvironment), plus a new "Additional Claude accounts" field in Settings → Advanced for folders stored elsewhere. The field only adds folders, like the custom scan roots. Many people setCLAUDE_CONFIG_DIRin a shell alias only, which is why discovery looks at the folders too.AppEnv.isBundledApp), soswift testnever touches real folders or the Keychain..claude.jsonis parsed again only when its modification date or size changes.Claude Code-credentials-<first 8 hex chars of SHA-256(CLAUDE_CONFIG_DIR)>(/Users/example/.claude-workmaps to-dd1118a7), or in<config dir>/.credentials.json. It hashes the variable as given:claude auth status(2.1.274) reports the same folder exported with a trailing slash as logged out. The app therefore tries the folder path, then the same path with a trailing slash (what shell completion writes). A missing item moves on without a prompt, and a cancelled prompt stops the lookup (fix(limits): prioritize user account over MCP placeholders to prevent duplicate Keychain prompts #280).Keychain rules are unchanged. Automatic polling never reads the Keychain. A manual refresh may prompt for each account. "Disable Keychain access" stops every account. With a claude.ai session key set, a manual refresh only prompts for folders listed by hand in Settings.
Popover
CapsuleTabBaris extracted from them, and their rendering is unchanged). With a single account nothing changes.CLAUDE_CONFIG_DIR=<folder>and a Retry button. 429 backoff is per account, so a rate-limited account no longer pauses the others.Tracked account. A new setting in Settings → General, shown only when there are several accounts: Automatic (last used, from the modification date of each folder's
history.jsonl), Highest usage, or one specific account. It drives the menu bar percentage, the warning state and companion mood, the floating pet hover, and the 5h forecast, which now uses that account's own 5h block.Tokens and cost per account
history.jsonl. The file is read incrementally, and prompt text is ignored. A session resumed on another account changes owner at that point.-p, SDK, older than the history) are not guessed. They appear as "Not linked to an account this month".projects/is added to the Claude scan roots. Aprojects/symlinked to~/.claude/projectscollapses into the default root.Candy and alerts apply to every gauge of every account. With several accounts, each notification names its account. Alerts put it after the percentage ("Claude 5-hour session at 96% · Acme Corp"), and the floating pet bubble, cut at two lines, drops the name when it does not fit, so the percentage always shows.
claude.fiveHour,claude.sevenDay), so existing saves stay valid. Other accounts useclaude.<8 hex chars of the folder hash>.fiveHour, so removing and adding a folder back doesn't pay twice.Claude 5 pricing.$99 instead of "$ —". It is a separate commit (
claude-opus-5andclaude-sonnet-5were missing fromModelPricing, so a day on Opus 5 showed "$—" in the menu bar and a zero cost per account. They now use the published rates (https://platform.claude.com/docs/en/about-claude/pricing): Opus 5 $5 / $25 per million (5-minute cache write $6.25, cache hit $0.50), Sonnet 5 $2 / $10 ($2.50, $0.20). The existing test that keptclaude-opus-5unpriced now checks both rates, and later ids such asclaude-opus-5-1stay unpriced. Checked on a real day of Opus 5 use: 127.6M tokens, aboutfix(pricing): …), so it can move to its own PR if you prefer.Docs. README (en, ko, ja) describes the new data sources (other config folders,
history.jsonl, the per-folder Keychain items) in the privacy notes.Relation to #201. #201 attributes token counts to accounts with an epoch ledger that assumes one live account at a time. This PR handles accounts that are live at the same time, and attributes usage through each folder's
history.jsonl. I'm happy to align the two designs.Type of change
UI changes
Demo data: the two tabs add up to the machine-wide totals (38.4M + 7.7M = 46.1M tokens, $41.85 + $8.37 = $50.22), and 640K tokens belong to a session missing from both histories.
Checklist
swift buildandswift testpass locally (1173 tests, 11 skipped, 0 failures;scripts/test-gate.shpasses at 93.91% line coverage of the logic core)AdditionalClaudeAccountsTests, plus cases inKeychainAutoPathTests,LocalizationInterpolationTestsandLocalUsageReaderTests; each new branch was checked by injecting a defect and watching a test fail)Open questions
apple-tool:partition. For an app outside that partition, macOS asks twice per item, even with "Always Allow": once for access, then once more after an "ACL partition mismatch" (seen in thesecuritydlog). With "Always Allow" this happens once per app signature (every build for an ad-hoc signed dev build). The app reads each item once per manual refresh (the silent read never prompts), so this is macOS behavior, but every extra account adds its own prompts, and a claude.ai session key only removes the default account's. Is a per-account session key worth a follow-up?CLAUDE_CONFIG_DIRspellings. The app covers the folder path with and without a trailing slash. A value such as~/./.claude-workor~/.claude-work//maps to yet another Keychain item that the app does not try. Worth reading the exported value verbatim instead?