44 *--------------------------------------------------------------------------------------------*/
55
66import assert from 'assert' ;
7- import { Event } from '../../../../../base/common/event.js' ;
7+ import { Emitter , Event } from '../../../../../base/common/event.js' ;
88import { DisposableStore } from '../../../../../base/common/lifecycle.js' ;
99import { observableValue } from '../../../../../base/common/observable.js' ;
1010import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js' ;
@@ -33,7 +33,7 @@ function stubServices(
3333 storedEntries ?: Map < string , string > ;
3434 setModelSpy ?: ( sessionId : string , modelId : string ) => void ;
3535 } ,
36- ) : { instantiationService : TestInstantiationService ; storage : Map < string , string > ; activeSession : ReturnType < typeof observableValue < IActiveSession | undefined > > } {
36+ ) : { instantiationService : TestInstantiationService ; storage : Map < string , string > ; activeSession : ReturnType < typeof observableValue < IActiveSession | undefined > > ; fireLanguageModelsChanged : ( ) => void } {
3737 const instantiationService = disposables . add ( new TestInstantiationService ( ) ) ;
3838 const models = opts ?. models ?? [ ] ;
3939 const storage = opts ?. storedEntries ?? new Map < string , string > ( ) ;
@@ -44,8 +44,10 @@ function stubServices(
4444
4545 const setModelSpy = opts ?. setModelSpy ?? ( ( ) => { } ) ;
4646
47+ const onDidChangeLanguageModelsEmitter = disposables . add ( new Emitter < { added ?: readonly { identifier : string } [ ] ; removed ?: readonly string [ ] } > ( ) ) ;
48+
4749 instantiationService . stub ( ILanguageModelsService , {
48- onDidChangeLanguageModels : Event . None ,
50+ onDidChangeLanguageModels : onDidChangeLanguageModelsEmitter . event ,
4951 getLanguageModelIds : ( ) => models . map ( m => m . identifier ) ,
5052 lookupLanguageModel : ( id : string ) => models . find ( m => m . identifier === id ) ?. metadata ,
5153 } as Partial < ILanguageModelsService > ) ;
@@ -72,7 +74,7 @@ function stubServices(
7274 // Stub IInstantiationService so SessionModelPicker can call createInstance for ModelPickerActionItem
7375 instantiationService . stub ( IInstantiationService , instantiationService ) ;
7476
75- return { instantiationService, storage, activeSession } ;
77+ return { instantiationService, storage, activeSession, fireLanguageModelsChanged : ( ) => onDidChangeLanguageModelsEmitter . fire ( { } ) } ;
7678}
7779
7880suite ( 'modelPickerStorageKey' , ( ) => {
@@ -185,4 +187,45 @@ suite('SessionModelPicker', () => {
185187 // CLI key should still be intact
186188 assert . strictEqual ( storage . get ( modelPickerStorageKey ( COPILOT_CLI_SESSION_TYPE ) ) , 'cli-m' ) ;
187189 } ) ;
190+
191+ test ( 'propagates selected model to a new session of the same type (#313385)' , ( ) => {
192+ const models = [ makeModel ( 'cli-a' , COPILOT_CLI_SESSION_TYPE ) , makeModel ( 'cli-b' , COPILOT_CLI_SESSION_TYPE ) ] ;
193+ const storedEntries = new Map ( [ [ modelPickerStorageKey ( COPILOT_CLI_SESSION_TYPE ) , 'cli-b' ] ] ) ;
194+ const calls : { sessionId : string ; modelId : string } [ ] = [ ] ;
195+ const { instantiationService, activeSession } = stubServices ( disposables , {
196+ models,
197+ activeSession : { providerId : 'default-copilot' , sessionId : 's1' , sessionType : COPILOT_CLI_SESSION_TYPE } ,
198+ storedEntries,
199+ setModelSpy : ( sessionId , modelId ) => calls . push ( { sessionId, modelId } ) ,
200+ } ) ;
201+ disposables . add ( instantiationService . createInstance ( SessionModelPicker ) ) ;
202+ // Initial session receives the remembered model.
203+ assert . ok ( calls . some ( c => c . sessionId === 's1' && c . modelId === 'cli-b' ) ) ;
204+
205+ // Switch to a new session of the same type (e.g. user picked a different repo).
206+ activeSession . set ( { providerId : 'default-copilot' , sessionId : 's2' , sessionType : COPILOT_CLI_SESSION_TYPE } as IActiveSession , undefined ) ;
207+
208+ // The new session must receive the same model so the request isn't sent with the default.
209+ assert . ok ( calls . some ( c => c . sessionId === 's2' && c . modelId === 'cli-b' ) ) ;
210+ } ) ;
211+
212+ test ( 'does not re-push model to the same session when language models change' , ( ) => {
213+ const models = [ makeModel ( 'cli-a' , COPILOT_CLI_SESSION_TYPE ) ] ;
214+ const calls : { sessionId : string ; modelId : string } [ ] = [ ] ;
215+ const { instantiationService, fireLanguageModelsChanged } = stubServices ( disposables , {
216+ models,
217+ activeSession : { providerId : 'default-copilot' , sessionId : 's1' , sessionType : COPILOT_CLI_SESSION_TYPE } ,
218+ setModelSpy : ( sessionId , modelId ) => calls . push ( { sessionId, modelId } ) ,
219+ } ) ;
220+ disposables . add ( instantiationService . createInstance ( SessionModelPicker ) ) ;
221+ const initialCallCount = calls . filter ( c => c . sessionId === 's1' ) . length ;
222+ assert . ok ( initialCallCount > 0 , 'expected initial setModel to fire' ) ;
223+
224+ // Re-fire language-models-changed multiple times. The active session and
225+ // selected model haven't changed, so the provider must not be re-notified.
226+ fireLanguageModelsChanged ( ) ;
227+ fireLanguageModelsChanged ( ) ;
228+
229+ assert . strictEqual ( calls . filter ( c => c . sessionId === 's1' ) . length , initialCallCount ) ;
230+ } ) ;
188231} ) ;
0 commit comments