@@ -3276,6 +3276,89 @@ describe("ClineProvider", () => {
32763276 await provider . dispose ( )
32773277 } )
32783278
3279+ it ( "does not re-point a pinned sibling's task that follows another profile when its profile is deleted" , async ( ) => {
3280+ // Defensive: the doubles in this suite can leave console writes pending
3281+ // at worker teardown.
3282+ vi . spyOn ( console , "log" ) . mockImplementation ( ( ) => { } )
3283+ vi . spyOn ( console , "warn" ) . mockImplementation ( ( ) => { } )
3284+ vi . spyOn ( console , "error" ) . mockImplementation ( ( ) => { } )
3285+ const provider = new ClineProvider (
3286+ mockContext ,
3287+ mockOutputChannel ,
3288+ "sidebar" ,
3289+ new ContextProxy ( mockContext ) ,
3290+ new WebviewFocusTracker ( ) ,
3291+ )
3292+ const sibling = new ClineProvider (
3293+ mockContext ,
3294+ mockOutputChannel ,
3295+ "editor" ,
3296+ new ContextProxy ( mockContext ) ,
3297+ new WebviewFocusTracker ( ) ,
3298+ )
3299+ try {
3300+ const oldProfile : ProviderSettingsEntry = {
3301+ name : "old-profile" ,
3302+ id : "old-id" ,
3303+ apiProvider : providerIdentifiers . openrouter ,
3304+ }
3305+ const keeperProfile : ProviderSettingsEntry = {
3306+ name : "keeper-profile" ,
3307+ id : "keeper-id" ,
3308+ apiProvider : providerIdentifiers . anthropic ,
3309+ }
3310+ await provider . contextProxy . setValue ( "listApiConfigMeta" , [ oldProfile , keeperProfile ] )
3311+ await provider . contextProxy . setValue ( "currentApiConfigName" , "keeper-profile" )
3312+ // The sibling pins the profile being deleted, but its active task
3313+ // follows a different sticky profile: the deletion must re-point the
3314+ // sibling's buffer to the surviving profile without switching that
3315+ // conversation's handler or rewriting its persisted profile name.
3316+ sibling [ "viewLocalState" ] . currentApiConfigName = "old-profile"
3317+ sibling [ "viewLocalState" ] . apiConfiguration = {
3318+ apiProvider : providerIdentifiers . openrouter ,
3319+ openRouterApiKey : "deleted-profile-secret" ,
3320+ }
3321+ vi . spyOn ( sibling , "postStateToWebview" ) . mockResolvedValue ( undefined )
3322+ const siblingTask = {
3323+ apiConfiguration : { apiProvider : providerIdentifiers . anthropic , apiKey : "other-key" } ,
3324+ taskApiConfigName : "other-profile" ,
3325+ taskId : "sibling-task" ,
3326+ setTaskApiConfigName : vi . fn ( ) ,
3327+ updateApiConfiguration : vi . fn ( ) ,
3328+ }
3329+ vi . spyOn ( sibling , "getCurrentTask" ) . mockReturnValue (
3330+ siblingTask as unknown as ReturnType < typeof sibling . getCurrentTask > ,
3331+ )
3332+ vi . spyOn ( provider , "postStateToWebview" ) . mockResolvedValue ( undefined )
3333+ vi . spyOn ( provider . providerSettingsManager , "getProfile" ) . mockResolvedValue ( {
3334+ name : "keeper-profile" ,
3335+ id : "keeper-id" ,
3336+ apiProvider : providerIdentifiers . anthropic ,
3337+ } )
3338+ vi . spyOn ( provider . providerSettingsManager , "deleteConfig" ) . mockResolvedValue ( undefined )
3339+
3340+ await provider . deleteProviderProfile ( oldProfile )
3341+
3342+ // The sibling's view buffer is still re-pointed to the surviving
3343+ // profile...
3344+ expect ( sibling [ "viewLocalState" ] . currentApiConfigName ) . toBe ( "keeper-profile" )
3345+ expect ( sibling [ "viewLocalState" ] . apiConfiguration ) . toEqual ( {
3346+ id : "keeper-id" ,
3347+ apiProvider : providerIdentifiers . anthropic ,
3348+ } )
3349+ // ...but the task follows another profile: neither the handler rebuild
3350+ // nor the sticky-name rewrite may touch it.
3351+ expect ( siblingTask . updateApiConfiguration ) . not . toHaveBeenCalled ( )
3352+ expect ( siblingTask . setTaskApiConfigName ) . not . toHaveBeenCalled ( )
3353+ // Let queued view-local persistence settle while the console spies are
3354+ // still installed, so teardown has no pending writes.
3355+ await new Promise ( ( resolve ) => setTimeout ( resolve , 50 ) )
3356+ } finally {
3357+ await sibling . dispose ( )
3358+ await provider . dispose ( )
3359+ }
3360+ } )
3361+
32793362 it ( "leaves the view buffer untouched when the deleted profile is neither globally active nor view-pinned" , async ( ) => {
32803363 const provider = new ClineProvider (
32813364 mockContext ,
0 commit comments