@@ -29,6 +29,7 @@ import {
2929 type ClineMessage ,
3030 type ClineSay ,
3131 type ClineAsk ,
32+ type ExtensionMessage ,
3233 type ToolProgressStatus ,
3334 type HistoryItem ,
3435 type CreateTaskOptions ,
@@ -523,7 +524,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
523524 this . messageQueueStateChangedHandler = ( ) => {
524525 this . emit ( RooCodeEventName . TaskUserMessage , this . taskId )
525526 this . emit ( RooCodeEventName . QueuedMessagesUpdated , this . taskId , this . messageQueueService . messages )
526- this . providerRef . deref ( ) ?. postStateToWebviewWithoutTaskHistory ( )
527+ this . postTaskStateToWebview ( ) . catch ( ( ) => undefined )
527528 }
528529
529530 this . messageQueueService . on ( "stateChanged" , this . messageQueueStateChangedHandler )
@@ -666,9 +667,13 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
666667
667668 this . providerProfileChangeListener = async ( ) => {
668669 try {
670+ if ( ! provider . isTaskVisible ( this . taskId ) ) {
671+ return
672+ }
669673 const newState = await provider . getState ( )
670674 if ( newState ?. apiConfiguration ) {
671675 this . updateApiConfiguration ( newState . apiConfiguration )
676+ this . setTaskApiConfigName ( newState . currentApiConfigName )
672677 }
673678 } catch ( error ) {
674679 console . error (
@@ -681,6 +686,50 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
681686 provider . on ( RooCodeEventName . ProviderProfileChanged , this . providerProfileChangeListener )
682687 }
683688
689+ private async getTaskScopedState ( ) : Promise < Awaited < ReturnType < ClineProvider [ "getState" ] > > | undefined > {
690+ const provider = this . providerRef . deref ( )
691+ if ( ! provider ) {
692+ return undefined
693+ }
694+
695+ const state = await provider . getState ( )
696+
697+ return {
698+ ...state ,
699+ mode : await this . getTaskMode ( ) . catch ( ( ) => state . mode ?? defaultModeSlug ) ,
700+ apiConfiguration : this . apiConfiguration ,
701+ currentApiConfigName : await this . getTaskApiConfigName ( ) . catch ( ( ) => state . currentApiConfigName ) ,
702+ }
703+ }
704+
705+ private async postTaskStateToWebview ( ) : Promise < void > {
706+ const provider = this . providerRef . deref ( )
707+ if ( ! provider ) {
708+ return
709+ }
710+
711+ if ( typeof provider . postTaskStateToWebview === "function" ) {
712+ await provider . postTaskStateToWebview ( this . taskId )
713+ return
714+ }
715+
716+ await provider . postStateToWebviewWithoutTaskHistory ?.( )
717+ }
718+
719+ private async postTaskMessageToWebview ( message : ExtensionMessage ) : Promise < void > {
720+ const provider = this . providerRef . deref ( )
721+ if ( ! provider ) {
722+ return
723+ }
724+
725+ if ( typeof provider . postTaskMessageToWebview === "function" ) {
726+ await provider . postTaskMessageToWebview ( this . taskId , message )
727+ return
728+ }
729+
730+ await provider . postMessageToWebview ?.( message )
731+ }
732+
684733 /**
685734 * Wait for the task mode to be initialized before proceeding.
686735 * This method ensures that any operations depending on the task mode
@@ -1155,10 +1204,9 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
11551204
11561205 private async addToClineMessages ( message : ClineMessage ) {
11571206 this . clineMessages . push ( message )
1158- const provider = this . providerRef . deref ( )
11591207 // Avoid resending large, mostly-static fields (notably taskHistory) on every chat message update.
11601208 // taskHistory is maintained in-memory in the webview and updated via taskHistoryItemUpdated.
1161- await provider ?. postStateToWebviewWithoutTaskHistory ( )
1209+ await this . postTaskStateToWebview ( )
11621210 this . emit ( RooCodeEventName . Message , { action : "created" , message } )
11631211 await this . saveClineMessages ( )
11641212
@@ -1190,8 +1238,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
11901238 }
11911239
11921240 private async updateClineMessage ( message : ClineMessage ) {
1193- const provider = this . providerRef . deref ( )
1194- await provider ?. postMessageToWebview ( { type : "messageUpdated" , clineMessage : message } )
1241+ await this . postTaskMessageToWebview ( { type : "messageUpdated" , clineMessage : message } )
11951242 this . emit ( RooCodeEventName . Message , { action : "updated" , message } )
11961243
11971244 // Check if we should sync to cloud and haven't already synced this message
@@ -1362,7 +1409,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
13621409
13631410 // Automatically approve if the ask according to the user's settings.
13641411 const provider = this . providerRef . deref ( )
1365- const state = provider ? await provider . getState ( ) : undefined
1412+ const state = await this . getTaskScopedState ( )
13661413 const approval = await checkAutoApproval ( { state, ask : type , text, isProtected } )
13671414
13681415 if ( approval . decision === "approve" ) {
@@ -1399,7 +1446,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
13991446 if ( message ) {
14001447 this . interactiveAsk = message
14011448 this . emit ( RooCodeEventName . TaskInteractive , this . taskId )
1402- provider ?. postMessageToWebview ( { type : "interactionRequired" } )
1449+ this . postTaskMessageToWebview ( { type : "interactionRequired" } ) . catch ( ( ) => undefined )
14031450 }
14041451 } , statusMutationTimeout ) ,
14051452 )
@@ -1608,7 +1655,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
16081655
16091656 // Update this task's API configuration to match the new profile
16101657 // This ensures the parser state is synchronized with the selected model
1611- const newState = await provider . getState ( )
1658+ const newState = await this . getTaskScopedState ( )
16121659 if ( newState ?. apiConfiguration ) {
16131660 this . updateApiConfiguration ( newState . apiConfiguration )
16141661 }
@@ -1653,7 +1700,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
16531700 const systemPrompt = await this . getSystemPrompt ( )
16541701
16551702 // Get condensing configuration
1656- const state = await this . providerRef . deref ( ) ?. getState ( )
1703+ const state = await this . getTaskScopedState ( )
16571704 const customCondensingPrompt = state ?. customSupportPrompts ?. CONDENSE
16581705 const { mode, apiConfiguration } = state ?? { }
16591706
@@ -1892,7 +1939,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
18921939 return { enabledToolCount : 0 , enabledServerCount : 0 }
18931940 }
18941941
1895- const { mcpEnabled } = ( await provider . getState ( ) ) ?? { }
1942+ const { mcpEnabled } = ( await this . getTaskScopedState ( ) ) ?? { }
18961943 if ( ! ( mcpEnabled ?? true ) ) {
18971944 return { enabledToolCount : 0 , enabledServerCount : 0 }
18981945 }
@@ -1948,7 +1995,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
19481995 // The todo list is already set in the constructor if initialTodos were provided
19491996 // No need to add any messages - the todoList property is already set
19501997
1951- await this . providerRef . deref ( ) ?. postStateToWebviewWithoutTaskHistory ( )
1998+ await this . postTaskStateToWebview ( )
19521999
19532000 await this . say ( "text" , task , images )
19542001
@@ -2593,7 +2640,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
25932640 )
25942641
25952642 const provider = this . providerRef . deref ( )
2596- const state = provider ? await provider . getState ( ) : undefined
2643+ const state = await this . getTaskScopedState ( )
25972644
25982645 const showRooIgnoredFiles = state ?. showRooIgnoredFiles ?? false
25992646 const includeDiagnosticMessages = state ?. includeDiagnosticMessages ?? true
@@ -2616,7 +2663,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
26162663 if ( slashCommandMode ) {
26172664 const provider = this . providerRef . deref ( )
26182665 if ( provider ) {
2619- const state = await provider . getState ( )
2666+ const state = await this . getTaskScopedState ( )
26202667 const targetMode = getModeBySlug ( slashCommandMode , state ?. customModes )
26212668 if ( targetMode ) {
26222669 await provider . handleModeSwitch ( slashCommandMode )
@@ -2671,7 +2718,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
26712718 } satisfies ClineApiReqInfo )
26722719
26732720 await this . saveClineMessages ( )
2674- await this . providerRef . deref ( ) ?. postStateToWebviewWithoutTaskHistory ( )
2721+ await this . postTaskStateToWebview ( )
26752722
26762723 try {
26772724 let cacheWriteTokens = 0
@@ -3300,7 +3347,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
33003347 )
33013348
33023349 // Apply exponential backoff similar to first-chunk errors when auto-resubmit is enabled
3303- const stateForBackoff = await this . providerRef . deref ( ) ?. getState ( )
3350+ const stateForBackoff = await this . getTaskScopedState ( )
33043351 if ( stateForBackoff ?. autoApprovalEnabled ) {
33053352 await this . backoffAndAnnounce ( currentItem . retryAttempt ?? 0 , error )
33063353
@@ -3432,7 +3479,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
34323479 }
34333480
34343481 await this . saveClineMessages ( )
3435- await this . providerRef . deref ( ) ?. postStateToWebviewWithoutTaskHistory ( )
3482+ await this . postTaskStateToWebview ( )
34363483
34373484 // No legacy text-stream tool parser state to reset.
34383485
@@ -3676,7 +3723,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
36763723 // apiConversationHistory at line 1876. Since the assistant failed to respond,
36773724 // we need to remove that message before retrying to avoid having two consecutive
36783725 // user messages (which would cause tool_result validation errors).
3679- let state = await this . providerRef . deref ( ) ?. getState ( )
3726+ let state = await this . getTaskScopedState ( )
36803727 if ( this . apiConversationHistory . length > 0 ) {
36813728 const lastMessage = this . apiConversationHistory [ this . apiConversationHistory . length - 1 ]
36823729 if ( lastMessage . role === "user" ) {
@@ -3773,7 +3820,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
37733820 }
37743821
37753822 private async getSystemPrompt ( ) : Promise < string > {
3776- const { mcpEnabled } = ( await this . providerRef . deref ( ) ?. getState ( ) ) ?? { }
3823+ const state = await this . getTaskScopedState ( )
3824+ const { mcpEnabled } = state ?? { }
37773825 let mcpHub : McpHub | undefined
37783826 if ( mcpEnabled ?? true ) {
37793827 const provider = this . providerRef . deref ( )
@@ -3797,8 +3845,6 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
37973845
37983846 const rooIgnoreInstructions = this . rooIgnoreController ?. getInstructions ( )
37993847
3800- const state = await this . providerRef . deref ( ) ?. getState ( )
3801-
38023848 const {
38033849 mode,
38043850 customModes,
@@ -3857,7 +3903,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
38573903 }
38583904
38593905 private async handleContextWindowExceededError ( ) : Promise < void > {
3860- const state = await this . providerRef . deref ( ) ?. getState ( )
3906+ const state = await this . getTaskScopedState ( )
38613907 const { profileThresholds = { } , mode, apiConfiguration } = state ?? { }
38623908
38633909 const { contextTokens } = this . getTokenUsage ( )
@@ -3881,7 +3927,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
38813927 `Forcing truncation to ${ FORCED_CONTEXT_REDUCTION_PERCENT } % of current context.` ,
38823928 )
38833929 // Send condenseTaskContextStarted to show in-progress indicator
3884- await this . providerRef . deref ( ) ?. postMessageToWebview ( { type : "condenseTaskContextStarted" , text : this . taskId } )
3930+ await this . postTaskMessageToWebview ( { type : "condenseTaskContextStarted" , text : this . taskId } )
38853931
38863932 // Build tools for condensing metadata (same tools used for normal API calls)
38873933 const provider = this . providerRef . deref ( )
@@ -3975,9 +4021,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
39754021 } finally {
39764022 // Notify webview that context management is complete (removes in-progress spinner)
39774023 // IMPORTANT: Must always be sent to dismiss the spinner, even on error
3978- await this . providerRef
3979- . deref ( )
3980- ?. postMessageToWebview ( { type : "condenseTaskContextResponse" , text : this . taskId } )
4024+ await this . postTaskMessageToWebview ( { type : "condenseTaskContextResponse" , text : this . taskId } )
39814025 }
39824026 }
39834027
@@ -3988,7 +4032,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
39884032 * the `api_req_rate_limit_wait` say type (not an error).
39894033 */
39904034 private async maybeWaitForProviderRateLimit ( retryAttempt : number ) : Promise < void > {
3991- const state = await this . providerRef . deref ( ) ?. getState ( )
4035+ const state = await this . getTaskScopedState ( )
39924036 const rateLimitSeconds =
39934037 state ?. apiConfiguration ?. rateLimitSeconds ?? this . apiConfiguration ?. rateLimitSeconds ?? 0
39944038
@@ -4019,7 +4063,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
40194063 retryAttempt : number = 0 ,
40204064 options : { skipProviderRateLimit ?: boolean } = { } ,
40214065 ) : ApiStream {
4022- const state = await this . providerRef . deref ( ) ?. getState ( )
4066+ const state = await this . getTaskScopedState ( )
40234067
40244068 const {
40254069 apiConfiguration,
@@ -4090,9 +4134,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
40904134 // This notification must be sent here (not earlier) because the early check uses stale token count
40914135 // (before user message is added to history), which could incorrectly skip showing the indicator
40924136 if ( contextManagementWillRun && autoCondenseContext ) {
4093- await this . providerRef
4094- . deref ( )
4095- ?. postMessageToWebview ( { type : "condenseTaskContextStarted" , text : this . taskId } )
4137+ await this . postTaskMessageToWebview ( { type : "condenseTaskContextStarted" , text : this . taskId } )
40964138 }
40974139
40984140 // Build tools for condensing metadata (same tools used for normal API calls)
@@ -4212,9 +4254,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
42124254 // This removes the in-progress spinner and allows the completed result to show
42134255 // IMPORTANT: Must always be sent to dismiss the spinner, even on error
42144256 if ( contextManagementWillRun && autoCondenseContext ) {
4215- await this . providerRef
4216- . deref ( )
4217- ?. postMessageToWebview ( { type : "condenseTaskContextResponse" , text : this . taskId } )
4257+ await this . postTaskMessageToWebview ( { type : "condenseTaskContextResponse" , text : this . taskId } )
42184258 }
42194259 }
42204260 }
@@ -4409,7 +4449,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
44094449 // Shared exponential backoff for retries (first-chunk and mid-stream)
44104450 private async backoffAndAnnounce ( retryAttempt : number , error : any ) : Promise < void > {
44114451 try {
4412- const state = await this . providerRef . deref ( ) ?. getState ( )
4452+ const state = await this . getTaskScopedState ( )
44134453 const baseDelay = state ?. requestDelaySeconds || 5
44144454
44154455 let exponentialDelay = Math . min (
0 commit comments