diff --git a/packages/app/src/context/prompt-state.ts b/packages/app/src/context/prompt-state.ts index 65f47257810d..33bfc9c1982f 100644 --- a/packages/app/src/context/prompt-state.ts +++ b/packages/app/src/context/prompt-state.ts @@ -49,6 +49,13 @@ export type PromptModel = { providerID: string modelID: string variant?: string | null + /** + * Agent the selection was made under. A manual model choice is an override + * scoped to that agent; it must not outlive switching to a different agent. + * Undefined means the value predates this field or was inherited from another + * tab, in which case a configured agent model takes precedence over it. + */ + agent?: string } export type FileContextItem = { diff --git a/packages/app/src/pages/session/composer/prompt-model-selection.ts b/packages/app/src/pages/session/composer/prompt-model-selection.ts index 3f5bb0f477e8..1f3ff62d7c60 100644 --- a/packages/app/src/pages/session/composer/prompt-model-selection.ts +++ b/packages/app/src/pages/session/composer/prompt-model-selection.ts @@ -7,7 +7,9 @@ import { useSDK } from "@/context/sdk" import { useSync } from "@/context/sync" import { useProviders } from "@/hooks/use-providers" -export function createPromptModelSelection(input: { agent: () => { model?: ModelKey; variant?: string } | undefined }) { +export function createPromptModelSelection(input: { + agent: () => { name?: string; model?: ModelKey; variant?: string } | undefined +}) { const sdk = useSDK() const sync = useSync() const models = useModels() @@ -37,8 +39,21 @@ export function createPromptModelSelection(input: { agent: () => { model?: Model })[0] } + const agentName = () => input.agent()?.name + + // A manual model pick is an override scoped to the agent it was made under. + // Unscoped values either predate this field or were inherited from another tab + // (see openNewTab in components/titlebar.tsx); those must not shadow an agent + // that has a model configured. + const override = () => { + const value = prompt.model.current() + if (!value) return + if (value.agent === undefined) return input.agent()?.model ? undefined : value + return value.agent === agentName() ? value : undefined + } + const current = () => { - const key = [prompt.model.current(), input.agent()?.model, configured(), recent(), fallback()].find( + const key = [override(), input.agent()?.model, configured(), recent(), fallback()].find( (item): item is ModelKey => !!item && valid(item), ) if (!key) return @@ -68,7 +83,7 @@ export function createPromptModelSelection(input: { agent: () => { model?: Model set(item: ModelKey | undefined, options?: { recent?: boolean }) { startTransition(() => batch(() => { - prompt.model.set(item ? { ...item, variant: prompt.model.current()?.variant } : undefined) + prompt.model.set(item ? { ...item, variant: override()?.variant, agent: agentName() } : undefined) if (!item) return models.setVisibility(item, true) if (options?.recent) models.recent.push(item) @@ -88,7 +103,7 @@ export function createPromptModelSelection(input: { agent: () => { model?: Model }) }, selected() { - return prompt.model.current()?.variant + return override()?.variant }, current() { const resolved = resolveModelVariant({ @@ -110,7 +125,12 @@ export function createPromptModelSelection(input: { agent: () => { model?: Model batch(() => { const model = current() if (!model) return - prompt.model.set({ providerID: model.provider.id, modelID: model.id, variant: value ?? null }) + prompt.model.set({ + providerID: model.provider.id, + modelID: model.id, + variant: value ?? null, + agent: agentName(), + }) models.variant.set({ providerID: model.provider.id, modelID: model.id }, value) }), )