Read the Muse Code CLI's sign-in from its credential file, not its presence - #49
Conversation
…esence
`muse logout` (and MSP `account/logout`) rewrite auth.json as
{"schema_version": 1, "providers": {}} instead of deleting it, so the
extension kept reporting the CLI signed in after a sign-out, and a panel
sign-out stayed on "Sign-out is in progress or credentials remain". On
macOS a sign-in lives in the login Keychain and auth.json is a pointer.
- credentialFile.ts parses only the file's structure (schema version,
whether a provider is named, its storage lane); tokens are dropped by
the schema and nothing from the file is kept or logged. Empty providers
are signed out; a Keychain pointer off macOS is a named error instead of
a `muse serve` that exits 3.
- A macOS pointer or an unrecognised file is confirmed with `account/read`
on a short-lived host, cached by path, size and mtime; on macOS only on
a user action, so a Keychain prompt follows one.
- Sign-out uses MSP `account/logout`, confirmed by `account/read`, with the
terminal `muse logout` as the fallback.
- Device sign-in ends on `granted` (confirmed by `account/read`), on
`account/read` while polling, or on a file change; `denied`, `expired`
and `failed` end it at once with their own messages.
- Diagnostics shows the file's verdict, the CLI's state and, on macOS,
whether the Keychain item exists (attribute lookup only).
- The unused `authState.ts`, which still read "file exists = signed in",
is removed with its test.
Drills A-Q in docs/certification/sign-in-detection.md; strings in all 14
tables; PRIVACY, SECURITY, README, AGENTS, PLAN and CHANGELOG updated.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…tion # Conflicts: # CHANGELOG.md # PLAN.md # docs/certification/README.md # src/host/backend/museCodeBackendManager.ts
…tion # Conflicts: # docs/certification/README.md
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1b28b75fd4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
PR #49 review (Codex). P1: the account/loginCompleted handlers were written from the MSP schema's words. P2: a poll awaiting account/read held up Cancel and the host's ending for 30 s when the CLI stopped answering, and loginCancel could take another 30 s. Live capture (owner-authorized; Muse Code 1.4.0-R4302.1, Windows, a throwaway home per run, 0 model attempts, the owner's auth.json stat'ed unchanged): `expired` arrives 600 s after loginStart with a message, and `cancelled` precedes the loginCancel answer. The frames, with the code replaced by its shape, are test/fixtures/msp/account-login-*.json; the unit tests and the fake CLI replay them. `granted`, `denied` and `failed` could not be captured: the Chrome Control extension is disabled in the owner's Chrome, so no code could be approved or declined. - `granted` is neither an ending nor a sign-in: account/read or a new file decides. `expired` keeps its own message; any other outcome ends the flow with one message that shows Muse Code's word (signInEnded, replacing signInDenied and signInNotSaved in all 15 tables). - Each poll, each wait and the pre-start calls race a stop signal (Cancel, or an ending); loginCancel is bounded at 2 s, then the host is closed anyway. - The extension now waits 11 minutes, past the captured code lifetime: at 5 it cancelled codes the browser could still approve, and Muse Code's own `expired` could never arrive. Red drills R to AA recorded in docs/certification/sign-in-detection.md. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0c13b4d5d1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The account/read probe before the device flow ran outside the flow's stop signal, so a CLI that never answered held Cancel and sign-out for the 30-second MSP timeout (the review of PR #49). It now races the flow's abort signal. Drill AB. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 11cc4aceed
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The review of PR #49: - Sign-out after a cancelled sign-in re-asked the CLI and joined the probe the cancel had left unanswered, waiting out the 30-second MSP deadline. Cancel now abandons it (CliAccount.abandonAsking): later questions start afresh, and its late answer is not remembered. - With a poll unanswered, a stop (Cancel, an ending) let the file-only fallback report a sign-in when the file changed, e.g. an unrelated sign-out rewrite. A stop now decides the flow; a real sign-in is still read from the file afterwards. Drills AC and AD. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@codex review |
jscpd found them identical once a racing write became a cancellation too. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6464c23707
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Every captured account/read says credentialRequired: true. The code read false as a keyless gateway, so signed in (the review of PR #49, AGENTS.md rule 13). A signed-out answer with it false is now unknown, like any answer we cannot place, and the device flow treats signed out as signed out whatever the flag. Drills AE and AF. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 934e09de2f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
A signed-out macOS file copied to Windows or Linux names no provider, so it points nowhere; it was read as a Keychain pointer there, which blocked browser sign-in (the review of PR #49). An empty provider map is now signed out before the platform verdict. Drill AG. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ae4f318cdb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
account/logout counted as confirmed on any account/read answer but a stored sign-in, so envKey and the uncaptured credentialRequired false skipped the terminal fallback (the review of PR #49, AGENTS.md rule 13). Only loggedOut with credentialRequired true confirms now; envKey gets its own log line. cliSignInFromAccount shares the check. Drill AH. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2c093c7c05
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
A refresh blocked in account/read could answer after a sign-out (or a new sign-in) finished and overwrite that state with "Sign-out is in progress" (the review of PR #49). A refresh from an older sign-out epoch now returns the current snapshot; a race with the hold release keeps the hold without publishing. Drill AI. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A device sign-in that sign-out cancelled still refreshed and announced "Sign-in cancelled" over the sign-out in progress. It now leaves the state to the sign-out. Found while sweeping for stale publishes after the review of PR #49. Drill AJ. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f2c886d7c7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 19e74b0749
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex on 19e74b0: restartHosts(true) cleared liveBackend after awaiting the restart, so a signed-in state a newer refresh published meanwhile was erased and a later switch skipped ending its conversations. set() now moves liveVersion each time it records the backend; restartHosts clears only if it is unchanged (not before the await, so a failed restart keeps the record of conversations still running). The sweep of every field written after an await in AuthService, CliAccount and runDeviceSignIn found two more: - isLogoutPersistenceFailed: an older logout-hold write that failed late marked the hold unsaved after newer writes were saved, shutting admission. Only the latest write's outcome counts (holdWrites). - CliAccount.answered: a probe about an older version of the file could overwrite the newer version's remembered answer. Starting a probe now abandons the one it replaces. The rest are single-writer by construction, identity-guarded, or only increase. Drills DC to DE each broke one guard, failed its suite and were restored by SHA-256. The local gate did not pass: four runs exited 1 in untouched real-process suites and a11y under load from other worktrees' gates (see the cert record); the touched suites, typecheck, lint, l10n and jscpd passed. The full gate is CI's three-OS run on the pushed commit. Docs: CHANGELOG, docs/certification/sign-in-detection.md. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 86d6365204
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex on 86d6365: when publishSelection's restart rejected late, its catch published signOutStopFailed with a new ticket, skipping the ticket and isCurrent guards the success path uses, so a stale attempt overwrote a newer refresh or a sign-out and closed admission. The failure is now published under the same guards, with the refresh's own ticket. The re-sweep of every catch, finally and error path after an await in AuthService, CliAccount, runDeviceSignIn and accountHost found two more: - confirmCliSignIn: a finished sign-in's rejected restart reached finishFailedCliSignIn and published signInFailed over a refresh made meanwhile; a newer state now stands. - installFailed, the install's error path, published the selection it awaited over a newer state; it now takes a ticket before asking. The other catches publish nothing or belong to the one operation that owns them; the finally blocks reset single-writer or identity-guarded fields. Drills DF to DH each broke one guard, failed its suite and were restored by SHA-256. Checks: authService.test.ts (103), typecheck, lint, l10n, jscpd, format; not the full gate (machine loaded by other worktrees' gates). The full gate is CI's three-OS run on the pushed commit. Docs: CHANGELOG, docs/certification/sign-in-detection.md. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 55b9e24cd7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex on 55b9e24: on macOS an approval may update only the Keychain, the pointer file's mtime unchanged. If Cancel won before the device host's next account/read, the flow reported signedOut and kept the CLI's answer from before it, although the login had landed. A Cancel after the code was shown, with the file unchanged, now forgets the CLI's answers; the cancellation shows at once, then a user-action refresh asks account/read afresh (bounded by the account host's deadline and the sign-out signal) and publishes through publishSelection's guards, with the cancellation as a notice. A Cancel before any code ends as before. Sibling: when the file changed as Cancel was pressed, the refresh was passive, so macOS estimated the new file instead of asking; a Cancel is a click, so it asks now. Drills DI to DK each broke one guard, failed its suite and were restored by SHA-256. Checks: authService, cliAccount, conversationController and the cliAccount e2e (389), typecheck, lint, l10n, jscpd, format; not the full gate (machine loaded). Docs: CHANGELOG, docs/certification/sign-in-detection.md. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2a324d48c9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex on 2a324d4: on macOS a sign-in or sign-out made elsewhere may change only the Keychain, the file's path, size and mtime as they were, and CliAccount.confirm reused a remembered definitive answer even for a user action, so Diagnostics could report a stale sign-in. Fixed in the class: on macOS a user action reuses a remembered answer only if it came from the same click (younger than MUSE_USER_ACTION_ANSWER_REUSE_MS, 5 s); otherwise it asks account/read afresh, bounded as before. A literal "never reuse" would ask, and maybe prompt for the Keychain, up to four times per click. Off macOS the file speaks for the sign-in, as before. Diagnostics, Check again, Cancel, the sign-in button, sign-out and the Sign Out command all ask as a user action; the sign-in's pre-check stays passive so nothing prompts before the code is shown. Drills DL and DM each broke the rule, failed its suite and were restored by SHA-256. Checks: cliAccount, authService, supportReport and the cliAccount e2e (153), typecheck, lint, l10n, jscpd, format; not the full gate (machine loaded). Docs: CHANGELOG, docs/certification/sign-in-detection.md. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 328efb52f6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex on 328efb5, two P2s: - With museSpark.backend forced to modelApi, the gate's refresh and host admission still asked the CLI for its sign-in, so an ambiguous file could keep a user with a stored key in checking until the MSP deadline. isCliSignInConsulted(setting) now says the choice needs no CLI answer: AuthService.choose and readBackendChoice (host admission in extension.ts) ask nothing. The logout hold's checks after a sign-out still look at the CLI. - With the logout hold keeping an accountLogin, a macOS re-sign-in to the same account may replace only the Keychain item, so neither a state change nor a file write showed it and the flow waited out its limit. The captured success, granted borne out by accountLogin, now counts whatever came before; granted alone still never does. Drills DN, DO and DP each broke one guard, failed its suite and were restored by SHA-256. Checks: backendSelection, authService, deviceSignIn, cliAccount and the cliAccount e2e (207), typecheck, lint, l10n, jscpd, format; not the full gate (machine loaded). Docs: PLAN.md D26, CHANGELOG, docs/certification/sign-in-detection.md. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@codex review |
|
Codex Review: Didn't find any major issues. 👍 Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
fix/cli-sign-in-detection at 2a324d4: the CLI's sign-in read from the credential file's structure, account/read where only the CLI can say, sign-out through account/logout. Conflicts in the certification index, report.ts's imports and deviceSignIn.ts, both sides kept. - Node 20: PR #49's deviceSignIn.ts and accountHost.ts used Promise.withResolvers, which VS Code 1.99/1.100 (Node 20.18, M62) lack; the host typecheck at ES2023 refused it. accountHost races the handshake with unlessAborted, the device sign-in stops through its own controller and settleOnAbort. PR #49's 220 sign-in tests pass unchanged. - The ACP agent's Muse Code readiness moves off credentialFileExists() (gone, and wrong after muse logout, which leaves the file emptied) to PR #49's CliAccount: META_API_KEY, else the file's verdict, else account/read on a short-lived host. unsupportedHere is 'cannot run' with the panel's sentence; authenticate forgets earlier answers. Tested over stdio (sign-in asked after muse logout) and in process against the fake CLI for every verdict. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
PR #49 now asks the CLI again on every macOS user action unless the answer is under 5 s old. The agent's user action is authenticate (the user saying they signed in): a session or a list reads the file, or on macOS takes the panel's passive estimate, instead of starting muse serve (and perhaps a Keychain prompt) on every call. Tested in process with a runtime reading the file as macOS does, on any runner; drill R10. The host API record regenerated for PR #49's code: 201 APIs. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A forced Model API backend asks the CLI nothing, and a same-account Keychain re-sign-in counts. No conflict; nothing the ACP agent uses changes (it chooses its backend by flag and never runs the device sign-in). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
PR #49 (merged in a209130) asks the CLI for the sign-in (account/read), and the fake answers from the credential file's contents: a `meta` entry holding `access_token` is a browser sign-in, anything else is signed out. test/hosts/fake-muse.sh still wrote the old placeholder {"fake":true}, so the extension and the agent saw Muse Code signed out and the Hosts jobs for code-server, JupyterLab ("Authentication required") and Emacs failed. It now writes the browser sign-in's shape as captured (test/unit/helpers/credentialShapes.ts), placeholders only. Locally on a209130: code-server 4.99.4 failed before and passes after; JupyterLab and Emacs pass with it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K9UjDkubgiZqw9y8c3hBDg
On top of 83833fe, another session's answer to the same review: its tests, decline log line and mode-on-load stay; this goes further where they differ. - Credential variables: the agent takes every credential variable (*_API_KEY and the names hooks never get) out of its own environment at start and hands them to Muse Code's processes only, as the extension's muse serve inherits the user's META_API_KEY (D1), where it still counts as the CLI's credential. The shell tool, hooks, git and the Windows helpers never see one. Rule 8, D61, docs/acp.md. - Logs: every backend, CLI or client failure in src/acp is named by failureForLog (PR #49), never by its message; describe() is gone. - Client answers: the elicitation answer is parsed with zod, and each answer against its question (a single choice only an offered option, as the form's oneOf; free text only where there are none; distinct choices within bounds, at least one when unset, as the panel's card); anything else declines. With the permission answers, every response the agent asks for is checked. - Load and resume: the session is set to the mode shown, a listed model and the effort shown, or the load fails. Drills C1-C4, L1, E1-E3, M1-M3 in docs/certification/pr32-integration.md. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Conflicts kept both sides: the README settings table keeps web fetch's sandboxNetwork row and takes main's environmentVariables row; the certification index lists M69 and sign-in detection. en.ts and the manifest tables merged on their own. Typecheck, check:l10n (0 problems) and the unit suite (2,837 passed) ran on the merge. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Fixes the panel reporting the Muse Code CLI as signed in after a sign-out, on every OS.
The bug
muse logout(and MSPaccount/logout) doesn't deleteauth.json. It rewrites it as{"schema_version": 1, "providers": {}}. This was confirmed on 1.3.0 Linux and on 1.4.0 Windows and Linux, in throwaway homes with a dummy key. The extension treated "the file exists" as signed in, so:On macOS a sign-in lives in the login Keychain, and
auth.jsonis only a token-free pointer.The fix
src/core/backends/musecode/credentialFile.ts). It keeps only the schema version, whether any provider is named, and each provider's storage lane. The zod schema drops every other field, tokens included. Nothing from the file is stored or logged.muse serveexits 3 there with that file.account/readon a short-livedexperimentalApihost.account/logout, confirmed byaccount/read. The terminalmuse logoutstays as a fallback.granted(confirmed byaccount/read), onaccount/readwhile polling, or on a new file.denied,expiredandfailedend it at once with localized messages, instead of waiting out 5 minutes.authState.ts, which still encoded "file exists = signed in".Checks
docs/certification/sign-in-detection.md).muse servenow servesaccount/readandaccount/logout.npm run qualityexit 0 on2a4f0db(joined with M57 and M58): 2,605 tests, 336 accessibility pages, no leaks, 0 Semgrep findings;extension.js434.9 KiB of 600.Needs a real sign-in (owner-authorized, on the rigs, after merge): a 1.4.0 macOS login (Keychain plus pointer), the real order of
grantedagainstaccount/read, a browser sign-in and sign-out on Windows and Linux 1.4.0-R4302.1.🤖 Generated with Claude Code