You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[FEATURE]: keep the tab's model when switching between plan and build #54375
I have verified this feature I'm about to request hasn't been suggested before.
Describe the enhancement you want to request
Switching agents with Tab restores the model each agent last used in the session (#47260). An agent without a configured model and without an earlier choice in the session falls back to the global default or the shared recent list, which is the model last picked in any tab or client. In practice I pick one model for a task and switch between plan and build several times, and expect that model to stay.
Proposal: agents without a configured model take the tab's current selection (model and variant) when switching. Agents with model in their config keep the current behavior, so a dedicated planning model still works. Since per-agent memory was added on purpose, this could be a setting, for example session.agent_model: "per-agent" | "shared", with per-agent as the default.
Thanks for the detailed proposal and the reference branch!
To restate it: when you switch between primary agents with Tab, agents that have no model in their config would keep the tab's current model and variant instead of the model remembered for that agent or the shared default. Agents with a configured model would keep their current behavior. This could be an opt-in TUI setting such as session.agent_model: "shared". Maintainers will consider it.
Agree. Very irritating when switching between loads of different sessions with different models. Has costed me a lot of tokens because of course cache can't be hit like that. Also cause confidentiality issue when switching between personal projects / models and work related ones.
Well, I choose a different model in Plan, and when I want to build - I press shift_tab - it will switch to prev model, which result in low response time (because of context resend) and even compression (different model have different context limits) - so I think this feature/setting was helpful.
Plan / Build mode models are also shared between tabs so if you use a build model X for on tab A, then switch to tab B that was in Plan mode with model B and switch it to build mode it will use model X silentely.
Workflow:
Tab A - build mode - model X
SWITCH MODE
Tab A - plan mode - model X
CHANGE TAB TO TAB B
Tab B - build mode - model X
SWITCH MODEL
Tab B - build mode - model Y
SWITCH TAB
Tab A - plan mode - model X
SWITCH MODE - Tab A - build mode - model Y
This is approximately what's happening. A silent model switch. Causing loads of slow down, token usage, and privacy concern depending on model
@lainwir3d thanks, reproduced on dev with a test. If build has no model of its own (and no global model is set) and hasn't been used in that tab since the session was opened, e.g. after a restart, it takes the model picked last in any tab. #54367 doesn't fix this; the reference branch above keeps the tab's model in this case.
@soda92 yours is the per-agent memory from #47260 working as designed; the setting proposed here would let you turn it off.
Confirmed the same behavior on stable 2.0.31 (also traced through the source at dev0a03b4b). A couple of concrete data points in support of the proposed session.agent_model: "shared" option:
The re-application is pure TUI client state — the server's model is session-wide and survives agent switches. The session row stores a single model, and POST /api/session/{id}/agent does not touch it. Verified with a scratch session: after switching Plan → Build, the server-side model was unchanged. So after an agent switch the TUI composer and the server model briefly disagree until the user re-selects or submits.
Exact code locations (2.0.31):
packages/tui/src/context/local.tsx — the selectionBySessionAgent map; agent.set() calls model.remember(), which stashes the outgoing agent's model, and the composer then reads the incoming agent's remembered model.
packages/tui/src/component/prompt/index.tsx — the remembered agent/model pair is committed on submit (switchAgent / switchModel).
/data/state/opencode/model.json (TUI state) only holds the global recent-model/variant list, not per-agent state — so the per-agent memory is not persisted anywhere server-side.
Repro on 2.0.31 (single local OpenAI-compatible endpoint, no agents.*.model pinned):
In a session with agent build, switch to plan (Tab) and select model B via /models.
Switch back to build and select model A.
Tab back to plan — the composer shows B again, even though the last explicit selection was A. The session DB shows an agent-switched event immediately followed by a model-switched event with no user message in between.
The shared option (last explicit selection wins across agents) would fix the common single-model case, and keeping per-agent as the default preserves the intentional behavior for anyone relying on it. The proposed knob is exactly the right shape for that.
Same here. I pick a model for a task and then Shift+Tab between plan/build/architect; the first switch to an agent that has no model chosen in that session jumps back to the global default, and the next request re-sends the whole context to a different model (prompt cache miss, sometimes different context limits).
A session.agent_model: "shared" option would cover this exactly: when an agent has no model configured, keep the tab's current selection (model and variant) instead of the per-agent memory or the global default.
Same experience on the Desktop app, which adds a client beyond the TUI reports above.
Environment: Desktop app, opencode v2.2.7 (CLI) / v2.2.6 (server), Linux x86_64, no plugins, no agents.*.model and no root model pinned.
Typical case: I work in Plan mode, then switch to Build — the app silently switches to the last model I ever used with Build, sometimes one selected in an entirely different conversation. A concrete example: I once picked an expensive model for a trivial task in another chat; ever since, returning to my main conversation and switching to Build re-selects that expensive model on its own.
For us this has caused more problems than the per-agent memory solves:
Cost and token waste — the full context is re-sent to an unintended model: prompt-cache misses plus a pricier model than the task needs.
Unnecessary compaction — a model with a smaller context window silently triggers compaction the intended model wouldn't need.
Easy to miss — the switch only becomes visible in the composer after changing modes, so a prompt can be submitted to the wrong model before noticing.
Strong support for the proposed session.agent_model: "shared" option, or for making "keep the tab's current model" the fallback for agents without a configured model.
Still reproducing on OpenCode Desktop 2.3.0 (Windows). This comment adds one data point not covered above: a root-level model amplifies this behaviour.
在 OpenCode Desktop 2.3.0(Windows)上仍然复现。这条评论补充一个上面没覆盖到的因素:根级 model 会放大这个现象。
Environment / 环境
opencode: 2.3.0 (desktop; resources/opencode-cli.version = 2.3.0, updated from 2.2.7)
exportfunctionselectChosenModel(local,durable,agent){if(local?.choice)return{providerID: local.choice.providerID,modelID: local.choice.modelID}// The Session's model belongs to the agent it was committed with.if(durable?.model&&(!durable.agent||durable.agent===agent))return{providerID: durable.model.providerID,modelID: durable.model.id}}
In step 2 there is no choices["plan"] entry, and durable.agent is "build", so the committed model A is deliberately skipped. With no agents.plan.model, the chain lands on configuredModel, which is the root model B.
在步骤 2 中,choices["plan"] 里没有任何记录,而 durable.agent 是 "build",所以已提交的模型 A 被有意跳过。加上没有配置 agents.plan.model,整条取值链就落到 configuredModel,也就是根级 model B。
Removing the root model line makes the same switch resolve to recentModel instead, which is A, so the symptom disappears.
把根级 model 那一行删掉后,同样的切换会改为落到 recentModel,也就是 A,现象随之消失。
Per-agent memory is unchanged either way.
无论删不删,per-agent 记忆本身都没有变。
Verified, not assumed / 实测而非推测
Read the source at the 2.3.0 tree for packages/app/src/providers/models/selection.tsx and selection-machine.ts.
Removed the root model line from the config, fully restarted the desktop app, and re-ran steps 1-2: Plan kept A instead of jumping to B.
Step 3 still shows A in Build after the config change, which confirms per-agent memory is untouched by the root model and that the root model is the amplifier, not the cause.
从配置中删掉根级 model 这一行,完全重启桌面端,重新执行步骤 1-2:Plan 保持了 A,没有跳到 B。
改配置后步骤 3 在 Build 中仍然显示 A,确认 per-agent 记忆不受根级 model 影响,根级 model 只是放大器,不是根因。
No opt-out in 2.3.0 / 2.3.0 没有任何开关
Checked https://opencode.ai/config.json: agent_model, agentModel, perAgent, and any per-agent memory switch are all absent.
Prompt-cache miss on every switch, because the whole context is re-sent to a different model.
Silent compaction when the fallback model has a smaller context window.
A root model pointing at an expensive or capability-different model gets picked up unintentionally by the fallback.
The change only becomes visible in the composer after the switch, so a prompt can be submitted to the wrong model first.
每次切换都会丢失 prompt 缓存,因为整个上下文被重新发给了另一个模型。
当回落模型的上下文窗口更小时,会静默触发压缩。
当根级 model 指向一个更贵或能力不同的模型时,回落逻辑会在用户不知情的情况下选中它。
切换后才在输入框里显示变化,所以有可能在察觉前就把消息发给了错误的模型。
Support session.agent_model: "shared" while keeping per-agent memory as the default, or make "keep the tab's current model" the fallback for agents that have no configured model.
支持加入 session.agent_model: "shared",同时保留 per-agent 记忆作为默认行为;或者把「保持标签页当前模型」作为没有配置 model 的 agent 的回落行为。
Thanks for the reports. @trt44343's trace matches dev: the Desktop app picks the model in its own code (packages/app), so the fix has to land there too. I've rebased the reference branch onto dev and added the Desktop change: agents without a configured model keep the tab's model and variant.
Activity
opencode-agent commentedon Oct 10, 2026
Thanks for the detailed proposal and the reference branch!
To restate it: when you switch between primary agents with Tab, agents that have no
modelin their config would keep the tab's current model and variant instead of the model remembered for that agent or the shared default. Agents with a configuredmodelwould keep their current behavior. This could be an opt-in TUI setting such assession.agent_model: "shared". Maintainers will consider it.Related issues:
OPENCODE_AGENTS_SWITCH_SINGLE_MODELenv var). It was closed as stale.A plugin can't do this today: the TUI plugin API can set the variant but not the model, and it has no hook for agent switches.
lainwir3d commentedon Oct 12, 2026
Agree. Very irritating when switching between loads of different sessions with different models. Has costed me a lot of tokens because of course cache can't be hit like that. Also cause confidentiality issue when switching between personal projects / models and work related ones.
It should probably be a setting I guess
cleardevice commentedon Oct 12, 2026
Thanks! If your case is switching between sessions/tabs (not plan ↔ build), that's #54363 — fixed in #54367. A 👍 or your scenario there would help.
soda92 commentedon Oct 15, 2026
Well, I choose a different model in Plan, and when I want to build - I press shift_tab - it will switch to prev model, which result in low response time (because of context resend) and even compression (different model have different context limits) - so I think this feature/setting was helpful.
lainwir3d commentedon Oct 15, 2026
Yeah that's exactly it.
Plan / Build mode models are also shared between tabs so if you use a build model X for on tab A, then switch to tab B that was in Plan mode with model B and switch it to build mode it will use model X silentely.
Workflow:
SWITCH MODE
CHANGE TAB TO TAB B
SWITCH MODEL
SWITCH TAB
SWITCH MODE
- Tab A - build mode - model Y
This is approximately what's happening. A silent model switch. Causing loads of slow down, token usage, and privacy concern depending on model
cleardevice commentedon Oct 15, 2026
@lainwir3d thanks, reproduced on
devwith a test. If build has nomodelof its own (and no globalmodelis set) and hasn't been used in that tab since the session was opened, e.g. after a restart, it takes the model picked last in any tab. #54367 doesn't fix this; the reference branch above keeps the tab's model in this case.@soda92 yours is the per-agent memory from #47260 working as designed; the setting proposed here would let you turn it off.
Caleb9 commentedon Oct 16, 2026
Hello!
Confirmed the same behavior on stable 2.0.31 (also traced through the source at
dev0a03b4b). A couple of concrete data points in support of the proposedsession.agent_model: "shared"option:The re-application is pure TUI client state — the server's model is session-wide and survives agent switches. The
sessionrow stores a singlemodel, andPOST /api/session/{id}/agentdoes not touch it. Verified with a scratch session: after switching Plan → Build, the server-side model was unchanged. So after an agent switch the TUI composer and the server model briefly disagree until the user re-selects or submits.Exact code locations (2.0.31):
packages/tui/src/context/local.tsx— theselectionBySessionAgentmap;agent.set()callsmodel.remember(), which stashes the outgoing agent's model, and the composer then reads the incoming agent's remembered model.packages/tui/src/component/prompt/index.tsx— the remembered agent/model pair is committed on submit (switchAgent/switchModel)./data/state/opencode/model.json(TUI state) only holds the global recent-model/variant list, not per-agent state — so the per-agent memory is not persisted anywhere server-side.Repro on 2.0.31 (single local OpenAI-compatible endpoint, no
agents.*.modelpinned):build, switch toplan(Tab) and select model B via/models.buildand select model A.plan— the composer shows B again, even though the last explicit selection was A. The session DB shows anagent-switchedevent immediately followed by amodel-switchedevent with no user message in between.The
sharedoption (last explicit selection wins across agents) would fix the common single-model case, and keepingper-agentas the default preserves the intentional behavior for anyone relying on it. The proposed knob is exactly the right shape for that.Thanks!
Lyapnev commentedon Nov 8, 2026
Same here. I pick a model for a task and then Shift+Tab between plan/build/architect; the first switch to an agent that has no model chosen in that session jumps back to the global default, and the next request re-sends the whole context to a different model (prompt cache miss, sometimes different context limits).
A
session.agent_model: "shared"option would cover this exactly: when an agent has nomodelconfigured, keep the tab's current selection (model and variant) instead of the per-agent memory or the global default.Abody81 commentedon Nov 12, 2026
Same experience on the Desktop app, which adds a client beyond the TUI reports above.
Environment: Desktop app, opencode v2.2.7 (CLI) / v2.2.6 (server), Linux x86_64, no plugins, no
agents.*.modeland no rootmodelpinned.Typical case: I work in Plan mode, then switch to Build — the app silently switches to the last model I ever used with Build, sometimes one selected in an entirely different conversation. A concrete example: I once picked an expensive model for a trivial task in another chat; ever since, returning to my main conversation and switching to Build re-selects that expensive model on its own.
For us this has caused more problems than the per-agent memory solves:
Strong support for the proposed
session.agent_model: "shared"option, or for making "keep the tab's current model" the fallback for agents without a configuredmodel.trt44343 commentedon Nov 13, 2026
Still reproducing on OpenCode Desktop 2.3.0 (Windows). This comment adds one data point not covered above: a root-level
modelamplifies this behaviour.在 OpenCode Desktop 2.3.0(Windows)上仍然复现。这条评论补充一个上面没覆盖到的因素:根级
model会放大这个现象。Environment / 环境
resources/opencode-cli.version= 2.3.0, updated from 2.2.7)resources/opencode-cli.version= 2.3.0,从 2.2.7 升级而来)latest(app-update.yml->opencode.ai/update/api/latest/desktop/opencode/)latest通道(app-update.yml->opencode.ai/update/api/latest/desktop/opencode/)purge.jspurge.jsmodelset to a model different from the one used in Build; noagents.plan.modeland noagents.build.modelmodel指向一个与 Build 所用不同的模型;没有agents.plan.model,也没有agents.build.modelReproduction / 复现步骤
Two models on the same custom
openai-compatibleprovider (command123): A =deepseek/deepseek-v4.1-flash, B =z-ai/glm-5.3-flash(also the rootmodel).同一个自建
openai-compatibleprovider(command123)上的两个模型:A =deepseek/deepseek-v4.1-flash,B =z-ai/glm-5.3-flash(同时是根级model)。New session, agent Build, select A, send a prompt. The session commits
{ agent: "build", model: A }.Switch to Plan. The model chip silently becomes B. The user never selected B.
In Plan, select a model, then switch back to Build.
Build shows A again, not the model just picked in Plan.
新建会话,agent 选 Build,选模型 A,发送一条消息。会话记录为
{ agent: "build", model: A }。切到 Plan。模型标签悄悄变成 B。用户从未选择过 B。
在 Plan 里选一个模型,然后切回 Build。
Build 又显示 A,不是在 Plan 里刚选的那个。
Expected: the tab keeps its current model across agent switches, or at minimum the model last explicitly selected wins.
期望:切换 agent 时,标签页保持当前模型;至少应该以最后一次显式选择为准。
Actual: each agent restores its own remembered model, and an agent with no memory in this session falls through to the root
model.实际:每个 agent 各自恢复它记住的模型;在当前会话里没有记忆的 agent,会一路回落到根级
model。Why a root
modelmakes it visible / 根级model为什么会放大这个现象packages/app/src/providers/models/selection.tsx:packages/app/src/providers/models/selection-machine.ts:In step 2 there is no
choices["plan"]entry, anddurable.agentis"build", so the committed model A is deliberately skipped. With noagents.plan.model, the chain lands onconfiguredModel, which is the rootmodelB.在步骤 2 中,
choices["plan"]里没有任何记录,而durable.agent是"build",所以已提交的模型 A 被有意跳过。加上没有配置agents.plan.model,整条取值链就落到configuredModel,也就是根级modelB。Removing the root
modelline makes the same switch resolve torecentModelinstead, which is A, so the symptom disappears.把根级
model那一行删掉后,同样的切换会改为落到recentModel,也就是 A,现象随之消失。Per-agent memory is unchanged either way.
无论删不删,per-agent 记忆本身都没有变。
Verified, not assumed / 实测而非推测
Read the source at the 2.3.0 tree for
packages/app/src/providers/models/selection.tsxandselection-machine.ts.Removed the root
modelline from the config, fully restarted the desktop app, and re-ran steps 1-2: Plan kept A instead of jumping to B.Step 3 still shows A in Build after the config change, which confirms per-agent memory is untouched by the root
modeland that the rootmodelis the amplifier, not the cause.读了 2.3.0 源码树里的
packages/app/src/providers/models/selection.tsx和selection-machine.ts。从配置中删掉根级
model这一行,完全重启桌面端,重新执行步骤 1-2:Plan 保持了 A,没有跳到 B。改配置后步骤 3 在 Build 中仍然显示 A,确认 per-agent 记忆不受根级
model影响,根级model只是放大器,不是根因。No opt-out in 2.3.0 / 2.3.0 没有任何开关
Checked
https://opencode.ai/config.json:agent_model,agentModel,perAgent, and any per-agent memory switch are all absent.查了
https://opencode.ai/config.json:agent_model、agentModel、perAgent以及任何 per-agent 记忆开关,全都不存在。Impact / 影响
Prompt-cache miss on every switch, because the whole context is re-sent to a different model.
Silent compaction when the fallback model has a smaller context window.
A root
modelpointing at an expensive or capability-different model gets picked up unintentionally by the fallback.The change only becomes visible in the composer after the switch, so a prompt can be submitted to the wrong model first.
每次切换都会丢失 prompt 缓存,因为整个上下文被重新发给了另一个模型。
当回落模型的上下文窗口更小时,会静默触发压缩。
当根级
model指向一个更贵或能力不同的模型时,回落逻辑会在用户不知情的情况下选中它。切换后才在输入框里显示变化,所以有可能在察觉前就把消息发给了错误的模型。
Support
session.agent_model: "shared"while keeping per-agent memory as the default, or make "keep the tab's current model" the fallback for agents that have no configuredmodel.支持加入
session.agent_model: "shared",同时保留 per-agent 记忆作为默认行为;或者把「保持标签页当前模型」作为没有配置model的 agent 的回落行为。cleardevice commentedon Nov 14, 2026
Thanks for the reports. @trt44343's trace matches
dev: the Desktop app picks the model in its own code (packages/app), so the fix has to land there too. I've rebased the reference branch ontodevand added the Desktop change: agents without a configuredmodelkeep the tab's model and variant.