Repository navigation
[Feature]: CLI - ACP: select the account profile at launch (personal and work token plans in one editor) #391
Description
Product or interface
CLI - ACP
中文摘要:
mcode acp启动时只调用一次prepareTuiDataDir(),整个 stdio server 的所有 session 共享同一个 runtime、同一个账号。目前选择账号的唯一手段是MINIMAX_DATA_DIR/MAVIS_DATA_DIR,但它是机器级全局变量 —— 给编辑器配了它,交互式 TUI 的账号也会一起被改掉。而main上并没有--profile标志:内部虽然有 profile 概念(getProfile()→~/.minimax-<profile>),但 commander 从未注册该选项,因此mcode acp --profile work报 unknown option。建议让 ACP 支持按 profile 启动,并让客户端能看出当前 agent 用的是哪个账号。
Use case and problem
I build an ACP client, and I also use mcode interactively on the same machine. The two need different accounts: a personal token plan for the interactive TUI, and a work token plan for the agent inside my editor.
The account is bound once per ACP process, before the client speaks. runTuiAcpCommand resolves the data directory a single time and builds one runtime around it, and that runtime serves every session on that stdio connection:
packages/tui/src/cli/run-acp-command.ts:59—const dataDir = await prepareTuiDataDir()packages/tui/src/cli/run-acp-command.ts:60-66— onecreateRuntime({ dataDir, ... surface: 'acp' })for the whole server
So the account is decided when the editor spawns the process, and no session on that connection can be anything else. An ACP server is long-lived — an editor may keep one alive across many threads, projects, and days — so "one account per server" means "one account per machine, in practice".
The only existing lever is a machine-global environment variable. The TUI resolves its data directory as readDataDirOverride(environment) ?? getDefaultDataDir() (packages/tui/src/runtime/data-dir.ts:39-44), and the override reads MINIMAX_DATA_DIR / MAVIS_DATA_DIR from process.env (packages/config/src/config.ts:1316-1320). Setting it in my editor's agent config to give the ACP server a work account also changes the account that the interactive TUI, mcode login, and every other mcode process on that machine use. There is no per-invocation alternative today.
And --profile does not exist on main. The profile concept is already in the resolution chain — getExplicitProfileEnv() reads --profile from raw argv and resolveDataDir({ profile }) produces ~/.minimax-<profile> — but no CLI surface registers the option. getExplicitProfileEnv is cliArgValue("profile") ?? runtimeEnv("PROFILE") (packages/config/src/config.ts:1333-1336), and cliArgValue is a raw-argv scan (packages/config/src/config.ts:1220) that runs whether or not commander knows the flag. The env fallback is behind isLegacyRuntimeEnvAllowed() (packages/config/src/config.ts:83-85), so it is off unless __MAVIS_ALLOW_LEGACY_RUNTIME_ENV=1.
I verified on 0.5.8 that mcode acp --profile work is rejected as an unknown option, while the internal scanner already understands --profile — the two disagree about what the flag is.
The protocol gives the client no way to see which account it got. initialize advertises exactly one authentication method, with no account dimension:
"authMethods": [
{ "type": "terminal", "id": "minimax-code-login",
"name": "Sign in to MiniMax Code", "args": ["login"] }
]That is the same single entry whether the server was started on the default profile or a named one. agent/authenticate rejects any other methodId with Unknown authentication method. (packages/tui/src/acp/agent.ts:45, :461-468, :486-492), and an unauthenticated runtime fails with authRequired and the message "Run mcode login and try again." (packages/tui/src/acp/agent.ts:2110-2115).
So an ACP client can ask the user to sign in, but it cannot express which sign-in, cannot display which account is live, and cannot host a personal thread and a work thread in the same editor.
Current chain
The data-directory resolution is already profile-aware end to end; only the ACP edge and the CLI surface are missing.
packages/config/src/config.ts:1333-1336—getExplicitProfileEnv()=cliArgValue("profile") ?? runtimeEnv("PROFILE")packages/config/src/config.ts:1506—resolveDataDir({ profile: explicitProfile })packages/config/src/data-dir.ts:36-42—basenameForProfile→getPrimaryDataDirPathreturns~/.minimax-<profile>packages/oauth-core/src/namespace.ts:21-41— the OAuth namespace derives both its paths and its credential key from that data directory, so a different data directory is a different tokenpackages/tui/src/cli/run-acp-command.ts:59— the single point where ACP binds it
Desired behavior
Let the ACP server start on a named profile, and let the client see which one it has.
Acceptance criteria
mcode acp --profile <name>starts the server bound to that profile's data directory and its stored credential. The flag is accepted onacpand onsession-taking subcommands, so a client can set it in its agentargswith no new protocol method.initializereports the active profile (in_metaor alongsideagentInfo), so a client can display which account the agent is signed into. Today the client cannot tell a personal agent from a work agent, which is the actual problem — a wrong-account session is invisible until it bills or writes to the wrong tenant.- Selecting a profile never mutates another profile's directory, and never changes the account the interactive TUI uses. This is the same isolation criterion PR feat: support session-scoped interactive model overrides #227 and issue [Feature]: CLI - ACP: expose the session context-window selection (parity with the /model picker) #384 held model selection to.
- A profile name that is not usable is refused with an error, not silently replaced by the default profile. The name becomes a path segment under
$HOME, so an unchecked value must not resolve outside the home directory, and a silent fallback would open the wrong account. - Nice to have: a per-session profile.
sessionCapabilitiesalready advertiseslist,fork,resume, andclose(packages/tui/src/acp/agent.ts:452-457), so one long-lived server could host a personal thread and a work thread side by side instead of requiring one server per account. Baseline is (1) + (2); this is the larger win.
Interaction example
# editor agent config — baseline
command: mcode
args: ["acp", "--profile", "work"]
initialize
→ _meta: { "minimax-code/profile": "work" }
→ authMethods: [ { id: "minimax-code-login", ... } ] # unchanged shape
# two servers, one editor
server A: mcode acp → personal, threads for side projects
server B: mcode acp --profile work → work, threads for client work
Platform
macOS (macOS 15 arm64, mcode 0.5.8, source at 9b9c07b). The gap is in the CLI/protocol surface, so it should apply to every platform.
Alternatives and additional context
MINIMAX_DATA_DIRper client instance. Already works, and is what I use today. The problem is that it is read fromprocess.envand is therefore machine-global in practice: the editor's agent config either leaks the work account into the interactive TUI or cannot be set without a wrapper. A first-class flag is the same isolation asmcode exec --config <path>but scoped to the account.- A separate binary or a
--lane-style internal switch.mcode --lanealready exists as an internal separation mechanism, but it is rejected in the public build and is documented as internal; it is not a user-facing account selector. - Related: [Bug]: Unable to use team token plan on new app #88 asks for switching between a personal and a team token plan, which is the same underlying need on the Desktop side. [Feature]: CLI - ACP: expose the session context-window selection (parity with the /model picker) #384 and Third-party desktop client integration: is ACP the right surface for embedding mcode? #176 are the same ACP surface this touches.
- I have a working implementation of criteria 1–4 on a branch (core module, the CLI flag, a
mcode profile list|current|removecommand, the login-restart fix, and tests) and can share a patch or a branch if that is useful. I am not a collaborator, so perCONTRIBUTING.mdI am not opening a PR — happy to provide the diff for someone with push access.
Verification: mcode 0.5.8; the authMethods transcript above is a live initialize over stdio. No private project content included.
Activity
minmax commentedon Sep 29, 2026
Diff is up now, so here is the concrete branch for criteria 1–4:
Compare against
main: main...minmax:feat/auth-profilesBranch: https://github.com/minmax/minimax-code/tree/feat/auth-profiles
Commit:
3da282c7— 1 commit, 22 files, based onmainat1e136bf, no merge commits.It implements the baseline only. It does not attempt criterion 5 (per-session profile):
runTuiAcpCommandresolves onedataDirper process and every session on that connection shares the runtime, so serving two accounts from one server needs a runtime change rather than another flag. That is the piece I would want direction on before building.An adversarial review of the first draft found four problems that are worth calling out, because two of them were the same wrong-account failure this issue is about:
--profile defaultresolved to~/.minimax-default— a second account with its own token, never listed bymcode profile listand refused bymcode profile remove default.defaultis now reserved and means the implicit profile.mcode profile currentread the environment directly, so with both selectors set it reported the environment's account while the process had already opened the flag's directory.--, so a prompt containing--profile workredirected the account; it also returned the first occurrence while commander returns the last.MINIMAX_DATA_DIRbut notMINIMAX_PROFILE, so a child could inherit a profile that disagreed with the data directory it was handed.Validation (macOS,
mainat1e136bf):pnpm verify --profile platform(13 of 14 gates)test:capabilitiesmcode acp --profile workhandshake over stdioauthMethodsentry, as onmainThe only failing gate is
test:sandbox, which fails identically on a cleanmainon this machine (srt-macosprobe vs. themavis-trashshim onPATH) — unrelated to this change.I checked the observations in the report against
mainbefore filing and corrected three line references that had drifted. Happy to answer anything about the diff, or to drop criterion 5 if you would rather keep the runtime untouched.hetaoBackend commentedon Sep 29, 2026
@minmax — I checked your chain against
mainand it holds up, and the branch is a strong contribution. Answering your open question below.Verified on
maingetExplicitProfileEnv()=cliArgValue("profile") ?? runtimeEnv("PROFILE")(packages/config/src/config.ts:1333-1334), feedingresolveDataDir({ profile })(:1506) andbasenameForProfile→~/.minimax-<profile>(packages/config/src/data-dir.ts:36-42).--profile— commander has no such option, somcode acp --profile workis rejected while the internal raw-argv scanner still understands the flag. The two disagreeing about what the flag is is a real, user-visible inconsistency.packages/oauth-core/src/namespace.ts), so a distinct data dir really is a distinct account — which is what makes the isolation criterion meaningful rather than cosmetic.runTuiAcpCommand(packages/tui/src/cli/run-acp-command.ts:59), so this is genuinely an edge-surface change.Your diagnosis is right that the only current lever is a machine-global
MINIMAX_DATA_DIR/MAVIS_DATA_DIR, and that it silently changes the account for the interactive TUI and every othermcodeprocess on the machine. That's a genuinely bad property for a long-lived ACP server.Your adversarial review is the most valuable part
The four issues you caught are exactly the class of bug this feature is dangerous if it gets wrong:
--profile default→~/.minimax-defaultcreating a phantom second account was a real correctness/security problem (wrong tenant, invisible toprofile list, unremovable). Reservingdefaultas the implicit profile is the right call.profile currentreading the environment while the process had already opened the flag's directory is a genuine "reports the wrong thing" inconsistency.--and returning the first occurrence — both are correct catches; commander takes the last occurrence, so a prompt containing--profile workcould silently redirect the account. That's the highest-severity item on your list.MINIMAX_DATA_DIRbut notMINIMAX_PROFILEis a subtle inherited-state leak that would have been very hard to find later.Fixing these before asking for review, and documenting them, is the right instinct. The validation numbers (13/14 gates, 5,241 capability tests, 127 profile-specific tests) make this easy to adopt.
Direction on criterion 5 (per-session profile) — my recommendation
Keep it out of this change, exactly as you've scoped it. Reasons:
runTuiAcpCommandbuilds one runtime per process and every session on that connection shares it. Making the account per-session means either multiple runtimes (significant lifecycle/credential implications) or making the data dir mutable mid-connection — both meaningfully larger than criteria 1–4, and neither is a prerequisite for the stated problem.So: land 1–4 as-is, and please don't drop it silently — file criterion 5 as a separate follow-up issue so it's tracked rather than lost. That way the runtime design gets its own discussion instead of being decided by whatever's convenient in this diff.
Suggested follow-ups worth attaching to criterion 5 (when you file it)
profile list/profile currentreport multiple live profiles in one process.Process
Same path as #149, #364, and your #384 branch: the branch is ready for a maintainer to bring in with attribution whenever they pick it up. To make that easy, please keep it a single commit rebased on current
main(you cited1e136bf) and re-runpnpm verifyat that point, since #394/#395 have landed since — in particulartest:sandboxwas failing for exactly themavis-trash-on-PATHreason you correctly identified, and is now fixed by pinning the probePATH, so that gate should pass for you too.Keeping this in
needs-triagefor a maintainer to review and merge. Good contribution.No credentials or private data echoed.