@@ -25,12 +25,18 @@ function createSessions(): SessionsListResult {
2525function createChatHeaderState (
2626 overrides : {
2727 model ?: string | null ;
28+ modelProvider ?: string | null ;
29+ defaultModel ?: string | null ;
30+ defaultModelProvider ?: string | null ;
2831 models ?: ModelCatalogEntry [ ] ;
2932 omitSessionFromList ?: boolean ;
3033 } = { } ,
3134) : { state : AppViewState ; request : ReturnType < typeof vi . fn > } {
35+ const defaultModel = overrides . defaultModel ?? "gpt-5" ;
36+ const defaultModelProvider = defaultModel ? ( overrides . defaultModelProvider ?? "openai" ) : null ;
3237 let currentModel = overrides . model ?? null ;
33- let currentModelProvider = currentModel ? "openai" : null ;
38+ let currentModelProvider =
39+ overrides . modelProvider ?? ( currentModel ? defaultModelProvider : null ) ;
3440 const omitSessionFromList = overrides . omitSessionFromList ?? false ;
3541 const catalog = overrides . models ?? [
3642 { id : "gpt-5" , name : "GPT-5" , provider : "openai" } ,
@@ -68,7 +74,7 @@ function createChatHeaderState(
6874 ts : 0 ,
6975 path : "" ,
7076 count : omitSessionFromList ? 0 : 1 ,
71- defaults : { modelProvider : "openai" , model : "gpt-5" , contextTokens : null } ,
77+ defaults : { modelProvider : defaultModelProvider , model : defaultModel , contextTokens : null } ,
7278 sessions : omitSessionFromList
7379 ? [ ]
7480 : [
@@ -95,7 +101,7 @@ function createChatHeaderState(
95101 ts : 0 ,
96102 path : "" ,
97103 count : omitSessionFromList ? 0 : 1 ,
98- defaults : { modelProvider : "openai" , model : "gpt-5" , contextTokens : null } ,
104+ defaults : { modelProvider : defaultModelProvider , model : defaultModel , contextTokens : null } ,
99105 sessions : omitSessionFromList
100106 ? [ ]
101107 : [
@@ -767,6 +773,55 @@ describe("chat view", () => {
767773 vi . unstubAllGlobals ( ) ;
768774 } ) ;
769775
776+ it ( "keeps provider-qualified model refs when switching across providers from the chat header picker" , async ( ) => {
777+ vi . stubGlobal (
778+ "fetch" ,
779+ vi . fn ( ) . mockResolvedValue ( {
780+ ok : false ,
781+ } satisfies Partial < Response > ) ,
782+ ) ;
783+ const { state, request } = createChatHeaderState ( {
784+ defaultModel : "MiniMax-M2.7" ,
785+ defaultModelProvider : "minimax-cn" ,
786+ models : [
787+ { id : "MiniMax-M2.7" , name : "MiniMax M2.7" , provider : "minimax-cn" } ,
788+ {
789+ id : "qwen3.5-flash" ,
790+ name : "Qwen 3.5 Flash" ,
791+ provider : "custom-dashscope-aliyuncs-com" ,
792+ } ,
793+ { id : "gemini-3-flash-preview" , name : "Gemini 3 Flash Preview" , provider : "google" } ,
794+ ] ,
795+ } ) ;
796+ const container = document . createElement ( "div" ) ;
797+ render ( renderChatSessionSelect ( state ) , container ) ;
798+
799+ const modelSelect = container . querySelector < HTMLSelectElement > (
800+ 'select[data-chat-model-select="true"]' ,
801+ ) ;
802+ expect ( modelSelect ) . not . toBeNull ( ) ;
803+
804+ const optionValues = Array . from ( modelSelect ?. querySelectorAll ( "option" ) ?? [ ] ) . map (
805+ ( option ) => option . value ,
806+ ) ;
807+ expect ( optionValues ) . toContain ( "custom-dashscope-aliyuncs-com/qwen3.5-flash" ) ;
808+ expect ( optionValues ) . toContain ( "google/gemini-3-flash-preview" ) ;
809+ expect ( optionValues ) . not . toContain ( "qwen3.5-flash" ) ;
810+ expect ( optionValues ) . not . toContain ( "gemini-3-flash-preview" ) ;
811+
812+ modelSelect ! . value = "custom-dashscope-aliyuncs-com/qwen3.5-flash" ;
813+ modelSelect ! . dispatchEvent ( new Event ( "change" , { bubbles : true } ) ) ;
814+ await flushTasks ( ) ;
815+
816+ expect ( request ) . toHaveBeenCalledWith ( "sessions.patch" , {
817+ key : "main" ,
818+ model : "custom-dashscope-aliyuncs-com/qwen3.5-flash" ,
819+ } ) ;
820+ expect ( state . sessionsResult ?. sessions [ 0 ] ?. model ) . toBe ( "qwen3.5-flash" ) ;
821+ expect ( state . sessionsResult ?. sessions [ 0 ] ?. modelProvider ) . toBe ( "custom-dashscope-aliyuncs-com" ) ;
822+ vi . unstubAllGlobals ( ) ;
823+ } ) ;
824+
770825 it ( "clears the session model override back to the default model" , async ( ) => {
771826 vi . stubGlobal (
772827 "fetch" ,
0 commit comments