feat(opencode): persist dynamic model selection

This commit is contained in:
DhanushSantosh
2026-01-12 23:07:05 +05:30
parent 9ce3cfee7d
commit 8094941385
4 changed files with 21 additions and 12 deletions

View File

@@ -158,6 +158,8 @@ export function parseLocalStorageSettings(): Partial<GlobalSettings> | null {
cursorDefaultModel: state.cursorDefaultModel as GlobalSettings['cursorDefaultModel'], cursorDefaultModel: state.cursorDefaultModel as GlobalSettings['cursorDefaultModel'],
enabledOpencodeModels: state.enabledOpencodeModels as GlobalSettings['enabledOpencodeModels'], enabledOpencodeModels: state.enabledOpencodeModels as GlobalSettings['enabledOpencodeModels'],
opencodeDefaultModel: state.opencodeDefaultModel as GlobalSettings['opencodeDefaultModel'], opencodeDefaultModel: state.opencodeDefaultModel as GlobalSettings['opencodeDefaultModel'],
enabledDynamicModelIds:
state.enabledDynamicModelIds as GlobalSettings['enabledDynamicModelIds'],
autoLoadClaudeMd: state.autoLoadClaudeMd as boolean, autoLoadClaudeMd: state.autoLoadClaudeMd as boolean,
keyboardShortcuts: state.keyboardShortcuts as GlobalSettings['keyboardShortcuts'], keyboardShortcuts: state.keyboardShortcuts as GlobalSettings['keyboardShortcuts'],
mcpServers: state.mcpServers as GlobalSettings['mcpServers'], mcpServers: state.mcpServers as GlobalSettings['mcpServers'],
@@ -517,6 +519,12 @@ export function hydrateStoreFromSettings(settings: GlobalSettings): void {
sanitizedEnabledOpencodeModels.push(sanitizedOpencodeDefaultModel); sanitizedEnabledOpencodeModels.push(sanitizedOpencodeDefaultModel);
} }
const persistedDynamicModelIds =
settings.enabledDynamicModelIds ?? current.enabledDynamicModelIds;
const sanitizedDynamicModelIds = persistedDynamicModelIds.filter(
(modelId) => !modelId.startsWith('amazon-bedrock/')
);
// Convert ProjectRef[] to Project[] (minimal data, features will be loaded separately) // Convert ProjectRef[] to Project[] (minimal data, features will be loaded separately)
const projects = (settings.projects ?? []).map((ref) => ({ const projects = (settings.projects ?? []).map((ref) => ({
id: ref.id, id: ref.id,
@@ -562,6 +570,7 @@ export function hydrateStoreFromSettings(settings: GlobalSettings): void {
cursorDefaultModel: settings.cursorDefaultModel ?? 'auto', cursorDefaultModel: settings.cursorDefaultModel ?? 'auto',
enabledOpencodeModels: sanitizedEnabledOpencodeModels, enabledOpencodeModels: sanitizedEnabledOpencodeModels,
opencodeDefaultModel: sanitizedOpencodeDefaultModel, opencodeDefaultModel: sanitizedOpencodeDefaultModel,
enabledDynamicModelIds: sanitizedDynamicModelIds,
autoLoadClaudeMd: settings.autoLoadClaudeMd ?? false, autoLoadClaudeMd: settings.autoLoadClaudeMd ?? false,
skipSandboxWarning: settings.skipSandboxWarning ?? false, skipSandboxWarning: settings.skipSandboxWarning ?? false,
keyboardShortcuts: { keyboardShortcuts: {
@@ -615,6 +624,7 @@ function buildSettingsUpdateFromStore(): Record<string, unknown> {
enhancementModel: state.enhancementModel, enhancementModel: state.enhancementModel,
validationModel: state.validationModel, validationModel: state.validationModel,
phaseModels: state.phaseModels, phaseModels: state.phaseModels,
enabledDynamicModelIds: state.enabledDynamicModelIds,
autoLoadClaudeMd: state.autoLoadClaudeMd, autoLoadClaudeMd: state.autoLoadClaudeMd,
skipSandboxWarning: state.skipSandboxWarning, skipSandboxWarning: state.skipSandboxWarning,
keyboardShortcuts: state.keyboardShortcuts, keyboardShortcuts: state.keyboardShortcuts,

View File

@@ -46,6 +46,7 @@ const SETTINGS_FIELDS_TO_SYNC = [
'cursorDefaultModel', 'cursorDefaultModel',
'enabledOpencodeModels', 'enabledOpencodeModels',
'opencodeDefaultModel', 'opencodeDefaultModel',
'enabledDynamicModelIds',
'autoLoadClaudeMd', 'autoLoadClaudeMd',
'keyboardShortcuts', 'keyboardShortcuts',
'mcpServers', 'mcpServers',

View File

@@ -592,7 +592,7 @@ export interface AppState {
// Dynamic models are session-only (not persisted) because they're discovered at runtime // Dynamic models are session-only (not persisted) because they're discovered at runtime
// from `opencode models` CLI and depend on current provider authentication state // from `opencode models` CLI and depend on current provider authentication state
dynamicOpencodeModels: ModelDefinition[]; // Dynamically discovered models from OpenCode CLI dynamicOpencodeModels: ModelDefinition[]; // Dynamically discovered models from OpenCode CLI
enabledDynamicModelIds: string[]; // Which dynamic models are enabled (session-only) enabledDynamicModelIds: string[]; // Which dynamic models are enabled
cachedOpencodeProviders: Array<{ cachedOpencodeProviders: Array<{
id: string; id: string;
name: string; name: string;
@@ -1241,7 +1241,7 @@ const initialState: AppState = {
enabledOpencodeModels: getAllOpencodeModelIds(), // All OpenCode models enabled by default enabledOpencodeModels: getAllOpencodeModelIds(), // All OpenCode models enabled by default
opencodeDefaultModel: DEFAULT_OPENCODE_MODEL, // Default to OpenCode free tier opencodeDefaultModel: DEFAULT_OPENCODE_MODEL, // Default to OpenCode free tier
dynamicOpencodeModels: [], // Empty until fetched from OpenCode CLI dynamicOpencodeModels: [], // Empty until fetched from OpenCode CLI
enabledDynamicModelIds: [], // All dynamic models enabled by default (populated when models are fetched) enabledDynamicModelIds: [], // Empty until user enables dynamic models
cachedOpencodeProviders: [], // Empty until fetched from OpenCode CLI cachedOpencodeProviders: [], // Empty until fetched from OpenCode CLI
autoLoadClaudeMd: false, // Default to disabled (user must opt-in) autoLoadClaudeMd: false, // Default to disabled (user must opt-in)
skipSandboxWarning: false, // Default to disabled (show sandbox warning dialog) skipSandboxWarning: false, // Default to disabled (show sandbox warning dialog)
@@ -2041,9 +2041,8 @@ export const useAppStore = create<AppState & AppActions>()((set, get) => ({
: state.enabledOpencodeModels.filter((m) => m !== model), : state.enabledOpencodeModels.filter((m) => m !== model),
})), })),
setDynamicOpencodeModels: (models) => { setDynamicOpencodeModels: (models) => {
// Dynamic models are session-only (not persisted to server) because they depend on // Dynamic models depend on CLI authentication state and are re-discovered each session.
// current CLI authentication state and are re-discovered each session // Persist enabled model IDs, but do not auto-enable new models.
// When setting dynamic models, auto-enable all of them if enabledDynamicModelIds is empty
const filteredModels = models.filter( const filteredModels = models.filter(
(model) => (model) =>
model.provider !== OPENCODE_BEDROCK_PROVIDER_ID && model.provider !== OPENCODE_BEDROCK_PROVIDER_ID &&
@@ -2051,14 +2050,10 @@ export const useAppStore = create<AppState & AppActions>()((set, get) => ({
); );
const currentEnabled = get().enabledDynamicModelIds; const currentEnabled = get().enabledDynamicModelIds;
const newModelIds = filteredModels.map((m) => m.id); const newModelIds = filteredModels.map((m) => m.id);
const filteredEnabled = currentEnabled.filter((modelId) => newModelIds.includes(modelId));
// If no models were previously enabled, enable all new ones const nextEnabled = currentEnabled.length === 0 ? [] : filteredEnabled;
if (currentEnabled.length === 0) { set({ dynamicOpencodeModels: filteredModels, enabledDynamicModelIds: nextEnabled });
set({ dynamicOpencodeModels: filteredModels, enabledDynamicModelIds: newModelIds });
} else {
// Keep existing enabled state, just update the models list
set({ dynamicOpencodeModels: filteredModels });
}
}, },
setEnabledDynamicModelIds: (ids) => set({ enabledDynamicModelIds: ids }), setEnabledDynamicModelIds: (ids) => set({ enabledDynamicModelIds: ids }),
toggleDynamicModel: (modelId, enabled) => toggleDynamicModel: (modelId, enabled) =>

View File

@@ -401,6 +401,8 @@ export interface GlobalSettings {
enabledOpencodeModels?: OpencodeModelId[]; enabledOpencodeModels?: OpencodeModelId[];
/** Default OpenCode model selection when switching to OpenCode CLI */ /** Default OpenCode model selection when switching to OpenCode CLI */
opencodeDefaultModel?: OpencodeModelId; opencodeDefaultModel?: OpencodeModelId;
/** Which dynamic OpenCode models are enabled (empty = all discovered) */
enabledDynamicModelIds?: string[];
// Input Configuration // Input Configuration
/** User's keyboard shortcut bindings */ /** User's keyboard shortcut bindings */
@@ -704,6 +706,7 @@ export const DEFAULT_GLOBAL_SETTINGS: GlobalSettings = {
cursorDefaultModel: 'auto', cursorDefaultModel: 'auto',
enabledOpencodeModels: getAllOpencodeModelIds(), enabledOpencodeModels: getAllOpencodeModelIds(),
opencodeDefaultModel: DEFAULT_OPENCODE_MODEL, opencodeDefaultModel: DEFAULT_OPENCODE_MODEL,
enabledDynamicModelIds: [],
keyboardShortcuts: DEFAULT_KEYBOARD_SHORTCUTS, keyboardShortcuts: DEFAULT_KEYBOARD_SHORTCUTS,
projects: [], projects: [],
trashedProjects: [], trashedProjects: [],