Release v5.0.1 - #44
Merged
Merged
Conversation
Empty commit to open a fresh PR into main and drive release.yml once GitHub Actions recovers from the outage. No code change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The subscription login did not survive a restart, on Linux and on Windows alike, and the cause was not persistence. The credential is in the IDE PasswordSafe and therefore in the OS store (KWallet or GNOME Keyring through the Secret Service, Keychain, Credential Manager), and it was still there after the reboot. What expired was the access token inside it - `auth login` issues one good for about ten hours, so any restart the next day found a perfect credential that authenticated nothing, `hasUsableToken()` answered false, and false meant signed out. The blob always carried a refresh token good for weeks, and nothing was allowed to spend it, since spending it means the binary rewriting `~/.claude/.credentials.json` - the file the vault exists to remove. The way out is in the binary and is a first-class path. With the environment carrying CLAUDE_CODE_OAUTH_REFRESH_TOKEN and CLAUDE_CODE_OAUTH_SCOPES, `claude auth login` takes a dedicated non-interactive branch, mints a credential into its own store and exits. Verified against 2.1.223 that the branch is genuinely non-interactive - an invalid refresh token fails on the HTTP round-trip and exits 1, with no browser and no TTY wait. So renewal is the binary's job, exactly as it always was, and the plugin's job stays what it was - capture the account while the config is freshest, harvest the credential off the disk, delete it. The plugin still holds no OAuth client, calls no token endpoint and never writes that file back. This is one bug rather than two. The binary's default credential store is its `plaintext` provider on every platform, so the vault path and the expiry are identical everywhere. No platform-specific code was needed; the only Windows-specific care is that the renewal environment strips CLAUDE_CODE_OAUTH_TOKEN case-insensitively, since environment names are case-insensitive there. An expired-but-renewable blob now counts as an identity (`CredentialsVault.canRenew`), the renewal runs off the EDT in `launch()` before the env is built and never while a sign-in is in flight, the refresh token rotates at every renewal so ordinary use extends it indefinitely, and a failure arms a five-minute cooldown because the boot watcher polls every three seconds. Also drops a leftover CC-TRACE prefix from a rate-limit debug log. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The usage windows and the extra-credit balance were rounded to a whole number on the Kotlin side before they ever reached the web app, so the dashboard bars and the composer dots could only ever read as integers. The percentage now travels as a Double and the front end formats it with `toFixed(1)`, which also makes the locale question visible - whether the decimal separator renders as a comma or a dot is now something the UI can be observed doing rather than guessed at. Removes `JcefSessionData.pctOf` and `JcefState.normalizePercent` along with it, since neither has a caller once the rounding moves to the display layer. The clamping they performed goes with them; the event-sourced windows are still multiplied by 100 exactly as `RateLimitInfo.utilizationPercent` did. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The 5.0.1 notes described the fix without naming the credential it applies to, which leaves an API-key user unable to tell whether it concerns them. It does not. An Anthropic API key is a different identity in a different slot (`providerApiKey:anthropic` in the same PasswordSafe, not `CLAUDE_CREDENTIALS_JSON`), it has no expiry and no refresh token, so nothing was lost across a restart and nothing is renewed now. `CredentialsVault.renew` reads the `claudeAiOauth` blob and nothing else, and `envOverlay` withdraws entirely when an API key is present. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…-session fix(auth): renew the vaulted login instead of asking again
Back-merge of the v5.0.0 release merge commit, so develop is up to date with main and the 5.0.1 release pull request can be merged. Content no-op: main carries no change develop does not already have.
…op-with-main chore: back-merge main into develop
serialexperimentslainnnn
added a commit
that referenced
this pull request
Aug 10, 2026
Merge pull request #44 from serialexperimentslainnnn/develop
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Promotes
developtomainfor the 5.0.1 patch release. See CHANGELOG.md for the full entry.What ships
The subscription login now survives a restart (#43). Reported on Linux and Windows, and it was one bug rather than two: the credential persisted correctly in the OS store all along — what expired was the access token inside it, issued for ~10 hours. The refresh token beside it, good for weeks, was never spent, because spending it means the binary rewriting the plaintext credentials file the vault exists to remove.
The fix uses the binary's own non-interactive
auth loginbranch (CLAUDE_CODE_OAUTH_REFRESH_TOKEN+CLAUDE_CODE_OAUTH_SCOPES) to mint a fresh credential with no browser, no TTY and no user, then takes custody of it exactly as every other credential path here does. The plugin still holds no OAuth client, calls no token endpoint and never writes that file back — the vault's invariant is intact, only its cost is gone.Scope: the subscription (OAuth) credential only. An Anthropic API key lives in a different slot, has no expiry and no refresh token, and is untouched.
Also in the release: plan-limit percentages now render with one decimal in the dashboard bars and composer dots.
Release gates
The full gate runs on this PR (
Static analysis,Dependency audit,Plugin verifier,Build pluginandNo bot PRs pending on developare skipped on PRs intodevelopand only apply here). Every one of them was also run locally on the merged tree:test koverVerifydetekt/spotlessChecknpm test(vitest)npm run lint/format:checknpm audit --omit=dev --audit-level=lowverifyPluginTo be merged with a merge commit, not a rebase, so the release tag points at a commit that exists on both branches (docs/RELEASE_PROCEDURE.md section 6). Publication remains gated on the
marketplaceenvironment approval.🤖 Generated with Claude Code