Skip to content

[FEATURE]: keep the tab's model when switching between plan and build #54375

Open

Description

@cleardevice

Feature hasn't been suggested before.

  • 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.

Reference implementation for the TUI and the Desktop app, without the setting yet: https://github.com/cleardevice/opencode/tree/feat/tui-shared-agent-model

Activity

  1. opencode-agent commented on Oct 10, 2026

    @opencode-agent
    Contributor

    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.

    Related issues:

    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.

  2. lainwir3d commented on Oct 12, 2026

    @lainwir3d

    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

  3. cleardevice commented on Oct 12, 2026

    @cleardevice
    Author

    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.

  4. soda92 commented on Oct 15, 2026

    @soda92

    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.

  5. lainwir3d commented on Oct 15, 2026

    @lainwir3d

    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:

    • 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

  6. cleardevice commented on Oct 15, 2026

    @cleardevice
    Author

    @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.

  7. Caleb9 commented on Oct 16, 2026

    @Caleb9

    Hello!

    Confirmed the same behavior on stable 2.0.31 (also traced through the source at dev 0a03b4b). 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):

    1. In a session with agent build, switch to plan (Tab) and select model B via /models.
    2. Switch back to build and select model A.
    3. 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.

    Thanks!

  8. Lyapnev commented on Nov 8, 2026

    @Lyapnev

    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.

  9. Abody81 commented on Nov 12, 2026

    @Abody81

    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.

  10. trt44343 commented on Nov 13, 2026

    @trt44343

    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)
    • opencode:2.3.0(桌面版;resources/opencode-cli.version = 2.3.0,从 2.2.7 升级而来)
    • OS: Windows 11, 10.0.22631 (win32 x64)
    • 系统:Windows 11,10.0.22631(win32 x64)
    • Install/channel: desktop installer, latest (app-update.yml -> opencode.ai/update/api/latest/desktop/opencode/)
    • 安装方式/通道:桌面安装包,latest 通道(app-update.yml -> opencode.ai/update/api/latest/desktop/opencode/)
    • Active plugins: a local purge.js
    • 已启用插件:本地 purge.js
    • Config: root-level model set to a model different from the one used in Build; no agents.plan.model and no agents.build.model
    • 配置:根级 model 指向一个与 Build 所用不同的模型;没有 agents.plan.model,也没有 agents.build.model

    Reproduction / 复现步骤

    Two models on the same custom openai-compatible provider (command123): A = deepseek/deepseek-v4.1-flash, B = z-ai/glm-5.3-flash (also the root model).

    同一个自建 openai-compatible provider(command123)上的两个模型:A = deepseek/deepseek-v4.1-flash,B = z-ai/glm-5.3-flash(同时是根级 model)。

    1. New session, agent Build, select A, send a prompt. The session commits { agent: "build", model: A }.

    2. Switch to Plan. The model chip silently becomes B. The user never selected B.

    3. In Plan, select a model, then switch back to Build.

    4. Build shows A again, not the model just picked in Plan.

    5. 新建会话,agent 选 Build,选模型 A,发送一条消息。会话记录为 { agent: "build", model: A }。

    6. 切到 Plan。模型标签悄悄变成 B。用户从未选择过 B。

    7. 在 Plan 里选一个模型,然后切回 Build。

    8. 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 model makes it visible / 根级 model 为什么会放大这个现象

    packages/app/src/providers/models/selection.tsx:

    const key = createMemo(() => {
      if (!configuredModel.ready()) return
    
      return (
        selectChosenModel(scope(), durable(), agent.current()?.name) ??
        firstModel(() => agent.current()?.model, fallback)
      )
    })
    
    const fallback = createMemo(() => firstModel(configuredModel, recentModel, defaultModel))

    packages/app/src/providers/models/selection-machine.ts:

    export function selectChosenModel(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.

    • 读了 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 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 的回落行为。

  11. cleardevice commented on Nov 14, 2026

    @cleardevice
    Author

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions