fix(ui): keep the selected model when switching agent modes (#2690)

fix(ui): keep the selected model when switching agent modes
This commit is contained in:
Bohdan Triapitsyn
2026-08-28 23:41:16 +03:00
committed by GitHub
3 changed files with 36 additions and 37 deletions
@@ -633,7 +633,6 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
];
const prevAgentNameRef = React.useRef<string | undefined>(undefined);
const explicitAgentSwitchRef = React.useRef<string | null>(null);
const latestLoadedUserChoiceRestoreRef = React.useRef<string | null>(null);
const currentSessionDirectory = currentSessionId ? getDirectoryForSession(currentSessionId) : undefined;
@@ -1051,9 +1050,6 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
prevAgentNameRef.current = currentAgentName;
if (currentAgentName && currentSessionId) {
const shouldPreferAgentModel = explicitAgentSwitchRef.current === currentAgentName;
explicitAgentSwitchRef.current = null;
await new Promise<void>((resolve) => {
const timer = setTimeout(resolve, 50);
abortController.signal.addEventListener('abort', () => {
@@ -1066,33 +1062,6 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
return;
}
const selectedAgent = shouldPreferAgentModel
? agents.find((agent) => agent.name === currentAgentName)
: undefined;
if (selectedAgent?.model?.providerID && selectedAgent.model.modelID) {
const result = tryApplyModelSelection(
selectedAgent.model.providerID,
selectedAgent.model.modelID,
currentAgentName,
);
if (result === 'applied' || result === 'provider-missing') {
if (result === 'applied') {
saveSessionModelSelection(
currentSessionId,
selectedAgent.model.providerID,
selectedAgent.model.modelID,
);
saveAgentModelForSession(
currentSessionId,
currentAgentName,
selectedAgent.model.providerID,
selectedAgent.model.modelID,
);
}
return;
}
}
const persistedChoice = getAgentModelForSession(currentSessionId, currentAgentName);
if (persistedChoice) {
@@ -1118,12 +1087,9 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
abortController.abort();
};
}, [
agents,
currentAgentName,
currentSessionId,
getAgentModelForSession,
saveAgentModelForSession,
saveSessionModelSelection,
tryApplyModelSelection,
contextHydrated,
]);
@@ -1212,7 +1178,6 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
const handleAgentChange = React.useCallback((agentName: string, options?: { closeModelSelector?: boolean }) => {
try {
explicitAgentSwitchRef.current = agentName;
setAgent(agentName);
addRecentAgent(agentName);
if (options?.closeModelSelector ?? true) {
@@ -698,6 +698,30 @@ describe('useConfigStore provider persistence', () => {
expect(state.currentModelId).toBe('model-a');
});
test('[issue-2531] setAgent keeps the manual model when switching to an agent without an override', () => {
const sessionId = 'ses_2531_mode_switch';
useSessionUIStore.setState({ currentSessionId: sessionId });
useConfigStore.setState({
activeDirectoryKey: DIRECTORY,
providers: [provider('deepseek', 'deepseek-v4-pro'), provider('kimi', 'kimi-k3')],
agents: [testAgent('build'), testAgent('plan')],
settingsDefaultModel: 'deepseek/deepseek-v4-pro',
currentProviderId: 'kimi',
currentModelId: 'kimi-k3',
currentAgentName: 'build',
selectionSource: 'manual',
currentVariant: undefined,
directoryScoped: {},
});
useConfigStore.getState().setAgent('plan');
const state = useConfigStore.getState();
expect(state.currentAgentName).toBe('plan');
expect(state.currentProviderId).toBe('kimi');
expect(state.currentModelId).toBe('kimi-k3');
});
test('loadAgents does not fetch OpenCode config directly', async () => {
useConfigStore.setState({
activeDirectoryKey: DIRECTORY,
+12 -2
View File
@@ -2435,6 +2435,9 @@ export const useConfigStore = create<ConfigStore>()(
currentProviderId,
currentModelId,
} = get();
// Captured before the first set below, which unconditionally
// marks the selection as manual.
const hadManualSelection = get().selectionSource === "manual";
set((state) => {
const directoryKey = state.activeDirectoryKey;
@@ -2554,8 +2557,7 @@ export const useConfigStore = create<ConfigStore>()(
// Prefer a session-level manual override for this agent over the
// agent's configured default. Re-applying setAgent after subtask
// completion / rematerialization must not clobber the override
// (issue #2404). Explicit agent-picker switches still force the
// agent default via ModelControls' shouldPreferAgentModel path.
// (issue #2404).
if (currentSessionId) {
const existingAgentModel = useSelectionStore.getState().getAgentModelForSession(currentSessionId, agentName);
if (existingAgentModel && hasProviderModel(providers, existingAgentModel.providerId, existingAgentModel.modelId)) {
@@ -2584,6 +2586,14 @@ export const useConfigStore = create<ConfigStore>()(
}
}
// The user has a live manual model selection and the target
// agent configures no model of its own. Switching modes or
// agents must not reset the selection to the settings default
// (issue #2531) — mode switches are not model changes.
if (hadManualSelection && currentProviderId && currentModelId) {
return;
}
// If the agent has no preferred model, use settings default.
if (settingsDefaultModel) {
const parsed = parseModelString(settingsDefaultModel);