Repository navigation
[Feature]: CLI - ACP: expose the session context-window selection (parity with the /model picker) #384
Description
Product or interface
CLI - ACP
中文摘要:
session/set_config_option目前只支持permissionMode/model/thinkingEffort三项,ACP 客户端无法设置上下文窗口。模型目录里已经带有contextWindowOptions: [512000, 1000000]和contextWindowOptionHints,TUI 的/model选择器也在用(Context 512K 1M (Tab to switch)),但这条能力没有暴露到 ACP 和 headless exec。建议补上第四个 config option,并把当前生效的窗口大小结构化暴露出来 —— 与 TUI 对齐,属于补齐接口,不是新增能力。
Use case and problem
I build an ACP client, and per the guidance in #176 I take model values from the session's advertised configOptions and treat them as opaque.
The catalog already carries a per-model context-window choice:
{ "providerId": "minimax", "modelId": "MiniMax-M3.1-Flash-Preview",
"contextLimit": 512000,
"contextWindowOptions": [512000, 1000000],
"contextWindowOptionHints": { "1000000": "higher_usage" } }and the TUI already consumes it — packages/tui/src/tui/features/model/picker.ts:295 renders Context [ 512K ] [ 1M ] (Tab to switch) plus the Higher usage with this context window. warning, and packages/config/src/local-model-provider-write.ts:205 persists the choice.
ACP has no way to reach it. ACP_CONFIG_* defines exactly three options — permissionMode, model, thinkingEffort (packages/tui/src/acp/control-state.ts:14-16) — and the set_config_option handler falls through to Unsupported Session configuration option for anything else (packages/tui/src/acp/agent.ts:1044). Observed on mcode 0.5.8, after initialize + session/new:
configId |
Result |
|---|---|
contextWindow |
Invalid params: Unsupported Session configuration option: contextWindow |
contextLimit |
same |
context |
same |
contextWindowSize |
same |
Headless exec has no equivalent either: --model accepts provider/model or provider/model#variant only (packages/tui/src/headless/model-selection.ts:34), and there is no --context flag in mcode exec --help. The model value encoding m:<provider>:<model>:u|v:<variant> (packages/tui/src/acp/control-state.ts:99) has no slot for it either.
So an ACP client that needs the 1M window has two options today, both bad: it silently runs at 512K, or it writes the user's global ~/.minimax/config.yaml (defaultModelContextWindow / minimaxModelContextLimits), which also changes the interactive TUI and every other session on the machine. mcode exec --config <path> exists for a per-process config, but mcode acp has no such flag.
Reading is barely better. The effective window is not in any structured ACP field. The only way to get it is to send /context as a session/prompt, which on a fresh session answers No Runtime context snapshot is available for this session yet. and otherwise costs a prompt round-trip (packages/tui/src/acp/commands.ts:224).
Current chain
The session-scoped selection the ACP handler already calls does carry a context limit — the ACP layer just never populates it:
packages/tui/src/acp/agent.ts:995—options.runtime.selectSessionModel(selection, sessionId)packages/tui/src/runtime/adapters/product-access.ts:123— forwards toservice("model.select").selectModel({ ...modelRequest(model), sessionId })packages/tui/src/runtime/adapters/product-access.ts:432—modelRequest()already mapscontextLimit?: numberonto the runtime request
The value is dropped at the edge, in parseModelConfigValue (packages/tui/src/acp/control-state.ts:75), which parses only providerId / modelId / variant. So this is a CLI-surface gap only.
Desired behavior
A fourth select config option next to thinkingEffort, so ACP clients get the same choice the TUI picker already offers.
Acceptance criteria
session/set_config_optionaccepts the newconfigId, returns the updatedconfigOptions, and emits aconfig_option_updatenotification — consistent with the three existing options.- Advertised values are opaque and come from the selected model's
contextWindowOptions; a value that is not advertised is rejected the same way an unadvertised model selection is. Clients must not construct the value (same rule asmodel). - The
higher_usagehint fromcontextWindowOptionHintsis carried into the option description, so a client can warn the user before switching to 1M. - The selection is session-scoped and does not write the user's global
config.yaml— the same criterion PR feat: support session-scoped interactive model overrides #227 held the interactive--modelflag to. - Nice to have: expose the effective context window in a structured field (e.g. on
session/new/session/load, or next to the existingusage_updatesize), so a client can read it without spending a prompt on/context.
Interaction example
session/new
→ configOptions: permissionMode, model, thinkingEffort, contextWindow
current=m:minimax:MiniMax-M3.1-Flash-Preview:v:thinking
session/set_config_option { configId: "contextWindow", value: <opaque 1M value> }
→ { configOptions: [... contextWindow ... current=<opaque 1M value>] }
← notification session/update { sessionUpdate: "config_option_update", ... }
Platform
macOS (macOS 15 arm64, mcode 0.5.8). The gap is protocol-level, so it should apply to every platform.
Alternatives and additional context
- [Feature]: 希望可以调整上下文大小 #140 asked for an adjustable context size; it closed on the TUI side. This request covers the ACP / headless surface, which that one did not.
- [Feature]
mcode provider addshould support context and output limits for custom/local providers #161 is the precedent for this framing: "So this is a CLI-surface gap only." - If a new config option is not wanted, the minimum acceptable alternative is parity with
mcode exec: givemcode acpa--config <path>flag, so a client can supply a per-processdefaultModelContextWindowwithout touching the global file. - The ACP surface does not appear to be documented in
docs/today; the only written guidance is the source pointers in Third-party desktop client integration: is ACP the right surface for embedding mcode? #176.
Verification: mcode 0.5.8, source at c593d3d, all observations from live mcode acp handshakes over stdio. No private project content included.
Activity
hetaoBackend commentedon Sep 28, 2026
This is an excellent, precisely-traced report — and I verified the whole chain line-for-line. Confirmed: this is a CLI-surface gap where the runtime plumbing already exists and only the ACP/headless edge drops the value. The same producer/consumer asymmetry pattern as #288.
Verification (against current
main)packages/tui/src/acp/control-state.ts:14-16declarespermissionMode,model,thinkingEffort— no context-window option. So any otherconfigIdfalls through toUnsupported Session configuration option, matching your observed error.parseModelConfigValue(control-state.ts:78-98) parses only them:<provider>:<model>:<u|v>:<variant>shape and returns only{ providerId, modelId, variant? }— there is nocontextLimitslot, so it cannot survive the ACP edge.modelRequest()inpackages/tui/src/runtime/adapters/product-access.ts:432-435already mapsmodel.contextLimitonto the runtime request (...(model.contextLimit !== undefined ? { contextLimit: model.contextLimit } : {})). And the TUI picker already consumes the catalog'scontextWindowOptions/contextWindowOptionHints, with the choice persisted inlocal-model-provider-write.ts. So the TUI path is complete; only ACP/headless is missing the hop.Your conclusion — "this is a CLI-surface gap only" — is correct and well-earned.
Classification
enhancement(interface parity for an existing capability; additive, no change to existing behavior)parseModelConfigValue→modelRequest's existingcontextLimit, keep it session-scoped). Parked inneeds-triagefor prioritization. (This tracker has noready-for-agentlabel, soneeds-triage+ this note is the routing.)Agreement on the acceptance criteria
All five of your criteria are the right ones. Two I'd underline:
contextWindowOptions, and an unadvertised value is rejected like an unadvertised model. This keeps the protocol client-agnostic and is consistent with howmodelalready works.--modelflag to. Falling back to "write the user's~/.minimax/config.yaml" is precisely the footgun you're avoiding, so preserving session scoping is important.Your point that a fresh ACP session's only way to read the effective window is to spend a prompt on
/context(which then says "No Runtime context snapshot…") also stands — the structured read exposure (criterion 5) is a reasonable nice-to-have but genuinely useful for clients.The
mcode acp --config <path>fallback (if a new config option isn't wanted) is a sensible minimum, and pairs naturally with the full option.No credentials or private project content echoed; the observations are protocol-level and already public in the report.
prawesh-12 commentedon Sep 28, 2026
Hello @hetaoBackend , here is my proposal for this feature. The issue and your triage already explain the problem. I only cover the extra design points and my test plan. Please let me know what you think.
Design points
Category. The ACP spec has a standard category called
model_config. The spec says it is for settings like context size. I want to use it for this option. The Claude ACP adapter uses the same category for its fast mode setting. Our SDK version (1.3.0) already supports it.Position. Clients show the options in the order mcode sends them. When a client has little space, it drops the last ones first. So I want to put the context window option last, after thinking effort.
Warning. In SDK 1.3.0 each choice in the list can have its own description. I want to put the text "Higher usage with this context window." only on the sizes that the model marks as
higher_usage. The 512K choice will have no warning.When the option is hidden. If the model has only one size, the option is not shown. It is also not shown if the current size of the session is not in the model's list. mcode already does the same thing for thinking effort.
A new option, not a change to the model value. The triage suggests passing the size through
parseModelConfigValue. I think a separate option is better. The model value format stays the same. Clients that already saved a model value will not break. The user can also change the size without changing the model.Value. The value is the number of tokens as a string, for example
"1000000". Clients should only send values from the list that mcode gives them. mcode will refuse any other value.Here is how the new option would look:
{ "type": "select", "id": "contextWindow", "name": "Context window", "description": "Sets the context window for the selected model in this Session.", "category": "model_config", "currentValue": "512000", "options": [ { "value": "512000", "name": "512K" }, { "value": "1000000", "name": "1M", "description": "Higher usage with this context window." } ] }What the code already does
I read the code on
main. Most of the work is already there.listModels(sessionId)already returns the current size of the session. It is set inprojectStoredParameters()inmodel-provider-application.ts. So the option can show the right size as soon as a session starts or loads. I think this also covers most of point 5 in the issue.validateContextLimit()in the same file already refuses sizes that the model does not support. So there is a second check after the ACP one.sessionLimitUpdate()sets the size back to that model's default. When the user only changes thinking effort, the size stays the same.formatContextWindow()inapplication/context-window.tsalready makes the "512K" and "1M" labels. The/modelpicker uses it too. So ACP and the terminal app will show the same labels.No change is needed in
local-runtime-v2.Code changes
In
packages/tui/src/acp/control-state.ts:thinkingEffortOption().configOptions().In
packages/tui/src/acp/agent.ts:contextWindowin thesetConfigOptionhandler. It runs insiderunSessionConfigMutation(), like the model and thinking effort cases.selectSessionModel()with the same provider, model and variant, plus the new size.thinking. So the thinking effort does not change.I will not move or add any files.
Tests
I will add tests to
packages/tui/test/unit/acp-agent.test.ts. This file is already part of thecapabilitytest group.contextLimit: 1000000withoutthinking. The reply and theconfig_option_updatemessage both show 1M.I will also test it by hand with
mcode acpand in Zed. Before I share anything, I will runpnpm verify. @minmax , you build an ACP client. If you have time, it would help a lot if you could try my branch with it.Not in this change
mcode exec. It uses different code, so I would keep it for a separate change.mcode acp --config <path>idea from the issue. With the new option, it is not needed for this case.I can work on either of these later if you want them.
Questions
model_configfine as the category? Or do you prefermodel?ctx:1000000?I am not a collaborator, so I cannot open a pull request. I will do the work on the
feat/acp-context-windowbranch in my fork. When it passes the checks, I will post the commit link here.References:
hetaoBackend commentedon Sep 28, 2026
@prawesh-12 — thanks for the detailed proposal. I checked your factual claims against
mainand against the ACP spec: everything you cite is accurate, and your central design correction is better than what I suggested in the triage note. Adopting it.Your claims, verified
validateContextLimit()exists and is a real second check —packages/local-runtime-v2/src/application/session/model-provider-application.ts:434-455. It already rejects any size not in the model'scontextWindowOptions(and enforces safe-integer,> 0, and an upper bound) withcontext_limit must be supported by the selected model. So the ACP layer doesn't need to be the only guard.projectStoredParameters()andsessionLimitUpdate()are in the same file, andcontextLimitis already threaded through selection, includingeffectiveModelContextWindow(:308). Confirmed: no change needed inlocal-runtime-v2, exactly as you said.formatContextWindow()already produces the512K/1Mlabels inpackages/tui/src/application/context-window.ts, and the/modelpicker consumes it. Reusing it means ACP and the terminal UI show identical labels.thinkingEffortOption()(control-state.ts:186-214) is the right template, and notably it already does the two hiding rules you propose — it returnsundefinedwhen the option list is empty, and also when the persisted value isn't in the list. Your "hide for a single-size model / hide when the current size isn't in the list" matches the existing pattern rather than inventing one.Your design correction is the right call — I'm withdrawing my earlier suggestion
I suggested carrying the size through
parseModelConfigValue. That was the worse option, for exactly the reason you give: it would change them:<provider>:<model>:<u|v>:<variant>value format, breaking clients that already persisted a model value, and it would couple "change the size" to "change the model". A separate config option is correct on both counts.Answers to your three questions
model_config— yes, go with it. The ACP spec definesmodel_configfor exactly this kind of setting and says clients SHOULD rendermodel_configoptions near themodelselector, with "no capability negotiation … required for category values". It also confirms your ordering reasoning: array order is significant, and clients that must drop options should prefer the beginning of the list — so puttingcontextWindowlast (afterthinkingEffort) is right. It also documents that each option entry can carry its own description, which supports putting the warning on the 1M choice only."1000000") — fine. The spec requires aselectvalue to be one of the strings in the advertisedoptionsarray, and we reject anything else, so there is no ambiguity for clients. No need for actx:-style prefix.thinkingEffortOption(), and the spec's "Agents MUST always provide a default value" is about defaults for options you do advertise, not a requirement to advertise a meaningless single-choice selector. Keeping it hidden also avoids a UI affordance that does nothing.One addition worth folding in
The spec notes the
set_config_optionresponse "always contains the complete configuration state" precisely so dependent options can be reflected. Your plan already rebuilds the list after every change — please make sure the rebuild also runs when the model changes (so the offered sizes follow the newly selected model), and that it is covered by the "after a switch to a model with one size, the option goes away" test you listed. That dependent-recompute case is the one most likely to be missed.Process
As you note, you're not a collaborator and can't open a PR here. That's fine — the same path as #149 and #364: do the work on a branch in your fork, and post the commit link on this issue when it passes checks. A maintainer can then bring it in with attribution. Please run
pnpm verifybefore posting, and include the ACP handshake evidence (mcode acpand ideally Zed) as you planned.Also, @minmax — @prawesh-12 is asking you to try their branch with your ACP client, which would be a useful independent check before adoption.
Keeping the issue in
needs-triagepending the commit. Good contribution — the spec grounding and the explicit "no change needed inlocal-runtime-v2" scope statement made this easy to review.No credentials or sensitive data echoed.
prawesh-12 commentedon Sep 29, 2026
@hetaoBackend the implementation is ready on my fork.
Commit: prawesh-12@f067774
Diff against
main: main...prawesh-12:minimax-code:feat/acp-context-windowBranch: https://github.com/prawesh-12/minimax-code/tree/feat/acp-context-window
The commit is based on
mainat4fdf523and changes 4 files.What changed
ACP sessions now have a fourth setting called
contextWindow. It follows the plan you approved.model_configcategory and comes last, after thinking effort.higher_usageshow the same warning as the/modelpicker.config.yaml. A test checks that the global default model is never changed.Only two source files changed:
packages/tui/src/acp/control-state.tsandpackages/tui/src/acp/agent.ts. The size list and the labels reuse the helpers from the/modelpicker. The error messages follow the wording of the existing model and thinking effort messages. I also added a short "ACP context window" section todocs/tui-capabilities.md, next to "ACP Skill commands". Nothing changed inlocal-runtime-v2. No files were added or moved.Evidence
Base:
mainat4fdf523pnpm typecheckpnpm lint:tuiacp-agent.test.ts)pnpm test:capabilitiespnpm buildpnpm test:smokepnpm check:sourcepnpm verifytest:windowsandtest:sandboxare not run on Linux, andtest:release-packageneeds a release archive.The fourth test passes on the old code too. It checks that the option stays hidden when the current size is not in the model's list. The old code never shows the option, so that is true there as well. The test is there to protect this rule in the future.
ACP handshake with the built CLI
I ran
node dist/cli.js acpand sent real JSON-RPC messages over stdio. I used a new temp data folder, a fake MiniMax API key and the network block frompnpm test:smoke. Both runs made no network calls. I ran the same script onmainand on my branch.On
main,session/newreturns["permissionMode", "model"]. Setting the context window fails:{"code":-32602,"message":"Invalid params: Unsupported Session configuration option: contextWindow"}On my branch,
session/newreturns["permissionMode", "model", "contextWindow"]. The new option:{ "type": "select", "id": "contextWindow", "name": "Context window", "description": "Sets the context window for the selected model in this Session.", "category": "model_config", "currentValue": "512000", "options": [ { "value": "512000", "name": "512K" }, { "value": "1000000", "name": "1M" } ] }Then:
"1000000"works. mcode sendsconfig_option_updateand the reply shows"currentValue": "1000000"."2000000"is refused:Context window is not available for the selected model: 2000000"1M"is refused:Invalid context window value: 1Mconfig_option_updateis sent for the two refused values.MiniMax-M2.7, which has one size, the list is["permissionMode", "model"].config.yamlhad no context window keys, and the default model was stillminimax/MiniMax-M3.The 1M choice has no warning in this run. The runtime only sends the
higher_usagehint for Token Plan accounts, and I used an API key. The terminal/modelpicker reads the same field. The unit tests cover the warning.I can share the full logs and the script if you want them.
Live test in Zed
I added the built CLI to Zed 1.21 as a custom agent and used a real OpenRouter model. mcode used a separate test data folder. The first model,
poolside/laguna-s-2.1:free, got two sizes (128K and 200K) that I added by hand inconfig.yaml. The second model,cohere/north-mini-code:free, had one size.What I saw:
/contextshowedBudget: 13,439 / 200,000 tokens. So the runtime really uses the chosen size./contextstill showed 200,000./contextshowed 128,000. After a switch back, the menu came back on 128K.config.yamlwas unchanged.The ACP log from Zed confirms it:
session/newreply hadcontextWindowat128000. So a new session does not take the choice of another session.session/set_config_optionagain right aftersession/new. That is why a new thread in Zed can show 200K. mcode's own reply tosession/newis always the default.contextWindow=200000again. mcode refused it withInvalid params: Context window is not available for the selected model: 200000, and Zed kept working.Screenshots:
The menu in Zed (128K and 200K):

After reopening the thread, still 200K, and
/contextshows 200,000:Model with one size: no menu, and
/contextshows 128,000:ACP log from Zed (only the context window and model messages)
Replies come in the same order as the requests. In sessions 2 and 3, the first reply answers the model change and the second answers the context window.
Not tested
config.yamlfor the test.@minmax if you have time, please try the branch with your client. Your feedback would help before it gets merged.
jaywedgeworth22 commentedon Oct 1, 2026
+1 from another ACP client. We integrate MiniMax Code through
mcode acpfrom a multi-bot desktop app. ACP exposespermissionMode,modelandthinkingEffort, but no context-window option, so the per-session 512K vs 1M choice can only follow the TUI's/modelsetting (defaultModelContextWindowinconfig.yaml), and a model switch drops it back to 512K. A session config option (for exampleconfigIdcontextWindowwith values512kand1m) or a CLI flag formcode acp, mirroringthinkingEffort, would solve this for us.