Repository navigation
feat(accounts): add native accounts and in-place Claude account switching - #919
Adam-Dalloul wants to merge 44 commits into
Conversation
Settings MCP still targets the built-in family row. Extra custom agents whose spec.env sets CLAUDE_CONFIG_DIR, CODEX_HOME, GROK_HOME, GEMINI_CONFIG_DIR, or OPENCODE_CONFIG_DIR now receive the same server on upsert and lose it on remove. Claude extra homes only get enabledPlugins; server defs stay in the shared ~/.claude.json. Auth files are never copied.
Custom agents whose spec.env sets a family isolator now have a Sign in card on the Agents page. That opens the official CLI login (claude/codex/grok/gemini/opencode) with the isolator env set. No tokens are copied. Account 1 stays on the built-in home. Headless and unit-test paths return the command instead of spawning a terminal.
Settings MCP still targets the built-in family row. Extra custom agents whose spec.env sets CLAUDE_CONFIG_DIR, CODEX_HOME, GROK_HOME, GEMINI_CONFIG_DIR, or OPENCODE_CONFIG_DIR now receive the same server on upsert and lose it on remove. Claude extra homes only get enabledPlugins; server defs stay in the shared ~/.claude.json. Auth files are never copied.
Custom agents whose spec.env sets a family isolator now have a Sign in card on the Agents page. That opens the official CLI login (claude/codex/grok/gemini/opencode) with the isolator env set. No tokens are copied. Account 1 stays on the built-in home. Headless and unit-test paths return the command instead of spawning a terminal.
4c9548b to
9ee89cc
Compare
…tches A connection whose task ends on its own (the agent failed to load or exited, or the task panicked) now parks its child in the manager's draining list, as every explicit teardown does, so a missing map entry is never taken as proof that the process stopped. The deferred cleanup is spawned on the connection's runtime handle, because the guard drops outside any runtime context. Account switches wait for every writer of the session before copying it and before rolling a target back, and a child parked after its task finished leaves the list as soon as it is reaped. A switch's staging copies are removed once the switch is committed, a rollback completes, or staging itself fails. They are kept only when a rollback fails.
Launching a named account re-applies the shared MCP servers to config files that the account's own CLIs rewrite too. The config writers now leave a file alone when its content would not change, and the config staleness check no longer syncs account folders at all. Pi accounts mirror pi's own server list instead of the empty ACP forwarding view, which removed their servers again on every launch. A missing account folder no longer fails the import scan or every MCP change; that account is skipped until its folder is restored.
…ount A conversation switched to another account keeps its session in that account's folder. Work-task retries, returns and merges, their fresh-session fallback, and chat-channel resumes now launch with the conversation's saved account instead of the Default one, which the prompt gate then refused.
A tab that starts as a draft keeps a virtual runtime key, so its detail is never fetched. After the first send it read its account as Default and held auto-connect and reconnects for good, for every user. The account now comes from the binding the view set itself, then the newer of the workspace list and detail copies, and auto-connect waits only while none is known. The in-chat switcher stays disabled until then. The conversation list gets the in-flight ordering folders already have: an older snapshot is discarded, and a row an upsert changed while a refresh was in flight keeps the upsert's copy.
Connecting or sending to a conversation of another agent was refused with "This conversation belongs to a different agent account", which sends the user looking for an account problem. The refusal now names the other agent; a different account of the same agent keeps the account message.
Removing an account keeps its folder and its conversations, but adding that folder back created a new account. The conversations stayed bound to the removed one, so none of them could continue. Adding the folder of a removed account of the same agent now brings that account back, under the name entered, with its conversations. The removal dialog names the folder and says so, and a conversation of a removed account tells which folder to add again.
… it left A Claude account switch copies the session into the target account and leaves the source account's copy in place. The import scan listed that copy as new in the source account, and importing it created a stale duplicate whose row then blocked switching the conversation back. A copy is now treated as already imported when a live conversation under another account holds everything in it: its transcript as a prefix, and its sub-agent and tool result folder and file history as older trees, the switch's own test for a copy it may replace. The scan shows it as imported, an import of it is skipped, and the folder sweep leaves it out. A copy with anything of its own, or one that appears in two project folders, stays importable.
Every open conversation tab, hidden ones included, fetched the account list for itself and refetched it on every agent registry change, so one change sent a request per tab. The list is now kept once per agent and shared, the way the agent registry is: one request per agent on each change, and a refetch after a web client reconnects, since changes made while it was disconnected are lost. A failed refresh keeps the last list instead of emptying every tab at once, which made a draft's chosen account look gone.
…them A custom agent set up as a second account of a built-in agent (an extra slot) and a native account of that agent now do the same job, but their sign-ins had drifted apart. The slot ran the bare login command, which fails when the CLI is only installed by codeg (the OpenCode binary, say), and kept credentials the terminal exports, which can stand in for the login it is meant to create. Both now build the command the same way: the vendor CLI is resolved like codeg resolves it, and inherited credentials are cleared unless the slot sets them itself. The slot's card also points to the built-in agent's accounts, which keep that agent's features, and names the folder to add there. OpenCode is the exception, since its slot names only a config folder.
|
codeg work task |
|
@xintaofei, the account and usage PRs (#470, #471, #919, and #472) are refreshed on v0.36.0, with conflicts resolved and desktop/mobile checks passing locally. Could you take another look when the remaining CI checks finish? |
The account migration is now m20261017_000001_agent_account and is registered last, after the four migrations v0.36.0 shipped. A backup records only the last registered migration, and a release restores any backup whose last migration it knows. With the account migration in the middle of the list, a backup from this build named m20261016_000002_conversation_assistant, so v0.36.0 restored it and then failed to start, since it has no file for the account migration. Now v0.36.0 refuses such a backup as coming from a newer version. A database that already ran a build with the old name must have its seaql_migrations row renamed to the new one.
The locale files now differ from main only by this branch's own strings. Merging v0.36.0 had moved four of main's blocks to the end of their sections in all ten locales: MentionPanelSettings, WindowAttentionSettings, and AcpAgentSettings' kiro and distribution. No string was lost, but every locale conflicted with later changes next to those blocks, and keeping main's side of a conflict listed kiro twice.
Kiro's and ZCode's settings showed the server's English reason, "Named accounts are not yet supported for this agent.", in every locale. The accounts card now shows a translated note for any built-in agent without accounts, and keeps the OpenClaw gateway note.
|
@Adam-Dalloul thanks for refreshing the stack on v0.36.0. The conflict resolutions hold up: I went through I pushed three commits on top of 1. The account migration now runs after v0.36.0's (
|
|
Thanks @xintaofei, I've addressed the isolation and backup points and carried your fixes into #472. The updated description includes the exact validation, including two existing Claude sign-ins on Windows. |
Extra subscriptions currently require custom agents, which lose behavior tied to the built-in agent type. This adds named accounts under the built-in agents, so separate sign-ins can run concurrently while keeping native controls, history and resume behavior.
Stacked on #471 and #470. Settings can create a profile or adopt an existing account folder, rename it, remove it without deleting files, and open the official sign-in flow. The browser keeps a copyable command when a terminal cannot be opened. Default continues to use the existing sign-in.
The account is saved on the conversation. Reconnect, fork, same-family delegation, transcript readers, imports, title refresh and selector preferences retain that binding. Removed or missing accounts fail explicitly. Identical native session IDs in different accounts stay distinct, and adopting a session from an older custom slot cannot create a second active writer.
Top-level Claude conversations can switch accounts in the same chat. The conversation, tab, native session ID, transcript, tool results and file history are retained. The target must acknowledge restoring the session before the saved account changes. Active turns and background work are refused; failed loads roll back and conflicting destination history is preserved. Other agents keep their original account. Account-specific mentions and remaining usage are in #472.
Named accounts cover Claude Code, Codex, Gemini, OpenCode, Grok, Hermes, CodeBuddy, Cline, Kimi Code, Pi, DeepSeek, Qoder and Antigravity. Cursor supports them on Windows and Linux. On macOS, use Default: Cursor's credential store requires replacing the home directory, so named-account creation and launches are disabled there. Existing records, folders and transcript readers remain intact. Kiro and ZCode do not yet support named accounts; OpenClaw uses its gateway's account.
OpenCode keeps
HOMEandUSERPROFILE, isolates its XDG folders, and removes inheritedOPENCODE_AUTH_CONTENTandOPENCODE_DBoverrides. OpenCode and Cursor preserve GitHub CLI's original config directory, including an explicit setting. Other tools using the separate configuration folders may need their own setup; the Accounts card explains that. Shared MCP and skills updates preserve account-local entries and authentication files.Backups retain account names, paths and bindings but exclude named-account folders and their transcripts/sign-ins. The backup screen explains this in all ten locales. The account guide covers separate backups and restoring the saved paths; a missing-folder error names the folder to restore.
The follow-up preserves the maintainer's migration-order fix, locale ordering and translated unsupported-agent notes through
21cd032a. The account migration remains last asm20261017_000001_agent_account.Validation on the combined stack: 198 focused frontend tests, TypeScript, ESLint, production export, server/MCP build, and real local HTTP/MCP plus desktop and phone browser checks. These exercise account binding, explicit Default, inheritance, removal refusal, child/Assistant resume, full-history switching and retained model/mode/draft. Native all-target server/MCP Clippy passes with warnings denied.
Real authentication checked on Windows: Claude Code 2.1.280 reports two distinct existing Claude sign-ins through their respective
CLAUDE_CONFIG_DIRsettings, and both authenticate to the live usage endpoint. Credential bytes remain unchanged. This does not claim a fresh OAuth login or live sign-ins for every agent. OpenCode 1.14.50 reads separate synthetic auth/config stores while Git, npm and gh retain their fixture settings; the pinned OpenCode 1.19.1 and Cursor 2026.10.14 sources were also checked.CI for
af3ac231passes all seven checks: frontend validation and desktop/server Rust checks on Linux, macOS and Windows. The Windows desktop job compiles tests and runs Clippy; it does not execute the desktop test suite.