fix(providers): keep the settings provider selection under one owner

The selected provider on the Providers settings page shared a field with
the chat's model resolution. Changing the chat model or agent, and
`loadAgents` reconciling a selection, all wrote to it, so the page jumped
to an unrelated provider while the user was reading or editing one.

Only the sidebar click writes it now. A refresh no longer re-derives it
either: a provider missing from a refreshed list is expected while
OpenCode restarts and plugin-registered providers re-register.
This commit is contained in:
Bohdan Triapitsyn
2026-08-22 20:31:31 +03:00
parent c452a8092c
commit 887127b5d0
2 changed files with 68 additions and 20 deletions
+14 -20
View File
@@ -187,10 +187,6 @@ const sanitizePersistedSelectedProviderId = (providerId: string | undefined): st
providerId === ADD_PROVIDER_SENTINEL ? "" : (providerId ?? "")
);
const preserveAddProviderSelection = (currentSelectedProviderId: string | undefined, nextProviderId: string): string => (
currentSelectedProviderId === ADD_PROVIDER_SENTINEL ? ADD_PROVIDER_SENTINEL : nextProviderId
);
const normalizeOptionalString = (value: unknown): string | undefined => {
if (typeof value !== "string") {
return undefined;
@@ -1609,10 +1605,13 @@ export const useConfigStore = create<ConfigStore>()(
const currentSelectedProviderId = state.activeDirectoryKey === directoryKey
? state.selectedProviderId
: baseSnapshot.selectedProviderId;
// Preserve the add-provider sentinel so a background refresh does not
// navigate the user out of the in-progress add-provider form (issue #1765).
const selectedProviderId = currentSelectedProviderId === ADD_PROVIDER_SENTINEL
|| processedProviders.some((provider) => provider.id === currentSelectedProviderId)
// The Providers settings selection belongs to the user, not to this
// loader. A refresh may report a different provider set — an OpenCode
// restart drops plugin-registered providers until they re-register —
// and re-deriving a selection here yanked the open provider away
// mid-edit. Keep whatever is selected; only fill in an empty one.
// The add-provider sentinel is kept for the same reason (issue #1765).
const selectedProviderId = currentSelectedProviderId
? currentSelectedProviderId
: (resolvedModel?.providerId ?? processedProviders[0]?.id ?? "");
@@ -1723,12 +1722,17 @@ export const useConfigStore = create<ConfigStore>()(
nextState.currentProviderId = parsed.providerId;
nextState.currentModelId = parsed.modelId;
nextState.currentVariant = currentVariant;
nextState.selectedProviderId = parsed.providerId;
nextSnapshot.currentProviderId = parsed.providerId;
nextSnapshot.currentModelId = parsed.modelId;
nextSnapshot.currentVariant = currentVariant;
nextSnapshot.selectedProviderId = parsed.providerId;
// Only adopt this as the settings selection when the user has
// none; a failed refresh must not move an existing one.
if (!state.selectedProviderId) {
nextState.selectedProviderId = parsed.providerId;
nextSnapshot.selectedProviderId = parsed.providerId;
}
}
}
}
@@ -1771,14 +1775,12 @@ export const useConfigStore = create<ConfigStore>()(
...baseSnapshot,
currentProviderId: providerId,
currentModelId: newModelId,
selectedProviderId: providerId,
selectionSource: "manual",
};
return {
currentProviderId: providerId,
currentModelId: newModelId,
selectedProviderId: providerId,
selectionSource: "manual",
directoryScoped: {
...state.directoryScoped,
@@ -2456,7 +2458,6 @@ export const useConfigStore = create<ConfigStore>()(
currentProviderId: providerId,
currentModelId: modelId,
currentVariant: variant,
selectedProviderId: preserveAddProviderSelection(state.selectedProviderId, providerId),
selectionSource: "manual",
};
@@ -2464,7 +2465,6 @@ export const useConfigStore = create<ConfigStore>()(
currentProviderId: providerId,
currentModelId: modelId,
currentVariant: variant,
selectedProviderId: preserveAddProviderSelection(state.selectedProviderId, providerId),
selectionSource: "manual",
directoryScoped: {
...state.directoryScoped,
@@ -2616,7 +2616,6 @@ export const useConfigStore = create<ConfigStore>()(
currentProviderId: resolvedProviderId,
currentModelId: resolvedModelId,
currentVariant: resolvedVariant,
selectedProviderId: preserveAddProviderSelection(state.selectedProviderId, resolvedProviderId),
}
: {}),
selectionSource: "auto",
@@ -2635,7 +2634,6 @@ export const useConfigStore = create<ConfigStore>()(
nextState.currentProviderId = resolvedProviderId;
nextState.currentModelId = resolvedModelId;
nextState.currentVariant = resolvedVariant;
nextState.selectedProviderId = preserveAddProviderSelection(state.selectedProviderId, resolvedProviderId);
}
return nextState;
@@ -2717,7 +2715,6 @@ export const useConfigStore = create<ConfigStore>()(
const currentProviderId = isActive ? state.currentProviderId : baseSnapshot.currentProviderId;
const currentModelId = isActive ? state.currentModelId : baseSnapshot.currentModelId;
const currentVariant = isActive ? state.currentVariant : baseSnapshot.currentVariant;
const currentSelectedProviderId = isActive ? state.selectedProviderId : baseSnapshot.selectedProviderId;
const nextSelection = resolveSelectionWithManualGuard({
agents,
providers,
@@ -2742,7 +2739,6 @@ export const useConfigStore = create<ConfigStore>()(
currentProviderId: nextSelection.providerId,
currentModelId: nextSelection.modelId,
currentVariant: nextSelection.variant,
selectedProviderId: preserveAddProviderSelection(currentSelectedProviderId, nextSelection.providerId),
}
: {}),
selectionSource: nextSelection.selectionSource,
@@ -2761,7 +2757,6 @@ export const useConfigStore = create<ConfigStore>()(
state.currentProviderId !== nextSelection.providerId
|| state.currentModelId !== nextSelection.modelId
|| state.currentVariant !== nextSelection.variant
|| state.selectedProviderId !== preserveAddProviderSelection(currentSelectedProviderId, nextSelection.providerId)
))
));
@@ -2781,7 +2776,6 @@ export const useConfigStore = create<ConfigStore>()(
nextState.currentProviderId = nextSelection.providerId;
nextState.currentModelId = nextSelection.modelId;
nextState.currentVariant = nextSelection.variant;
nextState.selectedProviderId = preserveAddProviderSelection(currentSelectedProviderId, nextSelection.providerId);
}
}