fix: start draft sessions from default model/agent and honor OpenCode default_agent
A new draft session inherited the previous session's model/agent instead of resetting to defaults, because opening a draft restored the directory snapshot without re-applying the startup default cascade. When the prior session ran in a worktree, defaults were resolved against the worktree directory's provider list, which omits project/global-scoped providers, so the default agent's model fell back to opencode/big-pickle. Resolve the default agent/model via a shared cascade (settings default -> OpenCode default_agent -> build -> first), resolve the model from the agent's pinned model/variant or OpenCode's config model, and activate the project's config (not the worktree's) when opening a draft.
This commit is contained in:
@@ -26,6 +26,9 @@ All notable changes to this project will be documented in this file.
|
|||||||
- Notifications: notification streams now stay connected more reliably behind proxies (thanks to @kostazol).
|
- Notifications: notification streams now stay connected more reliably behind proxies (thanks to @kostazol).
|
||||||
- Mobile: the empty Changes view keeps a close control visible (thanks to @lilyzhaun).
|
- Mobile: the empty Changes view keeps a close control visible (thanks to @lilyzhaun).
|
||||||
- Security: file previews and downloads now reject paths outside the allowed workspace unless access has been granted.
|
- Security: file previews and downloads now reject paths outside the allowed workspace unless access has been granted.
|
||||||
|
- Sessions: fixed a bug where a running session would briefly flicker as idle (in the sidebar, the send/stop button, and the status row) when the app is protected by a password.
|
||||||
|
- Desktop: you can now open developer tools from the Help menu.
|
||||||
|
- Sessions: new draft sessions now start from the default model and agent instead of inheriting the previous session's selection, and fall back to OpenCode's own `default_agent` (and its model) when no OpenChamber default is set.
|
||||||
|
|
||||||
## [1.12.4] - 2026-06-11
|
## [1.12.4] - 2026-06-11
|
||||||
|
|
||||||
|
|||||||
@@ -265,6 +265,128 @@ const resolveProviderModelSelection = ({
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type DefaultAgentModelSelection = {
|
||||||
|
agentName: string | undefined;
|
||||||
|
providerId?: string;
|
||||||
|
modelId?: string;
|
||||||
|
variant?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Shared default-selection cascade used both at startup (loadAgents) and when opening a
|
||||||
|
// fresh draft (applyDefaultModelAgentSelection), so the two paths stay identical.
|
||||||
|
//
|
||||||
|
// Agent: settings.defaultAgent → opencode default_agent → build → first primary → first
|
||||||
|
// Model: settings.defaultModel → resolved agent's pinned model+variant → opencode config.model
|
||||||
|
// → opencode/big-pickle → first
|
||||||
|
//
|
||||||
|
// The opencode default_agent / default model (config fields on the OpenCode server) are honored
|
||||||
|
// only when our own settings have no valid default. OpenCode itself resolves a model the same way:
|
||||||
|
// an agent's pinned model wins, otherwise the global `model` config applies — so we check the
|
||||||
|
// agent's model before opencodeDefaultModel. When the agent supplies the model, its `variant` is
|
||||||
|
// carried through too (if the model actually exposes that variant).
|
||||||
|
const resolveDefaultAgentModelSelection = ({
|
||||||
|
agents,
|
||||||
|
providers,
|
||||||
|
settingsDefaultAgent,
|
||||||
|
settingsDefaultModel,
|
||||||
|
settingsDefaultVariant,
|
||||||
|
opencodeDefaultAgent,
|
||||||
|
opencodeDefaultModel,
|
||||||
|
}: {
|
||||||
|
agents: Agent[];
|
||||||
|
providers: ProviderWithModelList[];
|
||||||
|
settingsDefaultAgent?: string;
|
||||||
|
settingsDefaultModel?: string;
|
||||||
|
settingsDefaultVariant?: string;
|
||||||
|
opencodeDefaultAgent?: string;
|
||||||
|
opencodeDefaultModel?: string;
|
||||||
|
}): DefaultAgentModelSelection => {
|
||||||
|
if (agents.length === 0) {
|
||||||
|
return { agentName: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolveVariant = (providerId: string, modelId: string, variant?: string): string | undefined => {
|
||||||
|
if (!variant) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const model = providers
|
||||||
|
.find((provider) => provider.id === providerId)
|
||||||
|
?.models.find((entry) => entry.id === modelId) as { variants?: Record<string, unknown> } | undefined;
|
||||||
|
return model?.variants && Object.prototype.hasOwnProperty.call(model.variants, variant)
|
||||||
|
? variant
|
||||||
|
: undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- Agent cascade ---
|
||||||
|
const primaryAgents = agents.filter((agent) => isPrimaryMode(agent.mode));
|
||||||
|
|
||||||
|
let resolvedAgent: Agent | undefined;
|
||||||
|
if (settingsDefaultAgent) {
|
||||||
|
resolvedAgent = agents.find((agent) => agent.name === settingsDefaultAgent);
|
||||||
|
}
|
||||||
|
if (!resolvedAgent && opencodeDefaultAgent) {
|
||||||
|
const candidate = agents.find((agent) => agent.name === opencodeDefaultAgent);
|
||||||
|
// OpenCode requires the default agent to be a visible primary agent.
|
||||||
|
if (candidate && isPrimaryMode(candidate.mode) && candidate.hidden !== true) {
|
||||||
|
resolvedAgent = candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!resolvedAgent) {
|
||||||
|
resolvedAgent = primaryAgents.find((agent) => agent.name === "build") || primaryAgents[0] || agents[0];
|
||||||
|
}
|
||||||
|
if (!resolvedAgent) {
|
||||||
|
return { agentName: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Model cascade ---
|
||||||
|
let providerId: string | undefined;
|
||||||
|
let modelId: string | undefined;
|
||||||
|
let variant: string | undefined;
|
||||||
|
|
||||||
|
if (settingsDefaultModel) {
|
||||||
|
const parsed = parseModelString(settingsDefaultModel);
|
||||||
|
if (parsed && hasProviderModel(providers, parsed.providerId, parsed.modelId)) {
|
||||||
|
providerId = parsed.providerId;
|
||||||
|
modelId = parsed.modelId;
|
||||||
|
variant = resolveVariant(providerId, modelId, settingsDefaultVariant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!providerId
|
||||||
|
&& resolvedAgent.model?.providerID
|
||||||
|
&& resolvedAgent.model?.modelID
|
||||||
|
&& hasProviderModel(providers, resolvedAgent.model.providerID, resolvedAgent.model.modelID)) {
|
||||||
|
providerId = resolvedAgent.model.providerID;
|
||||||
|
modelId = resolvedAgent.model.modelID;
|
||||||
|
variant = resolveVariant(providerId, modelId, resolvedAgent.variant);
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenCode's global default model — used when neither our settings nor the agent pin a model.
|
||||||
|
if (!providerId && opencodeDefaultModel) {
|
||||||
|
const parsed = parseModelString(opencodeDefaultModel);
|
||||||
|
if (parsed && hasProviderModel(providers, parsed.providerId, parsed.modelId)) {
|
||||||
|
providerId = parsed.providerId;
|
||||||
|
modelId = parsed.modelId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!providerId) {
|
||||||
|
if (hasProviderModel(providers, FALLBACK_PROVIDER_ID, FALLBACK_MODEL_ID)) {
|
||||||
|
providerId = FALLBACK_PROVIDER_ID;
|
||||||
|
modelId = FALLBACK_MODEL_ID;
|
||||||
|
} else {
|
||||||
|
const firstProvider = providers[0];
|
||||||
|
const firstModel = firstProvider?.models[0];
|
||||||
|
if (firstProvider && firstModel) {
|
||||||
|
providerId = firstProvider.id;
|
||||||
|
modelId = firstModel.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { agentName: resolvedAgent.name, providerId, modelId, variant };
|
||||||
|
};
|
||||||
|
|
||||||
const resolveGitGenerationModelSelection = ({
|
const resolveGitGenerationModelSelection = ({
|
||||||
providers,
|
providers,
|
||||||
settingsZenModel,
|
settingsZenModel,
|
||||||
@@ -652,6 +774,12 @@ interface ConfigStore {
|
|||||||
settingsDefaultModel: string | undefined; // format: "provider/model"
|
settingsDefaultModel: string | undefined; // format: "provider/model"
|
||||||
settingsDefaultVariant: string | undefined;
|
settingsDefaultVariant: string | undefined;
|
||||||
settingsDefaultAgent: string | undefined;
|
settingsDefaultAgent: string | undefined;
|
||||||
|
// OpenCode server's own `default_agent` config field (name of a primary agent), used as a
|
||||||
|
// fallback when our own settingsDefaultAgent is unset. Sourced from opencodeClient.getConfig().
|
||||||
|
opencodeDefaultAgent: string | undefined;
|
||||||
|
// OpenCode server's own global `model` config field ("provider/model"), used as a fallback
|
||||||
|
// when neither our settingsDefaultModel nor the resolved agent pins a model.
|
||||||
|
opencodeDefaultModel: string | undefined;
|
||||||
settingsAutoCreateWorktree: boolean;
|
settingsAutoCreateWorktree: boolean;
|
||||||
settingsGitmojiEnabled: boolean;
|
settingsGitmojiEnabled: boolean;
|
||||||
settingsDefaultFileViewerPreview: boolean;
|
settingsDefaultFileViewerPreview: boolean;
|
||||||
@@ -730,6 +858,7 @@ interface ConfigStore {
|
|||||||
cycleCurrentVariant: () => void;
|
cycleCurrentVariant: () => void;
|
||||||
getCurrentModelVariants: () => string[];
|
getCurrentModelVariants: () => string[];
|
||||||
setAgent: (agentName: string | undefined) => void;
|
setAgent: (agentName: string | undefined) => void;
|
||||||
|
applyDefaultModelAgentSelection: () => void;
|
||||||
setSelectedProvider: (providerId: string) => void;
|
setSelectedProvider: (providerId: string) => void;
|
||||||
setSettingsDefaultModel: (model: string | undefined) => void;
|
setSettingsDefaultModel: (model: string | undefined) => void;
|
||||||
setSettingsDefaultVariant: (variant: string | undefined) => void;
|
setSettingsDefaultVariant: (variant: string | undefined) => void;
|
||||||
@@ -790,6 +919,8 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
settingsDefaultModel: undefined,
|
settingsDefaultModel: undefined,
|
||||||
settingsDefaultVariant: undefined,
|
settingsDefaultVariant: undefined,
|
||||||
settingsDefaultAgent: undefined,
|
settingsDefaultAgent: undefined,
|
||||||
|
opencodeDefaultAgent: undefined,
|
||||||
|
opencodeDefaultModel: undefined,
|
||||||
settingsAutoCreateWorktree: false,
|
settingsAutoCreateWorktree: false,
|
||||||
settingsGitmojiEnabled: false,
|
settingsGitmojiEnabled: false,
|
||||||
settingsDefaultFileViewerPreview: false,
|
settingsDefaultFileViewerPreview: false,
|
||||||
@@ -1573,17 +1704,24 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
|
|
||||||
for (let attempt = 0; attempt < 3; attempt++) {
|
for (let attempt = 0; attempt < 3; attempt++) {
|
||||||
try {
|
try {
|
||||||
// Fetch agents and OpenChamber settings in parallel
|
// Fetch agents, OpenChamber settings, and the OpenCode config in parallel.
|
||||||
const [agents, openChamberDefaults] = await Promise.all([
|
// The OpenCode config is best-effort: a failure should not block agent
|
||||||
|
// loading, it just means we won't honor its default_agent this round.
|
||||||
|
const [agents, openChamberDefaults, opencodeConfig] = await Promise.all([
|
||||||
measureStartupTrace(
|
measureStartupTrace(
|
||||||
'loadAgents:api',
|
'loadAgents:api',
|
||||||
() => opencodeClient.withDirectory(fromDirectoryKey(directoryKey), () => opencodeClient.listAgents()),
|
() => opencodeClient.withDirectory(fromDirectoryKey(directoryKey), () => opencodeClient.listAgents()),
|
||||||
{ directoryKey, source, requestedDirectory, effectiveDirectory, attempt: attempt + 1 },
|
{ directoryKey, source, requestedDirectory, effectiveDirectory, attempt: attempt + 1 },
|
||||||
),
|
),
|
||||||
fetchOpenChamberDefaults(),
|
fetchOpenChamberDefaults(),
|
||||||
|
opencodeClient
|
||||||
|
.withDirectory(fromDirectoryKey(directoryKey), () => opencodeClient.getConfig())
|
||||||
|
.catch(() => null),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const safeAgents = Array.isArray(agents) ? agents : [];
|
const safeAgents = Array.isArray(agents) ? agents : [];
|
||||||
|
const opencodeDefaultAgent = normalizeOptionalString(opencodeConfig?.default_agent);
|
||||||
|
const opencodeDefaultModel = normalizeOptionalString(opencodeConfig?.model);
|
||||||
|
|
||||||
const providerLoad = _inFlightProviders.get(directoryKey);
|
const providerLoad = _inFlightProviders.get(directoryKey);
|
||||||
if (providerLoad) {
|
if (providerLoad) {
|
||||||
@@ -1635,6 +1773,8 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
settingsDefaultModel: openChamberDefaults.defaultModel,
|
settingsDefaultModel: openChamberDefaults.defaultModel,
|
||||||
settingsDefaultVariant: openChamberDefaults.defaultVariant,
|
settingsDefaultVariant: openChamberDefaults.defaultVariant,
|
||||||
settingsDefaultAgent: openChamberDefaults.defaultAgent,
|
settingsDefaultAgent: openChamberDefaults.defaultAgent,
|
||||||
|
opencodeDefaultAgent,
|
||||||
|
opencodeDefaultModel,
|
||||||
settingsAutoCreateWorktree: openChamberDefaults.autoCreateWorktree ?? false,
|
settingsAutoCreateWorktree: openChamberDefaults.autoCreateWorktree ?? false,
|
||||||
settingsGitmojiEnabled: openChamberDefaults.gitmojiEnabled ?? false,
|
settingsGitmojiEnabled: openChamberDefaults.gitmojiEnabled ?? false,
|
||||||
settingsDefaultFileViewerPreview: openChamberDefaults.defaultFileViewerPreview ?? false,
|
settingsDefaultFileViewerPreview: openChamberDefaults.defaultFileViewerPreview ?? false,
|
||||||
@@ -1727,82 +1867,45 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
return provider.models.some((m) => m.id === modelId);
|
return provider.models.some((m) => m.id === modelId);
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Agent Selection ---
|
// Detect invalid OpenChamber settings so we can clear them from storage.
|
||||||
// Priority: settings.defaultAgent → build → first primary → first agent
|
// This is independent of resolution: even though the cascade below falls
|
||||||
const primaryAgents = safeAgents.filter((agent) => isPrimaryMode(agent.mode));
|
// back gracefully, stale settings pointing at removed agents/models/variants
|
||||||
const buildAgent = primaryAgents.find((agent) => agent.name === "build");
|
// should be cleaned up.
|
||||||
const fallbackAgent = buildAgent || primaryAgents[0] || safeAgents[0];
|
const invalidSettings: { defaultModel?: string; defaultVariant?: string; defaultAgent?: string } = {};
|
||||||
|
if (openChamberDefaults.defaultAgent && !safeAgents.some((agent) => agent.name === openChamberDefaults.defaultAgent)) {
|
||||||
let resolvedAgent: Agent = fallbackAgent;
|
invalidSettings.defaultAgent = '';
|
||||||
|
|
||||||
// Track invalid settings to clear
|
|
||||||
const invalidSettings: { defaultModel?: string; defaultVariant?: string; defaultAgent?: string } = {};
|
|
||||||
|
|
||||||
// 1. Check OpenChamber settings for default agent
|
|
||||||
if (openChamberDefaults.defaultAgent) {
|
|
||||||
const settingsAgent = safeAgents.find((agent) => agent.name === openChamberDefaults.defaultAgent);
|
|
||||||
if (settingsAgent) {
|
|
||||||
resolvedAgent = settingsAgent;
|
|
||||||
} else {
|
|
||||||
// Agent no longer exists - mark for clearing
|
|
||||||
invalidSettings.defaultAgent = '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (openChamberDefaults.defaultModel) {
|
||||||
// --- Model Selection ---
|
const parsed = parseModelString(openChamberDefaults.defaultModel);
|
||||||
// Priority: settings.defaultModel → agent's preferred model → opencode/big-pickle
|
if (!parsed || !validateModel(parsed.providerId, parsed.modelId)) {
|
||||||
let resolvedProviderId: string | undefined;
|
invalidSettings.defaultModel = '';
|
||||||
let resolvedModelId: string | undefined;
|
} else if (openChamberDefaults.defaultVariant) {
|
||||||
let resolvedVariant: string | undefined;
|
const provider = providers.find((p) => p.id === parsed.providerId);
|
||||||
|
const model = provider?.models.find((m) => m.id === parsed.modelId) as { variants?: Record<string, unknown> } | undefined;
|
||||||
// 1. Check OpenChamber settings for default model
|
const variants = model?.variants;
|
||||||
if (openChamberDefaults.defaultModel) {
|
if (!(variants && Object.prototype.hasOwnProperty.call(variants, openChamberDefaults.defaultVariant))) {
|
||||||
const parsed = parseModelString(openChamberDefaults.defaultModel);
|
invalidSettings.defaultVariant = '';
|
||||||
if (parsed && validateModel(parsed.providerId, parsed.modelId)) {
|
|
||||||
resolvedProviderId = parsed.providerId;
|
|
||||||
resolvedModelId = parsed.modelId;
|
|
||||||
|
|
||||||
if (openChamberDefaults.defaultVariant) {
|
|
||||||
const provider = providers.find((p) => p.id === parsed.providerId);
|
|
||||||
const model = provider?.models.find((m) => m.id === parsed.modelId) as { variants?: Record<string, unknown> } | undefined;
|
|
||||||
const variants = model?.variants;
|
|
||||||
if (variants && Object.prototype.hasOwnProperty.call(variants, openChamberDefaults.defaultVariant)) {
|
|
||||||
resolvedVariant = openChamberDefaults.defaultVariant;
|
|
||||||
} else {
|
|
||||||
invalidSettings.defaultVariant = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Model no longer exists - mark for clearing
|
|
||||||
invalidSettings.defaultModel = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Fall back to agent's preferred model
|
|
||||||
if (!resolvedProviderId && resolvedAgent?.model?.providerID && resolvedAgent?.model?.modelID) {
|
|
||||||
const { providerID, modelID } = resolvedAgent.model;
|
|
||||||
if (validateModel(providerID, modelID)) {
|
|
||||||
resolvedProviderId = providerID;
|
|
||||||
resolvedModelId = modelID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Fall back to opencode/big-pickle
|
|
||||||
if (!resolvedProviderId) {
|
|
||||||
if (validateModel(FALLBACK_PROVIDER_ID, FALLBACK_MODEL_ID)) {
|
|
||||||
resolvedProviderId = FALLBACK_PROVIDER_ID;
|
|
||||||
resolvedModelId = FALLBACK_MODEL_ID;
|
|
||||||
} else {
|
|
||||||
// Last resort: first provider's first model
|
|
||||||
const firstProvider = providers[0];
|
|
||||||
const firstModel = firstProvider?.models[0];
|
|
||||||
if (firstProvider && firstModel) {
|
|
||||||
resolvedProviderId = firstProvider.id;
|
|
||||||
resolvedModelId = firstModel.id;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve agent + model via the shared cascade:
|
||||||
|
// settings.defaultAgent → opencode default_agent → build → first primary → first
|
||||||
|
// settings.defaultModel → resolved agent's model+variant → opencode/big-pickle → first
|
||||||
|
const resolvedDefault = resolveDefaultAgentModelSelection({
|
||||||
|
agents: safeAgents,
|
||||||
|
providers,
|
||||||
|
settingsDefaultAgent: openChamberDefaults.defaultAgent,
|
||||||
|
settingsDefaultModel: openChamberDefaults.defaultModel,
|
||||||
|
settingsDefaultVariant: openChamberDefaults.defaultVariant,
|
||||||
|
opencodeDefaultAgent,
|
||||||
|
opencodeDefaultModel,
|
||||||
|
});
|
||||||
|
const resolvedAgentName = resolvedDefault.agentName ?? safeAgents[0].name;
|
||||||
|
const resolvedProviderId = resolvedDefault.providerId;
|
||||||
|
const resolvedModelId = resolvedDefault.modelId;
|
||||||
|
const resolvedVariant = resolvedDefault.variant;
|
||||||
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const baseSnapshot: DirectoryScopedConfig = state.directoryScoped[directoryKey] ?? {
|
const baseSnapshot: DirectoryScopedConfig = state.directoryScoped[directoryKey] ?? {
|
||||||
providers,
|
providers,
|
||||||
@@ -1819,7 +1922,7 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
...baseSnapshot,
|
...baseSnapshot,
|
||||||
providers,
|
providers,
|
||||||
agents: safeAgents,
|
agents: safeAgents,
|
||||||
currentAgentName: resolvedAgent.name,
|
currentAgentName: resolvedAgentName,
|
||||||
currentProviderId: resolvedProviderId ?? baseSnapshot.currentProviderId,
|
currentProviderId: resolvedProviderId ?? baseSnapshot.currentProviderId,
|
||||||
currentModelId: resolvedModelId ?? baseSnapshot.currentModelId,
|
currentModelId: resolvedModelId ?? baseSnapshot.currentModelId,
|
||||||
currentVariant: resolvedVariant,
|
currentVariant: resolvedVariant,
|
||||||
@@ -1833,7 +1936,7 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (state.activeDirectoryKey === directoryKey) {
|
if (state.activeDirectoryKey === directoryKey) {
|
||||||
nextState.currentAgentName = resolvedAgent.name;
|
nextState.currentAgentName = resolvedAgentName;
|
||||||
if (resolvedProviderId && resolvedModelId) {
|
if (resolvedProviderId && resolvedModelId) {
|
||||||
nextState.currentProviderId = resolvedProviderId;
|
nextState.currentProviderId = resolvedProviderId;
|
||||||
nextState.currentModelId = resolvedModelId;
|
nextState.currentModelId = resolvedModelId;
|
||||||
@@ -2090,6 +2193,91 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Re-applies the same priority cascade used at app startup (see loadAgents):
|
||||||
|
// agent: settings.defaultAgent → build → first primary → first agent
|
||||||
|
// model: settings.defaultModel → agent's preferred model → opencode/big-pickle → first
|
||||||
|
// Used when entering a fresh draft session so model/agent reset to defaults
|
||||||
|
// instead of sticking to the previously open session's selection.
|
||||||
|
applyDefaultModelAgentSelection: () => {
|
||||||
|
const {
|
||||||
|
agents,
|
||||||
|
providers,
|
||||||
|
settingsDefaultModel,
|
||||||
|
settingsDefaultVariant,
|
||||||
|
settingsDefaultAgent,
|
||||||
|
opencodeDefaultAgent,
|
||||||
|
opencodeDefaultModel,
|
||||||
|
} = get();
|
||||||
|
|
||||||
|
if (agents.length === 0 || providers.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
agentName: resolvedAgentName,
|
||||||
|
providerId: resolvedProviderId,
|
||||||
|
modelId: resolvedModelId,
|
||||||
|
variant: resolvedVariant,
|
||||||
|
} = resolveDefaultAgentModelSelection({
|
||||||
|
agents,
|
||||||
|
providers,
|
||||||
|
settingsDefaultAgent,
|
||||||
|
settingsDefaultModel,
|
||||||
|
settingsDefaultVariant,
|
||||||
|
opencodeDefaultAgent,
|
||||||
|
opencodeDefaultModel,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!resolvedAgentName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
set((state) => {
|
||||||
|
const directoryKey = state.activeDirectoryKey;
|
||||||
|
const baseSnapshot: DirectoryScopedConfig = state.directoryScoped[directoryKey] ?? {
|
||||||
|
providers: state.providers,
|
||||||
|
agents: state.agents,
|
||||||
|
currentProviderId: state.currentProviderId,
|
||||||
|
currentModelId: state.currentModelId,
|
||||||
|
currentVariant: state.currentVariant,
|
||||||
|
currentAgentName: state.currentAgentName,
|
||||||
|
selectedProviderId: state.selectedProviderId,
|
||||||
|
agentModelSelections: state.agentModelSelections,
|
||||||
|
defaultProviders: state.defaultProviders,
|
||||||
|
};
|
||||||
|
|
||||||
|
const nextSnapshot: DirectoryScopedConfig = {
|
||||||
|
...baseSnapshot,
|
||||||
|
currentAgentName: resolvedAgentName,
|
||||||
|
...(resolvedProviderId && resolvedModelId
|
||||||
|
? {
|
||||||
|
currentProviderId: resolvedProviderId,
|
||||||
|
currentModelId: resolvedModelId,
|
||||||
|
currentVariant: resolvedVariant,
|
||||||
|
selectedProviderId: resolvedProviderId,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const nextState: Partial<ConfigStore> = {
|
||||||
|
currentAgentName: resolvedAgentName,
|
||||||
|
directoryScoped: {
|
||||||
|
...state.directoryScoped,
|
||||||
|
[directoryKey]: nextSnapshot,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (resolvedProviderId && resolvedModelId) {
|
||||||
|
nextState.currentProviderId = resolvedProviderId;
|
||||||
|
nextState.currentModelId = resolvedModelId;
|
||||||
|
nextState.currentVariant = resolvedVariant;
|
||||||
|
nextState.selectedProviderId = resolvedProviderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextState;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
setSettingsDefaultModel: (model: string | undefined) => {
|
setSettingsDefaultModel: (model: string | undefined) => {
|
||||||
set({ settingsDefaultModel: model });
|
set({ settingsDefaultModel: model });
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -617,7 +617,16 @@ export const useSessionUIStore = create<SessionUIState>()((set, get) => ({
|
|||||||
useInputStore.getState().setPendingInputText(options.initialPrompt)
|
useInputStore.getState().setPendingInputText(options.initialPrompt)
|
||||||
}
|
}
|
||||||
|
|
||||||
void activateConfigForDirectory(directory)
|
// Config (providers/agents/default model+agent) lives at the PROJECT level. When the user
|
||||||
|
// came from a worktree session, `directory` is the worktree path, whose provider list does
|
||||||
|
// not include project/global-scoped providers (e.g. the default agent's non-opencode model)
|
||||||
|
// — resolving defaults against it would wrongly fall back to opencode/big-pickle. Activate
|
||||||
|
// the project's config instead so the default cascade matches app startup, then re-apply it
|
||||||
|
// (a fresh draft must start from defaults, not inherit the previous session's selection).
|
||||||
|
const configDirectory = normalizePath(selectedProject?.path ?? null) ?? directory
|
||||||
|
void activateConfigForDirectory(configDirectory).then(() => {
|
||||||
|
useConfigStore.getState().applyDefaultModelAgentSelection()
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user