@@ -17,7 +17,6 @@ import {
1717 type ProviderSettings ,
1818 type RooCodeSettings ,
1919 type ProviderSettingsEntry ,
20- type ProviderSettingsWithId ,
2120 type TelemetryProperties ,
2221 type TelemetryPropertiesProvider ,
2322 type CodeActionId ,
@@ -66,6 +65,7 @@ import { fileExistsAtPath } from "../../utils/fs"
6665import { setTtsEnabled , setTtsSpeed } from "../../utils/tts"
6766import { getWorkspaceGitInfo } from "../../utils/git"
6867import { getWorkspacePath } from "../../utils/path"
68+ import { isRemoteControlEnabled } from "../../utils/remoteControl"
6969
7070import { setPanel } from "../../activate/registerCommands"
7171
@@ -112,6 +112,8 @@ export class ClineProvider
112112 protected mcpHub ?: McpHub // Change from private to protected
113113 private marketplaceManager : MarketplaceManager
114114 private mdmService ?: MdmService
115+ private taskCreationCallback : ( task : Task ) => void
116+ private taskEventListeners : WeakMap < Task , Array < ( ) => void > > = new WeakMap ( )
115117
116118 public isViewLaunched = false
117119 public settingsImportedAt ?: number
@@ -161,6 +163,40 @@ export class ClineProvider
161163
162164 this . marketplaceManager = new MarketplaceManager ( this . context , this . customModesManager )
163165
166+ this . taskCreationCallback = ( instance : Task ) => {
167+ this . emit ( RooCodeEventName . TaskCreated , instance )
168+
169+ // Create named listener functions so we can remove them later.
170+ const onTaskStarted = ( ) => this . emit ( RooCodeEventName . TaskStarted , instance . taskId )
171+ const onTaskCompleted = ( taskId : string , tokenUsage : any , toolUsage : any ) =>
172+ this . emit ( RooCodeEventName . TaskCompleted , taskId , tokenUsage , toolUsage )
173+ const onTaskAborted = ( ) => this . emit ( RooCodeEventName . TaskAborted , instance . taskId )
174+ const onTaskFocused = ( ) => this . emit ( RooCodeEventName . TaskFocused , instance . taskId )
175+ const onTaskUnfocused = ( ) => this . emit ( RooCodeEventName . TaskUnfocused , instance . taskId )
176+ const onTaskActive = ( taskId : string ) => this . emit ( RooCodeEventName . TaskActive , taskId )
177+ const onTaskIdle = ( taskId : string ) => this . emit ( RooCodeEventName . TaskIdle , taskId )
178+
179+ // Attach the listeners.
180+ instance . on ( RooCodeEventName . TaskStarted , onTaskStarted )
181+ instance . on ( RooCodeEventName . TaskCompleted , onTaskCompleted )
182+ instance . on ( RooCodeEventName . TaskAborted , onTaskAborted )
183+ instance . on ( RooCodeEventName . TaskFocused , onTaskFocused )
184+ instance . on ( RooCodeEventName . TaskUnfocused , onTaskUnfocused )
185+ instance . on ( RooCodeEventName . TaskActive , onTaskActive )
186+ instance . on ( RooCodeEventName . TaskIdle , onTaskIdle )
187+
188+ // Store the cleanup functions for later removal.
189+ this . taskEventListeners . set ( instance , [
190+ ( ) => instance . off ( RooCodeEventName . TaskStarted , onTaskStarted ) ,
191+ ( ) => instance . off ( RooCodeEventName . TaskCompleted , onTaskCompleted ) ,
192+ ( ) => instance . off ( RooCodeEventName . TaskAborted , onTaskAborted ) ,
193+ ( ) => instance . off ( RooCodeEventName . TaskFocused , onTaskFocused ) ,
194+ ( ) => instance . off ( RooCodeEventName . TaskUnfocused , onTaskUnfocused ) ,
195+ ( ) => instance . off ( RooCodeEventName . TaskActive , onTaskActive ) ,
196+ ( ) => instance . off ( RooCodeEventName . TaskIdle , onTaskIdle ) ,
197+ ] )
198+ }
199+
164200 // Initialize Roo Code Cloud profile sync.
165201 this . initializeCloudProfileSync ( ) . catch ( ( error ) => {
166202 this . log ( `Failed to initialize cloud profile sync: ${ error } ` )
@@ -296,6 +332,14 @@ export class ClineProvider
296332
297333 task . emit ( RooCodeEventName . TaskUnfocused )
298334
335+ // Remove event listeners before clearing the reference.
336+ const cleanupFunctions = this . taskEventListeners . get ( task )
337+
338+ if ( cleanupFunctions ) {
339+ cleanupFunctions . forEach ( ( cleanup ) => cleanup ( ) )
340+ this . taskEventListeners . delete ( task )
341+ }
342+
299343 // Make sure no reference kept, once promises end it will be
300344 // garbage collected.
301345 task = undefined
@@ -652,12 +696,17 @@ export class ClineProvider
652696 enableCheckpoints,
653697 fuzzyMatchThreshold,
654698 experiments,
699+ cloudUserInfo,
700+ remoteControlEnabled,
655701 } = await this . getState ( )
656702
657703 if ( ! ProfileValidator . isProfileAllowed ( apiConfiguration , organizationAllowList ) ) {
658704 throw new OrganizationAllowListViolationError ( t ( "common:errors.violated_organization_allowlist" ) )
659705 }
660706
707+ // Determine if TaskBridge should be enabled
708+ const enableTaskBridge = isRemoteControlEnabled ( cloudUserInfo , remoteControlEnabled )
709+
661710 const task = new Task ( {
662711 provider : this ,
663712 apiConfiguration,
@@ -671,7 +720,8 @@ export class ClineProvider
671720 rootTask : this . clineStack . length > 0 ? this . clineStack [ 0 ] : undefined ,
672721 parentTask,
673722 taskNumber : this . clineStack . length + 1 ,
674- onCreated : ( instance ) => this . emit ( RooCodeEventName . TaskCreated , instance ) ,
723+ onCreated : this . taskCreationCallback ,
724+ enableTaskBridge,
675725 ...options ,
676726 } )
677727
@@ -736,8 +786,13 @@ export class ClineProvider
736786 enableCheckpoints,
737787 fuzzyMatchThreshold,
738788 experiments,
789+ cloudUserInfo,
790+ remoteControlEnabled,
739791 } = await this . getState ( )
740792
793+ // Determine if TaskBridge should be enabled
794+ const enableTaskBridge = isRemoteControlEnabled ( cloudUserInfo , remoteControlEnabled )
795+
741796 const task = new Task ( {
742797 provider : this ,
743798 apiConfiguration,
@@ -750,7 +805,8 @@ export class ClineProvider
750805 rootTask : historyItem . rootTask ,
751806 parentTask : historyItem . parentTask ,
752807 taskNumber : historyItem . number ,
753- onCreated : ( instance ) => this . emit ( RooCodeEventName . TaskCreated , instance ) ,
808+ onCreated : this . taskCreationCallback ,
809+ enableTaskBridge,
754810 } )
755811
756812 await this . addClineToStack ( task )
@@ -1629,6 +1685,7 @@ export class ClineProvider
16291685 includeDiagnosticMessages,
16301686 maxDiagnosticMessages,
16311687 includeTaskHistoryInEnhance,
1688+ remoteControlEnabled,
16321689 } = await this . getState ( )
16331690
16341691 const telemetryKey = process . env . POSTHOG_API_KEY
@@ -1756,6 +1813,7 @@ export class ClineProvider
17561813 includeDiagnosticMessages : includeDiagnosticMessages ?? true ,
17571814 maxDiagnosticMessages : maxDiagnosticMessages ?? 50 ,
17581815 includeTaskHistoryInEnhance : includeTaskHistoryInEnhance ?? false ,
1816+ remoteControlEnabled : remoteControlEnabled ?? false ,
17591817 }
17601818 }
17611819
@@ -1943,6 +2001,8 @@ export class ClineProvider
19432001 maxDiagnosticMessages : stateValues . maxDiagnosticMessages ?? 50 ,
19442002 // Add includeTaskHistoryInEnhance setting
19452003 includeTaskHistoryInEnhance : stateValues . includeTaskHistoryInEnhance ?? false ,
2004+ // Add remoteControlEnabled setting
2005+ remoteControlEnabled : stateValues . remoteControlEnabled ?? false ,
19462006 }
19472007 }
19482008
@@ -2055,6 +2115,55 @@ export class ClineProvider
20552115 return true
20562116 }
20572117
2118+ /**
2119+ * Handle remote control enabled/disabled state changes
2120+ * Manages ExtensionBridgeService and TaskBridgeService lifecycle
2121+ */
2122+ public async handleRemoteControlToggle ( enabled : boolean ) : Promise < void > {
2123+ const {
2124+ CloudService : CloudServiceImport ,
2125+ ExtensionBridgeService,
2126+ TaskBridgeService,
2127+ } = await import ( "@roo-code/cloud" )
2128+ const userInfo = CloudServiceImport . instance . getUserInfo ( )
2129+
2130+ // Handle ExtensionBridgeService using static method
2131+ await ExtensionBridgeService . handleRemoteControlState ( userInfo , enabled , this , ( message : string ) =>
2132+ this . log ( message ) ,
2133+ )
2134+
2135+ if ( isRemoteControlEnabled ( userInfo , enabled ) ) {
2136+ // Set up TaskBridgeService for the currently active task if one exists
2137+ const currentTask = this . getCurrentCline ( )
2138+ if ( currentTask && ! currentTask . taskBridgeService ) {
2139+ try {
2140+ currentTask . taskBridgeService = TaskBridgeService . getInstance ( )
2141+ await currentTask . taskBridgeService . subscribeToTask ( currentTask )
2142+ this . log ( `[TaskBridgeService] Subscribed current task ${ currentTask . taskId } to TaskBridge` )
2143+ } catch ( error ) {
2144+ const message = `[TaskBridgeService#subscribeToTask] ${ error instanceof Error ? error . message : String ( error ) } `
2145+ this . log ( message )
2146+ console . error ( message )
2147+ }
2148+ }
2149+ } else {
2150+ // Disconnect TaskBridgeService for all tasks in the stack
2151+ for ( const task of this . clineStack ) {
2152+ if ( task . taskBridgeService ) {
2153+ try {
2154+ await task . taskBridgeService . unsubscribeFromTask ( task . taskId )
2155+ task . taskBridgeService = undefined
2156+ this . log ( `[TaskBridgeService] Unsubscribed task ${ task . taskId } from TaskBridge` )
2157+ } catch ( error ) {
2158+ const message = `[TaskBridgeService#unsubscribeFromTask] for task ${ task . taskId } : ${ error instanceof Error ? error . message : String ( error ) } `
2159+ this . log ( message )
2160+ console . error ( message )
2161+ }
2162+ }
2163+ }
2164+ }
2165+ }
2166+
20582167 /**
20592168 * Returns properties to be included in every telemetry event
20602169 * This method is called by the telemetry service to get context information
0 commit comments