Skip to content

fix(claude): activate account profiles and remove credential replay (Step 4 of 4) - #24434

Merged
Jinwoo-H merged 225 commits into
mainfrom
brennanb2025/claude-profile-activate
Oct 8, 2026
Merged

Jinwoo-H merged 225 commits into
mainfrom
brennanb2025/claude-profile-activate

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 97 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​3082 $\color{#cf222e}{\Huge{\mathbf{−}}}$​12143 $\color{#cf222e}{\Huge{\mathbf{−}}}$​9061
Prod 170 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​2377 $\color{#cf222e}{\Huge{\mathbf{−}}}$​7717 $\color{#cf222e}{\Huge{\mathbf{−}}}$​5340

ELI5

Before, Orca kept a spare copy of each Claude account's login and kept swapping it into the one place Claude reads its login from. Claude replaces its login every so often, so whenever Orca put back an older copy, Claude's server rejected it and you had to sign in again, sometimes repeatedly.

Now each account has its own Claude folder. Claude signs in there and looks after its own login, and Orca only tells Claude which folder to use. Orca never touches logins again.

What Changed

Step 4 of 4 for STA-3698 (#9582). It switches on #24300, #24351 and #24384 and deletes Orca's login-copying code.

What users see

  • One sign-in per saved account after upgrading. Nothing is copied over from the old copies. Accounts without a login show "Sign in again to use this account".
  • Sign-in runs in a visible terminal in Settings → Accounts. It runs CLAUDE_CONFIG_DIR=<folder> claude auth login (the PowerShell form on Windows, and inside the distro for WSL). The account is saved when the command finishes. A cancelled sign-in leaves no row.
  • Each row shows the email its folder is actually signed into. "System default: " is the user's own login.
  • Switching applies to the next Claude started anywhere: tabs, splits, chat, the model list, and terminals already open. Running sessions keep their account.
  • Removing an account deletes its Orca folder and its Keychain sign-in. History shared with System default stays.
  • Notices:
    • if the user's shell sets CLAUDE_CONFIG_DIR, terminals use that folder;
    • terminals opened before this update need reopening to follow the selected account;
    • if System default's email matches a saved account, an older Orca may have copied that login there.
  • MCP connector logins are per account. Connectors signed in under one account need signing in once under another. fix(claude-accounts): preserve shared MCP OAuth credentials across an account switch #21931 kept them shared by copying them between accounts during a switch; that code went with the copying. Sharing them would mean copying rotating secrets between folders again, which is the bug this stack removes.
  • Unchanged: users with no saved accounts, and SSH terminals. Usage reads are read-only and never renew a login.

Mechanism

  • The terminal daemon protocol goes to 42, so new terminals get the claude function after the update. Older daemons stay attachable.
  • Removed: the credential snapshot, restore, read-back and refresh code, and the Keychain writes. A ratchet test fails if credential-writing code comes back.
  • New runtime RPCs accounts.beginClaudeSignIn and accounts.finishClaudeSignIn, behind the capability accounts.claude-sign-in.v1.

Why

A separate config folder per account (CLAUDE_CONFIG_DIR) is Anthropic's documented way to run several Claude accounts. Each folder has its own login and Keychain entry, so nothing can replay an old login. The layout follows the common pattern other multi-account tools use.

We considered two alternatives:

  • Keep one shared login, but never write an older one. This can't be made race-free, because a running Claude keeps renewing its login without Orca seeing it.
  • Long-lived tokens. These lose claude.ai connectors and Remote Control.

Rollback

Fix forward only. Reverting this PR brings back the login-copying code. That would copy stale saved logins into the user's Claude folder and Keychain again, and could overwrite a personal login the user redid after upgrading.

Linked Issue

Fixes STA-3698. Fixes #9582.

Visual Proof

To follow: the sign-in dialog and account rows from real-machine testing.

Testing

  • Automated: account folder removal (links unlinked first, old layout untouched, every Keychain path spelling), router and WSL prepare/remove, the sign-in RPCs, CLI account listing, daemon protocol, and shell-setup snapshots. The snapshot diff against main is exactly the claude function.

  • Real-machine checks before merge:

    • macOS with two real accounts (login renewals, /resume across accounts, upgrade with an account selected);
    • Windows and WSL (sign-in, junction-safe removal, history per account on Windows);
    • Linux sign-in.
  • I manually tested these changes locally

  • Automated tests added/updated, or explained why not below

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

Review round 1 of the dormant profile setup found that the pieces could
be called without their safety checks, that one failed write or an
unreadable bookkeeping file could silently stop sharing for good, and
that Windows prompt history could bring back history the user cleared.

- One entry, provisionClaudeAccountProfile: the profile gate (namespace,
  no linked components, outside ~/.claude and ~/.config/claude, and an
  ownership marker beside the home naming the account and target) runs
  first and refuses before creating anything; then history sharing,
  config provisioning, and the hook install after the settings merge.
  Results come back per surface with closed warning codes instead of
  message text.
- The sharing ledger is keyed by surface name, records a value only
  after its write succeeded, and an unreadable ledger starts empty and
  is rewritten instead of blocking every surface.
- The profile state file goes through the same locked writer as folder
  trust (Claude's <file>.lock plus the in-process queue), generalized as
  updateClaudeGlobalConfig. Onboarding and trust are still applied when
  the personal state file is unreadable.
- WSL descriptors build guest POSIX paths; the state-file path style
  follows the injected platform.
- Orca's managed statusLine has one owner in a profile: the settings
  merge never shares it, a user's own statusLine is shared over it, and
  the profile installer follows the default home's slot so a default
  opt-out reaches every profile. remove() takes the same destination;
  the remote installer cannot accept one.
- Prompt history compares file identity (bigint dev+ino) on every
  platform, never drains the shared file into itself, drains retained
  copies in generation order, never reuses a stale cursor, and on Windows
  keeps a replaced default's old copy aside instead of replaying it.
  Directory merges keep going past a failed entry.
A user's own Claude hooks in ~/.claude (notifications, formatters) did
not run under a managed account, because the whole hooks key stayed
private. They are now shared like any other settings key: Orca's own
hook entries and its managed statusLine are stripped from both the
personal value and the profile's current value before the per-key
ledger comparison, so they never travel through the merge and never make
the key look user-owned. Orca entries already in the profile are kept on
write, and the profile hook installer adds them on top as before.

Prompt history: merged bytes that lack a final newline are terminated,
so Claude's next record no longer fuses onto the last merged line. When
a CLI rewrote the profile's history file (old records plus new), only
the lines past the part it shares with the default history are added,
instead of the whole file again.
Hooks and statusLine sharing:
- When ~/.claude holds only Orca's hook entries, the user's shared hooks
  now read as an empty value instead of a missing key. Removing the
  user's last own hook in ~/.claude therefore reaches profiles that
  never edited it, and deleting the only shared hook inside a profile
  stays deleted.
- A custom statusLine Orca shared, and the profile never edited, goes
  away when the default home drops it. When a shared custom line
  replaced Orca's line in a profile, the profile's statusline marker is
  dropped so Orca's line comes back once the default returns to it; a
  profile that opted out stays opted out. No other key gains deletion.
- install/remove/getStatus with a profile directory refuse when it is
  the default home, or its settings.json resolves to the default one,
  instead of editing System Default's hooks and opt-out state.
- The profile statusline rule reads the default settings under the
  userHome passed to the setup entry, not os.homedir().

Profile state and ownership:
- A malformed `projects` value skips only folder trust (new warning
  code trust-refused); onboarding and shared keys still apply.
- The ownership marker stores only host-local facts (account, runtime,
  distro). The execution host id is the caller's view of the host, so
  it stays in the in-memory descriptor and is not compared.

Prompt history interruption paths:
- With no cursor yet, a retained copy starts past the bytes it shares
  with the default history, so an interrupted share no longer replays
  the whole history.
- A retained name for the shared file itself is removed with its cursor
  instead of lingering until a later scrub makes it look new.
- The Windows link record is read three-state: unreadable stops the
  share instead of reading as "no link". If the record cannot be
  written after linking, the fresh link is undone.
- An unreadable retained copy is reported and no longer blocks linking.
hook-service.ts and hook-settings.ts are compiled into the packaged CLI
project, which lists every file explicitly. The statusline policy and
profile destination modules they now import were missing, so the CLI
typecheck failed with TS6307. The CLI still loads hook-service through
the existing managed-agent-hook-controls build entry, which bundles
both modules; neither imports electron.
- A profile whose hooks hold only Orca's entries and that sharing never
  recorded is no longer treated as a user edit, so the user's first own
  hook in ~/.claude reaches it (for example when the profile was set up
  before ~/.claude had any hooks).
- A retained prompt-history file is removed as a second name for the
  shared file only when the default history does not itself link to it;
  otherwise it holds the only copy and is kept.
- Default-home checks compare file identity: the profile hook
  destination check uses device and inode, and the profile/default
  separation check resolves on-disk case, so a case-only alias of
  ~/.claude is refused on case-insensitive filesystems.
- A test pins that an unreadable leftover session tree no longer blocks
  linking.
QA found that removing a setting from ~/.claude never reached a managed
account: deleting the whole `hooks` block left the user's hook running
there. Only statusLine followed the default away.

Every shared key now follows the same rule through the existing per-key
ledger: when a key disappears from ~/.claude/settings.json (or
mcpServers/theme from the personal state file), it is removed from the
profile if the profile still holds exactly what Orca last shared. A
value changed inside the account is kept. Keys Orca never shared,
including denylisted ones, are never touched. Deleting the whole hooks
block removes the user's shared hooks and keeps Orca's own entries. A
missing source counts as empty; an unreadable source removes nothing.
…e by capability

Restores the inline mobile allowlist so its source-scan guard sees every
accounts.* method again, and the generated params catalog to generator order.
…ed config dir

The function is defined only in a routed pane where claude is a real
executable (the codex function's guard), re-reads the pointer only while
CLAUDE_CONFIG_DIR is unset or still Orca's injected twin, accepts Git Bash
drive paths, and starts on its own line after the fish/PowerShell codex text.
…lifecycle triggers

Round-1 review fixes for the dormant profile routing:
- Panes get the selected profile's CLAUDE_CONFIG_DIR plus an Orca twin at
  spawn, so nested shells and scripts inherit the account; System Default
  injects nothing and its home is the inherited CLAUDE_CONFIG_DIR.
- An absent routing owner is System Default, never a throw; AI Vault and
  session-search scans receive profile roots from their parent, and the
  capability is advertised only where an owner is installed.
- Account listing never throws: per-account readiness, a stale pointer is
  republished in the background and reported on the snapshot.
- Profiles are set up at select and startup; a launch only sets up one that
  never was, and a worker fault on a prepared profile is a warning. The
  Claude version probe is cached per binary identity.
- Pre-trust goes through the existing deadline- and realpath-guarded writer
  against the launch env's profile config.
- Skill discovery keeps a caller's Claude root and a broken Claude selection
  no longer fails other providers.
- The durable record carries a provider-neutral launchAccountHome, read
  through one helper by the launch fallback and the model catalog.
…published

A pointer left naming the previous account would launch it silently; a
missing pointer makes the claude function refuse visibly. A newer selection
that raced the failed one keeps its pointer.
…r than 3.4

Shell tests skip system config and abort unless claude resolves to the fake.
…fig dir lookup

- An overtaken publish that fails leaves the newer selection's pointer.
- The runtime config dir falls back to the legacy home for an unresolvable
  account or a WSL target, so skill roots never fail for other providers.
- WSL guest reader roots merge verbatim, never realpathed on this thread.
- History readers include ~/.claude, where step-1 setup pools profile history.
- System Default ignores a config dir an outer Orca injected (twin-marked).
…eate resolver

A Claude agent-env CLAUDE_CONFIG_DIR the create path stored is now the home
the launch pins and the model probe accepts.
A worker thread's os.homedir() ignores its own env, so the hook and
statusline scripts now go under the home the job names. The worker test pins
the process HOME to a sentinel, refuses to run unless the worker sees it, and
asserts nothing lands there.
…d of setting null

On .NET 9+ (pwsh 7.5+) SetEnvironmentVariable with $null creates an empty
variable, so stripped auth vars reached claude as empty strings and the
restore left CLAUDE_CONFIG_DIR empty in the user's session.
…file publishes

A WSL pane now gets the same non-throwing, guest-free spawn env as a host
pane; only select, startup and Claude launches publish into the guest.
Overlapping publishes of one target share the newest publish while the
selection still names the same profile, instead of failing as superseded.
Publish issues name their WSL distro and drop out when the target is no
longer routed. A late inspect from an older selection no longer replaces
the newer one's verification, a failed guest request evicts the cached
guest, and readiness is derived per account from the guest's owned homes.
With profiles, a failed select or remove republishes just its own target
instead of running startup over every WSL distro, and a rollback failure is
logged instead of replacing the error that caused the rollback.
Vault and usage scans pass Claude profile roots through the same
running-distro filter as every other WSL root, so a stopped distro's UNC
paths are never walked.
The helper only ever runs inside WSL from the desktop, so it moves out of
the SSH relay artifacts (no upload, no relay version change) into
out/relay/wsl beside the other WSL-only guest bundles. The three WSL bundle
resolvers share one candidate list.
…wnload per caller

The pinned Node runtime needs glibc 2.28, so a distro below the floor is
refused before any download with a message naming both versions, as SSH
hosts are. The shared download again owns its own deadline and each caller
waits on its own signal, and the OpenCode reader keeps its architecture
error text.
…h the WSL runner

A cached guest no longer carries its 180 s preparation deadline into later
requests. The helper runs through runWslProcess (stdin payload, WSL_UTF8),
the distro is confirmed running once per preparation and once per request,
a failed `claude --version` probe continues with an unknown version like
native setup, the helper resolves from the WSL bundle dir, and the guest
entry decodes stdin once so split UTF-8 survives.
…ter account switch

Orca now marks the user's own CLAUDE_CONFIG_DIR as its value in every pane, so the claude
function swaps it for a selected account and restores it on System default, while an rc
export still wins.
A Dock-launched Orca reads CLAUDE_CONFIG_DIR from the user's rc through the
login-shell snapshot, so marking it made an rc export silently lose to the
selected account. Keep the user's value winning, with the existing note.

This reverts commit 9fe4291.
…te-lane

# Conflicts:
#	src/main/claude/claude-structured-launch-resolution.ts
#	src/main/runtime/structured-agent-runtime-registrations.ts
#	src/main/runtime/structured-claude-runtime-adapter.ts
…te-lane

# Conflicts:
#	src/renderer/src/components/native-chat/NativeChatComposer.tsx
…te-lane

# Conflicts:
#	src/main/claude/claude-structured-launch-resolution.test.ts
#	src/main/claude/claude-structured-launch-resolution.ts
#	src/renderer/src/i18n/en-runtime-required.json
…fter an older Orca's copy-based switching, and let it be dismissed
…straight into the account folder

Add Account and Sign in again run `claude auth login --claudeai` in the background, which opens
the browser, as they do today; the visible sign-in terminal and its dialog are gone. The login
runs with CLAUDE_CONFIG_DIR set to the account's own folder, so there is no temporary folder and
nothing to copy. A WSL account logs in through the distro's login shell with --exec, like the
CLI's WSL sign-in. Progress, Cancel and the failure toast are the ones the Accounts pane and the
usage card showed before. Cancel, timeout, a login Orca cannot read, or an account that is
already added delete a new folder and leave no row; signing in again keeps the saved folder.
…te when the shell's own CLAUDE_CONFIG_DIR wins

A CLAUDE_CONFIG_DIR the user set still takes precedence over the selected account in bash, zsh,
Git Bash, WSL, fish and PowerShell; the function now does so silently, as before account folders.
…te-lane

# Conflicts:
#	src/main/claude/claude-structured-launch-resolution.ts
#	src/main/runtime/orca-runtime-get-structured-agent-session-create-support.ts
#	src/main/runtime/orca-runtime-get-worktree-ps.ts
#	src/main/runtime/structured-agent-session-runtime.ts
… folder with no login opens Claude's own sign-in

The claude shell function refused a selected account whose folder was missing with a printed
note, which is every account saved before per-account folders. It now creates the folder and
runs Claude there, so Claude's own first run (theme, login method, browser) signs in. Orca sets
up the selected folder even when it is missing, before any login, and never writes the
onboarding flag, so that first run stays clean. A pointer naming no absolute folder runs nothing
rather than fall back to another account. PowerShell no longer writes errors either.
…count update, from the Codex banner's frame

A terminal opened before per-account Claude folders has no claude function, so claude there
uses System default's login. Such a pane running Claude, with an account selected, now shows a
strip saying so, with Open new terminal and Learn more. It asks main whether the pane's daemon
predates protocol 42; SSH and remote panes are never owned by a local daemon, so they never
show it. The Codex banner's frame, Learn more link, new-terminal routing and old-terminal
dialog are shared now. The Settings and status-bar notes it replaces are gone.
…ch account in the status-bar menu, for accounts that need a sign-in

On the first launch after the update, a user with a saved Claude account that has no login yet
sees one toast: Claude accounts now stay signed in on their own; sign in once to each. Its Sign
in runs Settings' hidden sign-in for the selected account, or the first one that needs it. The
shown flag persists like the other one-time notices. In the status-bar Claude menu, such an
account keeps its note and gets the same inline Sign in Codex rows have; a remote server's
accounts keep the note only, since this device cannot sign in for them.
…fers Sign in

"Claude is not signed in for the selected account" and the missing-account-folder failure had
no button. In a local Claude chat under a selected account, the failed start's notice now offers
Sign in instead of Retry, and a transcript row stating either failure carries Sign in too; both
run Settings' hidden sign-in. Once it succeeds the notice offers Retry again, so the message can
be resent; a later failure offers Sign in again. Codex, remote and System default chats are
unchanged.
…erShell surfaces Claude's own errors; private account folder

- Setup sets hasCompletedOnboarding and merges shared state keys only when the folder's
  .claude.json names a login (oauthAccount.emailAddress). Claude writes that file at the theme
  pick, before sign-in; setting the flag there skipped Claude's own sign-in screen.
- The PowerShell claude function writes the caught error again (claude missing, a throwing
  claude.ps1) and keeps exit code 1.
- The shell function creates a missing account folder with mode 700, matching Orca's setup.
- Toast title: "Each Claude account now has its own sign-in."
- The chat Sign in guards a double press with a ref, as the status-bar menu does.
…st when the selected account needs a sign-in; drop the CLAUDE_CONFIG_DIR note

With saved Claude accounts, a signed-out usage row no longer collapses into a Sign in shortcut to
Settings. It opens the account menu, which expands to show each account's "Sign in again" note and
inline Sign in. The shortcut stays for no saved accounts, and for a remote server, whose accounts
aren't in local settings.

Settings no longer says the shell's CLAUDE_CONFIG_DIR wins in terminals. It read Orca's own launch
environment, so it was wrong both ways. The user's own CLAUDE_CONFIG_DIR wins silently. The host
stops sending the optional userClaudeConfigDir field, which older clients already treat as absent.
@Jinwoo-H
Jinwoo-H merged commit 8fdad2a into main Oct 8, 2026
11 checks passed
Ethan-Rivas added a commit to Ethan-Rivas/orca that referenced this pull request Oct 8, 2026
…t-account

Rebuilds pinned Claude launches on main's per-account folders (stablyai#24434, Step 4),
which removed credential replay.

A `--account` (or project-saved) launch on an account that is not the selected
one now runs Claude straight from that account's own folder: CLAUDE_CONFIG_DIR
points at it and the pane gets no which-account pointer, so a later switch of the
selected account never moves it. The selected account still takes main's normal
path.

Because no login is copied anywhere any more, the credential seeding, Keychain
read-back, per-account reservations, usage-fetch and account-switch guards, and
the host-terminal account tracking are removed. The pinned PTY registry keeps only
which PTY runs which account, for the tab and status labels, and the refusal codes
those guards produced (and the toast's Retry) are pruned.
Jinwoo-H added a commit that referenced this pull request Oct 8, 2026
…6638)

Users with a saved Claude account were signed out of every Claude feature in Orca until they signed in again, and proxy setups (API key plus ANTHROPIC_BASE_URL) got 401s after signing in. Reverting before release to rework the upgrade path. The terminal daemon protocol moves forward to 43 so dev builds' v42 daemons with the claude function are not reused; v41 and v42 stay attachable. Nothing is deleted: per-account folders are left unused and the old saved-account store was never touched.
Jinwoo-H added a commit that referenced this pull request Oct 9, 2026
…and proxy fixes (#26801)

* Revert "Revert Claude per-account folders (#24434, #26405) before release (#26638)"

This reverts commit 9ca9b4e.

* Move the terminal daemon protocol to 44 for the re-landed claude function

v43 daemons (the revert) lack the claude account function, so new terminals must not reuse them. v42 and v43 stay attachable.

* Move the local build compatibility contract to daemon protocol 44

* Point the old-terminal test at daemon protocol 44

* Claude accounts re-land: never sign anyone out, keep proxies working, accounts mirror ~/.claude (#26769)

* Run a Claude account on System default until it has its own login

An account saved before per-account folders has no login in its folder. While System default (~/.claude, or the user's own CLAUDE_CONFIG_DIR) is signed in to the same email, the router sends every launch there and writes that choice into the pointer file, so nobody is logged out by the update. Once the account signs in to its own folder, the next launch uses it. The WSL router follows the same rule with the guest's ~/.claude.

* Refresh each Claude account folder from ~/.claude before every launch

~/.claude (or the user's own CLAUDE_CONFIG_DIR) is now the master copy. Each account folder gets its whole settings.json, including the proxy address and its key, and every .claude.json key except the login, Claude's account caches and install ids, so first-run questions are not asked again. Every other top-level entry is linked (folders) or copied (files), except Claude's per-folder daemon, jobs, live sessions, login files and throwaways. sessions is no longer shared, and an earlier link is undone.

The refresh runs before every Claude Orca starts, for each new terminal and on account switch. Orca never writes back to ~/.claude. The ledger that tracked selective sharing is gone.

* Route chat auth and unselected-account usage through the Claude router

Chats decided whether to drop inherited Anthropic auth from the selection alone, so an account running on System default lost the user's own key. They now ask the router, as terminals, AI commit messages, automations and usage already do. Usage for an unselected account is read where its launches would run. A test reads every entry point through the fallback and fails if a launch path outside the router reads the Claude selection or builds an account folder. The router's two launch errors move to their own file to keep it under the line limit.

* Show Claude account sign-in prompts only when an account cannot run

With the fallback, an account System default is signed in to already works, so:
- The one-time "Finish setting up your Claude accounts" toast is gone.
- Accounts System default covers no longer need a sign-in: no Sign in in the status bar menu or Settings, and they can be selected.
- The old-terminal banner shows only when claude in that terminal would run a different account than the selected one, and asks again after a switch. Its wording is unchanged.

The retired toast's saved flag stays in the paired-client schema so an older client's write is still accepted. The email comparison moves beside the login reader to keep the router under the line limit.

* Keep the user's shell Anthropic auth on Claude account launches

A signed-in account's launches used to drop ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN and CLAUDE_CODE_OAUTH_TOKEN from the inherited environment while ANTHROPIC_BASE_URL passed through, so a proxy set in the shell got the subscription login and answered 401. Host launches on an account now keep the shell's auth with its address, as System default does. Chats still name the account kind in a failed sign-in, now from the router rather than from whether auth was dropped.

* Share from ~/.claude only what Claude itself would seed into a new folder

The deny list missed much of Claude's runtime state, so tokens, locks and live files such as .session_ingress_token, server-sessions.json and remote-control were copied or linked over each account's own every launch. The list now mirrors the one Claude uses when it seeds a fresh config folder from ~/.claude: its runtime entries, every hidden entry, daemon files and agent memory. teams, ide, downloads and scratch are on it, so they stay per folder.

* Merge trusted folders and MCP servers into an account instead of replacing them

Each refresh replaced the account's projects and mcpServers with ~/.claude's, so folder trust and allowed tools recorded only in the account were lost, and a refresh landing between Orca's folder-trust write and Claude's start brought the trust dialog back. Both are now merged per folder path or server name: ~/.claude wins for entries it has, the account keeps the rest, and a folder trusted in the account stays trusted. Both writers take Claude's lock on the file, so the order no longer matters.

* Require the same organization before System default stands in for an account

The fallback compared emails only, so one email saved for two organizations made both accounts run on whichever organization System default was signed in to. When the saved account and System default's login both name an organization, it must now match too, on the host and in WSL, and the old-terminal banner uses the same comparison. The saved-account lookup the three checks repeated is now one helper.

* Treat v42 terminals as having the claude account function

v42 daemons shipped the same claude function and pointer path as v44, so claude in those terminals already follows the selected account. The old-terminal banner now shows only for daemons before v42 and for v43, the revert, which dropped the function.

* Fail the routing test when a new module resolves the claude binary

The census only caught code that read the Claude selection, not a new launcher that runs claude without asking the router. A second check now lists every module that resolves the claude binary or loads the Agent SDK, each with why it is routed, and fails on any new one, or on a listed one that no longer matches.

* Skip the account refresh on launches that run on System default

A launch that falls back to System default never uses the account folder, so starting a setup for it on every launch was wasted work. Account switches still set the folder up for a later sign-in.

* Give a terminal opened before the login shell's env only the account pointer

At startup, a pane could be routed before the login shell reported the user's own CLAUDE_CONFIG_DIR, so the fallback compared against ~/.claude.json instead. Until that env arrives a pane gets only the pointer, and the pointer is rewritten once it does, so claude typed there reads the right folder. The recent claude --version answer moves beside the version probe to keep the router under the line limit.

* Drop the chat-only Anthropic auth stripping and override refusal

Chats no longer drop the shell's Anthropic auth on any account, so the chat launch's override refusal and the policy's stripAuthEnv could only be reached from test fixtures. The structured auth policy is now just which login a failed sign-in names. Terminals keep their refusal, which still guards WSL launches; the failure reason stays in the shared vocabulary for older peers.

* Publish a new account state file whole

The first .claude.json write went straight to the target, so a crash mid-write could leave Claude a truncated state file. It is now written to a staged file and linked into place without replacing one Claude created meanwhile.

* Read an unselected WSL account's usage where the WSL router would run it

Usage for an unselected WSL account read its saved folder directly, so an account the guest's ~/.claude covers showed no usage, and the read bypassed the router. It now asks the WSL router, after the same check that the distro is already running.

* Keep the shell's Anthropic auth when the claude function runs an account

The claude shell function (POSIX, fish and PowerShell) unset ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, CLAUDE_CODE_OAUTH_TOKEN, AWS_BEARER_TOKEN_BEDROCK and auth-like ANTHROPIC_CUSTOM_HEADERS before running Claude in an account folder, so a proxy set in the shell lost its key while keeping its address. It now only sets CLAUDE_CONFIG_DIR and Orca's marker, as on System default. Protocol 44 is unreleased, so no new bump; the shell-wrapper snapshots are regenerated.

* Never drop the user's Anthropic auth from a Claude launch

Orca's launches stripped ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, CLAUDE_CODE_OAUTH_TOKEN, AWS_BEARER_TOKEN_BEDROCK and auth-like ANTHROPIC_CUSTOM_HEADERS for WSL terminals, terminal splits and through the preparation's stripAuthEnv flag, and refused a launch whose agent env set them. If the user's shell overrides the login, that is their setup: every launch now keeps it, on accounts, System default and WSL alike. The flag, the strip paths and the override refusal are gone; the refusal's reason stays in the shared vocabulary so older peers still decode it.

Orca's own process env and the claude --version probe still drop these variables: they are Orca's, not the user's launch.

* Leave out of an account's state everything Claude resets when it signs out

The .claude.json copy left out the login and keys matching cache patterns, but still carried account state such as additionalModelOptionsAnsweredAt, artifactRosterDenied, lastSeenOrgDefaultUpdatedAt and the subscription notices. The copy now leaves out the exact keys Claude resets on logout, beside the install ids, Console API key and first-token date; the cache patterns stay for account caches logout does not reset. Onboarding is still copied.

* Judge an old terminal's Claude banner by the pane's own runtime

The banner compared every old pane with the host selection and showed whenever only a WSL account was selected, so old host panes showed it with nothing to say and old WSL panes were judged by the host's account. The pane now names its runtime: a host pane is compared with the host selection, a WSL pane with its distro's selection through the WSL router, and a pane whose runtime is unknown, or with no account selected for it, stays hidden.

* Leave an account's settings file alone when only Orca's hooks differ from the default home's

* Reread a WSL account's Claude state only when the file changes

* Move tests merged from main onto the account-only Claude auth policy
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.

[Bug]: anthropic 401

2 participants