@@ -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
@@ -111,6 +111,8 @@ export class ClineProvider
111111 protected mcpHub ?: McpHub // Change from private to protected
112112 private marketplaceManager : MarketplaceManager
113113 private mdmService ?: MdmService
114+ private taskCreationCallback : ( task : Task ) => void
115+ private taskEventListeners : WeakMap < Task , Array < ( ) => void > > = new WeakMap ( )
114116
115117 public isViewLaunched = false
116118 public settingsImportedAt ?: number
@@ -162,6 +164,40 @@ export class ClineProvider
162164
163165 this . marketplaceManager = new MarketplaceManager ( this . context , this . customModesManager )
164166
167+ this . taskCreationCallback = ( instance : Task ) => {
168+ this . emit ( RooCodeEventName . TaskCreated , instance )
169+
170+ // Create named listener functions so we can remove them later.
171+ const onTaskStarted = ( ) => this . emit ( RooCodeEventName . TaskStarted , instance . taskId )
172+ const onTaskCompleted = ( taskId : string , tokenUsage : any , toolUsage : any ) =>
173+ this . emit ( RooCodeEventName . TaskCompleted , taskId , tokenUsage , toolUsage )
174+ const onTaskAborted = ( ) => this . emit ( RooCodeEventName . TaskAborted , instance . taskId )
175+ const onTaskFocused = ( ) => this . emit ( RooCodeEventName . TaskFocused , instance . taskId )
176+ const onTaskUnfocused = ( ) => this . emit ( RooCodeEventName . TaskUnfocused , instance . taskId )
177+ const onTaskActive = ( taskId : string ) => this . emit ( RooCodeEventName . TaskActive , taskId )
178+ const onTaskIdle = ( taskId : string ) => this . emit ( RooCodeEventName . TaskIdle , taskId )
179+
180+ // Attach the listeners.
181+ instance . on ( RooCodeEventName . TaskStarted , onTaskStarted )
182+ instance . on ( RooCodeEventName . TaskCompleted , onTaskCompleted )
183+ instance . on ( RooCodeEventName . TaskAborted , onTaskAborted )
184+ instance . on ( RooCodeEventName . TaskFocused , onTaskFocused )
185+ instance . on ( RooCodeEventName . TaskUnfocused , onTaskUnfocused )
186+ instance . on ( RooCodeEventName . TaskActive , onTaskActive )
187+ instance . on ( RooCodeEventName . TaskIdle , onTaskIdle )
188+
189+ // Store the cleanup functions for later removal.
190+ this . taskEventListeners . set ( instance , [
191+ ( ) => instance . off ( RooCodeEventName . TaskStarted , onTaskStarted ) ,
192+ ( ) => instance . off ( RooCodeEventName . TaskCompleted , onTaskCompleted ) ,
193+ ( ) => instance . off ( RooCodeEventName . TaskAborted , onTaskAborted ) ,
194+ ( ) => instance . off ( RooCodeEventName . TaskFocused , onTaskFocused ) ,
195+ ( ) => instance . off ( RooCodeEventName . TaskUnfocused , onTaskUnfocused ) ,
196+ ( ) => instance . off ( RooCodeEventName . TaskActive , onTaskActive ) ,
197+ ( ) => instance . off ( RooCodeEventName . TaskIdle , onTaskIdle ) ,
198+ ] )
199+ }
200+
165201 // Initialize Roo Code Cloud profile sync.
166202 this . initializeCloudProfileSync ( ) . catch ( ( error ) => {
167203 this . log ( `Failed to initialize cloud profile sync: ${ error } ` )
@@ -297,6 +333,14 @@ export class ClineProvider
297333
298334 task . emit ( RooCodeEventName . TaskUnfocused )
299335
336+ // Remove event listeners before clearing the reference.
337+ const cleanupFunctions = this . taskEventListeners . get ( task )
338+
339+ if ( cleanupFunctions ) {
340+ cleanupFunctions . forEach ( ( cleanup ) => cleanup ( ) )
341+ this . taskEventListeners . delete ( task )
342+ }
343+
300344 // Make sure no reference kept, once promises end it will be
301345 // garbage collected.
302346 task = undefined
@@ -654,12 +698,17 @@ export class ClineProvider
654698 enableCheckpoints,
655699 fuzzyMatchThreshold,
656700 experiments,
701+ cloudUserInfo,
702+ remoteControlEnabled,
657703 } = await this . getState ( )
658704
659705 if ( ! ProfileValidator . isProfileAllowed ( apiConfiguration , organizationAllowList ) ) {
660706 throw new OrganizationAllowListViolationError ( t ( "common:errors.violated_organization_allowlist" ) )
661707 }
662708
709+ // Determine if TaskBridge should be enabled
710+ const enableTaskBridge = isRemoteControlEnabled ( cloudUserInfo , remoteControlEnabled )
711+
663712 const task = new Task ( {
664713 provider : this ,
665714 apiConfiguration,
@@ -673,7 +722,8 @@ export class ClineProvider
673722 rootTask : this . clineStack . length > 0 ? this . clineStack [ 0 ] : undefined ,
674723 parentTask,
675724 taskNumber : this . clineStack . length + 1 ,
676- onCreated : ( instance ) => this . emit ( RooCodeEventName . TaskCreated , instance ) ,
725+ onCreated : this . taskCreationCallback ,
726+ enableTaskBridge,
677727 ...options ,
678728 } )
679729
@@ -738,8 +788,13 @@ export class ClineProvider
738788 enableCheckpoints,
739789 fuzzyMatchThreshold,
740790 experiments,
791+ cloudUserInfo,
792+ remoteControlEnabled,
741793 } = await this . getState ( )
742794
795+ // Determine if TaskBridge should be enabled
796+ const enableTaskBridge = isRemoteControlEnabled ( cloudUserInfo , remoteControlEnabled )
797+
743798 const task = new Task ( {
744799 provider : this ,
745800 apiConfiguration,
@@ -752,7 +807,8 @@ export class ClineProvider
752807 rootTask : historyItem . rootTask ,
753808 parentTask : historyItem . parentTask ,
754809 taskNumber : historyItem . number ,
755- onCreated : ( instance ) => this . emit ( RooCodeEventName . TaskCreated , instance ) ,
810+ onCreated : this . taskCreationCallback ,
811+ enableTaskBridge,
756812 } )
757813
758814 await this . addClineToStack ( task )
@@ -1631,6 +1687,7 @@ export class ClineProvider
16311687 includeDiagnosticMessages,
16321688 maxDiagnosticMessages,
16331689 includeTaskHistoryInEnhance,
1690+ remoteControlEnabled,
16341691 } = await this . getState ( )
16351692
16361693 const telemetryKey = process . env . POSTHOG_API_KEY
@@ -1758,6 +1815,7 @@ export class ClineProvider
17581815 includeDiagnosticMessages : includeDiagnosticMessages ?? true ,
17591816 maxDiagnosticMessages : maxDiagnosticMessages ?? 50 ,
17601817 includeTaskHistoryInEnhance : includeTaskHistoryInEnhance ?? false ,
1818+ remoteControlEnabled : remoteControlEnabled ?? false ,
17611819 }
17621820 }
17631821
@@ -1945,6 +2003,8 @@ export class ClineProvider
19452003 maxDiagnosticMessages : stateValues . maxDiagnosticMessages ?? 50 ,
19462004 // Add includeTaskHistoryInEnhance setting
19472005 includeTaskHistoryInEnhance : stateValues . includeTaskHistoryInEnhance ?? false ,
2006+ // Add remoteControlEnabled setting
2007+ remoteControlEnabled : stateValues . remoteControlEnabled ?? false ,
19482008 }
19492009 }
19502010
@@ -2057,6 +2117,55 @@ export class ClineProvider
20572117 return true
20582118 }
20592119
2120+ /**
2121+ * Handle remote control enabled/disabled state changes
2122+ * Manages ExtensionBridgeService and TaskBridgeService lifecycle
2123+ */
2124+ public async handleRemoteControlToggle ( enabled : boolean ) : Promise < void > {
2125+ const {
2126+ CloudService : CloudServiceImport ,
2127+ ExtensionBridgeService,
2128+ TaskBridgeService,
2129+ } = await import ( "@roo-code/cloud" )
2130+ const userInfo = CloudServiceImport . instance . getUserInfo ( )
2131+
2132+ // Handle ExtensionBridgeService using static method
2133+ await ExtensionBridgeService . handleRemoteControlState ( userInfo , enabled , this , ( message : string ) =>
2134+ this . log ( message ) ,
2135+ )
2136+
2137+ if ( isRemoteControlEnabled ( userInfo , enabled ) ) {
2138+ // Set up TaskBridgeService for the currently active task if one exists
2139+ const currentTask = this . getCurrentCline ( )
2140+ if ( currentTask && ! currentTask . taskBridgeService ) {
2141+ try {
2142+ currentTask . taskBridgeService = TaskBridgeService . getInstance ( )
2143+ await currentTask . taskBridgeService . subscribeToTask ( currentTask )
2144+ this . log ( `[TaskBridgeService] Subscribed current task ${ currentTask . taskId } to TaskBridge` )
2145+ } catch ( error ) {
2146+ const message = `[TaskBridgeService#subscribeToTask] ${ error instanceof Error ? error . message : String ( error ) } `
2147+ this . log ( message )
2148+ console . error ( message )
2149+ }
2150+ }
2151+ } else {
2152+ // Disconnect TaskBridgeService for all tasks in the stack
2153+ for ( const task of this . clineStack ) {
2154+ if ( task . taskBridgeService ) {
2155+ try {
2156+ await task . taskBridgeService . unsubscribeFromTask ( task . taskId )
2157+ task . taskBridgeService = undefined
2158+ this . log ( `[TaskBridgeService] Unsubscribed task ${ task . taskId } from TaskBridge` )
2159+ } catch ( error ) {
2160+ const message = `[TaskBridgeService#unsubscribeFromTask] for task ${ task . taskId } : ${ error instanceof Error ? error . message : String ( error ) } `
2161+ this . log ( message )
2162+ console . error ( message )
2163+ }
2164+ }
2165+ }
2166+ }
2167+ }
2168+
20602169 /**
20612170 * Returns properties to be included in every telemetry event
20622171 * This method is called by the telemetry service to get context information
0 commit comments